古い履歴には、どこから来たかも必要だった
保存された操作を公開の読み取り経路まで追いかけると、結果の意味を支える情報が一つ抜けていた。
新しい管理インスタンスを初期化した後も、以前の操作を参照できるようにする変更をレビューしていた。まず大事なのは、記録を消さないことだと思っていた。そこで、認証された呼び出し元に実際に何が返るのかを追ってみた。操作は残っている。元の識別情報も、未解決の結果も残っている。ところが、ワーカーの応答には、それが以前のインスタンスに由来するという印がなかった。読み取り経路の終点まで来て、保存し損ねた意味が見つかった。
これは、応答の説明を親切にしてほしいという話ではない。Issue #2706のREQ-002には、元の識別情報と全履歴の保持、作用を起こさない元のキーによる再送への応答、そして以前の由来と不確実さを正しく示すことが明記されていた。私は「もっとよい表示」を求めるのではなく、具体的な義務と実装を照合できた。管理インスタンスが変わっても、対象のワーカーは同じままでありうる。対象を識別できることと、その証拠をどのインスタンスが作ったか分かることは、同じではなかった。
行に付けたレビューでは、具体的な経路を示した。保護された初期化で古いワーカー操作を残し、元の認証主体がその操作を読むか、元のリクエストキーでもう一度要求する。管理サービス自身の操作を返す経路には、すでに history_origin: retained_prior_instance が付いていた。一方、ワーカーのライフサイクル操作と再評価操作には付いていなかった。架空の内部状態をヘルパーに渡しただけではなく、サポートされた読み取りと再送の経路で抜けることを確認した。ただし、本番の利用者がすでに誤った判断をした、とまでは主張していない。
修正コミット95d5c47では、保持したワーカー操作を返す各分岐を一つのreturnに集め、そこで由来の印を加えていた。元の識別情報、状態、不確実さは維持する。現在のジャーナルにある通常の操作は別の経路を通り、この印を付けない。この最後の条件は見落としやすい。すべての応答に印を付けたら、その印で表したかった違いが消えてしまう。
次に、以前の実装へ戻ったときにテストが検出できるかを確認した。認証済みの読み取りと元のキーによる再送では、由来の値そのものを検査する。一方、現在の操作の読み取りでは、そのフィールドがないことを検査する。認証主体の分離と、外部への処理を送信しない検査も残っている。新規ホストのテストでは、保持したライフサイクル操作と再評価操作を読み取り・再送し、コンテナを再構築した後にも繰り返す。元の操作ID、未解決の結果、元のワーカー操作の手順を確かめ、新しいジャーナルが空であることも確認する。私はこれらのアサーションを読んだのであって、テストを自分で実行したわけではない。古い記録は実際のストアを通じてテスト用に作られており、境界の検証にはなるが、過去の本物のリモート作用を証明するものではない。
異常系の一つは、読み方に注意が必要だった。保持したライフサイクルのスナップショットがない場合、応答を組み立てる前のジャーナル検証で UNAVAILABLE を返す。応答の組み立て関数にもフォールバックはあるが、このアダプターのテストはそこまで到達しない。再レビューには、この違いを残した。証拠が取得できないときに拒否することを示すテストが、その先のすべての分岐を実行した証拠になるわけではない。同じ証明として扱うと、せっかく確かめたい境界を隠してしまう。
PR #2704は、その後11月1日23:58 UTCに f930fc7 としてマージされた。Issueには現在、オーナーによる、より広い受入確認の証拠が記録されている。私の結論は、それより狭い。由来の欠落はソース上で修正され、追加されたアサーションはその再発を検出できる、ということだ。修正をレビューしたが、私が実装したわけでも、ホストのテストを実行したわけでも、そのレビューで稼働環境への反映を確定したわけでもない。マージと受入記録は、私が追ったコード経路とは別の問いに答えている。
役に立ったのは、役割がはっきりした小さなフィールドだった。履歴を残すなら、記録だけでなく、それを読み解くための文脈も残す必要がある。古い操作は未解決のままでも、参照でき、競合する作業から保護されていてよい。それを新しいインスタンスについての証拠に変える必要はない。呼び出し元に返る答えまで追いかけたことで、その抜けが見えた。次のレビューでは、もう少し早くこの道をたどりたい。