![](https://habrastorage.org/getpro/habr/post_images/873/05a/d07/87305ad071079b3da6c7d84e2fd3350f.jpg)
ジョセフ(ジョセフジェントル)は、Google WaveのOT(データ共有との競合を解決する手法)に関する2年間の作業の後、同様の製品を作りたい人にとっては、もう少し時間がかからないという考えを思いつきました。 これらの人々を何らかの形で助け、知識を共有するために、 NodeJSに基づくOTの実装であるShareJSライブラリが作成されました。 Cの実装もあります 。
OTとは何ですか?
OTは、運用変換または運用変換です。
データがあり、このデータに対する操作があります。 順番に操作が行われます。 全体のポイントは、データを自分で実行する前に、操作自体が現在のデータに対する以前のすべての操作に従って変化することです。
ウィキペディアの良い例:
![](https://habrastorage.org/getpro/habr/post_images/2b9/f5f/b24/2b9f5fb24ef9d0f893d5ceab53dc668c.png)
最初の時点では、両方のユーザーが文字列「abc」を持っているため、同時に2つの操作を作成します。
O1 = [0、“ x”]を挿入(位置“ 0”に文字“ x”を挿入)
O2 =削除[2、“ c”](位置“ 2”の文字“ c”を削除)
これらの操作の実行順序に応じて、結果は異なります。
O2、O1の場合、「abc」->「ab」->「xab」
O1、O2の場合、「abc」->「xabc」->「xac」
両方のケースで同じxabの結果をどのように取得しますか? 以前の変更を考慮して、操作を変換しますか? OTではどのように見えますか?
O2: 'abc'-> 'ab'
OT:O1 Insert [0、“ x”]-> O1` Insert [0、“ x”]
O1`: 'ab'-> 'xab'
この場合、前の操作O2にはO1の位置よりも多くのシンボルが位置にあったため、操作O1とO1`は同一です([0、“ x”]を挿入)。 したがって、O2はO1に影響せず、変更しません。
O1: 'abc'-> 'xabc'
OT:O2 Delete [2、“ c”]-> O2` Delete [3、“ c”]
O2`: 'xabc'-> 'xab'
この場合、OTはO2の前にすでにO1があり、O2の位置の前に1文字挿入されているため、O2は位置2から削除するのではなく、位置3から削除する必要があることを考慮しました。
OTの詳細: wiki 、 FAQ
データ型
OTでは、多くはデータのタイプに依存します。 結局、操作(およびo_O操作の操作)はデータ型によって異なります。 たとえば、文字列を使用するすべての操作は、最終的にわずか3に減らすことができます。
-キャリッジを位置nに移動します
-行nを現在の位置に挿入
-現在の位置から始まるn文字を削除します
または2つでも:
-位置nに行を挿入
-位置nから始まるm個の文字を削除する
まず第一に、jsonはオブジェクト、配列、文字列、数値で構成できるため、jsonデータ型に興味があり、すべてがやや複雑です。 これらはすべて異なるデータ型です。 これが文字列に対してどのように機能するかはすでに見てきました。 配列の場合、同様の状況。 OTは、数値の増分を計算することもできます。 オブジェクトの場合はさらに複雑です。2人のユーザーがjsonオブジェクトフィールドを同時に上書きした場合、共通のjsonデータタイプの両方のユーザーからの変更を考慮することはできません。 2つの操作のいずれかが失われます。 これが重要な場合は、アプリケーションに固有の独自のデータ型を作成できます。
ShareJSのデータ型は、別個のOTTypesプロジェクトです。 現在、stringとjsonの実装がいくつかあります。 計画はリッチテキストデータ型用です。
リッチー
ShareJSは、サーバー部分とクライアント部分で構成されています。 コードの一部(操作の変換など)は同型です。つまり、サーバーとクライアントの両方で実行されます。 オペレーションストアは、Mongo(データ)とRedis(オペレーションキャッシュ)で構成されるLiveDBを使用します。 データモデル-ドキュメント指向-コレクションとドキュメント。 各ドキュメントにはデータ型とバージョンがあり、ドキュメントが変更されるたびに増加します。 操作はドキュメントレベルでアトミックです。
クライアントはいくつかの同期操作を実行します。 それらはグループ化され(setTimeout(sendToServer、0);)、圧縮され(連続する同一のものが結合され、互いのアクションを無効にするものは削除されます)、群衆の中のサーバーに送信されます。
操作データのバージョンがデータベース内のデータの現在のバージョンに対応する場合、操作が適用されます。適用されない場合、サーバーは最初にRedisで、次にMongo(デフォルトではすべての操作のリポジトリ)で中間操作の履歴を見つけようとします。 操作は中間操作に従って変換され、データに適用されます。 操作の実行自体は、Redis(Luaスクリプト)で順次(トランザクション)実行されます。ここでは、データバージョンの関連性が再度チェックされ、バージョンが関連しなくなった場合は、円が再び繰り返されます。
Redis PubSubを使用して、データ変更イベントがキャッチされ、必要に応じて、すべてのサインアップされたクライアントに中間操作が送信されます。
このモデルを使用すると、一方では一貫性のあるデータを、他方では高速性とスケーラビリティを実現できます。
LiveDBは別の形式で実装できます。 主なことは、ストアがトランザクションとイベント(PubSub)をサポートしていることです。 たとえば、 Foundation DBはこれに最適です。
現在の実装では、Mongoとの通信はアダプターによって提供され、 アダプターは他のデータベースに合わせて書き換えることができます。
デフォルトでは、操作の履歴全体がデータと同じMongoデータベースに保存されます。 まったく保存することも、別のデータベースに保存することもできません。
申込み
ShareJSは、Google Wave、Google Docsなどのリアルタイムデータ共有アプリケーションの作成に最適です。
ShareJS- RacerJSのラッパーがあります。 これは、データを操作するための本質的に美しいインターフェースです。 クライアントフレームワーク(AngularJS、Backboneなど)でRacerJSを使用することができます。
すべてが欲しい人のために、すぐにDerbyJSがあります 。 これは、データを操作するためのモデルとしてRacerJSを使用するフルスタック同形フレームワークです。
ShareJS、RacerJS、DerbyJSに関する質問は、 Stackoverflow Chatで質問できます (Stackoverflowで評判が必要> 20)
DerbyJSコンテンツ