TT Lab
はじめる
学ぶ 学習パス コース

FDE総合演習:倉庫に同じ注文が3回届いた

倉庫に同じ注文が3回届いた

TT Labで続きを見る

目標

顧客の合成した注文CSVを検証し、架空の倉庫に予約するPython CLIを実装します。 同じファイルの再実行と、応答の消失があっても、予約を重複して作らず、未解決の項目を引き継ぎます。

なぜ重要なのか

API呼び出しの成功と、顧客の業務の完了は違います。応答を受け取れなかったからといって、無条件に再送すると、すでに保存した注文をもう一度作ってしまうおそれがあります。 逆に、再試行をすべて禁止すると、保存の前に失敗した正常な注文を失います。このラボは、入力の契約・冪等キー・永続的な元帳・再試行の上限を、あわせて扱います。 実際の出荷・決済・顧客情報がない、架空のロボット部品の倉庫です。相手側の元帳ファイルを直接読んだり直したりせず、HTTPの契約だけで連携します。

想定所要時間は80分です。デフォルトの60分のセッションが終わる前に、+時間で延長してください(最大180分)。 セッションが終わると、/root/handoffのファイルは消えます。必要なコードは、終了の前に、個人の作業スペースに別に保管してください。

用意するものと実行契約

まず、ブリーフと実行契約を読みます。入力は最大256KiB・100行で、数量は1から5までの1文字です。 保留されていない注文だけを、APIの本文のorder_id・sku・quantityとして用意します。メールは、送信・ログ・報告書から除きます。 評価ツールには、プログラムのパスと、plan/send/chaos/repeat/drift/outageのうちの検査名を渡します。 例: python3 /opt/lab/handoff/evaluate.py /root/handoff/sync.py plan 評価ツールが、隔離された一時的なデータとサーバーを作って終了するので、別のサーバーを先に立ち上げる必要はありません。 評価データでは、注文番号・SKU・数量の一部が変わります。提供されたCSVの結果をハードコードしないでください。

ステップ

  1. /root/handoffを作成し、スターターコードをsync.pyにコピーします。scope.jsonに、業務warehouse-reservation、ビジネスキーorder_id、send_pii=false、max_attempts=4、retryable_statuses=[503]、conflict_policy=hold_order、approval=review_onlyを記録します。JSONのキー名は、workflow・business_key・send_pii・max_attempts・retryable_statuses・conflict_policy・approvalです。
  2. sync.pyに、CSV全体の検証と、計画の報告書を実装します。デフォルトのモードは、HTTPリクエスト0回で、outcomesは空の配列です。同じ注文の不良・衝突は注文全体を保留し、同じ内容は最初の行だけが候補で、残りはduplicatesです。planの検査で確認します。
  3. sync.pyの--sendの分岐を実装します。正常なサーバーで、候補だけをPOSTし、GET /orders/<注文番号>で、ちょうど1つの予約と、内容・IDを確認します。報告書の7つのフィールドと、outcomesの4つのフィールド、終了コード0/2は、実行契約に従います。sendの検査で、実際の元帳と照合します。
  4. sync.pyに、リクエストごとの1秒の制限、同じ注文のキー、503・接続エラーでの最大4回の試行と0.1秒の間隔を実装します。保存前の503と、保存後の応答の消失を、どちらも処理できるように、chaosの検査を通します。4xxは再試行しません。
  5. sync.pyが、次の実行でも同じビジネスキーを使うことを確認します。repeatの検査は、同じ元帳でサーバーを再起動し、同じ入力をもう一度実行します。新しい予約が増えたり、IDが変わったりしたら、実行のたびに変わるキーと、再試行の分岐を直します。
  6. sync.pyが、すでに予約された注文の変更後の数量を、新しいキーで迂回しないように実装します。driftの検査は、2回目の実行で、一部の数量を変えます。409は、POST 1回で止めて、rejected・nullとして残し、既存の予約を維持する必要があります。
  7. sync.pyが、持続する503で、注文ごとにPOSTを4回行ったあとで止まり、unconfirmed・nullを記録することを確認します。outageの検査を実行します。参照で確認できなかった結果も、成功として表示しません。
  8. 現在の実装で、6つの検査をすべて実行して、handoff.jsonを作ります。評価ツールのreceiptモードを使い、実装とサンプルのハッシュ、事例の一覧、review_onlyの決定、未解決の不良の数量・衝突した注文と、実験の限界を残します。最終的な採点は、scope.json・sync.py・handoff.jsonをあわせて読み、実行を再び検証します。

参考

顧客の言葉から作業範囲を固定する

ビジネスキーと、配信イベントを区別してください。衝突は、エンジニアが数量を選ぶ代わりに、注文全体を保留します。

送る前に7行を分類する

csv.DictReaderでファイル全体を読み、order_idごとにまとめてから分類してください。CSVパーサーのデフォルトのフィールドの制限も、入力の上限に合わせてください。7行目を送ったあとで8行目の衝突を見つけたのでは、手遅れです。

正常な予約を参照で確認する

POSTの応答と、GETの元帳のID・SKU・数量を照合してください。保留が残る送信結果は、終了コード2で表します。

保存前の失敗と応答の消失を復旧する

同じorder_idを冪等キーとして維持してください。エラーの種類、試行回数、業務上の確認の有無は、別々のことです。

次の担当者が同じファイルを再実行する

キーに、現在の時刻や実行のUUIDが混ざっていないかを見てください。既存の予約の数だけでなく、IDも同じである必要があります。

変わった数量を新しい注文として隠さない

本文のハッシュをキーにすると、数量が変わったときに新しい予約になります。409は、再試行やキーの交換ではなく、拒否の結果として残してください。

倉庫が不調のままのときに止める

最大の試行回数に達しても、成功の状態に変えないでください。確認されていない予約IDは、nullです。

実行の根拠と未解決の項目を引き継ぐ

receiptモードで、現在のコードを再検証してください。コードを直したなら、ハッシュだけを直さず、検証をもう一度実行します。