エクスポネンシャルバックオフとは?意味・仕組み・実装方法を解説!

クライアントが通信が失敗した際に再試行(リトライ)までの待機時間を一定にすると、通信を受け取るサーバーに大きな負荷がかかる可能性があります。そこで「エクスポネンシャルバックオフ(Exponential Backoff)」という考え方があります。

この記事では『エクスポネンシャルバックオフ』について、以下の内容をサンプルコードを用いてわかりやすく解説します。

  • エクスポネンシャルバックオフとは
  • エクスポネンシャルバックオフの利点
  • エクスポネンシャルバックオフをJavaScriptで実装
  • エクスポネンシャルバックオフをPythonで実装

エクスポネンシャルバックオフとは

エクスポネンシャルバックオフとは

エクスポネンシャルバックオフ(Exponential Backoff)は、処理が失敗した際、再試行(リトライ)までの待機時間を指数関数的に増加させる方法です。最初は短い間隔でリトライを試みますが、失敗が続くと待機時間が徐々に長くなります。エクスポネンシャルバックオフにより、サーバの負荷を軽減したり、無駄なリクエストも省けます。

例えば以下のようにリトライを実行します。

  • 1回目のリクエスト失敗 → 1秒待機 → 再試行
  • 2回目のリクエスト失敗 → 2秒待機 → 再試行
  • 3回目のリクエスト失敗 → 4秒待機 → 再試行
  • 4回目のリクエスト失敗 → 8秒待機 → 再試行
  • 最大リトライ回数に達したら処理を中止する

このように、リトライ間隔を徐々に延ばし、最大リトライ回数に達したら処理を中止します。

エクスポネンシャルバックオフは、AWSやGoogle Cloudなど多くのクラウドプロバイダーが推奨しており、システムの安定性向上やリソース効率化に役立ちます。

具体的な増加パターン(例:待機時間を2倍にするなど)は実装によって異なる場合があります

エクスポネンシャルバックオフの利点

エクスポネンシャルバックオフの利点を以下に示します。

  • リソース効率の向上
    • 無駄なリクエストを減らすことで、システム全体の負荷を軽減します。
  • システムの安定性向上
    • ネットワークの輻輳を回避し、システムの回復時間を確保します。
  • 簡単な実装
    • アルゴリズムは単純で、ほとんどのプログラミング言語で実装可能です。

次に、JavaScriptとPythonでエクスポネンシャルバックオフを実装してみましょう。

エクスポネンシャルバックオフをJavaScriptで実装

以下は、JavaScriptでエクスポネンシャルバックオフを実装したシンプルなサンプルコードです。

async function exponentialBackoff(maxRetries, delay, operation) {
  let retryCount = 0; // 内部でリトライカウントを管理
  while (retryCount < maxRetries) {
    try {
      // 非同期操作を試行
      const result = await operation();
      return result;
    } catch (error) {
      retryCount++;
      if (retryCount >= maxRetries) {
        throw new Error(`リトライ回数が最大回数(${maxRetries})に達しました: ${error.message}`);
      }
      // 待機時間を指数関数的に増加
      const backoffDelay = delay * Math.pow(2, retryCount - 1);
      console.log(`${backoffDelay}ms待ってリトライします`);
      await new Promise((resolve) => setTimeout(resolve, backoffDelay));
    }
  }
}

// 使用例
const exampleOperation = async () => {
  // 失敗する可能性のある処理を記述
  const randomNumber = Math.random();
  console.log('randomNumber: ', randomNumber);
  if (randomNumber > 0.7) {
    return 'Success';
  } else {
    throw new Error('Failure');
  }
};

exponentialBackoff(5, 1000, exampleOperation)
  .then((result) => console.log(`処理が成功しました: ${result}`))
  .catch((error) => console.error(error.message));
  • retryCount: 現在のリトライ回数。
  • maxRetries: 最大リトライ回数。
  • delay: 初期待機時間(ミリ秒)。
  • operation: 実行する非同期操作。

上記のコードでは、最大5回リトライを試み、待機時間は最大8秒に制限されています。

なお、エクスポネンシャルバックオフでは、待機時間が無限に長くなるのを防ぐため、上限(maxDelay)を設定するのが一般的です。待機時間の上限を考慮したサンプルコードを以下に示します。

async function exponentialBackoff(maxRetries, delay, operation, maxDelay = 10000) {
  let retryCount = 0; // 内部でリトライカウントを管理
  while (retryCount < maxRetries) {
    try {
      // 非同期操作を試行
      const result = await operation();
      return result;
    } catch (error) {
      retryCount++;
      if (retryCount >= maxRetries) {
        throw new Error(`リトライ回数が最大回数(${maxRetries})に達しました: ${error.message}`);
      }
      // 待機時間を指数関数的に増加し、maxDelayを超えないように制限
      const backoffDelay = Math.min(delay * Math.pow(2, retryCount - 1), maxDelay);
      console.log(`${backoffDelay}ms待ってリトライします`);
      await new Promise((resolve) => setTimeout(resolve, backoffDelay));
    }
  }
}

// 使用例
const exampleOperation = async () => {
  // 失敗する可能性のある処理を記述
  const randomNumber = Math.random();
  console.log('randomNumber: ', randomNumber);
  if (randomNumber > 0.7) {
    return 'Success';
  } else {
    throw new Error('Failure');
  }
};

exponentialBackoff(5, 1000, exampleOperation, 8000)
  .then((result) => console.log(`処理が成功しました: ${result}`))
  .catch((error) => console.error(error.message));

上記のサンプルコードではMath.minを使用して、待機時間がmaxDelayを超えないように制御しています。

エクスポネンシャルバックオフをPythonで実装

以下は、Pythonでエクスポネンシャルバックオフを実装したシンプルなサンプルコードです。

import time
import random

def exponential_backoff(max_retries, delay, operation):
    retry_count = 0  # 内部でリトライカウントを管理
    while retry_count < max_retries:
        try:
            # 操作を試行
            result = operation()
            return result
        except Exception as e:
            retry_count += 1
            if retry_count >= max_retries:
                raise Exception(f"リトライ回数が最大回数({max_retries})に達しました: {e}")
            # 待機時間を指数関数的に増加
            backoff_delay = delay * (2 ** (retry_count - 1))
            print(f"{backoff_delay}ms待ってリトライします")
            time.sleep(backoff_delay / 1000)

# 使用例
def example_operation():
    # 失敗する可能性のある処理を記述
    random_number = random.random()
    print("randomNumber:", random_number)
    if random_number > 0.7:
        return 'Success'
    else:
        raise Exception('Failure')

try:
    result = exponential_backoff(5, 1000, example_operation)
    print(f"処理が成功しました: {result}")
except Exception as e:
    print(e)
  • retry_count: 現在のリトライ回数。
  • max_retries: 最大リトライ回数。
  • delay: 初期待機時間(ミリ秒)。
  • operation: 実行する操作。

上記のコードでも最大5回リトライを試み、待機時間は最大8秒に制限されています。なお、Pythonでは、time.sleep関数で待機時間を制御することができます。

本記事のまとめ

この記事では『エクスポネンシャルバックオフ』について、以下の内容を説明しました。

  • エクスポネンシャルバックオフとは
  • エクスポネンシャルバックオフの利点
  • エクスポネンシャルバックオフをJavaScriptで実装
  • エクスポネンシャルバックオフをPythonで実装

お読み頂きありがとうございました。