Editing the same document at the same time by two editors is a key feature of an online IDE we’re currently working on. The application is built with Meteor. Which options are there to implement concurrent editing based on operational transformation?
Existing packages
Installing mizzao:sharejs edemaine:sharejs
Fortunately, Atmosphere provides us with packages which cater for this functionality. The most used one is mizzao:sharejs and its variants containing ACE and Codemirror as code editors. For analysis we’ll create a demo app using it:
meteor create ot-mizzao-sharejs
cd ot-mizzao-sharejs
meteor add mizzao:sharejs
In the next step we integrate the text area in client/main.html:
{{> sharejsText docid="test" id="editor"}}
Unfortunately, starting Meteor gives us an error:
ReferenceError: ShareJS is not defined
at sharejs-server.js (packages/mizzao:sharejs/sharejs-server.js:8:24)
The problem is known since over a year and can be fixed by replacing the package with edemaine:sharejs:
meteor remove mizzao:sharejs
meteor add edemaine:sharejs
This package is preconfigured to use an in-memory database for the transactions. To use Mongo, a settings file, e.g. settings.json, must be created and used for starting the server:
{
"sharejs": {
"options": {
"db": {
"type": "mongo",
"opsCollectionPerDoc": true
}
}
}
}
meteor --settings settings.json
Now, it works.
Behind the scenes
The network tab of the developer toolbar reveals the communication between the browser and the server:
It shows two connections:
- The standard websocket connection of Meteor
- A second connection using long polling for ShareJs
Using Mongo to store the transformations creates a number of tables:
A docs table contains the content of all documents. For every document an ops table is created, just like ops.test in our test case. It contains the operational transformations.
Setting the option opsCollectionPerDoc to false will create only one table ops for all documents.
The pros and cons
The great advantage of this solution is the easy integration. It took less than an hour to get this feature working in a basic way. The server API of ShareJS is available to access the documents on the server.
I see the big disadvantage that this solutions creates something like a parallel universe to the Meteor data features, which already provide a real-time communication between client and server using websockets, which can be considered better for this type of communication. The browser opens a second connection to the same server, which needs twice the ports and thus more resources.
Alternatives
Use of native Meteor features for communication
The client does the diffing to generate the transformations. The transformations are collected by the server and written to a published collection (after adapting the transformation to the current state). The client subscribes to the collection and adopts the content of the editor accordingly (keeping in mind, that some transformations might not be processed by the server yet).
This would lead to a pure Meteor OT implementation.
Use the Meteor websocket for ShareJS
The websocket of Meteor is reused for the communication between ShareJS client and server.
This would give all the possibilities and optimizations of ShareJS, which is a well-tested piece of software. There are no API changes, so existing applications based on edemaine:sharejs or mizzao:sharejs will continue to work without any changes.
Also published on Medium.