- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理今ここ
- DB保存
線を引くと何が消えるかを見てきました。今回は逆に、線を引いても消えずに越えてくるものを見ます。ここでの「境界」は、変更の影響を閉じ込めるつもりで引いた線です。
まず結論
インタフェースが固定するのは、名前・引数・戻り値の形までです。相手がどれだけ速いかは書かれていません。だから呼ぶ側を1文字も変えなくても、線の向こうが遅くなるだけで応答が変わります。
TCPとIPの規格には、層をまたいで渡る値が明記されています。しかし自分たちが引いた線には、そういう文書がありません。そこが今回の焦点です。
呼ぶ側のコードを変えず、依存先の応答だけを 50ms から 1000ms に遅くしました。呼ぶ側に同時6本を投げたとき、最後に返る要求はどうなるでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
インタフェースが固定していないもの
まず語を切ります。ここでの「壁」は分散編の壁時計とは無関係で、越えられない仕切りという比喩です。
前回まで、線を引くと何が消えるかを見てきました。今回は逆に、線を引いても消えず、越えてくるものを見ます。まず、インタフェースを決めたときに固定されたものを数えます。
- 名前(どの手続きを呼ぶか)
- 引数(何を渡すか)
- 戻り値の形(何が返ってくるか)
ここまでです。「どれくらいで返るか」は、どこにも書かれていません。 型定義にも OpenAPI にも関数のシグネチャにも、その欄がありません。書く場所が無いのだから、相手の速さが変わっても定義書は変わらない。境界は形だけを固定して、それ以外を素通りさせます。
規格は、何が越えるかを書いている
第2期で、TCPのチェックサムの計算範囲にIPアドレスが入っていることを見ました。原文(RFC 9293 §3.1)はこう書いています。
The checksum also covers a pseudo-header (Figure 2) conceptually prefixed to the TCP header. ... Including the pseudo-header in the checksum gives the TCP connection protection against misrouted segments. This information is carried in IP headers and is transferred across the TCP/network interface in the arguments or results of calls by the TCP implementation on the IP layer.
最後の一文です。規格自身が「その情報は TCP とネットワークのインタフェースを越えて渡される」と書いています。 実装の都合ではなく規定として書かれているわけです。しかも同じ §3.1 に The TCP checksum is never optional. とあり、送信側は MUST generate、受信側は MUST check です。
RFC 9293 は2022年8月の Standards Track で、STD 7、RFC 793 を廃止しています。§3.7.1 には TCP obtains MMS_R and MMS_S from the IP layer ともあり、上の層が下の層から値を取得すると明記されています。§3.4.1 の ISN 生成式は SHOULD として ISN = M + F(localip, localport, remoteip, remoteport, secretkey) を示します。
規格で分けられた層ですら、越えて渡る値の一覧が文書に書いてあるのです。
TCPのチェックサムがIPアドレスを計算範囲に含むことについて、RFC 9293 §3.1 が書いているのはどれですか。
人が引いた線には、その文書が無い
第2期では「規格に定められた層でさえ完全には独立していない」ことを確かめました。今回はその先を問います。自分たちがモジュールやサービスの間に引いた線には、何が越えるかを書いた紙がありますか。
RFC 9293 を読めば、チェックサム・MSS・ISN の3件が越えることは節番号つきで読み取れます。しかし「注文サービス」と「在庫サービス」の間の線に、同じものを書いた文書は普通ありません。インタフェース定義は形しか書けないからです。
- ・どちらも遮断は完全ではない。違うのは、何が越えるかを読める文書の有無
- ・本教材で実測できたのは速さだけ。時刻は分散編『順序は与えられていない — 壁時計と、因果だけが分かる関係』が扱う
呼ぶ側を1文字も変えずに、応答の桁が変わる
書いた紙が無いなら、動かして見ます。
環境: Docker Engine 28.4.0(Docker Desktop) / linux/arm64 / kernel 6.10.14-linuxkit / Node v24.21.0 / curl 8.22.0。ホストは Apple Silicon の macOS。すべて 127.0.0.1 の自前サーバで、各1回の計測です。
作ったのは2つ。依存先は常に {"ok":true} を返し、環境変数で遅延だけを変えます。呼ぶ側は依存先を1回呼んで返すだけで、同時に張れる接続を2本に制限してあります(new http.Agent({ maxSockets: 2 }))。
呼ぶ側のコードは A と B で1文字も違いません。
########## A 依存先が速い(遅延 50ms) ##########自己検査(依存先を直接1本): 0.065765sreq6 status=200 total=0.073756sreq3 status=200 total=0.076358sreq2 status=200 total=0.129318sreq1 status=200 total=0.130875sreq4 status=200 total=0.181281sreq5 status=200 total=0.181747s########## B 依存先が遅い(遅延 1000ms) ##########自己検査(依存先を直接1本): 1.018296sreq2 status=200 total=1.023848sreq5 status=200 total=1.026023sreq6 status=200 total=2.036487sreq1 status=200 total=2.037469sreq3 status=200 total=3.041255sreq4 status=200 total=3.041881s
A も B も、6本が2本ずつ3つの波に分かれています。 構造は同じで、違うのは波の間隔だけ。その間隔は依存先の遅延です。
最後に返った要求は 0.18秒から 3.04秒。呼ぶ側のコードも、受け取った要求も、返した JSON も変わっていません。書けるのは「桁が変わった」「波の間隔が依存先の遅延になった」まで。各1回の計測なので、倍率を法則としては扱えません。
本教材の実測で、依存先を遅くしたときに呼ぶ側の応答が伸びた理由として、書ける範囲はどれですか。
到着を増やした場合(第13期で見た)
1件あたりの仕事は増やさず、単位時間に届く要求の数だけを増やします。自分の側が混んで応答が伸びます。第13期『余力は、待ち時間の購入代金』の構造です。
項目 変えたか届く要求の数 変えた1件あたりの処理 変えない線の向こうの速さ 変えない
依存先を遅くした場合(今回の実測)
届く要求は A も B も6本で固定し、依存先の応答時間だけを 50ms から 1000ms にしました。呼ぶ側のコードは同一。最後に返った要求は 0.18秒から 3.04秒。各1回の計測です。
項目 変えたか届く要求の数 変えない(6本)1件あたりの処理 変えない線の向こうの速さ 変えた
どちらも「仕事は増えていないのに遅くなった」という見え方をします。切り分けるには、何を確かめればよいでしょうか。
グラフの形ではなく、「自分は今回、何を変えていないか」を先に数えてみてください。
この章のまとめ
- インタフェースが固定するのは名前・引数・戻り値の形まで。相手の速さを書く欄が無い。
- RFC 9293 §3.1 は、疑似ヘッダの情報が
transferred across the TCP/network interfaceだと明記。チェックサムはnever optional。 - 規格が引いた層には、越えるものを書いた文書がある。人が引いた線には無い。 これが今回の差分。
- 実測(各1回)では、呼ぶ側を1文字も変えず依存先を 50ms→1000ms にしただけで、最後に返る要求が 0.18秒→3.04秒。上限2本は意図的な設定で、既定は
Infinity。 - 「仕事は増えていないのに遅い」でも、到着を増やした場合(第13期)と依存先を遅くした場合は別。まず何を変えていないかを数える。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
「インタフェースを固定したので、向こう側の実装が変わっても呼ぶ側には影響しない」という説明の評価はどれですか。
用語集(3語)
- 境界(この教材での言い方)
- 変更の影響を内側に閉じ込めるつもりで引いた線。関数・モジュール・サービスのどれでも同じで、形だけを固定して他を素通りさせる。
- 疑似ヘッダ (pseudo header)
- TCPのチェックサム計算のために組み立てる、送信元・宛先IPアドレスを含む仮想的なヘッダ。RFC 9293 §3.1 が規定し、IPv4 で96ビット、IPv6 で320ビット。
- maxSockets
- Node.js の `http.Agent` が、同一の相手に同時に持てるソケット数の上限。公式の既定値は `Infinity` で、超過分は保留の列に入ると規定されている。有限にすると、相手の遅さが呼ぶ側の応答に何倍にも現れる経路になる。