Przeglądaj źródła

NodeDeletion acts as EdgeUpdate in the context of update/update conflicts.

Joeri Exelmans 3 lat temu
rodzic
commit
1e51259e9c
2 zmienionych plików z 50 dodań i 30 usunięć
  1. 14 14
      src/onion/micro_op.test.ts
  2. 36 16
      src/onion/micro_op.ts

+ 14 - 14
src/onion/micro_op.test.ts

@@ -118,20 +118,20 @@ describe("Delta", () => {
     assert(sourceDeletion.getConflicts().length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
   });
 
-  // it("Update edge after deletion of source node (= update/update) conflict", () => {
-  //   // create edge between 2 nodes, and then properly delete the source node
-  //   const sourceCreation = new NodeCreation(getId());
-  //   const targetCreation = new NodeCreation(getId());
-  //   const edgeCreation = new EdgeCreation(sourceCreation, "label", targetCreation);
-  //   const sourceDeletion = new NodeDeletion(sourceCreation, [edgeCreation]);
-
-  //   // no conflicts so far
-
-  //   const newTargetCreation = new NodeCreation(getId()); // no conflict
-  //   const edgeUpdate = new EdgeUpdate(edgeCreation, newTargetCreation);
-  //   assert(edgeUpdate.getConflicts().length === 1, "expected conflict between edgeUpdate and sourceDeletion");
-  //   assert(sourceDeletion.getConflicts().length === 1, "expected conflict between edgeUpdate and sourceDeletion");
-  // })
+  it("Update edge after deletion of source node (= update/update) conflict", () => {
+    // create edge between 2 nodes, and then properly delete the source node
+    const sourceCreation = new NodeCreation(getId());
+    const targetCreation = new NodeCreation(getId());
+    const edgeCreation = new EdgeCreation(sourceCreation, "label", targetCreation);
+    const sourceDeletion = new NodeDeletion(sourceCreation, [edgeCreation]);
+
+    // no conflicts so far
+
+    const newTargetCreation = new NodeCreation(getId()); // no conflict
+    const edgeUpdate = new EdgeUpdate(edgeCreation, newTargetCreation);
+    assert(edgeUpdate.getConflicts().length === 1, "expected conflict between edgeUpdate and sourceDeletion");
+    assert(sourceDeletion.getConflicts().length === 1, "expected conflict between edgeUpdate and sourceDeletion");
+  });
 
 
 

+ 36 - 16
src/onion/micro_op.ts

@@ -30,8 +30,8 @@ export class NodeDeletion implements Delta {
   // Dependency: The node being deleted.
   readonly creation: NodeCreation;
 
-  // Dependency: Deletion of a node depends on deletion of its outgoing edges.
-  readonly deletedOutgoingEdges: Array<EdgeCreation>;
+  // Dependency: All incoming and outgoing edges of this node must be deleted also.
+  readonly deletedEdges: Array<EdgeCreation | EdgeUpdate>;
 
   // Conflicts: Concurrent deletion of the same node.
   deleteConflicts: Array<NodeDeletion> = [];
@@ -40,11 +40,11 @@ export class NodeDeletion implements Delta {
   edgeSourceConflicts: Array<EdgeCreation> = [];
 
   // Conflicts: Concurrent update of an edge, when the edge is also deleted.
-  // updateConflicts: Array<EdgeUpdate> = [];
+  updateConflicts: Array<EdgeUpdate | NodeDeletion> = [];
 
-  constructor(creation: NodeCreation, deletedOutgoingEdges: Array<EdgeCreation>) {
+  constructor(creation: NodeCreation, deletedEdges: Array<EdgeCreation | EdgeUpdate>) {
     this.creation = creation;
-    this.deletedOutgoingEdges = deletedOutgoingEdges;
+    this.deletedEdges = deletedEdges;
 
     // Detect conflicts
 
@@ -57,20 +57,30 @@ export class NodeDeletion implements Delta {
 
     // Concurrently created outgoing edges of this node
     for (const outgoingEdgeCreation of this.creation.outgoingEdges) {
-      if (!this.deletedOutgoingEdges.includes(outgoingEdgeCreation)) {
+      if (!this.deletedEdges.some(edge => edge.getCreation() === outgoingEdgeCreation)) {
         // Symmetric
         this.edgeSourceConflicts.push(outgoingEdgeCreation);
         outgoingEdgeCreation.deleteSourceConflicts.push(this);
       }
     }
 
+    // Concurrent edge updates
+    for (const deletedEdge of this.deletedEdges) {
+      for (const concurrentEdgeUpdate of deletedEdge.overwrittenBy) {
+        // Symmetric
+        this.updateConflicts.push(concurrentEdgeUpdate);
+        concurrentEdgeUpdate.updateConflicts.push(this);
+      }
+    }
+
     // Create inverse dependencies
 
     this.creation.deletions.push(this);
 
-    // for (const deletedOutgoingEdge of this.deletedOutgoingEdges) {
-    //   deletedOutgoingEdge.overwrittenBy.push(this);
-    // }
+    for (const deletedEdge of this.deletedEdges) {
+      // NodeDeletion acts a bit as an EdgeUpdate here
+      deletedEdge.overwrittenBy.push(this);
+    }
   }
 
   getDependencies(): [NodeCreation] {
@@ -81,7 +91,7 @@ export class NodeDeletion implements Delta {
     return Array<Delta>().concat(
       this.deleteConflicts,
       this.edgeSourceConflicts,
-      // this.updateConflicts,
+      this.updateConflicts,
     );
   }
 }
@@ -94,8 +104,8 @@ 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
+  overwrittenBy: Array<EdgeUpdate | NodeDeletion> = []; // append-only
+  // overwrittenBy: Array<EdgeUpdate> = []; // append-only
 
   // Conflicts: Concurrent creations of the same edge.
   createConflicts: Array<EdgeCreation> = []; // append-only
@@ -121,7 +131,7 @@ export class EdgeCreation implements Delta {
 
     // Concurrent deletions of source node
     for (const sourceDeletion of this.source.deletions) {
-      if (sourceDeletion.deletedOutgoingEdges.includes(this))
+      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")
       // Symmetric
       this.deleteSourceConflicts.push(sourceDeletion);
@@ -132,6 +142,11 @@ export class EdgeCreation implements Delta {
     this.source.outgoingEdges.push(this);
   }
 
+  // Helper
+  getCreation(): EdgeCreation {
+    return this;
+  }
+
   getDependencies(): [NodeCreation, NodeCreation] {
     return [this.source, this.target];
   }
@@ -151,12 +166,12 @@ 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
+  overwrittenBy: Array<EdgeUpdate | NodeDeletion> = []; // append-only
+  // overwrittenBy: Array<EdgeUpdate> = []; // append-only
 
   // Conflicts: Concurrent updates
   // updateConflicts: Array<EdgeUpdate | NodeDeletion> = []; // append-only
-  updateConflicts: Array<EdgeUpdate> = []; // append-only
+  updateConflicts: Array<EdgeUpdate | NodeDeletion> = []; // append-only
 
   constructor(overwrites: EdgeCreation | EdgeUpdate, newTarget: NodeCreation) {
     this.overwrites = overwrites;
@@ -173,6 +188,11 @@ export class EdgeUpdate implements Delta {
     this.overwrites.overwrittenBy.push(this);
   }
 
+  // Helper
+  getCreation(): EdgeCreation {
+    return this.overwrites.getCreation();
+  }
+
   getDependencies(): Array<Delta> {
     return [this.overwrites, this.newTarget];
   }