ジョブキューの解説:ワーカー、リトライ、スケジューリング
APIエンドポイントで一括登録後に500通のウェルカムメールを送信する必要があるとします。各メールを同期的に送信する間、ユーザーを45秒間待たせますか?それとも、その前にサーバーがタイムアウトしますか?
これこそがジョブキューが解決する問題です。ジョブキューを使用すると、時間のかかる処理をバックグラウンドプロセスにオフロードでき、タスクが裏側で確実に完了する間もアプリケーションの応答性を維持できます。
この記事では、ジョブキューの仕組みを概念レベルで説明します。ワーカー、リトライ戦略、スケジューリングに焦点を当て、バックエンドの内部構造に迷うことなく、自信を持って統合できるようにします。
重要なポイント
- ジョブキューはバックグラウンド処理のためにタスクを保存し、ワーカーが時間のかかる操作を独立して処理する間、アプリケーションの応答性を維持します。
- ほとんどのキューシステムは最低1回配信(at-least-once delivery)を提供するため、ジョブハンドラーは冪等性を持つ必要があり、重複実行の可能性に安全に対処できなければなりません。
- リトライには、ジッター付きの指数バックオフを実装し、最大リトライ回数を設定し、永続的に失敗したジョブはデッドレターキューに送って確認できるようにします。
- 1回限りの遅延ジョブと繰り返し実行されるcron形式のスケジュールを区別し、重複実行やタイムゾーンの不一致などの落とし穴に注意してください。
ジョブキューとは?
ジョブキューは、後で処理するためにタスクを保存するシステムです。リクエスト内で作業をすぐに実行する代わりに、アプリケーションはジョブをキューに追加します。その後、バックグラウンドワーカーがこれらのジョブを取得し、独立して実行します。
レストランの厨房を想像してください。ウェイター(あなたのAPI)が注文を受け取り、厨房(キュー)に渡します。料理人(ワーカー)が自分のペースで料理を準備します。お客様はコンロの前で待つ必要はありません。
一般的なユースケースには以下が含まれます:
- トランザクションメールの送信
- アップロードされたファイルや画像の処理
- レポートの生成
- 外部サービスとのデータ同期
- 定期的なメンテナンスタスクの実行
バックグラウンドジョブワーカーの動作
ワーカーは、キューからジョブを取得し、実行し、完了または失敗としてマークする専用プロセスです。
最低1回配信(At-Least-Once Delivery)
ここで重要な現実があります:ほとんどのジョブキューシステムは、**最低1回配信(at-least-once delivery)**を提供し、正確に1回(exactly-once)ではありません。つまり、ジョブが複数回実行される可能性があります。ワーカーが実行中にクラッシュした場合、別のワーカーが同じジョブを再試行する可能性があります。
これはバグではありません。信頼性のための意図的なトレードオフです。しかし、これはジョブハンドラーが**冪等性(idempotent)**を持つ必要があることを意味します:2回実行しても、1回実行した場合と同じ結果を生成する必要があります。ジョブがクレジットカードに請求を行う場合、重複請求に対する保護措置が必要です。
並行性とスケーリング
ワーカーは通常、並列で実行されます。ワーカープロセスを追加することで水平スケーリングが可能です。最新のキューシステムのほとんどは、並行性の制限(1つのワーカーが同時に処理するジョブの数)を設定できます。
グレースフルシャットダウンは標準的な手法です。新しいコードをデプロイする際、ワーカーは作業を途中で放棄するのではなく、現在のジョブを完了してから停止する必要があります。
Discover how at OpenReplay.com.
ジョブキューのリトライ戦略
ジョブは失敗します。ネットワークがタイムアウトします。外部APIがエラーを返します。堅牢なリトライ戦略は、システムが正常に回復するか、混乱に陥るかを決定します。
ジッター付き指数バックオフ
標準的なアプローチは**指数バックオフ(exponential backoff)**です:最初のリトライの前に1秒待機し、次に2秒、次に4秒、次に8秒と待機します。これにより、失敗しているサービスへの連続攻撃を防ぎます。
ジッター(jitter)—小さなランダムな遅延—を追加すると、数百の失敗したジョブがすべて同じ瞬間にリトライする「サンダリングハード(thundering herd)」を防ぎます。
リトライ制限とデッドレター処理
最大リトライ回数を設定してください。10回失敗するジョブは、一時的なエラーではなく、おそらくバグがあります。リトライを使い果たした後、ジョブは多くの場合、**デッドレターキュー(dead letter queue)**に移動されるか、手動確認のために永続的に失敗としてマークされます。
リトライ可能なエラー(ネットワークタイムアウト、レート制限)と永続的な失敗(無効なデータ、リソースの欠落)を区別してください。成功する見込みのないジョブにリトライを無駄にしないでください。
遅延ジョブとスケジューリング
ジョブキューは即時タスク以上のものを処理します。スケジューリング機能により、ジョブをいつ実行するかを制御できます。
1回限りの遅延ジョブ
特定の将来の時刻に実行するようにジョブをスケジュールします:「このリマインダーメールを24時間後に送信する」。ジョブは、スケジュールされた時刻が来るまでキューに残ります。
繰り返しおよびCron形式のジョブ
定期的な作業(日次レポート、時間ごとのクリーンアップ)については、ほとんどのシステムがcronのようなスケジューリングをサポートしています。パターンを定義すると、システムが自動的にジョブを作成します。
スケジューリングの一般的な落とし穴
重複実行:スケジューリングロジックが複数の場所で実行される場合、同じ繰り返しジョブが複数回作成される可能性があります。スケジュールされたジョブの作成は、1つのスケジューラーインスタンスのみが担当するようにしてください。
ダウンタイムギャップ:スケジュールされたジョブが実行されるべき時にシステムがダウンしている場合、スキップされるか、遅れて実行される可能性があります。キューの動作は異なるため、事前に理解しておく必要があります。
タイムゾーン:「午前9時」にスケジュールされたジョブにはタイムゾーンが必要です。バックエンドシステムにはUTCが最も安全で、ユーザー向け機能では変換をエッジで処理します。
フロントエンドのジョブキュー統合
ユーザーが開始したバックグラウンド作業にフィードバックが必要な場合、フロントエンドアプリケーションは通常、ステータスポーリングまたはリアルタイム更新を通じてジョブキューと対話します。
一般的なパターンには、ジョブステータスのエンドポイントのポーリング、WebSocket更新の受信、またはServer-Sent Eventsの使用が含まれます。一般的なアプローチは、すぐにジョブIDを返し、その後フロントエンドが進捗を個別に追跡することです。
結論
ジョブキューは遅い作業をリクエストパスから移動させますが、意図的な設計が必要です。最低1回配信を前提とし、冪等なハンドラーを構築してください。ジッター付きの指数バックオフと適切なリトライ制限を実装してください。1回限りの遅延ジョブと繰り返しスケジュールの違いを理解してください。
これらは高度な追加機能ではなく、本番システムの基本的な要件です。これらの基礎をマスターすれば、負荷がかかってもレスポンシブを維持し、バックグラウンド作業を確実に処理するアプリケーションを構築できます。
よくある質問
メッセージキューはサービス間でメッセージを渡すための汎用システムですが、ジョブキューはリトライ、スケジューリング、ワーカー管理などの機能を備えたバックグラウンドタスクを特に管理します。ジョブキューは多くの場合、メッセージキューの上に構築されますが、進捗追跡やデッドレター処理などのタスク固有の機能を追加します。
一意の識別子を使用して完了した作業を追跡し、処理前に確認します。データベースに冪等キーを保存し、一意制約付きのデータベーストランザクションを使用するか、Stripeの冪等キーなどの外部サービス機能を活用します。操作を繰り返しても、最初の実行以降に追加の効果がないように設計してください。
タスクが数百ミリ秒以上かかる場合、失敗する可能性のある外部サービスに依存する場合、スケジュールされた時刻に実行する必要がある場合、または並列処理の恩恵を受ける可能性がある場合は、ジョブキューを使用してください。即座のフィードバックが不可欠な高速操作には、同期処理を維持してください。
CPUコアごとに1つのワーカーから始めて、ワークロードに基づいて調整してください。API呼び出しのようなIO制約のあるジョブは、ワーカーあたりの並行性を高く設定できますが、CPU制約のあるジョブは、並行性を低くしてより多くのワーカーが必要です。キューの深さと処理時間を監視して、システムに適したバランスを見つけてください。
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.