|
@@ -0,0 +1,119 @@
|
|
|
+
|
|
|
+export class NodeCreation {
|
|
|
+ readonly uuid: UUID;
|
|
|
+
|
|
|
+ // Inverse dependency: Incoming and outgoing edges.
|
|
|
+ edges: Array<EdgeCreation>; // append-only
|
|
|
+
|
|
|
+ // Inverse dependency: Deletions of this node.
|
|
|
+ deletions: Array<NodeDeletion>; // append-only
|
|
|
+
|
|
|
+ constructor(uuid: UUID) {
|
|
|
+ this.uuid = uuid;
|
|
|
+ this.edges = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Whenever a node is deleted more than once, concurrently, there's a {DELETE,DELETE}-conflict
|
|
|
+ getDeleteDeleteConflicts(): Array<[NodeDeletion, NodeDeletion]> {
|
|
|
+ // Basically get all pairs of 'deletions'
|
|
|
+ const result = new Array(this.deletions**2)
|
|
|
+ let i = 0;
|
|
|
+ for (const d1 of this.deletions) {
|
|
|
+ for (const d2 of this.deletions) {
|
|
|
+ result[i] = [d1, d2];
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Whenever a node is deleted, and the source/target of a concurrent edge creation, there's a {DELETE,REQUIRE}-conflict.
|
|
|
+ getDeleteRequireConflicts(): Array<[NodeDeletion, EdgeCreation]> {
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class NodeDeletion {
|
|
|
+ // Dependency: The node being deleted.
|
|
|
+ readonly deletes: NodeCreation;
|
|
|
+
|
|
|
+ // Dependency: Deletion of a node depends on deletion of its incoming and outgoing edges.
|
|
|
+ readonly edgeDeletions: Array<EdgeDeletion>;
|
|
|
+
|
|
|
+ constructor(deletes: NodeCreation, edgeDeletions: Array<EdgeDeletion>) {
|
|
|
+ this.deletes = deletes;
|
|
|
+ this.edgeDeletions = edgeDeletions;
|
|
|
+
|
|
|
+ // Create inverse dependency
|
|
|
+ this.deletes.deletions.append(this);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export interface EdgeOperation {
|
|
|
+ // Edge operations always form a tree (or sequence, if non-conflicting) where the path from root to leaf is as follows: create, update, update, ..., update, delete
|
|
|
+ // Therefore, any 'node' in this tree traces back to a 'root', the creation.
|
|
|
+ getCreation(): EdgeCreation;
|
|
|
+}
|
|
|
+
|
|
|
+export class EdgeCreation implements EdgeOperation {
|
|
|
+ // Dependency: source node
|
|
|
+ readonly source: NodeCreation;
|
|
|
+
|
|
|
+ // Dependency: target node
|
|
|
+ readonly target: NodeCreation;
|
|
|
+
|
|
|
+ readonly label: string;
|
|
|
+
|
|
|
+ // Inverse dependency: Next operation on the edge
|
|
|
+ nextOperations: Array<EdgeUpdate | EdgeDeletion>; // append-only
|
|
|
+
|
|
|
+ constructor(source: NodeCreation, label: string, target: NodeCreation) {
|
|
|
+ this.source = source;
|
|
|
+ this.target = target;
|
|
|
+ this.label = label;
|
|
|
+ this.nextOperations = [];
|
|
|
+
|
|
|
+ // Create inverse dependency
|
|
|
+ this.source.edges.push(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ getCreation(): EdgeCreation {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class EdgeUpdate implements EdgeOperation {
|
|
|
+ readonly edge: EdgeCreation | EdgeUpdate; // UPD-dependency
|
|
|
+ readonly newTarget: NodeCreation; // REQ-dependency
|
|
|
+
|
|
|
+ // Inverse dependency: Next operation on the edge
|
|
|
+ nextOperations: Array<EdgeUpdate | EdgeDeletion>; // append-only
|
|
|
+
|
|
|
+ constructor(edge: EdgeCreation | EdgeUpdate, newTarget: NodeCreation) {
|
|
|
+ this.edge = edge;
|
|
|
+ this.newTarget = newTarget;
|
|
|
+ this.nextOperations = [];
|
|
|
+
|
|
|
+ // Create inverse dependency
|
|
|
+ this.edge.nextOperations.push(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ getCreation(): EdgeCreation {
|
|
|
+ return this.edge.getCreation();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class EdgeDeletion implements EdgeOperation {
|
|
|
+ readonly edge: EdgeCreation | EdgeUpdate; // UPD-dependency
|
|
|
+
|
|
|
+ constructor(edge: EdgeCreation | EdgeUpdate) {
|
|
|
+ this.edge = edge;
|
|
|
+
|
|
|
+ // Create inverse dependency
|
|
|
+ this.edge.nextOperations.push(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ getCreation(): EdgeCreation {
|
|
|
+ return this.edge.getCreation();
|
|
|
+ }
|
|
|
+}
|