Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce object creation interface with initial values #756

Merged
merged 36 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9263c17
Add testcode for garbage collection
highcloud100 Jan 4, 2024
3987dba
Draft the SetNewObject function for given json literal
highcloud100 Jan 7, 2024
feb4633
Add support for additional primitive types in buildCRDTElement function
highcloud100 Jan 8, 2024
0f6949a
Add support for Array type in buildCRDTElement function and update re…
highcloud100 Jan 8, 2024
cdeb834
Refactor element registration and deregistration in Root
highcloud100 Jan 8, 2024
2f8d50a
Add support for Counter type in buildCRDTElement function and relevan…
highcloud100 Jan 8, 2024
6a338b8
Merge branch 'main' of https://github.com/yorkie-team/yorkie into set…
highcloud100 Jan 8, 2024
6e830a3
Naive implementation for text, tree type in buildCRDTElement function
highcloud100 Jan 8, 2024
ca7579b
Add test code for set nested object
highcloud100 Jan 9, 2024
a8c58f9
Lint
highcloud100 Jan 9, 2024
2dd5891
Add comment for counter due to lint
highcloud100 Jan 9, 2024
085c495
Handle the primitive null type and unsupported type in buildCRDTElement
highcloud100 Jan 10, 2024
a7e7c90
Apply the comment
highcloud100 Jan 10, 2024
32a2188
Add tests for duplicated deregister check and nested object primitive…
highcloud100 Jan 10, 2024
c34c09e
Merge branch 'main' of https://github.com/yorkie-team/yorkie into set…
highcloud100 Jan 10, 2024
035a5f0
Fix duplicate register
highcloud100 Jan 10, 2024
68599a2
Update object_test.go with new JSON object literal tests
highcloud100 Jan 10, 2024
a30fb8a
Merge branch 'main' of https://github.com/yorkie-team/yorkie into set…
highcloud100 Jan 10, 2024
0cc5001
Remove register code in NewRoot function that trigger duplicate
highcloud100 Jan 10, 2024
51de8a1
Refactor object test code
highcloud100 Jan 12, 2024
883c836
Add support for initializing Array and Object with values
highcloud100 Jan 16, 2024
9970280
Add error handler in NewArray function
highcloud100 Jan 16, 2024
aac72ef
Replace CreateCounter with NewCounter
highcloud100 Jan 16, 2024
b03df91
Replace CreateText with NewText
highcloud100 Jan 16, 2024
0563131
Replace CreateTree with NewTree
highcloud100 Jan 16, 2024
44c1809
Lint
highcloud100 Jan 16, 2024
4ffdbf3
Refactor SetNewArray method to accept initial values and add tests
highcloud100 Jan 16, 2024
e6fc05d
Revise test codes
hackerwins Jan 17, 2024
896eaf1
Refactor object test, Array and Object types
highcloud100 Jan 17, 2024
652540c
Refactor array test, newObject
highcloud100 Jan 17, 2024
0e71bdf
Refactor array and object, tree
highcloud100 Jan 17, 2024
657f595
Refactor json text, tree
highcloud100 Jan 17, 2024
68b4e32
Add some comment for test
highcloud100 Jan 17, 2024
df46de3
Revise codes
hackerwins Jan 17, 2024
bbe3f8c
Revise comments
hackerwins Jan 17, 2024
cee9b6d
Remove unnecessary newline
hackerwins Jan 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions pkg/document/crdt/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,41 @@ func (r *Root) FindByCreatedAt(createdAt *time.Ticket) Element {
}

