writeSyncStep1 とは
Yjs の WebSocket サーバーを実装していると、 writeSyncStep1 という謎のステップがあることがわかる。
共同編集に新しいクライアント(共同編集者)が参加した時に、今共同編集中のドキュメント全体を貰う必要がある。
クライアントが共同編集に参加した後に、writeSyncStep1 メッセージを送ると、その応答として、共同編集中の別のクライアントが、ドキュメント全体を送ってくれる。
新しく参加したクライアントは、この応答をもって初めて共同編集処理がスタートされる。writeSyncStep1 の応答がない場合は、共同編集が正しくスタートされない場合があるので注意。
複数のクライアントが共同編集に参加している場合は、複数のクライアントから応答が返ってきてしまうが、メッセージの重複を排除する処理が Yjs には組み込まれているので、重複して送られてきても問題は無い。
クライアントとサーバー間の通信(WebSocketの場合)を図で表すと以下の通りである。クライアント同士の接続の場合も、サーバーが別のユーザーになるだけで同じだ。
より詳細な仕様
Yjs では、バイナリでメッセージをやり取りしている。
まず、最初の1桁目の数字が何かによって以下の分類がされる。メッセージ名は y-websocket のサーバーサイド実装例の
数字 | 意味 |
---|---|
0 | 共同編集中のドキュメントに関するメッセージ |
1 | カーソルの位置情報など、ドキュメントの内容以外のメッセージ |
さらに、これが「0」の場合は、次の数字1桁(バイナリだと多分2ビット)によって、更に細分される。
数字 | 意味 |
---|---|
0 | writeSyncStep1 のメッセージ |
1 | writeSyncStep1に対する応答のメッセージ |
2 | ドキュメントの内容の更新に関するメッセージ |
0と1は、ドキュメントの共同編集開始時に利用されるメッセージで、2は、共同編集中の更新内容のやり取りに使われるメッセージである。
y-websocket のサーバーサイド実装を見てみると、以下の部分でwriteSyncStep1 に対する応答のメッセージが送信されている(と思われる)。これが非常に分かりづらかった。
writeSyncStep1 も、messageSync
のメッセージであるので、それに対する応答については、 case messageSync
以下の部分で送られている。
readSyncMessage
に渡したメッセージが writeSyncStep1
だった場合は、それに対する応答のメッセージが encoder に書き込まれる。encoding.length(encoder) > 1
の場合に send としているのがそれである。
encoding.length(encoder) > 1
している2行上で、encoding.writeVarUint(encoder, messageSync)
しているので、ここで encoder
の length は1になっている。
その後、syncProtocol.readSyncMessage(decoder, encoder, doc, null)
の中で、必要があれば(受け取ったメッセージの2桁目が0 = writeSyncStep1だった場合は)、encoder に、1(writeSyncStep1 の応答であること)と、ドキュメント全体が書き込まれる。
よって、encoding.length(encoder)
が1よりも大きいということは、 writeSyncStep1 の応答メッセージである、ということになるので、writeSyncStep1 を送ってきたクライアントに、ドキュメント全体を返している。
ここは、「2桁目が1である」という判定でも良いと思うが(むしろ、わかりやすいのでそっちのほうが良い)、実装が簡単なのでこのようになっているのだろうか...