キューワーカーの可観測性
キューの深さは、待機中の作業の量を示します。キュー待機は、各ジョブのバックログにかかるコストを示します。実行時間は、ワーカーが実行に費やす時間を示します。最終的な結果と再試行回数は、作業が最終的に成功するかどうかを示します。
正常なトラフィック バーストと、遅れているキュー、遅い依存関係、再試行の嵐、またはワーカーの失踪を区別するには、4 つの信号すべてが必要です。
テール パーセンタイルは、平均値で解消できる遅いジョブを明らかにします。
前提条件
- Telemetry API キー
- エンキュー、開始、および最終結果の境界を公開するワーカー
- 安定した論理ジョブ識別子とカーディナリティの低いジョブ名
- 重要なジョブの種類ごとに予想される完了時間枠
スナップショットと結果を個別にモデル化する
異なる質問に答えるため、2 つのイベント シェイプを使用します。
| イベント | 穀物 | 使用する |
|---|---|---|
queue_snapshot |
1 つのサンプリング時間に 1 つのキュー | 現在の深さ、利用可能なワーカー、最古の待機年齢 |
background_job_completed |
論理ジョブごとに 1 つの最終結果 | 待機、実行時間、再試行、成功、永続的な失敗 |
スナップショットはサンプリングされた状態であるため、時間にわたるキューの深さを合計しないでください。最終結果は完了した作業であるため、開始および消滅したジョブを見つけることはできません。軽量化を追加 job_started そして job_finished 停止したジョブの検出が必要な場合のライフサイクル イベント。
ターミナルイベントコントラクトを定義する
を使用します。 バックグラウンドジョブの完了スキーマ 出発点として:
| フィールド | 意味 |
|---|---|
event_id |
重複排除に使用される固有の端末イベント |
job_id |
試行間で共有される安定した論理ジョブ ID |
job_name |
動的ペイロードや ID ではなく、制限されたジョブ タイプ |
queue_name |
ジョブを実行したキュー |
account_id |
結果の影響を受ける仮名アカウント |
attempt_count |
ターミナル試行を含む合計試行数 |
queue_wait_ms |
最初の実行までのエンキュー |
duration_ms |
ターミナル試行の実行時間 |
status |
success または永久的な error |
release |
ワーカーまたはアプリケーションのバージョン |
ジョブの引数、資格情報、電子メール アドレス、ドキュメントの内容、および生の例外テキストをイベントに入れないでください。境界のあるものを使用する error_type オペレータが障害カテゴリを必要とする場合。
結果の境界を計測する
JavaScript SDK をインストールして初期化します。
npm install telemetry-sh
import telemetry from "telemetry-sh";
telemetry.init("YOUR_API_KEY");
同じクロックでエンキュー、最初の開始、および終了の完了を測定します。成功した後、または再試行が完了した後に 1 つのターミナル行を出力します。
async function processJob(job) {
const startedAtMs = Date.now();
let terminalAttemptStartedAtMs = startedAtMs;
let attemptCount = 0;
try {
const result = await runWithRetry(async () => {
attemptCount += 1;
terminalAttemptStartedAtMs = Date.now();
return performJob(job);
});
await telemetry.log("background_job_completed", {
event_id: crypto.randomUUID(),
job_id: job.id,
job_name: job.name,
queue_name: job.queue,
account_id: job.accountId,
attempt_count: attemptCount,
queue_wait_ms: startedAtMs - job.enqueuedAtMs,
duration_ms: Date.now() - terminalAttemptStartedAtMs,
total_elapsed_ms: Date.now() - startedAtMs,
status: "success",
release: process.env.APP_RELEASE ?? "unknown"
});
return result;
} catch (error) {
await telemetry.log("background_job_completed", {
event_id: crypto.randomUUID(),
job_id: job.id,
job_name: job.name,
queue_name: job.queue,
account_id: job.accountId,
attempt_count: attemptCount,
queue_wait_ms: startedAtMs - job.enqueuedAtMs,
duration_ms: Date.now() - terminalAttemptStartedAtMs,
total_elapsed_ms: Date.now() - startedAtMs,
status: "error",
error_type: classifyJobError(error),
release: process.env.APP_RELEASE ?? "unknown"
});
throw error;
}
}
この例では、端末の試行を維持します。 duration_ms および完全な再試行ポリシーのウォールタイム total_elapsed_ms。再試行ライブラリがこれらの境界を異なる方法で報告する場合は、文書化されている 2 つの意味を維持しながらタイマーを調整します。
テレメトリ呼び出しは、ジョブの永続状態の変更に従う必要があります。すでに成功したジョブをビジネス上の副作用の再試行に変えずに、アプリケーションがテレメトリ配信の失敗を処理する方法を決定します。保存する event_id 同じ端末イベントが再送信された場合、テレメトリ配信の再試行をまたいで実行されます。
サンプルキュー状態
キューの信頼できる状態から一定の間隔でスナップショットを収集します。
async function recordQueueSnapshot(queue) {
const state = await queue.inspect();
await telemetry.log("queue_snapshot", {
event_id: crypto.randomUUID(),
queue_name: queue.name,
depth: state.waitingCount,
active_workers: state.activeWorkers,
oldest_wait_ms: state.oldestEnqueuedAtMs
? Date.now() - state.oldestEnqueuedAtMs
: 0,
release: process.env.APP_RELEASE ?? "unknown"
});
}
この間隔は、意味のあるバックログを検出できる程度の頻度に保ちますが、同じサンプルがイベント量を占めるほど頻繁にはしないでください。ゼロの深さをゼロとして記録します。省略しないでください。
実行時からキュー待機を分離する
このクエリは、標準的な待機時間とテール待機時間とテール実行時間を比較します。
SELECT
job_name,
COUNT(*) AS jobs,
approx_percentile_cont(queue_wait_ms, 0.50) AS p50_wait_ms,
approx_percentile_cont(queue_wait_ms, 0.95) AS p95_wait_ms,
approx_percentile_cont(duration_ms, 0.95) AS p95_run_ms
FROM background_job_completed
WHERE timestamp_utc >= now() - INTERVAL '24 hours'
GROUP BY job_name
HAVING COUNT(*) >= 20
ORDER BY p95_wait_ms DESC;
通常の実行時間での高い p95 待機は、キャパシティ、スケジューリング、優先順位付け、またはバースト処理を示しています。通常の待機で実行時間が長い場合は、ジョブ コードまたは依存関係を示します。両方が同時に上昇している場合は、作業の遅さがワーカーの能力を消費し、バックログが発生していることを意味している可能性があります。
再試行と端末障害を測定する
終了イベントは1つの論理ジョブを記録するため、, attempt_count > 1 少なくとも 1 回の再試行後にジョブが回復されたことを意味します。
SELECT
job_name,
COUNT(*) AS jobs,
SUM(CASE WHEN attempt_count > 1 THEN 1 ELSE 0 END) AS retried_jobs,
SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) AS permanent_failures,
100.0 * SUM(CASE WHEN attempt_count > 1 THEN 1 ELSE 0 END)
/ NULLIF(COUNT(*), 0) AS retried_job_rate_pct,
100.0 * SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END)
/ NULLIF(COUNT(*), 0) AS permanent_failure_rate_pct
FROM background_job_completed
WHERE timestamp_utc >= now() - INTERVAL '7 days'
GROUP BY job_name
ORDER BY permanent_failure_rate_pct DESC, retried_job_rate_pct DESC;
試行ごとに 1 行を出力する場合は、代わりに attempt 畑と厩舎 job_id;分母と解釈が異なります。再試行が誤って個別の顧客ジョブとしてカウントされないように、すべてのクエリの横に行粒度を文書化します。
作業の停止と失われた作業を検出
ターミナルのみのテーブルでは、長時間実行されているジョブとワーカーのクラッシュ後に失われたジョブを区別できません。放出する job_started プラス job_completed または job_failed 同じように job_id、次に左結合を使用して、終了イベントのない開始を見つけます。
しきい値はジョブ固有であるか、予想される完了ウィンドウから導出される必要があります。長時間実行されるジョブにはハートビート イベントが必要な場合があります。最新の行で誤検知が発生しないように、イベント配信ラグに短い猶予期間を追加します。
完全なものを使用する 停止したバックグラウンド ジョブ クエリ キューの深さを特定のジョブが滞っている証拠として扱うのではなく、
デッドレター、優先順位、シャットダウンを考慮する
再試行ポリシーが使い果たされ、ジョブが配信不能キューに入ったときに、個別の端末カテゴリまたはイベントを記録します。オリジナルにリンクされた新しい操作アクションとしてリプレイを追跡する job_id;元の失敗を黙って書き直さないでください。
ワーカーが交換可能でない場合は、キューまたは優先度によってキャパシティ シグナルをセグメント化します。健全なバルク キューは、全体の平均でブロックされたクリティカル キューを隠すことができます。
デプロイ時およびシャットダウン時:
- 労働者を解雇する前に新たな仕事の受け入れを停止する。
- 文書化された排水間隔を許可します。
- 意図的な再キューと実行の失敗を区別します。
- 論理的なものを保持する
job_idそして試行状態をインクリメントします。 - 開始されたすべてのジョブが最終的に終了イベントを生成することを確認します。
ダッシュボードを構築する
の バックグラウンドジョブダッシュボードの例 SQL、合成結果、および解釈を提供します。実稼働ダッシュボードには以下を含める必要があります。
- 現在のキューの深さとキューごとの最も古い待機期間。
- p50 および p95 キューはジョブ名によって待機します。
- p95 ジョブ名別の実行時間。
- ジョブの再試行率と永続的な失敗率。
- 安全な相関識別子を持つ現在停止中のジョブ。
- リリースごとのボリュームを確認できるため、デプロイメントと変更を比較できます。
トレンドには完全なタイム バケットを使用します。レートをランク付けする前に、最小ボリュームのルールを設定します。静かなキュー内の単一の失敗したジョブが運用上重要になるのは、そのワークフローが重要な場合のみです。それ以外の場合は、パーセンテージだけで大量の回帰を上回ることはできません。
単なるしきい値ではなく、応答に対するアラート
各アラートを所有者とアクションに関連付けます。
| 状態 | ありそうな質問 | 最初の対応 |
|---|---|---|
| 深さと最古の待機上昇 | 需要がキャパシティを超えていますか? | 到着率、ワーカー、優先順位、依存関係の健全性を確認する |
| 待機が安定している間に実行時間が増加する | ジョブコードまたは依存関係によって速度が低下しましたか? | リリースとエラー カテゴリの比較 |
| リトライ率が上がる | 一時的な故障により仕事が増幅されますか? | 制限されたエラーの種類とプロバイダーの健全性を検査する |
| 永続的な障害が増加する | 回復力は尽きているのでしょうか? | 影響を受けるアカウントと配信不能状態を特定する |
| 開始には終了イベントがありません | 作業員がクラッシュしたり、計器類が消えたりしましたか? | ワーカー、ハートビート、ジョブの状態を検査する |
1 つの不完全なバケットでページングを行うことは避けてください。継続的な違反または重大な最終障害を必要とし、しきい値をワークフローの予想完了時間に合わせて維持します。
本番前に検証する
以下のフィクスチャを実行します。
- 最初の試みは成功。
- 再試行後に成功。
- 永続的な障害とデッドレターエントリ。
- 重複したイベント配信。
- 作業員の衝突事故後の
job_started; - ハートビートを伴う長時間実行ジョブ。
- 通常通りに排出されるキュー バースト。
- デプロイメントのシャットダウンと再キュー。
論理ジョブ数、試行回数、キューの待機時間、実行時間、欠落している端末イベント、および影響を受けるアカウントの数を確認します。また、テレメトリ障害が非冪等のビジネス操作を再生できないことも確認します。
次のステップ
続けて、 キュー待機レシピ, バックグラウンドジョブの再試行率のレシピ, 停止したバックグラウンド ジョブのレシピ、そして バックグラウンドジョブ監視の使用例.