@feltdb/core 0.8.4 → 0.8.5
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.
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +165 -0
- package/dist/create/server-source/Cargo.toml +9 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +3 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +44 -44
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +12 -24
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +7 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +13 -10
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +12 -9
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +78 -25
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +16 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +13 -5
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +41 -22
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +33 -15
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +100 -33
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +56 -20
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +116 -41
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +186 -55
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +64 -26
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +46 -15
- package/dist/create/server-source/crates/feltdb/src/admission.rs +8 -15
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +43 -13
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +15 -42
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +65 -19
- package/dist/create/server-source/crates/feltdb/src/application.rs +113 -30
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +475 -140
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +17 -15
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +266 -114
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +25 -7
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +10 -15
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +3 -11
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +14 -5
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +21 -25
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +14 -11
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +103 -22
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +18 -15
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +12 -8
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +1 -5
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +87 -23
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +3 -7
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +10 -9
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +1 -6
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +35 -38
- package/dist/create/server-source/crates/feltdb/src/lib.rs +507 -6
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +4 -1
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +1 -2
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +107 -30
- package/dist/create/server-source/crates/feltdb/src/observability.rs +19 -6
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +12 -11
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +9 -4
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +65 -18
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +193 -57
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +37 -27
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +38 -10
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +20 -14
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +394 -229
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +8 -6
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +11 -13
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +79 -70
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +3 -3
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +33 -11
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +6 -6
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +14 -56
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +29 -27
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +35 -17
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +8 -2
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +70 -17
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +23 -6
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +12 -3
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +41 -11
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +56 -25
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +701 -245
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +31 -13
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +5 -5
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +48 -18
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +30 -11
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +4 -3
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +36 -10
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +516 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +13 -4
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +222 -0
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +82 -54
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +2 -2
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +1514 -537
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +672 -0
- package/dist/create/server-source/crates/feltdb/src/submission.rs +5 -11
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +24 -35
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +24 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +248 -59
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +17 -20
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +749 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +512 -4
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +3 -4
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +288 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +392 -0
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +436 -220
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +1916 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +1878 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +1862 -0
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +2 -1
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +80 -25
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +5 -8
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +52 -44
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +33 -16
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +22 -7
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +41 -22
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +25 -8
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +9 -6
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +120 -69
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +21 -10
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +1032 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +868 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +221 -0
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +338 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +1039 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +739 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +427 -0
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +3 -3
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +25 -22
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +1799 -0
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +1316 -0
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +53 -61
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +156 -61
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +255 -0
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +249 -0
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +994 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +2 -1
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +326 -6
- package/dist/studio-app/assets/{feltdb_wasm-CVQWgXO-.js → feltdb_wasm-DaNwCLRX.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-DnsHNv6g.wasm +0 -0
- package/dist/studio-app/assets/{index-DwgNAIIX.js → index-j8IlhNqJ.js} +1 -1
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workload.d.ts +2 -1
- package/dist/workload.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-CNVpvaZV.wasm +0 -0
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
/// READY
|
|
25
25
|
/// ↓
|
|
26
26
|
/// APPLIED
|
|
27
|
-
|
|
28
27
|
use crate::convergence::VectorClock;
|
|
29
28
|
use std::collections::HashMap;
|
|
30
29
|
|
|
@@ -74,7 +73,7 @@ pub enum OperationState {
|
|
|
74
73
|
/// Operation is waiting for causal dependencies to be satisfied
|
|
75
74
|
WaitingForDependencies {
|
|
76
75
|
required_clock: VectorClock,
|
|
77
|
-
missing_dependencies: Vec<String>,
|
|
76
|
+
missing_dependencies: Vec<String>, // human-readable list
|
|
78
77
|
},
|
|
79
78
|
/// All dependencies satisfied, ready to apply
|
|
80
79
|
Ready,
|
|
@@ -284,7 +283,10 @@ impl CausalDependencyBarrier {
|
|
|
284
283
|
let mut to_ready = Vec::new();
|
|
285
284
|
|
|
286
285
|
for (op_id, pending_op) in self.pending.iter() {
|
|
287
|
-
if !self
|
|
286
|
+
if !self
|
|
287
|
+
.local_frontier
|
|
288
|
+
.happens_before(&pending_op.required_dependencies)
|
|
289
|
+
{
|
|
288
290
|
to_ready.push(op_id.clone());
|
|
289
291
|
newly_ready.push(op_id.clone());
|
|
290
292
|
}
|
|
@@ -346,7 +348,10 @@ impl CausalDependencyBarrier {
|
|
|
346
348
|
let mut to_ready = Vec::new();
|
|
347
349
|
for op_id in candidates {
|
|
348
350
|
if let Some(pending_op) = self.pending.get(&op_id) {
|
|
349
|
-
if !self
|
|
351
|
+
if !self
|
|
352
|
+
.local_frontier
|
|
353
|
+
.happens_before(&pending_op.required_dependencies)
|
|
354
|
+
{
|
|
350
355
|
to_ready.push(op_id.clone());
|
|
351
356
|
newly_ready.push(op_id);
|
|
352
357
|
}
|
|
@@ -386,10 +391,7 @@ impl CausalDependencyBarrier {
|
|
|
386
391
|
for (node, required_ts) in &required.clocks {
|
|
387
392
|
let current_ts = self.local_frontier.get(node);
|
|
388
393
|
if current_ts < *required_ts {
|
|
389
|
-
missing.push(format!(
|
|
390
|
-
"{}:{} (have {})",
|
|
391
|
-
node, required_ts, current_ts
|
|
392
|
-
));
|
|
394
|
+
missing.push(format!("{}:{} (have {})", node, required_ts, current_ts));
|
|
393
395
|
}
|
|
394
396
|
}
|
|
395
397
|
|
|
@@ -609,7 +611,8 @@ impl CausalDependencyBarrier {
|
|
|
609
611
|
#[cfg(test)]
|
|
610
612
|
fn index_operation(&mut self, op_id: &str, required: &VectorClock) {
|
|
611
613
|
for (node, timestamp) in &required.clocks {
|
|
612
|
-
self.dependency_index
|
|
614
|
+
self.dependency_index
|
|
615
|
+
.waiters
|
|
613
616
|
.entry((node.clone(), *timestamp))
|
|
614
617
|
.or_insert_with(Vec::new)
|
|
615
618
|
.push(op_id.to_string());
|
|
@@ -620,7 +623,11 @@ impl CausalDependencyBarrier {
|
|
|
620
623
|
#[cfg(test)]
|
|
621
624
|
fn unindex_operation(&mut self, op_id: &str, required: &VectorClock) {
|
|
622
625
|
for (node, timestamp) in &required.clocks {
|
|
623
|
-
if let Some(waiters) = self
|
|
626
|
+
if let Some(waiters) = self
|
|
627
|
+
.dependency_index
|
|
628
|
+
.waiters
|
|
629
|
+
.get_mut(&(node.clone(), *timestamp))
|
|
630
|
+
{
|
|
624
631
|
waiters.retain(|id| id != op_id);
|
|
625
632
|
}
|
|
626
633
|
}
|
|
@@ -705,7 +712,7 @@ mod tests {
|
|
|
705
712
|
|
|
706
713
|
// Advance frontier to satisfy requirement
|
|
707
714
|
barrier.advance_frontier(&required);
|
|
708
|
-
barrier.clear_metrics();
|
|
715
|
+
barrier.clear_metrics(); // Reset metrics after setup
|
|
709
716
|
|
|
710
717
|
// Pre-fill buffer with unrelated operations
|
|
711
718
|
for i in 0..buffer_size {
|
|
@@ -713,14 +720,11 @@ mod tests {
|
|
|
713
720
|
unrelated.increment(&format!("other_{}", i));
|
|
714
721
|
let _ = barrier.receive_operation(format!("op_buffer_{}", i), unrelated);
|
|
715
722
|
}
|
|
716
|
-
barrier.clear_metrics();
|
|
723
|
+
barrier.clear_metrics(); // Reset metrics after filling
|
|
717
724
|
|
|
718
725
|
// Critical test: receive satisfied operation
|
|
719
726
|
// Expected: Should be ready, metrics should show frontier check not buffer scan
|
|
720
|
-
let result = barrier.receive_operation(
|
|
721
|
-
"op_test".to_string(),
|
|
722
|
-
required.clone(),
|
|
723
|
-
);
|
|
727
|
+
let result = barrier.receive_operation("op_test".to_string(), required.clone());
|
|
724
728
|
|
|
725
729
|
let metrics = barrier.metrics();
|
|
726
730
|
|
|
@@ -778,10 +782,13 @@ mod tests {
|
|
|
778
782
|
|
|
779
783
|
let mut required = VectorClock::new();
|
|
780
784
|
required.increment("node_a");
|
|
781
|
-
required.increment("node_a");
|
|
785
|
+
required.increment("node_a"); // {node_a: 2}
|
|
782
786
|
|
|
783
787
|
let result = barrier.receive_operation("op1".to_string(), required);
|
|
784
|
-
assert!(matches!(
|
|
788
|
+
assert!(matches!(
|
|
789
|
+
result,
|
|
790
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
791
|
+
));
|
|
785
792
|
assert_eq!(barrier.get_pending_operations().len(), 1);
|
|
786
793
|
}
|
|
787
794
|
|
|
@@ -792,10 +799,13 @@ mod tests {
|
|
|
792
799
|
// Operation requires {node_a: 2}
|
|
793
800
|
let mut required = VectorClock::new();
|
|
794
801
|
required.increment("node_a");
|
|
795
|
-
required.increment("node_a");
|
|
802
|
+
required.increment("node_a"); // {node_a: 2}
|
|
796
803
|
|
|
797
804
|
let result = barrier.receive_operation("op1".to_string(), required);
|
|
798
|
-
assert!(matches!(
|
|
805
|
+
assert!(matches!(
|
|
806
|
+
result,
|
|
807
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
808
|
+
));
|
|
799
809
|
assert_eq!(barrier.get_pending_operations().len(), 1);
|
|
800
810
|
|
|
801
811
|
// Advance frontier to satisfy dependency
|
|
@@ -817,14 +827,17 @@ mod tests {
|
|
|
817
827
|
|
|
818
828
|
// Op2 depends on Op1 having been applied
|
|
819
829
|
// Op1 has no dependencies
|
|
820
|
-
let op1_required = VectorClock::new();
|
|
830
|
+
let op1_required = VectorClock::new(); // Op1 requires nothing
|
|
821
831
|
|
|
822
832
|
let mut op2_required = VectorClock::new();
|
|
823
|
-
op2_required.increment("node_a");
|
|
833
|
+
op2_required.increment("node_a"); // Op2 requires {node_a: 1}
|
|
824
834
|
|
|
825
835
|
// Receive Op2 before Op1
|
|
826
836
|
let result = barrier.receive_operation("op2".to_string(), op2_required.clone());
|
|
827
|
-
assert!(matches!(
|
|
837
|
+
assert!(matches!(
|
|
838
|
+
result,
|
|
839
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
840
|
+
));
|
|
828
841
|
|
|
829
842
|
// Receive Op1 (which has no dependencies)
|
|
830
843
|
let result = barrier.receive_operation("op1".to_string(), op1_required.clone());
|
|
@@ -864,7 +877,7 @@ mod tests {
|
|
|
864
877
|
barrier.receive_operation("op1".to_string(), op1_clock.clone());
|
|
865
878
|
|
|
866
879
|
// All should be buffered except Op1 (which can apply immediately)
|
|
867
|
-
assert_eq!(barrier.get_pending_operations().len(), 2);
|
|
880
|
+
assert_eq!(barrier.get_pending_operations().len(), 2); // Op2 and Op3
|
|
868
881
|
|
|
869
882
|
// Apply Op1
|
|
870
883
|
let mut apply_clock = VectorClock::new();
|
|
@@ -1051,7 +1064,7 @@ mod tests {
|
|
|
1051
1064
|
// Trigger full-scan directly: should see all 10 items
|
|
1052
1065
|
let mut partial = VectorClock::new();
|
|
1053
1066
|
partial.increment("node_a");
|
|
1054
|
-
partial.increment("node_a");
|
|
1067
|
+
partial.increment("node_a"); // Only {node_a: 2}, not enough for requirement
|
|
1055
1068
|
{
|
|
1056
1069
|
let current = barrier.get_frontier().clone();
|
|
1057
1070
|
barrier.get_prev_frontier_mut().clone_from(¤t);
|
|
@@ -1077,7 +1090,7 @@ mod tests {
|
|
|
1077
1090
|
let mut more_partial = VectorClock::new();
|
|
1078
1091
|
more_partial.increment("node_a");
|
|
1079
1092
|
more_partial.increment("node_a");
|
|
1080
|
-
more_partial.increment("node_a");
|
|
1093
|
+
more_partial.increment("node_a"); // Only {node_a: 3}, still not enough
|
|
1081
1094
|
{
|
|
1082
1095
|
let current = barrier.get_frontier().clone();
|
|
1083
1096
|
barrier.get_prev_frontier_mut().clone_from(¤t);
|
|
@@ -1120,14 +1133,20 @@ mod tests {
|
|
|
1120
1133
|
a3_required.increment("node_a");
|
|
1121
1134
|
a3_required.increment("node_a");
|
|
1122
1135
|
let result_a3 = barrier.receive_operation("A3".to_string(), a3_required.clone());
|
|
1123
|
-
assert!(matches!(
|
|
1136
|
+
assert!(matches!(
|
|
1137
|
+
result_a3,
|
|
1138
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
1139
|
+
));
|
|
1124
1140
|
|
|
1125
1141
|
// A2: requires {node_a: 2}
|
|
1126
1142
|
let mut a2_required = VectorClock::new();
|
|
1127
1143
|
a2_required.increment("node_a");
|
|
1128
1144
|
a2_required.increment("node_a");
|
|
1129
1145
|
let result_a2 = barrier.receive_operation("A2".to_string(), a2_required.clone());
|
|
1130
|
-
assert!(matches!(
|
|
1146
|
+
assert!(matches!(
|
|
1147
|
+
result_a2,
|
|
1148
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
1149
|
+
));
|
|
1131
1150
|
|
|
1132
1151
|
// Now frontier is only {node_a: 1}, both A2 and A3 are buffered
|
|
1133
1152
|
assert_eq!(barrier.get_pending_operations().len(), 2);
|
|
@@ -1143,7 +1162,10 @@ mod tests {
|
|
|
1143
1162
|
let pending = barrier.get_pending_operations();
|
|
1144
1163
|
assert_eq!(pending.len(), 1);
|
|
1145
1164
|
assert_eq!(pending[0].operation_id, "A3");
|
|
1146
|
-
assert!(matches!(
|
|
1165
|
+
assert!(matches!(
|
|
1166
|
+
pending[0].state,
|
|
1167
|
+
OperationState::WaitingForDependencies { .. }
|
|
1168
|
+
));
|
|
1147
1169
|
|
|
1148
1170
|
// Apply A3 with {node_a: 3}
|
|
1149
1171
|
let mut a3_contribution = VectorClock::new();
|
|
@@ -1167,16 +1189,19 @@ mod tests {
|
|
|
1167
1189
|
let mut a5_required = VectorClock::new();
|
|
1168
1190
|
a5_required.increment("B");
|
|
1169
1191
|
a5_required.increment("B");
|
|
1170
|
-
a5_required.increment("B");
|
|
1192
|
+
a5_required.increment("B"); // {B: 3}
|
|
1171
1193
|
for _ in 0..7 {
|
|
1172
1194
|
a5_required.increment("C");
|
|
1173
|
-
}
|
|
1195
|
+
} // {C: 7}
|
|
1174
1196
|
a5_required.increment("D");
|
|
1175
|
-
a5_required.increment("D");
|
|
1197
|
+
a5_required.increment("D"); // {D: 2}
|
|
1176
1198
|
|
|
1177
1199
|
// Try to receive A5 before any dependencies
|
|
1178
1200
|
let result = barrier.receive_operation("A5".to_string(), a5_required.clone());
|
|
1179
|
-
assert!(matches!(
|
|
1201
|
+
assert!(matches!(
|
|
1202
|
+
result,
|
|
1203
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
1204
|
+
));
|
|
1180
1205
|
|
|
1181
1206
|
// Partially satisfy: apply B3
|
|
1182
1207
|
let mut b3_clock = VectorClock::new();
|
|
@@ -1245,7 +1270,10 @@ mod tests {
|
|
|
1245
1270
|
b3_required.increment("B");
|
|
1246
1271
|
b3_required.increment("B");
|
|
1247
1272
|
let result_b3 = barrier.receive_operation("B3".to_string(), b3_required.clone());
|
|
1248
|
-
assert!(matches!(
|
|
1273
|
+
assert!(matches!(
|
|
1274
|
+
result_b3,
|
|
1275
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
1276
|
+
));
|
|
1249
1277
|
|
|
1250
1278
|
// At this point:
|
|
1251
1279
|
// - applied_frontier[B] = 1 (only B1 applied)
|
|
@@ -1258,7 +1286,10 @@ mod tests {
|
|
|
1258
1286
|
a5_required.increment("B");
|
|
1259
1287
|
a5_required.increment("B");
|
|
1260
1288
|
let result_a5 = barrier.receive_operation("A5".to_string(), a5_required.clone());
|
|
1261
|
-
assert!(matches!(
|
|
1289
|
+
assert!(matches!(
|
|
1290
|
+
result_a5,
|
|
1291
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
1292
|
+
));
|
|
1262
1293
|
|
|
1263
1294
|
// NOW apply B3
|
|
1264
1295
|
barrier.apply_operation("B3", &b3_required);
|
|
@@ -1285,7 +1316,10 @@ mod tests {
|
|
|
1285
1316
|
|
|
1286
1317
|
for i in 0..5 {
|
|
1287
1318
|
let result = barrier.receive_operation(format!("op_{}", i), required.clone());
|
|
1288
|
-
assert!(matches!(
|
|
1319
|
+
assert!(matches!(
|
|
1320
|
+
result,
|
|
1321
|
+
ReceiveResult::BufferedWaitingForDependencies { .. }
|
|
1322
|
+
));
|
|
1289
1323
|
}
|
|
1290
1324
|
|
|
1291
1325
|
assert_eq!(barrier.get_pending_operations().len(), 5);
|
|
@@ -1470,12 +1504,18 @@ mod tests {
|
|
|
1470
1504
|
barrier.receive_operation("op_a3".to_string(), req_a3);
|
|
1471
1505
|
|
|
1472
1506
|
// Verify index is correct
|
|
1473
|
-
let a1_waiters = barrier
|
|
1507
|
+
let a1_waiters = barrier
|
|
1508
|
+
.dependency_index
|
|
1509
|
+
.waiters
|
|
1510
|
+
.get(&("node_a".to_string(), 1))
|
|
1474
1511
|
.map(|w| w.clone())
|
|
1475
1512
|
.unwrap_or_default();
|
|
1476
1513
|
assert!(a1_waiters.contains(&"op_a1".to_string()));
|
|
1477
1514
|
|
|
1478
|
-
let a2_waiters = barrier
|
|
1515
|
+
let a2_waiters = barrier
|
|
1516
|
+
.dependency_index
|
|
1517
|
+
.waiters
|
|
1518
|
+
.get(&("node_a".to_string(), 2))
|
|
1479
1519
|
.map(|w| w.clone())
|
|
1480
1520
|
.unwrap_or_default();
|
|
1481
1521
|
assert!(a2_waiters.contains(&"op_a2".to_string()));
|
|
@@ -1487,7 +1527,8 @@ mod tests {
|
|
|
1487
1527
|
|
|
1488
1528
|
// Check which operations are Ready
|
|
1489
1529
|
let pending = barrier.get_pending_operations();
|
|
1490
|
-
let ready: Vec<_> = pending
|
|
1530
|
+
let ready: Vec<_> = pending
|
|
1531
|
+
.iter()
|
|
1491
1532
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1492
1533
|
.map(|p| p.operation_id.clone())
|
|
1493
1534
|
.collect();
|
|
@@ -1498,7 +1539,8 @@ mod tests {
|
|
|
1498
1539
|
assert!(ready.contains(&"op_a2".to_string()));
|
|
1499
1540
|
|
|
1500
1541
|
// op_a3 should still be Waiting (needs A:3)
|
|
1501
|
-
let waiting: Vec<_> = pending
|
|
1542
|
+
let waiting: Vec<_> = pending
|
|
1543
|
+
.iter()
|
|
1502
1544
|
.filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
|
|
1503
1545
|
.map(|p| p.operation_id.clone())
|
|
1504
1546
|
.collect();
|
|
@@ -1518,9 +1560,13 @@ mod tests {
|
|
|
1518
1560
|
// Add 20 operations with varied dependencies
|
|
1519
1561
|
for i in 0..20 {
|
|
1520
1562
|
let mut required = VectorClock::new();
|
|
1521
|
-
required
|
|
1563
|
+
required
|
|
1564
|
+
.clocks
|
|
1565
|
+
.insert("node_a".to_string(), ((i % 5) + 1) as u64);
|
|
1522
1566
|
if i % 3 == 0 {
|
|
1523
|
-
required
|
|
1567
|
+
required
|
|
1568
|
+
.clocks
|
|
1569
|
+
.insert("node_b".to_string(), ((i / 3) + 1) as u64);
|
|
1524
1570
|
}
|
|
1525
1571
|
barrier.receive_operation(format!("op_{}", i), required);
|
|
1526
1572
|
}
|
|
@@ -1530,11 +1576,16 @@ mod tests {
|
|
|
1530
1576
|
for (node, ts) in &pending_op.required_dependencies.clocks {
|
|
1531
1577
|
let key = (node.clone(), *ts);
|
|
1532
1578
|
assert!(
|
|
1533
|
-
barrier
|
|
1579
|
+
barrier
|
|
1580
|
+
.dependency_index
|
|
1581
|
+
.waiters
|
|
1582
|
+
.get(&key)
|
|
1534
1583
|
.map(|waiters| waiters.contains(op_id))
|
|
1535
1584
|
.unwrap_or(false),
|
|
1536
1585
|
"Operation {} not found in index for ({}, {})",
|
|
1537
|
-
op_id,
|
|
1586
|
+
op_id,
|
|
1587
|
+
node,
|
|
1588
|
+
ts
|
|
1538
1589
|
);
|
|
1539
1590
|
}
|
|
1540
1591
|
}
|
|
@@ -1551,11 +1602,12 @@ mod tests {
|
|
|
1551
1602
|
let result = barrier.receive_operation("op_1".to_string(), required.clone());
|
|
1552
1603
|
|
|
1553
1604
|
// Verify it's indexed
|
|
1554
|
-
assert!(
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
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));
|
|
1559
1611
|
|
|
1560
1612
|
// Apply operation (moving it from pending to applied)
|
|
1561
1613
|
barrier.apply_operation("op_1", &required);
|
|
@@ -1580,12 +1632,18 @@ mod tests {
|
|
|
1580
1632
|
barrier.receive_operation("op_dup".to_string(), required.clone());
|
|
1581
1633
|
|
|
1582
1634
|
// Should be indexed exactly once for (node_a, 5)
|
|
1583
|
-
let waiters_at_5 = barrier
|
|
1635
|
+
let waiters_at_5 = barrier
|
|
1636
|
+
.dependency_index
|
|
1637
|
+
.waiters
|
|
1638
|
+
.get(&("node_a".to_string(), 5))
|
|
1584
1639
|
.map(|w| w.iter().filter(|id| id == &"op_dup").count())
|
|
1585
1640
|
.unwrap_or(0);
|
|
1586
1641
|
|
|
1587
1642
|
// VectorClock naturally deduplicates, so should only appear once
|
|
1588
|
-
assert_eq!(
|
|
1643
|
+
assert_eq!(
|
|
1644
|
+
waiters_at_5, 1,
|
|
1645
|
+
"Operation should appear once per unique dependency"
|
|
1646
|
+
);
|
|
1589
1647
|
}
|
|
1590
1648
|
|
|
1591
1649
|
#[test]
|
|
@@ -1601,15 +1659,24 @@ mod tests {
|
|
|
1601
1659
|
barrier.receive_operation("op_multi".to_string(), required);
|
|
1602
1660
|
|
|
1603
1661
|
// Should be findable via any dependency
|
|
1604
|
-
assert!(barrier
|
|
1662
|
+
assert!(barrier
|
|
1663
|
+
.dependency_index
|
|
1664
|
+
.waiters
|
|
1665
|
+
.get(&("node_a".to_string(), 2))
|
|
1605
1666
|
.map(|w| w.contains(&"op_multi".to_string()))
|
|
1606
1667
|
.unwrap_or(false));
|
|
1607
1668
|
|
|
1608
|
-
assert!(barrier
|
|
1669
|
+
assert!(barrier
|
|
1670
|
+
.dependency_index
|
|
1671
|
+
.waiters
|
|
1672
|
+
.get(&("node_b".to_string(), 3))
|
|
1609
1673
|
.map(|w| w.contains(&"op_multi".to_string()))
|
|
1610
1674
|
.unwrap_or(false));
|
|
1611
1675
|
|
|
1612
|
-
assert!(barrier
|
|
1676
|
+
assert!(barrier
|
|
1677
|
+
.dependency_index
|
|
1678
|
+
.waiters
|
|
1679
|
+
.get(&("node_c".to_string(), 1))
|
|
1613
1680
|
.map(|w| w.contains(&"op_multi".to_string()))
|
|
1614
1681
|
.unwrap_or(false));
|
|
1615
1682
|
}
|
|
@@ -1639,11 +1706,13 @@ mod tests {
|
|
|
1639
1706
|
|
|
1640
1707
|
// Check which operations are Ready vs still Waiting
|
|
1641
1708
|
let pending = barrier.get_pending_operations();
|
|
1642
|
-
let ready_ops: Vec<_> = pending
|
|
1709
|
+
let ready_ops: Vec<_> = pending
|
|
1710
|
+
.iter()
|
|
1643
1711
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1644
1712
|
.map(|p| p.operation_id.clone())
|
|
1645
1713
|
.collect();
|
|
1646
|
-
let waiting_ops: Vec<_> = pending
|
|
1714
|
+
let waiting_ops: Vec<_> = pending
|
|
1715
|
+
.iter()
|
|
1647
1716
|
.filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
|
|
1648
1717
|
.map(|p| p.operation_id.clone())
|
|
1649
1718
|
.collect();
|
|
@@ -1675,7 +1744,10 @@ mod tests {
|
|
|
1675
1744
|
barrier.receive_operation("op_2".to_string(), req_2);
|
|
1676
1745
|
|
|
1677
1746
|
// Index should have entries
|
|
1678
|
-
assert!(barrier
|
|
1747
|
+
assert!(barrier
|
|
1748
|
+
.dependency_index
|
|
1749
|
+
.waiters
|
|
1750
|
+
.get(&("node_a".to_string(), 1))
|
|
1679
1751
|
.map(|w| w.contains(&"op_1".to_string()))
|
|
1680
1752
|
.unwrap_or(false));
|
|
1681
1753
|
|
|
@@ -1700,7 +1772,9 @@ mod tests {
|
|
|
1700
1772
|
// Add mix of operations
|
|
1701
1773
|
for i in 0..5 {
|
|
1702
1774
|
let mut required = VectorClock::new();
|
|
1703
|
-
required
|
|
1775
|
+
required
|
|
1776
|
+
.clocks
|
|
1777
|
+
.insert("node_a".to_string(), ((i % 3) + 1) as u64);
|
|
1704
1778
|
barrier.receive_operation(format!("op_{}", i), required);
|
|
1705
1779
|
}
|
|
1706
1780
|
|
|
@@ -1708,11 +1782,16 @@ mod tests {
|
|
|
1708
1782
|
for (op_id, pending_op) in &barrier.pending {
|
|
1709
1783
|
for (node, ts) in &pending_op.required_dependencies.clocks {
|
|
1710
1784
|
assert!(
|
|
1711
|
-
barrier
|
|
1785
|
+
barrier
|
|
1786
|
+
.dependency_index
|
|
1787
|
+
.waiters
|
|
1788
|
+
.get(&(node.clone(), *ts))
|
|
1712
1789
|
.map(|w| w.contains(op_id))
|
|
1713
1790
|
.unwrap_or(false),
|
|
1714
1791
|
"Operation {} not indexed for ({}, {})",
|
|
1715
|
-
op_id,
|
|
1792
|
+
op_id,
|
|
1793
|
+
node,
|
|
1794
|
+
ts
|
|
1716
1795
|
);
|
|
1717
1796
|
}
|
|
1718
1797
|
}
|
|
@@ -1726,7 +1805,10 @@ mod tests {
|
|
|
1726
1805
|
for (op_id, pending_op) in &barrier.pending {
|
|
1727
1806
|
for (node, ts) in &pending_op.required_dependencies.clocks {
|
|
1728
1807
|
assert!(
|
|
1729
|
-
barrier
|
|
1808
|
+
barrier
|
|
1809
|
+
.dependency_index
|
|
1810
|
+
.waiters
|
|
1811
|
+
.get(&(node.clone(), *ts))
|
|
1730
1812
|
.map(|w| w.contains(op_id))
|
|
1731
1813
|
.unwrap_or(false),
|
|
1732
1814
|
"Remaining operation {} should still be in index",
|
|
@@ -1761,7 +1843,9 @@ mod tests {
|
|
|
1761
1843
|
let mut rng_seed = hasher.finish();
|
|
1762
1844
|
|
|
1763
1845
|
fn next_rng(seed: &mut u64) -> u64 {
|
|
1764
|
-
*seed = seed
|
|
1846
|
+
*seed = seed
|
|
1847
|
+
.wrapping_mul(6364136223846793005)
|
|
1848
|
+
.wrapping_add(1442695040888963407);
|
|
1765
1849
|
*seed
|
|
1766
1850
|
}
|
|
1767
1851
|
|
|
@@ -1836,12 +1920,16 @@ mod tests {
|
|
|
1836
1920
|
barrier_opt.clear_metrics();
|
|
1837
1921
|
|
|
1838
1922
|
// Reference
|
|
1839
|
-
barrier_ref
|
|
1923
|
+
barrier_ref
|
|
1924
|
+
.prev_frontier
|
|
1925
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
1840
1926
|
barrier_ref.local_frontier.merge(advance);
|
|
1841
1927
|
let _ref_ready = barrier_ref.check_unblocked_operations_full_scan();
|
|
1842
1928
|
|
|
1843
1929
|
// Optimized
|
|
1844
|
-
barrier_opt
|
|
1930
|
+
barrier_opt
|
|
1931
|
+
.prev_frontier
|
|
1932
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
1845
1933
|
barrier_opt.local_frontier.merge(advance);
|
|
1846
1934
|
let _opt_ready = barrier_opt.check_unblocked_operations_indexed();
|
|
1847
1935
|
|
|
@@ -1863,7 +1951,9 @@ mod tests {
|
|
|
1863
1951
|
assert!(
|
|
1864
1952
|
waiters.contains(op_id),
|
|
1865
1953
|
"Seed {}: Operation {} not in index for {:?}",
|
|
1866
|
-
seed,
|
|
1954
|
+
seed,
|
|
1955
|
+
op_id,
|
|
1956
|
+
key
|
|
1867
1957
|
);
|
|
1868
1958
|
}
|
|
1869
1959
|
}
|
|
@@ -1890,20 +1980,26 @@ mod tests {
|
|
|
1890
1980
|
let mut frontier = VectorClock::new();
|
|
1891
1981
|
frontier.clocks.insert("node_a".to_string(), 5);
|
|
1892
1982
|
|
|
1893
|
-
barrier_ref
|
|
1983
|
+
barrier_ref
|
|
1984
|
+
.prev_frontier
|
|
1985
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
1894
1986
|
barrier_ref.local_frontier.merge(&frontier);
|
|
1895
1987
|
barrier_ref.check_unblocked_operations_full_scan();
|
|
1896
1988
|
|
|
1897
|
-
barrier_opt
|
|
1989
|
+
barrier_opt
|
|
1990
|
+
.prev_frontier
|
|
1991
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
1898
1992
|
barrier_opt.local_frontier.merge(&frontier);
|
|
1899
1993
|
barrier_opt.check_unblocked_operations_indexed();
|
|
1900
1994
|
|
|
1901
1995
|
// Both should have released all 10
|
|
1902
|
-
let ref_ready = barrier_ref
|
|
1996
|
+
let ref_ready = barrier_ref
|
|
1997
|
+
.get_pending_operations()
|
|
1903
1998
|
.iter()
|
|
1904
1999
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1905
2000
|
.count();
|
|
1906
|
-
let opt_ready = barrier_opt
|
|
2001
|
+
let opt_ready = barrier_opt
|
|
2002
|
+
.get_pending_operations()
|
|
1907
2003
|
.iter()
|
|
1908
2004
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1909
2005
|
.count();
|
|
@@ -1929,20 +2025,26 @@ mod tests {
|
|
|
1929
2025
|
let mut frontier = VectorClock::new();
|
|
1930
2026
|
frontier.clocks.insert("node_a".to_string(), 7);
|
|
1931
2027
|
|
|
1932
|
-
barrier_ref
|
|
2028
|
+
barrier_ref
|
|
2029
|
+
.prev_frontier
|
|
2030
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
1933
2031
|
barrier_ref.local_frontier.merge(&frontier);
|
|
1934
2032
|
barrier_ref.check_unblocked_operations_full_scan();
|
|
1935
2033
|
|
|
1936
|
-
barrier_opt
|
|
2034
|
+
barrier_opt
|
|
2035
|
+
.prev_frontier
|
|
2036
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
1937
2037
|
barrier_opt.local_frontier.merge(&frontier);
|
|
1938
2038
|
barrier_opt.check_unblocked_operations_indexed();
|
|
1939
2039
|
|
|
1940
2040
|
// Both should have released op_1 through op_7
|
|
1941
|
-
let ref_ready = barrier_ref
|
|
2041
|
+
let ref_ready = barrier_ref
|
|
2042
|
+
.get_pending_operations()
|
|
1942
2043
|
.iter()
|
|
1943
2044
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1944
2045
|
.count();
|
|
1945
|
-
let opt_ready = barrier_opt
|
|
2046
|
+
let opt_ready = barrier_opt
|
|
2047
|
+
.get_pending_operations()
|
|
1946
2048
|
.iter()
|
|
1947
2049
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1948
2050
|
.count();
|
|
@@ -1967,19 +2069,25 @@ mod tests {
|
|
|
1967
2069
|
let mut frontier = VectorClock::new();
|
|
1968
2070
|
frontier.clocks.insert("node_a".to_string(), 5);
|
|
1969
2071
|
|
|
1970
|
-
barrier_ref
|
|
2072
|
+
barrier_ref
|
|
2073
|
+
.prev_frontier
|
|
2074
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
1971
2075
|
barrier_ref.local_frontier.merge(&frontier);
|
|
1972
2076
|
barrier_ref.check_unblocked_operations_full_scan();
|
|
1973
2077
|
|
|
1974
|
-
barrier_opt
|
|
2078
|
+
barrier_opt
|
|
2079
|
+
.prev_frontier
|
|
2080
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
1975
2081
|
barrier_opt.local_frontier.merge(&frontier);
|
|
1976
2082
|
barrier_opt.check_unblocked_operations_indexed();
|
|
1977
2083
|
|
|
1978
|
-
let ref_ready = barrier_ref
|
|
2084
|
+
let ref_ready = barrier_ref
|
|
2085
|
+
.get_pending_operations()
|
|
1979
2086
|
.iter()
|
|
1980
2087
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1981
2088
|
.count();
|
|
1982
|
-
let opt_ready = barrier_opt
|
|
2089
|
+
let opt_ready = barrier_opt
|
|
2090
|
+
.get_pending_operations()
|
|
1983
2091
|
.iter()
|
|
1984
2092
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
1985
2093
|
.count();
|
|
@@ -1997,11 +2105,15 @@ mod tests {
|
|
|
1997
2105
|
let mut frontier = VectorClock::new();
|
|
1998
2106
|
frontier.clocks.insert("node_a".to_string(), 10);
|
|
1999
2107
|
|
|
2000
|
-
barrier_ref
|
|
2108
|
+
barrier_ref
|
|
2109
|
+
.prev_frontier
|
|
2110
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
2001
2111
|
barrier_ref.local_frontier.merge(&frontier);
|
|
2002
2112
|
let ref_ready = barrier_ref.check_unblocked_operations_full_scan();
|
|
2003
2113
|
|
|
2004
|
-
barrier_opt
|
|
2114
|
+
barrier_opt
|
|
2115
|
+
.prev_frontier
|
|
2116
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
2005
2117
|
barrier_opt.local_frontier.merge(&frontier);
|
|
2006
2118
|
let opt_ready = barrier_opt.check_unblocked_operations_indexed();
|
|
2007
2119
|
|
|
@@ -2016,7 +2128,8 @@ mod tests {
|
|
|
2016
2128
|
|
|
2017
2129
|
for i in 0..50 {
|
|
2018
2130
|
let mut req = VectorClock::new();
|
|
2019
|
-
req.clocks
|
|
2131
|
+
req.clocks
|
|
2132
|
+
.insert("node_a".to_string(), ((i % 10) + 1) as u64);
|
|
2020
2133
|
barrier.receive_operation(format!("op_{}", i), req);
|
|
2021
2134
|
}
|
|
2022
2135
|
|
|
@@ -2047,7 +2160,8 @@ mod tests {
|
|
|
2047
2160
|
// Build up some state
|
|
2048
2161
|
for i in 0..20 {
|
|
2049
2162
|
let mut req = VectorClock::new();
|
|
2050
|
-
req.clocks
|
|
2163
|
+
req.clocks
|
|
2164
|
+
.insert("node_a".to_string(), ((i % 5) + 1) as u64);
|
|
2051
2165
|
barrier1.receive_operation(format!("op_{}", i), req);
|
|
2052
2166
|
}
|
|
2053
2167
|
|
|
@@ -2060,14 +2174,10 @@ mod tests {
|
|
|
2060
2174
|
|
|
2061
2175
|
let mut barrier2 = CausalDependencyBarrier::new();
|
|
2062
2176
|
for pending_op in pending_snapshot {
|
|
2063
|
-
barrier2
|
|
2064
|
-
|
|
2065
|
-
pending_op.clone(),
|
|
2066
|
-
);
|
|
2067
|
-
barrier2.index_operation(
|
|
2068
|
-
&pending_op.operation_id,
|
|
2069
|
-
&pending_op.required_dependencies,
|
|
2070
|
-
);
|
|
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);
|
|
2071
2181
|
}
|
|
2072
2182
|
|
|
2073
2183
|
// Verify index is equivalent
|
|
@@ -2105,11 +2215,15 @@ mod tests {
|
|
|
2105
2215
|
let mut frontier = VectorClock::new();
|
|
2106
2216
|
frontier.clocks.insert("node_a".to_string(), 5);
|
|
2107
2217
|
|
|
2108
|
-
barrier_ref
|
|
2218
|
+
barrier_ref
|
|
2219
|
+
.prev_frontier
|
|
2220
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
2109
2221
|
barrier_ref.local_frontier.merge(&frontier);
|
|
2110
2222
|
let _ref_ready = barrier_ref.check_unblocked_operations_full_scan();
|
|
2111
2223
|
|
|
2112
|
-
barrier_opt
|
|
2224
|
+
barrier_opt
|
|
2225
|
+
.prev_frontier
|
|
2226
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
2113
2227
|
barrier_opt.local_frontier.merge(&frontier);
|
|
2114
2228
|
let _opt_ready = barrier_opt.check_unblocked_operations_indexed();
|
|
2115
2229
|
|
|
@@ -2120,8 +2234,13 @@ mod tests {
|
|
|
2120
2234
|
assert_eq!(ref_pending.len(), opt_pending.len());
|
|
2121
2235
|
|
|
2122
2236
|
for ref_op in ref_pending.iter() {
|
|
2123
|
-
let opt_op = opt_pending
|
|
2124
|
-
.
|
|
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
|
+
));
|
|
2125
2244
|
|
|
2126
2245
|
assert_eq!(
|
|
2127
2246
|
std::mem::discriminant(&ref_op.state),
|
|
@@ -2155,20 +2274,26 @@ mod tests {
|
|
|
2155
2274
|
partial.clocks.insert("node_b".to_string(), 1);
|
|
2156
2275
|
partial.clocks.insert("node_c".to_string(), 1);
|
|
2157
2276
|
|
|
2158
|
-
barrier_ref
|
|
2277
|
+
barrier_ref
|
|
2278
|
+
.prev_frontier
|
|
2279
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
2159
2280
|
barrier_ref.local_frontier.merge(&partial);
|
|
2160
2281
|
barrier_ref.check_unblocked_operations_full_scan();
|
|
2161
2282
|
|
|
2162
|
-
barrier_opt
|
|
2283
|
+
barrier_opt
|
|
2284
|
+
.prev_frontier
|
|
2285
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
2163
2286
|
barrier_opt.local_frontier.merge(&partial);
|
|
2164
2287
|
barrier_opt.check_unblocked_operations_indexed();
|
|
2165
2288
|
|
|
2166
2289
|
// op_all_three should still be waiting
|
|
2167
|
-
let ref_waiting = barrier_ref
|
|
2290
|
+
let ref_waiting = barrier_ref
|
|
2291
|
+
.get_pending_operations()
|
|
2168
2292
|
.iter()
|
|
2169
2293
|
.filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
|
|
2170
2294
|
.count();
|
|
2171
|
-
let opt_waiting = barrier_opt
|
|
2295
|
+
let opt_waiting = barrier_opt
|
|
2296
|
+
.get_pending_operations()
|
|
2172
2297
|
.iter()
|
|
2173
2298
|
.filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
|
|
2174
2299
|
.count();
|
|
@@ -2182,20 +2307,26 @@ mod tests {
|
|
|
2182
2307
|
full.clocks.insert("node_b".to_string(), 3);
|
|
2183
2308
|
full.clocks.insert("node_c".to_string(), 2);
|
|
2184
2309
|
|
|
2185
|
-
barrier_ref
|
|
2310
|
+
barrier_ref
|
|
2311
|
+
.prev_frontier
|
|
2312
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
2186
2313
|
barrier_ref.local_frontier.merge(&full);
|
|
2187
2314
|
barrier_ref.check_unblocked_operations_full_scan();
|
|
2188
2315
|
|
|
2189
|
-
barrier_opt
|
|
2316
|
+
barrier_opt
|
|
2317
|
+
.prev_frontier
|
|
2318
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
2190
2319
|
barrier_opt.local_frontier.merge(&full);
|
|
2191
2320
|
barrier_opt.check_unblocked_operations_indexed();
|
|
2192
2321
|
|
|
2193
2322
|
// Now op_all_three should be ready
|
|
2194
|
-
let ref_ready = barrier_ref
|
|
2323
|
+
let ref_ready = barrier_ref
|
|
2324
|
+
.get_pending_operations()
|
|
2195
2325
|
.iter()
|
|
2196
2326
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2197
2327
|
.count();
|
|
2198
|
-
let opt_ready = barrier_opt
|
|
2328
|
+
let opt_ready = barrier_opt
|
|
2329
|
+
.get_pending_operations()
|
|
2199
2330
|
.iter()
|
|
2200
2331
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2201
2332
|
.count();
|
|
@@ -2226,21 +2357,27 @@ mod tests {
|
|
|
2226
2357
|
frontier.clocks.insert(n.to_string(), ts);
|
|
2227
2358
|
}
|
|
2228
2359
|
|
|
2229
|
-
barrier_ref
|
|
2360
|
+
barrier_ref
|
|
2361
|
+
.prev_frontier
|
|
2362
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
2230
2363
|
barrier_ref.local_frontier.merge(&frontier);
|
|
2231
2364
|
barrier_ref.check_unblocked_operations_full_scan();
|
|
2232
2365
|
|
|
2233
|
-
barrier_opt
|
|
2366
|
+
barrier_opt
|
|
2367
|
+
.prev_frontier
|
|
2368
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
2234
2369
|
barrier_opt.local_frontier.merge(&frontier);
|
|
2235
2370
|
barrier_opt.check_unblocked_operations_indexed();
|
|
2236
2371
|
}
|
|
2237
2372
|
|
|
2238
2373
|
// Both should have released all 5 operations
|
|
2239
|
-
let ref_ready = barrier_ref
|
|
2374
|
+
let ref_ready = barrier_ref
|
|
2375
|
+
.get_pending_operations()
|
|
2240
2376
|
.iter()
|
|
2241
2377
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2242
2378
|
.count();
|
|
2243
|
-
let opt_ready = barrier_opt
|
|
2379
|
+
let opt_ready = barrier_opt
|
|
2380
|
+
.get_pending_operations()
|
|
2244
2381
|
.iter()
|
|
2245
2382
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2246
2383
|
.count();
|
|
@@ -2268,20 +2405,26 @@ mod tests {
|
|
|
2268
2405
|
let mut frontier = VectorClock::new();
|
|
2269
2406
|
frontier.clocks.insert("node_a".to_string(), 25);
|
|
2270
2407
|
|
|
2271
|
-
barrier_ref
|
|
2408
|
+
barrier_ref
|
|
2409
|
+
.prev_frontier
|
|
2410
|
+
.clone_from(&barrier_ref.local_frontier);
|
|
2272
2411
|
barrier_ref.local_frontier.merge(&frontier);
|
|
2273
2412
|
barrier_ref.check_unblocked_operations_full_scan();
|
|
2274
2413
|
|
|
2275
|
-
barrier_opt
|
|
2414
|
+
barrier_opt
|
|
2415
|
+
.prev_frontier
|
|
2416
|
+
.clone_from(&barrier_opt.local_frontier);
|
|
2276
2417
|
barrier_opt.local_frontier.merge(&frontier);
|
|
2277
2418
|
barrier_opt.check_unblocked_operations_indexed();
|
|
2278
2419
|
|
|
2279
2420
|
// Both should release exactly 25 operations
|
|
2280
|
-
let ref_ready = barrier_ref
|
|
2421
|
+
let ref_ready = barrier_ref
|
|
2422
|
+
.get_pending_operations()
|
|
2281
2423
|
.iter()
|
|
2282
2424
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2283
2425
|
.count();
|
|
2284
|
-
let opt_ready = barrier_opt
|
|
2426
|
+
let opt_ready = barrier_opt
|
|
2427
|
+
.get_pending_operations()
|
|
2285
2428
|
.iter()
|
|
2286
2429
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2287
2430
|
.count();
|
|
@@ -2314,11 +2457,13 @@ mod tests {
|
|
|
2314
2457
|
barrier_opt.advance_frontier(&frontier);
|
|
2315
2458
|
|
|
2316
2459
|
// All operations should be ready
|
|
2317
|
-
let ref_ready = barrier_ref
|
|
2460
|
+
let ref_ready = barrier_ref
|
|
2461
|
+
.get_pending_operations()
|
|
2318
2462
|
.iter()
|
|
2319
2463
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2320
2464
|
.count();
|
|
2321
|
-
let opt_ready = barrier_opt
|
|
2465
|
+
let opt_ready = barrier_opt
|
|
2466
|
+
.get_pending_operations()
|
|
2322
2467
|
.iter()
|
|
2323
2468
|
.filter(|p| matches!(p.state, OperationState::Ready))
|
|
2324
2469
|
.count();
|
|
@@ -2342,7 +2487,9 @@ mod tests {
|
|
|
2342
2487
|
for (op_id, pending_op) in &barrier.pending {
|
|
2343
2488
|
let mut found = false;
|
|
2344
2489
|
for (node, ts) in &pending_op.required_dependencies.clocks {
|
|
2345
|
-
if let Some(waiters) =
|
|
2490
|
+
if let Some(waiters) =
|
|
2491
|
+
barrier.dependency_index.waiters.get(&(node.clone(), *ts))
|
|
2492
|
+
{
|
|
2346
2493
|
if waiters.contains(op_id) {
|
|
2347
2494
|
found = true;
|
|
2348
2495
|
break;
|
|
@@ -2378,7 +2525,9 @@ mod tests {
|
|
|
2378
2525
|
assert!(
|
|
2379
2526
|
new_ts >= *prev_ts,
|
|
2380
2527
|
"Frontier regressed for {} from {} to {}",
|
|
2381
|
-
node,
|
|
2528
|
+
node,
|
|
2529
|
+
prev_ts,
|
|
2530
|
+
new_ts
|
|
2382
2531
|
);
|
|
2383
2532
|
}
|
|
2384
2533
|
|
|
@@ -2408,7 +2557,10 @@ mod tests {
|
|
|
2408
2557
|
for pending_op in barrier.get_pending_operations() {
|
|
2409
2558
|
if matches!(pending_op.state, OperationState::Ready) {
|
|
2410
2559
|
seen_ready.insert(pending_op.operation_id.clone());
|
|
2411
|
-
} else if matches!(
|
|
2560
|
+
} else if matches!(
|
|
2561
|
+
pending_op.state,
|
|
2562
|
+
OperationState::WaitingForDependencies { .. }
|
|
2563
|
+
) {
|
|
2412
2564
|
assert!(
|
|
2413
2565
|
!seen_ready.contains(&pending_op.operation_id),
|
|
2414
2566
|
"Operation {} regressed from Ready to Waiting",
|