第81話のserver counterで止まったcardには、本文しかなかった。
南口に着いたよ。
誰が、どのroomへ送ったのか。
新しいmessageなのか、同じmessageのretryなのか。
serverは一つも決められない。
イトはcardの空欄を見て、鉛筆を走らせた。
senderにはIto。
roomにはroom-blue。
時刻には、wall clockの数字。
IDには、思いついた206。
「これなら、全部そろった」
send leverを倒す。
serverはcardをroom historyへappendした。
その直後、senderへ戻るresponse railだけが暗くなった。
保存されたのか、分からない
イトのscreenには、赤い文字が残った。
response not received。
失敗したように見える。
けれどserver counterの奥では、最初のmessageがすでに保存されていた。
イトには見えない。
通知testまで、あと九十秒。
新しいroom eventが一件増えるたび、壁の呼び鈴も一回鳴るfixtureだ。
イトは同じ本文をもう一度cardへ書き、send leverを倒した。
今度はresponseが戻る。
同時に、ユイの会話欄へ同じ文が二つ並んだ。
10:02:14 南口に着いたよ
10:02:16 南口に着いたよ
room events appended 2
notification triggers 2
intended messages 1
ユイ
二回送りたかったんじゃなくて、届いたか分からなかったから、もう一度押したんですよね
イト
本文が同じなら、serverが同じmessageだと気づくと思った
マコト
同じ『了解』を、本当に二回送ることもあるよ
本文だけを比べても、retryと新しいmessageは区別できなかった。
誰が書くfieldなのか
ピコは、二枚のcardをcyan lightで照らした。
二枚とも、senderはItoと書いてある。
ただし、その文字を書いたのはserverではなく、client側のイトだった。
もしclientがsender欄へMakotoと書けば、serverはそれを信じてよいのか。
時刻も同じだった。
clientのclockが五分ずれていたら、その数字をroomの順番へそのまま使うのか。
一枚のmessage eventに必要なfieldがあっても、すべてをclientに決めさせてよいわけではない。
client can propose
room route / transaction key / message type / content
server must establish in this fixture
authenticated sender / authorization / event ID / server time
room routeも、文字列があるだけでは足りない。
第81話で作ったserver-owned membershipと照合し、authenticated senderがそのroomへ送れるかを確かめる。
三つの作り方
イトは、二重になったmessageを消さずにtest boardへ残した。
その横へ、三枚の案を置く。
イト
A。sender、時刻、event IDまで、clientが全部書く。serverは速く受け取れる
イト
B。retryのたびに新しいIDを作る。どのrequestも別物として保存する
イト
C。clientは同じ送信を示すtransaction keyを持つ。serverはsenderをsessionから取り、event IDとserver時刻を自分で付ける
Aでは、clientが別人のsenderや都合のよいserver時刻を書けてしまう。
Bでは、responseだけを失ったretryが新しいeventになる。
Cでは、clientが「これはさっきと同じ送信試行」と伝えられる。
ただし、transaction keyを使えば世界中のmessageが自動で重複しなくなるわけではない。
どのdeviceの、どのendpointへのrequestで同じとみなすかを、application protocolが決める必要がある。
Picoは三つの札に触れなかった。
response railの切れ目と、二重になったroom historyだけを照らしている。
イトがfieldの持ち主を分けた
イトはAのclient writes sender gateを閉じた。
Bのnew ID on every retry drumも止める。
そしてCのcounterを、自分の手で組み直した。
incoming request
authenticated session Ito-device-A
room route room-blue
transaction key tx-206
event type image-message
content caption + attachment reference
server result
authenticated sender Ito
event ID ev-900
server time 10:02:14.280
JSONのobjectで表すなら、fieldはnameとvalueの組になる。
上から書かれた順番に意味を頼らず、roomやcontentのようなnameで役割を読む。
ただし、どのfieldを必須にするか、誰が作るかはJSONそのものではなく、chat applicationのprotocolが決める。

attachment本体は、cardへ詰め込まなかった
今回送りたいのは、南口に着いたよというcaptionと、駅の案内図だった。
画像は3.8 MBある。
イトは、そのbyte列を小さなroom event cardへ無理に押し込もうとした。
cardが膨らみ、event railの入口で止まる。
そこで画像本体をmedia shelfへ先に預けた。
uploadが完了すると、shelfはその画像を取りに行くためのreferenceを一つ返す。
message contentが運ぶのは、caption、message type、attachment reference、表示に必要な最小限のmetadataだ。
media shelf
image bytes 3.8 MB
message content
type image
caption 南口に着いたよ
attachment reference media/ref/blue-map
media type image/jpeg
width / height 1600 / 900
これは今回のfixtureで採用した分け方だ。
serviceやprotocolによってfield名もupload手順も変わる。
referenceがあるだけで、誰でも画像を見てよいことにもならない。取得時のauthorizationや保存期限は別に設計する。
同じtransaction keyで、もう一度送った
イトはroom historyを空に戻し、notification triggerも0へ戻した。
一回目のrequestを送る。
+000 ms request tx-206 arrives
+018 ms server appends ev-900
+020 ms response rail is cut
serverにはev-900が一件保存された。
clientはresponseを受け取っていない。
イトは、今度は新しいkeyへ変えなかった。
同じdevice、同じroom send endpoint、同じtx-206でretryする。
server counterは、保存済みの対応表を見つけた。
tx-206 -> ev-900
新しいeventをappendせず、最初と同じev-900をresponseへ返す。
requests received 2
room events appended 1
event ID returned ev-900
notification triggers 1
duplicate bubbles 0
ユイの会話欄には、南口に着いたよが一つだけ残った。
イトはようやく、sender側の送信済みlampを点けた。
transaction keyとevent IDは、同じ番号ではない
確認のため、イトは同じ本文をもう一度、本当に新しいmessageとして送った。
今度はtx-207を作る。
serverは新しいev-901を付け、二件目としてappendした。
tx-206 -> ev-900 first send and its retry
tx-207 -> ev-901 intentional second message
本文が同じでも、二回目が新しい送信なら別eventになる。
本文が少し違っても、同じtransaction keyのretryなら、勝手に別eventへ変えない。
client transaction keyはrequestの再送を見分ける印。
server event IDは、保存されたroom eventをあとで指す印。
二つを一つの役割にしない。
sender、server time、accepted / delivered / readも、clientが自由に書く一枚のstatus札へまとめなかった。
誰が観測し、誰が確定したstateかを分けておく。
一つのeventから、呼び鈴が起きた
test boardの最後のlampが点いた。
room event appended: 1。
壁の呼び鈴へ、細いrailが一本だけ伸びる。
ユイのappは閉じていた。
呼び鈴の手前で、event cardは二つの窓に分かれた。
一つの窓には、本文と画像の小さなpreviewが見える。
もう一つの窓には、新しいメッセージという合図だけが入る。
画面がlockされているとき、どちらを見せるのか。
通知を断っているdeviceへ、呼び鈴を鳴らしてよいのか。
イトは一枚になったevent cardを持ち上げたが、二つの通知窓にはまだ入れなかった。
次回、ピコルート第83話。
イトは、本文を通知へ載せる?|画面を閉じた相手への呼び鈴

