Skip to content

Commit

Permalink
Replace maxCreatedAtMapByActor with change's version vector
Browse files Browse the repository at this point in the history
  • Loading branch information
chacha912 committed Dec 9, 2024
1 parent 98fe2a3 commit 36b4a38
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 62 deletions.
6 changes: 5 additions & 1 deletion packages/sdk/src/document/change/change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ export class Change<P extends Indexable> {
const reverseOps: Array<HistoryOperation<P>> = [];

for (const operation of this.operations) {
const executionResult = operation.execute(root, source);
const executionResult = operation.execute(
root,
source,
this.id.getVersionVector(),
);
// NOTE(hackerwins): If the element was removed while executing undo/redo,
// the operation is not executed and executionResult is undefined.
if (!executionResult) continue;
Expand Down
59 changes: 41 additions & 18 deletions packages/sdk/src/document/crdt/rga_tree_split.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ import { SplayNode, SplayTree } from '@yorkie-js-sdk/src/util/splay_tree';
import { LLRBTree } from '@yorkie-js-sdk/src/util/llrb_tree';
import {
InitialTimeTicket,
MaxTimeTicket,
MaxLamport,
TimeTicket,
TimeTicketStruct,
} from '@yorkie-js-sdk/src/document/time/ticket';
import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector';
import { GCChild, GCPair, GCParent } from '@yorkie-js-sdk/src/document/crdt/gc';
import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error';

Expand Down Expand Up @@ -439,12 +440,17 @@ export class RGATreeSplitNode<T extends RGATreeSplitValue>
/**
* `canDelete` checks if node is able to delete.
*/
public canDelete(editedAt: TimeTicket, maxCreatedAt: TimeTicket): boolean {
public canDelete(
editedAt: TimeTicket,
maxCreatedAt: TimeTicket | undefined,
clientLamportAtChange: bigint,
): boolean {
const justRemoved = !this.removedAt;
if (
!this.getCreatedAt().after(maxCreatedAt) &&
(!this.removedAt || editedAt.after(this.removedAt))
) {
const nodeExisted = maxCreatedAt
? !this.getCreatedAt().after(maxCreatedAt)
: this.getCreatedAt().getLamport() <= clientLamportAtChange;

if (nodeExisted && (!this.removedAt || editedAt.after(this.removedAt))) {
return justRemoved;
}

Expand All @@ -454,11 +460,16 @@ export class RGATreeSplitNode<T extends RGATreeSplitValue>
/**
* `canStyle` checks if node is able to set style.
*/
public canStyle(editedAt: TimeTicket, maxCreatedAt: TimeTicket): boolean {
return (
!this.getCreatedAt().after(maxCreatedAt) &&
(!this.removedAt || editedAt.after(this.removedAt))
);
public canStyle(
editedAt: TimeTicket,
maxCreatedAt: TimeTicket | undefined,
clientLamportAtChange: bigint,
): boolean {
const nodeExisted = maxCreatedAt
? !this.getCreatedAt().after(maxCreatedAt)
: this.getCreatedAt().getLamport() <= clientLamportAtChange;

return nodeExisted && (!this.removedAt || editedAt.after(this.removedAt));
}

/**
Expand Down Expand Up @@ -552,6 +563,7 @@ export class RGATreeSplit<T extends RGATreeSplitValue> implements GCParent {
editedAt: TimeTicket,
value?: T,
maxCreatedAtMapByActor?: Map<string, TimeTicket>,
versionVector?: VersionVector,
): [
RGATreeSplitPos,
Map<string, TimeTicket>,
Expand All @@ -568,6 +580,7 @@ export class RGATreeSplit<T extends RGATreeSplitValue> implements GCParent {
nodesToDelete,
editedAt,
maxCreatedAtMapByActor,
versionVector,
);

const caretID = toRight ? toRight.getID() : toLeft.getID();
Expand Down Expand Up @@ -878,6 +891,7 @@ export class RGATreeSplit<T extends RGATreeSplitValue> implements GCParent {
candidates: Array<RGATreeSplitNode<T>>,
editedAt: TimeTicket,
maxCreatedAtMapByActor?: Map<string, TimeTicket>,
versionVector?: VersionVector,
): [
Array<ValueChange<T>>,
Map<string, TimeTicket>,
Expand All @@ -894,6 +908,7 @@ export class RGATreeSplit<T extends RGATreeSplitValue> implements GCParent {
candidates,
editedAt,
maxCreatedAtMapByActor,
versionVector,
);

const createdAtMapByActor = new Map();
Expand Down Expand Up @@ -922,8 +937,8 @@ export class RGATreeSplit<T extends RGATreeSplitValue> implements GCParent {
candidates: Array<RGATreeSplitNode<T>>,
editedAt: TimeTicket,
maxCreatedAtMapByActor?: Map<string, TimeTicket>,
versionVector?: VersionVector,
): [Array<RGATreeSplitNode<T>>, Array<RGATreeSplitNode<T> | undefined>] {
const isRemote = !!maxCreatedAtMapByActor;
const nodesToDelete: Array<RGATreeSplitNode<T>> = [];
const nodesToKeep: Array<RGATreeSplitNode<T> | undefined> = [];

Expand All @@ -932,14 +947,22 @@ export class RGATreeSplit<T extends RGATreeSplitValue> implements GCParent {

for (const node of candidates) {
const actorID = node.getCreatedAt().getActorID();

const maxCreatedAt = isRemote
? maxCreatedAtMapByActor!.has(actorID)
let maxCreatedAt: TimeTicket | undefined;
let clientLamportAtChange = 0n;
if (versionVector === undefined && maxCreatedAtMapByActor === undefined) {
// Local edit - use version vector comparison
clientLamportAtChange = MaxLamport;
} else if (versionVector!.size() > 0) {
clientLamportAtChange = versionVector!.get(actorID)
? versionVector!.get(actorID)!
: 0n;
} else {
maxCreatedAt = maxCreatedAtMapByActor!.has(actorID)
? maxCreatedAtMapByActor!.get(actorID)
: InitialTimeTicket
: MaxTimeTicket;
: InitialTimeTicket;
}

if (node.canDelete(editedAt, maxCreatedAt!)) {
if (node.canDelete(editedAt, maxCreatedAt, clientLamportAtChange)) {
nodesToDelete.push(node);
} else {
nodesToKeep.push(node);
Expand Down
28 changes: 20 additions & 8 deletions packages/sdk/src/document/crdt/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
*/

import {
MaxLamport,
InitialTimeTicket,
MaxTimeTicket,
TimeTicket,
} from '@yorkie-js-sdk/src/document/time/ticket';
import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector';
import { Indexable } from '@yorkie-js-sdk/src/document/document';
import { RHT, RHTNode } from '@yorkie-js-sdk/src/document/crdt/rht';
import { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element';
Expand Down Expand Up @@ -224,6 +225,7 @@ export class CRDTText<A extends Indexable = Indexable> extends CRDTElement {
editedAt: TimeTicket,
attributes?: Record<string, string>,
maxCreatedAtMapByActor?: Map<string, TimeTicket>,
versionVector?: VersionVector,
): [
Map<string, TimeTicket>,
Array<TextChange<A>>,
Expand All @@ -243,6 +245,7 @@ export class CRDTText<A extends Indexable = Indexable> extends CRDTElement {
editedAt,
crdtTextValue,
maxCreatedAtMapByActor,
versionVector,
);

const changes: Array<TextChange<A>> = valueChanges.map((change) => ({
Expand Down Expand Up @@ -278,6 +281,7 @@ export class CRDTText<A extends Indexable = Indexable> extends CRDTElement {
attributes: Record<string, string>,
editedAt: TimeTicket,
maxCreatedAtMapByActor?: Map<string, TimeTicket>,
versionVector?: VersionVector,
): [Map<string, TimeTicket>, Array<GCPair>, Array<TextChange<A>>] {
// 01. split nodes with from and to
const [, toRight] = this.rgaTreeSplit.findNodeWithSplit(range[1], editedAt);
Expand All @@ -294,14 +298,22 @@ export class CRDTText<A extends Indexable = Indexable> extends CRDTElement {

for (const node of nodes) {
const actorID = node.getCreatedAt().getActorID();
let maxCreatedAt: TimeTicket | undefined;
let clientLamportAtChange = 0n;
if (versionVector === undefined && maxCreatedAtMapByActor === undefined) {
// Local edit - use version vector comparison
clientLamportAtChange = MaxLamport;
} else if (versionVector!.size() > 0) {
clientLamportAtChange = versionVector!.get(actorID)
? versionVector!.get(actorID)!
: 0n;
} else {
maxCreatedAt = maxCreatedAtMapByActor!.has(actorID)
? maxCreatedAtMapByActor!.get(actorID)
: InitialTimeTicket;
}

const maxCreatedAt = maxCreatedAtMapByActor?.size
? maxCreatedAtMapByActor!.has(actorID)
? maxCreatedAtMapByActor!.get(actorID)!
: InitialTimeTicket
: MaxTimeTicket;

if (node.canStyle(editedAt, maxCreatedAt)) {
if (node.canStyle(editedAt, maxCreatedAt, clientLamportAtChange)) {
const maxCreatedAt = createdAtMapByActor.get(actorID);
const createdAt = node.getCreatedAt();
if (!maxCreatedAt || createdAt.after(maxCreatedAt)) {
Expand Down
Loading

0 comments on commit 36b4a38

Please sign in to comment.