順序番号が付いている理由
一言でいうと
相場フィードは、順序も到着も保証しない伝送の上に載っており、そのためメッセージごとに順序番号が付いています。欠けたものと遅れたものを区別する唯一の手段が、その数字です。
なぜ必要なのか
相場データは、ほかのデータと要求条件が正反対です。数ミリ秒遅れた正確な値より、今届いた値のほうが役に立ち、1銘柄の気配値は1秒に何十回も変わります。そのため、取引所の相場は、たいていTCPではなくUDPマルチキャストで出ていきます。再送も順序保証もない代わりに、1回送れば購読者全員が同時に受け取ります。
保証がないということは、2つのことが実際に起きるという意味です。
유실 보낸 메시지가 우리에게 오지 않는다
재정렬 나중에 보낸 메시지가 먼저 도착한다
2つの韓国語コメントは、順に、欠落は送ったメッセージがこちらに届かないこと、順序の入れ替わりはあとで送ったメッセージが先に届くことを述べています。
受け取る側から見ると、この2つはまったく同じに見えます。順序番号100の次に102が来たとき、101がなくなったのか、遅れて来る途中なのかは、その瞬間にはわかりません。そのため、到着順に順序番号を数える方式は、常に水増しされた数が出ます。実際のデータで、到着順基準のギャップが1,138件なのに、本当に欠けたのは204件である場合を、このコースのラボで自分で見ることになります。
この違いがなぜ問題かというと、ギャップを見つけたフィードハンドラーがすることが、再送要求だからです。1,138回要求すると、その要求トラフィックが回線を食い、回線が詰まると遅延がさらに大きくなり、遅延が大きくなると順序の入れ替わりがさらに増え、入れ替わりが増えるとギャップがさらに多く見えます。自分で作った問題を、自分で大きくします。実際に、寄り付き直後にこのフィードバックに陥って、フィードが数分間麻痺する事故が繰り返されます。
どう動くのか
ギャップは、順序番号の集合で判定します。届いた順序番号を集めておき、期待する範囲の中で一度も来なかったものだけを、欠けたものとみなします。遅れて来たものは集合に入ってくるので、自然に除外されます。判定には待つ時間が付きます。通常は数十ミリ秒待ってもなければ、そのときに欠けたものとして確定します。
欠けたものを埋める道は3つです。
A/B 중재 거래소가 같은 내용을 두 채널로 따로 보낸다.
한쪽 구멍은 대개 다른 쪽에 있다. 요청 없이 메워진다.
재전송 요청 빠진 순번을 콕 집어 다시 달라고 한다.
거래소 재전송 버퍼를 넘어가면 못 받는다.
스냅샷 복구 현재 호가창 전체를 다시 받는다.
무겁지만 한 번에 끝난다. 구멍이 길면 이쪽이 싸다.
3つの韓国語の説明は、順に、A/B調停は、取引所が同じ内容を2つのチャネルで別々に送り、一方の穴はたいてい他方にあるので、要求なしで埋まること、再送要求は、欠けた順序番号をピンポイントでもう一度求めるもので、取引所の再送バッファを超えると受け取れないこと、スナップショット復旧は、現在の板全体を受け取り直すもので、重いが一度で終わり、穴が長いときはこちらが安いことを述べています。
順序がそのままコストの順序です。A/B調停を先に試さず、いきなり再送を要求する実装がよくありますが、ラボのデータでは、Aチャネルの穴204個のうち180個は、Bチャネルを見るだけで埋まります。要求自体が不要なものが88%です。
重複も正常です。A/Bの2つのチャネルを両方受け取れば、同じメッセージが2回来ます。再送を受ければ、また来ます。順序番号で除外しなければ、約定量の集計が2倍になり、その値で売買代金のランキングを作れば、ランキングがまるごと入れ替わります。
遅延は、平均ではなくテールで見ます。ラボのデータで、全体の平均は10,071マイクロ秒なのに、中央値は1,049マイクロ秒です。平均が中央値の10倍だということは、ごく一部の非常に遅い件が平均を引っ張ったという意味で、その一部がまさに事故の区間です。平均を監視指標にしておくと、この事故は、指標上は何事もなかったことになります。
パーセンタイルは、計算方式まで確定する必要があります。最近接順位の方式と線形補間の方式は、同じデータで異なる値を出します。こちらのダッシュボードと取引所のSLA文書が異なる定義を使っていると、同じ回線について、一方は違反だと言い、他方は違反ではないと言います。実際に、そのような会議が開かれます。
現場での姿
ある現場で、「寄り付きの序盤にだけ気配値が跳ねる」という報告がありました。遅延ダッシュボードは平均だけを描いており、寄り付き直後も12ms程度で穏やかでした。キャプチャを取って分単位に分割してみると、09:00の1分間と09:08の1分間の2つの区間のp99が380msを超えており、その2つの区間に、10ms超過の件の95%が集中していました。残りの9つの区間は、p99が1.6msでした。1つの平均が、2つの世界を混ぜて、1つの数字にしていたのです。
遅延が300msを超えるということは、こちらが見ている気配値がすでに過ぎ去った値だという意味です。その値で指値を計算して注文を出すと、約定しないか、逆に不利な値で約定します。p99が310msなら、100件に1件は、0.3秒遅れた絵を見て注文したということです。これが、相場の品質がそのままお金である理由です。
順序番号が途切れたとき実際にすること
相場フィードで順序番号(sequence)が途切れることは、例外的な状況ではなく日常です。そのため、途切れをどう扱うかが、システムの品質を決めます。
まず、途切れを確実に検知します。受け取った順序番号が期待より大きければ、その間が空いています。小さければ、重複か、順序が入れ替わったものです。2つは別々に扱う必要があります。
expected = last + 1
if seq == expected: 정상 처리
elif seq > expected: 갭 — 복구 시작
else: 중복 — 버린다(이미 반영했다)
3つの韓国語コメントは、順に、正常処理、ギャップなので復旧を開始、重複なのですでに反映済みとして捨てる、という意味です。
ギャップが生じたら、その銘柄の状態を「疑わしい」に変えます。処理を続けると、間違った板の上で計算が回ります。何の値も出さないことより悪いのは、間違った値を正常であるかのように出すことです。
復旧の経路は、3つのうちの1つです。再送要求(replay)、スナップショットの再受信、そして補助フィードへの切り替え。どれを使うにしても、復旧している間に入ってくるリアルタイムメッセージを捨てず、キューに溜めておき、スナップショットを適用したあと、そのキューを順序番号の順に流します。この順序を守らないと、復旧直後にまたギャップが出ます。
遅延を測る時計を1つに揃えます。取引所が刻んだ時刻とこちらが受け取った時刻の差が遅延ですが、2つの時計がずれると、負の数が出たり、遅延が誇張されたりします。PTPやNTPで合わせ、2つの時刻をどちらも保存して、あとで補正できるようにします。
急増したときに何を捨てるかを、あらかじめ決めておきます。1秒あたりのメッセージが処理能力を超えると、キューが育ちます。このとき、すべてを処理しようとすると、遅延が大きくなり続け、結局、非常に古い値を最新であるかのように出すことになります。気配値は最新だけが意味を持つので、途中の値を飛ばすこと(conflation)が正しく、約定は1件も捨ててはいけないので、別の経路で処理します。
指標を2つ置くだけでも、大きく違います。ギャップの発生回数と、処理遅延のテール値。平均遅延は、急増を示しません。
次のラボですること
12,000個の順序番号のA/B二重化フィードのキャプチャを自分で作り、到着順で数えたギャップと、順序番号の集合で数えたギャップが、どれだけ違うかを確認します。Bチャネルで埋められるものと本当の穴を分け、残った穴を再送とスナップショットに振り分けます。遅延は最近接順位のパーセンタイルで測り、分単位に分割して、テールが集中した区間を指摘します。