@feltdb/core 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/dist/agent-state.d.ts +20 -0
  2. package/dist/agent-state.d.ts.map +1 -1
  3. package/dist/agent-state.js +36 -0
  4. package/dist/build-identity.json +12 -4
  5. package/dist/cli/commands.js +187 -4
  6. package/dist/cli/publish-engine.js +116 -16
  7. package/dist/collection.d.ts.map +1 -1
  8. package/dist/collection.js +3 -0
  9. package/dist/create/build-identity.json +12 -4
  10. package/dist/create/managed-account.js +93 -35
  11. package/dist/create/package-versions.js +1 -1
  12. package/dist/create/server-source/Cargo.lock +6 -509
  13. package/dist/create/server-source/Cargo.toml +2 -13
  14. package/dist/create/server-source/crates/feltdb-client/Cargo.toml +1 -1
  15. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +1 -1
  16. package/dist/create/server-source/crates/feltdb-server/src/main.rs +255 -12
  17. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +1 -1
  18. package/dist/db.d.ts +11 -1
  19. package/dist/db.d.ts.map +1 -1
  20. package/dist/db.js +15 -2
  21. package/dist/error-codes.d.ts +5 -0
  22. package/dist/error-codes.d.ts.map +1 -1
  23. package/dist/error-codes.js +9 -1
  24. package/dist/http-db.d.ts +16 -0
  25. package/dist/http-db.d.ts.map +1 -1
  26. package/dist/http-db.js +36 -6
  27. package/dist/managed-recovery.d.ts +6 -0
  28. package/dist/managed-recovery.d.ts.map +1 -1
  29. package/dist/managed-recovery.js +9 -0
  30. package/dist/studio-app/assets/{feltdb_wasm-BjM1ylL3.js → feltdb_wasm-DtybUY0q.js} +1 -1
  31. package/dist/studio-app/assets/feltdb_wasm_bg-DsgIUwaB.wasm +0 -0
  32. package/dist/studio-app/assets/index-C4OffsZl.js +37 -0
  33. package/dist/studio-app/index.html +1 -1
  34. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  35. package/dist/wasm/package.json +1 -1
  36. package/dist/workload.d.ts +55 -3
  37. package/dist/workload.d.ts.map +1 -1
  38. package/dist/workload.js +23 -0
  39. package/dist/workspace/workspace-connection.d.ts.map +1 -1
  40. package/dist/workspace/workspace-connection.js +14 -3
  41. package/package.json +1 -1
  42. package/dist/create/server-source/crates/feltdb/Cargo.lock +0 -175
  43. package/dist/create/server-source/crates/feltdb/Cargo.toml +0 -102
  44. package/dist/create/server-source/crates/feltdb/LICENSE +0 -21
  45. package/dist/create/server-source/crates/feltdb/README.md +0 -75
  46. package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +0 -370
  47. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +0 -589
  48. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +0 -247
  49. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +0 -440
  50. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +0 -275
  51. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +0 -381
  52. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +0 -467
  53. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +0 -489
  54. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +0 -281
  55. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +0 -606
  56. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +0 -531
  57. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +0 -462
  58. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +0 -505
  59. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +0 -484
  60. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +0 -758
  61. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +0 -421
  62. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +0 -329
  63. package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +0 -1705
  64. package/dist/create/server-source/crates/feltdb/src/acquisition.rs +0 -286
  65. package/dist/create/server-source/crates/feltdb/src/admission.rs +0 -185
  66. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +0 -507
  67. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +0 -539
  68. package/dist/create/server-source/crates/feltdb/src/agent_state.rs +0 -320
  69. package/dist/create/server-source/crates/feltdb/src/agent_transition_contract.rs +0 -1545
  70. package/dist/create/server-source/crates/feltdb/src/analytics.rs +0 -521
  71. package/dist/create/server-source/crates/feltdb/src/application.rs +0 -2674
  72. package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +0 -1070
  73. package/dist/create/server-source/crates/feltdb/src/authority.rs +0 -479
  74. package/dist/create/server-source/crates/feltdb/src/authority_failover.rs +0 -847
  75. package/dist/create/server-source/crates/feltdb/src/authorization.rs +0 -1030
  76. package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +0 -1122
  77. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority-client.rs +0 -32
  78. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority.rs +0 -19
  79. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +0 -1474
  80. package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +0 -8
  81. package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +0 -418
  82. package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +0 -482
  83. package/dist/create/server-source/crates/feltdb/src/capability.rs +0 -903
  84. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +0 -263
  85. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -77
  86. package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +0 -496
  87. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +0 -2574
  88. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +0 -212
  89. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +0 -422
  90. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +0 -445
  91. package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +0 -465
  92. package/dist/create/server-source/crates/feltdb/src/convergence.rs +0 -634
  93. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +0 -427
  94. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +0 -376
  95. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +0 -365
  96. package/dist/create/server-source/crates/feltdb/src/cron.rs +0 -294
  97. package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +0 -483
  98. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +0 -477
  99. package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +0 -1003
  100. package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +0 -1334
  101. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +0 -368
  102. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +0 -231
  103. package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +0 -482
  104. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +0 -203
  105. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +0 -317
  106. package/dist/create/server-source/crates/feltdb/src/equality_index.rs +0 -595
  107. package/dist/create/server-source/crates/feltdb/src/execution.rs +0 -426
  108. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +0 -214
  109. package/dist/create/server-source/crates/feltdb/src/indexing.rs +0 -776
  110. package/dist/create/server-source/crates/feltdb/src/lib.rs +0 -7365
  111. package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +0 -234
  112. package/dist/create/server-source/crates/feltdb/src/materialization.rs +0 -184
  113. package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -266
  114. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +0 -310
  115. package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +0 -736
  116. package/dist/create/server-source/crates/feltdb/src/observability.rs +0 -379
  117. package/dist/create/server-source/crates/feltdb/src/operation.rs +0 -290
  118. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +0 -439
  119. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +0 -349
  120. package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +0 -332
  121. package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +0 -718
  122. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +0 -487
  123. package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +0 -166
  124. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +0 -289
  125. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +0 -566
  126. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +0 -3391
  127. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +0 -203
  128. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +0 -261
  129. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +0 -502
  130. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +0 -216
  131. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +0 -303
  132. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +0 -323
  133. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +0 -531
  134. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +0 -406
  135. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +0 -436
  136. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +0 -387
  137. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +0 -378
  138. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +0 -359
  139. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +0 -372
  140. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +0 -404
  141. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +0 -376
  142. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +0 -461
  143. package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +0 -2125
  144. package/dist/create/server-source/crates/feltdb/src/production_api.rs +0 -418
  145. package/dist/create/server-source/crates/feltdb/src/provenance.rs +0 -207
  146. package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +0 -126
  147. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +0 -218
  148. package/dist/create/server-source/crates/feltdb/src/read_basis.rs +0 -257
  149. package/dist/create/server-source/crates/feltdb/src/references.rs +0 -404
  150. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +0 -401
  151. package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +0 -501
  152. package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +0 -680
  153. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +0 -163
  154. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +0 -133
  155. package/dist/create/server-source/crates/feltdb/src/routing.rs +0 -409
  156. package/dist/create/server-source/crates/feltdb/src/semantic_decision.rs +0 -2029
  157. package/dist/create/server-source/crates/feltdb/src/semantic_query.rs +0 -599
  158. package/dist/create/server-source/crates/feltdb/src/sharding.rs +0 -549
  159. package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +0 -516
  160. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +0 -6003
  161. package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +0 -222
  162. package/dist/create/server-source/crates/feltdb/src/state_facade.rs +0 -320
  163. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +0 -291
  164. package/dist/create/server-source/crates/feltdb/src/state_model.rs +0 -4158
  165. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +0 -379
  166. package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +0 -673
  167. package/dist/create/server-source/crates/feltdb/src/storage.rs +0 -273
  168. package/dist/create/server-source/crates/feltdb/src/submission.rs +0 -471
  169. package/dist/create/server-source/crates/feltdb/src/sync.rs +0 -495
  170. package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +0 -822
  171. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +0 -433
  172. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +0 -462
  173. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +0 -965
  174. package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +0 -1120
  175. package/dist/create/server-source/crates/feltdb/src/transactions.rs +0 -643
  176. package/dist/create/server-source/crates/feltdb/src/trigger.rs +0 -390
  177. package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +0 -749
  178. package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +0 -708
  179. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +0 -1269
  180. package/dist/create/server-source/crates/feltdb/src/workflow.rs +0 -713
  181. package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +0 -510
  182. package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +0 -354
  183. package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +0 -594
  184. package/dist/create/server-source/crates/feltdb/src/workload.rs +0 -4222
  185. package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +0 -456
  186. package/dist/create/server-source/crates/feltdb/tests/agent_state_native.rs +0 -1188
  187. package/dist/create/server-source/crates/feltdb/tests/agent_transition_contract_conformance.rs +0 -2884
  188. package/dist/create/server-source/crates/feltdb/tests/authority_process.rs +0 -297
  189. package/dist/create/server-source/crates/feltdb/tests/authority_transaction_identity.rs +0 -534
  190. package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +0 -132
  191. package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +0 -299
  192. package/dist/create/server-source/crates/feltdb/tests/common/mod.rs +0 -96
  193. package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +0 -272
  194. package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +0 -467
  195. package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +0 -309
  196. package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +0 -445
  197. package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +0 -518
  198. package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +0 -392
  199. package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +0 -850
  200. package/dist/create/server-source/crates/feltdb/tests/fixtures/agent_transition_corpus.json +0 -23340
  201. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +0 -1916
  202. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +0 -1878
  203. package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +0 -1862
  204. package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +0 -123
  205. package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +0 -299
  206. package/dist/create/server-source/crates/feltdb/tests/local_process_ownership.rs +0 -69
  207. package/dist/create/server-source/crates/feltdb/tests/managed_incident_regression.rs +0 -191
  208. package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +0 -278
  209. package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +0 -234
  210. package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +0 -947
  211. package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +0 -404
  212. package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +0 -916
  213. package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +0 -1045
  214. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +0 -347
  215. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +0 -361
  216. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +0 -330
  217. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +0 -93
  218. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +0 -622
  219. package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +0 -517
  220. package/dist/create/server-source/crates/feltdb/tests/production_certification.rs +0 -1153
  221. package/dist/create/server-source/crates/feltdb/tests/production_contract.rs +0 -771
  222. package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +0 -1383
  223. package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +0 -868
  224. package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +0 -221
  225. package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +0 -417
  226. package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +0 -338
  227. package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +0 -1039
  228. package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +0 -739
  229. package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +0 -452
  230. package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +0 -81
  231. package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +0 -438
  232. package/dist/create/server-source/crates/feltdb/tests/semantic_decision.rs +0 -1165
  233. package/dist/create/server-source/crates/feltdb/tests/semantic_query.rs +0 -767
  234. package/dist/create/server-source/crates/feltdb/tests/semantic_query_contract.rs +0 -363
  235. package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +0 -1799
  236. package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +0 -1316
  237. package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +0 -358
  238. package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +0 -365
  239. package/dist/create/server-source/crates/feltdb/tests/state_ref_authority.rs +0 -918
  240. package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +0 -299
  241. package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +0 -328
  242. package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +0 -255
  243. package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +0 -249
  244. package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +0 -994
  245. package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +0 -484
  246. package/dist/studio-app/assets/feltdb_wasm_bg-TseJn2Ax.wasm +0 -0
  247. package/dist/studio-app/assets/index-1wd0uKua.js +0 -30
