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

Concurrent case handling for Yorkie.tree (#611) #4

Merged
merged 1 commit into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion public/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const network = {
(event.type == 'stream-connection-status-changed' &&
event.value == 'connected') ||
(event.type == 'document-sync-result' && event.value == 'synced') ||
event.type == 'documents-changed')
event.type == 'document-changed')
) {
network.showOnline(elem);
}
Expand Down
55 changes: 43 additions & 12 deletions src/api/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { RGATreeList } from '@yorkie-js-sdk/src/document/crdt/rga_tree_list';
import { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element';
import { CRDTObject } from '@yorkie-js-sdk/src/document/crdt/object';
import { CRDTArray } from '@yorkie-js-sdk/src/document/crdt/array';
import { CRDTTreePos } from './../document/crdt/tree';
import {
RGATreeSplit,
RGATreeSplitNode,
Expand Down Expand Up @@ -78,6 +79,7 @@ import {
TreeNode as PbTreeNode,
TreeNodes as PbTreeNodes,
TreePos as PbTreePos,
TreeNodeID as PbTreeNodeID,
} from '@yorkie-js-sdk/src/api/yorkie/v1/resources_pb';
import { IncreaseOperation } from '@yorkie-js-sdk/src/document/operation/increase_operation';
import {
Expand All @@ -87,7 +89,7 @@ import {
import {
CRDTTree,
CRDTTreeNode,
CRDTTreePos,
CRDTTreeNodeID,
} from '@yorkie-js-sdk/src/document/crdt/tree';
import { traverse } from '../util/index_tree';
import { TreeStyleOperation } from '../document/operation/tree_style_operation';
Expand Down Expand Up @@ -261,11 +263,21 @@ function toTextNodePos(pos: RGATreeSplitPos): PbTextNodePos {
*/
function toTreePos(pos: CRDTTreePos): PbTreePos {
const pbTreePos = new PbTreePos();
pbTreePos.setCreatedAt(toTimeTicket(pos.getCreatedAt()));
pbTreePos.setOffset(pos.getOffset());
pbTreePos.setParentId(toTreeNodeID(pos.getParentID()));
pbTreePos.setLeftSiblingId(toTreeNodeID(pos.getLeftSiblingID()));
return pbTreePos;
}

/**
* `toTreeNodeID` converts the given model to Protobuf format.
*/
function toTreeNodeID(treeNodeID: CRDTTreeNodeID): PbTreeNodeID {
const pbTreeNodeID = new PbTreeNodeID();
pbTreeNodeID.setCreatedAt(toTimeTicket(treeNodeID.getCreatedAt()));
pbTreeNodeID.setOffset(treeNodeID.getOffset());
return pbTreeNodeID;
}

/**
* `toOperation` converts the given model to Protobuf format.
*/
Expand Down Expand Up @@ -380,6 +392,11 @@ function toOperation(operation: Operation): PbOperation {
} else if (operation instanceof TreeEditOperation) {
const treeEditOperation = operation as TreeEditOperation;
const pbTreeEditOperation = new PbOperation.TreeEdit();
const pbCreatedAtMapByActor =
pbTreeEditOperation.getCreatedAtMapByActorMap();
for (const [key, value] of treeEditOperation.getMaxCreatedAtMapByActor()) {
pbCreatedAtMapByActor.set(key, toTimeTicket(value)!);
}
pbTreeEditOperation.setParentCreatedAt(
toTimeTicket(treeEditOperation.getParentCreatedAt()),
);
Expand Down Expand Up @@ -545,7 +562,7 @@ function toTreeNodes(node: CRDTTreeNode): Array<PbTreeNode> {
const pbTreeNodes: Array<PbTreeNode> = [];
traverse(node, (n, depth) => {
const pbTreeNode = new PbTreeNode();
pbTreeNode.setPos(toTreePos(n.pos));
pbTreeNode.setId(toTreeNodeID(n.id));
pbTreeNode.setType(n.type);
if (n.isText) {
pbTreeNode.setValue(n.value);
Expand Down Expand Up @@ -856,8 +873,6 @@ function fromElementSimple(pbElementSimple: PbJSONElementSimple): CRDTElement {
fromTimeTicket(pbElementSimple.getCreatedAt())!,
);
}

throw new YorkieError(Code.Unimplemented, `unimplemented element`);
}

/**
Expand Down Expand Up @@ -909,8 +924,18 @@ function fromTextNode(pbTextNode: PbTextNode): RGATreeSplitNode<CRDTTextValue> {
*/
function fromTreePos(pbTreePos: PbTreePos): CRDTTreePos {
return CRDTTreePos.of(
fromTimeTicket(pbTreePos.getCreatedAt())!,
pbTreePos.getOffset(),
fromTreeNodeID(pbTreePos.getParentId()!),
fromTreeNodeID(pbTreePos.getLeftSiblingId()!),
);
}

/**
* `fromTreeNodeID` converts the given Protobuf format to model format.
*/
function fromTreeNodeID(pbTreeNodeID: PbTreeNodeID): CRDTTreeNodeID {
return CRDTTreeNodeID.of(
fromTimeTicket(pbTreeNodeID.getCreatedAt())!,
pbTreeNodeID.getOffset(),
);
}

Expand All @@ -925,10 +950,8 @@ function fromTreeNodesWhenEdit(
}

const treeNodes: Array<CRDTTreeNode> = [];

pbTreeNodes.forEach((node) => {
const treeNode = fromTreeNodes(node.getContentList());

treeNodes.push(treeNode!);
});

Expand Down Expand Up @@ -971,8 +994,8 @@ function fromTreeNodes(
* `fromTreeNode` converts the given Protobuf format to model format.
*/
function fromTreeNode(pbTreeNode: PbTreeNode): CRDTTreeNode {
const pos = fromTreePos(pbTreeNode.getPos()!);
const node = CRDTTreeNode.create(pos, pbTreeNode.getType());
const id = fromTreeNodeID(pbTreeNode.getId()!);
const node = CRDTTreeNode.create(id, pbTreeNode.getType());
if (node.isText) {
node.value = pbTreeNode.getValue();
} else {
Expand All @@ -982,6 +1005,9 @@ function fromTreeNode(pbTreeNode: PbTreeNode): CRDTTreeNode {
});
node.attrs = attrs;
}

node.removedAt = fromTimeTicket(pbTreeNode.getRemovedAt());

return node;
}

Expand Down Expand Up @@ -1073,10 +1099,15 @@ function fromOperations(pbOperations: Array<PbOperation>): Array<Operation> {
);
} else if (pbOperation.hasTreeEdit()) {
const pbTreeEditOperation = pbOperation.getTreeEdit();
const createdAtMapByActor = new Map();
pbTreeEditOperation!.getCreatedAtMapByActorMap().forEach((value, key) => {
createdAtMapByActor.set(key, fromTimeTicket(value));
});
operation = TreeEditOperation.create(
fromTimeTicket(pbTreeEditOperation!.getParentCreatedAt())!,
fromTreePos(pbTreeEditOperation!.getFrom()!),
fromTreePos(pbTreeEditOperation!.getTo()!),
createdAtMapByActor,
fromTreeNodesWhenEdit(pbTreeEditOperation!.getContentsList()),
fromTimeTicket(pbTreeEditOperation!.getExecutedAt())!,
);
Expand Down
28 changes: 19 additions & 9 deletions src/api/yorkie/v1/resources.proto
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ message Operation {
TimeTicket executed_at = 6;
map<string, string> attributes = 7;
}
// NOTE(hackerwins): Select Operation is not used in the current version.
// In the previous version, it was used to represent selection of Text.
// However, it has been replaced by Presence now. It is retained for backward
// compatibility purposes.
message Select {
TimeTicket parent_created_at = 1;
TextNodePos from = 2;
Expand All @@ -117,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 Expand Up @@ -233,11 +238,11 @@ message TextNodeID {
}

message TreeNode {
TreePos pos = 1;
TreeNodeID id = 1;
string type = 2;
string value = 3;
TimeTicket removed_at = 4;
TreePos ins_prev_pos = 5;
TreeNodeID ins_prev_id = 5;
int32 depth = 6;
map<string, NodeAttr> attributes = 7;
}
Expand All @@ -246,11 +251,16 @@ message TreeNodes {
repeated TreeNode content = 1;
}

message TreePos {
message TreeNodeID {
TimeTicket created_at = 1;
int32 offset = 2;
}

message TreePos {
TreeNodeID parent_id = 1;
TreeNodeID left_sibling_id = 2;
}

/////////////////////////////////////////
// Messages for Common //
/////////////////////////////////////////
Expand Down Expand Up @@ -343,12 +353,12 @@ enum ValueType {
}

enum DocEventType {
DOC_EVENT_TYPE_DOCUMENTS_CHANGED = 0;
DOC_EVENT_TYPE_DOCUMENTS_WATCHED = 1;
DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED = 2;
DOC_EVENT_TYPE_DOCUMENT_CHANGED = 0;
DOC_EVENT_TYPE_DOCUMENT_WATCHED = 1;
DOC_EVENT_TYPE_DOCUMENT_UNWATCHED = 2;
}

message DocEvent {
DocEventType type = 1;
bytes publisher = 2;
string publisher = 2;
}
76 changes: 52 additions & 24 deletions src/api/yorkie/v1/resources_pb.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,9 @@ export namespace Operation {
hasTo(): boolean;
clearTo(): TreeEdit;

getCreatedAtMapByActorMap(): jspb.Map<string, TimeTicket>;
clearCreatedAtMapByActorMap(): TreeEdit;

getContentsList(): Array<TreeNodes>;
setContentsList(value: Array<TreeNodes>): TreeEdit;
clearContentsList(): TreeEdit;
Expand All @@ -570,6 +573,7 @@ export namespace Operation {
parentCreatedAt?: TimeTicket.AsObject,
from?: TreePos.AsObject,
to?: TreePos.AsObject,
createdAtMapByActorMap: Array<[string, TimeTicket.AsObject]>,
contentsList: Array<TreeNodes.AsObject>,
executedAt?: TimeTicket.AsObject,
}
Expand Down Expand Up @@ -1119,10 +1123,10 @@ export namespace TextNodeID {
}

export class TreeNode extends jspb.Message {
getPos(): TreePos | undefined;
setPos(value?: TreePos): TreeNode;
hasPos(): boolean;
clearPos(): TreeNode;
getId(): TreeNodeID | undefined;
setId(value?: TreeNodeID): TreeNode;
hasId(): boolean;
clearId(): TreeNode;

getType(): string;
setType(value: string): TreeNode;
Expand All @@ -1135,10 +1139,10 @@ export class TreeNode extends jspb.Message {
hasRemovedAt(): boolean;
clearRemovedAt(): TreeNode;

getInsPrevPos(): TreePos | undefined;
setInsPrevPos(value?: TreePos): TreeNode;
hasInsPrevPos(): boolean;
clearInsPrevPos(): TreeNode;
getInsPrevId(): TreeNodeID | undefined;
setInsPrevId(value?: TreeNodeID): TreeNode;
hasInsPrevId(): boolean;
clearInsPrevId(): TreeNode;

getDepth(): number;
setDepth(value: number): TreeNode;
Expand All @@ -1156,11 +1160,11 @@ export class TreeNode extends jspb.Message {

export namespace TreeNode {
export type AsObject = {
pos?: TreePos.AsObject,
id?: TreeNodeID.AsObject,
type: string,
value: string,
removedAt?: TimeTicket.AsObject,
insPrevPos?: TreePos.AsObject,
insPrevId?: TreeNodeID.AsObject,
depth: number,
attributesMap: Array<[string, NodeAttr.AsObject]>,
}
Expand All @@ -1186,14 +1190,40 @@ export namespace TreeNodes {
}
}

export class TreePos extends jspb.Message {
export class TreeNodeID extends jspb.Message {
getCreatedAt(): TimeTicket | undefined;
setCreatedAt(value?: TimeTicket): TreePos;
setCreatedAt(value?: TimeTicket): TreeNodeID;
hasCreatedAt(): boolean;
clearCreatedAt(): TreePos;
clearCreatedAt(): TreeNodeID;

getOffset(): number;
setOffset(value: number): TreePos;
setOffset(value: number): TreeNodeID;

serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): TreeNodeID.AsObject;
static toObject(includeInstance: boolean, msg: TreeNodeID): TreeNodeID.AsObject;
static serializeBinaryToWriter(message: TreeNodeID, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): TreeNodeID;
static deserializeBinaryFromReader(message: TreeNodeID, reader: jspb.BinaryReader): TreeNodeID;
}

export namespace TreeNodeID {
export type AsObject = {
createdAt?: TimeTicket.AsObject,
offset: number,
}
}

export class TreePos extends jspb.Message {
getParentId(): TreeNodeID | undefined;
setParentId(value?: TreeNodeID): TreePos;
hasParentId(): boolean;
clearParentId(): TreePos;

getLeftSiblingId(): TreeNodeID | undefined;
setLeftSiblingId(value?: TreeNodeID): TreePos;
hasLeftSiblingId(): boolean;
clearLeftSiblingId(): TreePos;

serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): TreePos.AsObject;
Expand All @@ -1205,8 +1235,8 @@ export class TreePos extends jspb.Message {

export namespace TreePos {
export type AsObject = {
createdAt?: TimeTicket.AsObject,
offset: number,
parentId?: TreeNodeID.AsObject,
leftSiblingId?: TreeNodeID.AsObject,
}
}

Expand Down Expand Up @@ -1528,10 +1558,8 @@ export class DocEvent extends jspb.Message {
getType(): DocEventType;
setType(value: DocEventType): DocEvent;

getPublisher(): Uint8Array | string;
getPublisher_asU8(): Uint8Array;
getPublisher_asB64(): string;
setPublisher(value: Uint8Array | string): DocEvent;
getPublisher(): string;
setPublisher(value: string): DocEvent;

serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DocEvent.AsObject;
Expand All @@ -1544,7 +1572,7 @@ export class DocEvent extends jspb.Message {
export namespace DocEvent {
export type AsObject = {
type: DocEventType,
publisher: Uint8Array | string,
publisher: string,
}
}

Expand All @@ -1565,7 +1593,7 @@ export enum ValueType {
VALUE_TYPE_TREE = 13,
}
export enum DocEventType {
DOC_EVENT_TYPE_DOCUMENTS_CHANGED = 0,
DOC_EVENT_TYPE_DOCUMENTS_WATCHED = 1,
DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED = 2,
DOC_EVENT_TYPE_DOCUMENT_CHANGED = 0,
DOC_EVENT_TYPE_DOCUMENT_WATCHED = 1,
DOC_EVENT_TYPE_DOCUMENT_UNWATCHED = 2,
}
Loading