-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
2524 lines (2472 loc) · 113 KB
/
index.html
File metadata and controls
2524 lines (2472 loc) · 113 KB
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
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- BEGIN Info -->
<meta name="description"
content="Pennant - A cloud-based collaborative computational notebook that brings note-taking, code execution and real-time collaboration to a single platform." />
<meta name="title" property="og:title" content="pennant" />
<meta property="og:type" content="Website" />
<meta name="image" property="og:image" content="images/thumb.png" />
<meta name="description" property="og:description"
content="Pennant - A cloud-based collaborative computational notebook that brings note-taking, code execution and real-time collaboration to a single platform.." />
<meta name="author" content="Pennant" />
<!-- END Info -->
<script defer data-domain="trypennant.com" src="https://plausible.io/js/plausible.js"></script>
<!-- BEGIN favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="images/favicon/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="images/favicon/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="images/favicon/favicon-16x16.png" />
<link rel="manifest" href="images/favicon/site.webmanifest" />
<link rel="mask-icon" href="images/favicon/safari-pinned-tab.svg" color="#5bbad5" />
<link rel="shortcut icon" href="images/favicon/favicon.ico" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="msapplication-config" content="images/favicon/browserconfig.xml" />
<meta name="theme-color" content="#ffffff" />
<!-- END favicon -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pennant</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" />
<link rel="stylesheet" href="https://unpkg.com/@tailwindcss/typography@0.2.x/dist/typography.min.css" />
<link rel="stylesheet" href="stylesheets/reset.css" />
<link rel="stylesheet" href="stylesheets/style.css" />
<link rel="stylesheet" href="stylesheets/responsive.css" />
</head>
<body>
<header class="mobile-menu-closed">
<div id="header">
<a href="/">
<img src="images/logo/logo-name-horizontal-purple.png" />
</a>
<nav>
<a href="#start-here" class="selected">Start Here</a>
<a href="#case-study">Case Study</a>
<a href="#presentation">Presentation</a>
<a href="#our-team">Our Team</a>
<a href="https://www.trypennant.com/">Notebook</a>
<!-- <a
href="http://pennant-demo.s3.us-east-2.amazonaws.com/chat-demo/index.html"
target="_blank"
>Demo</a
> -->
<!-- <a href="https://trypennant.com/@trypennant/7c776af1-ebfe-4559-9a5d-b785e5070dab" target="_blank">Docs</a> -->
<a href="https://github.com/pennant-notebook" target="_blank" class="icon"><i class="fab fa-github"></i></a>
</nav>
<div id="menu">
<button type="button">
<svg id="mobile-open" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<svg id="mobile-close" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<div id="header-buffer"></div>
<div id="mobile-menu">
<a href="#start-here" class="selected">Start Here</a>
<a href="#case-study">Case Study</a>
<a href="#presentation">Presentation</a>
<a href="#our-team">Our Team</a>
<a href="https://www.trypennant.com/">Notebook</a>
<!-- <a
href="http://pennant-demo.s3.us-east-2.amazonaws.com/chat-demo/index.html"
target="_blank"
>Demo</a
> -->
<!-- <a href="https://trypennant.com/@trypennant/7c776af1-ebfe-4559-9a5d-b785e5070dab" target="_blank">Docs</a> -->
<a href="https://github.com/pennant-realtime" target="_blank"><i class="fab fa-github"></i> GitHub</a>
</div>
</header>
<div id="start-here" class="main-section">
<div class="h-full">
<div class="static-logo-color"></div>
<div class="bg-blue">
<img class="pennant sm-screen lg:hidden" src="images/logo/logo-name-vertical.png" />
<!-- <img class="pennant lg:block xs:hidden" src="images/logo/pennant-name-white.png" /> -->
<p class="light-text">
A collaborative computational notebook that brings
note-taking, <span class="text-pink">code execution</span> and<br />
<span class="text-teal">real-time collaboration</span><br />
to a single platform.
</p>
<button class="try-pennant-container">
<a href="https://www.trypennant.com/" class="try-pennant-link">
<p class="try-pennant-text">
Try the Notebook
</p>
</a>
</button>
</div>
</div>
<div class="h-full">
<div class="bg-teal static-logo-teal-light">
<h2>Get started quickly</h2>
</div>
<div class="bg-teal">
<h2 class="sm-header">Cloud-based</h2>
<p>
As a cloud-based tool, Pennant requires no installation, allowing users to get
started quickly.
</p>
<video autoplay loop muted playsinline>
<source src="images/pennant-deploy-teal.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
</div>
<div class="h-full">
<div class="bg-pink static-logo-pink-light">
<h2>Notebook Style Execution</h2>
</div>
<div class="bg-pink">
<h2 class="sm-header">Empowers users to experiment with
their code</h2>
<p>
Pennant segments code into cells, enabling users
to run sections of code step-by-step, and displaying results directly below the executed cell.
</p>
<div style="width: 600px; height: 450px; overflow: hidden;">
<p>
<img width="600" height="500" src="images/notebook-pink-intro.gif"
style="position: relative; bottom: -50px;" />
</p>
</div>
</div>
</div>
</div>
<aside id="toc">
<ul type="none">
<!-- Section 1 -->
<li data-section="section-1" class="selected">
<a href="#section-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Pennant Overview</p>
</div>
</a>
</li>
<li data-section="section-1" class="subitem">
<a href="#section-1-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Computational Notebooks</p>
</div>
</a>
</li>
<li data-section="section-1" class="subitem">
<a href="#section-1-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>How Pennant is Used</p>
</div>
</a>
</li>
<!-- Section 2 -->
<li data-section="section-2">
<a href="#section-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Real-Time Collaboration (RTC)</p>
</div>
</a>
</li>
<li data-section="section-2" class="subitem">
<a href="#section-2-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Historical Model</p>
</div>
</a>
</li>
<li data-section="section-2-1" class="subitem">
<a href="#section-2-1-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Request response model</p>
</div>
</a>
</li>
<li data-section="section-2-1" class="subitem">
<a href="#section-2-1-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Overwriting and Lockouts</p>
</div>
</a>
</li>
<li data-section="section-2" class="subitem">
<a href="#section-2-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Collaborative Model</p>
</div>
</a>
</li>
<li data-section="section-2-2" class="subitem">
<a href="#section-2-2-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Streaming Model for Real-Time Synchronization</p>
</div>
</a>
</li>
<li data-section="section-2-2" class="subitem">
<a href="#section-2-2-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Requirements of the collaborative model</p>
</div>
</a>
</li>
<li data-section="section-2" class="subitem">
<a href="#section-2-3">
<div>
<div class="bullet">
<div></div>
</div>
<p>Challenges in Code Execution</p>
</div>
</a>
</li>
<li data-section="section-2-3" class="subitem">
<a href="#section-2-3-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>More flexible code execution</p>
</div>
</a>
</li>
<li data-section="section-2-3" class="subitem">
<a href="#section-2-3-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Safe code execution</p>
</div>
</a>
</li>
<li data-section="section-2-3" class="subitem">
<a href="#section-2-3-3">
<div>
<div class="bullet">
<div></div>
</div>
<p>Collaborative code execution</p>
</div>
</a>
</li>
<!-- Section 3 -->
<li data-section="section-3">
<a href="#section-3">
<div>
<div class="bullet">
<div></div>
</div>
<p>Implementing RTC</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Infrastructure needs</p>
</div>
</a>
</li>
<li data-section="section-3-1" class="subitem">
<a href="#section-3-1-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>P2P vs Client Server</p>
</div>
</a>
</li>
<li data-section="section-3-1" class="subitem">
<a href="#section-3-1-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>A pub/sub model</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Understanding the Problem</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-3">
<div>
<div class="bullet">
<div></div>
</div>
<p>Algorithms for state convergence</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-4">
<div>
<div class="bullet">
<div></div>
</div>
<p>Yjs: The Chosen CRDT</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-5">
<div>
<div class="bullet">
<div></div>
</div>
<p>The Mechanism Behind Yjs</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-6">
<div>
<div class="bullet">
<div></div>
</div>
<p>Conflict resolution in Yjs</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-7">
<div>
<div class="bullet">
<div></div>
</div>
<p>Editor Bindings</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-8">
<div>
<div class="bullet">
<div></div>
</div>
<p>Connection Providers</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-8-1" ->
<div>
<div class="bullet">
<div></div>
</div>
<p>Unique Challenges of a Multi-Cell Notebook</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-8-2" ->
<div>
<div class="bullet">
<div></div>
</div>
<p>Rooms</p>
</div>
</a>
</li>
<li data-section="section-3" class="subitem">
<a href="#section-3-9">
<div>
<div class="bullet">
<div></div>
</div>
<p>Making it all work</p>
</div>
</a>
</li>
<!-- Section 4 -->
<li data-section="section-4">
<a href="#section-4">
<div>
<div class="bullet">
<div></div>
</div>
<p>Backend system design for RTC</p>
</div>
</a>
</li>
<li data-section="section-4" class="subitem">
<a href="#section-4-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Challenges of stateful communication protocols</p>
</div>
</a>
</li>
<li data-section="section-4" class="subitem">
<a href="#section-4-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Implementing a stateful backend</p>
</div>
</a>
</li>
<li data-section="section-4" class="subitem">
<a href="#section-4-3">
<div>
<div class="bullet">
<div></div>
</div>
<p>Existing solutions</p>
</div>
</a>
</li>
<li data-section="section-4" class="subitem">
<a href="#section-4-4">
<div>
<div class="bullet">
<div></div>
</div>
<p>Our solution</p>
</div>
</a>
</li>
<!-- Section 5 -->
<li data-section="section-5">
<a href="#section-5">
<div>
<div class="bullet">
<div></div>
</div>
<p>Execution Engine</p>
</div>
</a>
</li>
<li data-section="section-5" class="subitem">
<a href="#section-5-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Running code through remotely hosted code editor</p>
</div>
</a>
</li>
<li data-section="section-5" class="subitem">
<a href="#section-5-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Why remote code execution engines use workers</p>
</div>
</a>
</li>
<li data-section="section-5" class="subitem">
<a href="#section-5-3">
<div>
<div class="bullet">
<div></div>
</div>
<p>Containerization for resource control</p>
</div>
</a>
</li>
<li data-section="section-5" class="subitem">
<a href="#section-5-4">
<div>
<div class="bullet">
<div></div>
</div>
<p>Sandboxing for security</p>
</div>
</a>
</li>
<!-- Section 6 -->
<li data-section="section-6">
<a href="#section-6">
<div>
<div class="bullet">
<div></div>
</div>
<p>Remote execution: Notebooks vs Code Editors</p>
</div>
</a>
</li>
<li data-section="section-6" class="subitem">
<a href="#section-6-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Long-lived vs Short-lived workers</p>
</div>
</a>
</li>
<li data-section="section-6" class="subitem">
<a href="#section-6-2">
<div>
<div class="bullet">
<div></div>
</div>
<p>Queues HOLB</p>
</div>
</a>
</li>
<li data-section="section-6" class="subitem">
<a href="#section-6-3">
<div>
<div class="bullet">
<div></div>
</div>
<p>Script Cleaning for Enabling Notebook-style Execution</p>
</div>
</a>
</li>
<li data-section="section-6" class="subitem">
<a href="#section-6-4">
<div>
<div class="bullet">
<div></div>
</div>
<p>Executing code in chunks</p>
</div>
</a>
</li>
<li data-section="section-7" class="subitem">
<a href="#section-7-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Tradeoffs of long-lived workers</p>
</div>
</a>
</li>
<li data-section="section-8" class="subitem">
<a href="#section-8-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Why we can’t allow users to run code on their own machines</p>
</div>
</a>
</li>
<!-- Section 7-->
<li data-section="section-9">
<a href="#section-9">
<div>
<div class="bullet">
<div></div>
</div>
<p>Final architecture</p>
</div>
</a>
</li>
<!-- Section 8 -->
<li data-section="section-10">
<a href="#section-10">
<div>
<div class="bullet">
<div></div>
</div>
<p>Next steps</p>
</div>
</a>
</li>
<li data-section="section-10" class="subitem">
<a href="#section-10-1">
<div>
<div class="bullet">
<div></div>
</div>
<p>Scaling</p>
</div>
</a>
</li>
<li data-section="section-11">
<a href="#section-11">
<div>
<div class="bullet">
<div></div>
</div>
<p>References</p>
</div>
</a>
</li>
</ul>
</aside>
<div id="case-study" class="main-section">
<div id="case-study-content">
<div class="prose">
<h1>Case Study</h1>
<!-- Section 1 -->
<h2 id="section-1">1.Pennant Overview</h2>
<p>Pennant is a collaborative computational notebook for web development students and
professionals that brings note-taking, code execution, and real-time collaboration to a
single
platform.</p>
<p>As a <strong>cloud-based</strong> tool, Pennant requires no installation, allowing users to get
started quickly and providing secure server-side code execution. And importantly, Pennant supports seamless real-time
collaboration as students and professionals become increasingly remote.</p>
<p><img src="images/11-opening-notebook-gif.gif" /></p>
<p>
This white paper will generally explore the technical challenges of computational notebooks, such
as code
execution and real-time user collaboration, our team’s specific architectural decisions to overcome those
challenges, and the resulting tradeoffs from those decisions. We’ll explore each of these
technical
choices in depth and our final architecture, but first, we’ll provide a brief background on the product domain.
</p>
<h3 id="section-1-1">1.1 Computational Notebooks</h3>
<p>Computational notebooks allow users to write, document, and execute code in a single tool. Unlike scripts,
<strong><em>Pennant segments code into cells</em></strong> that enable users to run sections of code
step-by-step,
displaying immediate, inline results directly below the executed cell. This empowers users to experiment with
their
code, and users can receive immediate feedback on how changing their code influences the
output.
</p>
<p><img src="images/111-computational-notebook.gif" /></p>
<p>Computational notebooks facilitate the creation of detailed notes through cells with markdown functionality.
Markdown
cells render text, images, and other elements to deliver rich and visually engaging documentation notes.
Consequently,
data scientists and researchers utilize computational notebooks as a primary tool for exploring datasets and
sharing
findings.</p>
<p>
Computational notebooks are also viable for learning. Students from diverse academic fields can learn how to
code with Jupyter Notebook with no prior experience. Instructors can create interactive lecture notes while
students can experiment with code snippets on the same notebook <a
href="#section-11">[1]</a>. Computational notebooks also circumvent
technical setup requirements that can be difficult for beginners, such as setting up development environments
<a href="#section-11">[2]</a>.
</p>
<h3 id="section-1-2">1.2 How Pennant is Used</h3>
<p>Pennant brings the educational benefits of using computational notebooks to web development students. Unlike
note-taking tools like Notion and Obsidian, a computational notebook allows users to read tutorials, take
notes,
<em>and</em> experiment with code in one place.
</p>
<p><strong>Pennant is more suited for learning JavaScript than existing computational
notebooks</strong>.
The quirks and idiosyncrasies of the language present a challenge when attempting to deliver accurate and
consistent variable
state across all cells and all edge cases.</p>
<p>Other web development notebooks require users to learn non-standard flavors of Javascript to account for the
difficulties of non-linear notebook-style code execution. For example Observable <a href="#section-11">[3]</a> chose an
implementation
tradeoff that simply bans all variable declarations to circumvent syntax errors.</p>
<p>In contrast, <strong><em>Pennant preserves standard JavaScript syntax while allowing
notebook-style code execution </em></strong> through our code execution engine optimizations. By featuring
vanilla Javascript, students can
use the
same language conventions they would anywhere else.</p>
<p><img src="images/112-vanilla-javascript-gif.gif" /></p>
<p><strong><em>Pennant notebooks allow seamless, real-time collaboration for up to 5 users per
notebook</em></strong>.
</p>
<p>User interactions, including cursor movements, text inputs, and highlighting, are visible in ‘real time’, and
updates
are automatically saved without conflicts.</p>
<p>Each notebook is paired with a dedicated, server-side execution engine, and <strong><em>all collaborators
have access
to code execution without any technical setup requirements</em></strong> (e.g., VS Code Live Share) or
additional
tools (like Coderpad).</p>
<p><img src="images/112-tryout-pennant-gif.gif" /></p>
<p><strong><em>You can try out Pennant <a href="https://www.trypennant.com">here</a>.</em></strong></p>
<!-- Section 2 -->
<h2 id="section-2">2. Real-time Collaboration</h2>
<p>The standard definition of real-time collaboration refers to the capability allowing multiple individuals to
simultaneously work on a shared piece of data, whether it's a rich text document, a presentation, or simple
task
descriptions.</p>
<p>While many applications tout real-time features, not all demand collaborative editing. For instance, a live
news feed
updates in real-time but doesn't involve mutual data contribution. Moreover, certain write-intensive apps
limit
simultaneous editing or use mechanisms like 'locks' to avert conflicts.</p>
<p>Most real-time collaboration applications have similar technical challenges involved in their architecture.
In the
following subsections, we’ll explore some of those challenges in the context of collaborative computational
notebooks
along with the tradeoffs that influenced Pennant’s technical decisions. Following our exploration of
challenges tied
to code execution within these notebooks, we'll delve into Pennant’s overarching architecture.</p>
<p><strong><em>Real-time collaboration presents many specific challenges such as:</em></strong></p>
<ul type="none">
<li>Sharing state across all users with minimal latency.</li>
<li>Resolving conflicting state updates from multiple users</li>
<li>Broadcasting presence and awareness data in “real-time”</li>
</ul>
<p>In the next section, we will discuss the workflows that existed before collaborative applications and the
problems
that led to the emergence of the collaborative model.</p>
<h3 id="section-2-1">2.1 The historical model</h3>
<h4 id="section-2-1-1">2.1.1 Request response model</h4>
<p>Before the rise of collaborative applications, documents resided in shared data sources (e.g., shared hard
drive,
server, or database). All users collaborating on a document should reasonably expect the document on the data
store to
be the single source of truth for its current state. Users request the document and create a local copy on
their
machine. The user modifies the local copy and, at an undetermined point in the future, saves that local copy
back to
the shared data source.</p>
<h4 id="section-2-1-2">2.1.2 Overwriting and Lockouts</h4>
<p>The historical model, in its primitive form, made it easy for document updates to be unintentionally
overwritten.
Imagine multiple users each working on their local copies of a shared document. As one user saves their local
copy
back to the central store, they might inadvertently overwrite another user's recently saved changes. This is
visually
depicted below, where the left user's save action erases the updates made by the user on the right.</p>
<p><img src="images/212-overwrites.gif" />
</p>
<p>To mitigate such overwrite issues, many systems employed a "lockout" mechanism. In this approach, once User A
accesses a document, the central store locks it, ensuring its consistency. Any subsequent access requests, say
from
User B, are denied until User A's lock is released. While this approach guarantees the document's strong
consistency
in storage, it significantly hamstrings real-time collaboration, as multiple users can't simultaneously edit
the
document.</p>
<p><img src="images/212-lockouts.gif" />
</p>
<h3 id="section-2-2">2.2 The collaborative model</h3>
<h4 id="section-2-2-1">2.2.1 Streaming Model for Real-Time Synchronization</h4>
<p>The collaborative model is a departure from the historical model's constraints. Unlike the historical model,
where
overwrite issues often led to the implementation of a "lockout" mechanism, the collaborative model promotes
uninterrupted collaboration. It ensures real-time synchronization across user versions, effectively
eliminating the
possibility of interaction with stale states.</p>
<p><img src="images/221-streaming-model.gif" />
</p>
<p>It emphasizes quick, consistent updates, often applied at every keystroke or word completion. While updates
can occur
at longer intervals to save computational resources, the goal is to converge all client versions to a unified
state
with each document update.</p>
<p>However, achieving this real-time collaboration brings challenges such as latency, network reliability, and
the need
for efficient mechanisms to handle concurrent updates.</p>
<h4 id="section-2-2-2">2.2.2 Requirements of the collaborative model</h4>
<p>To fully harness the potential of the collaborative model, it's essential to understand and cater to its
unique data
requirements:</p>
<p><strong>A.</strong> <strong>Real-time latency</strong></p>
<p>Pennant is text based and must be performant in a way that users expect. We need to ensure that our latency
in
updating states aligns with other well-known collaborative products like Google Docs.</p>
<p><strong>B.</strong> <strong>Conflict resolution and eventual consistency</strong></p>
<p>All users must eventually see the same content on their browsers. Our conflict resolution must occur within
the
expected real-time latency window.</p>
<p><strong>C.</strong> <strong>Automatic updates</strong></p>
<p>As users collaborate, remote updates must be seamlessly merged on screen.</p>
<p><strong>D.</strong><strong> Presence & Awareness</strong></p>
<p>Users should be able to see who is changing what and where. The visual conventions are relatively well
established
for text-based applications: text highlighting, cursor highlighting and distinct icons for active users. The
data
streamed for awareness and presence is generally considered ephemeral and is not typically logged or stored to
disk.
</p>
<p><img src="images/222-presence-awareness.gif" />
</p>
<h3 id="section-2-3">2.3 Challenges in Code Execution</h3>
<p>
<strong><em>Code execution presents many technical challenges such as:</em></strong>
</p>
<ul type="none">
<li>
Allowing for flexible ‘notebook-style’ code execution
</li>
<li>
Securing client-submitted code execution and minimizing performance
costs of implementing these security measures
</li>
<li>
Designing for the unique challenges of running code collaboratively
</li>
</ul>
<p>
In this section, we will explore some technical challenges involved in
designing a performant code execution engine. We will first look at two
fundamentally different models of code execution along with their tradeoffs.
Then, we will look at how execution engines securely run code and the
performance costs of implementing safety measures. Finally, we will look at
the unique challenges of running code collaboratively.
</p>
<h4 id="section-2-3-1">2.3.1 More flexible code execution</h4>
<p>
There are two broad models of code execution, with one of them generic to
typical code execution and the other one specific to notebooks.
</p>
<p>
In the generic model, when code is executed, the code in a file runs from
top to bottom. If there are computationally heavy parts of the code, the
entire file needs to be run each time it is edited and tested. In some
cases, complex code (for example uploading a large database or running an AI
model) can run for hours or days or more.
</p>
<p>
<br />
<img src="images/231-editor-execution.gif" />
</p>
<p>
The original use case for notebooks was specifically to reduce the
computational load for researchers who wanted to load data once, and then
experiment with that data over time. Notebooks can save these users hours or
more of time.
</p>
<p>
However, notebooks save time even when playing with code without a database,
and often clarify code testing. Users can add new cases to test in new
cells. Users of notebooks hit play right on that cell they want to test, and
receive outputs right below that cell. This makes testing code easier and
more organized.
</p>
<p>
<img src="images/231-notebook-execution.gif" />
</p>
<p>
Generic code execution engines can be computationally intensive, but they
are less complex and also can save resources. This is because they do not
have to accumulate and store any of the previous code executions.
</p>
<p>
By contrast,
<strong>
<em>Notebooks give flexibility in code execution and allow easy visual
segmentation, but come with the tradeoffs of increased memory usage and
complexity of design</em>
</strong>
. This is because notebooks have to accumulate and store data on all of the
previous code executions.
</p>
<h4 id="section-2-3-2">2.3.2 Safe code execution</h4>
<p>
No matter which type of code execution engine you are building, the primary
concern is that running a client’s code is dangerous. There are many ways
in which untrusted code can pose a threat, either maliciously or by
accident. It is impossible to filter for every dangerous situation and
reduce the threat of running client code to zero.
</p>
<p>
For this reason,
<strong>
<em>most code execution engines implement sandboxing mechanisms,
creating an isolated environment inside a program from which untrusted
code cannot escape.</em>
</strong>
This minimizes the attack surface area and limits harmful effects on the
code base or sensitive user data.
</p>
<p>
<img src="images/232-sandbox.png" />
</p>
<p>
Isolation of user code can keep an app and its users safe, but it can be
complicated to implement and consumes a large amount of overhead memory and
space. In the past, this was the realm of specialists because its
implementation was specific to each operating system. To add to the
complexity, there were also layers of configuration that had to be completed
separately on each machine.
</p>
<p>
Due to advances in sandboxing and container technology, engineers no longer
need to configure individual machines, making sandboxing somewhat easier.
Containers are isolated processes that can be passed between different
operating systems with code inside. Users of containers can add sandboxing
mechanisms that apply to all containers on the network. While the isolation
of processes has become somewhat easier, it still requires a substantial
amount of additional memory and space. Especially if a sandbox is built
around a container, building these two layers of isolation can use more
memory than the code it holds.
</p>
<h4 id="section-2-3-3">1.3.3 Collaborative code execution</h4>
<p>
Unlike text-editor collaborations, where changes can be made on your version
and shipped to all other versions, <strong><em>code collaborations require all code to
be sent to and executed on one central location</em></strong>. This central bottleneck
actually benefits code execution engines, because it ensures that the users
share one history, one context in which they all run their code.
</p>
<p>
In coderpad and most code collaboration, code can be executed in a stateless
fashion, which means each time you hit play, the code can be executed
anywhere, on any server, regardless of where your teammates code has been
sent. Each code submission does not access any shared data from any
previous submissions. That is why in the below example, the second
user-submission has no access to the value of x from the first
user-submission.
</p>
<p>
<img src="images/233-stateless-execution.png" />
</p>
<p>
In Pennant and other notebooks, code execution is stateful. Each code
submission needs access to the context from previous executions.
</p>
<p>
This means all code from a notebook needs to be submitted to the same engine
instance.
</p>
<p>
<img src="images/233-state-execution.png" />
</p>
<p>
Why can’t changes in code execution context just be shipped from user to
user like document updates? Nobody does this because it is extremely
difficult, or impossible, to send execution context from user to user.
Therefore code execution needs to be centralized on a server.
</p>
<div align="center">
<hr size="0" width="100%" align="center" />
</div>
<!-- Section 3: Implementation -->
<h2 id="section-3">3: Implementation</h2>
<h3 id="section-3-1">3.1 Infrastructure needs</h3>
<p>
We’ve covered the general problems associated with real-time collaboration
and code execution and how they relate more specifically to computational
notebooks. Now we’ll cover the details of Pennant’s architectural decisions
to address those challenges and the technical tradeoffs made along the way.
In this section, we will explore the four core components of Pennant and how