Upgrade to Pro — share decks privately, control downloads, hide ads and more …

内製ワークフローエンジンの設計とメルカリでの活用事例

Avatar for sapuri sapuri
April 27, 2026

 内製ワークフローエンジンの設計とメルカリでの活用事例

「Background Job Talk 〜 Temporal 活用と独自実装の舞台裏編〜」の登壇資料です。
https://upsider.connpass.com/event/382902/

マイクロサービスアーキテクチャのような分散システムでは、複数のサービスにまたがる処理のデータ整合性をどう保つか、いわゆる分散トランザクションの扱いが大きな課題となります。メルカリではこの課題をSagaパターンによる結果整合性で解決するために、自社でワークフローエンジンを開発して運用しています。本セッションでは、内製に至った背景と、ワークフローエンジンの具体的な設計についてご紹介します。また、社内での活用事例とあわせて、内製フレームワークの運用における課題と改善のための取り組みについてもお話しします。

Avatar for sapuri

sapuri

April 27, 2026

Other Decks in Programming

Transcript

  1. 7 • 各サービスが独自のDBを持つため、単純なロールバックができない • 起きうる不整合の例: ◦ 決済が失敗したのにメルコインの JPY残高が減っている ◦ 残高は減ったがBTCが加算されない

    ◦ BTCと交換できているのに取引が完了扱いになっていない • Two-Phase Commitは長期間リソースをロックするためサービスの可用性を 下げる可能性がある • → 結果整合性で解決する 分散トランザクション管理
  2. 10 ワークフローエンジンの検討 • GCP Workflows ◦ 各処理をHTTPエンドポイント化する必要がある。ユニットテストが難しい ◦ YAMLではなくGoのコードでワークフローを記述したい •

    Cadence / Temporal ◦ Cloud Spannerに対応していなかった ◦ システムの規模が大きく、運用のために専門家が必要になりそう
  3. 11 • 要件を満たすものがなかったため自社で開発した ◦ Cadence / Temporalのインターフェースの良さを取り入れる ◦ メルペイのPayment Serviceで既に実績があった「DBへの実行状態の永続化

    x インメモリキュー x Workerでの実行管理 」のアーキテクチャを再利用 ◦ Go専用、必要な機能のみに絞って小規模に ▪ 数人の兼務メンテナーで運用できている ワークフローエンジンの検討
  4. 16 • manager.Workflow().Execute() ◦ WorkerがWorkflowStartedイベントをsubscribe ▪ Registryから実行する関数を取得し、reflect.ValueOf(fn).Call(args) で実行 ▪ Engine

    ServerにWorkflowの完了をリクエスト • Workflowの実行結果を保存し、完了状態に遷移 • ChannelにWorkflowCompletedイベントをpublish アーキテクチャ
  5. 18 アーキテクチャ • Manager: SDKのエントリーポイント ◦ アプリケーションは Workflow() / Activity()

    / RegisterWorkflows() などを呼び出す ◦ リクエストに応じてEngine Serverと通信し、Channelへ開始イベントを発行 • Engine Server: Create / Complete / List などのgRPC APIを提供するサーバー ◦ DBにWorkflow / ActivityのI/Oと状態を保存 ◦ 完了済のWorkflow / Activityの結果をそのまま返すので冪等にリトライされる • Channel: Workflow / Activityの状態遷移イベントのハブとなるインメモリキュー
  6. 22 3種類のエラーを定義している エラーハンドリング エラー種別 例 ワークフローエンジンの挙動 Completable Error 失敗として完了させて良い 想定されたエラー

    残高不足 / 利用制限 Workflowを完了 Retryable Error 一時的なエラー 即時リトライ Incompletable Error (default) 一時的なエラー / 予期しないエ ラー Workflowを停止 Recovery Workerが後で リトライ
  7. 23 Completable Error • Completable Errorはクライアント側でErrorMarshalerを実装したエラーと して定義される • 該当しないエラーは未完了として実行停止 →

    Recovery Worker によってリトライされる • 明示的にCompletable Errorを返さない限りWorkflowは完了しない ◦ 異常な状態でWorkflowが完了することがなくなる設計
  8. 26 • 例: 回線開通フロー • Workflow + Child Workflowで同期レスポンスと非同期処理を分離 ◦

    Workflow: 回線開通リクエストを DBに保存 → Child Workflowをfire-and-forgetで起動 → レスポンス返却 ◦ Child Workflow (非同期): 回線開通PubSubイベントを発行 ◦ 失敗時はRecovery WorkerがChildWorkflowを復旧 (PubSub発行まで保証) 事例1: メルカリモバイル
  9. 27 • SpannerではなくPostgreSQLを採用しているプロジェクト • 購買代行パートナーを経由して海外のお客さまが日本のメルカリの商品を購入する • 例: チェックアウト確定フロー ◦ クーポン消費

    → 注文作成 → 購買代行パートナーへ注文連携 → 注文確定通知送信 ◦ 失敗時はSagaによる補償トランザクションでキャンセル • なぜTemporalではなく内製のワークフローエンジンを採用した? ◦ 社内に既にある類似実装や運用基盤を活用したい ◦ メンテナーが社内にいるので直接サポートを受けられる。必要な機能を柔軟に追加できて最適 化しやすい 事例2: メルカリ グローバル EC基盤
  10. 28 • 例: お客さま本人確認フロー ◦ 書類の自動検証 → [審査待ち - 数時間〜数日]

    → 結果に応じて承認 or 拒否 • 人による審査待ちを含むlong-runningワークフロー • Signal (Temporalと同様のインターフェース) で外部からWorkflowに 干渉できる ◦ Workflow中断 → 外部からWorkflowに対して情報を送って再開 ◦ 長期フローを分割せず1本のWorkflowとして表現できる 事例3: eKYC
  11. 32 • フレームワークのドキュメントが充実しているに越したことはないが、 AIは内部の実装をすぐに理解できる • インターネットに情報が無くても昔ほど利用のハードルが高くないかも 最近の運用 課題 対策 コミュニティ情報の不足

    (類似事例・Q&Aが探せない) 社内コードの横断検索 + AI (Sourcegraph MCPなど) でドキュメントや社内実装から類似 事例を発見できる 間違った使い方に気付けない 専用のLinterを提供する LLM Analyzer?
  12. 33 • 内製ワークフローエンジン専用のLinter • 予想される実装のミスパターンごとに専用のAnalyzerを用意している • 静的解析では問題の検出が難しいパターンがある ◦ 例: Activityには引数として非決定的な値

    (実行ごとに変わる値 ) は使えないという制約がある のでこれを検出したい • → Analyzerの中でLLMを使って検出できないか実験してみた Linter
  13. 35 LLM内蔵Linter - 仕組み 1. AST を走査して wm.Activity() を含む関数を収集する 2.

    呼び出し元 (1階層上のcaller) も収集し、引数の由来を追跡する 3. 関数のソースコードをLLMに送信し、非決定的値の有無を分析する (任意のモデルを利用できる) 4. Structured Outputsで結果をパース、pass.Reportf() で診断を報告する