delta.test.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import {Delta, TargetNode, TargetValue} from "../lib/delta";
  2. import {DeltaRegistry} from "../lib/delta_registry";
  3. import {mockUuid,} from "../lib/util/test_helpers";
  4. import {assert,} from "../lib/util/assert";
  5. describe("Primitive Delta", () => {
  6. it("Delete/delete node conflict", () => {
  7. const registry = new DeltaRegistry();
  8. const getId = mockUuid();
  9. const creation = registry.newNodeCreation(getId());
  10. assert(creation.conflictsWith.length === 0, "did not expect node creation to be conflicting with any other operation");
  11. const deletion1 = registry.newNodeDeletion(creation, [], []);
  12. assert(deletion1.conflictsWith.length === 0, "did not expect first deletion alone to be conflicting with anything");
  13. const deletion2 = registry.newNodeDeletion(creation, [], []);
  14. assert(deletion1 === deletion2, "expected deltas to be identical");
  15. assert(deletion1.conflictsWith.length === 0, "expected no conflicts");
  16. assert(deletion1.hash.equals(deletion2.hash), "deletions should have equal hash");
  17. });
  18. it("Create/create edge conflict", () => {
  19. const registry = new DeltaRegistry();
  20. const getId = mockUuid();
  21. const sourceCreation = registry.newNodeCreation(getId());
  22. const target1Creation = registry.newNodeCreation(getId());
  23. const target2Creation = registry.newNodeCreation(getId());
  24. const edge1Creation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), target1Creation);
  25. assert(edge1Creation.conflictsWith.length === 0, "expected a single edge alone to not be involved in conflicts");
  26. const edge2Creation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), target2Creation);
  27. assert(edge1Creation.conflictsWith.length === 1, "expected conflict: same edge created twice, concurrently")
  28. assert(edge2Creation.conflictsWith.length === 1, "expected conflict: same edge created twice, concurrently")
  29. });
  30. it("Update/update edge conflict", () => {
  31. const registry = new DeltaRegistry();
  32. const getId = mockUuid();
  33. const sourceCreation = registry.newNodeCreation(getId());
  34. const targetCreation = registry.newNodeCreation(getId());
  35. const newTarget1Creation = registry.newNodeCreation(getId());
  36. const newTarget2Creation = registry.newNodeCreation(getId());
  37. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  38. const update1 = registry.newEdgeUpdate(edgeCreation.overwrite(), newTarget1Creation);
  39. assert(update1.conflictsWith.length === 0, "expected no conflict with a single edge update.")
  40. const update2 = registry.newEdgeUpdate(edgeCreation.overwrite(),newTarget2Creation);
  41. assert(update1.conflictsWith.length === 1, "expected conflict between concurrent edge updates.")
  42. assert(update2.conflictsWith.length === 1, "expected conflict between concurrent edge updates.")
  43. });
  44. it("Delete/require (edge source) conflict", () => {
  45. const registry = new DeltaRegistry();
  46. const getId = mockUuid();
  47. const sourceCreation = registry.newNodeCreation(getId());
  48. const targetCreation = registry.newNodeCreation(getId());
  49. const sourceDeletion = registry.newNodeDeletion(sourceCreation, [], []);
  50. assert(sourceDeletion.conflictsWith.length === 0, "expected no conflicts so far");
  51. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  52. assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
  53. assert(sourceDeletion.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
  54. });
  55. it("Delete/require (edge source) conflict (reverse order)", () => {
  56. const registry = new DeltaRegistry();
  57. const getId = mockUuid();
  58. // same as before, but now the 'order' of edgeCreation and sourceDeletion is reversed.
  59. const sourceCreation = registry.newNodeCreation(getId());
  60. const targetCreation = registry.newNodeCreation(getId());
  61. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  62. assert(edgeCreation.conflictsWith.length === 0, "expected no conflicts so far");
  63. const sourceDeletion = registry.newNodeDeletion(sourceCreation, [], []);
  64. assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
  65. assert(sourceDeletion.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
  66. });
  67. it("Require (edge source), then delete (no conflict)", () => {
  68. const registry = new DeltaRegistry();
  69. const getId = mockUuid();
  70. // proper way of deleting the source of an edge: the deletion must depend on the edgeCreation
  71. const sourceCreation = registry.newNodeCreation(getId());
  72. const targetCreation = registry.newNodeCreation(getId());
  73. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  74. const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
  75. const sourceDeletion = registry.newNodeDeletion(sourceCreation, [edgeDeletion], []);
  76. assert(sourceDeletion.conflictsWith.length === 0, "Since node deletion was aware of outgoing edge deletion, there should be no conflict");
  77. });
  78. it("Delete/require (edge source) conflict (3)", () => {
  79. const registry = new DeltaRegistry();
  80. const getId = mockUuid();
  81. // Same as (2), really, because the additional EdgeUpdate doesn't change anything.
  82. // Only the earliest conflict, between EdgeCreation and source NodeDeletion, matters.
  83. const sourceCreation = registry.newNodeCreation(getId());
  84. const targetCreation = registry.newNodeCreation(getId());
  85. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  86. const newTargetCreation = registry.newNodeCreation(getId());
  87. const edgeUpdate = registry.newEdgeUpdate(edgeCreation.overwrite(), newTargetCreation);
  88. // no conflicts so far
  89. const sourceDeletion = registry.newNodeDeletion(sourceCreation, [], []);
  90. assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
  91. assert(sourceDeletion.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
  92. assert(edgeUpdate.conflictsWith.length === 0, "edge update should not be involved in any conflict");
  93. });
  94. it("Update edge after deletion of source node conflict", () => {
  95. const registry = new DeltaRegistry();
  96. const getId = mockUuid();
  97. // create edge between 2 nodes, and then properly delete the source node
  98. const sourceCreation = registry.newNodeCreation(getId());
  99. const targetCreation = registry.newNodeCreation(getId());
  100. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  101. const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
  102. const sourceDeletion = registry.newNodeDeletion(sourceCreation, [edgeDeletion], []);
  103. // no conflicts so far
  104. const newTargetCreation = registry.newNodeCreation(getId()); // no conflict
  105. const edgeUpdate = registry.newEdgeUpdate(edgeCreation.overwrite(), newTargetCreation);
  106. // console.log("edgeUpdate.conflictsWith", edgeUpdate.conflictsWith, "edgeDeletion.conflictsWith", edgeDeletion.conflictsWith);
  107. assert(edgeUpdate.conflictsWith.length === 2, "expected U/U conflict");
  108. assert(edgeUpdate.conflictsWith.some(([d])=>d===edgeDeletion), "expected U/U conflict");
  109. assert(edgeUpdate.conflictsWith.some(([d])=>d===sourceDeletion), "expected U/U conflict");
  110. assert(edgeDeletion.conflictsWith.length === 1, "expected U/U conflict");
  111. assert(edgeDeletion.conflictsWith.some(([d])=>d===edgeUpdate), "expected U/U conflict");
  112. });
  113. it("Delete/require (edge target) conflict", () => {
  114. const registry = new DeltaRegistry();
  115. const getId = mockUuid();
  116. const sourceCreation = registry.newNodeCreation(getId());
  117. const targetCreation = registry.newNodeCreation(getId());
  118. const targetDeletion = registry.newNodeDeletion(targetCreation, [], []);
  119. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  120. assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict");
  121. assert(targetDeletion.conflictsWith.length === 1, "expected require/delete conflict");
  122. });
  123. it("Delete/require (edge target) conflict (reverse order)", () => {
  124. const registry = new DeltaRegistry();
  125. const getId = mockUuid();
  126. const sourceCreation = registry.newNodeCreation(getId());
  127. const targetCreation = registry.newNodeCreation(getId());
  128. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  129. // delete target of edge, unaware that edge exists:
  130. const targetDeletion = registry.newNodeDeletion(targetCreation, [], []);
  131. assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict");
  132. assert(targetDeletion.conflictsWith.length === 1, "expected require/delete conflict");
  133. });
  134. it("Require (edge target), then delete (no conflict)", () => {
  135. const registry = new DeltaRegistry();
  136. const getId = mockUuid();
  137. const sourceCreation = registry.newNodeCreation(getId());
  138. const targetCreation = registry.newNodeCreation(getId());
  139. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  140. const edgeUpdate = registry.newEdgeUpdate(edgeCreation.overwrite(), sourceCreation); // turn edge into self-edge
  141. // because of the edgeUpdate, 'target' is no longer the target of the edge, and can be deleted:
  142. const targetDeletion = registry.newNodeDeletion(targetCreation, [], [edgeUpdate]);
  143. // console.log(edgeCreation.conflictsWith)
  144. assert(edgeCreation.conflictsWith.length === 0, "expected no require/delete conflict");
  145. assert(targetDeletion.conflictsWith.length === 0, "expected no require/delete conflict");
  146. });
  147. it("Delete source and target of edge (no conflict)", () => {
  148. const registry = new DeltaRegistry();
  149. const getId = mockUuid();
  150. const sourceCreation = registry.newNodeCreation(getId());
  151. const targetCreation = registry.newNodeCreation(getId());
  152. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
  153. const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
  154. const sourceDeletion = registry.newNodeDeletion(sourceCreation, [edgeDeletion], []);
  155. assert(sourceDeletion.conflictsWith.length === 0, "expected no conflicts");
  156. const targetDeletion = registry.newNodeDeletion(targetCreation, [], [edgeDeletion]);
  157. assert(targetDeletion.conflictsWith.length === 0, "expected no conflicts");
  158. });
  159. it("Delete node with self-edge", () => {
  160. const registry = new DeltaRegistry();
  161. const getId = mockUuid();
  162. const nodeCreation = registry.newNodeCreation(getId());
  163. const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(nodeCreation, "label"), nodeCreation);
  164. const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
  165. const nodeDeletion = registry.newNodeDeletion(nodeCreation, [edgeDeletion], [edgeDeletion]);
  166. // console.log(nodeDeletion.conflictsWith);
  167. assert(nodeDeletion.conflictsWith.length === 0, "expected no conflicts");
  168. });
  169. it("Read/write conflict", () => {
  170. const registry = new DeltaRegistry();
  171. const getId = mockUuid();
  172. const envCreation = registry.newNodeCreation(getId());
  173. const xInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "x"), 1);
  174. const yInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "y"), 2);
  175. // so now, x == 1, y == 2
  176. // then, x := x + 1
  177. const xUpdate = registry.newEdgeUpdate(xInitial.overwrite(), 2, [xInitial.overwrite()]);
  178. // and concurrently, y := x + y
  179. const yUpdate = registry.newEdgeUpdate(yInitial.overwrite(), 3, [xInitial.overwrite(), yInitial.overwrite()]);
  180. assert(xUpdate.conflictsWith.length === 1 && yUpdate.conflictsWith.length === 1, "expected one conflict");
  181. assert(xUpdate.conflictsWith.some(([d]) => d === yUpdate), "expected one conflict");
  182. });
  183. it("No Read/Read conflict", () => {
  184. const registry = new DeltaRegistry();
  185. const getId = mockUuid();
  186. const envCreation = registry.newNodeCreation(getId());
  187. const xInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "x"), 1);
  188. // so now, x == 1
  189. // then, y := x + 1
  190. const yInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "y"), 2, [xInitial.overwrite()]);
  191. // and concurrently, z := x + 2
  192. const zInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "z"), 3, [xInitial.overwrite()]);
  193. assert(yInitial.conflictsWith.length === 0 && zInitial.conflictsWith.length === 0, "expected no conflicts");
  194. });
  195. it("R*/U conflict", () => {
  196. const registry = new DeltaRegistry();
  197. const getId = mockUuid();
  198. const collection = registry.newNodeCreation(getId());
  199. const newEdgeX = registry.newEdgeCreation(collection, "x");
  200. assert(newEdgeX === registry.newEdgeCreation(collection, "x"), "Should return same object");
  201. const addItemX = registry.newEdgeUpdate(newEdgeX, "x");
  202. assert(newEdgeX === registry.newEdgeCreation(collection, "x"), "Should return same object");
  203. assert(addItemX.conflictsWith.length === 0, "No conflicts so far");
  204. const readAll = registry.newReadAllOutgoing(collection, [newEdgeX]);
  205. // No conflict so far...
  206. // Now we'll create our conflict:
  207. const newEdgeY = registry.newEdgeCreation(collection, "y");
  208. const addItemY = registry.newEdgeUpdate(newEdgeY, "y");
  209. console.log(collection);
  210. console.log("RACW:", readAll.conflictsWith)
  211. console.log("NIYCW:", newEdgeY.conflictsWith)
  212. assert(readAll.conflictsWith.length === 1
  213. && newEdgeY.conflictsWith.length === 1, "Expected one conflict here");
  214. assert(readAll.conflictsWith.some(([d]) => d === newEdgeY)
  215. && newEdgeY.conflictsWith.some(([d]) => d === readAll), "Expected 'addItemY' and 'readAll' to be conflicting");
  216. assert(newEdgeX.conflictsWith.length === 0, "Still no conflicts here");
  217. })
  218. });