アクティビティの制約
アクティビティは永続境界を越えて IO と副作用を実行します。ワークフローコードのようにはリプレイされず、少なくとも一回実行されます。リトライ、リース期限切れ、再配信により、同じ論理的な処理を複数回観測することがあります。
これは契約の一部です。課金、メール送信、他のシステムへの書き込み、外部リソースの変更を行うアクティビティは、安全に繰り返せる必要があります。
実際の動作
- 一つの論理的なアクティビティ実行に対する既定の冪等識別子は
activity_execution_idです。 - 各リトライには、独自の
activity_attempt_idもあります。 - Worker は外部処理を完了し、リースを失った後に結果を報告することがあります。別の Worker が先に有効な永続結果を確定したため、エンジンが遅れた報告を拒否しても、外部副作用はすでに発生している可能性があります。
- アクティビティは IO、実際の時計、変更可能なプロセス状態を使えます。ワークフローコードは使えません。この境界を明確にしてください。
推奨する冪等パターン
多くの外部 API は Idempotency-Key を受け入れます。リモートサービスが対応する場合、ランタイムが提供する論理的なアクティビティ識別子を使ってください。
- リモートシステムがリトライを同じ論理リクエストとして扱う場合は、
activity_execution_idを使います。 - 同じ処理の個別の試行を区別する必要がある場合にだけ、
activity_attempt_idを使います。
ほかにも次の方法があります。
- 決定的な外部リソース名や自然キーに書き込む。
- 永続識別子をキーにした upsert や重複排除テーブルを使う。
- 二回目の呼び出しが何も変更しないよう、操作を自然に冪等にする。
自然に冪等な操作もあります。同じ動画を二回エンコードしても、最終的には同じ動画になります。同じファイルを二回削除しても、二回目の削除は何も変えません。
操作が本質的に冪等でなくても、重複のほうが安全な場合があります。メールが送信済みか確認できない場合は、通知を失うより重複送信のほうが適切かもしれません。このトレードオフを明確に判断してください。
仮定してはいけないこと
- 一回のアクティビティ試行が、常に一つの Worker だけで実行されるとは限りません。
- リトライは、前の外部副作用が失敗したことを意味しません。
- 遅れた完了報告は、アクティビティが実行されなかったことを意味しません。
- リトライを避けるために副作用をワークフローコードへ移すと、冪等性の問題が決定性の不具合に変わります。