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.

Bartosz Sypytkowski

August 03, 2022
Tweet

More Decks by Bartosz Sypytkowski

Other Decks in Programming

Transcript

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

    View Slide

  2. INTRODUCTION
    Bartosz Sypytkowski
    @Horusiath
    [email protected]
    bartoszsypytkowski.com

    View Slide

  3. — When do we need CRDTs
    — YATA: conflict resolution for arrays and maps
    — Update merge & split
    — Optimizations
    AGENDA

    View Slide

  4. YJS.DEV

    View Slide

  5. PROBLEM STATEMENT
    COLLABORATIVE TEXT EDITOR

    View Slide

  6. COLLABORATIVE
    TEXT EDITOR
    Alice
    a
    Carol
    a
    Bob
    a

    View Slide

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

    View Slide

  8. COLLABORATIVE
    TEXT EDITOR
    Alice
    a
    Carol
    ac
    Bob
    ab

    View Slide

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

    View Slide

  10. COLLABORATIVE
    TEXT EDITOR
    Alice
    abc
    Carol
    abc
    Bob
    acb

    View Slide

  11. MAYBE JUST SYNCHRONIZE VIA CENTRAL SERVER?

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  17. SERVER-DRIVEN
    TEXT EDITOR
    Alice
    acb
    Carol
    acb
    Bob
    acb
    server
    acb

    View Slide

  18. COMPAR
    ING
    VECTOR
    CLOCKS
    ISSUES
    1. Latency
    2. Online-only / Network issues
    3. Characters interleaving
    4. Server: bottleneck & point of failure

    View Slide

  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

    View Slide

  20. CONFLICT AVOIDANCE
    “Let the majority decide on
    the correct order.”
    Decisions are made by
    quorum

    View Slide

  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

    View Slide

  22. CONFLICT RESOLUTION
    IT’S ALL ABOUT PRESERVING THE ORIGINAL INTENT

    View Slide

  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

    View Slide

  24. DON’T USE INDEXES FOR POSITIONING
    THEIR MEANING CHANGES OVER TIME

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  29. CONFLICT RESOLUTION

    View Slide

  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

    View Slide

  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

    View Slide

  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
    ?

    View Slide

  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

    View Slide

  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

    View Slide

  35. YATA FOR MAPS

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  41. MOVING ELEMENTS

    View Slide

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

    View Slide

  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

    View Slide

  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)

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  51. HOW ARE WE GOING TO READ THAT?

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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”

    View Slide

  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”

    View Slide

  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

    View Slide

  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”

    View Slide

  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”

    View Slide

  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

    View Slide

  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”

    View Slide

  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”

    View Slide

  63. OPTIMIZATIONS
    1. KNOW YOUR ENVIRONMENT

    View Slide

  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

    View Slide

  65. OPTIMIZATIONS
    2. REDUCE NUMBER OF OPERATIONS

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  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

    View Slide

  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’))

    View Slide

  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

    View Slide

  73. WHAT IF WE WANT TO INSERT ELEMENT WITHIN BLOCK?

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  77. OPTIMIZATIONS
    3. IMPROVE BLOCK INSERTION TIME

    View Slide

  78. PREFER SEQUENTIAL ACCESS WHENEVER POSSIBLE

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  82. IT’S ALSO BETTER FOR SYNCHRONIZING DATA

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  89. OPTIMIZATIONS
    4. INSERTING ELEMENTS CONSECUTIVELY

    View Slide

  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”);

    View Slide

  91. MAPPING BETWEEN USER INDEX AND BLOCK ID
    INTRODUCES COST

    View Slide

  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”);

    View Slide

  93. SUMMARY

    View Slide

  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

    View Slide

  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

    View Slide

  96. THANK YOU

    View Slide