Google DocsのようなCollaborationを実現するGoogle Realtime API

Google DocsのようなCollaborationを実現するGoogle Realtime API

Google Docsのような複数人のリアルタイム編集を実現できるAPI
2013年のGoogle I/Oで初出の話題らしいけど、2016年時点のGoogle APIのドキュメントにも居るのでまだ古くない?

Video
とりあえずこれを観る daiiz

Google Docs Style instant collaboration
全部JS, サーバ不要
コンフリクト全自動解消!!
バックエンドはGoogle Drive

Realtime cube
ルービックキューブの操作アニメーション同期

システム構築
JSONのやりとり
ユーザが作るもの
Realtime data model
(only) your application!

Working with the data model
gaip.drive.realtime.load(fileId, onFileLoaded, initModel)
Google Drive上のファイルにデータを保存する?
onFileLoadedでevent listenerを仕掛ける

作例
数独アプリ
クラウドIDE
ダイアグラム、フローチャート作成アプリ

裏側に迫る
リアルタイム編集
大量の編集差分リクエストを受け付けて、全てのクライアント上のドキュメントで整合性を保たなければいけない
とにかくすべてのmutaionsを保存しておく
append only

mutation
insert
指定位置 (index) に文字列を挿入する
delete
指定位置から何文字かを消す
サーバにすべてのmutationをstoreしておく
ドキュメントが読み込まれたら、これらのmutationからビルドされるsnapshotを送信する

複数人での同期を図る仕組み
mutationを文字通りそのまま受け入れていたら自分や他人の編集が衝突しまくり、ドキュメントが壊れる
思考実験

Aさんが index=4 の位置に5文字 insert
NG: Aさんのmutationを受け入れ前に、Bさんが index=14 の位置に文字列を追加
OK: Aさんのmutationを受け入れ前に、Bさんが index=20 の位置に文字列を追加
直前までの変化を汲んで適用するmutationを読み替えていかないといけない

Story1. サーバでの変換処理 Transformation
A -> Google: insert @4 rainy
B (locally changed): insert @14 brightly
B -> Google: insert @14 brightly
Google -> B (locally merged) : insert @4 rainy (... A's mutation)
Google -> A (locally merged) : insert @20 brightly (... modified B's mutation)

Story2. サーバはすべてのchangesは変換できない

The server might not know about A's mutation after it send B's to A.
B -> Google: *insert @14 6-char : 一番乗りのcommitなので、serverがmodifyする必要ない
A (locally changed): **insert @4 5-char
この変更はまだserverは知らない
Google -> A: * (... B's commit)
衝突するので、Aが見ているドキュメントが壊れる
A -> Google: ** (... A's commit)
サーバでは整合性がとれている (変換の必要なし)
Google -> B: ** (... A's commit)
Bが見ているドキュメントは整合性が取れている

Story2の解決策
サーバでだけでなく、Clientでもtransformする
Keep a client-side queue of sent mutations and transform incoming mutations against the queue
送信済みのcommitsのキューを持っておく
受信しているcommitsをキューの内容に対して変換する
各々のクライアントでTransform Managerというやつを用意する
Pending Mutation Queue 送信待ちキュー
Story2での Step2 の **insert @4 5-char がここに居る
サーバーからcommitを受け取る
もちろん、このcommitは、クライアントのPending Mutaion Queueとの衝突は考慮していない (できるはずがないので当然の状態)
Transform Manager内で自身に適用するmodified commitを作成する
サーバから ACK を返されたら、Pending Queueの中からcommitを消去できる

Undoの仕組み
自分がやったmutationを取り消して元に戻す仕組み
あるmutationを確定した際に、Undo Stackに inverse mutation (真逆のcommit) を積んでいく
insertを確定したならば、指定位置から指定文字数分消去する delete commit を積む
これだと、他人に編集されると打ち消し位置が壊れる
inverse mutationの他に、すべての共同編集者から受信したmutationsを積んでいく
なるほどdaiizdaiiz
これでさっきと同じTransform Functionを使えそう
共同編集者の編集情報を考慮した上で、自分の操作を取り消せる
取り消し開始位置をshiftして対処できる

Event planning system を作りながら、data model の構築を学ぶ
意味のある複数のプロパティをひとかたまりに定義できる
各々のプロパティを到着順に解決していると、サーバへの到着順によっては、days, 20 のような組み合わせになる可能性がある
トランザクション定義のようなものか
タスクの割当処理は atomic ではない
一つのtaskを誰かに割り当てるための操作は2ステップ
taskを古い所属から消す、新しい所属にtaskを追加する
うまいことやらないと、新しい所属先が2個同時に設定されたりする

Export data model as JSON
逆に、JSONをアップロードすると、Model Diff を抽出して更新してくれる

#2017-09-19
Powered by Helpfeel