Skip to content

Commit

Permalink
Handle concurrent editing of Tree.Edit (#607)
Browse files Browse the repository at this point in the history
Introduced new logical timestamp for identifying the position in
local/remote editing, and ensures commutative editing in concurrent
cases.

This logical timestamp consists of {parentID, leftSiblingID}. This
allows editing at the front of text nodes by using a reference to the
parent, as well as getting rid of the local offset used to access an
element node's children previously by using the leftSiblingID.

---------

Co-authored-by: MoonGyu1 <[email protected]>
Co-authored-by: sejongk <[email protected]>
  • Loading branch information
3 people committed Aug 17, 2023
1 parent 21b3afc commit ac41998
Show file tree
Hide file tree
Showing 14 changed files with 2,603 additions and 799 deletions.
28 changes: 25 additions & 3 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 Expand Up @@ -620,7 +628,7 @@ func FromTreeNodesWhenEdit(pbNodes []*api.TreeNodes) ([]*crdt.TreeNode, error) {
}

func fromTreeNode(pbNode *api.TreeNode) (*crdt.TreeNode, error) {
pos, err := fromTreePos(pbNode.Pos)
id, err := fromTreeNodeID(pbNode.Id)
if err != nil {
return nil, err
}
Expand All @@ -635,20 +643,34 @@ func fromTreeNode(pbNode *api.TreeNode) (*crdt.TreeNode, error) {
}

return crdt.NewTreeNode(
pos,
id,
pbNode.Type,
attrs,
pbNode.Value,
), nil
}

func fromTreePos(pbPos *api.TreePos) (*crdt.TreePos, error) {
parentID, err := fromTreeNodeID(pbPos.ParentId)
if err != nil {
return nil, err
}

leftSiblingID, err := fromTreeNodeID(pbPos.LeftSiblingId)
if err != nil {
return nil, err
}

return crdt.NewTreePos(parentID, leftSiblingID), nil
}

func fromTreeNodeID(pbPos *api.TreeNodeID) (*crdt.TreeNodeID, error) {
createdAt, err := fromTimeTicket(pbPos.CreatedAt)
if err != nil {
return nil, err
}

return crdt.NewTreePos(
return crdt.NewTreeNodeID(
createdAt,
int(pbPos.Offset),
), nil
Expand Down
21 changes: 17 additions & 4 deletions api/converter/to_bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ func toTreeNode(treeNode *crdt.TreeNode, depth int) *api.TreeNode {
}

pbNode := &api.TreeNode{
Pos: toTreePos(treeNode.Pos),
Id: toTreeNodeID(treeNode.ID),
Type: treeNode.Type(),
Value: treeNode.Value,
RemovedAt: ToTimeTicket(treeNode.RemovedAt),
Expand All @@ -307,15 +307,28 @@ func toTreeNode(treeNode *crdt.TreeNode, depth int) *api.TreeNode {
}

if treeNode.InsPrev != nil {
pbNode.InsPrevPos = toTreePos(treeNode.InsPrev.Pos)
pbNode.InsPrevId = toTreeNodeID(treeNode.InsPrev.ID)
}

return pbNode
}

func toTreePos(pos *crdt.TreePos) *api.TreePos {
return &api.TreePos{
func toTreeNodeID(pos *crdt.TreeNodeID) *api.TreeNodeID {
return &api.TreeNodeID{
CreatedAt: ToTimeTicket(pos.CreatedAt),
Offset: int32(pos.Offset),
}
}

func toTreePos(pos *crdt.TreePos) *api.TreePos {
return &api.TreePos{
ParentId: &api.TreeNodeID{
CreatedAt: ToTimeTicket(pos.ParentID.CreatedAt),
Offset: int32(pos.ParentID.Offset),
},
LeftSiblingId: &api.TreeNodeID{
CreatedAt: ToTimeTicket(pos.LeftSiblingID.CreatedAt),
Offset: int32(pos.LeftSiblingID.Offset),
},
}
}
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
Loading

0 comments on commit ac41998

Please sign in to comment.