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

YATA: collaborative documents and how to make them fast

YATA: collaborative documents and how to make them fast

Slides made for presentation with the same title, made live at Lambda Days conference in July 2022. It discusses technical details behind YATA: a conflict resolution algorithm for rich text CRDT documents used in Yrs/Yrs libraries.

009174d68aa95ae39054afa749013f78?s=128

Bartosz Sypytkowski

August 03, 2022
Tweet

Other Decks in Programming

Transcript

  1. YATA: COLLABORATIVE DOCS AND HOW TO MAKE THEM FAST

  2. INTRODUCTION Bartosz Sypytkowski @Horusiath b.sypytkowski@gmail.com bartoszsypytkowski.com

  3. — When do we need CRDTs — YATA: conflict resolution

    for arrays and maps — Update merge & split — Optimizations AGENDA
  4. YJS.DEV

  5. PROBLEM STATEMENT COLLABORATIVE TEXT EDITOR

  6. COLLABORATIVE TEXT EDITOR Alice a Carol a Bob a

  7. COLLABORATIVE TEXT EDITOR Alice a Carol a Bob a insert(1,

    ‘b’) insert(1, ‘c’)
  8. COLLABORATIVE TEXT EDITOR Alice a Carol ac Bob ab

  9. COLLABORATIVE TEXT EDITOR Alice a Carol ac Bob ab insert(1,

    ‘b’) insert(1, ‘c’) insert(1, ‘b’) insert(1, ‘c’)
  10. COLLABORATIVE TEXT EDITOR Alice abc Carol abc Bob acb

  11. MAYBE JUST SYNCHRONIZE VIA CENTRAL SERVER?

  12. SERVER-DRIVEN TEXT EDITOR Alice a Carol a Bob a server

    a
  13. SERVER-DRIVEN TEXT EDITOR Alice a Carol a Bob a server

    a insert(1, ‘b’) insert(1, ‘c’)
  14. SERVER-DRIVEN TEXT EDITOR Alice a Carol a Bob a server

    a insert(1, ‘b’) insert(1, ‘c’) insert(1, ‘b’) insert(1, ‘c’) E1 E2
  15. SERVER-DRIVEN TEXT EDITOR Alice a Carol a Bob a server

    ab insert(1, ‘b’) insert(1, ‘b’) E2 insert(1, ‘b’)
  16. SERVER-DRIVEN TEXT EDITOR Alice ab Carol ab Bob ab server

    acb insert(1, ‘c’) insert(1, ‘c’) insert(1, ‘c’)
  17. SERVER-DRIVEN TEXT EDITOR Alice acb Carol acb Bob acb server

    acb
  18. COMPAR ING VECTOR CLOCKS ISSUES 1. Latency 2. Online-only /

    Network issues 3. Characters interleaving 4. Server: bottleneck & point of failure
  19. FIELDS SHARING SIMILAR PROBLEMS 1. Collaborative text editors 2. Cross-continental

    data replication 3. Vehicle apps 4. Remote areas 5. One-account/multi-device sync
  20. CONFLICT AVOIDANCE “Let the majority decide on the correct order.”

    Decisions are made by quorum
  21. CONFLICT AVOIDANCE CONFLICT RESOLUTION “Let the majority decide on the

    correct order.” “Given enough context everyone should come to the same conclusion.” Decisions are made by quorum Decisions are made individually
  22. CONFLICT RESOLUTION IT’S ALL ABOUT PRESERVING THE ORIGINAL INTENT

  23. SEMANTICS MATTER 1. Insert characters one after another 2. Fixing

    a typo 3. Move an element 4. Move a range of elements 1. Insert characters at positions X, X+1, X+2, etc. 2. Insert/remove character at position X 3. Remove an element then re-insert it 4. Delete then re-insert range of elements
  24. DON’T USE INDEXES FOR POSITIONING THEIR MEANING CHANGES OVER TIME

  25. LAMPORT CLOCK A:1 Unique peer ID Sequence Number

  26. CONTEXT-AWARE INSERTION insert_between(A:1, B:1, (B:2, ‘b’))

  27. CONTEXT-AWARE INSERTION insert_between(A:1, B:1, (B:2, ‘b’)) inserted block ID inserted

    block content
  28. CONTEXT-AWARE INSERTION insert_between(A:1, B:1, (B:2, ‘b’)) left neighbor right neighbor

    inserted block ID inserted block content
  29. CONFLICT RESOLUTION

  30. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING CONFLICT RESOLUTION
  31. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING CONFLICT RESOLUTION
  32. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING CONFLICT RESOLUTION ?
  33. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING CONFLICT RESOLUTION Use block IDs to skip over blocks with lower precedence
  34. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING CONFLICT RESOLUTION
  35. YATA FOR MAPS

  36. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” JSON Document

    state YMap “key” A:1 ENTRY INSERTION YMAP
  37. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” JSON Document

    state YMap “key” A:1 ENTRY INSERTION YMAP ymap.set(‘key’, ‘b’)
  38. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” JSON Document

    state YMap “key” A:1 ENTRY INSERTION YMAP ymap.set(‘key’, ‘b’) A:2 ID A:1 LEFT NULL RIGHT CONTENT “b” JSON Create new block representing insert operation
  39. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” JSON Document

    state YMap “key” A:2 ENTRY INSERTION YMAP ymap.set(‘key’, ‘b’) A:2 ID A:1 LEFT NULL RIGHT CONTENT “b” JSON Insert block at the end of “key”’s sequence of values
  40. A:1 ID NULL LEFT NULL RIGHT CONTENT length = 1

    DELETED Document state YMap “key” A:2 ENTRY INSERTION YMAP ymap.set(‘key’, ‘b’) A:2 ID A:1 LEFT NULL RIGHT CONTENT “b” JSON Tombstone all blocks for “key”’s values except the latest one
  41. MOVING ELEMENTS

  42. WHY DON’T JUST USE REMOVE + INSERT?

  43. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING MOVING ELEMENTS
  44. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING MOVING ELEMENTS doc.move(1..2, 0)
  45. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING MOVING ELEMENTS doc.move(1..2, 0)
  46. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING MOVING ELEMENTS doc.move(1..2, 0) A:3 ID NULL LEFT A:1 RIGHT CONTENT (A:2..B:2) MOVED Create new block representing move operation
  47. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING MOVING ELEMENTS doc.move(1..2, 0) A:3 ID NULL LEFT A:1 RIGHT CONTENT (A:2..B:2) MOVED Range 1..2 maps onto continuous sequence of blocks from A:2 to B:2
  48. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING MOVING ELEMENTS doc.move(1..2, 0) A:3 ID NULL LEFT A:1 RIGHT CONTENT (A:2..B:2) MOVED Destination index 0 suggests to insert this block before A:1
  49. A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING B:1

    ID A:1 LEFT NULL RIGHT CONTENT “d” STRING Document state B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 ID NULL LEFT A:1 RIGHT CONTENT (A:2..B:2) MOVED MOVING ELEMENTS
  50. Document state MOVING ELEMENTS doc.move(1..2, 0) A:3 ID NULL LEFT

    A:1 RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED Mark moved elements
  51. HOW ARE WE GOING TO READ THAT?

  52. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR
  53. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack A:3 A:2..B:2 Move frame informs if we’re currently within moved range context
  54. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack A:3 A:2..B:2 Jump to the beginning of moved range
  55. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack A:3 A:2..B:2 “b”
  56. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack A:3 A:2..B:2 “b” “c”
  57. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack A:3 A:2..B:2 “b” “c” We reached the end of a current move frame
  58. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack “b” “c”
  59. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack “b” “c” “a”
  60. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR Move stack “b” “c” “a” Skip over moved blocks that aren’t part of a current move frame
  61. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR “b” “c” “a”
  62. READING MOVED ELEMENTS Document state A:3 ID NULL LEFT A:1

    RIGHT CONTENT (A:2..B:2) MOVED NULL MOVED A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING NULL MOVED A:2 ID A:1 LEFT B:1 RIGHT CONTENT “b” STRING A:3 MOVED B:2 ID A:1 LEFT B:1 RIGHT CONTENT “c” STRING A:3 MOVED B:1 ID A:1 LEFT NULL RIGHT CONTENT “d” STRING NULL MOVED ITERATOR “b” “c” “a” “d”
  63. OPTIMIZATIONS 1. KNOW YOUR ENVIRONMENT

  64. KNOW THE DIFFERENCE Peer to Peer Client / Server Examples

    Yjs/Yrs, Automerge RiakDB, AntidoteDB, DynamoDB Ops / sec. few* (related to single user activity) > 1000 ops / sec Collaborators unknown, limited control known, under full control Network / connections heterogenous, unreliable Homogenous, fairly stable Data volume fits in memory (hopefully) greater than disk
  65. OPTIMIZATIONS 2. REDUCE NUMBER OF OPERATIONS

  66. OPTIMIZATIONS BLOCK MERGING A:1 ID NULL LEFT NULL RIGHT CONTENT

    “a” STRING A:2 ID A:1 LEFT NULL RIGHT CONTENT “b” STRING Document state
  67. OPTIMIZATIONS BLOCK MERGING A:1 ID NULL LEFT NULL RIGHT CONTENT

    “a” STRING A:2 ID A:1 LEFT NULL RIGHT CONTENT “b” STRING Document state Both blocks have sequential IDs
  68. OPTIMIZATIONS BLOCK MERGING A:1 ID NULL LEFT NULL RIGHT CONTENT

    “a” STRING A:2 ID A:1 LEFT NULL RIGHT CONTENT “b” STRING Document state Block was intended to be placed sequentially
  69. OPTIMIZATIONS BLOCK MERGING A:1 ID NULL LEFT NULL RIGHT CONTENT

    “ab” STRING Document state Block A:1 is responsible for holding 2 elements now (range from A:1 to A:2)
  70. OPTIMIZATIONS BLOCK MERGING A:1 ID NULL LEFT NULL RIGHT CONTENT

    “ab” STRING Document state A:1 ID NULL LEFT NULL RIGHT CONTENT “a” STRING A:2 ID A:1 LEFT NULL RIGHT CONTENT “b” STRING Document state These two representations are logically equivalent
  71. OPTIMIZATIONS BLOCK MERGING A:1 ID NULL LEFT NULL RIGHT CONTENT

    “ab” STRING A:3 ID A:2 LEFT NULL RIGHT CONTENT “c” STRING Document state Next block ID = last block ID + last block length insert_between(A:2, NULL, (A:3, ‘c’))
  72. OPTIMIZATIONS BLOCK MERGING A:1 ID NULL LEFT NULL RIGHT CONTENT

    “ab” STRING A:3 ID A:2 LEFT NULL RIGHT CONTENT “c” STRING Document state Next block ID = last block ID + last block length insert_between(A:2, NULL, (A:3, ‘c’)) Left neighbor point to last ID not block ID
  73. WHAT IF WE WANT TO INSERT ELEMENT WITHIN BLOCK?

  74. A:1 ID NULL LEFT NULL RIGHT CONTENT “helo” STRING A:5

    ID A:3 LEFT A:4 RIGHT CONTENT “l” STRING Document state insert_between(A:3, A:4, (A:5, ‘l’)) OPTIMIZATIONS BLOCK SPLITTING
  75. A:4 ID A:3 LEFT NULL RIGHT CONTENT “o” STRING A:5

    ID A:3 LEFT A:4 RIGHT CONTENT “l” STRING Document state insert_between(A:3, A:4, (A:5, ‘l’)) OPTIMIZATIONS BLOCK SPLITTING A:1 ID NULL LEFT NULL RIGHT CONTENT “hel” STRING Split blocks to create space
  76. A:4 ID A:3 LEFT NULL RIGHT CONTENT “o” STRING A:5

    ID A:3 LEFT A:4 RIGHT CONTENT “l” STRING Document state OPTIMIZATIONS BLOCK SPLITTING A:1 ID NULL LEFT NULL RIGHT CONTENT “hel” STRING
  77. OPTIMIZATIONS 3. IMPROVE BLOCK INSERTION TIME

  78. PREFER SEQUENTIAL ACCESS WHENEVER POSSIBLE

  79. DOCUMENT BLOCK STRUCTURE UNDER THE HOOD A B C Clients

    Block store A:1 A:2 A:3 B:1 B:2 C:1 C:2 C:3 Client block list Root types “name” BRANCH START Pointer to a CRDT list head
  80. DOCUMENT BLOCK STRUCTURE UNDER THE HOOD A B C Clients

    Block store A:1 A:2 A:3 B:1 B:2 C:1 C:2 C:3 Client block list Root types “name” BRANCH START Pointer to a CRDT list head New operation is always appended to the end
  81. DOCUMENT BLOCK STRUCTURE UNDER THE HOOD A B C Clients

    Block store A:1 A:2 A:3 B:1 B:2 C:1 C:2 C:3 Client block list Root types “name” BRANCH START Pointer to a CRDT list head Finding block by ID (e.g. C:2) is done by binary search
  82. IT’S ALSO BETTER FOR SYNCHRONIZING DATA

  83. DELTA REPLICATION A B C Block store A:1 A:2 A:3

    B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START A B C Block store A:1 B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START Alice Bob
  84. DELTA REPLICATION A B C Block store A:1 A:2 A:3

    B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START A B C Block store A:1 B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START Alice Bob Bob is missing some of the updates from Alice
  85. DELTA REPLICATION A B C Block store A:1 A:2 A:3

    B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START A B C Block store A:1 B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START Alice Bob Bob creates a vector clock of his most recent updates A:2 B:3 C:4
  86. DELTA REPLICATION A B C Block store A:1 A:2 A:3

    B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START A B C Block store A:1 B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START Alice Bob Alice compares Bob’s vector clock against her own known state A:2 B:3 C:4
  87. DELTA REPLICATION A B C Block store A:1 A:2 A:3

    B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START A B C Block store A:1 B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START Alice Bob Alice produces a delta with blocks that Bob is missing A A:2 A:3
  88. DELTA REPLICATION A B C Block store A:1 A:2 A:3

    B:1 B:2 C:1 C:2 C:3 Root types “name” BRANCH START Block store Root types “name” BRANCH START Alice Bob Bob applies incoming updates on his side A A:2 A:3 A B C A:1 A:2 A:3 B:1 B:2 C:1 C:2 C:3
  89. OPTIMIZATIONS 4. INSERTING ELEMENTS CONSECUTIVELY

  90. OPTIMIZATIONS CACHING LATEST POSITION let doc = Doc::new(); let txt

    = doc.transact().get_text(“text”); txt.insert(&mut doc.transact(), 0, “hello”); txt.insert(&mut doc.transact(), 5, “world”);
  91. MAPPING BETWEEN USER INDEX AND BLOCK ID INTRODUCES COST

  92. OPTIMIZATIONS CURSORS let doc = Doc::new(); let txt = doc.transact().get_text(“text”);

    let mut cursor = txt.seek(0); cursor.insert(&mut doc.transact(), “hello”); cursor.insert(&mut doc.transact(), “world”);
  93. SUMMARY

  94. — Yjs/Yrs: https://crates.io/crates/yrs — Automerge: https://automerge.org/ — Ditto.live: https://www.ditto.live/ —

    RiakDB: https://riak.com/ — Amazon DynamoDB: https://aws.amazon.com/dynamodb/ — AntidoteDB: https://www.antidotedb.eu/ CRDT PROJECTS
  95. — CRDTs deep dive: https://bartoszsypytkowski.com/tag/crdt/ — List of aggregated CRDT

    articles: https://crdt.tech — Making CRDTs faster: https://josephg.com/blog/crdts-go-brrr/ REFERENCES
  96. THANK YOU