第79話の二枚のevent cardを片づけても、机の上には十二枚の白い票が残った。
どれも、viewerからserverへ送った同じ質問だった。
「shutterは変わった?」
返事は十一回、同じversion 42。
そのうち一回だけが、新しいversion 43だった。
イトは500 ms timerを見た。
「順番は守れた。でも、変わったかどうかをずっと聞かないといけない」
live labでは、server側のstateが変わってから150 ms以内にviewerへ見せる必要がある。
空振りの確認は、六秒間に20回以下。
次のsceneが始まるまで、あと六秒だった。
500 msごとでは、変化の直後を逃した
ユイは同じ六秒のtraceを再生した。
networkのone-way delayは20 msに固定する。
serverのstateは、+2,051 msにversion 42から43へ変わった。
poll interval 500 ms
poll requests 12
no-new-version replies 11
state changed +2,051 ms
next request sent +2,500 ms
new state visible +2,540 ms
visible delay 489 ms
+2,000 msに出したrequestの返事は、+2,040 msに戻っていた。
その11 msあとにstateが変わる。
次のrequestまで、viewerには知る方法がなかった。
イト
通信が遅いんじゃない。次に聞くまで待っていたんだ
ユイ
pollingの間隔が、見えるまでの待ち時間に入っています
マコト
では、間隔を短くすれば終わりかな
イトはtimerを500 msから50 msへ回した。
六秒のあいだに、requestは120回。
変化を運んだ返事は一回だけだった。
poll interval 50 ms
poll requests 120
no-new-version replies 119
new state visible +2,090 ms
visible delay 39 ms
150 msの締切には間に合った。
けれど、空振り20回以下という条件を大きく越えた。
速く聞くほど、何も変わっていない返事も増える。
「もっと短く」は、二つの条件を同時には満たさなかった。
三つの連絡方法
イトはtimerの前へ三枚の案を並べた。
A:500 ms pollingを続ける
requestは12回で少ない。
しかし今回のvisible delayは489 ms。150 ms以内を満たさない。
B:50 ms pollingへ縮める
visible delayは39 ms。
しかしrequest 120回、no-new-version 119回になった。
C:最初にWebSocket connectionを一つ開き、変化時にserverから送る
opening handshakeが終わったあとは、clientだけでなくserver側も必要なときにapplication messageを送れる。
変化がない時間に「変わった?」を繰り返さなくてよい。
ただし、切れたconnectionが自動で元どおりになるわけではない。
再接続とstateの取り直しまで、同じ案に含める必要がある。
ユイ
どれを使うかは、欲しい速さだけでなく、変化の頻度でも変わります
マコト
HTTPでも下のconnectionを再利用できる。ここで比べるのは、毎回TCPを張るかではなく、applicationが尋ね続ける形だ
イト
じゃあC。聞く回数を増やさず、変わった側から送れる道を作る
Picoは案を選ばなかった。
timerにも、serverのsend switchにも触れない。
ただ、閉じた票の山と、まだ何も流れていない一本のrailをcyan lightで照らした。
イトが一本のconnectionをOPENにした
イトは50 ms timerを止めた。
viewerとserverのあいだに、secureなwss connectionを一つ開く。
opening handshakeが完了すると、connection stateはOPENになった。
CONNECTING
↓ opening handshake complete
OPEN
これは、HTTPが使えないという意味ではない。
WebSocketのopening handshakeもHTTPから始まる。
違うのは、そのあと一件ごとのpollingへ戻らず、同じconnection上で双方がmessageを送れることだった。
イトはserver側へ一つだけ条件を置いた。
when state.version changes
send current absolute state
serverのstateが+2,051 msにversion 43 CLOSEDへ変わる。
serverは次のpollを待たず、その場でmessageを送った。
+2,071 ms、viewerのstate lockがamberに変わった。
opening handshakes before run 1
poll requests during run 0
no-new-version replies 0
state messages 1
visible delay 20 ms
イト
扉を開けたから速いんじゃない。変化した側が、次の質問を待たずに送ったんだ
ユイ
同じconnectionでviewerからもserverからもmessageを送れます
マコト
WebSocketはrealtimeという目的に使える一つの道具で、どの通信にも自動で最適とは限らない

