summaryrefslogblamecommitdiffstats
path: root/talks/cowboy-d3/cowboy-d3.html
blob: 2c24fae48c20132ef9449dd2efef04dc7bd97e50 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565




















































































































































































































































































































































































































































































































































































                                                                                                                                         
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<title>D3 + Websocket for live Web applications</title>
<!-- metadata -->
<meta charset="utf8" />
<meta name="generator" content="S5" />
<meta name="version" content="S5 1.1" />
<meta name="presdate" content="20140307" />
<meta name="author" content="Loïc Hoguin" />
<meta name="company" content="Nine Nines" />
<!-- configuration parameters -->
<meta name="defaultView" content="slideshow" />
<meta name="controlVis" content="visible" />
<!-- style sheet links -->
<link rel="stylesheet" href="ui/default/slides.css" type="text/css" media="projection" id="slideProj" />
<link rel="stylesheet" href="ui/default/outline.css" type="text/css" media="screen" id="outlineStyle" />
<link rel="stylesheet" href="ui/default/print.css" type="text/css" media="print" id="slidePrint" />
<link rel="stylesheet" href="ui/default/opera.css" type="text/css" media="projection" id="operaFix" />
<link href="ui/sh/sh99s.css" rel="stylesheet"/>
<!-- S5 JS -->
<script src="ui/default/slides.js" type="text/javascript"></script>
<!-- syntax highlighter JS -->
<script type="text/javascript" src="ui/sh/shCore.js"></script>
<script type="text/javascript" src="ui/sh/shBrushErlang.js"></script>
<script type="text/javascript" src="ui/sh/shBrushJScript.js"></script>
<script type="text/javascript" src="ui/sh/shBrushXml.js"></script>
</head>
<body>

<div class="layout">
<div id="controls"><!-- DO NOT EDIT --></div>
<div id="currentSlide"><!-- DO NOT EDIT --></div>
<div id="header">
    <div id="sub_header"></div>
    <div id="logo"><img src="ui/img/logo.svg"/></div>
</div>
<div id="footer">
<div id="footer_shadow"></div>
<h1>Erlang Factory SF 2014</h1>
<h2>D3.js + Websocket for live Web applications, Nine Nines</h2>
</div>

</div>


<div class="presentation">

<div class="slide">
<h1>D3.js + Websocket for live Web applications</h1>
<h2>Not all JS is bad. True story!</h2>
<h3>Loïc Hoguin - @lhoguin</h3>
<h4>Erlang Cowboy and Nine Nines Founder</h4>
</div>


<div class="slide">
<h1>Background</h1>
</div>


<div class="slide">
<h1>LeoFS</h1>
<ul>
<li>Distributed file system (S3 and REST API)</li>
<li>Local and multi-DC replication</li>
<li>Potentially huge systems</li>
<li><a href="http://leo-project.net/leofs/">http://leo-project.net/leofs/</a></li>
</ul>
</div>


<div class="slide">
<h1>LeoFS sponsored project</h1>
<ul>
<li>LeoFS developers and QA team need tools to inspect their systems</li>
<li>Users need tools to help debug issues with LeoFS</li>
<li>A centralized tool would help greatly</li>
<li>Alien Shaman is a project to make it happen</li>
</ul>
</div>


<div class="slide">
<h1>Inspired by Bigwig</h1>
<ul>
<li>Bigwig, Spawnfest 2011 winner, was a good start</li>
<li>Unfortunately was never maintained (grrrr!)</li>
<li>Also was local-only, not distributed</li>
</ul>
</div>


<div class="slide">
<h1>Not a fork of Bigwig</h1>
<ul>
<li>Modifying Bigwig would be more work than not</li>
<li>Distribution means events and UI need to be decoupled</li>
<li>Event/probe library is Alien</li>
<li>Web UI is Shaman</li>
</ul>
</div>


