|
@@ -13,6 +13,9 @@ export class NodeCreation implements Delta {
|
|
|
// Inverse dependency: Creation outgoing edges.
|
|
|
outgoingEdges: Array<EdgeCreation> = []; // append-only
|
|
|
|
|
|
+ // Inverse dependency: Creation/update of incoming edges.
|
|
|
+ incomingEdges: Array<EdgeCreation | EdgeUpdate> = []; // append-only
|
|
|
+
|
|
|
constructor(id: UUID) {
|
|
|
this.id = id;
|
|
|
}
|
|
@@ -39,6 +42,9 @@ export class NodeDeletion implements Delta {
|
|
|
// Conflicts: Concurrent creation of an edge with as source the deleted node.
|
|
|
edgeSourceConflicts: Array<EdgeCreation> = [];
|
|
|
|
|
|
+ // Conflicts: Concurrent creation/update of an edge with as target the deleted node.
|
|
|
+ edgeTargetConflicts: Array<EdgeCreation | EdgeUpdate> = [];
|
|
|
+
|
|
|
// Conflicts: Concurrent update of an edge, when the edge is also deleted.
|
|
|
updateConflicts: Array<EdgeUpdate | NodeDeletion> = [];
|
|
|
|
|
@@ -64,6 +70,21 @@ export class NodeDeletion implements Delta {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Concurrently created incoming edges of this node
|
|
|
+ for (const incomingEdge of this.creation.incomingEdges) {
|
|
|
+ if (!this.deletedEdges.some(edge => {
|
|
|
+ while (true) {
|
|
|
+ if (edge === incomingEdge) return true;
|
|
|
+ if (edge instanceof EdgeUpdate) edge = edge.overwrites;
|
|
|
+ else return false;
|
|
|
+ }
|
|
|
+ })) {
|
|
|
+ // Symmetric
|
|
|
+ this.edgeTargetConflicts.push(incomingEdge);
|
|
|
+ incomingEdge.deleteTargetConflicts.push(this);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Concurrent edge updates
|
|
|
for (const deletedEdge of this.deletedEdges) {
|
|
|
for (const concurrentEdgeUpdate of deletedEdge.overwrittenBy) {
|
|
@@ -91,6 +112,7 @@ export class NodeDeletion implements Delta {
|
|
|
return Array<Delta>().concat(
|
|
|
this.deleteConflicts,
|
|
|
this.edgeSourceConflicts,
|
|
|
+ this.edgeTargetConflicts,
|
|
|
this.updateConflicts,
|
|
|
);
|
|
|
}
|
|
@@ -105,7 +127,6 @@ export class EdgeCreation implements Delta {
|
|
|
// Inverse dependency
|
|
|
// NodeDeletion if source of edge is deleted.
|
|
|
overwrittenBy: Array<EdgeUpdate | NodeDeletion> = []; // append-only
|
|
|
- // overwrittenBy: Array<EdgeUpdate> = []; // append-only
|
|
|
|
|
|
// Conflicts: Concurrent creations of the same edge.
|
|
|
createConflicts: Array<EdgeCreation> = []; // append-only
|
|
@@ -113,6 +134,9 @@ export class EdgeCreation implements Delta {
|
|
|
// Conflicts: Concurrent deletions of source node.
|
|
|
deleteSourceConflicts: Array<NodeDeletion> = []; // append-only
|
|
|
|
|
|
+ // Conflicts: Concurrent deletion of target node.
|
|
|
+ deleteTargetConflicts: Array<NodeDeletion> = []; // append-only
|
|
|
+
|
|
|
constructor(source: NodeCreation, label: string, target: NodeCreation) {
|
|
|
this.source = source;
|
|
|
this.label = label;
|
|
@@ -132,14 +156,31 @@ export class EdgeCreation implements Delta {
|
|
|
// Concurrent deletions of source node
|
|
|
for (const sourceDeletion of this.source.deletions) {
|
|
|
if (sourceDeletion.deletedEdges.some(edge => edge.getCreation() === this))
|
|
|
- throw new Error("Assertion failed - did not expect existing deletion to be aware of a new edge creation")
|
|
|
+ throw new Error("Assertion failed - did not expect existing deletion to be aware of a new edge creation");
|
|
|
// Symmetric
|
|
|
this.deleteSourceConflicts.push(sourceDeletion);
|
|
|
sourceDeletion.edgeSourceConflicts.push(this);
|
|
|
}
|
|
|
|
|
|
+ // Concurrent deletion of target node
|
|
|
+ for (const targetDeletion of this.target.deletions) {
|
|
|
+ if (targetDeletion.deletedEdges.some(edge => {
|
|
|
+ while (true) {
|
|
|
+ if (edge === this) return true;
|
|
|
+ if (edge instanceof EdgeUpdate) edge = edge.overwrites;
|
|
|
+ else return false;
|
|
|
+ }
|
|
|
+ })) {
|
|
|
+ throw new Error("Assertion failed - did not expect existing deletion to be aware of a new edge update");
|
|
|
+ }
|
|
|
+ // Symmetric
|
|
|
+ this.deleteTargetConflicts.push(targetDeletion);
|
|
|
+ targetDeletion.edgeTargetConflicts.push(this);
|
|
|
+ }
|
|
|
+
|
|
|
// Create inverse dependency
|
|
|
this.source.outgoingEdges.push(this);
|
|
|
+ this.target.incomingEdges.push(this);
|
|
|
}
|
|
|
|
|
|
// Helper
|
|
@@ -155,6 +196,7 @@ export class EdgeCreation implements Delta {
|
|
|
return Array<Delta>().concat(
|
|
|
this.createConflicts,
|
|
|
this.deleteSourceConflicts,
|
|
|
+ this.deleteTargetConflicts,
|
|
|
);
|
|
|
}
|
|
|
}
|
|
@@ -167,25 +209,45 @@ export class EdgeUpdate implements Delta {
|
|
|
// Inverse dependency
|
|
|
// NodeDeletion if source of edge is deleted.
|
|
|
overwrittenBy: Array<EdgeUpdate | NodeDeletion> = []; // append-only
|
|
|
- // overwrittenBy: Array<EdgeUpdate> = []; // append-only
|
|
|
|
|
|
// Conflicts: Concurrent updates
|
|
|
- // updateConflicts: Array<EdgeUpdate | NodeDeletion> = []; // append-only
|
|
|
updateConflicts: Array<EdgeUpdate | NodeDeletion> = []; // append-only
|
|
|
|
|
|
+ // Conflicts: Concurrent deletion of target node.
|
|
|
+ deleteTargetConflicts: Array<NodeDeletion> = []; // append-only
|
|
|
+
|
|
|
constructor(overwrites: EdgeCreation | EdgeUpdate, newTarget: NodeCreation) {
|
|
|
this.overwrites = overwrites;
|
|
|
this.newTarget = newTarget;
|
|
|
|
|
|
// Detect conflicts
|
|
|
+
|
|
|
+ // Concurrent updates (by EdgeUpdate or NodeDeletion)
|
|
|
for (const concurrentUpdate of this.overwrites.overwrittenBy) {
|
|
|
// Symmetric
|
|
|
this.updateConflicts.push(concurrentUpdate);
|
|
|
concurrentUpdate.updateConflicts.push(this);
|
|
|
}
|
|
|
|
|
|
+ // Concurrent deletion of target node
|
|
|
+ for (const targetDeletion of this.newTarget.deletions) {
|
|
|
+ if (targetDeletion.deletedEdges.some(edge => {
|
|
|
+ while (true) {
|
|
|
+ if (edge === this) return true;
|
|
|
+ if (edge instanceof EdgeUpdate) edge = edge.overwrites;
|
|
|
+ else return false;
|
|
|
+ }
|
|
|
+ })) {
|
|
|
+ throw new Error("Assertion failed - did not expect existing deletion to be aware of a new edge update");
|
|
|
+ }
|
|
|
+ // Symmetric
|
|
|
+ this.deleteTargetConflicts.push(targetDeletion);
|
|
|
+ targetDeletion.edgeTargetConflicts.push(this);
|
|
|
+ }
|
|
|
+
|
|
|
// Create inverse dependency
|
|
|
this.overwrites.overwrittenBy.push(this);
|
|
|
+ this.newTarget.incomingEdges.push(this);
|
|
|
}
|
|
|
|
|
|
// Helper
|
|
@@ -198,7 +260,10 @@ export class EdgeUpdate implements Delta {
|
|
|
}
|
|
|
|
|
|
getConflicts(): Array<Delta> {
|
|
|
- return this.updateConflicts;
|
|
|
+ return Array<Delta>().concat(
|
|
|
+ this.updateConflicts,
|
|
|
+ this.deleteTargetConflicts,
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
|