wssは、誰に送ってよいかまで決めない
イトが開いたのはwssのconnectionだった。
通信路はTLSで保護される。
けれど、鍵の付いた道があるだけで、誰に何を見せてよいかは決まらない。
誰がつないだか。
どのroomへ参加してよいか。
どのmessageを送ってよいか。
認証と権限は、application側で別に確かめる。
今回は一人のtest viewerと一つのshutterだけに閉じた。
chat roomの振り分けは、次のfixtureへ残す。
開いた扉も、切れる
最初の結果を見て、イトはOPEN lampへ完了札を掛けようとした。
その瞬間、labがnetworkを180 msだけ切った。
+3,500 ms、cyan railが暗くなる。
+3,550 ms、切れているあいだにserver stateはversion 44 OPENへ変わった。
viewerはそのmessageを受け取っていない。
+3,680 ms、clientがabnormal closeを観測した。
OPENだったという過去の表示は、いまつながっている証拠にはならなかった。
イト
もう一度OPENにすれば、届かなかった44も勝手に来る?
ユイ
connectionを戻すことと、離れていた間のstateを戻すことは別です
マコト
しかも全clientが切断直後から連打すると、serverへ再接続が集中する
イトは即時連打のreconnect switchを切った。
このfixtureでは250 ms待ち、一回だけ再接続する。
この250 msは一般解ではない。
実運用では、失敗が続くほど待ちを延ばし、開始時刻もずらすなど、同時再接続を避ける方針が要る。
+3,930 ms、イトがreconnectを一回開始。
+3,970 ms、opening handshakeが完了し、connectionは再びOPENになった。
イトはそこで終わらせなかった。
viewerが最後に受け取ったaccepted version 43を、自分の手でserverへ送る。
serverはcurrent snapshotのversion 44 OPENを返した。
+4,010 ms、viewerとserverのstateがそろった。
reconnect attempts 1
client before resync 43 CLOSED
server current 44 OPEN
client after resync 44 OPEN
final state mismatch 0
ready before +4,200 ms yes
WebSocketが直したもの、直していないもの
イトは結果を二列に分けた。
WebSocketが開いたのは、双方が必要なときにmessageを送れるconnectionだった。
変化がないのにrequestを繰り返す必要はなくなった。
一方で、次のことは自動では決まらない。
- どのstateやmessageを送るか
- 誰が受け取ってよいか
- 切断をいつ検知するか
- いつ再接続するか
- 離れていた間のstateをどう取り直すか
- messageが保存・配達・既読のどこまで進んだか
変化をすぐ受け取りたいなら、polling intervalを縮め続ける前に、変化したserver側から送れるconnectionが合うかを比べる。
WebSocketを選んでも、切断後の再接続とstateの取り直しは別に設計する。
接続が戻ってもclient / serverのversionがそろわないなら、application-level resyncまで確認する。
OPENは、applicationの完成を意味しない。
通信できる状態が一つ増えただけだった。
一本のrailへ、宛先の違う札が来た
+4,200 ms。
next live sceneには間に合った。
poll request 0。
no-new-version reply 0。
最初のstate changeは20 msで表示。
切断後も、version 43からcurrent 44を取り直した。
イトはtimerを机の端へ移し、reconnectとresyncの二枚の札をconnectionの横へ残した。
そのとき、開いたrailへ三枚のmessage cardが同時に滑り込んだ。
一枚はイトだけへ。
一枚は四人のroomへ。
もう一枚には、本文とは別の小さなstatus lampがついている。
イトは三枚を受け止めたが、どの箱へ入れるかはまだ決められなかった。
イト
道は開いた。でも、誰に届けて、どこまで届いた印を付ける?
ピコ
次は、connectionの先にあるchatの配達路を見よう
Picoはmessage cardを運ばない。
宛先の違う三枚と、イトの空いた三つのtrayを照らした。
次回、ピコルート第81話。
チャットの仕組みはどこを通る?