<div class="slide">
<h1>Alien</h1>
<ul>
<li>Event/probe library</li>
<li>Different probes: ondemand, inline and process</li>
<li>Routing and filtering rules</li>
<li>Allows grouping events into batches (minimal impact on the running system)</li>
<li>Can send events through Erlang, TCP, UDP, you name it</li>
<li>Most of the work is done</li>
</ul>
</div>


<div class="slide">
<h1>Shaman</h1>
<ul>
<li>Web UI</li>
<li>Forward events to UI using Websocket</li>
<li>Can handle many types of events, even custom ones</li>
<li>Pluggable interface</li>
<li>Live interface, no database required</li>
<li>Work in progress!</li>
</ul>
</div>


<div class="slide">
<h1>Events storage</h1>
<ul>
<li>Events should be stored on disk for later retrieval</li>
<li>Alien relays can be used for this</li>
<li>Shaman will need an interface to preload data/load older data</li>
<li>This isn't even a WIP yet, but it will come eventually</li>
</ul>
</div>


<div class="slide">
<h1>Alien Shaman demo</h1>
</div>


<div class="slide">
<h1>D3.js</h1>
</div>


<div class="slide">
<h1>Data-Driven Documents</h1>
<ul>
<li>Javascript library for manipulating documents</li>
<li>Bind data to the DOM</li>
<li>Apply transformations to it based on data</li>
<li>All using standard HTML, SVG and CSS</li>
</ul>
</div>


<div class="slide">
<h1>Standards-driven</h1>
<ul>
<li>You are not limited to what the library offers</li>
<li>Use all the features offered by browsers as soon as they come out</li>
<li>Easy to debug using browser inspectors</li>
</ul>
</div>


<div class="slide">
<h1>What can you do with D3.js?</h1>
<ul>
<li>Update existing data on a page</li>
<li>Generate HTML tables</li>
<li>Draw diagrams using SVG</li>
<li>Create complex animated representations of your data</li>
</ul>
</div>


<div class="slide">
<h1>To this end, D3.js provides</h1>
<ul>
<li>Selectors</li>
<li>Dynamic properties</li>
<li>Enter and exit selections</li>
<li>Transitions</li>
</ul>
</div>


<div class="slide">
<h1>Enter and exit</h1>
<ul>
<li>Nodes are bound to data</li>
<li>Data passed to D3 with no corresponding node: enter</li>
<li>Data passed to D3 with corresponding node: update</li>
<li>Other nodes with no corresponding data: exit</li>
</ul>
</div>


<div class="slide">
<h1>Table update example</h1>
<div><script type="syntaxhighlighter" class="brush: js"><![CDATA[
function(data){
	var rows = d3.select("#mytable>tbody")
		.selectAll("tr").data(data, function(d){return d.name});
	rows.enter().append("tr");
	rows.exit().remove();

	var cells = rows.selectAll("td").data(function(d){
		return selectData("#mytable>thead>tr>th", d);});
	cells.enter().append("td");
	cells.text(function(d){return d.v});
	cells.exit().remove();
}
]]></script></div>
</div>


<div class="slide">
<h1>__data__</h1>
<ul>
<li>Data bound to nodes is stored in the __data__ property of the node</li>
<li>It can be retrieved, manipulated, updated</li>
<li>(It is generally not needed to do so)</li>
<li>Selectors return a list of nodes so it makes it easy to access too!</li>
</ul>
</div>


<div class="slide">
<h1>Protocol</h1>
</div>


<div class="slide">
<h1>Events forwarding</h1>
<ul>
<li>Websocket frame contains list of events</li>
<li>Events encoded using msgpack</li>
<li>Websocket connection simply encodes and forwards events</li>
</ul>
</div>


<div class="slide">
<h1>Client commands</h1>
<ul>
<li>Client may send commands</li>
<li>Example: nodes.connect to connect to a node</li>
<li>Will be used to dynamically connect to nodes and install probes on them remotely</li>
<li>Commands are always ran from the Websocket process and may return errors to the UI</li>
<li>Client doesn't really need to wait for completion, an event will arrive instead</li>
</ul>
</div>


