メインコンテンツまでスキップ

水平スケーリング

フロントエンドは自分の状態を持たず(/api/* をプロキシするだけで、リクエストのあいだに何も残しません)、自由にスケールできます。以下はすべてバックエンドの話です。

2 レプリカ以上で動かすための要件​

要件理由
PostgreSQLSQLite はシングルライター、シングルプロセスです。全レプリカが 1 つのデータベースを共有し、レプリカを協調させるのもそのデータベースです
SKILLS_DIR の共有全レプリカが同じエージェントスキルストアをマウントする必要があります(ReadWriteMany)。実行は開始時のスキルのリビジョンに固定され、その実行の次のリクエストはどのレプリカに届くか分かりません
トランザクションプーリングのプロキシを挟まない後述のコネクションプーリングを参照してください
MCP_PROXY_TLS_DIR の共有各レプリカは MCP プロキシに対する自分の身元を示す資材をそこに書き、そこから読みます。プロキシも同じディレクトリを読みます。レプリカごとに別の場所を持つと、プロキシが見たことのない資材を発行することになります
スティッキーセッション不要です。ルーティングのアフィニティに求めたい役割は、後述のアドバイザリロックが果たします

MCP プロキシ自体は別の理屈でスケールします。呼び出しのあいだに持つのはダウンロードのキャッシュだけなので、レプリカ間の協調は一切不要です。MCP_SANDBOX_DIR はレプリカ間で共有しても、それぞれが別に持ってもかまいません。バックエンドのレプリカ数ではなく、同時に走るツール呼び出しの数に合わせて増やしてください。

1 つの会話につき 1 つのドライバー​

1 つの会話への書き込みは、レプリカをまたいでもすでに安全です。セッションサービスが、追記トランザクションの全体にわたって追記先の行に SELECT ... FOR UPDATE を掛けるので、追記は直列化され、会話の状態もイベントの行も失われません。

手当てが要るのは読み取りのほうです。エージェントのランタイムは 1 回の呼び出しのあいだ、メモリ上のセッションを 1 つ抱えます。そのため、そのあいだに別のレプリカが追記したイベントは届かず、実行の残りは欠けた会話をもとに考えることになります。書き込みを直列化してもこれは直りません。直せるのは、1 つのセッションを同時に 1 つのドライバーに限ることだけです。

そこで各エージェント実行(POST /api/v1/workflow-executions/{id}/agent、POST /api/v1/workflows/{id}/agent)は、PostgreSQL のセッションレベルのアドバイザリロックを取り、SSE ストリームの全体にわたって保持します。

同じセッションの 2 つ目の同時実行は、静かにずれていくのではなく、SSE のヘッダーを送る前に拒否されます。異なるセッションどうしが競合することはありません。ロックはあきらめる前に少しだけ待つので、ストリームを中断してすぐ再試行したクライアントが、放棄された実行の後始末中に拒否されることもありません。

人が介在する処理には影響しません。フロントエンドのツール呼び出しは実行を終わらせてストリームを閉じ、ロックを解放します。承認は新しいターンとして再開し、これはどのレプリカで動いてもかまいません。

各レプリカはセッションランナーも動かします。これは、ブラウザが動かさないターン、つまり実行のキックオフと承認後の再開を行うものです。同じロックを待たずに取りにいくので、すでに別のレプリカが動かしているセッションはそのレプリカに任せます。停止したレプリカがやりかけにしたターンも引き継ぎます。1 つのレプリカが同時に動かすターンの数は SESSION_RUNNER_CONCURRENCY で決まります(設定リファレンス)。

コネクションプーリング​

このロックはトランザクションレベルではなくセッションレベルなので、アプリと PostgreSQL のあいだにトランザクションプーリングのプロキシ(transaction モードの PgBouncer や、大半のサーバーレス PostgreSQL のプーラー)を挟んではいけません。ロックがステートメントをまたいで残らないからです。セッションレベルのプーリング、または直結が必要です。

通知メール​

送信メールのワーカーについて、こちらで調整することはありません。API プロセスの中で動いていても専用プロセスであっても、email-queue アドバイザリロックがデプロイ全体でちょうど 1 つの送信者を選ぶので、レプリカが二重に送ることはなく、設定したレート制限も正確なままです。ワーカーのレプリカを増やして得られるのはフェイルオーバーで、スループットではありません。プロセス構成を参照してください。