Skip to content

Commit

Permalink
Fix gc for multiple nodes in text and tree type (yorkie-team#855)
Browse files Browse the repository at this point in the history
  • Loading branch information
chacha912 authored May 10, 2024
1 parent 5a77d08 commit 503ed59
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 1 deletion.
2 changes: 1 addition & 1 deletion pkg/document/crdt/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (r *Root) GarbageCollect(ticket *time.Ticket) (int, error) {
return 0, err
}

if purgedNodes > 0 {
if node.removedNodesLen() == 0 {
delete(r.elementHasRemovedNodesSetByCreatedAt, node.CreatedAt().Key())
}
count += purgedNodes
Expand Down
150 changes: 150 additions & 0 deletions test/integration/gc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,4 +452,154 @@ func TestGarbageCollection(t *testing.T) {
assert.Equal(t, 5, d1.GarbageCollect(time.MaxTicket))
})

t.Run("Should work properly when there are multiple nodes to be collected in text type", func(t *testing.T) {
ctx := context.Background()
d1 := document.New(helper.TestDocKey(t))
err := c1.Attach(ctx, d1)
assert.NoError(t, err)

d2 := document.New(helper.TestDocKey(t))
err = c2.Attach(ctx, d2)
assert.NoError(t, err)

err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.SetNewText("text").Edit(0, 0, "z")
return nil
})
assert.NoError(t, err)
err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetText("text").Edit(0, 1, "a")
return nil
})
assert.NoError(t, err)
err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetText("text").Edit(1, 1, "b")
return nil
})
assert.NoError(t, err)
err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetText("text").Edit(2, 2, "d")
return nil
})
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)

assert.Equal(t, `{"text":[{"val":"a"},{"val":"b"},{"val":"d"}]}`, d1.Marshal())
assert.Equal(t, `{"text":[{"val":"a"},{"val":"b"},{"val":"d"}]}`, d2.Marshal())
assert.Equal(t, 1, d1.GarbageLen()) // z

err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetText("text").Edit(2, 2, "c")
return nil
})
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)
assert.Equal(t, `{"text":[{"val":"a"},{"val":"b"},{"val":"c"},{"val":"d"}]}`, d1.Marshal())
assert.Equal(t, `{"text":[{"val":"a"},{"val":"b"},{"val":"c"},{"val":"d"}]}`, d2.Marshal())

err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetText("text").Edit(1, 3, "")
return nil
})
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
assert.Equal(t, `{"text":[{"val":"a"},{"val":"d"}]}`, d1.Marshal())
assert.Equal(t, 2, d1.GarbageLen()) // b,c

err = c2.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
assert.Equal(t, `{"text":[{"val":"a"},{"val":"d"}]}`, d2.Marshal())
assert.Equal(t, 0, d1.GarbageLen())
})

t.Run("Should work properly when there are multiple nodes to be collected in tree type", func(t *testing.T) {
ctx := context.Background()
d1 := document.New(helper.TestDocKey(t))
err := c1.Attach(ctx, d1)
assert.NoError(t, err)

d2 := document.New(helper.TestDocKey(t))
err = c2.Attach(ctx, d2)
assert.NoError(t, err)

err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.SetNewTree("tree", &json.TreeNode{
Type: "r",
Children: []json.TreeNode{{
Type: "text", Value: "z",
}},
})
return nil
})
assert.NoError(t, err)
err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetTree("tree").EditByPath([]int{0}, []int{1}, &json.TreeNode{Type: "text", Value: "a"}, 0)
return nil
})
assert.NoError(t, err)
err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetTree("tree").EditByPath([]int{1}, []int{1}, &json.TreeNode{Type: "text", Value: "b"}, 0)
return nil
})
assert.NoError(t, err)
err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetTree("tree").EditByPath([]int{2}, []int{2}, &json.TreeNode{Type: "text", Value: "d"}, 0)
return nil
})
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)

assert.Equal(t, `<r>abd</r>`, d1.Root().GetTree("tree").ToXML())
assert.Equal(t, `<r>abd</r>`, d2.Root().GetTree("tree").ToXML())
assert.Equal(t, 1, d1.GarbageLen()) // z

err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetTree("tree").EditByPath([]int{2}, []int{2}, &json.TreeNode{Type: "text", Value: "c"}, 0)
return nil
})
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)
assert.Equal(t, `<r>abcd</r>`, d1.Root().GetTree("tree").ToXML())
assert.Equal(t, `<r>abcd</r>`, d2.Root().GetTree("tree").ToXML())

err = d1.Update(func(root *json.Object, p *presence.Presence) error {
root.GetTree("tree").EditByPath([]int{1}, []int{3}, nil, 0)
return nil
})
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
assert.Equal(t, `<r>ad</r>`, d1.Root().GetTree("tree").ToXML())
assert.Equal(t, 2, d1.GarbageLen()) // b,c

err = c2.Sync(ctx)
assert.NoError(t, err)
err = c2.Sync(ctx)
assert.NoError(t, err)
err = c1.Sync(ctx)
assert.NoError(t, err)
assert.Equal(t, `<r>ad</r>`, d2.Root().GetTree("tree").ToXML())
assert.Equal(t, 0, d1.GarbageLen())
})
}

0 comments on commit 503ed59

Please sign in to comment.