テレビのアニメはどこから来る? / 第82話

第82話:イトは、同じメッセージを二度送った|本文だけでは届かない

ピコルート第82話。返事が消えたため同じメッセージを再送すると、会話に二つ並んだ。イトは本文、room、送信者、transaction ID、event ID、添付参照の役割を分けます。

メッセージpayloadtransaction IDevent ID添付参照10分更新

第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が決める。

イトが同じtransaction sealを持つretry cardをreturn trayへ戻し、一枚のevent cardだけを青いroom ledgerへ通す
同じ本文かではなく、同じ送信試行を示すtransaction keyでretryを見分ける。

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話。

イトは、本文を通知へ載せる?|画面を閉じた相手への呼び鈴

この物語を、解説で深める

第82話で生まれた疑問を、解説記事で少し深く見ていきます。物語で感じた不思議さを残したまま、言葉と仕組みを順番に整理しましょう。

メッセージの仕組み|本文・宛先・状態を届けて保存する流れを読む