// RegisterElement registers the given element to hash table.
func (r *Root) RegisterElement(elem Element) {
r.elementMapByCreatedAt[elem.CreatedAt().Key()] = elem
func (r *Root) RegisterElement(element Element) {
chacha912 marked this conversation as resolved.
Show resolved Hide resolved
r.elementMapByCreatedAt[element.CreatedAt().Key()] = element

switch element := element.(type) {
case Container:
{
element.Descendants(func(elem Element, parent Container) bool {
r.RegisterElement(elem)
return false
})
}
}
}

// DeregisterElement deregister the given element from hash tables.
func (r *Root) DeregisterElement(elem Element) {
createdAt := elem.CreatedAt().Key()
delete(r.elementMapByCreatedAt, createdAt)
delete(r.removedElementPairMapByCreatedAt, createdAt)
func (r *Root) DeregisterElement(element Element) {

chacha912 marked this conversation as resolved.
Show resolved Hide resolved
deregisterElementInternal := func(elem Element) {
createdAt := elem.CreatedAt().Key()
delete(r.elementMapByCreatedAt, createdAt)
delete(r.removedElementPairMapByCreatedAt, createdAt)
}

deregisterElementInternal(element)

switch element := element.(type) {
case Container:
{
element.Descendants(func(elem Element, parent Container) bool {
deregisterElementInternal(elem)
return false
})
}
}

}

// RegisterRemovedElementPair register the given element pair to hash table.
Expand Down Expand Up @@ -159,6 +185,7 @@ func (r *Root) GarbageLen() int {
switch elem := pair.elem.(type) {
case Container:
elem.Descendants(func(elem Element, parent Container) bool {

seen[elem.CreatedAt().Key()] = true
return false
})
Expand Down
14 changes: 14 additions & 0 deletions pkg/document/json/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ func NewCounter(ctx *change.Context, counter *crdt.Counter) *Counter {
}
}

// TempCounter is a transmission object for creating a counter inside.
type TempCounter struct {
valueType crdt.CounterType
value interface{}
}

// NewTempCounter creates a new instance of TempCounter.
func NewTempCounter(t crdt.CounterType, n interface{}) *TempCounter {
return &TempCounter{
valueType: t,
value: n,
}
}

// Increase adds an increase operations.
// Only numeric types are allowed as operand values, excluding
// uint64 and uintptr.
Expand Down
85 changes: 84 additions & 1 deletion pkg/document/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@
// Package json provides the JSON document implementation.
package json

import "github.com/yorkie-team/yorkie/pkg/document/crdt"
import (
gotime "time"

"github.com/yorkie-team/yorkie/pkg/document/change"
"github.com/yorkie-team/yorkie/pkg/document/crdt"
"github.com/yorkie-team/yorkie/pkg/document/time"
)

func toOriginal(elem crdt.Element) crdt.Element {
switch elem := elem.(type) {
Expand All @@ -37,3 +43,80 @@ func toOriginal(elem crdt.Element) crdt.Element {

panic("unsupported type")
}

func buildCRDTElement(
context *change.Context,
value interface{},
ticket *time.Ticket,
) crdt.Element {
highcloud100 marked this conversation as resolved.
Show resolved Hide resolved
switch elem := value.(type) {
// primitive type
case string, int, int32, int64, float32, float64, []byte, bool, gotime.Time:
primitive, err := crdt.NewPrimitive(elem, ticket)
if err != nil {
panic(err)
}
return primitive

case *TreeNode:

return NewTree(context, crdt.NewTree(buildRoot(context, elem, ticket), ticket))

case *Text:
return NewText(context, crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ticket))
case *TempCounter:
switch elem.valueType {
case crdt.IntegerCnt:
counter, err := crdt.NewCounter(crdt.IntegerCnt, elem.value, ticket)
if err != nil {
panic(err)
}
return NewCounter(
context,
counter,
)
case crdt.LongCnt:
counter, err := crdt.NewCounter(crdt.LongCnt, elem.value, ticket)
if err != nil {
panic(err)
}
return NewCounter(
context,
counter,
)
default:
panic("unsupported type")
}

case []interface{}:
array := NewArray(context, crdt.NewArray(crdt.NewRGATreeList(), ticket))
for _, v := range elem {
ticket := context.IssueTimeTicket()
value := buildCRDTElement(context, v, ticket)
value = toOriginal(value)
if err := array.InsertAfter(array.LastCreatedAt(), value); err != nil {
panic(err)
}
array.context.RegisterElement(value)
chacha912 marked this conversation as resolved.
Show resolved Hide resolved
}
return array
case map[string]interface{}:
obj := NewObject(context, crdt.NewObject(crdt.NewElementRHT(), ticket))
members := buildObjectMember(context, elem)

for key, value := range members {
value = toOriginal(value)
_ = obj.Set(key, value)
}

return obj
default:
// Null
primitive, err := crdt.NewPrimitive(nil, ticket)
if err != nil {
panic(err)
}
return primitive
}
highcloud100 marked this conversation as resolved.
Show resolved Hide resolved

}
62 changes: 51 additions & 11 deletions pkg/document/json/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package json

import (
"strings"
gotime "time"

"github.com/yorkie-team/yorkie/pkg/document/change"
Expand All @@ -41,12 +42,28 @@ func NewObject(ctx *change.Context, root *crdt.Object) *Object {
}

// SetNewObject sets a new Object for the given key.
func (p *Object) SetNewObject(k string) *Object {
v := p.setInternal(k, func(ticket *time.Ticket) crdt.Element {
return NewObject(p.context, crdt.NewObject(crdt.NewElementRHT(), ticket))
})
func (p *Object) SetNewObject(kv ...interface{}) *Object {
chacha912 marked this conversation as resolved.
Show resolved Hide resolved

switch len(kv) {
case 1: // key only
k := kv[0].(string)
v := p.setInternal(k, func(ticket *time.Ticket) crdt.Element {
return NewObject(p.context, crdt.NewObject(crdt.NewElementRHT(), ticket))
})
return v.(*Object)

case 2: // key with value
k := kv[0].(string)
v := p.setInternal(k, func(ticket *time.Ticket) crdt.Element {
json := kv[1].(map[string]interface{})
return buildCRDTElement(p.context, json, ticket)
})

return v.(*Object)
default:
panic("too many arguments in call to SetNewObject")
}

return v.(*Object)
}

// SetNewArray sets a new Array for the given key.
Expand Down Expand Up @@ -185,6 +202,7 @@ func (p *Object) SetDouble(k string, v float64) *Object {

// SetString sets the given string for the given key.
func (p *Object) SetString(k, v string) *Object {

p.setInternal(k, func(ticket *time.Ticket) crdt.Element {
primitive, err := crdt.NewPrimitive(v, ticket)
if err != nil {
Expand Down Expand Up @@ -329,25 +347,47 @@ func (p *Object) setInternal(
creator func(ticket *time.Ticket) crdt.Element,
) crdt.Element {
ticket := p.context.IssueTimeTicket()
elem := creator(ticket)
elem := creator(ticket) // create value -> object

value := toOriginal(elem)

copiedValue, err := value.DeepCopy()
if err != nil {
panic(err)
}

removed := p.Set(k, value)
p.context.RegisterElement(value)
if removed != nil {
p.context.RegisterRemovedElementPair(p, removed)
}

p.context.Push(operations.NewSet(
p.CreatedAt(),
k,
copiedValue,
ticket,
))
return elem
}

removed := p.Set(k, value)
p.context.RegisterElement(value)
if removed != nil {
p.context.RegisterRemovedElementPair(p, removed)
// return the element map of the given json
func buildObjectMember(
context *change.Context,
json map[string]interface{},
) map[string]crdt.Element {

members := make(map[string]crdt.Element)

for key, value := range json {
if strings.Contains(key, ".") {
panic("key must not contain the '.'.")
}

ticket := context.IssueTimeTicket()
elem := buildCRDTElement(context, value, ticket)
members[key] = elem
}

return elem
return members
}
2 changes: 1 addition & 1 deletion test/integration/gc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/yorkie-team/yorkie/pkg/document"
"github.com/yorkie-team/yorkie/pkg/document/json"
"github.com/yorkie-team/yorkie/pkg/document/presence"
Expand Down Expand Up @@ -430,4 +429,5 @@ func TestGarbageCollection(t *testing.T) {
assert.Equal(t, d1.GarbageCollect(time.MaxTicket), 3)
assert.Equal(t, d2.GarbageCollect(time.MaxTicket), 3)
})

}
Loading
Loading