Real-Time with Flowdock

40
Real-Time with I’m going to talk about the most prominent aspect in our single page app.

description

Ville Lautanala describes different transport channels that allow pushing data from servers to clients in real time. He also introduces a case study of Flowdock's experience with socket.io and WebSockets. Presentation from Frontend Finland meetup, March 14th. A slightly modified version was presented at SFJS, April 3rd.

Transcript of Real-Time with Flowdock

Page 1: Real-Time with Flowdock

Real-Time with

I’m going to talk about the most prominent aspect in our single page app.

Page 2: Real-Time with Flowdock

@lautisWear funny hats and have a messy desk at Flowdock. Occasionally write code. Spend much time in backend these days.

Page 3: Real-Time with Flowdock

“There’s no time like real-time”

Real-time is necessary for a chat app. Interval polling wouldn’t work.Big part of our app, no Backbone etc.

Page 4: Real-Time with Flowdock

Demo

Page 5: Real-Time with Flowdock

Stream changes from backend

And send messages

* stream and apply changes* send some messages to server=> To achieve this, some kind of transport channel between server and client is necessary.

Page 6: Real-Time with Flowdock

Long-pollingXHR-streamingHTMLFile

WebSocketServer-Sent Events

JSONP streamingXHR multipart

Each have their shortcomings, work on different browsers, and some work cross-domain, others not.

Page 7: Real-Time with Flowdock

3 implementations with 7 transports

We couldn’t really decide what to use. There isn’t really a one-size fits all option, but you definitely could do with less.

Page 8: Real-Time with Flowdock

Long-polling

HTTP/1.1 200 OKContent-Type: application/json;

[{'content': 'json'}]

The most obvious choice, works on every browser. Wait until you have something to tell to client. Rinse, repeat.

Page 9: Real-Time with Flowdock

XHR-streaming

HTTP/1.1 200 OK

{'content': 'json'}{'content': 'json'}{'content': 'json'}

Streaming: multiple chunks in one response. Widely used, not much advertised.XHR-streaming works by parsing the response after each onReadyStateChange.ResponseText will be huge if your HTTP request runs for long time, so periodic reconnection is needed. Flowdock uses 60 seconds, requests have payload about user activity.

Page 10: Real-Time with Flowdock

Server-Sent EventsHTTP/1.1 200 OK

Content-Type: text/event-stream

data: {'content': 'json'}

id: 2

data: {'content': 'json'}Sent to server on reconnect

SSE aka ES is standardization of XHR streaming pattern. ID attribute allows seamless reconnections from client point-of-view.Hopefully will be in FD mobile soon.

Page 11: Real-Time with Flowdock

In JavaScript

var es = new EventSource('/events');es.onmessage = function (e) { console.log(JSON.parse(e.data));};

There’s actually something worth showing. Simple, bind message event listener.Other events too

Page 12: Real-Time with Flowdock

Built-in in modern browsers

Good news: Chrome, Safari 5, FF6, Opera 11

Page 13: Real-Time with Flowdock

No IE

Even IE10 seems unlikely

Page 14: Real-Time with Flowdock

No Android

Unlike iOS, Andriod doesn’t have ES. Even Android 4.0 is missing this. Maybe Chrome for Android will fix this.

Page 15: Real-Time with Flowdock

Use shim insteadhttps://github.com/Yaffle/EventSource

Because SSE are “just” HTTP, you can implement it in JavaScript, today. And many have done it. Use some available shim.

Page 16: Real-Time with Flowdock

Streams are unidirectional

XHR requests are needed to post data

Page 17: Real-Time with Flowdock

Sockets are bidirectional

Some kind of socket interface would be nice. Luckily for us, WebSockets are part of HTML5.

Page 18: Real-Time with Flowdock

WebSocketsvar socket = new WebSocket('/socket');socket.onmessage = function (e) { console.log(JSON.parse(e.data));};

socket.onopen = function() { var m = {'content': 'json'}; socket.send(JSON.stringify(m));}

I’m not going to exaplain the WebSocket protocol here, the standardized version is a bit more complicated than what we saw previously. The JavaScript API is more interesting.Receiving messages with websockets is pretty similar to EventSource. Sending messages is trivial as long as you have received onopen.

Page 19: Real-Time with Flowdock

Caveats

•Limited browser support

•Proxy problems

•Safari crashes when used with Proxy Auto-Configuration

