@feltdb/core 0.4.12 → 0.4.14

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 (165) hide show
  1. package/dist/cli/commands.js +42 -25
  2. package/dist/cli/index.js +1 -1
  3. package/dist/create/cli.js +39 -4
  4. package/dist/create/create.js +30 -18
  5. package/dist/create/docker-compose-generator.js +68 -11
  6. package/dist/create/package-versions.js +1 -1
  7. package/dist/create/server-source/Cargo.lock +2238 -0
  8. package/dist/create/server-source/Cargo.toml +7 -0
  9. package/dist/create/server-source/crates/feltdb/Cargo.lock +175 -0
  10. package/dist/create/server-source/crates/feltdb/Cargo.toml +79 -0
  11. package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
  12. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
  13. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
  14. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
  15. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
  16. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
  17. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
  18. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
  19. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
  20. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
  21. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
  22. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
  23. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
  24. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
  25. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
  26. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
  27. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
  28. package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
  29. package/dist/create/server-source/crates/feltdb/src/acquisition.rs +286 -0
  30. package/dist/create/server-source/crates/feltdb/src/admission.rs +192 -0
  31. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
  32. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
  33. package/dist/create/server-source/crates/feltdb/src/analytics.rs +475 -0
  34. package/dist/create/server-source/crates/feltdb/src/application.rs +2244 -0
  35. package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
  36. package/dist/create/server-source/crates/feltdb/src/authorization.rs +1030 -0
  37. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
  38. package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
  39. package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
  40. package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
  41. package/dist/create/server-source/crates/feltdb/src/capability.rs +903 -0
  42. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
  43. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
  44. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
  45. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
  46. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
  47. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
  48. package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +465 -0
  49. package/dist/create/server-source/crates/feltdb/src/convergence.rs +618 -0
  50. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
  51. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +380 -0
  52. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
  53. package/dist/create/server-source/crates/feltdb/src/cron.rs +294 -0
  54. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
  55. package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
  56. package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
  57. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
  58. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
  59. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
  60. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +316 -0
  61. package/dist/create/server-source/crates/feltdb/src/execution.rs +426 -0
  62. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
  63. package/dist/create/server-source/crates/feltdb/src/indexing.rs +779 -0
  64. package/dist/create/server-source/crates/feltdb/src/lib.rs +2838 -0
  65. package/dist/create/server-source/crates/feltdb/src/materialization.rs +184 -0
  66. package/dist/create/server-source/crates/feltdb/src/metrics.rs +267 -0
  67. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
  68. package/dist/create/server-source/crates/feltdb/src/observability.rs +366 -0
  69. package/dist/create/server-source/crates/feltdb/src/operation.rs +251 -0
  70. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
  71. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +344 -0
  72. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
  73. package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +166 -0
  74. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
  75. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
  76. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
  77. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
  78. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
  79. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
  80. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
  81. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
  82. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
  83. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
  84. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
  85. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
  86. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
  87. package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
  88. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
  89. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
  90. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
  91. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
  92. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
  93. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
  94. package/dist/create/server-source/crates/feltdb/src/production_api.rs +400 -0
  95. package/dist/create/server-source/crates/feltdb/src/provenance.rs +207 -0
  96. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +217 -0
  97. package/dist/create/server-source/crates/feltdb/src/references.rs +404 -0
  98. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
  99. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +160 -0
  100. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
  101. package/dist/create/server-source/crates/feltdb/src/routing.rs +409 -0
  102. package/dist/create/server-source/crates/feltdb/src/sharding.rs +523 -0
  103. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +3118 -0
  104. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +291 -0
  105. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
  106. package/dist/create/server-source/crates/feltdb/src/storage.rs +267 -0
  107. package/dist/create/server-source/crates/feltdb/src/submission.rs +477 -0
  108. package/dist/create/server-source/crates/feltdb/src/sync.rs +483 -0
  109. package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +822 -0
  110. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
  111. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +473 -0
  112. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
  113. package/dist/create/server-source/crates/feltdb/src/transactions.rs +646 -0
  114. package/dist/create/server-source/crates/feltdb/src/trigger.rs +390 -0
  115. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
  116. package/dist/create/server-source/crates/feltdb/src/workflow.rs +713 -0
  117. package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
  118. package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
  119. package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
  120. package/dist/create/server-source/crates/feltdb/src/workload.rs +1310 -0
  121. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +23 -0
  122. package/dist/create/server-source/crates/feltdb-server/README.md +79 -0
  123. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +73 -0
  124. package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
  125. package/dist/create/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
  126. package/dist/create/server-source/crates/feltdb-server/src/audit.rs +59 -0
  127. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +308 -0
  128. package/dist/create/server-source/crates/feltdb-server/src/authorization.rs +228 -0
  129. package/dist/create/server-source/crates/feltdb-server/src/backup.rs +416 -0
  130. package/dist/create/server-source/crates/feltdb-server/src/causal.rs +218 -0
  131. package/dist/create/server-source/crates/feltdb-server/src/certification.rs +223 -0
  132. package/dist/create/server-source/crates/feltdb-server/src/clock.rs +132 -0
  133. package/dist/create/server-source/crates/feltdb-server/src/cluster.rs +165 -0
  134. package/dist/create/server-source/crates/feltdb-server/src/connections.rs +1044 -0
  135. package/dist/create/server-source/crates/feltdb-server/src/content.rs +111 -0
  136. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +250 -0
  137. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +78 -0
  138. package/dist/create/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
  139. package/dist/create/server-source/crates/feltdb-server/src/leases.rs +123 -0
  140. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +25 -0
  141. package/dist/create/server-source/crates/feltdb-server/src/main.rs +8715 -0
  142. package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +97 -0
  143. package/dist/create/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
  144. package/dist/create/server-source/crates/feltdb-server/src/principals.rs +510 -0
  145. package/dist/create/server-source/crates/feltdb-server/src/providers.rs +269 -0
  146. package/dist/create/server-source/crates/feltdb-server/src/releases.rs +2563 -0
  147. package/dist/create/server-source/crates/feltdb-server/src/sessions.rs +156 -0
  148. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
  149. package/dist/create/server-source/crates/feltdb-server/src/versions.rs +264 -0
  150. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
  151. package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
  152. package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
  153. package/dist/flowspec.d.ts +2 -0
  154. package/dist/flowspec.d.ts.map +1 -1
  155. package/dist/flowspec.js +28 -4
  156. package/dist/state-contract.d.ts +7 -1
  157. package/dist/state-contract.d.ts.map +1 -1
  158. package/dist/studio/components/ApplicationDesigner.d.ts.map +1 -1
  159. package/dist/studio/components/ConflictExplorer.d.ts.map +1 -1
  160. package/dist/studio/components/index.js +1 -1
  161. package/dist/studio/{components-Q0Nx6gLE.js → components-9kDSWiGL.js} +163 -47
  162. package/dist/studio/index.js +31 -28
  163. package/dist/studio-app/assets/{index-BHOmLZF-.js → index-DVdvmf69.js} +9 -9
  164. package/dist/studio-app/index.html +1 -1
  165. package/package.json +1 -1