@@ -1,2574 +0,0 @@
1
- /// Causal Consistency Dependency Barrier (Gate 4)
2
- ///
3
- /// Implements the critical invariant: No operation becomes visible at a replica
4
- /// until all causal dependencies encoded by its StateVersion are satisfied.
5
- ///
6
- /// Explicit state machine:
7
- ///
8
- /// received operation O
9
- /// ↓
10
- /// check_dependencies(O)
11
- /// ↓
12
- /// dependencies_satisfied?
13
- /// / \
14
- /// yes no
15
- /// ↓ ↓
16
- /// READY WAITING_FOR_CAUSAL_DEPENDENCIES
17
- /// ↓ ↓
18
- /// APPLIED (buffered, waiting)
19
- /// ↓
20
- /// (when new op arrives that unblocks O)
21
- /// ↓
22
- /// re-check_dependencies(O)
23
- /// ↓
24
- /// READY
25
- /// ↓
26
- /// APPLIED
27
- use crate::convergence::VectorClock;
28
- use std::collections::HashMap;
29
-
30
- /// Metrics for the causal dependency barrier (Phase 7.0.1 audit instrumentation)
31
- #[derive(Clone, Default, Debug)]
32
- pub struct BarrierMetrics {
33
- /// Number of times frontier was checked for readiness
34
- pub frontier_checks: u64,
35
- /// Number of times pending buffer was scanned
36
- pub pending_scans: u64,
37
- /// Total entries examined during scans
38
- pub pending_entries_examined: u64,
39
- /// Operations released due to frontier advancement
40
- pub operations_released: u64,
41
- }
42
-
43
- /// Phase 7.1-opt: Dependency index for selective candidate discovery
44
- /// Maps (node, timestamp) → list of operation IDs waiting for that dependency
45
- #[derive(Clone, Default, Debug)]
46
- pub struct DependencyIndex {
47
- /// For each (node, timestamp) in a required vector clock,
48
- /// track which pending operations depend on it
49
- pub waiters: HashMap<(String, u64), Vec<String>>,
50
- }
51
-
52
- impl DependencyIndex {
53
- /// Get the number of index entries
54
- pub fn entry_count(&self) -> usize {
55
- self.waiters.len()
56
- }
57
- }
58
-
59
- impl BarrierMetrics {
60
- pub fn reset(&mut self) {
61
- self.frontier_checks = 0;
62
- self.pending_scans = 0;
63
- self.pending_entries_examined = 0;
64
- self.operations_released = 0;
65
- }
66
- }
67
-
68
- /// State of an operation in the dependency barrier
69
- #[derive(Clone, Debug, PartialEq, Eq)]
70
- pub enum OperationState {
71
- /// Operation has been received
72
- Received,
73
- /// Operation is waiting for causal dependencies to be satisfied
74
- WaitingForDependencies {
75
- required_clock: VectorClock,
76
- missing_dependencies: Vec<String>, // human-readable list
77
- },
78
- /// All dependencies satisfied, ready to apply
79
- Ready,
80
- /// Operation has been applied
81
- Applied,
82
- }
83
-
84
- /// Represents a pending operation waiting for dependencies
85
- #[derive(Clone, Debug)]
86
- pub struct PendingOperation {
87
- pub operation_id: String,
88
- pub required_dependencies: VectorClock,
89
- pub state: OperationState,
90
- /// Size on the durable log of the envelope this entry stands for.
91
- ///
92
- /// The entry does not hold the payload -- that stays in the operation log
93
- /// -- but the backlog's byte bound is about payload volume, so the size has
94
- /// to be carried even though the bytes are not.
95
- pub payload_bytes: usize,
96
- }
97
-
98
- impl PendingOperation {
99
- pub fn new(operation_id: String, required_dependencies: VectorClock) -> Self {
100
- Self {
101
- operation_id,
102
- required_dependencies,
103
- state: OperationState::Received,
104
- payload_bytes: 0,
105
- }
106
- }
107
- }
108
-
109
- /// Causal dependency barrier manager
110
- pub struct CausalDependencyBarrier {
111
- /// Current replica's vector clock frontier
112
- local_frontier: VectorClock,
113
- /// Previous frontier (used to detect which dimensions advanced)
114
- prev_frontier: VectorClock,
115
- /// Operations waiting for dependencies
116
- pending: HashMap<String, PendingOperation>,
117
- /// Operations that have been applied
118
- applied: Vec<String>,
119
- /// Metrics for Phase 7.0.1 audit and benchmarking
120
- metrics: BarrierMetrics,
121
- /// Phase 7.1-opt: Dependency index for selective candidate discovery
122
- dependency_index: DependencyIndex,
123
- /// High-water mark of pending entries, for occupancy metrics.
124
- max_pending_entries_seen: usize,
125
- /// High-water mark of pending payload bytes, for occupancy metrics.
126
- max_pending_payload_bytes_seen: usize,
127
- }
128
-
129
- impl CausalDependencyBarrier {
130
- pub fn new() -> Self {
131
- Self {
132
- local_frontier: VectorClock::new(),
133
- prev_frontier: VectorClock::new(),
134
- pending: HashMap::new(),
135
- applied: Vec::new(),
136
- metrics: BarrierMetrics::default(),
137
- dependency_index: DependencyIndex::default(),
138
- max_pending_entries_seen: 0,
139
- max_pending_payload_bytes_seen: 0,
140
- }
141
- }
142
-
143
- /// Get current metrics (for testing and benchmarking)
144
- pub fn metrics(&self) -> BarrierMetrics {
145
- self.metrics.clone()
146
- }
147
-
148
- /// Reset metrics (for testing and benchmarking)
149
- pub fn clear_metrics(&mut self) {
150
- self.metrics.reset();
151
- }
152
-
153
- /// Get current metrics snapshot (for testing and benchmarking)
154
- pub fn get_metrics(&self) -> BarrierMetrics {
155
- self.metrics.clone()
156
- }
157
-
158
- /// Get reference to dependency index (for testing and benchmarking)
159
- pub fn get_dependency_index(&self) -> &DependencyIndex {
160
- &self.dependency_index
161
- }
162
-
163
- /// Get previous frontier (for benchmarking frontier management)
164
- pub fn get_prev_frontier_mut(&mut self) -> &mut VectorClock {
165
- &mut self.prev_frontier
166
- }
167
-
168
- /// Get current frontier (for testing)
169
- pub fn get_frontier_mut(&mut self) -> &mut VectorClock {
170
- &mut self.local_frontier
171
- }
172
-
173
- /// Receive an operation and check if its dependencies are satisfied
174
- pub fn receive_operation(
175
- &mut self,
176
- operation_id: String,
177
- required_dependencies: VectorClock,
178
- ) -> ReceiveResult {
179
- // Track frontier check (Phase 7.0.1 instrumentation)
180
- #[cfg(test)]
181
- {
182
- self.metrics.frontier_checks += 1;
183
- }
184
-
185
- // Check if dependencies are already satisfied
186
- if self.local_frontier.happens_before(&required_dependencies) {
187
- // Dependencies not yet satisfied, buffer the operation
188
- let missing = self.compute_missing_dependencies(&required_dependencies);
189
- let pending_op = PendingOperation {
190
- operation_id: operation_id.clone(),
191
- required_dependencies: required_dependencies.clone(),
192
- state: OperationState::WaitingForDependencies {
193
- required_clock: required_dependencies.clone(),
194
- missing_dependencies: missing,
195
- },
196
- payload_bytes: 0,
197
- };
198
- self.pending.insert(operation_id.clone(), pending_op);
199
-
200
- // Phase 7.1-opt: Index this operation for later candidate discovery
201
- #[cfg(test)]
202
- self.index_operation(&operation_id, &required_dependencies);
203
-
204
- ReceiveResult::BufferedWaitingForDependencies { operation_id }
205
- } else {
206
- // Dependencies are satisfied, operation can be applied immediately
207
- ReceiveResult::ReadyToApply { operation_id }
208
- }
209
- }
210
-
211
- /// Apply a ready operation and advance the frontier
212
- pub fn apply_operation(
213
- &mut self,
214
- operation_id: &str,
215
- operation_clock: &VectorClock,
216
- ) -> ApplyResult {
217
- // Remove from pending if present
218
- if let Some(pending_op) = self.pending.remove(operation_id) {
219
- // Phase 7.1-opt: Unindex when operation is removed
220
- #[cfg(test)]
221
- self.unindex_operation(operation_id, &pending_op.required_dependencies);
222
- }
223
-
224
- // Advance the frontier
225
- #[cfg(test)]
226
- {
227
- self.prev_frontier.clone_from(&self.local_frontier);
228
- }
229
- self.local_frontier.merge(operation_clock);
230
-
231
- // Record as applied
232
- self.applied.push(operation_id.to_string());
233
-
234
- // Check if this unblocks any waiting operations
235
- let newly_ready = self.check_unblocked_operations();
236
-
237
- ApplyResult {
238
- operation_id: operation_id.to_string(),
239
- newly_unblocked: newly_ready,
240
- }
241
- }
242
-
243
- /// Get current state of an operation
244
- pub fn get_operation_state(&self, operation_id: &str) -> Option<OperationState> {
245
- if self.applied.contains(&operation_id.to_string()) {
246
- Some(OperationState::Applied)
247
- } else if let Some(pending) = self.pending.get(operation_id) {
248
- Some(pending.state.clone())
249
- } else {
250
- None
251
- }
252
- }
253
-
254
- /// Get all pending operations
255
- pub fn get_pending_operations(&self) -> Vec<PendingOperation> {
256
- self.pending.values().cloned().collect()
257
- }
258
-
259
- /// Get count of operations in each state
260
- pub fn get_state_counts(&self) -> StateCounters {
261
- let waiting_count = self.pending.len();
262
- let applied_count = self.applied.len();
263
-
264
- StateCounters {
265
- received: 0,
266
- waiting_for_dependencies: waiting_count,
267
- ready: 0,
268
- applied: applied_count,
269
- }
270
- }
271
-
272
- /// Reference implementation: Check all pending operations (Phase 7.1 baseline)
273
- /// Full-scan implementation: examines every pending operation
274
- /// Used as correctness oracle for differential testing
275
- pub fn check_unblocked_operations_full_scan(&mut self) -> Vec<String> {
276
- // Track pending scan (Phase 7.0.1 instrumentation)
277
- if !self.pending.is_empty() {
278
- self.metrics.pending_scans += 1;
279
- self.metrics.pending_entries_examined += self.pending.len() as u64;
280
- }
281
-
282
- let mut newly_ready = Vec::new();
283
- let mut to_ready = Vec::new();
284
-
285
- for (op_id, pending_op) in self.pending.iter() {
286
- if !self
287
- .local_frontier
288
- .happens_before(&pending_op.required_dependencies)
289
- {
290
- to_ready.push(op_id.clone());
291
- newly_ready.push(op_id.clone());
292
- }
293
- }
294
-
295
- // Track operations released (Phase 7.0.1 instrumentation)
296
- self.metrics.operations_released += newly_ready.len() as u64;
297
-
298
- for op_id in to_ready {
299
- if let Some(pending) = self.pending.get_mut(&op_id) {
300
- pending.state = OperationState::Ready;
301
- }
302
- }
303
-
304
- newly_ready
305
- }
306
-
307
- /// Indexed implementation: selective candidate discovery via dependency index
308
- /// Phase 7.1-opt: Only examines operations whose dependencies changed
309
- /// Production release path (validated faster in all tested scenarios)
310
- pub fn check_unblocked_operations_indexed(&mut self) -> Vec<String> {
311
- let mut newly_ready = Vec::new();
312
- let mut candidates = std::collections::HashSet::new();
313
-
314
- // Find operations affected by frontier advancement
315
- for (node, old_ts) in &self.prev_frontier.clocks {
316
- let new_ts = self.local_frontier.get(node);
317
- if new_ts > *old_ts {
318
- // Frontier advanced for this node
319
- // Operations waiting for timestamps in (old_ts, new_ts] are candidates
320
- for ts in (*old_ts + 1)..=new_ts {
321
- if let Some(waiters) = self.dependency_index.waiters.get(&(node.clone(), ts)) {
322
- for op_id in waiters {
323
- candidates.insert(op_id.clone());
324
- }
325
- }
326
- }
327
- }
328
- }
329
-
330
- // Also check for new nodes that appeared in frontier
331
- for (node, new_ts) in &self.local_frontier.clocks {
332
- if !self.prev_frontier.clocks.contains_key(node) {
333
- // New node appeared, check all dependencies on it
334
- for ts in 1..=*new_ts {
335
- if let Some(waiters) = self.dependency_index.waiters.get(&(node.clone(), ts)) {
336
- for op_id in waiters {
337
- candidates.insert(op_id.clone());
338
- }
339
- }
340
- }
341
- }
342
- }
343
-
344
- // Track candidate count for instrumentation
345
- let candidate_count = candidates.len();
346
-
347
- // Test candidates using readiness theorem
348
- let mut to_ready = Vec::new();
349
- for op_id in candidates {
350
- if let Some(pending_op) = self.pending.get(&op_id) {
351
- if !self
352
- .local_frontier
353
- .happens_before(&pending_op.required_dependencies)
354
- {
355
- to_ready.push(op_id.clone());
356
- newly_ready.push(op_id);
357
- }
358
- }
359
- }
360
-
361
- // Track operations released (Phase 7.0.1 instrumentation)
362
- self.metrics.operations_released += newly_ready.len() as u64;
363
- if candidate_count > 0 {
364
- self.metrics.pending_scans += 1;
365
- self.metrics.pending_entries_examined += candidate_count as u64;
366
- }
367
-
368
- // Update state (keep in index for discovery purposes)
369
- for op_id in to_ready {
370
- if let Some(pending) = self.pending.get_mut(&op_id) {
371
- pending.state = OperationState::Ready;
372
- // Note: Don't unindex here - operation remains in index for potential re-discovery
373
- // Unindex only happens when operation is actually removed from pending via apply_operation
374
- }
375
- }
376
-
377
- newly_ready
378
- }
379
-
380
- /// Production dispatch: Check which pending operations have had their dependencies satisfied
381
- /// Phase 7.1.3 crossover analysis shows indexed is faster in all tested scenarios
382
- /// Routes to indexed implementation for selective candidate discovery
383
- pub fn check_unblocked_operations(&mut self) -> Vec<String> {
384
- self.check_unblocked_operations_indexed()
385
- }
386
-
387
- /// Compute which nodes/clocks are missing to satisfy dependencies
388
- fn compute_missing_dependencies(&self, required: &VectorClock) -> Vec<String> {
389
- let mut missing = Vec::new();
390
-
391
- for (node, required_ts) in &required.clocks {
392
- let current_ts = self.local_frontier.get(node);
393
- if current_ts < *required_ts {
394
- missing.push(format!("{}:{} (have {})", node, required_ts, current_ts));
395
- }
396
- }
397
-
398
- missing
399
- }
400
-
401
- /// Get the current frontier
402
- pub fn get_frontier(&self) -> &VectorClock {
403
- &self.local_frontier
404
- }
405
-
406
- // ---------------------------------------------------------------------
407
- // Production causal-delivery API.
408
- //
409
- // The methods above were built for, and are exercised by, in-process tests
410
- // that drive the barrier directly. The methods below are what the
411
- // replicated receive path uses. They differ in three ways that matter:
412
- //
413
- // 1. Eligibility is dominance, not `happens_before`. `receive_operation`
414
- // buffers when `local_frontier.happens_before(deps)`, which returns
415
- // false for concurrent clocks and would therefore admit an operation
416
- // whose dependencies are unmet whenever the frontier is ahead in some
417
- // unrelated dimension.
418
- // 2. Release scanning does not consult the dependency index, because
419
- // `index_operation` is `#[cfg(test)]` and so the index is empty in a
420
- // shipping build. A full scan is correct at any build setting.
421
- // 3. Nothing here holds a payload. Pending entries carry an identifier and
422
- // the dependency clock; the envelope itself is replayed from the
423
- // durable operation log when it becomes eligible.
424
- // ---------------------------------------------------------------------
425
-
426
- /// True when every dependency this clock names has already been applied.
427
- pub fn is_eligible(&self, dependencies: &VectorClock) -> bool {
428
- self.local_frontier.dominates(dependencies)
429
- }
430
-
431
- /// Retain an operation that cannot be applied yet.
432
- ///
433
- /// Returns false when the key is already pending, so a duplicate delivery
434
- /// of a blocked envelope stays one logical operation.
435
- pub fn retain_pending(&mut self, key: String, dependencies: VectorClock) -> bool {
436
- self.retain_pending_sized(key, dependencies, 0)
437
- }
438
-
439
- /// Retain an operation that cannot be applied yet, recording the size of
440
- /// the envelope it stands for.
441
- ///
442
- /// The entry still holds no payload. The size is carried because the
443
- /// backlog's byte bound is about how much durable payload is outstanding,
444
- /// and that number is not recoverable from metadata alone.
445
- ///
446
- /// Returns false when the key is already pending, so a duplicate delivery
447
- /// of a blocked envelope stays one logical operation.
448
- pub fn retain_pending_sized(
449
- &mut self,
450
- key: String,
451
- dependencies: VectorClock,
452
- payload_bytes: usize,
453
- ) -> bool {
454
- if self.pending.contains_key(&key) {
455
- return false;
456
- }
457
- let missing = self.compute_missing_dependencies(&dependencies);
458
- self.pending.insert(
459
- key.clone(),
460
- PendingOperation {
461
- operation_id: key,
462
- required_dependencies: dependencies.clone(),
463
- state: OperationState::WaitingForDependencies {
464
- required_clock: dependencies,
465
- missing_dependencies: missing,
466
- },
467
- payload_bytes,
468
- },
469
- );
470
- self.observe_occupancy();
471
- true
472
- }
473
-
474
- /// Record the high-water marks after the pending set grows.
475
- ///
476
- /// Occupancy metrics have to report maximum as well as current, because a
477
- /// backlog that filled and drained between two reads is invisible to a
478
- /// current-value gauge, and "did this ever approach its bound" is the
479
- /// question an operator actually has.
480
- fn observe_occupancy(&mut self) {
481
- let entries = self.pending.len();
482
- if entries > self.max_pending_entries_seen {
483
- self.max_pending_entries_seen = entries;
484
- }
485
- let bytes = self.pending_payload_bytes();
486
- if bytes > self.max_pending_payload_bytes_seen {
487
- self.max_pending_payload_bytes_seen = bytes;
488
- }
489
- }
490
-
491
- /// Durable payload bytes the backlog is currently waiting on.
492
- ///
493
- /// This is the volume of log the pending entries stand for, not a heap
494
- /// measurement: the payloads are on disk. It is what the byte bound is
495
- /// expressed in.
496
- pub fn pending_payload_bytes(&self) -> usize {
497
- self.pending.values().map(|op| op.payload_bytes).sum()
498
- }
499
-
500
- /// The most pending entries ever held at once.
501
- pub fn max_pending_entries_seen(&self) -> usize {
502
- self.max_pending_entries_seen
503
- }
504
-
505
- /// The most pending payload bytes ever held at once.
506
- pub fn max_pending_payload_bytes_seen(&self) -> usize {
507
- self.max_pending_payload_bytes_seen
508
- }
509
-
510
- /// Record that an operation was applied, advancing the frontier.
511
- ///
512
- /// The frontier advances here and nowhere else on the production path, so
513
- /// it always describes what has been *successfully applied* rather than
514
- /// what has been received or claimed.
515
- pub fn record_applied(&mut self, key: &str, applied_clock: &VectorClock) {
516
- self.pending.remove(key);
517
- self.local_frontier.merge(applied_clock);
518
- self.applied.push(key.to_string());
519
- }
520
-
521
- /// Every pending operation whose dependencies the frontier now satisfies.
522
- ///
523
- /// Returns them in a stable order so that two replicas releasing the same
524
- /// set release it identically.
525
- pub fn eligible_pending(&self) -> Vec<String> {
526
- let mut ready: Vec<String> = self
527
- .pending
528
- .iter()
529
- .filter(|(_, op)| self.local_frontier.dominates(&op.required_dependencies))
530
- .map(|(key, _)| key.clone())
531
- .collect();
532
- ready.sort();
533
- ready
534
- }
535
-
536
- /// Whether this key is currently held as pending.
537
- pub fn is_pending(&self, key: &str) -> bool {
538
- self.pending.contains_key(key)
539
- }
540
-
541
- /// Keys of applied operations, in the order they were applied.
542
- pub fn applied_keys(&self) -> &[String] {
543
- &self.applied
544
- }
545
-
546
- /// Keys currently held as pending, sorted.
547
- pub fn pending_keys(&self) -> Vec<String> {
548
- let mut keys: Vec<String> = self.pending.keys().cloned().collect();
549
- keys.sort();
550
- keys
551
- }
552
-
553
- /// Occupancy of the pending set: how many operations are waiting.
554
- ///
555
- /// Instrumentation only. What may be retained, and what happens when that
556
- /// limit is reached, is deliberately not decided here -- see
557
- /// docs/architecture/causal-backlog-bound.md.
558
- pub fn pending_count(&self) -> usize {
559
- self.pending.len()
560
- }
561
-
562
- /// Approximate heap footprint of the pending metadata, in bytes.
563
- ///
564
- /// Counts the key, both clock copies an entry carries, and the missing
565
- /// dependency set, because the cost of a pending entry scales with cluster
566
- /// size rather than with the size of the operation it stands for.
567
- pub fn pending_metadata_bytes(&self) -> usize {
568
- self.pending
569
- .iter()
570
- .map(|(key, op)| {
571
- let clock_bytes = |clock: &VectorClock| {
572
- clock
573
- .clocks
574
- .keys()
575
- .map(|node| node.len() + std::mem::size_of::<u64>())
576
- .sum::<usize>()
577
- };
578
- let state_bytes = match &op.state {
579
- OperationState::WaitingForDependencies {
580
- required_clock,
581
- missing_dependencies,
582
- } => {
583
- clock_bytes(required_clock)
584
- + missing_dependencies
585
- .iter()
586
- .map(|dep| dep.len() + std::mem::size_of::<u64>())
587
- .sum::<usize>()
588
- }
589
- _ => 0,
590
- };
591
- key.len()
592
- + op.operation_id.len()
593
- + clock_bytes(&op.required_dependencies)
594
- + state_bytes
595
- + std::mem::size_of::<PendingOperation>()
596
- })
597
- .sum()
598
- }
599
-
600
- /// Manually advance frontier (for testing or external coordination)
601
- pub fn advance_frontier(&mut self, clock: &VectorClock) {
602
- #[cfg(test)]
603
- {
604
- self.prev_frontier.clone_from(&self.local_frontier);
605
- }
606
- self.local_frontier.merge(clock);
607
- self.check_unblocked_operations();
608
- }
609
-
610
- /// Phase 7.1-opt: Index an operation by its required dependencies
611
- #[cfg(test)]
612
- fn index_operation(&mut self, op_id: &str, required: &VectorClock) {
613
- for (node, timestamp) in &required.clocks {
614
- self.dependency_index
615
- .waiters
616
- .entry((node.clone(), *timestamp))
617
- .or_insert_with(Vec::new)
618
- .push(op_id.to_string());
619
- }
620
- }
621
-
622
- /// Phase 7.1-opt: Remove an operation from the dependency index
623
- #[cfg(test)]
624
- fn unindex_operation(&mut self, op_id: &str, required: &VectorClock) {
625
- for (node, timestamp) in &required.clocks {
626
- if let Some(waiters) = self
627
- .dependency_index
628
- .waiters
629
- .get_mut(&(node.clone(), *timestamp))
630
- {
631
- waiters.retain(|id| id != op_id);
632
- }
633
- }
634
- }
635
-
636
- /// Check if operation is ready based on frontier alone (Phase 7.0.1: readiness theorem)
637
- /// Returns true if required_vc <= applied_frontier
638
- pub fn is_ready_frontier(&self, required: &VectorClock) -> bool {
639
- // Operation is ready if its required dependencies don't happen-before our frontier
640
- !self.local_frontier.happens_before(required)
641
- }
642
-
643
- /// Check if operation is ready using current buffer-based logic (Phase 7.0.1: readiness theorem)
644
- /// This is the reference implementation to compare against frontier check
645
- pub fn is_ready_buffer(&self, required: &VectorClock) -> bool {
646
- // Current implementation: same as is_ready_frontier
647
- // This method exists to allow comparing future optimizations
648
- !self.local_frontier.happens_before(required)
649
- }
650
- }
651
-
652
- impl Default for CausalDependencyBarrier {
653
- fn default() -> Self {
654
- Self::new()
655
- }
656
- }
657
-
658
- /// Result of receiving an operation
659
- #[derive(Clone, Debug)]
660
- pub enum ReceiveResult {
661
- /// Operation is buffered, waiting for dependencies
662
- BufferedWaitingForDependencies { operation_id: String },
663
- /// Operation can be applied immediately
664
- ReadyToApply { operation_id: String },
665
- }
666
-
667
- /// Result of applying an operation
668
- #[derive(Clone, Debug)]
669
- pub struct ApplyResult {
670
- pub operation_id: String,
671
- pub newly_unblocked: Vec<String>,
672
- }
673
-
674
- /// Counters for operation states
675
- #[derive(Clone, Debug, PartialEq, Eq)]
676
- pub struct StateCounters {
677
- pub received: usize,
678
- pub waiting_for_dependencies: usize,
679
- pub ready: usize,
680
- pub applied: usize,
681
- }
682
-
683
- #[cfg(test)]
684
- mod tests {
685
- use super::*;
686
-
687
- #[test]
688
- fn test_barrier_creation() {
689
- let barrier = CausalDependencyBarrier::new();
690
- assert_eq!(barrier.get_pending_operations().len(), 0);
691
- }
692
-
693
- /// Phase 7.0.2: Verify readiness theorem and buffer independence
694
- /// This test confirms that receive_operation on a satisfied dependency
695
- /// produces consistent latency regardless of pending buffer size.
696
- #[test]
697
- fn phase_7_0_2_readiness_independent_of_pending_buffer() {
698
- // Theorem: ready(op) ⟺ required_vc <= applied_frontier
699
- // Should be true regardless of buffer contents
700
-
701
- let required = {
702
- let mut vc = VectorClock::new();
703
- vc.increment("node_a");
704
- vc.increment("node_b");
705
- vc
706
- };
707
-
708
- // Test with different buffer sizes
709
- for buffer_size in [0, 10, 100] {
710
- let mut barrier = CausalDependencyBarrier::new();
711
- barrier.clear_metrics();
712
-
713
- // Advance frontier to satisfy requirement
714
- barrier.advance_frontier(&required);
715
- barrier.clear_metrics(); // Reset metrics after setup
716
-
717
- // Pre-fill buffer with unrelated operations
718
- for i in 0..buffer_size {
719
- let mut unrelated = VectorClock::new();
720
- unrelated.increment(&format!("other_{}", i));
721
- let _ = barrier.receive_operation(format!("op_buffer_{}", i), unrelated);
722
- }
723
- barrier.clear_metrics(); // Reset metrics after filling
724
-
725
- // Critical test: receive satisfied operation
726
- // Expected: Should be ready, metrics should show frontier check not buffer scan
727
- let result = barrier.receive_operation("op_test".to_string(), required.clone());
728
-
729
- let metrics = barrier.metrics();
730
-
731
- // Check result
732
- match result {
733
- ReceiveResult::ReadyToApply { .. } => {
734
- // Correct: dependencies satisfied
735
- assert!(
736
- true,
737
- "Operation should be ReadyToApply when frontier satisfies"
738
- );
739
- }
740
- ReceiveResult::BufferedWaitingForDependencies { .. } => {
741
- panic!(
742
- "Operation should be ready! Frontier was advanced with requirement: {:?}",
743
- required
744
- );
745
- }
746
- }
747
-
748
- // Check instrumentation
749
- // Should have 1 frontier check, 0 pending scans (on receive path)
750
- println!("Buffer size: {}", buffer_size);
751
- println!(
752
- " frontier_checks: {} (expected 1)",
753
- metrics.frontier_checks
754
- );
755
- println!(
756
- " pending_scans: {} (expected 0 on receive path)",
757
- metrics.pending_scans
758
- );
759
-
760
- // The key invariant: pending_scans should be 0 on receive_operation
761
- // If this fails, it means receive_operation is scanning the buffer
762
- assert_eq!(
763
- metrics.pending_scans, 0,
764
- "receive_operation should not scan pending buffer; \
765
- buffer-size-dependent cost would indicate buffer scanning on readiness path"
766
- );
767
- }
768
- }
769
-
770
- #[test]
771
- fn test_operation_with_no_dependencies() {
772
- let mut barrier = CausalDependencyBarrier::new();
773
- let op_clock = VectorClock::new();
774
-
775
- let result = barrier.receive_operation("op1".to_string(), op_clock);
776
- assert!(matches!(result, ReceiveResult::ReadyToApply { .. }));
777
- }
778
-
779
- #[test]
780
- fn test_operation_buffered_when_dependencies_missing() {
781
- let mut barrier = CausalDependencyBarrier::new();
782
-
783
- let mut required = VectorClock::new();
784
- required.increment("node_a");
785
- required.increment("node_a"); // {node_a: 2}
786
-
787
- let result = barrier.receive_operation("op1".to_string(), required);
788
- assert!(matches!(
789
- result,
790
- ReceiveResult::BufferedWaitingForDependencies { .. }
791
- ));
792
- assert_eq!(barrier.get_pending_operations().len(), 1);
793
- }
794
-
795
- #[test]
796
- fn test_unblocking_when_dependency_satisfied() {
797
- let mut barrier = CausalDependencyBarrier::new();
798
-
799
- // Operation requires {node_a: 2}
800
- let mut required = VectorClock::new();
801
- required.increment("node_a");
802
- required.increment("node_a"); // {node_a: 2}
803
-
804
- let result = barrier.receive_operation("op1".to_string(), required);
805
- assert!(matches!(
806
- result,
807
- ReceiveResult::BufferedWaitingForDependencies { .. }
808
- ));
809
- assert_eq!(barrier.get_pending_operations().len(), 1);
810
-
811
- // Advance frontier to satisfy dependency
812
- let mut satisfying_clock = VectorClock::new();
813
- satisfying_clock.increment("node_a");
814
- satisfying_clock.increment("node_a");
815
-
816
- barrier.advance_frontier(&satisfying_clock);
817
-
818
- // Now op1 should be ready
819
- let pending = barrier.get_pending_operations();
820
- assert_eq!(pending.len(), 1);
821
- assert_eq!(pending[0].state, OperationState::Ready);
822
- }
823
-
824
- #[test]
825
- fn test_out_of_order_arrival_buffering() {
826
- let mut barrier = CausalDependencyBarrier::new();
827
-
828
- // Op2 depends on Op1 having been applied
829
- // Op1 has no dependencies
830
- let op1_required = VectorClock::new(); // Op1 requires nothing
831
-
832
- let mut op2_required = VectorClock::new();
833
- op2_required.increment("node_a"); // Op2 requires {node_a: 1}
834
-
835
- // Receive Op2 before Op1
836
- let result = barrier.receive_operation("op2".to_string(), op2_required.clone());
837
- assert!(matches!(
838
- result,
839
- ReceiveResult::BufferedWaitingForDependencies { .. }
840
- ));
841
-
842
- // Receive Op1 (which has no dependencies)
843
- let result = barrier.receive_operation("op1".to_string(), op1_required.clone());
844
- assert!(matches!(result, ReceiveResult::ReadyToApply { .. }));
845
-
846
- // Apply Op1, which contributes {node_a: 1} to the frontier
847
- let mut op1_contribution = VectorClock::new();
848
- op1_contribution.increment("node_a");
849
- barrier.apply_operation("op1", &op1_contribution);
850
-
851
- // Now Op2 should be unblocked because frontier is {node_a: 1}
852
- let pending = barrier.get_pending_operations();
853
- assert_eq!(pending.len(), 1);
854
- assert_eq!(pending[0].operation_id, "op2");
855
- assert_eq!(pending[0].state, OperationState::Ready);
856
- }
857
-
858
- #[test]
859
- fn test_dependency_cascade() {
860
- let mut barrier = CausalDependencyBarrier::new();
861
-
862
- // Op1 has no dependencies
863
- let op1_clock = VectorClock::new();
864
-
865
- // Op2 depends on Op1: {node_a: 1}
866
- let mut op2_required = VectorClock::new();
867
- op2_required.increment("node_a");
868
-
869
- // Op3 depends on Op2: {node_a: 2}
870
- let mut op3_required = VectorClock::new();
871
- op3_required.increment("node_a");
872
- op3_required.increment("node_a");
873
-
874
- // Receive in order: Op3, Op2, Op1
875
- barrier.receive_operation("op3".to_string(), op3_required);
876
- barrier.receive_operation("op2".to_string(), op2_required);
877
- barrier.receive_operation("op1".to_string(), op1_clock.clone());
878
-
879
- // All should be buffered except Op1 (which can apply immediately)
880
- assert_eq!(barrier.get_pending_operations().len(), 2); // Op2 and Op3
881
-
882
- // Apply Op1
883
- let mut apply_clock = VectorClock::new();
884
- apply_clock.increment("node_a");
885
- barrier.apply_operation("op1", &apply_clock);
886
-
887
- // Now Op2 should be ready
888
- let pending = barrier.get_pending_operations();
889
- let op2_state = pending.iter().find(|p| p.operation_id == "op2").unwrap();
890
- assert_eq!(op2_state.state, OperationState::Ready);
891
-
892
- // Apply Op2
893
- let mut apply_clock2 = VectorClock::new();
894
- apply_clock2.increment("node_a");
895
- apply_clock2.increment("node_a");
896
- barrier.apply_operation("op2", &apply_clock2);
897
-
898
- // Now Op3 should be ready
899
- let pending = barrier.get_pending_operations();
900
- let op3_state = pending.iter().find(|p| p.operation_id == "op3").unwrap();
901
- assert_eq!(op3_state.state, OperationState::Ready);
902
- }
903
-
904
- #[test]
905
- fn test_state_counts() {
906
- let mut barrier = CausalDependencyBarrier::new();
907
-
908
- let mut required = VectorClock::new();
909
- required.increment("node_a");
910
-
911
- barrier.receive_operation("op1".to_string(), required.clone());
912
- barrier.receive_operation("op2".to_string(), required);
913
-
914
- let counts = barrier.get_state_counts();
915
- assert_eq!(counts.waiting_for_dependencies, 2);
916
- assert_eq!(counts.applied, 0);
917
- }
918
-
919
- #[test]
920
- fn test_causal_invariant_preserved() {
921
- // Verify that operations are applied in causal order
922
- let mut barrier = CausalDependencyBarrier::new();
923
-
924
- // Op1 has no dependencies
925
- let op1_required = VectorClock::new();
926
-
927
- // Op2 requires {a: 2}
928
- let mut op2_required = VectorClock::new();
929
- op2_required.increment("a");
930
- op2_required.increment("a");
931
-
932
- // Receive Op2 before Op1 (out of order)
933
- barrier.receive_operation("op2".to_string(), op2_required.clone());
934
-
935
- // Op1 should be ready immediately (no dependencies)
936
- let result = barrier.receive_operation("op1".to_string(), op1_required);
937
- assert!(matches!(result, ReceiveResult::ReadyToApply { .. }));
938
-
939
- // After applying Op1, it contributes {a: 1} to frontier
940
- let mut op1_contribution = VectorClock::new();
941
- op1_contribution.increment("a");
942
- barrier.apply_operation("op1", &op1_contribution);
943
-
944
- // Op2 still waiting (needs a:2, but frontier is only a:1)
945
- let pending = barrier.get_pending_operations();
946
- let op2_state = pending.iter().find(|p| p.operation_id == "op2").unwrap();
947
- assert!(matches!(
948
- op2_state.state,
949
- OperationState::WaitingForDependencies { .. }
950
- ));
951
- }
952
-
953
- #[test]
954
- fn test_readiness_theorem_equivalence() {
955
- // Phase 7.0.1: Prove that frontier-based and buffer-based readiness checks are equivalent
956
- let mut barrier = CausalDependencyBarrier::new();
957
-
958
- // Create a required vector clock
959
- let mut required = VectorClock::new();
960
- required.increment("node_a");
961
- required.increment("node_b");
962
-
963
- // Before frontier is advanced, operation should NOT be ready
964
- assert!(!barrier.is_ready_frontier(&required));
965
- assert!(!barrier.is_ready_buffer(&required));
966
-
967
- // Advance frontier to satisfy dependencies
968
- let mut satisfying = VectorClock::new();
969
- satisfying.increment("node_a");
970
- satisfying.increment("node_b");
971
- barrier.advance_frontier(&satisfying);
972
-
973
- // Now both methods should agree: operation IS ready
974
- assert!(barrier.is_ready_frontier(&required));
975
- assert!(barrier.is_ready_buffer(&required));
976
- }
977
-
978
- #[test]
979
- fn test_instrumentation_frontier_checks() {
980
- // Phase 7.0.1: Verify frontier_checks counter is incremented
981
- let mut barrier = CausalDependencyBarrier::new();
982
- assert_eq!(barrier.metrics().frontier_checks, 0);
983
-
984
- let required = VectorClock::new();
985
- barrier.receive_operation("op1".to_string(), required.clone());
986
- assert_eq!(barrier.metrics().frontier_checks, 1);
987
-
988
- barrier.receive_operation("op2".to_string(), required);
989
- assert_eq!(barrier.metrics().frontier_checks, 2);
990
- }
991
-
992
- #[test]
993
- fn test_instrumentation_pending_scans() {
994
- // Phase 7.0.1: Verify pending_scans counter when checking unblocked operations
995
- let mut barrier = CausalDependencyBarrier::new();
996
- barrier.clear_metrics();
997
-
998
- // Add buffered operation
999
- let mut required = VectorClock::new();
1000
- required.increment("node_a");
1001
- barrier.receive_operation("op_buffered".to_string(), required);
1002
-
1003
- assert_eq!(barrier.metrics().pending_scans, 0);
1004
- assert_eq!(barrier.metrics().pending_entries_examined, 0);
1005
-
1006
- // Advance frontier to trigger check_unblocked_operations
1007
- let mut satisfying = VectorClock::new();
1008
- satisfying.increment("node_a");
1009
- barrier.advance_frontier(&satisfying);
1010
-
1011
- // Should have scanned pending buffer
1012
- assert_eq!(barrier.metrics().pending_scans, 1);
1013
- assert_eq!(barrier.metrics().pending_entries_examined, 1);
1014
- }
1015
-
1016
- #[test]
1017
- fn test_instrumentation_operations_released() {
1018
- // Phase 7.0.1: Verify operations_released counter
1019
- let mut barrier = CausalDependencyBarrier::new();
1020
- barrier.clear_metrics();
1021
-
1022
- // Add multiple buffered operations
1023
- let mut required = VectorClock::new();
1024
- required.increment("node_a");
1025
-
1026
- for i in 0..3 {
1027
- barrier.receive_operation(format!("op_{}", i), required.clone());
1028
- }
1029
-
1030
- assert_eq!(barrier.metrics().operations_released, 0);
1031
-
1032
- // Advance frontier to release operations
1033
- let mut satisfying = VectorClock::new();
1034
- satisfying.increment("node_a");
1035
- barrier.advance_frontier(&satisfying);
1036
-
1037
- // All 3 should be released
1038
- assert_eq!(barrier.metrics().operations_released, 3);
1039
- }
1040
-
1041
- #[test]
1042
- fn test_instrumentation_buffer_scanning_scales_with_size() {
1043
- // Phase 7.0.1: Demonstrate that buffer scanning scales with buffer size
1044
- // This test uses full-scan oracle to validate full-scan instrumentation behavior
1045
- // Production dispatch uses indexed path; this tests the oracle implementation
1046
- let mut barrier = CausalDependencyBarrier::new();
1047
- barrier.clear_metrics();
1048
-
1049
- // Create a requirement for {node_a: 5}
1050
- let mut required = VectorClock::new();
1051
- required.increment("node_a");
1052
- required.increment("node_a");
1053
- required.increment("node_a");
1054
- required.increment("node_a");
1055
- required.increment("node_a");
1056
-
1057
- // Add 10 operations that can't be satisfied yet
1058
- for i in 0..10 {
1059
- barrier.receive_operation(format!("op_{}", i), required.clone());
1060
- }
1061
-
1062
- barrier.clear_metrics();
1063
-
1064
- // Trigger full-scan directly: should see all 10 items
1065
- let mut partial = VectorClock::new();
1066
- partial.increment("node_a");
1067
- partial.increment("node_a"); // Only {node_a: 2}, not enough for requirement
1068
- {
1069
- let current = barrier.get_frontier().clone();
1070
- barrier.get_prev_frontier_mut().clone_from(&current);
1071
- }
1072
- barrier.get_frontier_mut().merge(&partial);
1073
- let _ = barrier.check_unblocked_operations_full_scan();
1074
-
1075
- let first_scan_entries = barrier.metrics().pending_entries_examined;
1076
- assert_eq!(first_scan_entries, 10);
1077
-
1078
- barrier.clear_metrics();
1079
-
1080
- // Add 5 more operations
1081
- for i in 10..15 {
1082
- barrier.receive_operation(format!("op_{}", i), required.clone());
1083
- }
1084
-
1085
- // Now there should be 15 items in pending
1086
- let pending_count = barrier.get_pending_operations().len();
1087
- assert_eq!(pending_count, 15);
1088
-
1089
- // Trigger full-scan again: should see all 15 items
1090
- let mut more_partial = VectorClock::new();
1091
- more_partial.increment("node_a");
1092
- more_partial.increment("node_a");
1093
- more_partial.increment("node_a"); // Only {node_a: 3}, still not enough
1094
- {
1095
- let current = barrier.get_frontier().clone();
1096
- barrier.get_prev_frontier_mut().clone_from(&current);
1097
- }
1098
- barrier.get_frontier_mut().merge(&more_partial);
1099
- let _ = barrier.check_unblocked_operations_full_scan();
1100
-
1101
- let second_scan_entries = barrier.metrics().pending_entries_examined;
1102
- assert_eq!(second_scan_entries, 15);
1103
-
1104
- // Verify that entries_examined scales with buffer size
1105
- assert!(second_scan_entries > first_scan_entries);
1106
- }
1107
-
1108
- // ============================================================================
1109
- // LIFECYCLE INVARIANT TESTS (Phase 7.0.1: Prove theorem over actual states)
1110
- // ============================================================================
1111
-
1112
- #[test]
1113
- fn lifecycle_invariant_out_of_order_operations() {
1114
- // Phase 7.0.1: Proof that frontier theorem holds with out-of-order arrivals
1115
- // Sequence: A1 → A3 → A2
1116
- // Expected: A1 applies, A3 buffers, then when A2 applies, A3 can be checked
1117
- let mut barrier = CausalDependencyBarrier::new();
1118
-
1119
- // A1: no dependencies (ready immediately)
1120
- let a1_required = VectorClock::new();
1121
- let result_a1 = barrier.receive_operation("A1".to_string(), a1_required.clone());
1122
- assert!(matches!(result_a1, ReceiveResult::ReadyToApply { .. }));
1123
-
1124
- // Apply A1 with {node_a: 1}
1125
- let mut a1_contribution = VectorClock::new();
1126
- a1_contribution.increment("node_a");
1127
- barrier.apply_operation("A1", &a1_contribution);
1128
- assert_eq!(barrier.get_frontier().get("node_a"), 1);
1129
-
1130
- // A3: requires {node_a: 3}
1131
- let mut a3_required = VectorClock::new();
1132
- a3_required.increment("node_a");
1133
- a3_required.increment("node_a");
1134
- a3_required.increment("node_a");
1135
- let result_a3 = barrier.receive_operation("A3".to_string(), a3_required.clone());
1136
- assert!(matches!(
1137
- result_a3,
1138
- ReceiveResult::BufferedWaitingForDependencies { .. }
1139
- ));
1140
-
1141
- // A2: requires {node_a: 2}
1142
- let mut a2_required = VectorClock::new();
1143
- a2_required.increment("node_a");
1144
- a2_required.increment("node_a");
1145
- let result_a2 = barrier.receive_operation("A2".to_string(), a2_required.clone());
1146
- assert!(matches!(
1147
- result_a2,
1148
- ReceiveResult::BufferedWaitingForDependencies { .. }
1149
- ));
1150
-
1151
- // Now frontier is only {node_a: 1}, both A2 and A3 are buffered
1152
- assert_eq!(barrier.get_pending_operations().len(), 2);
1153
-
1154
- // Apply A2 with {node_a: 2}
1155
- let mut a2_contribution = VectorClock::new();
1156
- a2_contribution.increment("node_a");
1157
- a2_contribution.increment("node_a");
1158
- barrier.apply_operation("A2", &a2_contribution);
1159
-
1160
- // Now frontier is {node_a: 2}, A2 should be applied, A3 still waiting
1161
- assert_eq!(barrier.get_frontier().get("node_a"), 2);
1162
- let pending = barrier.get_pending_operations();
1163
- assert_eq!(pending.len(), 1);
1164
- assert_eq!(pending[0].operation_id, "A3");
1165
- assert!(matches!(
1166
- pending[0].state,
1167
- OperationState::WaitingForDependencies { .. }
1168
- ));
1169
-
1170
- // Apply A3 with {node_a: 3}
1171
- let mut a3_contribution = VectorClock::new();
1172
- a3_contribution.increment("node_a");
1173
- a3_contribution.increment("node_a");
1174
- a3_contribution.increment("node_a");
1175
- barrier.apply_operation("A3", &a3_contribution);
1176
-
1177
- // Now all should be applied
1178
- assert_eq!(barrier.get_frontier().get("node_a"), 3);
1179
- assert_eq!(barrier.get_pending_operations().len(), 0);
1180
- }
1181
-
1182
- #[test]
1183
- fn lifecycle_invariant_multiple_dependencies() {
1184
- // Phase 7.0.1: Proof with multiple independent dependency chains
1185
- // A5 depends on: B3, C7, D2
1186
- let mut barrier = CausalDependencyBarrier::new();
1187
-
1188
- // Build the requirement: {B: 3, C: 7, D: 2}
1189
- let mut a5_required = VectorClock::new();
1190
- a5_required.increment("B");
1191
- a5_required.increment("B");
1192
- a5_required.increment("B"); // {B: 3}
1193
- for _ in 0..7 {
1194
- a5_required.increment("C");
1195
- } // {C: 7}
1196
- a5_required.increment("D");
1197
- a5_required.increment("D"); // {D: 2}
1198
-
1199
- // Try to receive A5 before any dependencies
1200
- let result = barrier.receive_operation("A5".to_string(), a5_required.clone());
1201
- assert!(matches!(
1202
- result,
1203
- ReceiveResult::BufferedWaitingForDependencies { .. }
1204
- ));
1205
-
1206
- // Partially satisfy: apply B3
1207
- let mut b3_clock = VectorClock::new();
1208
- b3_clock.increment("B");
1209
- b3_clock.increment("B");
1210
- b3_clock.increment("B");
1211
- barrier.receive_operation("B3".to_string(), b3_clock.clone());
1212
- barrier.apply_operation("B3", &b3_clock);
1213
- assert_eq!(barrier.get_frontier().get("B"), 3);
1214
-
1215
- // A5 still waiting (needs C7 and D2)
1216
- assert!(matches!(
1217
- barrier.get_operation_state("A5"),
1218
- Some(OperationState::WaitingForDependencies { .. })
1219
- ));
1220
-
1221
- // Satisfy C7
1222
- let mut c7_clock = VectorClock::new();
1223
- for _ in 0..7 {
1224
- c7_clock.increment("C");
1225
- }
1226
- barrier.receive_operation("C7".to_string(), c7_clock.clone());
1227
- barrier.apply_operation("C7", &c7_clock);
1228
- assert_eq!(barrier.get_frontier().get("C"), 7);
1229
-
1230
- // A5 still waiting (needs D2)
1231
- assert!(matches!(
1232
- barrier.get_operation_state("A5"),
1233
- Some(OperationState::WaitingForDependencies { .. })
1234
- ));
1235
-
1236
- // Satisfy D2
1237
- let mut d2_clock = VectorClock::new();
1238
- d2_clock.increment("D");
1239
- d2_clock.increment("D");
1240
- barrier.receive_operation("D2".to_string(), d2_clock.clone());
1241
- barrier.apply_operation("D2", &d2_clock);
1242
- assert_eq!(barrier.get_frontier().get("D"), 2);
1243
-
1244
- // Now A5 should be ready (frontier has all dependencies)
1245
- let pending = barrier.get_pending_operations();
1246
- let a5_state = pending.iter().find(|p| p.operation_id == "A5");
1247
- assert!(a5_state.is_some());
1248
- assert!(matches!(a5_state.unwrap().state, OperationState::Ready));
1249
- }
1250
-
1251
- #[test]
1252
- fn lifecycle_invariant_buffered_but_unapplied() {
1253
- // Phase 7.0.1: Critical case - operation received but not yet applied
1254
- // This tests the distinction between observed_frontier and applied_frontier
1255
- // Scenario: B1 is applied first (frontier = {B: 1}),
1256
- // then we receive B3 (but don't apply it),
1257
- // then A5 that requires {B: 3}
1258
- let mut barrier = CausalDependencyBarrier::new();
1259
-
1260
- // First apply B1 to establish frontier
1261
- let mut b1_clock = VectorClock::new();
1262
- b1_clock.increment("B");
1263
- barrier.receive_operation("B1".to_string(), b1_clock.clone());
1264
- barrier.apply_operation("B1", &b1_clock);
1265
- assert_eq!(barrier.get_frontier().get("B"), 1);
1266
-
1267
- // Receive B3 (requires {B: 3} but frontier only has {B: 1}, so it gets buffered)
1268
- let mut b3_required = VectorClock::new();
1269
- b3_required.increment("B");
1270
- b3_required.increment("B");
1271
- b3_required.increment("B");
1272
- let result_b3 = barrier.receive_operation("B3".to_string(), b3_required.clone());
1273
- assert!(matches!(
1274
- result_b3,
1275
- ReceiveResult::BufferedWaitingForDependencies { .. }
1276
- ));
1277
-
1278
- // At this point:
1279
- // - applied_frontier[B] = 1 (only B1 applied)
1280
- // - We have B3 in the buffer (received but not applied)
1281
- // - A5 requires {B: 3}
1282
-
1283
- // A5 should be WAITING because applied_frontier < required
1284
- let mut a5_required = VectorClock::new();
1285
- a5_required.increment("B");
1286
- a5_required.increment("B");
1287
- a5_required.increment("B");
1288
- let result_a5 = barrier.receive_operation("A5".to_string(), a5_required.clone());
1289
- assert!(matches!(
1290
- result_a5,
1291
- ReceiveResult::BufferedWaitingForDependencies { .. }
1292
- ));
1293
-
1294
- // NOW apply B3
1295
- barrier.apply_operation("B3", &b3_required);
1296
- assert_eq!(barrier.get_frontier().get("B"), 3);
1297
-
1298
- // THEN A5 can be ready
1299
- let pending = barrier.get_pending_operations();
1300
- let a5_state = pending.iter().find(|p| p.operation_id == "A5");
1301
- assert!(a5_state.is_some());
1302
- assert!(matches!(a5_state.unwrap().state, OperationState::Ready));
1303
- }
1304
-
1305
- #[test]
1306
- fn lifecycle_invariant_multiple_buffered_become_ready() {
1307
- // Phase 7.0.1: Release mechanism test
1308
- // Multiple operations in buffer all become ready when frontier advances
1309
- let mut barrier = CausalDependencyBarrier::new();
1310
-
1311
- // Buffer 5 operations, all requiring {node_a: 3}
1312
- let mut required = VectorClock::new();
1313
- required.increment("node_a");
1314
- required.increment("node_a");
1315
- required.increment("node_a");
1316
-
1317
- for i in 0..5 {
1318
- let result = barrier.receive_operation(format!("op_{}", i), required.clone());
1319
- assert!(matches!(
1320
- result,
1321
- ReceiveResult::BufferedWaitingForDependencies { .. }
1322
- ));
1323
- }
1324
-
1325
- assert_eq!(barrier.get_pending_operations().len(), 5);
1326
-
1327
- // Clear metrics and check that no operations are marked Ready yet
1328
- barrier.clear_metrics();
1329
- for i in 0..5 {
1330
- let state = barrier.get_operation_state(&format!("op_{}", i));
1331
- assert!(matches!(
1332
- state,
1333
- Some(OperationState::WaitingForDependencies { .. })
1334
- ));
1335
- }
1336
-
1337
- // Advance frontier to {node_a: 3}
1338
- let mut satisfying = VectorClock::new();
1339
- satisfying.increment("node_a");
1340
- satisfying.increment("node_a");
1341
- satisfying.increment("node_a");
1342
- barrier.advance_frontier(&satisfying);
1343
-
1344
- // Check that:
1345
- // 1. All 5 operations were examined in the scan
1346
- // 2. All 5 operations were released
1347
- assert_eq!(barrier.metrics().pending_entries_examined, 5);
1348
- assert_eq!(barrier.metrics().operations_released, 5);
1349
-
1350
- // 3. All 5 operations now marked Ready
1351
- for i in 0..5 {
1352
- let state = barrier.get_operation_state(&format!("op_{}", i));
1353
- assert!(matches!(state, Some(OperationState::Ready)));
1354
- }
1355
- }
1356
-
1357
- #[test]
1358
- fn phase_7_0_2_adversarial_satisfied_with_arbitrary_pending() {
1359
- // Phase 7.0.2: Readiness Theorem Adversarial Test
1360
- // Hypothesis: ready(op) ⟺ required_vc(op) <= applied_frontier
1361
- // This should be true REGARDLESS of what's in the pending buffer
1362
- //
1363
- // Test: Apply frontier -> fill pending -> check clearly satisfied op
1364
- let mut barrier = CausalDependencyBarrier::new();
1365
-
1366
- // 1. Set applied frontier to {A: 10, B: 10, C: 10}
1367
- let mut frontier = VectorClock::new();
1368
- frontier.increment("node_a");
1369
- for _ in 0..9 {
1370
- frontier.increment("node_a");
1371
- }
1372
- frontier.increment("node_b");
1373
- for _ in 0..9 {
1374
- frontier.increment("node_b");
1375
- }
1376
- frontier.increment("node_c");
1377
- for _ in 0..9 {
1378
- frontier.increment("node_c");
1379
- }
1380
- barrier.advance_frontier(&frontier);
1381
-
1382
- // 2. Fill pending with high-timestamp operations
1383
- // These are NOT applied yet, just buffered
1384
- for i in 0..1000 {
1385
- let mut high_ts = VectorClock::new();
1386
- for j in 0..10 {
1387
- high_ts.increment(&format!("node_a"));
1388
- }
1389
- high_ts.increment(&format!("pending_{}", i));
1390
- let _ = barrier.receive_operation(format!("pending_{}", i), high_ts);
1391
- }
1392
-
1393
- // 3. Check a CLEARLY satisfied operation
1394
- // Required: {A: 5, B: 7, C: 3} which is clearly less than frontier {A:10, B:10, C:10}
1395
- let mut required = VectorClock::new();
1396
- for _ in 0..5 {
1397
- required.increment("node_a");
1398
- }
1399
- for _ in 0..7 {
1400
- required.increment("node_b");
1401
- }
1402
- for _ in 0..3 {
1403
- required.increment("node_c");
1404
- }
1405
-
1406
- // Expected: READY, because frontier >= required
1407
- let result = barrier.receive_operation("clearly_satisfied".to_string(), required);
1408
-
1409
- // Verify it's ready
1410
- assert!(
1411
- matches!(result, ReceiveResult::ReadyToApply { .. }),
1412
- "Operation with satisfied frontier should be ready even with 1000 pending"
1413
- );
1414
-
1415
- // Verify no buffer scan happened
1416
- let metrics = barrier.metrics();
1417
- assert_eq!(
1418
- metrics.pending_scans, 0,
1419
- "readiness check should not scan pending buffer; frontier alone is sufficient"
1420
- );
1421
- }
1422
-
1423
- #[test]
1424
- fn phase_7_0_2_adversarial_pending_not_applied() {
1425
- // Phase 7.0.2: Critical Distinction Test
1426
- // Ensure we distinguish between:
1427
- // - observed_frontier (max timestamp we've SEEN)
1428
- // - applied_frontier (max timestamp we've APPLIED)
1429
- //
1430
- // Test: Buffer a high-timestamp operation, then check a requirement
1431
- // that depends on that timestamp. Should NOT be ready until it's actually applied.
1432
- let mut barrier = CausalDependencyBarrier::new();
1433
-
1434
- // 1. Start with applied frontier at {A: 5}
1435
- let mut frontier = VectorClock::new();
1436
- for _ in 0..5 {
1437
- frontier.increment("node_a");
1438
- }
1439
- barrier.advance_frontier(&frontier);
1440
-
1441
- // 2. Receive operation A:10 (not yet applied)
1442
- // This increments observed_frontier but not applied_frontier
1443
- let mut high_ts = VectorClock::new();
1444
- for _ in 0..10 {
1445
- high_ts.increment("node_a");
1446
- }
1447
- let result = barrier.receive_operation("unmet_op".to_string(), high_ts.clone());
1448
-
1449
- // Should be buffered (A:10 not yet applied)
1450
- assert!(
1451
- matches!(result, ReceiveResult::BufferedWaitingForDependencies { .. }),
1452
- "Operation A:10 should be buffered since frontier is only at A:5"
1453
- );
1454
-
1455
- // 3. Now try to apply a NEW operation that requires A:10
1456
- // This new operation's dependencies are NOT satisfied
1457
- let result2 = barrier.receive_operation("test_op".to_string(), high_ts.clone());
1458
-
1459
- assert!(
1460
- matches!(result2, ReceiveResult::BufferedWaitingForDependencies { .. }),
1461
- "Operation requiring A:10 should NOT be ready even though A:10 exists in pending buffer; \
1462
- applied_frontier is still at A:5"
1463
- );
1464
-
1465
- // Verify instrumentation shows the semantic logic is working
1466
- let metrics = barrier.metrics();
1467
- println!(
1468
- "Metrics: frontier_checks={}, pending_scans={}",
1469
- metrics.frontier_checks, metrics.pending_scans
1470
- );
1471
-
1472
- // Should have done 2 frontier checks (for the two receive_operation calls)
1473
- // Should NOT have done any scans (readiness is frontier-only)
1474
- assert!(
1475
- metrics.frontier_checks >= 2,
1476
- "Should have checked frontier at least twice"
1477
- );
1478
- assert_eq!(
1479
- metrics.pending_scans, 0,
1480
- "Should not need buffer scans for readiness - frontier is sufficient"
1481
- );
1482
- }
1483
-
1484
- // ============================================================================
1485
- // PHASE 7.1-OPT: DIFFERENTIAL TESTING (Index vs Reference)
1486
- // ============================================================================
1487
-
1488
- #[test]
1489
- fn phase_7_1_opt_index_correctness_candidate_discovery() {
1490
- // Test that index correctly identifies which operations are affected by frontier advancement
1491
- let mut barrier = CausalDependencyBarrier::new();
1492
-
1493
- // Create operations with clear dependency patterns on same node
1494
- let mut req_a1 = VectorClock::new();
1495
- req_a1.clocks.insert("node_a".to_string(), 1);
1496
- barrier.receive_operation("op_a1".to_string(), req_a1);
1497
-
1498
- let mut req_a2 = VectorClock::new();
1499
- req_a2.clocks.insert("node_a".to_string(), 2);
1500
- barrier.receive_operation("op_a2".to_string(), req_a2);
1501
-
1502
- let mut req_a3 = VectorClock::new();
1503
- req_a3.clocks.insert("node_a".to_string(), 3);
1504
- barrier.receive_operation("op_a3".to_string(), req_a3);
1505
-
1506
- // Verify index is correct
1507
- let a1_waiters = barrier
1508
- .dependency_index
1509
- .waiters
1510
- .get(&("node_a".to_string(), 1))
1511
- .map(|w| w.clone())
1512
- .unwrap_or_default();
1513
- assert!(a1_waiters.contains(&"op_a1".to_string()));
1514
-
1515
- let a2_waiters = barrier
1516
- .dependency_index
1517
- .waiters
1518
- .get(&("node_a".to_string(), 2))
1519
- .map(|w| w.clone())
1520
- .unwrap_or_default();
1521
- assert!(a2_waiters.contains(&"op_a2".to_string()));
1522
-
1523
- // Advance frontier to A:2
1524
- let mut frontier = VectorClock::new();
1525
- frontier.clocks.insert("node_a".to_string(), 2);
1526
- barrier.advance_frontier(&frontier);
1527
-
1528
- // Check which operations are Ready
1529
- let pending = barrier.get_pending_operations();
1530
- let ready: Vec<_> = pending
1531
- .iter()
1532
- .filter(|p| matches!(p.state, OperationState::Ready))
1533
- .map(|p| p.operation_id.clone())
1534
- .collect();
1535
-
1536
- // op_a1 and op_a2 should be Ready
1537
- assert_eq!(ready.len(), 2);
1538
- assert!(ready.contains(&"op_a1".to_string()));
1539
- assert!(ready.contains(&"op_a2".to_string()));
1540
-
1541
- // op_a3 should still be Waiting (needs A:3)
1542
- let waiting: Vec<_> = pending
1543
- .iter()
1544
- .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
1545
- .map(|p| p.operation_id.clone())
1546
- .collect();
1547
- assert_eq!(waiting.len(), 1);
1548
- assert!(waiting.contains(&"op_a3".to_string()));
1549
- }
1550
-
1551
- // ============================================================================
1552
- // PHASE 7.1-OPT: INDEX INVARIANT TESTS
1553
- // ============================================================================
1554
-
1555
- #[test]
1556
- fn phase_7_1_opt_index_invariant_all_operations_discoverable() {
1557
- // Invariant: Every pending operation must be discoverable through index
1558
- let mut barrier = CausalDependencyBarrier::new();
1559
-
1560
- // Add 20 operations with varied dependencies
1561
- for i in 0..20 {
1562
- let mut required = VectorClock::new();
1563
- required
1564
- .clocks
1565
- .insert("node_a".to_string(), ((i % 5) + 1) as u64);
1566
- if i % 3 == 0 {
1567
- required
1568
- .clocks
1569
- .insert("node_b".to_string(), ((i / 3) + 1) as u64);
1570
- }
1571
- barrier.receive_operation(format!("op_{}", i), required);
1572
- }
1573
-
1574
- // Verify each pending operation is in index for all its dependencies
1575
- for (op_id, pending_op) in &barrier.pending {
1576
- for (node, ts) in &pending_op.required_dependencies.clocks {
1577
- let key = (node.clone(), *ts);
1578
- assert!(
1579
- barrier
1580
- .dependency_index
1581
- .waiters
1582
- .get(&key)
1583
- .map(|waiters| waiters.contains(op_id))
1584
- .unwrap_or(false),
1585
- "Operation {} not found in index for ({}, {})",
1586
- op_id,
1587
- node,
1588
- ts
1589
- );
1590
- }
1591
- }
1592
- }
1593
-
1594
- #[test]
1595
- fn phase_7_1_opt_index_invariant_removed_on_apply() {
1596
- // Invariant: When operation is applied, it must be removed from index
1597
- let mut barrier = CausalDependencyBarrier::new();
1598
-
1599
- // Add operation
1600
- let mut required = VectorClock::new();
1601
- required.clocks.insert("node_a".to_string(), 1);
1602
- let result = barrier.receive_operation("op_1".to_string(), required.clone());
1603
-
1604
- // Verify it's indexed
1605
- assert!(barrier
1606
- .dependency_index
1607
- .waiters
1608
- .get(&("node_a".to_string(), 1))
1609
- .map(|w| w.contains(&"op_1".to_string()))
1610
- .unwrap_or(false));
1611
-
1612
- // Apply operation (moving it from pending to applied)
1613
- barrier.apply_operation("op_1", &required);
1614
-
1615
- // Verify it's removed from index (because apply calls unindex when removing from pending)
1616
- for waiters in barrier.dependency_index.waiters.values() {
1617
- assert!(
1618
- !waiters.contains(&"op_1".to_string()),
1619
- "Applied operation should be removed from all index entries"
1620
- );
1621
- }
1622
- }
1623
-
1624
- #[test]
1625
- fn phase_7_1_opt_index_invariant_duplicate_dependencies() {
1626
- // Invariant: Handle operations with duplicate node dependencies
1627
- let mut barrier = CausalDependencyBarrier::new();
1628
-
1629
- // Create operation with same node in required_vc (vector clock deduplicates)
1630
- let mut required = VectorClock::new();
1631
- required.clocks.insert("node_a".to_string(), 5);
1632
- barrier.receive_operation("op_dup".to_string(), required.clone());
1633
-
1634
- // Should be indexed exactly once for (node_a, 5)
1635
- let waiters_at_5 = barrier
1636
- .dependency_index
1637
- .waiters
1638
- .get(&("node_a".to_string(), 5))
1639
- .map(|w| w.iter().filter(|id| id == &"op_dup").count())
1640
- .unwrap_or(0);
1641
-
1642
- // VectorClock naturally deduplicates, so should only appear once
1643
- assert_eq!(
1644
- waiters_at_5, 1,
1645
- "Operation should appear once per unique dependency"
1646
- );
1647
- }
1648
-
1649
- #[test]
1650
- fn phase_7_1_opt_index_invariant_multiple_nodes() {
1651
- // Invariant: Correctly handle multi-node dependencies
1652
- let mut barrier = CausalDependencyBarrier::new();
1653
-
1654
- let mut required = VectorClock::new();
1655
- required.clocks.insert("node_a".to_string(), 2);
1656
- required.clocks.insert("node_b".to_string(), 3);
1657
- required.clocks.insert("node_c".to_string(), 1);
1658
-
1659
- barrier.receive_operation("op_multi".to_string(), required);
1660
-
1661
- // Should be findable via any dependency
1662
- assert!(barrier
1663
- .dependency_index
1664
- .waiters
1665
- .get(&("node_a".to_string(), 2))
1666
- .map(|w| w.contains(&"op_multi".to_string()))
1667
- .unwrap_or(false));
1668
-
1669
- assert!(barrier
1670
- .dependency_index
1671
- .waiters
1672
- .get(&("node_b".to_string(), 3))
1673
- .map(|w| w.contains(&"op_multi".to_string()))
1674
- .unwrap_or(false));
1675
-
1676
- assert!(barrier
1677
- .dependency_index
1678
- .waiters
1679
- .get(&("node_c".to_string(), 1))
1680
- .map(|w| w.contains(&"op_multi".to_string()))
1681
- .unwrap_or(false));
1682
- }
1683
-
1684
- // ============================================================================
1685
- // PHASE 7.1-OPT: CORE FUNCTIONALITY TESTS
1686
- // ============================================================================
1687
-
1688
- #[test]
1689
- fn phase_7_1_opt_core_sequential_release() {
1690
- // Core test: Sequential operations released (marked Ready) in correct order
1691
- let mut barrier = CausalDependencyBarrier::new();
1692
-
1693
- // Add 5 operations with sequential dependencies
1694
- for i in 1..=5 {
1695
- let mut required = VectorClock::new();
1696
- required.clocks.insert("node_a".to_string(), i as u64);
1697
- barrier.receive_operation(format!("op_{}", i), required);
1698
- }
1699
-
1700
- assert_eq!(barrier.get_pending_operations().len(), 5);
1701
-
1702
- // Advance to 3
1703
- let mut frontier = VectorClock::new();
1704
- frontier.clocks.insert("node_a".to_string(), 3);
1705
- barrier.advance_frontier(&frontier);
1706
-
1707
- // Check which operations are Ready vs still Waiting
1708
- let pending = barrier.get_pending_operations();
1709
- let ready_ops: Vec<_> = pending
1710
- .iter()
1711
- .filter(|p| matches!(p.state, OperationState::Ready))
1712
- .map(|p| p.operation_id.clone())
1713
- .collect();
1714
- let waiting_ops: Vec<_> = pending
1715
- .iter()
1716
- .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
1717
- .map(|p| p.operation_id.clone())
1718
- .collect();
1719
-
1720
- // op_1, op_2, op_3 should be Ready
1721
- assert_eq!(ready_ops.len(), 3);
1722
- assert!(ready_ops.contains(&"op_1".to_string()));
1723
- assert!(ready_ops.contains(&"op_2".to_string()));
1724
- assert!(ready_ops.contains(&"op_3".to_string()));
1725
-
1726
- // op_4, op_5 should still be Waiting
1727
- assert_eq!(waiting_ops.len(), 2);
1728
- assert!(waiting_ops.contains(&"op_4".to_string()));
1729
- assert!(waiting_ops.contains(&"op_5".to_string()));
1730
- }
1731
-
1732
- #[test]
1733
- fn phase_7_1_opt_core_index_maintained_on_release() {
1734
- // Core test: Index is properly maintained when operations are released
1735
- let mut barrier = CausalDependencyBarrier::new();
1736
-
1737
- // Add operations
1738
- let mut req_1 = VectorClock::new();
1739
- req_1.clocks.insert("node_a".to_string(), 1);
1740
- barrier.receive_operation("op_1".to_string(), req_1);
1741
-
1742
- let mut req_2 = VectorClock::new();
1743
- req_2.clocks.insert("node_a".to_string(), 2);
1744
- barrier.receive_operation("op_2".to_string(), req_2);
1745
-
1746
- // Index should have entries
1747
- assert!(barrier
1748
- .dependency_index
1749
- .waiters
1750
- .get(&("node_a".to_string(), 1))
1751
- .map(|w| w.contains(&"op_1".to_string()))
1752
- .unwrap_or(false));
1753
-
1754
- // Advance frontier
1755
- let mut frontier = VectorClock::new();
1756
- frontier.clocks.insert("node_a".to_string(), 1);
1757
- barrier.advance_frontier(&frontier);
1758
-
1759
- // After releasing op_1, it should still be in pending but marked Ready
1760
- // and it should be removed from the index if using optimized version
1761
- // (but for now, just verify it's marked as Ready)
1762
- let pending = barrier.get_pending_operations();
1763
- let op_1 = pending.iter().find(|p| p.operation_id == "op_1").unwrap();
1764
- assert!(matches!(op_1.state, OperationState::Ready));
1765
- }
1766
-
1767
- #[test]
1768
- fn phase_7_1_opt_core_index_maintains_consistency() {
1769
- // Core test: Index stays consistent with pending operations
1770
- let mut barrier = CausalDependencyBarrier::new();
1771
-
1772
- // Add mix of operations
1773
- for i in 0..5 {
1774
- let mut required = VectorClock::new();
1775
- required
1776
- .clocks
1777
- .insert("node_a".to_string(), ((i % 3) + 1) as u64);
1778
- barrier.receive_operation(format!("op_{}", i), required);
1779
- }
1780
-
1781
- // Verify all pending ops are in index
1782
- for (op_id, pending_op) in &barrier.pending {
1783
- for (node, ts) in &pending_op.required_dependencies.clocks {
1784
- assert!(
1785
- barrier
1786
- .dependency_index
1787
- .waiters
1788
- .get(&(node.clone(), *ts))
1789
- .map(|w| w.contains(op_id))
1790
- .unwrap_or(false),
1791
- "Operation {} not indexed for ({}, {})",
1792
- op_id,
1793
- node,
1794
- ts
1795
- );
1796
- }
1797
- }
1798
-
1799
- // Advance and check that released ops are removed from index
1800
- let mut frontier = VectorClock::new();
1801
- frontier.clocks.insert("node_a".to_string(), 2);
1802
- barrier.advance_frontier(&frontier);
1803
-
1804
- // Verify remaining pending ops are still in index
1805
- for (op_id, pending_op) in &barrier.pending {
1806
- for (node, ts) in &pending_op.required_dependencies.clocks {
1807
- assert!(
1808
- barrier
1809
- .dependency_index
1810
- .waiters
1811
- .get(&(node.clone(), *ts))
1812
- .map(|w| w.contains(op_id))
1813
- .unwrap_or(false),
1814
- "Remaining operation {} should still be in index",
1815
- op_id
1816
- );
1817
- }
1818
- }
1819
- }
1820
-
1821
- // ============================================================================
1822
- // PHASE 7.1.1: COMPREHENSIVE DIFFERENTIAL VALIDATION
1823
- // ============================================================================
1824
- // Permanent infrastructure for regression testing.
1825
- // Ensures optimized and reference implementations remain semantically equivalent.
1826
-
1827
- /// Scenario generator for differential testing
1828
- #[derive(Clone, Debug)]
1829
- struct DifferentialScenario {
1830
- seed: u64,
1831
- initial_frontier: VectorClock,
1832
- pending_operations: Vec<(String, VectorClock)>,
1833
- frontier_advances: Vec<VectorClock>,
1834
- }
1835
-
1836
- impl DifferentialScenario {
1837
- fn random_with_seed(seed: u64) -> Self {
1838
- use std::collections::hash_map::DefaultHasher;
1839
- use std::hash::{Hash, Hasher};
1840
-
1841
- let mut hasher = DefaultHasher::new();
1842
- seed.hash(&mut hasher);
1843
- let mut rng_seed = hasher.finish();
1844
-
1845
- fn next_rng(seed: &mut u64) -> u64 {
1846
- *seed = seed
1847
- .wrapping_mul(6364136223846793005)
1848
- .wrapping_add(1442695040888963407);
1849
- *seed
1850
- }
1851
-
1852
- // Generate random frontier
1853
- let mut initial_frontier = VectorClock::new();
1854
- let node_count = (next_rng(&mut rng_seed) % 5) as usize + 2; // 2-6 nodes
1855
- for i in 0..node_count {
1856
- let ts = (next_rng(&mut rng_seed) % 20) as u64;
1857
- if ts > 0 {
1858
- initial_frontier.clocks.insert(format!("node_{}", i), ts);
1859
- }
1860
- }
1861
-
1862
- // Generate random pending operations
1863
- let pending_count = (next_rng(&mut rng_seed) % 100) as usize + 10; // 10-109
1864
- let mut pending_operations = Vec::new();
1865
- for i in 0..pending_count {
1866
- let mut required = VectorClock::new();
1867
- let req_node_count = (next_rng(&mut rng_seed) % node_count as u64) as usize + 1;
1868
- for j in 0..req_node_count {
1869
- let node_idx = next_rng(&mut rng_seed) % node_count as u64;
1870
- let ts = (next_rng(&mut rng_seed) % 100) as u64 + 1;
1871
- required.clocks.insert(format!("node_{}", node_idx), ts);
1872
- }
1873
- pending_operations.push((format!("op_{}", i), required));
1874
- }
1875
-
1876
- // Generate random frontier advances
1877
- let advance_count = (next_rng(&mut rng_seed) % 20) as usize + 5; // 5-24
1878
- let mut frontier_advances = Vec::new();
1879
- for _ in 0..advance_count {
1880
- let mut advance = initial_frontier.clone();
1881
- let node_to_advance = next_rng(&mut rng_seed) % node_count as u64;
1882
- let node_key = format!("node_{}", node_to_advance);
1883
- let new_ts = (next_rng(&mut rng_seed) % 100) as u64 + 1;
1884
- advance.clocks.insert(node_key, new_ts);
1885
- frontier_advances.push(advance);
1886
- }
1887
-
1888
- Self {
1889
- seed,
1890
- initial_frontier,
1891
- pending_operations,
1892
- frontier_advances,
1893
- }
1894
- }
1895
- }
1896
-
1897
- #[test]
1898
- fn phase_7_1_1_differential_random_scenarios_seed_based() {
1899
- // Test 1: Random scenarios with deterministic seed
1900
- // This ensures reproducibility and comprehensive coverage
1901
-
1902
- for seed in 0..50 {
1903
- let scenario = DifferentialScenario::random_with_seed(seed);
1904
-
1905
- // Reference implementation
1906
- let mut barrier_ref = CausalDependencyBarrier::new();
1907
- for (op_id, required) in &scenario.pending_operations {
1908
- barrier_ref.receive_operation(op_id.clone(), required.clone());
1909
- }
1910
-
1911
- // Optimized implementation
1912
- let mut barrier_opt = CausalDependencyBarrier::new();
1913
- for (op_id, required) in &scenario.pending_operations {
1914
- barrier_opt.receive_operation(op_id.clone(), required.clone());
1915
- }
1916
-
1917
- // Run through advances
1918
- for advance in &scenario.frontier_advances {
1919
- barrier_ref.clear_metrics();
1920
- barrier_opt.clear_metrics();
1921
-
1922
- // Reference
1923
- barrier_ref
1924
- .prev_frontier
1925
- .clone_from(&barrier_ref.local_frontier);
1926
- barrier_ref.local_frontier.merge(advance);
1927
- let _ref_ready = barrier_ref.check_unblocked_operations_full_scan();
1928
-
1929
- // Optimized
1930
- barrier_opt
1931
- .prev_frontier
1932
- .clone_from(&barrier_opt.local_frontier);
1933
- barrier_opt.local_frontier.merge(advance);
1934
- let _opt_ready = barrier_opt.check_unblocked_operations_indexed();
1935
-
1936
- // Compare results
1937
- let ref_pending_count = barrier_ref.get_pending_operations().len();
1938
- let opt_pending_count = barrier_opt.get_pending_operations().len();
1939
-
1940
- assert_eq!(
1941
- ref_pending_count, opt_pending_count,
1942
- "Seed {}: Pending count mismatch after advance",
1943
- seed
1944
- );
1945
-
1946
- // Verify index consistency in optimized version
1947
- for (op_id, pending_op) in &barrier_opt.pending {
1948
- for (node, ts) in &pending_op.required_dependencies.clocks {
1949
- let key = (node.clone(), *ts);
1950
- if let Some(waiters) = barrier_opt.dependency_index.waiters.get(&key) {
1951
- assert!(
1952
- waiters.contains(op_id),
1953
- "Seed {}: Operation {} not in index for {:?}",
1954
- seed,
1955
- op_id,
1956
- key
1957
- );
1958
- }
1959
- }
1960
- }
1961
- }
1962
- }
1963
- }
1964
-
1965
- #[test]
1966
- fn phase_7_1_1_differential_adversarial_multiple_ops_same_dependency() {
1967
- // Adversarial: Multiple operations depending on same (node, ts)
1968
- let mut barrier_ref = CausalDependencyBarrier::new();
1969
- let mut barrier_opt = CausalDependencyBarrier::new();
1970
-
1971
- // Create 10 operations all requiring A:5
1972
- for i in 0..10 {
1973
- let mut required = VectorClock::new();
1974
- required.clocks.insert("node_a".to_string(), 5);
1975
- barrier_ref.receive_operation(format!("op_{}", i), required.clone());
1976
- barrier_opt.receive_operation(format!("op_{}", i), required);
1977
- }
1978
-
1979
- // Advance to A:5
1980
- let mut frontier = VectorClock::new();
1981
- frontier.clocks.insert("node_a".to_string(), 5);
1982
-
1983
- barrier_ref
1984
- .prev_frontier
1985
- .clone_from(&barrier_ref.local_frontier);
1986
- barrier_ref.local_frontier.merge(&frontier);
1987
- barrier_ref.check_unblocked_operations_full_scan();
1988
-
1989
- barrier_opt
1990
- .prev_frontier
1991
- .clone_from(&barrier_opt.local_frontier);
1992
- barrier_opt.local_frontier.merge(&frontier);
1993
- barrier_opt.check_unblocked_operations_indexed();
1994
-
1995
- // Both should have released all 10
1996
- let ref_ready = barrier_ref
1997
- .get_pending_operations()
1998
- .iter()
1999
- .filter(|p| matches!(p.state, OperationState::Ready))
2000
- .count();
2001
- let opt_ready = barrier_opt
2002
- .get_pending_operations()
2003
- .iter()
2004
- .filter(|p| matches!(p.state, OperationState::Ready))
2005
- .count();
2006
-
2007
- assert_eq!(ref_ready, opt_ready);
2008
- assert_eq!(ref_ready, 10);
2009
- }
2010
-
2011
- #[test]
2012
- fn phase_7_1_1_differential_adversarial_frontier_jump() {
2013
- // Adversarial: Large frontier jump
2014
- let mut barrier_ref = CausalDependencyBarrier::new();
2015
- let mut barrier_opt = CausalDependencyBarrier::new();
2016
-
2017
- for i in 1..=10 {
2018
- let mut req = VectorClock::new();
2019
- req.clocks.insert("node_a".to_string(), i as u64);
2020
- barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2021
- barrier_opt.receive_operation(format!("op_{}", i), req);
2022
- }
2023
-
2024
- // Jump to A:7
2025
- let mut frontier = VectorClock::new();
2026
- frontier.clocks.insert("node_a".to_string(), 7);
2027
-
2028
- barrier_ref
2029
- .prev_frontier
2030
- .clone_from(&barrier_ref.local_frontier);
2031
- barrier_ref.local_frontier.merge(&frontier);
2032
- barrier_ref.check_unblocked_operations_full_scan();
2033
-
2034
- barrier_opt
2035
- .prev_frontier
2036
- .clone_from(&barrier_opt.local_frontier);
2037
- barrier_opt.local_frontier.merge(&frontier);
2038
- barrier_opt.check_unblocked_operations_indexed();
2039
-
2040
- // Both should have released op_1 through op_7
2041
- let ref_ready = barrier_ref
2042
- .get_pending_operations()
2043
- .iter()
2044
- .filter(|p| matches!(p.state, OperationState::Ready))
2045
- .count();
2046
- let opt_ready = barrier_opt
2047
- .get_pending_operations()
2048
- .iter()
2049
- .filter(|p| matches!(p.state, OperationState::Ready))
2050
- .count();
2051
-
2052
- assert_eq!(ref_ready, opt_ready);
2053
- assert_eq!(ref_ready, 7);
2054
- }
2055
-
2056
- #[test]
2057
- fn phase_7_1_1_differential_adversarial_simultaneous_release() {
2058
- // Adversarial: All operations ready at same frontier advance
2059
- let mut barrier_ref = CausalDependencyBarrier::new();
2060
- let mut barrier_opt = CausalDependencyBarrier::new();
2061
-
2062
- for i in 0..20 {
2063
- let mut req = VectorClock::new();
2064
- req.clocks.insert("node_a".to_string(), 5);
2065
- barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2066
- barrier_opt.receive_operation(format!("op_{}", i), req);
2067
- }
2068
-
2069
- let mut frontier = VectorClock::new();
2070
- frontier.clocks.insert("node_a".to_string(), 5);
2071
-
2072
- barrier_ref
2073
- .prev_frontier
2074
- .clone_from(&barrier_ref.local_frontier);
2075
- barrier_ref.local_frontier.merge(&frontier);
2076
- barrier_ref.check_unblocked_operations_full_scan();
2077
-
2078
- barrier_opt
2079
- .prev_frontier
2080
- .clone_from(&barrier_opt.local_frontier);
2081
- barrier_opt.local_frontier.merge(&frontier);
2082
- barrier_opt.check_unblocked_operations_indexed();
2083
-
2084
- let ref_ready = barrier_ref
2085
- .get_pending_operations()
2086
- .iter()
2087
- .filter(|p| matches!(p.state, OperationState::Ready))
2088
- .count();
2089
- let opt_ready = barrier_opt
2090
- .get_pending_operations()
2091
- .iter()
2092
- .filter(|p| matches!(p.state, OperationState::Ready))
2093
- .count();
2094
-
2095
- assert_eq!(ref_ready, opt_ready);
2096
- assert_eq!(ref_ready, 20);
2097
- }
2098
-
2099
- #[test]
2100
- fn phase_7_1_1_differential_adversarial_empty_buffer() {
2101
- // Adversarial: Empty pending buffer
2102
- let mut barrier_ref = CausalDependencyBarrier::new();
2103
- let mut barrier_opt = CausalDependencyBarrier::new();
2104
-
2105
- let mut frontier = VectorClock::new();
2106
- frontier.clocks.insert("node_a".to_string(), 10);
2107
-
2108
- barrier_ref
2109
- .prev_frontier
2110
- .clone_from(&barrier_ref.local_frontier);
2111
- barrier_ref.local_frontier.merge(&frontier);
2112
- let ref_ready = barrier_ref.check_unblocked_operations_full_scan();
2113
-
2114
- barrier_opt
2115
- .prev_frontier
2116
- .clone_from(&barrier_opt.local_frontier);
2117
- barrier_opt.local_frontier.merge(&frontier);
2118
- let opt_ready = barrier_opt.check_unblocked_operations_indexed();
2119
-
2120
- assert_eq!(ref_ready.len(), 0);
2121
- assert_eq!(opt_ready.len(), 0);
2122
- }
2123
-
2124
- #[test]
2125
- fn phase_7_1_1_index_invariant_no_orphaned_entries() {
2126
- // Verify index never references non-existent operations
2127
- let mut barrier = CausalDependencyBarrier::new();
2128
-
2129
- for i in 0..50 {
2130
- let mut req = VectorClock::new();
2131
- req.clocks
2132
- .insert("node_a".to_string(), ((i % 10) + 1) as u64);
2133
- barrier.receive_operation(format!("op_{}", i), req);
2134
- }
2135
-
2136
- // Release some operations
2137
- let mut frontier = VectorClock::new();
2138
- frontier.clocks.insert("node_a".to_string(), 5);
2139
- barrier.prev_frontier.clone_from(&barrier.local_frontier);
2140
- barrier.local_frontier.merge(&frontier);
2141
- barrier.check_unblocked_operations();
2142
-
2143
- // Verify all index entries reference operations in pending
2144
- for waiters in barrier.dependency_index.waiters.values() {
2145
- for op_id in waiters {
2146
- assert!(
2147
- barrier.pending.contains_key(op_id),
2148
- "Index entry references non-existent operation {}",
2149
- op_id
2150
- );
2151
- }
2152
- }
2153
- }
2154
-
2155
- #[test]
2156
- fn phase_7_1_1_recovery_index_reconstruction() {
2157
- // Verify index can be reconstructed from pending operations
2158
- let mut barrier1 = CausalDependencyBarrier::new();
2159
-
2160
- // Build up some state
2161
- for i in 0..20 {
2162
- let mut req = VectorClock::new();
2163
- req.clocks
2164
- .insert("node_a".to_string(), ((i % 5) + 1) as u64);
2165
- barrier1.receive_operation(format!("op_{}", i), req);
2166
- }
2167
-
2168
- let mut frontier = VectorClock::new();
2169
- frontier.clocks.insert("node_a".to_string(), 2);
2170
- barrier1.advance_frontier(&frontier);
2171
-
2172
- // Simulate recovery: extract pending, rebuild index
2173
- let pending_snapshot: Vec<_> = barrier1.pending.values().cloned().collect();
2174
-
2175
- let mut barrier2 = CausalDependencyBarrier::new();
2176
- for pending_op in pending_snapshot {
2177
- barrier2
2178
- .pending
2179
- .insert(pending_op.operation_id.clone(), pending_op.clone());
2180
- barrier2.index_operation(&pending_op.operation_id, &pending_op.required_dependencies);
2181
- }
2182
-
2183
- // Verify index is equivalent
2184
- assert_eq!(
2185
- barrier1.dependency_index.waiters.len(),
2186
- barrier2.dependency_index.waiters.len()
2187
- );
2188
-
2189
- for (key, waiters1) in &barrier1.dependency_index.waiters {
2190
- let waiters2 = barrier2.dependency_index.waiters.get(key);
2191
- assert!(waiters2.is_some(), "Recovered index missing key {:?}", key);
2192
- let mut w1 = waiters1.clone();
2193
- let mut w2 = waiters2.unwrap().clone();
2194
- w1.sort();
2195
- w2.sort();
2196
- assert_eq!(w1, w2, "Index mismatch for key {:?}", key);
2197
- }
2198
- }
2199
-
2200
- #[test]
2201
- fn phase_7_1_1_state_hash_equivalence_simple() {
2202
- // Simpler state equivalence test: verify both paths mark same ops as Ready
2203
- let mut barrier_ref = CausalDependencyBarrier::new();
2204
- let mut barrier_opt = CausalDependencyBarrier::new();
2205
-
2206
- // Add 10 operations with varied dependencies
2207
- for i in 0..10 {
2208
- let mut req = VectorClock::new();
2209
- req.clocks.insert("node_a".to_string(), (i + 1) as u64);
2210
- barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2211
- barrier_opt.receive_operation(format!("op_{}", i), req);
2212
- }
2213
-
2214
- // Advance frontier to release first 5 operations
2215
- let mut frontier = VectorClock::new();
2216
- frontier.clocks.insert("node_a".to_string(), 5);
2217
-
2218
- barrier_ref
2219
- .prev_frontier
2220
- .clone_from(&barrier_ref.local_frontier);
2221
- barrier_ref.local_frontier.merge(&frontier);
2222
- let _ref_ready = barrier_ref.check_unblocked_operations_full_scan();
2223
-
2224
- barrier_opt
2225
- .prev_frontier
2226
- .clone_from(&barrier_opt.local_frontier);
2227
- barrier_opt.local_frontier.merge(&frontier);
2228
- let _opt_ready = barrier_opt.check_unblocked_operations_indexed();
2229
-
2230
- // Compare operation states
2231
- let ref_pending = barrier_ref.get_pending_operations();
2232
- let opt_pending = barrier_opt.get_pending_operations();
2233
-
2234
- assert_eq!(ref_pending.len(), opt_pending.len());
2235
-
2236
- for ref_op in ref_pending.iter() {
2237
- let opt_op = opt_pending
2238
- .iter()
2239
- .find(|op| op.operation_id == ref_op.operation_id)
2240
- .expect(&format!(
2241
- "Operation {} missing in optimized",
2242
- ref_op.operation_id
2243
- ));
2244
-
2245
- assert_eq!(
2246
- std::mem::discriminant(&ref_op.state),
2247
- std::mem::discriminant(&opt_op.state),
2248
- "State mismatch for operation {}",
2249
- ref_op.operation_id
2250
- );
2251
- }
2252
- }
2253
-
2254
- #[test]
2255
- fn phase_7_1_1_property_multidimensional_frontier_satisfaction() {
2256
- // Property test: Multi-node frontier satisfaction
2257
- // Operations requiring multiple nodes should only release when ALL dimensions satisfied
2258
- let mut barrier_ref = CausalDependencyBarrier::new();
2259
- let mut barrier_opt = CausalDependencyBarrier::new();
2260
-
2261
- // Create operations with multi-node dependencies
2262
- // op_all_three: requires A:5, B:3, C:2
2263
- let mut req_all = VectorClock::new();
2264
- req_all.clocks.insert("node_a".to_string(), 5);
2265
- req_all.clocks.insert("node_b".to_string(), 3);
2266
- req_all.clocks.insert("node_c".to_string(), 2);
2267
-
2268
- barrier_ref.receive_operation("op_all_three".to_string(), req_all.clone());
2269
- barrier_opt.receive_operation("op_all_three".to_string(), req_all);
2270
-
2271
- // Partial advance: only A:5 satisfied
2272
- let mut partial = VectorClock::new();
2273
- partial.clocks.insert("node_a".to_string(), 5);
2274
- partial.clocks.insert("node_b".to_string(), 1);
2275
- partial.clocks.insert("node_c".to_string(), 1);
2276
-
2277
- barrier_ref
2278
- .prev_frontier
2279
- .clone_from(&barrier_ref.local_frontier);
2280
- barrier_ref.local_frontier.merge(&partial);
2281
- barrier_ref.check_unblocked_operations_full_scan();
2282
-
2283
- barrier_opt
2284
- .prev_frontier
2285
- .clone_from(&barrier_opt.local_frontier);
2286
- barrier_opt.local_frontier.merge(&partial);
2287
- barrier_opt.check_unblocked_operations_indexed();
2288
-
2289
- // op_all_three should still be waiting
2290
- let ref_waiting = barrier_ref
2291
- .get_pending_operations()
2292
- .iter()
2293
- .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
2294
- .count();
2295
- let opt_waiting = barrier_opt
2296
- .get_pending_operations()
2297
- .iter()
2298
- .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
2299
- .count();
2300
-
2301
- assert_eq!(ref_waiting, 1);
2302
- assert_eq!(opt_waiting, 1);
2303
-
2304
- // Full advance: all dimensions satisfied
2305
- let mut full = VectorClock::new();
2306
- full.clocks.insert("node_a".to_string(), 5);
2307
- full.clocks.insert("node_b".to_string(), 3);
2308
- full.clocks.insert("node_c".to_string(), 2);
2309
-
2310
- barrier_ref
2311
- .prev_frontier
2312
- .clone_from(&barrier_ref.local_frontier);
2313
- barrier_ref.local_frontier.merge(&full);
2314
- barrier_ref.check_unblocked_operations_full_scan();
2315
-
2316
- barrier_opt
2317
- .prev_frontier
2318
- .clone_from(&barrier_opt.local_frontier);
2319
- barrier_opt.local_frontier.merge(&full);
2320
- barrier_opt.check_unblocked_operations_indexed();
2321
-
2322
- // Now op_all_three should be ready
2323
- let ref_ready = barrier_ref
2324
- .get_pending_operations()
2325
- .iter()
2326
- .filter(|p| matches!(p.state, OperationState::Ready))
2327
- .count();
2328
- let opt_ready = barrier_opt
2329
- .get_pending_operations()
2330
- .iter()
2331
- .filter(|p| matches!(p.state, OperationState::Ready))
2332
- .count();
2333
-
2334
- assert_eq!(ref_ready, 1);
2335
- assert_eq!(opt_ready, 1);
2336
- }
2337
-
2338
- #[test]
2339
- fn phase_7_1_1_adversarial_coordinate_by_coordinate_satisfaction() {
2340
- // Adversarial: Dependencies satisfied one coordinate at a time
2341
- let mut barrier_ref = CausalDependencyBarrier::new();
2342
- let mut barrier_opt = CausalDependencyBarrier::new();
2343
-
2344
- for i in 0..5 {
2345
- let mut req = VectorClock::new();
2346
- req.clocks.insert("node_a".to_string(), 10 + i as u64);
2347
- req.clocks.insert("node_b".to_string(), 20 + i as u64);
2348
- req.clocks.insert("node_c".to_string(), 30 + i as u64);
2349
- barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2350
- barrier_opt.receive_operation(format!("op_{}", i), req);
2351
- }
2352
-
2353
- // Satisfy one coordinate at a time for each operation
2354
- for coord in vec![("node_a", 14), ("node_b", 24), ("node_c", 34)] {
2355
- let mut frontier = VectorClock::new();
2356
- for (n, ts) in vec![("node_a", 14u64), ("node_b", 24u64), ("node_c", 34u64)] {
2357
- frontier.clocks.insert(n.to_string(), ts);
2358
- }
2359
-
2360
- barrier_ref
2361
- .prev_frontier
2362
- .clone_from(&barrier_ref.local_frontier);
2363
- barrier_ref.local_frontier.merge(&frontier);
2364
- barrier_ref.check_unblocked_operations_full_scan();
2365
-
2366
- barrier_opt
2367
- .prev_frontier
2368
- .clone_from(&barrier_opt.local_frontier);
2369
- barrier_opt.local_frontier.merge(&frontier);
2370
- barrier_opt.check_unblocked_operations_indexed();
2371
- }
2372
-
2373
- // Both should have released all 5 operations
2374
- let ref_ready = barrier_ref
2375
- .get_pending_operations()
2376
- .iter()
2377
- .filter(|p| matches!(p.state, OperationState::Ready))
2378
- .count();
2379
- let opt_ready = barrier_opt
2380
- .get_pending_operations()
2381
- .iter()
2382
- .filter(|p| matches!(p.state, OperationState::Ready))
2383
- .count();
2384
-
2385
- assert_eq!(ref_ready, 5);
2386
- assert_eq!(opt_ready, 5);
2387
- }
2388
-
2389
- #[test]
2390
- fn phase_7_1_1_adversarial_large_pending_buffer() {
2391
- // Adversarial: Large pending buffer with low release selectivity
2392
- // 50 operations, only some will be released
2393
- let mut barrier_ref = CausalDependencyBarrier::new();
2394
- let mut barrier_opt = CausalDependencyBarrier::new();
2395
-
2396
- // Add 50 operations with escalating dependencies on single node
2397
- for i in 0..50 {
2398
- let mut req = VectorClock::new();
2399
- req.clocks.insert("node_a".to_string(), (i + 1) as u64);
2400
- barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2401
- barrier_opt.receive_operation(format!("op_{}", i), req);
2402
- }
2403
-
2404
- // Advance frontier to release only first 25 (those requiring node_a <= 25)
2405
- let mut frontier = VectorClock::new();
2406
- frontier.clocks.insert("node_a".to_string(), 25);
2407
-
2408
- barrier_ref
2409
- .prev_frontier
2410
- .clone_from(&barrier_ref.local_frontier);
2411
- barrier_ref.local_frontier.merge(&frontier);
2412
- barrier_ref.check_unblocked_operations_full_scan();
2413
-
2414
- barrier_opt
2415
- .prev_frontier
2416
- .clone_from(&barrier_opt.local_frontier);
2417
- barrier_opt.local_frontier.merge(&frontier);
2418
- barrier_opt.check_unblocked_operations_indexed();
2419
-
2420
- // Both should release exactly 25 operations
2421
- let ref_ready = barrier_ref
2422
- .get_pending_operations()
2423
- .iter()
2424
- .filter(|p| matches!(p.state, OperationState::Ready))
2425
- .count();
2426
- let opt_ready = barrier_opt
2427
- .get_pending_operations()
2428
- .iter()
2429
- .filter(|p| matches!(p.state, OperationState::Ready))
2430
- .count();
2431
-
2432
- assert_eq!(ref_ready, 25);
2433
- assert_eq!(opt_ready, 25);
2434
- }
2435
-
2436
- #[test]
2437
- fn phase_7_1_1_adversarial_independent_node_dependencies() {
2438
- // Adversarial: Operations depending on different independent nodes
2439
- let mut barrier_ref = CausalDependencyBarrier::new();
2440
- let mut barrier_opt = CausalDependencyBarrier::new();
2441
-
2442
- // Create enough operations with dependencies on different nodes
2443
- for i in 0..5 {
2444
- let mut req = VectorClock::new();
2445
- req.clocks.insert(format!("node_{}", i), 10);
2446
- barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2447
- barrier_opt.receive_operation(format!("op_{}", i), req);
2448
- }
2449
-
2450
- // Advance all nodes to their required values
2451
- let mut frontier = VectorClock::new();
2452
- for i in 0..5 {
2453
- frontier.clocks.insert(format!("node_{}", i), 10);
2454
- }
2455
-
2456
- barrier_ref.advance_frontier(&frontier);
2457
- barrier_opt.advance_frontier(&frontier);
2458
-
2459
- // All operations should be ready
2460
- let ref_ready = barrier_ref
2461
- .get_pending_operations()
2462
- .iter()
2463
- .filter(|p| matches!(p.state, OperationState::Ready))
2464
- .count();
2465
- let opt_ready = barrier_opt
2466
- .get_pending_operations()
2467
- .iter()
2468
- .filter(|p| matches!(p.state, OperationState::Ready))
2469
- .count();
2470
-
2471
- assert_eq!(ref_ready, 5);
2472
- assert_eq!(opt_ready, 5);
2473
- }
2474
-
2475
- #[test]
2476
- fn phase_7_1_1_property_index_completeness() {
2477
- // Property test: Index must contain all pending operations
2478
- for seed in 0..10 {
2479
- let scenario = DifferentialScenario::random_with_seed(seed);
2480
- let mut barrier = CausalDependencyBarrier::new();
2481
-
2482
- for (op_id, required) in &scenario.pending_operations {
2483
- barrier.receive_operation(op_id.clone(), required.clone());
2484
- }
2485
-
2486
- // Every pending operation must be discoverable through index
2487
- for (op_id, pending_op) in &barrier.pending {
2488
- let mut found = false;
2489
- for (node, ts) in &pending_op.required_dependencies.clocks {
2490
- if let Some(waiters) =
2491
- barrier.dependency_index.waiters.get(&(node.clone(), *ts))
2492
- {
2493
- if waiters.contains(op_id) {
2494
- found = true;
2495
- break;
2496
- }
2497
- }
2498
- }
2499
- assert!(found, "Operation {} not found in any index entry", op_id);
2500
- }
2501
- }
2502
- }
2503
-
2504
- #[test]
2505
- fn phase_7_1_1_property_frontier_only_advances() {
2506
- // Property test: Frontier never regresses in any dimension
2507
- for seed in 0..10 {
2508
- let scenario = DifferentialScenario::random_with_seed(seed);
2509
- let mut barrier = CausalDependencyBarrier::new();
2510
-
2511
- for (op_id, required) in &scenario.pending_operations {
2512
- barrier.receive_operation(op_id.clone(), required.clone());
2513
- }
2514
-
2515
- let mut prev_frontier = barrier.local_frontier.clone();
2516
-
2517
- for advance in &scenario.frontier_advances {
2518
- barrier.prev_frontier.clone_from(&barrier.local_frontier);
2519
- barrier.local_frontier.merge(advance);
2520
- barrier.check_unblocked_operations();
2521
-
2522
- // Check frontier only advanced (never regressed)
2523
- for (node, prev_ts) in &prev_frontier.clocks {
2524
- let new_ts = barrier.local_frontier.get(node);
2525
- assert!(
2526
- new_ts >= *prev_ts,
2527
- "Frontier regressed for {} from {} to {}",
2528
- node,
2529
- prev_ts,
2530
- new_ts
2531
- );
2532
- }
2533
-
2534
- prev_frontier = barrier.local_frontier.clone();
2535
- }
2536
- }
2537
- }
2538
-
2539
- #[test]
2540
- fn phase_7_1_1_property_ready_operations_never_waiting() {
2541
- // Property test: Once an operation is marked Ready, it never regresses to Waiting
2542
- for seed in 0..10 {
2543
- let scenario = DifferentialScenario::random_with_seed(seed);
2544
- let mut barrier = CausalDependencyBarrier::new();
2545
-
2546
- for (op_id, required) in &scenario.pending_operations {
2547
- barrier.receive_operation(op_id.clone(), required.clone());
2548
- }
2549
-
2550
- let mut seen_ready = std::collections::HashSet::new();
2551
-
2552
- for advance in &scenario.frontier_advances {
2553
- barrier.prev_frontier.clone_from(&barrier.local_frontier);
2554
- barrier.local_frontier.merge(advance);
2555
- barrier.check_unblocked_operations();
2556
-
2557
- for pending_op in barrier.get_pending_operations() {
2558
- if matches!(pending_op.state, OperationState::Ready) {
2559
- seen_ready.insert(pending_op.operation_id.clone());
2560
- } else if matches!(
2561
- pending_op.state,
2562
- OperationState::WaitingForDependencies { .. }
2563
- ) {
2564
- assert!(
2565
- !seen_ready.contains(&pending_op.operation_id),
2566
- "Operation {} regressed from Ready to Waiting",
2567
- pending_op.operation_id
2568
- );
2569
- }
2570
- }
2571
- }
2572
- }
2573
- }
2574
- }