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

取り消せない変更

顧客変更を最後まで引き継ぐ

TT Labで続きを見る

一言でいうと

顧客の変更の調整役は、承認された入力を固定し、実行の応答・DBの観測・報告の保存・別の補償を、互いに異なる結果として管理しなければなりません。

なぜ必要なのか

宇宙のお祭りのキャンセル作業の最終日です。顧客はキャンセルを承認し、注文は実際にcancelledになりました。ところが、担当者の画面にエラーが出ました。報告書を保存するディスクに問題が起きたからです。画面にエラーがあるのだから、キャンセルをもう一度実行すべきでしょうか。元に戻すべきでしょうか。何が失敗したのかを区別できなければ、復旧作業が新たな事故になります。

前の単元では、入力の境界、条件付きの変更、監査、補償、チャンクの再開、スキーマの共存、証拠の報告を、別々に学びました。今回は、それらの関数をもう一度長く書くことが目標ではありません。すでに検証した機能を小さなライブラリとして受け取り、顧客のリクエスト1件を処理する調整役を作ります。どの入力を、誰の承認として受け入れるのか、どの結果のあとに、何をさらに呼んでよいのかが、新しい課題です。

提供されるライブラリがSQLをうまく処理しても、調整役が新しいIDで再試行し続けたり、報告のエラーを理由に自動で補償したりすれば、システム全体は間違っています。部品の試験の合格と、部品をつないだ業務の正確性は、別のことです。総合課題は、その間を検査します。

どう動くのか

1. プレビューと承認は、同じファイルではない

プレビューは、特定の顧客の、明示したIDを読み、revisionと数量を見せます。この観測から、実行リクエストの形を作ることはできますが、approvedはFalseのままにします。観測した値があるというだけで、その値の変更を人が承認したと見なしてはいけません。

学習者が内容を確認して、approvedを実際にTrueに変えたファイルだけを、実行リクエストとして受け取ります。文字列のtrueや整数の1は、承認として扱いません。このフラグは、今回の仮想の業務の明示的な入力の契約であり、電子署名やユーザー認証ではありません。実際の組織では、誰がどんな権限で承認したのかを確認する、別の体制が必要です。JSONファイルを編集できるという事実は、承認者の身元を証明しません。

承認のあとで注文の値が変わっても、以前の承認を、新しい値にこっそり更新しません。顧客がqty=4、revision=9を見て承認したのに、実行の直前にqty=6、revision=10に変えて適用するのは、同じ対象でも別のリクエストです。失敗を減らそうとして、自動でpreviewを呼び直すコードが、かえって承認の境界を崩すことがあります。

2. 実行の応答は、4種類に分ける

提供されるapplyがTrueを返せば、今回の呼び出しが新しい変更を確定しました。Falseは、同じID・同じ承認内容の記録がすでにあるので、再適用しなかったという意味です。これだけでは、現在の行まで望む値だとは保証されません。その後に別の作業者が変更できるからです。

Conflictは、提供される操作が、承認・現在の条件の衝突を拒否した場合です。それ以外の一般の例外は、結果不明として分けます。特に、コミットのあとに応答の伝達に失敗すると、例外を受け取っても、DBの変更は存在しうるのです。調整役は、例外を、失敗の確定に変えたり、無条件の成功に変えたりしません。

もう一度呼ぶ必要が生じても、同じ承認と同じIDを維持しなければならない理由は、ここにあります。今回の調整役は、自動の再試行をせず、実行1回の結果と、その後の読み取りの観測を返します。人や上位のシステムが、次の行動を選ぶ根拠を作るのであり、その選択自体を推測しません。

3. 観測では、IDだけでなく承認の内容も照合する

実行の応答を受け取ったあと、observeで、元の承認・監査・現在の行を確認します。保存されたchange_idが同じでも、tenantやtargetsが違えば、別の承認です。個数だけが同じ一覧、同じIDの別のrevisionや数量は許可しません。別の承認に該当する報告書を、現在のリクエストの結果のように発行することも防ぎます。

観測がない場合や、DBの参照が失敗した場合は、unknownです。同じ承認だという照合に失敗すれば、mismatchです。参照に成功して、同じ承認なら、前の単元のcomplete・incomplete・holdの判定を、そのまま維持します。実行の応答がreplayedだからといって、holdをcompleteに変えません。

実行と観測が、1つのトランザクションなのではありません。この構造は、確定した作業を、あとで調査する流れです。PostgreSQLの分離レベルを読むときは、1つのトランザクションの一貫性と、別々の呼び出しの間の現在の状態を区別してください。調整役が実行を終えたという事実が、その後の書き込みを止めてくれるわけではありません。

4. 報告の失敗は、別の作業の失敗

