Skip to content

Commit

Permalink
Introduce latestCreatedAtMapByActor for concurrent insertion and dele…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
sejongk committed Aug 15, 2023
1 parent 941975e commit 72d9715
Show file tree
Hide file tree
Showing 9 changed files with 571 additions and 292 deletions.
8 changes: 8 additions & 0 deletions api/converter/from_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,13 @@ func fromTreeEdit(pbTreeEdit *api.Operation_TreeEdit) (*operations.TreeEdit, err
return nil, err
}

createdAtMapByActor, err := fromCreatedAtMapByActor(
pbTreeEdit.CreatedAtMapByActor,
)
if err != nil {
return nil, err
}

nodes, err := FromTreeNodesWhenEdit(pbTreeEdit.Contents)
if err != nil {
return nil, err
Expand All @@ -500,6 +507,7 @@ func fromTreeEdit(pbTreeEdit *api.Operation_TreeEdit) (*operations.TreeEdit, err
parentCreatedAt,
from,
to,
createdAtMapByActor,
nodes,
executedAt,
), nil
Expand Down
11 changes: 6 additions & 5 deletions api/converter/to_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,12 @@ func toIncrease(increase *operations.Increase) (*api.Operation_Increase_, error)
func toTreeEdit(e *operations.TreeEdit) (*api.Operation_TreeEdit_, error) {
return &api.Operation_TreeEdit_{
TreeEdit: &api.Operation_TreeEdit{
ParentCreatedAt: ToTimeTicket(e.ParentCreatedAt()),
From: toTreePos(e.FromPos()),
To: toTreePos(e.ToPos()),
Contents: ToTreeNodesWhenEdit(e.Contents()),
ExecutedAt: ToTimeTicket(e.ExecutedAt()),
ParentCreatedAt: ToTimeTicket(e.ParentCreatedAt()),
From: toTreePos(e.FromPos()),
To: toTreePos(e.ToPos()),
CreatedAtMapByActor: toCreatedAtMapByActor(e.CreatedAtMapByActor()),
Contents: ToTreeNodesWhenEdit(e.Contents()),
ExecutedAt: ToTimeTicket(e.ExecutedAt()),
},
}, nil
}
Expand Down
529 changes: 356 additions & 173 deletions api/yorkie/v1/resources.pb.go

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions api/yorkie/v1/resources.proto
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@ message Operation {
TimeTicket parent_created_at = 1;
TreePos from = 2;
TreePos to = 3;
repeated TreeNodes contents = 4;
TimeTicket executed_at = 5;
map<string, TimeTicket> created_at_map_by_actor = 4;
repeated TreeNodes contents = 5;
TimeTicket executed_at = 6;
}
message TreeStyle {
TimeTicket parent_created_at = 1;
Expand Down
100 changes: 66 additions & 34 deletions pkg/document/crdt/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,17 @@ func (n *TreeNode) SplitElement(offset int) (*TreeNode, error) {
}

// remove marks the node as removed.
func (n *TreeNode) remove(removedAt *time.Ticket) {
func (n *TreeNode) remove(removedAt *time.Ticket, latestCreatedAt *time.Ticket) bool {
justRemoved := n.RemovedAt == nil
if n.RemovedAt == nil || n.RemovedAt.Compare(removedAt) > 0 {
if !n.Pos.CreatedAt.After(latestCreatedAt) &&
(n.RemovedAt == nil || n.RemovedAt.Compare(removedAt) > 0) {
n.RemovedAt = removedAt
if justRemoved {
n.IndexTreeNode.UpdateAncestorsSize()
}
return true
}

if justRemoved {
n.IndexTreeNode.UpdateAncestorsSize()
}
return false
}

// InsertAt inserts the given node at the given offset.
Expand Down Expand Up @@ -527,17 +529,21 @@ func (t *Tree) ToXML() string {

// EditByIndex edits the given range with the given value.
// This method uses indexes instead of a pair of TreePos for testing.
func (t *Tree) EditByIndex(start, end int, contents []*TreeNode, editedAt *time.Ticket) error {
func (t *Tree) EditByIndex(start, end int,
latestCreatedAtMapByActor map[string]*time.Ticket,
contents []*TreeNode,
editedAt *time.Ticket,
) (map[string]*time.Ticket, error) {
fromPos, err := t.FindPos(start)
if err != nil {
return err
return nil, err
}
toPos, err := t.FindPos(end)
if err != nil {
return err
return nil, err
}

return t.Edit(fromPos, toPos, contents, editedAt)
return t.Edit(fromPos, toPos, latestCreatedAtMapByActor, contents, editedAt)
}

// FindPos finds the position of the given index in the tree.
Expand Down Expand Up @@ -593,19 +599,23 @@ func (t *Tree) FindPos(offset int) (*TreePos, error) {

// Edit edits the tree with the given range and content.
// If the content is undefined, the range will be removed.
func (t *Tree) Edit(from, to *TreePos, contents []*TreeNode, editedAt *time.Ticket) error {
func (t *Tree) Edit(from, to *TreePos,
latestCreatedAtMapByActor map[string]*time.Ticket,
contents []*TreeNode,
editedAt *time.Ticket,
) (map[string]*time.Ticket, error) {
// 01. split text nodes at the given range if needed.
fromParent, fromLeft, err := t.findTreeNodesWithSplitText(from, editedAt)
if err != nil {
return err
return nil, err
}
toParent, toLeft, err := t.findTreeNodesWithSplitText(to, editedAt)
if err != nil {
return err
return nil, err
}

// 02. remove the nodes and update linked list and index tree.
toBeRemoveds := make([]*TreeNode, 0)
createdAtMapByActor := make(map[string]*time.Ticket)

if fromLeft != toLeft {
var fromChildIndex int
Expand All @@ -623,27 +633,43 @@ func (t *Tree) Edit(from, to *TreePos, contents []*TreeNode, editedAt *time.Tick

parentChildern := parent.Children(true)
for i := fromChildIndex; i <= toChildIndex; i++ {
node := parentChildern[i]
node := parentChildern[i].Value
actorIDHex := node.Pos.CreatedAt.ActorIDHex()

if node.Value.Pos.CreatedAt.Lamport() ==
editedAt.Lamport() &&
node.Value.Pos.CreatedAt.ActorID().String() != editedAt.ActorID().String() {
continue
var latestCreatedAt *time.Ticket
if latestCreatedAtMapByActor == nil {
latestCreatedAt = time.MaxTicket
} else {
createdAt, ok := latestCreatedAtMapByActor[actorIDHex]
if ok {
latestCreatedAt = createdAt
} else {
latestCreatedAt = time.InitialTicket
}
}

// traverse the nodes including tombstones
index.TraverseNode(node, func(node *index.Node[*TreeNode], depth int) {
if !node.Value.IsRemoved() {
toBeRemoveds = append(toBeRemoveds, node.Value)
if node.remove(editedAt, latestCreatedAt) {
latestCreatedAt = createdAtMapByActor[actorIDHex]
createdAt := node.Pos.CreatedAt
if latestCreatedAt == nil || createdAt.After(latestCreatedAt) {
createdAtMapByActor[actorIDHex] = createdAt
}
})
}

for _, node := range toBeRemoveds {
node.remove(editedAt)

if node.IsRemoved() {
t.removedNodeMap[node.Pos.ToIDString()] = node

// traverse the nodes including tombstones
index.TraverseNode(node.IndexTreeNode, func(node *index.Node[*TreeNode], depth int) {
if node.Value.remove(editedAt, time.MaxTicket) {
// TODO(sejongk): Refactor the repeated code.
latestCreatedAt = latestCreatedAtMapByActor[actorIDHex]
createdAt := node.Value.Pos.CreatedAt
if latestCreatedAt == nil || createdAt.After(latestCreatedAt) {
createdAtMapByActor[actorIDHex] = createdAt
}

t.removedNodeMap[node.Value.Pos.ToIDString()] = node.Value
}
})
}
}
}
Expand All @@ -658,13 +684,13 @@ func (t *Tree) Edit(from, to *TreePos, contents []*TreeNode, editedAt *time.Tick
// 03-1-1. when there's no leftSibling, then insert content into very front of parent's children List
err := fromParent.InsertAt(content.IndexTreeNode, 0)
if err != nil {
return err
return nil, err
}
} else {
// 03-1-2. insert after leftSibling
err := fromParent.InsertAfter(content.IndexTreeNode, leftInChildren)
if err != nil {
return err
return nil, err
}
}

Expand All @@ -673,16 +699,22 @@ func (t *Tree) Edit(from, to *TreePos, contents []*TreeNode, editedAt *time.Tick
// if insertion happens during concurrent editing and parent node has been removed,
// make new nodes as tombstone immediately
if fromParent.Value.IsRemoved() {
node.Value.remove(editedAt)
actorIDHex := node.Value.Pos.CreatedAt.ActorIDHex()
if node.Value.remove(editedAt, time.MaxTicket) {
latestCreatedAt := latestCreatedAtMapByActor[actorIDHex]
createdAt := node.Value.Pos.CreatedAt
if latestCreatedAt == nil || createdAt.After(latestCreatedAt) {
createdAtMapByActor[actorIDHex] = createdAt
}
}
t.removedNodeMap[node.Value.Pos.ToIDString()] = node.Value
}

t.NodeMapByPos.Put(node.Value.Pos, node.Value)
})
}
}

return nil
return createdAtMapByActor, nil
}

// StyleByIndex applies the given attributes of the given range.
Expand Down
Loading

1 comment on commit 72d9715

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go Benchmark

Benchmark suite Current: 72d9715 Previous: 1f63722 Ratio
BenchmarkDocument/constructor_test - ns/op 1807 ns/op 1571 ns/op 1.15
BenchmarkDocument/constructor_test - B/op 984 B/op 984 B/op 1
BenchmarkDocument/constructor_test - allocs/op 16 allocs/op 16 allocs/op 1
BenchmarkDocument/status_test - ns/op 1076 ns/op 889.9 ns/op 1.21
BenchmarkDocument/status_test - B/op 952 B/op 952 B/op 1
BenchmarkDocument/status_test - allocs/op 14 allocs/op 14 allocs/op 1
BenchmarkDocument/equals_test - ns/op 11086 ns/op 8561 ns/op 1.29
BenchmarkDocument/equals_test - B/op 6192 B/op 6192 B/op 1
BenchmarkDocument/equals_test - allocs/op 106 allocs/op 106 allocs/op 1
BenchmarkDocument/nested_update_test - ns/op 25204 ns/op 23849 ns/op 1.06
BenchmarkDocument/nested_update_test - B/op 11689 B/op 11689 B/op 1
BenchmarkDocument/nested_update_test - allocs/op 248 allocs/op 248 allocs/op 1
BenchmarkDocument/delete_test - ns/op 34482 ns/op 30251 ns/op 1.14
BenchmarkDocument/delete_test - B/op 14915 B/op 14915 B/op 1
BenchmarkDocument/delete_test - allocs/op 327 allocs/op 327 allocs/op 1
BenchmarkDocument/object_test - ns/op 12106 ns/op 10605 ns/op 1.14
BenchmarkDocument/object_test - B/op 6448 B/op 6448 B/op 1
BenchmarkDocument/object_test - allocs/op 110 allocs/op 110 allocs/op 1
BenchmarkDocument/array_test - ns/op 40741 ns/op 40110 ns/op 1.02
BenchmarkDocument/array_test - B/op 11546 B/op 11545 B/op 1.00
BenchmarkDocument/array_test - allocs/op 264 allocs/op 264 allocs/op 1
BenchmarkDocument/text_test - ns/op 43921 ns/op 38207 ns/op 1.15
BenchmarkDocument/text_test - B/op 14618 B/op 14618 B/op 1
BenchmarkDocument/text_test - allocs/op 470 allocs/op 470 allocs/op 1
BenchmarkDocument/text_composition_test - ns/op 45858 ns/op 38782 ns/op 1.18
BenchmarkDocument/text_composition_test - B/op 18002 B/op 18002 B/op 1
BenchmarkDocument/text_composition_test - allocs/op 471 allocs/op 471 allocs/op 1
BenchmarkDocument/rich_text_test - ns/op 121565 ns/op 103540 ns/op 1.17
BenchmarkDocument/rich_text_test - B/op 36816 B/op 36807 B/op 1.00
BenchmarkDocument/rich_text_test - allocs/op 1131 allocs/op 1131 allocs/op 1
BenchmarkDocument/counter_test - ns/op 25291 ns/op 20994 ns/op 1.20
BenchmarkDocument/counter_test - B/op 9969 B/op 9970 B/op 1.00
BenchmarkDocument/counter_test - allocs/op 235 allocs/op 235 allocs/op 1
BenchmarkDocument/text_edit_gc_100 - ns/op 4623812 ns/op 3963886 ns/op 1.17
BenchmarkDocument/text_edit_gc_100 - B/op 1552964 B/op 1553087 B/op 1.00
BenchmarkDocument/text_edit_gc_100 - allocs/op 17162 allocs/op 17161 allocs/op 1.00
BenchmarkDocument/text_edit_gc_1000 - ns/op 365212989 ns/op 313689090 ns/op 1.16
BenchmarkDocument/text_edit_gc_1000 - B/op 136646317 B/op 136631552 B/op 1.00
BenchmarkDocument/text_edit_gc_1000 - allocs/op 210778 allocs/op 210701 allocs/op 1.00
BenchmarkDocument/text_split_gc_100 - ns/op 5352367 ns/op 4598879 ns/op 1.16
BenchmarkDocument/text_split_gc_100 - B/op 2218153 B/op 2217588 B/op 1.00
BenchmarkDocument/text_split_gc_100 - allocs/op 16594 allocs/op 16589 allocs/op 1.00
BenchmarkDocument/text_split_gc_1000 - ns/op 421673945 ns/op 361375128 ns/op 1.17
BenchmarkDocument/text_split_gc_1000 - B/op 214849666 B/op 214859917 B/op 1.00
BenchmarkDocument/text_split_gc_1000 - allocs/op 211408 allocs/op 211446 allocs/op 1.00
BenchmarkDocument/text_delete_all_10000 - ns/op 21182037 ns/op 19578579 ns/op 1.08
BenchmarkDocument/text_delete_all_10000 - B/op 5903112 B/op 5904621 B/op 1.00
BenchmarkDocument/text_delete_all_10000 - allocs/op 41121 allocs/op 41128 allocs/op 1.00
BenchmarkDocument/text_delete_all_100000 - ns/op 274009256 ns/op 254618054 ns/op 1.08
BenchmarkDocument/text_delete_all_100000 - B/op 53832500 B/op 53839944 B/op 1.00
BenchmarkDocument/text_delete_all_100000 - allocs/op 415938 allocs/op 415993 allocs/op 1.00
BenchmarkDocument/text_100 - ns/op 396057 ns/op 330774 ns/op 1.20
BenchmarkDocument/text_100 - B/op 118212 B/op 118213 B/op 1.00
BenchmarkDocument/text_100 - allocs/op 5074 allocs/op 5074 allocs/op 1
BenchmarkDocument/text_1000 - ns/op 4165948 ns/op 3639084 ns/op 1.14
BenchmarkDocument/text_1000 - B/op 1152832 B/op 1152823 B/op 1.00
BenchmarkDocument/text_1000 - allocs/op 50078 allocs/op 50078 allocs/op 1
BenchmarkDocument/array_1000 - ns/op 2028926 ns/op 1797916 ns/op 1.13
BenchmarkDocument/array_1000 - B/op 1102824 B/op 1102821 B/op 1.00
BenchmarkDocument/array_1000 - allocs/op 11867 allocs/op 11867 allocs/op 1
BenchmarkDocument/array_10000 - ns/op 23113689 ns/op 20787542 ns/op 1.11
BenchmarkDocument/array_10000 - B/op 9905632 B/op 9907150 B/op 1.00
BenchmarkDocument/array_10000 - allocs/op 120713 allocs/op 120719 allocs/op 1.00
BenchmarkDocument/array_gc_100 - ns/op 214436 ns/op 183158 ns/op 1.17
BenchmarkDocument/array_gc_100 - B/op 98150 B/op 98169 B/op 1.00
BenchmarkDocument/array_gc_100 - allocs/op 1243 allocs/op 1243 allocs/op 1
BenchmarkDocument/array_gc_1000 - ns/op 2370861 ns/op 1976201 ns/op 1.20
BenchmarkDocument/array_gc_1000 - B/op 1170205 B/op 1170344 B/op 1.00
BenchmarkDocument/array_gc_1000 - allocs/op 12905 allocs/op 12906 allocs/op 1.00
BenchmarkDocument/counter_1000 - ns/op 348254 ns/op 285428 ns/op 1.22
BenchmarkDocument/counter_1000 - B/op 198533 B/op 198534 B/op 1.00
BenchmarkDocument/counter_1000 - allocs/op 6503 allocs/op 6503 allocs/op 1
BenchmarkDocument/counter_10000 - ns/op 3816271 ns/op 3095974 ns/op 1.23
BenchmarkDocument/counter_10000 - B/op 2165464 B/op 2165456 B/op 1.00
BenchmarkDocument/counter_10000 - allocs/op 69510 allocs/op 69510 allocs/op 1
BenchmarkDocument/object_1000 - ns/op 2223594 ns/op 1901665 ns/op 1.17
BenchmarkDocument/object_1000 - B/op 1451444 B/op 1451351 B/op 1.00
BenchmarkDocument/object_1000 - allocs/op 9915 allocs/op 9915 allocs/op 1
BenchmarkDocument/object_10000 - ns/op 26597659 ns/op 23966450 ns/op 1.11
BenchmarkDocument/object_10000 - B/op 12369176 B/op 12371453 B/op 1.00
BenchmarkDocument/object_10000 - allocs/op 101216 allocs/op 101226 allocs/op 1.00
BenchmarkRPC/client_to_server - ns/op 463580741 ns/op 401201105 ns/op 1.16
BenchmarkRPC/client_to_server - B/op 12208309 B/op 12262480 B/op 1.00
BenchmarkRPC/client_to_server - allocs/op 176658 allocs/op 177181 allocs/op 1.00
BenchmarkRPC/client_to_client_via_server - ns/op 787561270 ns/op 685664009 ns/op 1.15
BenchmarkRPC/client_to_client_via_server - B/op 22637532 B/op 22584304 B/op 1.00
BenchmarkRPC/client_to_client_via_server - allocs/op 331417 allocs/op 330481 allocs/op 1.00
BenchmarkRPC/attach_large_document - ns/op 1617908941 ns/op 1473754095 ns/op 1.10
BenchmarkRPC/attach_large_document - B/op 1800190616 B/op 1819078872 B/op 0.99
BenchmarkRPC/attach_large_document - allocs/op 9438 allocs/op 9660 allocs/op 0.98
BenchmarkRPC/adminCli_to_server - ns/op 679299416 ns/op 559190893 ns/op 1.21
BenchmarkRPC/adminCli_to_server - B/op 20401952 B/op 20410700 B/op 1.00
BenchmarkRPC/adminCli_to_server - allocs/op 321624 allocs/op 321614 allocs/op 1.00
BenchmarkLocker - ns/op 159.4 ns/op 123.1 ns/op 1.29
BenchmarkLocker - B/op 16 B/op 16 B/op 1
BenchmarkLocker - allocs/op 1 allocs/op 1 allocs/op 1
BenchmarkLockerParallel - ns/op 162.2 ns/op 126.5 ns/op 1.28
BenchmarkLockerParallel - B/op 0 B/op 0 B/op NaN
BenchmarkLockerParallel - allocs/op 0 allocs/op 0 allocs/op NaN
BenchmarkLockerMoreKeys - ns/op 428.4 ns/op 440.8 ns/op 0.97
BenchmarkLockerMoreKeys - B/op 13 B/op 13 B/op 1
BenchmarkLockerMoreKeys - allocs/op 0 allocs/op 0 allocs/op NaN
BenchmarkSync/memory_sync_10_test - ns/op 8723 ns/op 7461 ns/op 1.17
BenchmarkSync/memory_sync_10_test - B/op 1283 B/op 1283 B/op 1
BenchmarkSync/memory_sync_10_test - allocs/op 38 allocs/op 38 allocs/op 1
BenchmarkSync/memory_sync_100_test - ns/op 78749 ns/op 69442 ns/op 1.13
BenchmarkSync/memory_sync_100_test - B/op 8764 B/op 8763 B/op 1.00
BenchmarkSync/memory_sync_100_test - allocs/op 280 allocs/op 280 allocs/op 1
BenchmarkSync/memory_sync_1000_test - ns/op 771060 ns/op 699971 ns/op 1.10
BenchmarkSync/memory_sync_1000_test - B/op 81641 B/op 81716 B/op 1.00
BenchmarkSync/memory_sync_1000_test - allocs/op 2577 allocs/op 2579 allocs/op 1.00
BenchmarkSync/memory_sync_10000_test - ns/op 8128674 ns/op 7491717 ns/op 1.09
BenchmarkSync/memory_sync_10000_test - B/op 851338 B/op 850438 B/op 1.00
BenchmarkSync/memory_sync_10000_test - allocs/op 26869 allocs/op 26946 allocs/op 1.00
BenchmarkTextEditing - ns/op 29468685830 ns/op 27697786985 ns/op 1.06
BenchmarkTextEditing - B/op 8457065136 B/op 8457437952 B/op 1.00
BenchmarkTextEditing - allocs/op 20614967 allocs/op 20616814 allocs/op 1.00

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.