Upgrade to Pro — share decks privately, control downloads, hide ads and more …

livedb-rethinkdb: Building collaborative apps ...

livedb-rethinkdb: Building collaborative apps with ShareJS and RethinkDB

Jorge Silva

April 28, 2015
Tweet

More Decks by Jorge Silva

Other Decks in Technology

Transcript

  1. Overview What is Operational Transformation? ShareJS: A Node.js library for

    OT RethinkDB: A database for realtime apps livedb-rethinkdb: RethinkDB + ShareJS
  2. abc User #1 User #2 abc { op: [1, ‘bc’]

    } { op: [1, ‘bc’] } Server abc
  3. User #1 User #2 { op: [3, ‘efg’] } 012abc

    1ms later: { op: [0, ‘012’ ] } abcefg Server 012efgabc
  4. 012efgabc User #1 User #2 012abcefg { op: [3, ‘efg’]

    } { op: [0, ‘012’ ] } Server 012efgabc
  5. This strategy is not able to accomodate the user’s intentions

    User #2’s changes are not inserted at the right cursor position.
  6. A class of algorithms that do multi-site realtime concurrency. Gives

    you eventual consistency - between multiple users - without retries - without errors - without any data being overwritten http://sharejs.org/
  7. User #1 User #2 { op: [3, ‘def’] } 012abc

    1ms later: { op: [0, ‘012’] } abcdef Server 012abcdef { op: [0, ‘012’] } { op: [6, ‘def’] } Transformed into: Transformed into:
  8. 012abc User #1 User #2 012abcdef Server 012abcdef { op:

    [0, ‘012’] } Not Applied Transformed into: { op: [0, ‘012’] } { op: [0, ‘012’] }
  9. 012abcdef User #1 User #2 abcdef Server 012abcdef { op:

    [6, ‘def’] } { op: [6, ‘def’] } Transformed into: { op: [6, ‘def’] } Not Applied
  10. Possible applications Collaborative text and code editors Realtime wikis Any

    type of concurent text editing Examples: Google Docs, hackpad, etc.
  11. Enter ShareJS “ShareJS is an Operational Transform library for NodeJS

    & browsers. It lets you easily do live concurrent editing in your app.”
  12. // WebSocket Connection var ws = new WebSocket(‘ws://localhost:8005’); var shareJS

    = new window.sharejs.Connection(ws); // Textarea var textareaDoc = shareJS.get(‘documents’, ‘helloworld’); textareaDoc.subscribe(); // Wait for document to be ready textareaDoc.whenReady(function () { setTimeout(function () { if (textareaDoc.type && textareaDoc.type.name === ‘text’) { // Attach textarea to document var elem = document.getElementById(‘helloworld’); textareaDoc.attachTextarea(elem); } }); }); ShareJS on the client https://github.com/thejsj/sharejs-rethinkdb-example/blob/master/client/index.html#L102-L124
  13. // WebSocket Connection var ws = new WebSocket(‘ws://localhost:8005’); var shareJS

    = new window.sharejs.Connection(ws); // Textarea var textareaDoc = shareJS.get(‘documents’, ‘helloworld’); textareaDoc.subscribe(); // Wait for document to be ready textareaDoc.whenReady(function () { setTimeout(function () { if (textareaDoc.type && textareaDoc.type.name === ‘text’) { // Attach textarea to document var elem = document.getElementById(‘helloworld’); textareaDoc.attachTextarea(elem); } }); }); ShareJS on the client https://github.com/thejsj/sharejs-rethinkdb-example/blob/master/client/index.html#L102-L124
  14. var sharejs = require(‘share’); var livedb = require(‘livedb’); // Connect

    to the database var db = require(‘livedb-rethinkdb’)({ host: ‘localhost’, port: 28015, db: ‘sharejs’ }); var backend = livedb.client(db); // Attach livedb instances to ShareJS var share = sharejs.server.createClient({ backend: backend }); ShareJS on the server: database https://github.com/thejsj/sharejs-rethinkdb-example/blob/master/server/livedb-client.js
  15. var sharejs = require(‘share’); var livedb = require(‘livedb’); // Connect

    to the database var db = require(‘livedb-rethinkdb’)({ host: ‘localhost’, port: 28015, db: ‘sharejs’ }); var backend = livedb.client(db); // Attach livedb instances to ShareJS var share = sharejs.server.createClient({ backend: backend }); ShareJS on the server: database https://github.com/thejsj/sharejs-rethinkdb-example/blob/master/server/livedb-client.js
  16. var wss = new WebSocketServer(); var Duplex = require(‘stream’).Duplex; var

    share = sharejs.server.createClient({ backend: backend }); // On socket connection wss.on(‘connection’, function () { var stream = new Duplex({ objectMode: true }); stream._write = function (chunk, encoding, callback) { client.send(JSON.stringify(chunk)); return callback(); }; client.on(‘message’, function (data) { return stream.push(JSON.parse(data)); }); return share.listen(stream); }); ShareJS on the server https://github.com/thejsj/sharejs-rethinkdb-example/blob/master/server/socket-handler.js#L8-L49
  17. var wss = new WebSocketServer(); var Duplex = require(‘stream’).Duplex; var

    share = sharejs.server.createClient({ backend: backend }); // On socket connection wss.on(‘connection’, function () { var stream = new Duplex({ objectMode: true }); stream._write = function (chunk, encoding, callback) { client.send(JSON.stringify(chunk)); return callback(); }; client.on(‘message’, function (data) { return stream.push(JSON.parse(data)); }); return share.listen(stream); }); ShareJS on the server https://github.com/thejsj/sharejs-rethinkdb-example/blob/master/server/socket-handler.js#L8-L49
  18. Open source database for building realtime applications NoSQL database that

    stores schemaless JSON documents Distributed database that is easy to scale What is RethinkDB?
  19. Subscribe to change notifications from database queries No more polling

    – the database pushes changes to your app Reduce the amount of plumbing needed to stream live updates Built for realtime
  20. var r = require(‘rethinkdb’); r .db(‘share_js’) .table(‘documents’) .filter({ name: ‘helloworld’

    }) .run(conn) .then(function (cursor) { // Go through every row returned by the query cursor.each(function (row) { console.log(row); }); }); Changefeeds
  21. var r = require(‘rethinkdb’); r .db(‘share_js’) .table(‘documents’) .filter({ name: ‘helloworld’

    }) .run(conn) .then(function (cursor) { // Go through every row returned by the query cursor.each(function (row) { console.log(row); }); }); Changefeeds
  22. var r = require(‘rethinkdb’); r .db(‘share_js’) .table(‘documents’) .filter({ name: ‘helloworld’

    }) .changes() .run(conn) .then(function (cursor) { // Gets fired every time a row changes cursor.each(function (row) { console.log(row); }); }); Changefeeds