ピーク時に計測パイプラインがイベントを失い、アトリビューションの数字がビッダーのログと決して合わない。これは一つの症状の裏にある二つの障害である。誰も数えていない継ぎ目で、そもそも届かなかったイベント。そして届いたが、別の規則で数えられたイベントだ。ここでは、継ぎ目、キー、遅延ウィンドウ、そして突合の橋をどう作るかを扱う。
ピーク時にイベントを失い、ビッダーのログと決して突き合わないイベントパイプラインは、一つの症状の裏にある二つの障害でありうる。一つはトランスポートだ。生成されたのに、誰も数えていない継ぎ目でどこにも着かなかったイベントである。もう一つは定義の問題だ。着地して数えられたが、オークションの記録とは別の規則で数えられたイベントである。
よくある枠組み ― パイプラインがイベントを落とすのだから、パイプラインを作り直せ ― で直るのは、多くても二つのうちの一つである。以下では両者を切り分け、突合が何を生むのかを述べる。それは一致ではない。以下で引く既定値は、トランスポート側が Kafka、ストレージ側が ClickHouse のものである。
短い答えは構造にある。オークションごとに一つの識別子を端から端まで運ぶこと、すべてのホップの両側にカウンタを置くこと、そしてすでに閉じたウィンドウに対して突合すること。amBrain が公に裏付けられるのは AdTech の仕事である。DSP 開発、リアルタイムビディングのプラットフォーム、アドエクスチェンジのエンジニアリングだ。どの RTB スタックでも、入札・落札・インプレッションの記録が出てくるのはその側である。以下のパイプラインは、私たちの事例からではなく、問題の仕組みから記述している。
一つ目の障害は損失である。イベントは生成されたのに、特定のホップで、特定の理由で届かなかった。ページから出て行かなかったビーコン、デプロイの途中で再起動したエッジ、いっぱいになったプロデューサのバッファ、処理の前にオフセットをコミットしたコンシューマである。
二つ目は、そもそも障害ではない。計測側が数えるのはクライアント起点のイベントであり、ビッダーが記録するのはサーバー側のオークション結果である。一方は落札であり、もう一方はその落札に何が起きたかの観測である。MRC の計測ガイドラインは、プリフェッチ、プリレンダー、自動更新を、それぞれ検出して開示すべき別個の事象として扱う。これは数え方の規則であって、トランスポートの障害ではない。二つを見分けるのにかかるのは、クエリ一本といくらかの忍耐である。
その曲線が無いあいだ、議論の双方は意見にすぎない。曲線ができれば、その形が、この記事のどちらの半分が当てはまるかを決める。そして二つの半分は排他ではない。
広告イベントが生成されたあと、静かに存在しなくなる場所は七つある。加えて、保証に見えて保証ではない設定が一つある。
この規則はエンジニアリングの規則ではなく、レポーティングの規則である。損失は名前の付いた継ぎ目に帰属させるか、まったく帰属させないかのどちらかだ。静かな継ぎ目を見えるようにしておく警報は二つ。保持期間に対して時間で測ったコンシューマラグと、飛ばす代わりに失敗するリセット方針である。
重複排除キーは、宛先側で都合よく選ぶ主キーではない。すべてのリトライより上流で、オークションの時点またはイベント生成の時点で割り当てられるものであり、受け手が勝手に作ることはない。到着タイムスタンプは含めない。リトライは新しい到着時刻を持ち、新しいキーになってしまうからだ。
キーはすべての試行で同一でなければならず、そこから構成要素が決まる。エクスチェンジまたはシート、オークション識別子、インプレッション識別子、そしてイベント種別である。トピックはそのキーでパーティション分割し、リトライが同じ場所に落ち、キーごとの順序が保たれるようにする。この指示はトランスポートに対してのみのものだ。同じ言葉をカラムナストアに当てはめると、イベントごとに一つのパーティションができ、ブロックあたりの上限で挿入が死ぬ。ストレージは時刻でパーティション分割し、キーで並べる。
だから機能する形は、冪等なキーを伴う at-least-once のトランスポートである。書き込み時の重複排除はストレージの請求を正気に保ち、読み取り時の重複排除が数字を正しくする。マージが異なるパーティションのパートを結合することはないので、次のパーティションに落ちた重複は、クエリが尋ねたときにだけ解決される。
過負荷のとき、システムの選択肢は三つある。送り手を遅くするか、カウンタを付けて捨てるか、静かに失うかだ。受け入れられないのは三つ目だけであり、それは、この問いを一度も投げられなかったコードの既定の振る舞いである。ピーク時の損失とは、メモリが尽きるまで伸びたキューか、永続化の前に返した確認応答のことだ。
ラベル付きカウンタを伴う破棄は、あとで突き合わせられる既知の量である。カウンタのない破棄は、失われたデータではなく、失われた数字である。
OpenRTB の実装ガイダンスはそれを直接述べている。広告リクエストからオークションを経て描画と課金に至る一連の流れは、本質的にトランザクショナルではない。二つの数のあいだには、当事者が多すぎるのだ。
遅延は例外ではなく想定内である。入札リクエストはインプレッションの有効期限を、入札はビッダーが許容する遅延を運べる。同じガイダンスは目安も示しており、web では1分程度から、キャッシュされたアプリ内フォーマットやスティッチされた動画ではそれよりはるかに長い。
どちらのフィールドも契約ではない。ガイダンスははっきり述べている。ビッダーが宣言した有効期限より遅れて届いた課金通知でも、なお課金対象になりうる ― プロトコルが課すことではなく、ビッダーとエクスチェンジのあいだの方針の話である。
ウィンドウの後に届くイベントは、パイプラインの欠陥ではなく、媒体の性質である。実際の選択肢は一つだけだ。ウィンドウが開いているあいだに数字を公の場で動かすか、それとも後から人知れず動かすかである。
エクスチェンジが通知や計測用の URL に埋め込む識別子は、入札リクエストのオークション識別子、インプレッション識別子、そしてビッダーが発行していれば入札識別子である。三つとも、それ単独ではキーにならない。
仕様はオークション識別子を、グローバルに一意ではなく、エクスチェンジ内で一意だとしている。二つのエクスチェンジが同じ日に同じ文字列を渡してくることはありうる。インプレッション識別子が一意なのは自分の入札リクエストの内側だけであり、多くの場合は文字どおり 1 である。入札識別子は任意である。
持ちこたえるキーは複合キーである。取引したエクスチェンジまたはシート、オークション識別子、そしてインプレッション識別子だ。オークションの時点でビッダー側が発行し、それより短いものはキーではなく接頭辞として扱う。
ビーコンにそれらのマクロが無ければ、イベント単位の突合は不可能であり、残るのは時刻・配置面・クリエイティブによる照合だけになる。代わりに作れるのは、六つの数からなる橋である。各段は、一つ上の段と食い違う理由を名指しする。
目指すのは段階間の比率が安定していることであり、説明のつかない動きが警報である。一つのシステムの内側では、ホップごとの比率は1であるべきで、そこからの逸脱が信号になる。オークションから計測へまたぐ境界では、1のほうが疑わしい値である。
同じカウンタを比として読むと、問いは算術になる。送信に対する受理、受理に対する生成、生成に対する消費、消費に対する挿入である。一枚のグラフに四つの比を並べれば、誰かがログを開く前にイベントの行き先がわかる。さらに送信元とパーティションごとにプロデューサ側のシーケンス番号を付ければ、欠落は疑いではなく証拠になる。
橋が答えず、契約が答える問いが一つある。これらの数のどれで支払うのか、である。売り手は自分の課金対象イベントで収益を計上し、買い手は自分の側で配信ペースを決める。ガイダンスは、乖離が続く場合を当事者間のサポート上の協議として扱う。
どの数を消化額の正本とするか、そしてどれだけの乖離でレポートの注記がエクスチェンジへのチケットに変わるかを、あらかじめ決めておく。ここまでの作業で手に入るのは、損失の帰属、正直な重複、そして一行ずつ説明できる突合である。手に入らないものは次のとおりだ。
それらの開示項目に答えられるパイプラインには、整合性の裏付けがある。答えられないパイプラインにあるのは意見であり、四半期の終わりに揉めるのは意見のほうだ。
設計で備える価値がある障害は、調査の口火を切る「消えた1時間」ではない。静かなほうである。カウンタ無しで捨てる継ぎ目、永続化される前に確認応答を返した挿入、そしてまだ開いているウィンドウに対する突合だ。
amBrain が公に裏付けられること:amBrain はアルメニア・エレバンのソフトウェアエンジニアリング企業であり、低 latency のトレーディングプラットフォーム、matching engine、リアルタイムビディングのシステムを Rust で作っている。amBrain は2019年からソフトウェアを作ってきた。計測パイプラインの作り直しは、ここで説明している仕事ではない。数字が合わなくなっているのがビッダーとエクスチェンジの側 ― 入札・落札・インプレッションの記録そのもの ― であれば、それが価値のある話であり、作り直しではなく収束曲線から始まる。
現在のアーキテクチャと気になっている障害シナリオをお持ちください。30 分で一緒に確認します。