<div class="slide">
<h1>Identifying frames</h1>
<ul>
<li>All frames, regardless of origin, have one common key <code>t</code></li>
<li>This is the type of the frame</li>
<li>Events are of type <code>data</code></li>
<li>Each <code>data</code> frame also has a name in key <code>n</code> and actual data in key <code>d</code></li>
</ul>
</div>


<div class="slide">
<h1>Dynamic JS event handlers</h1>
<ul>
<li>The JS code registers handlers for each <code>t</code></li>
<li>But also for each type of data <code>n</code></li>
<li>Custom handlers can be registered easily</li>
</ul>
</div>


<div class="slide">
<h1>Receiving events from JS</h1>
<ul>
<li>Receive Websocket frame</li>
<li>For each event, find the handler for <code>t</code> and pass it the event</li>
<li>If the event is <code>data</code>, find the handler for <code>n</code> and pass it the data <code>d</code></li>
<li>Let D3.js magically process this data and update the page</li>
</ul>
</div>


<div class="slide">
<h1>Specially crafted HTML...</h1>
<div><script type="syntaxhighlighter" class="brush: html"><![CDATA[
<table id="nodes-table" class="table table-striped">
<thead>
	<tr>
		<th data-name="name">Name</th>
		<th data-name="release">Release</th>
		<th data-name="alien_started">Alien started?</th>
		<th data-name="shaman_enabled">Shaman enabled?</th>
	</tr>
</thead>
<tbody></tbody>
</table>
]]></script></div>
</div>


<div class="slide">
<h1>Plus a simple mapping function make...</h1>
<div><script type="syntaxhighlighter" class="brush: js"><![CDATA[
var selectData = function(selector, data){
	var ret = [];

	d3.selectAll(selector).each(function(){
		var name = $(this).attr("data-name");
		ret.push({n: name, v: data[name]});
	});

	return ret;
}
]]></script></div>
</div>


<div class="slide">
<h1>A developer really bored</h1>
<ul>
<li>No specific code needed to update pages</li>
<li>Rows are created automatically</li>
<li>Cells get the expected data values automatically</li>
<li>Boring means good!</li>
</ul>
</div>


<div class="slide">
<h1>This is truly powerful</h1>
</div>


<div class="slide">
<h1>I mean, seriously, powerful</h1>
<ul>
<li>Adding new information is a two step process</li>
<li>Modify Erlang code to send more data</li>
<li>Write a little HTML where values will be displayed</li>
<li>No Javascript!</li>
<li>*applause*</li>
</ul>
</div>


<div class="slide">
<h1>And this is just the beginning</h1>
<ul>
<li>What about client-side calculations?</li>
<li>Data that is shown in different places?</li>
<li>And graphs? Show me pretty colors!</li>
</ul>
</div>


<div class="slide">
<h1>Client-side calculations</h1>
</div>


<div class="slide">
<h1>Make the client do the work</h1>
<ul>
<li>Avoid doing calculation in the Erlang node</li>
<li>Only do if it means sending a lot less data</li>
<li>Let the client perform the calculation</li>
<li>Example: difference of a value between T and T+1</li>
</ul>
</div>


<div class="slide">
<h1>Tricks required</h1>
<ul>
<li>D3.js helps very little here</li>
<li>It's still possible by accessing the data directly in the HTML elements</li>
<li>Accessing the data like this is documented, so it's still good</li>
</ul>
</div>


<div class="slide">
<h1>Update __data__ in the DOM</h1>
<div><script type="syntaxhighlighter" class="brush: js"><![CDATA[
var cells = rows.selectAll("td").data(function(d){
	var new_reds = 0;
	if (this.length && this[2].__data__){
		new_reds = d.reds - this[2].__data__.v;
		this[0].parentElement.__data__.new_reds = new_reds;
	}

	return selectData("#processes-table>thead>tr>th", d);
});
]]></script></div>
</div>