@@ -0,0 +1,383 @@
1
+ /// Phase 7.1.2: Optimization Benchmark & Regression Gate
2
+ ///
3
+ /// Proves three things independently:
4
+ /// 1. The optimized release path is materially faster
5
+ /// 2. The speedup comes from reducing candidate examination (causal evidence)
6
+ /// 3. The optimization doesn't create a new bottleneck elsewhere
7
+ ///
8
+ /// Key principle: Don't declare victory from a microbenchmark.
9
+ /// Systematically measure: latency, examination counts, memory, concurrency.
10
+
11
+ use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
12
+ use feltdb::causal_dependency_barrier::CausalDependencyBarrier;
13
+ use feltdb::convergence::VectorClock;
14
+
15
+ /// Benchmark workload configuration
16
+ #[derive(Clone, Debug)]
17
+ struct BenchmarkWorkload {
18
+ pending_size: usize, // 10, 100, 1K, 10K, 100K
19
+ frontier_advances: usize, // 1, 10
20
+ release_selectivity: f64, // Fraction of operations that will be released
21
+ }
22
+
23
+ /// Metrics collected during a benchmark run
24
+ #[derive(Clone, Default, Debug)]
25
+ struct ReleaseMetrics {
26
+ // Reference implementation
27
+ ref_pending_examined: u64,
28
+ ref_operations_released: u64,
29
+ ref_readiness_checks: u64,
30
+ ref_latency_ns: u64,
31
+
32
+ // Optimized implementation
33
+ opt_candidates_discovered: u64,
34
+ opt_candidates_examined: u64,
35
+ opt_operations_released: u64,
36
+ opt_readiness_checks: u64,
37
+ opt_latency_ns: u64,
38
+
39
+ // Index maintenance
40
+ opt_index_entries: u64,
41
+ }
42
+
43
+ impl ReleaseMetrics {
44
+ /// Calculate candidate reduction percentage
45
+ fn candidate_reduction_pct(&self) -> f64 {
46
+ if self.ref_pending_examined == 0 {
47
+ 0.0
48
+ } else {
49
+ (1.0 - (self.opt_candidates_examined as f64 / self.ref_pending_examined as f64))
50
+ * 100.0
51
+ }
52
+ }
53
+
54
+ /// Calculate speedup ratio
55
+ fn speedup_ratio(&self) -> f64 {
56
+ if self.opt_latency_ns == 0 {
57
+ 0.0
58
+ } else {
59
+ self.ref_latency_ns as f64 / self.opt_latency_ns as f64
60
+ }
61
+ }
62
+ }
63
+
64
+ /// Generate a benchmark workload with reproducible randomness
65
+ fn generate_workload(workload: &BenchmarkWorkload, seed: u64) -> (Vec<(String, VectorClock)>, Vec<VectorClock>) {
66
+ use std::collections::hash_map::DefaultHasher;
67
+ use std::hash::{Hash, Hasher};
68
+
69
+ let mut hasher = DefaultHasher::new();
70
+ seed.hash(&mut hasher);
71
+ let mut rng_seed = hasher.finish();
72
+
73
+ fn next_rng(seed: &mut u64) -> u64 {
74
+ *seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
75
+ *seed
76
+ }
77
+
78
+ // Generate pending operations
79
+ let mut operations = Vec::new();
80
+ let node_count = 5; // Fixed for consistency
81
+
82
+ for i in 0..workload.pending_size {
83
+ let mut required = VectorClock::new();
84
+ // Create dependency that will be satisfied based on selectivity
85
+ let release_threshold = (workload.pending_size as f64 * workload.release_selectivity) as usize;
86
+ let ts = if i < release_threshold {
87
+ 10 // Will be released when frontier advances to 10
88
+ } else {
89
+ 100 // Will remain pending
90
+ };
91
+ required.clocks.insert(format!("node_0"), ts);
92
+ operations.push((format!("op_{}", i), required));
93
+ }
94
+
95
+ // Generate frontier advances
96
+ let mut advances = Vec::new();
97
+ for _ in 0..workload.frontier_advances {
98
+ let mut frontier = VectorClock::new();
99
+ frontier.clocks.insert("node_0".to_string(), 10);
100
+ advances.push(frontier);
101
+ }
102
+
103
+ (operations, advances)
104
+ }
105
+
106
+ /// Run reference implementation benchmark
107
+ fn benchmark_reference_release(
108
+ workload: &BenchmarkWorkload,
109
+ operations: &[(String, VectorClock)],
110
+ advances: &[VectorClock],
111
+ ) -> ReleaseMetrics {
112
+ let mut barrier = CausalDependencyBarrier::new();
113
+ let mut metrics = ReleaseMetrics::default();
114
+
115
+ // Load operations
116
+ for (op_id, required) in operations {
117
+ barrier.receive_operation(op_id.clone(), required.clone());
118
+ }
119
+
120
+ // Execute advances and measure
121
+ for advance in advances {
122
+ barrier.clear_metrics();
123
+ {
124
+ let current = barrier.get_frontier().clone();
125
+ barrier.get_prev_frontier_mut().clone_from(&current);
126
+ }
127
+ barrier.get_frontier_mut().merge(advance);
128
+
129
+ let start = std::time::Instant::now();
130
+ let _released = barrier.check_unblocked_operations_full_scan();
131
+ metrics.ref_latency_ns += start.elapsed().as_nanos() as u64;
132
+
133
+ metrics.ref_pending_examined += barrier.get_metrics().pending_entries_examined;
134
+ metrics.ref_operations_released += barrier.get_metrics().operations_released;
135
+ metrics.ref_readiness_checks += barrier.get_metrics().frontier_checks;
136
+ }
137
+
138
+ metrics
139
+ }
140
+
141
+ /// Run optimized implementation benchmark
142
+ fn benchmark_optimized_release(
143
+ workload: &BenchmarkWorkload,
144
+ operations: &[(String, VectorClock)],
145
+ advances: &[VectorClock],
146
+ ) -> ReleaseMetrics {
147
+ let mut barrier = CausalDependencyBarrier::new();
148
+ let mut metrics = ReleaseMetrics::default();
149
+
150
+ // Load operations
151
+ for (op_id, required) in operations {
152
+ barrier.receive_operation(op_id.clone(), required.clone());
153
+ }
154
+
155
+ // Execute advances and measure
156
+ for advance in advances {
157
+ barrier.clear_metrics();
158
+ {
159
+ let current = barrier.get_frontier().clone();
160
+ barrier.get_prev_frontier_mut().clone_from(&current);
161
+ }
162
+ barrier.get_frontier_mut().merge(advance);
163
+
164
+ let start = std::time::Instant::now();
165
+ let _released = barrier.check_unblocked_operations_indexed();
166
+ metrics.opt_latency_ns += start.elapsed().as_nanos() as u64;
167
+
168
+ let barrier_metrics = barrier.get_metrics();
169
+ metrics.opt_candidates_examined += barrier_metrics.pending_entries_examined;
170
+ metrics.opt_operations_released += barrier_metrics.operations_released;
171
+ metrics.opt_readiness_checks += barrier_metrics.frontier_checks;
172
+
173
+ // Track index size
174
+ metrics.opt_index_entries = barrier.get_dependency_index().waiters.len() as u64;
175
+ }
176
+
177
+ metrics
178
+ }
179
+
180
+ /// Phase 7.1.2 Stage 1: Single-threaded Optimization Benchmark
181
+ fn benchmark_optimization_matrix(c: &mut Criterion) {
182
+ let mut group = c.benchmark_group("phase_7_1_2_optimization");
183
+ group.sample_size(10); // Fewer samples for larger workloads
184
+
185
+ // Primary matrix: pending × advances × selectivity
186
+ let pending_sizes = vec![10, 100, 1_000, 10_000];
187
+ let advance_counts = vec![1, 10];
188
+ let selectivities = vec![0.10]; // Start with 10% release
189
+
190
+ println!("\n=== Phase 7.1.2: Optimization Matrix Metrics ===");
191
+ println!("{:<8} {:<10} {:<10} {:<15} {:<15} {:<12} {:<12}",
192
+ "Pending", "Advances", "Selectivity", "Ref (µs)", "Opt (µs)", "Speedup", "Candidate %");
193
+
194
+ for pending in &pending_sizes {
195
+ for advances in &advance_counts {
196
+ for selectivity in &selectivities {
197
+ let workload = BenchmarkWorkload {
198
+ pending_size: *pending,
199
+ frontier_advances: *advances,
200
+ release_selectivity: *selectivity,
201
+ };
202
+
203
+ let (ops, advs) = generate_workload(&workload, 42);
204
+ let ref_metrics = benchmark_reference_release(&workload, &ops, &advs);
205
+ let opt_metrics = benchmark_optimized_release(&workload, &ops, &advs);
206
+
207
+ let ref_us = ref_metrics.ref_latency_ns as f64 / 1000.0;
208
+ let opt_us = opt_metrics.opt_latency_ns as f64 / 1000.0;
209
+ let speedup = ref_metrics.ref_latency_ns as f64 / opt_metrics.opt_latency_ns as f64;
210
+ let candidate_reduction = opt_metrics.candidate_reduction_pct();
211
+
212
+ println!("{:<8} {:<10} {:<10.0}% {:<15.1} {:<15.1} {:<12.2}x {:<12.1}%",
213
+ pending, advances, selectivity * 100.0, ref_us, opt_us, speedup, candidate_reduction);
214
+
215
+ let bench_name = format!("pending_{}_advances_{}_sel_{:.0}pct",
216
+ pending, advances, selectivity * 100.0);
217
+
218
+ group.bench_with_input(
219
+ BenchmarkId::new("reference", &bench_name),
220
+ &workload,
221
+ |b, w| {
222
+ let (ops, advs) = generate_workload(w, 42);
223
+ b.iter(|| {
224
+ benchmark_reference_release(black_box(w), black_box(&ops), black_box(&advs))
225
+ });
226
+ },
227
+ );
228
+
229
+ group.bench_with_input(
230
+ BenchmarkId::new("optimized", &bench_name),
231
+ &workload,
232
+ |b, w| {
233
+ let (ops, advs) = generate_workload(w, 42);
234
+ b.iter(|| {
235
+ benchmark_optimized_release(black_box(w), black_box(&ops), black_box(&advs))
236
+ });
237
+ },
238
+ );
239
+ }
240
+ }
241
+ }
242
+
243
+ group.finish();
244
+ }
245
+
246
+ /// Phase 7.1.2 Stage 2: Release Selectivity Variation
247
+ fn benchmark_selectivity_variation(c: &mut Criterion) {
248
+ let mut group = c.benchmark_group("phase_7_1_2_selectivity");
249
+ group.sample_size(10);
250
+
251
+ // Fixed pending size, vary selectivity
252
+ let pending_size = 1_000;
253
+ let selectivities = vec![0.0, 0.01, 0.10, 0.25, 0.50, 0.75, 1.0];
254
+
255
+ println!("\n=== Phase 7.1.2: Selectivity Variation Metrics ===");
256
+ println!("{:<12} {:<15} {:<15} {:<12} {:<12}",
257
+ "Selectivity", "Ref (µs)", "Opt (µs)", "Speedup", "Candidate %");
258
+
259
+ for selectivity in selectivities {
260
+ let workload = BenchmarkWorkload {
261
+ pending_size,
262
+ frontier_advances: 1,
263
+ release_selectivity: selectivity,
264
+ };
265
+
266
+ let (ops, advs) = generate_workload(&workload, 42);
267
+ let ref_metrics = benchmark_reference_release(&workload, &ops, &advs);
268
+ let opt_metrics = benchmark_optimized_release(&workload, &ops, &advs);
269
+
270
+ let ref_us = ref_metrics.ref_latency_ns as f64 / 1000.0;
271
+ let opt_us = opt_metrics.opt_latency_ns as f64 / 1000.0;
272
+ let speedup = if opt_metrics.opt_latency_ns > 0 {
273
+ ref_metrics.ref_latency_ns as f64 / opt_metrics.opt_latency_ns as f64
274
+ } else {
275
+ 0.0
276
+ };
277
+ let candidate_reduction = opt_metrics.candidate_reduction_pct();
278
+
279
+ println!("{:<12.0}% {:<15.1} {:<15.1} {:<12.2}x {:<12.1}%",
280
+ selectivity * 100.0, ref_us, opt_us, speedup, candidate_reduction);
281
+
282
+ let bench_name = format!("pending_1000_sel_{:.0}pct", selectivity * 100.0);
283
+
284
+ group.bench_with_input(
285
+ BenchmarkId::new("reference", &bench_name),
286
+ &workload,
287
+ |b, w| {
288
+ let (ops, advs) = generate_workload(w, 42);
289
+ b.iter(|| {
290
+ benchmark_reference_release(black_box(w), black_box(&ops), black_box(&advs))
291
+ });
292
+ },
293
+ );
294
+
295
+ group.bench_with_input(
296
+ BenchmarkId::new("optimized", &bench_name),
297
+ &workload,
298
+ |b, w| {
299
+ let (ops, advs) = generate_workload(w, 42);
300
+ b.iter(|| {
301
+ benchmark_optimized_release(black_box(w), black_box(&ops), black_box(&advs))
302
+ });
303
+ },
304
+ );
305
+ }
306
+
307
+ group.finish();
308
+ }
309
+
310
+ /// Phase 7.1.2 Stage 3: Pathological Scaling Case
311
+ /// Previous evidence: 100 pending × 10 advances = 1000 examinations vs 100-200 candidates
312
+ /// Now scale it to see the curve
313
+ fn benchmark_pathological_scaling(c: &mut Criterion) {
314
+ let mut group = c.benchmark_group("phase_7_1_2_pathological_scaling");
315
+ group.sample_size(10); // Criterion requires minimum 10 samples
316
+
317
+ let pending_sizes = vec![100, 1_000, 10_000];
318
+ let advance_count = 10;
319
+ let selectivity = 0.10; // 10% of pending operations become ready each advance
320
+
321
+ println!("\n=== Phase 7.1.2: Pathological Scaling (10 advances) ===");
322
+ println!("{:<10} {:<15} {:<15} {:<12} {:<12}",
323
+ "Pending", "Ref (µs)", "Opt (µs)", "Speedup", "Candidate %");
324
+
325
+ for pending in &pending_sizes {
326
+ let workload = BenchmarkWorkload {
327
+ pending_size: *pending,
328
+ frontier_advances: advance_count,
329
+ release_selectivity: selectivity,
330
+ };
331
+
332
+ let (ops, advs) = generate_workload(&workload, 42);
333
+ let ref_metrics = benchmark_reference_release(&workload, &ops, &advs);
334
+ let opt_metrics = benchmark_optimized_release(&workload, &ops, &advs);
335
+
336
+ let ref_us = ref_metrics.ref_latency_ns as f64 / 1000.0;
337
+ let opt_us = opt_metrics.opt_latency_ns as f64 / 1000.0;
338
+ let speedup = if opt_metrics.opt_latency_ns > 0 {
339
+ ref_metrics.ref_latency_ns as f64 / opt_metrics.opt_latency_ns as f64
340
+ } else {
341
+ 0.0
342
+ };
343
+ let candidate_reduction = opt_metrics.candidate_reduction_pct();
344
+
345
+ println!("{:<10} {:<15.1} {:<15.1} {:<12.2}x {:<12.1}%",
346
+ pending, ref_us, opt_us, speedup, candidate_reduction);
347
+
348
+ let bench_name = format!("pending_{}_advances_{}", pending, advance_count);
349
+
350
+ group.bench_with_input(
351
+ BenchmarkId::new("reference", &bench_name),
352
+ &workload,
353
+ |b, w| {
354
+ let (ops, advs) = generate_workload(w, 42);
355
+ b.iter(|| {
356
+ benchmark_reference_release(black_box(w), black_box(&ops), black_box(&advs))
357
+ });
358
+ },
359
+ );
360
+
361
+ group.bench_with_input(
362
+ BenchmarkId::new("optimized", &bench_name),
363
+ &workload,
364
+ |b, w| {
365
+ let (ops, advs) = generate_workload(w, 42);
366
+ b.iter(|| {
367
+ benchmark_optimized_release(black_box(w), black_box(&ops), black_box(&advs))
368
+ });
369
+ },
370
+ );
371
+ }
372
+
373
+ group.finish();
374
+ }
375
+
376
+ criterion_group!(
377
+ benches,
378
+ benchmark_optimization_matrix,
379
+ benchmark_selectivity_variation,
380
+ benchmark_pathological_scaling,
381
+ );
382
+
383
+ criterion_main!(benches);
@@ -0,0 +1,298 @@
1
+ /// Phase 7.1.3: Crossover Analysis & Dispatch Economics
2
+ ///
3
+ /// Determines where full_scan vs indexed dispatch should switch.
4
+ ///
5
+ /// Measures across the full workload matrix:
6
+ /// - Pending size: 32, 64, 128, 256, 512, 1K, 10K
7
+ /// - Release selectivity: 0%, 1%, 10%, 25%, 50%, 75%, 100%
8
+ /// - Frontier advances: 1, 10, 100
9
+ ///
10
+ /// Output: CSV table suitable for crossover curve analysis
11
+
12
+ use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
13
+ use feltdb::causal_dependency_barrier::CausalDependencyBarrier;
14
+ use feltdb::convergence::VectorClock;
15
+ use std::fs::File;
16
+ use std::io::Write;
17
+
18
+ /// Full workload configuration
19
+ #[derive(Clone, Debug)]
20
+ struct CrossoverWorkload {
21
+ pending_size: usize,
22
+ frontier_advances: usize,
23
+ release_selectivity: f64,
24
+ }
25
+
26
+ /// Complete measurement record
27
+ #[derive(Clone, Debug)]
28
+ struct CrossoverMetrics {
29
+ // Scenario
30
+ pending: usize,
31
+ advances: usize,
32
+ selectivity: f64,
33
+
34
+ // Full scan path
35
+ fs_latency_ns: u64,
36
+ fs_entries_examined: u64,
37
+ fs_ops_released: u64,
38
+
39
+ // Indexed path
40
+ ix_latency_ns: u64,
41
+ ix_candidates_discovered: u64,
42
+ ix_candidates_examined: u64,
43
+ ix_ops_released: u64,
44
+ ix_index_entries: u64,
45
+
46
+ // Derived
47
+ speedup: f64,
48
+ winner: char, // 'F' for full_scan, 'I' for indexed
49
+ }
50
+
51
+ impl CrossoverMetrics {
52
+ fn to_csv(&self) -> String {
53
+ format!(
54
+ "{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
55
+ self.pending,
56
+ self.advances,
57
+ self.selectivity * 100.0,
58
+ self.fs_latency_ns,
59
+ self.fs_entries_examined,
60
+ self.fs_ops_released,
61
+ self.ix_latency_ns,
62
+ self.ix_candidates_discovered,
63
+ self.ix_candidates_examined,
64
+ self.ix_ops_released,
65
+ self.ix_index_entries,
66
+ self.speedup,
67
+ self.winner,
68
+ if self.winner == 'I' { "INDEXED" } else { "FULL_SCAN" }
69
+ )
70
+ }
71
+ }
72
+
73
+ /// Generate reproducible workload
74
+ fn generate_workload(
75
+ workload: &CrossoverWorkload,
76
+ seed: u64,
77
+ ) -> (Vec<(String, VectorClock)>, Vec<VectorClock>) {
78
+ use std::collections::hash_map::DefaultHasher;
79
+ use std::hash::{Hash, Hasher};
80
+
81
+ let mut hasher = DefaultHasher::new();
82
+ seed.hash(&mut hasher);
83
+ let _rng_seed = hasher.finish();
84
+
85
+ // Generate pending operations
86
+ let mut operations = Vec::new();
87
+ for i in 0..workload.pending_size {
88
+ let mut required = VectorClock::new();
89
+ let release_threshold = (workload.pending_size as f64 * workload.release_selectivity) as usize;
90
+ let ts = if i < release_threshold {
91
+ 10 // Will be released when frontier advances to 10
92
+ } else {
93
+ 100 // Will remain pending
94
+ };
95
+ required.clocks.insert(format!("node_0"), ts);
96
+ operations.push((format!("op_{}", i), required));
97
+ }
98
+
99
+ // Generate frontier advances
100
+ let mut advances = Vec::new();
101
+ for _ in 0..workload.frontier_advances {
102
+ let mut frontier = VectorClock::new();
103
+ frontier.clocks.insert("node_0".to_string(), 10);
104
+ advances.push(frontier);
105
+ }
106
+
107
+ (operations, advances)
108
+ }
109
+
110
+ /// Measure full_scan path
111
+ fn measure_full_scan(
112
+ workload: &CrossoverWorkload,
113
+ operations: &[(String, VectorClock)],
114
+ advances: &[VectorClock],
115
+ ) -> (u64, u64, u64) {
116
+ let mut barrier = CausalDependencyBarrier::new();
117
+ let mut latency_ns = 0u64;
118
+ let mut entries_examined = 0u64;
119
+ let mut ops_released = 0u64;
120
+
121
+ // Load operations
122
+ for (op_id, required) in operations {
123
+ barrier.receive_operation(op_id.clone(), required.clone());
124
+ }
125
+
126
+ // Execute advances and measure
127
+ for advance in advances {
128
+ barrier.clear_metrics();
129
+ {
130
+ let current = barrier.get_frontier().clone();
131
+ barrier.get_prev_frontier_mut().clone_from(&current);
132
+ }
133
+ barrier.get_frontier_mut().merge(advance);
134
+
135
+ let start = std::time::Instant::now();
136
+ let _released = barrier.check_unblocked_operations_full_scan();
137
+ latency_ns += start.elapsed().as_nanos() as u64;
138
+
139
+ let metrics = barrier.get_metrics();
140
+ entries_examined += metrics.pending_entries_examined;
141
+ ops_released += metrics.operations_released;
142
+ }
143
+
144
+ (latency_ns, entries_examined, ops_released)
145
+ }
146
+
147
+ /// Measure indexed path
148
+ fn measure_indexed(
149
+ workload: &CrossoverWorkload,
150
+ operations: &[(String, VectorClock)],
151
+ advances: &[VectorClock],
152
+ ) -> (u64, u64, u64, u64, u64) {
153
+ let mut barrier = CausalDependencyBarrier::new();
154
+ let mut latency_ns = 0u64;
155
+ let mut candidates_discovered = 0u64;
156
+ let mut candidates_examined = 0u64;
157
+ let mut ops_released = 0u64;
158
+
159
+ // Load operations
160
+ for (op_id, required) in operations {
161
+ barrier.receive_operation(op_id.clone(), required.clone());
162
+ }
163
+
164
+ // Execute advances and measure
165
+ for advance in advances {
166
+ barrier.clear_metrics();
167
+ {
168
+ let current = barrier.get_frontier().clone();
169
+ barrier.get_prev_frontier_mut().clone_from(&current);
170
+ }
171
+ barrier.get_frontier_mut().merge(advance);
172
+
173
+ // Track candidates before
174
+ let index_before = barrier.get_dependency_index().waiters.len() as u64;
175
+
176
+ let start = std::time::Instant::now();
177
+ let _released = barrier.check_unblocked_operations_indexed();
178
+ latency_ns += start.elapsed().as_nanos() as u64;
179
+
180
+ let metrics = barrier.get_metrics();
181
+ candidates_examined += metrics.pending_entries_examined;
182
+ ops_released += metrics.operations_released;
183
+
184
+ // Track index entries
185
+ candidates_discovered = barrier.get_dependency_index().waiters.len() as u64;
186
+ }
187
+
188
+ (latency_ns, candidates_discovered, candidates_examined, ops_released, candidates_discovered)
189
+ }
190
+
191
+ /// Phase 7.1.3: Crossover Matrix Benchmark
192
+ fn benchmark_crossover_matrix(c: &mut Criterion) {
193
+ let mut group = c.benchmark_group("phase_7_1_3_crossover");
194
+ group.sample_size(10);
195
+
196
+ // Expanded matrix for crossover analysis
197
+ let pending_sizes = vec![32, 64, 128, 256, 512, 1_000, 10_000];
198
+ let selectivities = vec![0.0, 0.01, 0.10, 0.25, 0.50, 0.75, 1.0];
199
+ let advance_counts = vec![1, 10];
200
+
201
+ let mut results = Vec::new();
202
+
203
+ println!("\n=== Phase 7.1.3: Crossover Analysis ===");
204
+ println!("{:>7} {:>3} {:>5} {:>10} {:>12} {:>12} {:>7}",
205
+ "Pending", "Adv", "Sel%", "FS (µs)", "IX (µs)", "Speedup", "Winner");
206
+
207
+ for pending in &pending_sizes {
208
+ for advances in &advance_counts {
209
+ for selectivity in &selectivities {
210
+ let workload = CrossoverWorkload {
211
+ pending_size: *pending,
212
+ frontier_advances: *advances,
213
+ release_selectivity: *selectivity,
214
+ };
215
+
216
+ let (ops, advs) = generate_workload(&workload, 42);
217
+
218
+ // Measure both paths
219
+ let (fs_latency, fs_examined, fs_released) = measure_full_scan(&workload, &ops, &advs);
220
+ let (ix_latency, ix_discovered, ix_examined, ix_released, ix_entries) =
221
+ measure_indexed(&workload, &ops, &advs);
222
+
223
+ let speedup = if ix_latency > 0 {
224
+ fs_latency as f64 / ix_latency as f64
225
+ } else {
226
+ 0.0
227
+ };
228
+
229
+ let winner = if ix_latency < fs_latency { 'I' } else { 'F' };
230
+
231
+ let fs_us = fs_latency as f64 / 1000.0;
232
+ let ix_us = ix_latency as f64 / 1000.0;
233
+
234
+ println!("{:>7} {:>3} {:>5.0} {:>10.1} {:>12.1} {:>12.2}x {}",
235
+ pending, advances, selectivity * 100.0, fs_us, ix_us, speedup,
236
+ if winner == 'I' { "INDEXED" } else { "FULL_SCAN" });
237
+
238
+ let metrics = CrossoverMetrics {
239
+ pending: *pending,
240
+ advances: *advances,
241
+ selectivity: *selectivity,
242
+ fs_latency_ns: fs_latency,
243
+ fs_entries_examined: fs_examined,
244
+ fs_ops_released: fs_released,
245
+ ix_latency_ns: ix_latency,
246
+ ix_candidates_discovered: ix_discovered,
247
+ ix_candidates_examined: ix_examined,
248
+ ix_ops_released: ix_released,
249
+ ix_index_entries: ix_entries,
250
+ speedup,
251
+ winner,
252
+ };
253
+
254
+ results.push(metrics);
255
+
256
+ // Criterion benchmarks for regression gates
257
+ let bench_name = format!("pending_{}_adv_{}_sel_{:.0}pct",
258
+ pending, advances, selectivity * 100.0);
259
+
260
+ group.bench_with_input(
261
+ BenchmarkId::new("full_scan", &bench_name),
262
+ &workload,
263
+ |b, w| {
264
+ let (ops, advs) = generate_workload(w, 42);
265
+ b.iter(|| measure_full_scan(black_box(w), black_box(&ops), black_box(&advs)));
266
+ },
267
+ );
268
+
269
+ group.bench_with_input(
270
+ BenchmarkId::new("indexed", &bench_name),
271
+ &workload,
272
+ |b, w| {
273
+ let (ops, advs) = generate_workload(w, 42);
274
+ b.iter(|| measure_indexed(black_box(w), black_box(&ops), black_box(&advs)));
275
+ },
276
+ );
277
+ }
278
+ }
279
+ }
280
+
281
+ group.finish();
282
+
283
+ // Write results to CSV for analysis
284
+ if let Ok(mut file) = File::create("target/phase_7_1_3_crossover.csv") {
285
+ let _ = writeln!(file,
286
+ "pending,advances,selectivity_pct,fs_latency_ns,fs_entries_examined,fs_ops_released,\
287
+ ix_latency_ns,ix_candidates_discovered,ix_candidates_examined,ix_ops_released,\
288
+ ix_index_entries,speedup,winner,winner_name");
289
+ for result in &results {
290
+ let _ = writeln!(file, "{}", result.to_csv());
291
+ }
292
+ }
293
+
294
+ println!("\nCrossover analysis written to target/phase_7_1_3_crossover.csv");
295
+ }
296
+
297
+ criterion_group!(benches, benchmark_crossover_matrix);
298
+ criterion_main!(benches);