Dev Study
AWSサービスの内部原理 コース

95. オーケストレーションは指揮者、コレオグラフィは群舞である — 中央の楽譜と疎結合なイベントの使い分け地図

同じ「注文処理」を、中央の指揮者が楽譜を持って各サービスを順に呼ぶやり方と、指揮者を置かず各サービスがイベントを発行して勝手に踊るやり方の2通りに描き分け、どちらをどこで使うのかを図で追っていきます。

① Lambdaを直列に呼び連ねると何が壊れるか

注文作成関数Create order
応答を待つ間も課金(赤)
同期invoke(待つ)
支払処理関数Process payment
これも待機のまま課金(赤)
この直列invokeが4つの問題を生む
請求作成関数Create invoice
いちばん遅い。実際に働いているのはここだけ
コスト待ち時間も二重・三重に課金
エラー処理取り消すか再試行かが複雑化
密結合可用性が最遅の関数に律速
スケーリング3関数の同時実行数が連動
  • 公式は「Create order 関数が Process payment を呼び、それが Create invoice を呼ぶ」構図をアンチパターンとして図示している
  • コスト: 「Create invoice が走る間、他の2関数も待機状態(図で赤)で走っている」——待ち時間そのものが課金される
  • エラー処理: 入れ子の失敗時、Process payment が課金を取り消す(reverse the charge)のか、Create invoice を再試行するのかが複雑化する
  • 密結合: 「ワークフロー全体の可用性は最も遅い関数に律速される」
  • スケーリング: 「3関数の concurrency がすべて等しくなければならず、busyなシステムでは必要以上の同時実行数を使う」
  • 根拠: Lambda dg / concepts-event-driven-architectures「Lambda functions calling Lambda functions」

Lambda関数を同期で呼び連ねるのは公式が名指しするアンチパターン。サーバなら固定費なので「待ち」はタダだが、従量課金のLambdaでは待ち時間そのものが課金され、エラー処理・可用性・同時実行数まで隣の関数と絡み合う。

② 解決策A: 指揮者を1人立てる(オーケストレーション)

Step Functionsステートマシン=指揮者
楽譜=ASL(Amazon States Language)
全ステートの遷移が1本にまとまる
Task: 在庫Lambda等・ロジックのみ
Task: 支払Lambda等・ロジックのみ
Task: 請求Lambda等・ロジックのみ
execution1つの実行=1本の実行履歴
どのステートで止まったかが常に言える
  • 公式はこのアンチパターンの回避策を2つ挙げる——関数間にSQSキューを挟む方法と、Step Functionsを使う方法
  • 後者の核心: 「Step Functions orchestrates the work and robustly handles errors and retries, and the Lambda functions contain only business logic(Step Functionsが仕事を編成しエラーと再試行を頑健に引き受け、Lambda関数はビジネスロジックだけを持つ)」
  • 実行中のワークフローのインスタンスを execution と呼ぶ
  • ワークフローは2種——Standardは exactly-once・最長1年で、実行履歴をStep Functions内に保持し視覚デバッグできる。Expressは at-least-once・最長5分で、実行履歴はログレベルに応じてCloudWatchへ送られる
  • 楽譜にあたる定義言語が Amazon States Language(ASL)
  • 根拠: Lambda dg / concepts-event-driven-architectures、Step Functions dg / welcome(ASLの語は同ガイドのerror-handling等で定義)

オーケストレーションは中央に「楽譜を読む指揮者」を1人立てる方式。各関数はビジネスロジックだけを持ち、順序・エラー・再試行は指揮者に集約される。フロー全体が1枚の図と1本の実行履歴になるので、「どこで止まったか」を一点で言い切れる。

③ 解決策B: 指揮者を置かずイベントで踊る(コレオグラフィ)

注文サービスイベントの生産者
内容でフィルタ・ルーティング
EventBridge / SNSイベントの通り道(pub/sub)
中央の指揮者ではない
在庫関心ある者が勝手に反応
支払関心ある者が勝手に反応
分析関心ある者が勝手に反応
★新消費者既存サービスに触らず追加
生産者は消費者が誰かを知らない
  • 公式の利点: 「Producers of events have no knowledge of event consumers(イベントの生産者は消費者を知らない)」ので、既存の注文処理・支払処理マイクロサービスに影響を与えず新しい消費者を足せる(拡張性)
  • SNS/SQSのようなメッセージングサービスが「弾力的なバッファ(elastic buffer)」として働いてトラフィック増を受け止める
  • EventBridgeはルールに従い、イベントの内容でフィルタ・ルーティングする
  • 根拠: Lambda dg / concepts-event-driven-architectures「Improving scalability and extensibility」

コレオグラフィは指揮者を置かず、各サービスがイベントを発行し、関心のある者が勝手に踊る方式。生産者が消費者を知らないので、既存サービスに触れずに消費者を追加でき、トラフィックにも自然にスケールする——これはメッセージング編で見た pub/sub とファンアウトそのもの。

④ 群舞の代償: 全体の状態が一点で見えない

