大規模システムと長時間実行のバックグラウンドジョブの構築。クレジット:Ilias Chebbi on Unsplash 数ヶ月前、私はインフラ構築が必要な役割を引き受けました大規模システムと長時間実行のバックグラウンドジョブの構築。クレジット:Ilias Chebbi on Unsplash 数ヶ月前、私はインフラ構築が必要な役割を引き受けました

説教のためのSpotifyを構築する。

2025/12/11 21:15

大規模システムと長時間実行バックグラウンドジョブのための構築。

クレジット:Ilias Chebbi on Unsplash

数ヶ月前、私はメディア(オーディオ)ストリーミングのためのインフラ構築が必要な役割を引き受けました。しかし、ストリーム可能なチャンクとしてオーディオを提供するだけでなく、長時間実行のメディア処理ジョブと、文字起こし、トランスコーディング、埋め込み、連続的なメディア更新に対応する広範なRAGパイプラインがありました。プロダクション思考でMVPを構築することで、シームレスなシステムを実現するまで何度も繰り返しました。私たちのアプローチは、機能と優先順位の基盤となるスタックを統合するものでした。

主な懸念事項:

構築の過程で、各イテレーションは即時かつしばしば「包括的な」ニーズへの対応として現れました。最初の懸念はジョブのキューイングでしたが、Redisで十分に対応できました。単に発行して忘れるだけでした。NEST JSフレームワークのBull MQは、リトライ、バックログ、デッドレターキューに対するさらに良いコントロールを提供しました。ローカルで、そして本番環境でいくつかのペイロードを使用して、メディアフローを正しく設定しました。すぐに観測性の重みに悩まされるようになりました:
ログ → ジョブの記録(リクエスト、レスポンス、エラー)。
メトリクス → これらのジョブがどれだけ/どれくらいの頻度で実行、失敗、完了するかなど。
トレース → ジョブがサービス間で取ったパス(フローパス内で呼び出された関数/メソッド)。

APIを設計し、それらをプラグインするカスタムダッシュボードを構築することで、これらの一部を解決できますが、スケーラビリティの問題が十分になります。実際、私たちはAPIを設計しました。

観測性のための構築

複雑で長時間実行するバックエンドワークフローを管理する課題、失敗が回復可能でなければならず、状態が耐久性を持つ必要がある場合、Inngestは私たちのアーキテクチャ的救済となりました。それは根本的に私たちのアプローチを再構成しました:各長時間実行バックグラウンドジョブは、特定のイベントによってトリガーされるバックグラウンド関数になります。

例えば、Transcription.request イベントはTranscribeAudio 関数をトリガーします。この関数には、fetch_audio_metadata、deepgram_transcribe、parse_save_trasncription、notify_userなどのステップ実行が含まれる場合があります。

ワークフローの分解:Inngest関数とステップ実行

コアの耐久性プリミティブはステップ実行です。バックグラウンド関数は内部的にこれらのステップ実行に分解され、それぞれが最小限の原子的なロジックブロックを含んでいます。

  • 原子的ロジック: 関数はビジネスロジックをステップバイステップで実行します。ステップが失敗した場合、実行全体の状態は保存され、実行を再試行できます。これにより、関数は最初から再起動されます。個々のステップやステップ実行は単独で再試行することはできません。
  • レスポンスのシリアル化: ステップ実行はそのレスポンスによって定義されます。このレスポンスは自動的にシリアル化され、実行境界を越えて複雑または強く型付けされたデータ構造を保存するために不可欠です。後続のステップ実行はこのシリアル化されたレスポンスを確実に解析できるか、効率性のためにロジックを単一のステップにマージできます。
  • デカップリングとスケジューリング: 関数内で、条件付きで新しい依存イベントをキューに入れたりスケジュールしたりでき、複雑なファンアウト/ファンインパターンと最大1年間の長期スケジューリングを可能にします。どの時点でのエラーや成功も捕捉され、分岐し、ワークフローの下流で処理できます。

Inngest関数の抽象:

import { inngest } from 'inngest-client';

export const createMyFunction = (dependencies) => {
return inngest.createFunction(
{
id: 'my-function',
name: 'My Example Function',
retries: 3, // 失敗時に実行全体を再試行
concurrency: { limit: 5 },
onFailure: async ({ event, error, step }) => {
// ここでエラーを処理
await step.run('handle-error', async () => {
console.error('Error processing event:', error);
});
},
},
{ event: 'my/event.triggered' },
async ({ event, step }) => {
const { payload } = event.data;

// ステップ1:最初のステップを定義
const step1Result = await step.run('step-1', async () => {
// ステップ1のロジック
return `Processed ${payload}`;
});

// ステップ2:2番目のステップを定義
const step2Result = await step.run('step-2', async () => {
// ステップ2のロジック
return step1Result + ' -> step 2';
});

// ステップN:必要に応じて続ける
await step.run('final-step', async () => {
// 最終化ロジック
console.log('Finished processing:', step2Result);
});

return { success: true };
},
);
};

