From 0d714bdf113775becbfaf9e810e06e4d9c63537a Mon Sep 17 00:00:00 2001 From: Yourim Cha Date: Mon, 9 Dec 2024 09:48:05 +0900 Subject: [PATCH] Replace maxCreatedAtMapByActor with change's version vector --- packages/sdk/src/document/change/change.ts | 6 +- .../sdk/src/document/crdt/rga_tree_split.ts | 59 ++++++--- packages/sdk/src/document/crdt/text.ts | 28 +++-- packages/sdk/src/document/crdt/tree.ts | 115 +++++++++++++----- .../src/document/operation/edit_operation.ts | 9 +- .../sdk/src/document/operation/operation.ts | 2 + .../src/document/operation/style_operation.ts | 9 +- .../document/operation/tree_edit_operation.ts | 9 +- .../operation/tree_style_operation.ts | 12 +- 9 files changed, 187 insertions(+), 62 deletions(-) diff --git a/packages/sdk/src/document/change/change.ts b/packages/sdk/src/document/change/change.ts index 4ab29590e..8f68dee30 100644 --- a/packages/sdk/src/document/change/change.ts +++ b/packages/sdk/src/document/change/change.ts @@ -162,7 +162,11 @@ export class Change

{ const reverseOps: Array> = []; 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; diff --git a/packages/sdk/src/document/crdt/rga_tree_split.ts b/packages/sdk/src/document/crdt/rga_tree_split.ts index 45d8fd34d..41c45e257 100644 --- a/packages/sdk/src/document/crdt/rga_tree_split.ts +++ b/packages/sdk/src/document/crdt/rga_tree_split.ts @@ -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'; @@ -439,12 +440,17 @@ export class RGATreeSplitNode /** * `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; } @@ -454,11 +460,16 @@ export class RGATreeSplitNode /** * `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)); } /** @@ -552,6 +563,7 @@ export class RGATreeSplit implements GCParent { editedAt: TimeTicket, value?: T, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [ RGATreeSplitPos, Map, @@ -568,6 +580,7 @@ export class RGATreeSplit implements GCParent { nodesToDelete, editedAt, maxCreatedAtMapByActor, + versionVector, ); const caretID = toRight ? toRight.getID() : toLeft.getID(); @@ -878,6 +891,7 @@ export class RGATreeSplit implements GCParent { candidates: Array>, editedAt: TimeTicket, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [ Array>, Map, @@ -894,6 +908,7 @@ export class RGATreeSplit implements GCParent { candidates, editedAt, maxCreatedAtMapByActor, + versionVector, ); const createdAtMapByActor = new Map(); @@ -922,8 +937,8 @@ export class RGATreeSplit implements GCParent { candidates: Array>, editedAt: TimeTicket, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [Array>, Array | undefined>] { - const isRemote = !!maxCreatedAtMapByActor; const nodesToDelete: Array> = []; const nodesToKeep: Array | undefined> = []; @@ -932,14 +947,22 @@ export class RGATreeSplit 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); diff --git a/packages/sdk/src/document/crdt/text.ts b/packages/sdk/src/document/crdt/text.ts index 7633d7c65..c7ce8d2ea 100644 --- a/packages/sdk/src/document/crdt/text.ts +++ b/packages/sdk/src/document/crdt/text.ts @@ -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'; @@ -224,6 +225,7 @@ export class CRDTText extends CRDTElement { editedAt: TimeTicket, attributes?: Record, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [ Map, Array>, @@ -243,6 +245,7 @@ export class CRDTText extends CRDTElement { editedAt, crdtTextValue, maxCreatedAtMapByActor, + versionVector, ); const changes: Array> = valueChanges.map((change) => ({ @@ -278,6 +281,7 @@ export class CRDTText extends CRDTElement { attributes: Record, editedAt: TimeTicket, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [Map, Array, Array>] { // 01. split nodes with from and to const [, toRight] = this.rgaTreeSplit.findNodeWithSplit(range[1], editedAt); @@ -294,14 +298,22 @@ export class CRDTText 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)) { diff --git a/packages/sdk/src/document/crdt/tree.ts b/packages/sdk/src/document/crdt/tree.ts index 03127041a..abe2eaf3c 100644 --- a/packages/sdk/src/document/crdt/tree.ts +++ b/packages/sdk/src/document/crdt/tree.ts @@ -18,8 +18,9 @@ import { TimeTicket, InitialTimeTicket, TimeTicketStruct, - MaxTimeTicket, + MaxLamport, } from '@yorkie-js-sdk/src/document/time/ticket'; +import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector'; import { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element'; import { @@ -630,25 +631,34 @@ export class CRDTTreeNode /** * `canDelete` checks if node is able to delete. */ - public canDelete(editedAt: TimeTicket, maxCreatedAt: TimeTicket): boolean { - return ( - !this.getCreatedAt().after(maxCreatedAt) && - (!this.removedAt || editedAt.after(this.removedAt)) - ); + public canDelete( + 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)); } /** * `canStyle` checks if node is able to style. */ - public canStyle(editedAt: TimeTicket, maxCreatedAt: TimeTicket): boolean { + public canStyle( + editedAt: TimeTicket, + maxCreatedAt: TimeTicket | undefined, + clientLamportAtChange: bigint, + ): boolean { if (this.isText) { return false; } + const nodeExisted = maxCreatedAt + ? !this.getCreatedAt().after(maxCreatedAt) + : this.getCreatedAt().getLamport() <= clientLamportAtChange; - return ( - !this.getCreatedAt().after(maxCreatedAt) && - (!this.removedAt || editedAt.after(this.removedAt)) - ); + return nodeExisted && (!this.removedAt || editedAt.after(this.removedAt)); } /** @@ -662,7 +672,7 @@ export class CRDTTreeNode this.attrs = new RHT(); } - const pairs = new Array<[RHTNode | undefined, RHTNode | undefined]>(); + const pairs: Array<[RHTNode | undefined, RHTNode | undefined]> = []; for (const [key, value] of Object.entries(attrs)) { pairs.push(this.attrs.set(key, value, editedAt)); } @@ -879,6 +889,7 @@ export class CRDTTree extends CRDTElement implements GCParent { attributes: { [key: string]: string } | undefined, editedAt: TimeTicket, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [Map, Array, Array] { const [fromParent, fromLeft] = this.findNodesAndSplitText( range[0], @@ -899,13 +910,28 @@ export class CRDTTree extends CRDTElement implements GCParent { toLeft, ([node]) => { const actorID = node.getCreatedAt().getActorID(); - const maxCreatedAt = maxCreatedAtMapByActor - ? maxCreatedAtMapByActor!.has(actorID) - ? maxCreatedAtMapByActor!.get(actorID)! - : InitialTimeTicket - : MaxTimeTicket; + 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; + } - if (node.canStyle(editedAt, maxCreatedAt) && attributes) { + if ( + node.canStyle(editedAt, maxCreatedAt, clientLamportAtChange) && + attributes + ) { const maxCreatedAt = createdAtMapByActor!.get(actorID); const createdAt = node.getCreatedAt(); if (!maxCreatedAt || createdAt.after(maxCreatedAt)) { @@ -960,6 +986,7 @@ export class CRDTTree extends CRDTElement implements GCParent { attributesToRemove: Array, editedAt: TimeTicket, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [Map, Array, Array] { const [fromParent, fromLeft] = this.findNodesAndSplitText( range[0], @@ -977,13 +1004,28 @@ export class CRDTTree extends CRDTElement implements GCParent { toLeft, ([node]) => { const actorID = node.getCreatedAt().getActorID(); - const maxCreatedAt = maxCreatedAtMapByActor - ? maxCreatedAtMapByActor!.has(actorID) - ? maxCreatedAtMapByActor!.get(actorID)! - : InitialTimeTicket - : MaxTimeTicket; + 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; + } - if (node.canStyle(editedAt, maxCreatedAt) && attributesToRemove) { + if ( + node.canStyle(editedAt, maxCreatedAt, clientLamportAtChange) && + attributesToRemove + ) { const maxCreatedAt = createdAtMapByActor!.get(actorID); const createdAt = node.getCreatedAt(); if (!maxCreatedAt || createdAt.after(maxCreatedAt)) { @@ -1031,6 +1073,7 @@ export class CRDTTree extends CRDTElement implements GCParent { editedAt: TimeTicket, issueTimeTicket: (() => TimeTicket) | undefined, maxCreatedAtMapByActor?: Map, + versionVector?: VersionVector, ): [Array, Array, Map] { // 01. find nodes from the given range and split nodes. const [fromParent, fromLeft] = this.findNodesAndSplitText( @@ -1069,16 +1112,28 @@ export class CRDTTree extends CRDTElement implements GCParent { } const actorID = node.getCreatedAt().getActorID(); - const maxCreatedAt = maxCreatedAtMapByActor - ? maxCreatedAtMapByActor!.has(actorID) - ? maxCreatedAtMapByActor!.get(actorID)! - : InitialTimeTicket - : MaxTimeTicket; + 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; + } // NOTE(sejongk): If the node is removable or its parent is going to // be removed, then this node should be removed. if ( - node.canDelete(editedAt, maxCreatedAt) || + node.canDelete(editedAt, maxCreatedAt, clientLamportAtChange) || nodesToBeRemoved.includes(node.parent!) ) { const maxCreatedAt = maxCreatedAtMap.get(actorID); diff --git a/packages/sdk/src/document/operation/edit_operation.ts b/packages/sdk/src/document/operation/edit_operation.ts index 290d6a819..0c346f726 100644 --- a/packages/sdk/src/document/operation/edit_operation.ts +++ b/packages/sdk/src/document/operation/edit_operation.ts @@ -15,6 +15,7 @@ */ import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; +import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { RGATreeSplitPos } from '@yorkie-js-sdk/src/document/crdt/rga_tree_split'; import { CRDTText } from '@yorkie-js-sdk/src/document/crdt/text'; @@ -22,6 +23,7 @@ import { Operation, OperationInfo, ExecutionResult, + OpSource, } from '@yorkie-js-sdk/src/document/operation/operation'; import { Indexable } from '../document'; import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; @@ -80,7 +82,11 @@ export class EditOperation extends Operation { /** * `execute` executes this operation on the given `CRDTRoot`. */ - public execute(root: CRDTRoot): ExecutionResult { + public execute( + root: CRDTRoot, + _: OpSource, + versionVector?: VersionVector, + ): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { throw new YorkieError( @@ -102,6 +108,7 @@ export class EditOperation extends Operation { this.getExecutedAt(), Object.fromEntries(this.attributes), this.maxCreatedAtMapByActor, + versionVector, ); for (const pair of pairs) { diff --git a/packages/sdk/src/document/operation/operation.ts b/packages/sdk/src/document/operation/operation.ts index 18057b00a..6718fb137 100644 --- a/packages/sdk/src/document/operation/operation.ts +++ b/packages/sdk/src/document/operation/operation.ts @@ -16,6 +16,7 @@ import { ActorID } from '@yorkie-js-sdk/src/document/time/actor_id'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; +import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector'; import { TreeNode } from '@yorkie-js-sdk/src/document/crdt/tree'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { Indexable } from '@yorkie-js-sdk/src/document/document'; @@ -248,5 +249,6 @@ export abstract class Operation { public abstract execute( root: CRDTRoot, source: OpSource, + versionVector?: VersionVector, ): ExecutionResult | undefined; } diff --git a/packages/sdk/src/document/operation/style_operation.ts b/packages/sdk/src/document/operation/style_operation.ts index b7b4af721..f216b5117 100644 --- a/packages/sdk/src/document/operation/style_operation.ts +++ b/packages/sdk/src/document/operation/style_operation.ts @@ -15,6 +15,7 @@ */ import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; +import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { RGATreeSplitPos } from '@yorkie-js-sdk/src/document/crdt/rga_tree_split'; import { CRDTText } from '@yorkie-js-sdk/src/document/crdt/text'; @@ -22,6 +23,7 @@ import { Operation, OperationInfo, ExecutionResult, + OpSource, } from '@yorkie-js-sdk/src/document/operation/operation'; import { Indexable } from '../document'; import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; @@ -74,7 +76,11 @@ export class StyleOperation extends Operation { /** * `execute` executes this operation on the given `CRDTRoot`. */ - public execute(root: CRDTRoot): ExecutionResult { + public execute( + root: CRDTRoot, + _: OpSource, + versionVector?: VersionVector, + ): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { throw new YorkieError( @@ -94,6 +100,7 @@ export class StyleOperation extends Operation { this.attributes ? Object.fromEntries(this.attributes) : {}, this.getExecutedAt(), this.maxCreatedAtMapByActor, + versionVector, ); for (const pair of pairs) { diff --git a/packages/sdk/src/document/operation/tree_edit_operation.ts b/packages/sdk/src/document/operation/tree_edit_operation.ts index 3557a765a..7d1733a21 100644 --- a/packages/sdk/src/document/operation/tree_edit_operation.ts +++ b/packages/sdk/src/document/operation/tree_edit_operation.ts @@ -15,6 +15,7 @@ */ import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; +import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { CRDTTree, @@ -26,6 +27,7 @@ import { Operation, OperationInfo, ExecutionResult, + OpSource, } from '@yorkie-js-sdk/src/document/operation/operation'; import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; @@ -82,7 +84,11 @@ export class TreeEditOperation extends Operation { /** * `execute` executes this operation on the given `CRDTRoot`. */ - public execute(root: CRDTRoot): ExecutionResult { + public execute( + root: CRDTRoot, + _: OpSource, + versionVector?: VersionVector, + ): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { throw new YorkieError( @@ -124,6 +130,7 @@ export class TreeEditOperation extends Operation { return issueTimeTicket; })(), this.maxCreatedAtMapByActor, + versionVector, ); for (const pair of pairs) { diff --git a/packages/sdk/src/document/operation/tree_style_operation.ts b/packages/sdk/src/document/operation/tree_style_operation.ts index ecaad81da..11756759e 100644 --- a/packages/sdk/src/document/operation/tree_style_operation.ts +++ b/packages/sdk/src/document/operation/tree_style_operation.ts @@ -15,6 +15,7 @@ */ import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; +import { VersionVector } from '@yorkie-js-sdk/src/document/time/version_vector'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { CRDTTree, @@ -25,6 +26,7 @@ import { Operation, OperationInfo, ExecutionResult, + OpSource, } from '@yorkie-js-sdk/src/document/operation/operation'; import { GCPair } from '@yorkie-js-sdk/src/document/crdt/gc'; import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; @@ -74,7 +76,7 @@ export class TreeStyleOperation extends Operation { toPos, maxCreatedAtMapByActor, attributes, - new Array(), + [], executedAt, ); } @@ -104,7 +106,11 @@ export class TreeStyleOperation extends Operation { /** * `execute` executes this operation on the given `CRDTRoot`. */ - public execute(root: CRDTRoot): ExecutionResult { + public execute( + root: CRDTRoot, + _: OpSource, + versionVector: VersionVector, + ): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { throw new YorkieError( @@ -130,6 +136,7 @@ export class TreeStyleOperation extends Operation { attributes, this.getExecutedAt(), this.maxCreatedAtMapByActor, + versionVector, ); } else { const attributesToRemove = this.attributesToRemove; @@ -139,6 +146,7 @@ export class TreeStyleOperation extends Operation { attributesToRemove, this.getExecutedAt(), this.maxCreatedAtMapByActor, + versionVector, ); }