Concurrent editing with Meteor

Concurrent editing with Meteor

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. 

No design, just functional: Editing the same document at the same time finally works.

Behind the scenes

The network tab of the developer toolbar reveals the communication between the browser and the server:

Let’s take a look at the network tab!

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:

The Mongo collections created for this example.

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.


Photo by rawpixel on Unsplash


Also published on Medium.

Leave a Reply

Your email address will not be published. Required fields are marked *