Slide 1

Slide 1 text

livedb-rethinkdb: Building collaborative apps with ShareJS and RethinkDB Jorge Silva · @thejsj · Developer Evangelist @ RethinkDB

Slide 2

Slide 2 text

Overview What is Operational Transformation? ShareJS: A Node.js library for OT RethinkDB: A database for realtime apps livedb-rethinkdb: RethinkDB + ShareJS

Slide 3

Slide 3 text

Text here...

Slide 4

Slide 4 text

Text here... Text here... User #1 User #2

Slide 5

Slide 5 text

Text here... Text here... User #1 User #2 How could these two users collaborate?

Slide 6

Slide 6 text

Every time a user makes an edit, push an updated document

Slide 7

Slide 7 text

abc User #1 User #2 ‘abc’ Server abc

Slide 8

Slide 8 text

abc User #1 User #2 ‘abc’ ‘abc’ abc Server abc

Slide 9

Slide 9 text

abc User #1 User #2 ‘xyz’ xyz 1ms later: Server xyz ‘abc’

Slide 10

Slide 10 text

xyz User #1 User #2 ‘abc’ ‘xyz’ xyz ‘abc’ ‘xyz’ Server xyz

Slide 11

Slide 11 text

This system doesn’t take latency into consideration

Slide 12

Slide 12 text

What if, instead of sending a completely updated document, we just sent the user’s actions?

Slide 13

Slide 13 text

User #1 User #2 abc { op: [1, ‘bc’] } a Server abc

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

This strategy is not able to accomodate the user’s intentions User #2’s changes are not inserted at the right cursor position.

Slide 18

Slide 18 text

Merely sending operations to the server doesn’t solve our problem. We need to tranform our operations.

Slide 19

Slide 19 text

What is Operational Transformation?

Slide 20

Slide 20 text

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/

Slide 21

Slide 21 text

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:

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Possible applications Collaborative text and code editors Realtime wikis Any type of concurent text editing Examples: Google Docs, hackpad, etc.

Slide 25

Slide 25 text

OT is an important tool for building realtime applications

Slide 26

Slide 26 text

How can I use this in my own projects?

Slide 27

Slide 27 text

Enter ShareJS “ShareJS is an Operational Transform library for NodeJS & browsers. It lets you easily do live concurrent editing in your app.”

Slide 28

Slide 28 text

Demo http://livedb-rethinkdb.thejsj.com/

Slide 29

Slide 29 text

// 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

Slide 30

Slide 30 text

// 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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Open source database for building realtime applications NoSQL database that stores schemaless JSON documents Distributed database that is easy to scale What is RethinkDB?

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

ShareJS & RethinkDB npm install sharejs livedb livedb-rethinkdb --save

Slide 41

Slide 41 text

Access documents

Slide 42

Slide 42 text

Access every single operation

Slide 43

Slide 43 text

Listen for new operations

Slide 44

Slide 44 text

Try it! https://github.com/thejsj/sharejs-rethinkdb-example

Slide 45

Slide 45 text

ShareJS http://sharejs.org/ | https://github.com/share/ShareJS RethinkDB http://rethinkdb.com/ | @rethinkdb livedb-rethinkdb https://github.com/thejsj/livedb-rethinkdb Twitter @thejsj Questions?