Safari has different protocol than others, IE 10 will be the first IE to support WS. NO SHIMSWebSockets require full HTTP 1.1.No way to detect if user has PAC. Rare.

Page 20: Real-Time with Flowdock

Socket.IO“It's care-free realtime

100% in JavaScript.”

Abstraction layer on top of different transports. NO SSE. Server-side and client-side.We’ve been rolling Socket.IO out slowly for last month or so (bugs!).

Page 21: Real-Time with Flowdock

•Reconnection race-conditions

• Infinite loops with XHR-polling

•DOM Exception 11

Bugs

*Socket.IO didn’t know that there already was a reconnection attempt on going and established multiple connections.* XHR-polling transport client could get in state where it didn’t handshake properly but only hammered the XHR-polling endpoint.* INVALID_STATE_ERR, send data with unopened websocket. Yet another race-condition.

Page 22: Real-Time with Flowdock

Socket.IO“It's care-free realtime

100% in JavaScript.”care-free

Page 23: Real-Time with Flowdock

Why bother?

If it’s so horrible, why even bother?

Page 24: Real-Time with Flowdock

Science!It Works, Bitches.

Let’s look at the numbers.

Page 25: Real-Time with Flowdock

Latency matters

We knew websockets would be fastest, but by how much?Benchmark by sending a message and test how long it took to receive it

Page 26: Real-Time with Flowdock

Round-trip latency

0

75

150

225

300

Ping XHR-streaming WebSocket

Baseline: ping to our servers in Germany was 52msXHR: 240ms. TCP and especially SSL handshake is expensive. Streaming part doesn’t add much penalty.WebSockets came pretty close to ping, about 61ms. Node proxy in test setup + our backend server added latency.

Page 27: Real-Time with Flowdock

Real-world metrics from Flowdock

How many can use websockets? Clients as guinea pigs.

Page 28: Real-Time with Flowdock

96% use WebSockets

No Flash fallback

Over 80% have up-to-date Webkit based browsers.

Page 29: Real-Time with Flowdock

3% have obsolete browsers

IE users are minority.

Page 30: Real-Time with Flowdock

1% have network issues

(or have disabled WebSockets)

We don’t really know.

Page 31: Real-Time with Flowdock

Network still sucksCan’t just wait for reply even with websockets.Network latencies vary, we have clients all around the world.

Page 32: Real-Time with Flowdock

Render optimistically

You have two basic choices, either use loading indicator or render users changes optimistically. Needless to say, we try to be on the optimistic end of the road.

Page 33: Real-Time with Flowdock

1.Post new stuff2.Receive new stuff3.Modify existing stuff

Basic operations in Flowdock

Post new stuff: send messagesReceive: simple, just append to the stream. unless conflictsModify: mostly tag edits, but can also be message deletion. Highlights and unread handling are tags.

Page 34: Real-Time with Flowdock

•Clients process messages as much as possible

•Server adds unique ID and timestamp to data

•Message echoed to client

Posting messages

Client processes messages. In our case, this basically means that tags are parsed and message rendered to DOM. Later, if necessary, these changes can be reverted.Server adds some information to messages, which is needed to add new tags. Client uses this information to complete the message.Last, message is echoed to all clients, including sender. This causes duplication, but data is needed to “complete” outgoing messages.

Page 35: Real-Time with Flowdock

SUP

LOL

FOOBAR

FOO

Order sync

"Undo" pending operationsApply operation from networkRe-apply pending opsA bit like git rebase, but cheat a little

Page 36: Real-Time with Flowdock

Modifying stuff

•Add/remove tag to message

•Mark message as deleted

Page 37: Real-Time with Flowdock

"Operation, which can be applied multiple times without changing the result beyond the

initial application."

Idempotent changes

Tags are good fit for this. You can add the same tag to a message multiple times. This is used as our event-stream contains messages the client has sent.

Page 38: Real-Time with Flowdock

Error handling is tricky

Handling errors becomes quickly quite tricky. Good MVC would make it easier, but also it’s not easy to know when some operation has failed because of poor network with WebSockets and Socket.IO.UX implications!

Page 39: Real-Time with Flowdock

Protips

•Socket.IO will bite you

•SSE is safe choice for streaming

•Design for broken internet

People put laptop to sleep, live with it. Important for single page apps. Avoid reloading.

Page 40: Real-Time with Flowdock

Thanks!