<div class="slide">
<h1>Multiple destinations</h1>
</div>


<div class="slide">
<h1>Use case: modals</h1>
<ul>
<li>I have a table with general information about processes</li>
<li>I want to be able to click a row and display a modal with extra data</li>
<li>Problem: data is tied to the rows</li>
</ul>
</div>


<div class="slide">
<h1>Modal solution</h1>
<ul>
<li>Creating/updating HTML for every possible modal is madness</li>
<li>We need to retrieve data directly from the row elements and assign that to the modal</li>
<li>That operation needs to be done each update</li>
</ul>
</div>


<div class="slide">
<h1>Retrieving data</h1>
<div><script type="syntaxhighlighter" class="brush: js"><![CDATA[
var md = d3.selectAll('#processes-table>tbody>tr')
	.filter(function(d){return d.pid == self.modalPid})
	[0][0].__data__;
]]></script></div>
</div>


<div class="slide">
<h1>Assigning data</h1>
<div><script type="syntaxhighlighter" class="brush: js"><![CDATA[
var vals = d3.selectAll("#processes-modal .data")
	.data(function(){
		return selectData("#processes-modal .data", md);
	});
vals.text(function(d){return d.v});
]]></script></div>
</div>


<div class="slide">
<h1>Graphs</h1>
</div>


<div class="slide">
<h1>Accumulate data</h1>
<ul>
<li>Graphs typically show data across a period of time</li>
<li>We need to accumulate data</li>
<li>Everytime we get an event we push that data to the graph</li>
<li>And then update the graph</li>
</ul>
</div>


<div class="slide">
<h1>Simplistic graph example</h1>
<div><script type="syntaxhighlighter" class="brush: js"><![CDATA[
// Keep the last 60 new_reds and update the graph.
self.modalReds.push(md.new_reds);
self.modalReds = self.modalReds.slice(-60);
self.modalRedsHistGraph.update(self.modalReds);
]]></script></div>
</div>


<div class="slide">
<h1>Library of graphs</h1>
<ul>
<li>Graphs can have a very similar interface regardless of how they display data</li>
<li>We can have a library of reusable graphs</li>
<li>We can even give the option to change the type of graph displayed in one click</li>
<li>Still a work in progress</li>
</ul>
</div>


<div class="slide">
<h1>Future of Alien Shaman</h1>
</div>


<div class="slide">
<h1>To start</h1>
<ul>
<li>Replicate Bigwig/Observer functionality</li>
<li>We need to see processes, applications, etc.</li>
<li>We want some general view of all the nodes (example: memory usage)</li>
<li>We want a common interface for receiving and displaying logs</li>
</ul>
</div>


<div class="slide">
<h1>Tracing</h1>
<ul>
<li>We want to be able to trace systems</li>
<li>We want to be able to trace not just one node, but follow messages across nodes</li>
<li>Thankfully Erlang comes with distributed tracing tools!</li>
<li>(Phew!)</li>
</ul>
</div>


<div class="slide">
<h1>Attach to any node</h1>
<ul>
<li>We want to be able to use probes even on systems without Alien loaded</li>
<li>That means we'll need to send the whole application using distribution and start it</li>
<li>It also means that for this operation we need to have the Erlang distribution running, it's not required once configured though</li>
</ul>
</div>


<div class="slide">
<h1>And the most important part</h1>
<ul>
<li>Get a nice logo!</li>
<li>Make it pretty!</li>
<li>Use special effects!</li>
</ul>
</div>


<div class="slide">
<h1>I think we're done</h1>
</div>


<div class="slide">
<h1>Questions?</h1>
<ul>
<li><a href="http://ninenines.eu">http://ninenines.eu</a></li>
<li>Twitter: @lhoguin</li>
<li>IRC: #ninenines on Freenode</li>
</ul>
</div>


</div>

<script type="text/javascript">SyntaxHighlighter.all();</script>

</body>
</html>