Inngestのイベント駆動モデルは、すべてのワークフロー実行に対する詳細な洞察を提供します:

  • 包括的なイベントトレース: キューに入れられた関数実行はすべて、その発生元イベントに対してログに記録されます。これにより、単一のユーザーアクションに関連するすべてのアクティビティの明確な高レベルの追跡が提供されます。
  • 詳細な実行インサイト: 各関数実行(成功と失敗の両方)について、Inngestはack(確認応答)とnack(否定応答)レポートを通じて詳細なログを提供します。これらのログには、エラースタックトレース、完全なリクエストペイロード、および個々のステップ実行ごとのシリアル化されたレスポンスペイロードが含まれます。
  • 運用メトリクス: ログを超えて、成功率、失敗率、再試行回数を含む関数の健全性に関する重要なメトリクスを獲得し、分散ワークフローの信頼性とレイテンシを継続的に監視できるようになりました。

レジリエンスのための構築

純粋なイベント処理に依存する際の注意点は、Inngestが関数実行を効率的にキューに入れる一方で、イベント自体は従来のメッセージングブローカーの意味で内部的にキューに入れられないことです。明示的なイベントキューがないことは、取り込みエンドポイントが過負荷になった場合の潜在的な競合状態やドロップされたイベントにより、高トラフィックシナリオで問題になる可能性があります。

これに対処し、厳格なイベント耐久性を強制するために、バッファとして専用のキューイングシステムを実装しました。

既存のAWSインフラストラクチャを考慮して、AWS Simple Queue System (SQS)が選択されたシステムでした(ただし、堅牢なキューイングシステムであれば何でも可能です)。私たちは2つのキューシステムを設計しました:メインキューデッドレターキュー (DLQ)です。

私たちは、メインキューから直接メッセージを消費するように特別に構成されたElastic Beanstalk(EB)ワーカー環境を確立しました。メインキュー内のメッセージがEBワーカーによって一定回数処理に失敗すると、メインキューは自動的に失敗したメッセージを専用のDLQに移動します。これにより、イベントがトリガーに失敗したり、Inngestによって取得されなかったりしても、永続的に失われることがないようにします。このワーカー環境は標準のEBウェブサーバー環境とは異なり、その唯一の責任はメッセージの消費と処理(この場合、消費されたメッセージをInngest APIエンドポイントに転送すること)です。

制限と仕様の理解

エンタープライズスケールのインフラストラクチャを構築する際の控えめでありながら関連性の高い部分は、それがリソースを消費し、長時間実行されることです。マイクロサービスアーキテクチャはサービスごとのスケーラビリティを提供します。ストレージ、RAM、リソースのタイムアウトが重要になります。例えば、AWSインスタンスタイプの仕様は、t3.microからt3.smallへと急速に移行し、現在はt3.mediumに固定されています。長時間実行されるCPU集約型のバックグラウンドジョブでは、小さなインスタンスによる水平スケーリングは失敗します。なぜなら、ボトルネックは単一のジョブを処理するのにかかる時間であり、キューに入る新しいジョブの量ではないからです。

トランスコーディングや埋め込みなどのジョブ関数は、通常CPU制約メモリ制約があります。CPU制約は持続的で強力なCPU使用率が必要なため、メモリ制約は大きなモデルをロードしたり、大きなファイルやペイロードを効率的に処理したりするために大量のRAMが必要なことが多いためです。

最終的に、SQSの耐久性とEBワーカー環境の制御された実行をInngest APIの直接上流に配置するこの拡張アーキテクチャは、不可欠なレジリエンスを提供しました。私たちは厳格なイベント所有権を達成し、トラフィックスパイク中の競合状態を排除し、不揮発性のデッドレターメカニズムを獲得しました。ワークフローオーケストレーションとデバッグ機能のためにInngestを活用しながら、最大のメッセージスループットと耐久性のためにAWSプリミティブに依存しました。結果として得られたシステムはスケーラブルであるだけでなく、高度に監査可能であり、複雑で長時間実行するバックエンドジョブを安全で観測可能で障害に強いマイクロステップに変換することに成功しました。


Building Spotify for Sermons.はもともとMediumのCoinmonksで公開され、人々はこの記事をハイライトして返信することで会話を続けています。

免責事項:このサイトに転載されている記事は、公開プラットフォームから引用されており、情報提供のみを目的としています。MEXCの見解を必ずしも反映するものではありません。すべての権利は原著者に帰属します。コンテンツが第三者の権利を侵害していると思われる場合は、削除を依頼するために [email protected] までご連絡ください。MEXCは、コンテンツの正確性、完全性、適時性について一切保証せず、提供された情報に基づいて行われたいかなる行動についても責任を負いません。本コンテンツは、財務、法律、その他の専門的なアドバイスを構成するものではなく、MEXCによる推奨または支持と見なされるべきではありません。