イベントの流れ複数サービスを次々と流れる
どこで何が起きたか、状態を再現できない
結果整合性eventually consistent
重複処理・トランザクションの扱い・「全体の正確な状態」の判定が難しい
デバッグに必要な3点セット
ログが別々各サービス・各invocationごと
堅牢なログCloudWatch
相関IDTxのIDを各ステップで記録
横断ツールX-Rayで複数invocationを串刺し
  • 公式は代償を明記——多数のイベントが流れる系は「often eventually consistent」で、「determine the exact overall state of a system(システム全体の正確な状態の判定)」が複雑化する
  • エラー発生時に複数サービスの正確な状態を記録・再現することはできず、各サービス・各関数invocationのログは別々
  • デバッグの3点セット: (1)堅牢なログ基盤(CloudWatch)、(2)「every event has a transaction identifier that is logged at each step(すべてのイベントにトランザクション識別子を持たせ各ステップで記録)」、(3)X-Rayのような横断ツールで複数のLambda invocationとサービスのログを串刺しに解析する
  • 根拠: Lambda dg / concepts-event-driven-architectures「Eventual consistency」「Debugging across services and functions」

疎結合の代償は「全体の状態が一点で見えない」こと。②の指揮者なら実行履歴1本で済んだ問いが、ここではログ+相関ID+X-Rayという可観測性編の道具立てを必要とする。中央集権(見通し)と分散自律(拡張性)のトレードオフが、ここに素直に現れる。

⑤ 使い分け地図: 敵ではなく階層で共存する

Txの内側順序・補償・完了の定義が要る所
━ ドメイン(文脈)の境界を越えると ━
オーケストレーションStep Functions
実行履歴1本。止まった所が言える
イベントで疎結合に
越境の通知他チーム・他サービスへの知らせ
両者は排他ではなく階層で重なる
コレオグラフィEventBridge / SNS
生産者は消費者を知らない
越境イベント起動EventBridge→SFN実行を開始(ASYNC)
境界を越えて届いたイベントが、内側の指揮者を呼び起こす
  • 経験則——1つの業務トランザクションの内側(順序・補償・完了の定義が必要)は指揮者に、ドメイン境界を越える通知はイベントで疎結合に。両者は排他ではなく階層で共存する
  • EventBridgeはルールのターゲットとして「Step Functions state machine」を公式にサポートしており(非同期起動)、境界を越えて届いたイベントをきっかけにステートマシンの実行を開始できる
  • 逆向きの統合(Step FunctionsからEventBridgeへイベントを発行する)もwelcomeの統合表に掲載されており、指揮者が踊り手たちへ知らせを出すこともできる
  • 根拠: EventBridge userguide / eb-targets(ターゲット一覧に「Step Functions state machine (ASYNC)」)、Step Functions dg / welcome(サービス統合)

最終回答は「どちらか一方」ではなく「境界で使い分ける」。トランザクションの内側は指揮者、境界を越える通知はイベント。しかも両者は敵ではなく、越境イベントが内側の指揮者を起動するという形で階層に重なる——DynamoDB編以来コースを貫いてきた「中央集権 vs 分散自律」への、この拡張編としての答え。

⑥ 締め: 指揮者の中身は既習の基礎の組み合わせだった

ステートマシンFSM=有限個の状態と遷移
「今どのステートか」を持つ
その記録の保証は
イベントログ実行履歴=追記される遷移の列
イベントソーシング。だから履歴を再生できる
タスクをどう待つか
実行保証Standard=exactly-once / Express=at-least-once
失敗したら
3つの待ち方Request Response / .sync / .waitForTaskToken
同時にやるなら
Retry / Catch再試行 or 代替ステップへ
バックオフ+ジッター
データセット全体なら
Parallel分岐して同時実行し、合流(fork/join)
外部の完了を待つなら
Map各要素に同じ流れを適用(データ並列)
分散トランザクションを束ねると
タスクトークン.waitForTaskTokenで外部の完了を待つ(継続)
Saga各ステップに成功/失敗の枝
失敗時は補償トランザクションで巻き戻す
  • welcomeの用例が地図の各点を裏づける——Retry/Catchで「失敗タスクを再試行、または捕捉して代替ステップを実行」、Parallelで「入力データを並列ステップで処理」、Mapで「データセットの各項目にワークフローを実行、反復は並列」、.waitForTaskTokenで「タスクトークンが返るまで待つ(human in the loop)」
  • バックオフ+ジッターはRetryの公式仕様——BackoffRate(再試行間隔の乗数、デフォルト2.0)と JitterStrategy(FULL/NONE、「再試行をランダム化した遅延区間に分散させ、同時再試行を減らす」)
  • Sagaは prescriptive-guidance が「失敗管理パターン。各ステップに成功/失敗を扱う別ステップを持つ」とStep Functionsの図で示し、「補償トランザクションを設計する複雑なプログラミングモデルを要し、デバッグが難しく、マイクロサービス数とともに複雑さが増す」と明記
  • 実行保証(Standard=exactly-once・最長1年 / Express=at-least-once・最長5分)はwelcomeより
  • 根拠: Step Functions dg / welcome、同 / concepts-error-handling(BackoffRate・JitterStrategy)、prescriptive-guidance / saga-pattern

指揮者の中身を開けてみれば、それは新しい魔法ではなかった——ステートマシン(FSM)、追記されるイベントログ(実行履歴)、再試行、そして冪等性という、このコースで一つずつ積み上げてきた基礎の組み合わせだった。at-least-once(メッセージング編)を頑健にするのが冪等性であり、追記ログ(DynamoDB編)を再生するのがイベントソーシングであり、相関ID(可観測性編)が分散側の見通しを取り戻す。オーケストレータとは、既習の基礎を1枚の楽譜に束ねたものである。

公式ドキュメントで詳しく ↗