実行と観測が終わったあとで、報告のファイルを保存します。結果は、savedまたはfailedとして、別に持ちます。savedなら、返されたSHA-256も残しますが、それが作成者の認証だという意味ではありません。DBの観測がcompleteでも、報告の保存はfailedのことがあり、報告のファイルの保存が成功しても、その中の業務上の判定はholdのことがあります。

報告だけを作り直すreportの動作には、applyやundoの呼び出しがあってはいけません。すでにキャンセルされた注文を、また変更したり、再びpendingにしたりする理由はありません。報告のエラーのメッセージに、復旧という言葉が入っているからといって、業務上の補償の権限が生まれるわけでもありません。

観測と報告書の収集も、別々の呼び出しです。その間にその後の変更が起きると、先に返したobservedと、あとでファイルに入った判定が、違うことがあります。observedは先の観測の結果で、report=savedは、ファイルを保存できたかどうかです。この2つをあわせて、「現在のシステム全体が正常で、報告書も同じ時点だ」と主張しません。顧客に渡す最終的な内容は、報告のファイルの観測時刻・範囲とあわせて読む必要があります。

5. 補償は、自動のエラー処理ではない

undoの動作には、別のundo_id、元のchange_id、空白だけではない理由、明示的な承認が必要です。元の監査を検証し、現在の行が、元の変更の直後の顧客・数量・状態・revisionと合っているときだけ、条件付きの補償を実行します。途中の1行が変わっていたなら、前の行の復元まで、あわせてロールバックします。

補償は、revisionを過去の値に下げません。pendingに戻っても、新しい変更なので、バージョンが増え、別の監査が残ります。元の承認と元の監査は、削除しません。エラーの痕跡を消すことと、エラーの影響を復旧することは、別の作業です。

補償でも、一般の例外はuncertainです。補償の結果が不明確なのに、別のundo_idを作って再び実行することはしません。今回の調整役は、補償のあとの自動の後続作業を行いません。補償のレシートと現在の状態を確認する手順は、前の補償の単元のinspect_undoとreconcile_undoにつながります。自動化の範囲と、人が確認すべき境界を、文書に残すことも、引き継ぎの一部です。

現場での姿

エラーの境界を広く取りすぎたとき

tryブロック1つの中にapply、observe、publishを入れて、exceptでundoを呼ぶと、どの段階が失敗したのかが消えます。書き込みのエラーと、読み取りの障害と、ファイルのエラーは、必要な次の行動が違います。このラボは、小さな関数で境界を分け、各段階の結果を合成します。単に例外を捕まえたからといって、安定性が生まれるわけではありません。

psycopgのトランザクション管理は、トランザクションのコンテキストと、接続の寿命を区別します。提供される操作は、各呼び出しが所有する接続を片付けるので、調整役で、リクエスト全体を外側のトランザクションとして包み直すことはしません。そう包むと、下位の関数の完了が、実際のコミットなのか、savepointの解放なのかが、変わってしまうことがあります。ラボは、インストールされているpsycopg 3.2.3で、実際の効果を確認します。

終了コード0を、業務の完了と取り違えたとき

CLIは、リクエストを処理して、構造化された結果を出力できたら、終了コード0を返します。JSONの中のexecution=conflict、observed=hold、report=failedも、正常な処理結果でありうるのです。コード0だけを見て成功の通知を送る上位の自動化は、誤った結論を作るおそれがあります。リクエストの形式やファイルの解釈そのものが失敗したら、コード2と固定のエラーオブジェクトを返します。

エラーの出力に、DSN・パスワード・元の例外の文字列を、そのまま入れないでください。学習資料は仮想のDBですが、実際の運用の習慣は、ここで形づくられます。誤ったUTF-8、重複したキー、途中で切れたJSON、有限でない定数、サイズの制限も扱います。PythonのJSONドキュメントのobject_pairs_hookとparse_constantは、こうした入力をデータとして検査するための道具です。evalで実行してはいけません。

顧客の次の行動を明確にするFDE

Palantir FDSEの求人は、顧客に合わせたソフトウェアと、ステークホルダーとの協業を扱っています。この総合課題は、その要求を、「顧客がどの変更を承認し、いま何を確認し、次に誰が何を決めるのかを説明するツール」と解釈した、独自の学習課題です。特定の会社の内部の手続きや、採用試験を再現したものではありません。

次のラボですること

宇宙のお祭りのキャンセルのリクエストをJSONで受け取り、プレビュー・実行・観測・報告・別の補償へつなぎます。実際のDBで、古い承認の拒否、コミットのあとの応答の消失、報告のファイルの失敗、その後の変更と補償を確認します。最後に、実際のCLIを呼んで、終了コードと業務上の結果を、別々に読みます。正解だけでなく、自動の再承認・自動の補償・偽りの完了を作る誤答も、拒否します。

受講生ごとの仮想のデータと、使い捨てのDBだけを使います。顧客の権限管理、承認の署名、外部への報告の送信、分散トランザクション、サーバーの電源障害への耐久性を、完成させるレッスンではありません。