@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,194 @@
1
+ /// Phase 7.1: Release Economics Testing
2
+ ///
3
+ /// Direct measurement of frontier advancement costs without criterion harness
4
+ /// This provides rapid feedback on release behavior
5
+
6
+ #[cfg(test)]
7
+ mod phase_7_1_release_economics {
8
+ use crate::causal_dependency_barrier::CausalDependencyBarrier;
9
+ use crate::convergence::VectorClock;
10
+ use std::time::Instant;
11
+
12
+ #[test]
13
+ fn phase_7_1_release_single_scan_small() {
14
+ // Test 1: Single release scan with small pending buffer
15
+ println!("\n=== Phase 7.1 Test 1: Single Release Scan (Small) ===");
16
+
17
+ for pending_size in [0, 10, 100].iter() {
18
+ let mut barrier = CausalDependencyBarrier::new();
19
+
20
+ // Fill with blocked operations
21
+ for i in 0..*pending_size {
22
+ let mut required = VectorClock::new();
23
+ required.clocks.insert("node_a".to_string(), (1000 + i) as u64);
24
+ let _ = barrier.receive_operation(format!("pending_{}", i), required);
25
+ }
26
+
27
+ // Measure one frontier advance
28
+ let start = Instant::now();
29
+ let mut frontier = VectorClock::new();
30
+ frontier.clocks.insert("node_a".to_string(), 1);
31
+ barrier.advance_frontier(&frontier);
32
+ let elapsed = start.elapsed();
33
+
34
+ let metrics = barrier.metrics();
35
+ println!(
36
+ "Pending={:5} | Cost={:?} | Entries examined={} | Released={}",
37
+ pending_size, elapsed, metrics.pending_entries_examined, metrics.operations_released
38
+ );
39
+ }
40
+ }
41
+
42
+ #[test]
43
+ fn phase_7_1_release_selectivity_test() {
44
+ // Test 2: Does readiness % affect cost?
45
+ println!("\n=== Phase 7.1 Test 2: Release Selectivity ===");
46
+
47
+ for ready_percent in [0, 10, 50, 100].iter() {
48
+ let mut barrier = CausalDependencyBarrier::new();
49
+ let pending_size = 100;
50
+
51
+ let ready_count = (pending_size * ready_percent) / 100;
52
+ let blocked_count = pending_size - ready_count;
53
+
54
+ // Add ready operations
55
+ for i in 0..ready_count {
56
+ let mut required = VectorClock::new();
57
+ required.clocks.insert("node_a".to_string(), 100);
58
+ let _ = barrier.receive_operation(format!("ready_{}", i), required);
59
+ }
60
+
61
+ // Add blocked operations
62
+ for i in 0..blocked_count {
63
+ let mut required = VectorClock::new();
64
+ required.clocks.insert("node_a".to_string(), 5000);
65
+ let _ = barrier.receive_operation(format!("blocked_{}", i), required);
66
+ }
67
+
68
+ // Measure frontier advance to release ready operations
69
+ let start = Instant::now();
70
+ let mut frontier = VectorClock::new();
71
+ frontier.clocks.insert("node_a".to_string(), 2000);
72
+ barrier.advance_frontier(&frontier);
73
+ let elapsed = start.elapsed();
74
+
75
+ let metrics = barrier.metrics();
76
+ let released_pct = if metrics.pending_entries_examined > 0 {
77
+ (metrics.operations_released as f64 / metrics.pending_entries_examined as f64) * 100.0
78
+ } else {
79
+ 0.0
80
+ };
81
+
82
+ println!(
83
+ "Ready%={:3} | Cost={:?} | Examined={:3} | Released={:3} | Ratio={:.1}%",
84
+ ready_percent, elapsed, metrics.pending_entries_examined, metrics.operations_released, released_pct
85
+ );
86
+ }
87
+ }
88
+
89
+ #[test]
90
+ fn phase_7_1_release_pathological_simplified() {
91
+ // Test 3: Simplified pathological test
92
+ // Creates sequential dependencies but only advances frontier a few times
93
+ println!("\n=== Phase 7.1 Test 3: Pathological (Simplified) ===");
94
+
95
+ let pending_size = 100;
96
+ let mut barrier = CausalDependencyBarrier::new();
97
+
98
+ // Create sequential dependencies
99
+ for i in 0..pending_size {
100
+ let mut required = VectorClock::new();
101
+ required.clocks.insert("node_a".to_string(), (i + 1) as u64);
102
+ let _ = barrier.receive_operation(format!("seq_{}", i), required);
103
+ }
104
+
105
+ println!("Initial pending: {}", barrier.get_pending_operations().len());
106
+
107
+ // Advance frontier multiple times
108
+ let start = Instant::now();
109
+ let mut total_entries_examined = 0;
110
+ let mut total_released = 0;
111
+
112
+ for step in 1..=10 {
113
+ barrier.clear_metrics();
114
+ let mut frontier = VectorClock::new();
115
+ frontier.clocks.insert("node_a".to_string(), step as u64);
116
+ barrier.advance_frontier(&frontier);
117
+
118
+ let metrics = barrier.metrics();
119
+ total_entries_examined += metrics.pending_entries_examined;
120
+ total_released += metrics.operations_released;
121
+
122
+ println!(
123
+ "Step {:2}: frontier A:{} | examined={:3} | released={} | remaining={:3}",
124
+ step,
125
+ step,
126
+ metrics.pending_entries_examined,
127
+ metrics.operations_released,
128
+ barrier.get_pending_operations().len()
129
+ );
130
+ }
131
+
132
+ let elapsed = start.elapsed();
133
+ println!(
134
+ "\nTotal across 10 steps: elapsed={:?} | total_examined={} | total_released={}",
135
+ elapsed, total_entries_examined, total_released
136
+ );
137
+
138
+ // Analysis
139
+ if total_entries_examined > 500 {
140
+ println!("⚠️ WARNING: Examined {} entries for 100 pending (10 advances)", total_entries_examined);
141
+ println!(" Expected if O(m): ~100-200");
142
+ println!(" This suggests possible rescanning behavior");
143
+ } else {
144
+ println!("✅ GOOD: Linear scaling confirmed");
145
+ println!(" Examined {} entries total", total_entries_examined);
146
+ }
147
+ }
148
+
149
+ #[test]
150
+ fn phase_7_1_release_mixed_advances() {
151
+ // Test 4: Realistic mixed advances
152
+ println!("\n=== Phase 7.1 Test 4: Mixed Frontier Advances ===");
153
+
154
+ for pending_size in [50, 100].iter() {
155
+ println!("\nPending size: {}", pending_size);
156
+ let mut barrier = CausalDependencyBarrier::new();
157
+
158
+ // Create mixed dependencies
159
+ for i in 0..*pending_size {
160
+ let mut required = VectorClock::new();
161
+ let dependency_level = ((i * 997) % 1000) as u64 + 1;
162
+ required.clocks.insert("node_a".to_string(), dependency_level);
163
+ let _ = barrier.receive_operation(format!("mixed_{}", i), required);
164
+ }
165
+
166
+ let start = Instant::now();
167
+ let mut total_examined = 0;
168
+ let mut total_released = 0;
169
+
170
+ // Series of frontier advances
171
+ for level in (100..=500).step_by(100) {
172
+ barrier.clear_metrics();
173
+ let mut frontier = VectorClock::new();
174
+ frontier.clocks.insert("node_a".to_string(), level as u64);
175
+ barrier.advance_frontier(&frontier);
176
+
177
+ let metrics = barrier.metrics();
178
+ total_examined += metrics.pending_entries_examined;
179
+ total_released += metrics.operations_released;
180
+
181
+ println!(
182
+ " Advance to A:{} | examined={:2} | released={:2}",
183
+ level, metrics.pending_entries_examined, metrics.operations_released
184
+ );
185
+ }
186
+
187
+ let elapsed = start.elapsed();
188
+ println!(
189
+ "Total: elapsed={:?} | examined={} | released={}",
190
+ elapsed, total_examined, total_released
191
+ );
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,427 @@
1
+ /// Concurrency Fuzzing Framework
2
+ ///
3
+ /// Generates randomized concurrent transactions with variable:
4
+ /// - Operation count per transaction
5
+ /// - Read/write set sizes
6
+ /// - Conflicts between transactions
7
+ /// - Execution interleaving
8
+ ///
9
+ /// Verifies final state matches deterministic reference model.
10
+
11
+ use crate::convergence::VectorClock;
12
+ use crate::state_hash::StateHash;
13
+ use crate::transactions::*;
14
+ use rand::Rng;
15
+ use std::collections::{HashMap, HashSet};
16
+
17
+ /// Fuzzing configuration
18
+ #[derive(Clone, Debug)]
19
+ pub struct FuzzingConfig {
20
+ pub num_transactions: usize,
21
+ pub max_operations_per_transaction: usize,
22
+ pub max_records: usize,
23
+ pub conflict_probability: f32, // 0.0 to 1.0
24
+ pub seed: u64,
25
+ }
26
+
27
+ impl Default for FuzzingConfig {
28
+ fn default() -> Self {
29
+ Self {
30
+ num_transactions: 100,
31
+ max_operations_per_transaction: 5,
32
+ max_records: 20,
33
+ conflict_probability: 0.3,
34
+ seed: 42,
35
+ }
36
+ }
37
+ }
38
+
39
+ /// Generated transaction for fuzzing
40
+ #[derive(Clone, Debug)]
41
+ pub struct FuzzedTransaction {
42
+ pub transaction_id: String,
43
+ pub operations: Vec<Operation>,
44
+ pub write_set: HashSet<String>, // record IDs written
45
+ pub read_set: HashSet<String>, // record IDs read
46
+ }
47
+
48
+ /// Fuzzing statistics
49
+ #[derive(Clone, Debug)]
50
+ pub struct FuzzingStats {
51
+ pub total_transactions: usize,
52
+ pub total_operations: usize,
53
+ pub total_conflicts: usize,
54
+ pub concurrent_transactions: usize,
55
+ pub sequential_transactions: usize,
56
+ }
57
+
58
+ /// Concurrency fuzzer
59
+ pub struct ConcurrencyFuzzer {
60
+ config: FuzzingConfig,
61
+ rng: rand::rngs::StdRng,
62
+ }
63
+
64
+ impl ConcurrencyFuzzer {
65
+ pub fn new(config: FuzzingConfig) -> Self {
66
+ use rand::SeedableRng;
67
+ let rng = rand::rngs::StdRng::seed_from_u64(config.seed);
68
+
69
+ Self { config, rng }
70
+ }
71
+
72
+ /// Generate random transaction
73
+ fn generate_transaction(&mut self, id: usize) -> FuzzedTransaction {
74
+ let num_ops = self.rng.gen_range(1..=self.config.max_operations_per_transaction);
75
+ let mut write_set = HashSet::new();
76
+ let mut read_set = HashSet::new();
77
+ let mut operations = Vec::new();
78
+
79
+ let mut vc = VectorClock::new();
80
+ vc.increment("fuzzer");
81
+
82
+ let base_version = StateVersion::new(vc.clone(), format!("genesis_{}", id));
83
+
84
+ for op_idx in 0..num_ops {
85
+ let record_id = format!(
86
+ "rec_{}",
87
+ self.rng.gen_range(0..self.config.max_records)
88
+ );
89
+ let value = self.rng.gen_range(0..1000);
90
+
91
+ let op_id = OperationId::new("fuzzer".to_string(), ((id * 100) + op_idx) as u64);
92
+
93
+ let mut op = Operation::new(
94
+ op_id,
95
+ base_version.clone(),
96
+ format!("FuzzOp_{}", op_idx),
97
+ OperationCommand {
98
+ op_type: "UpdateField".to_string(),
99
+ collection: "fuzzing".to_string(),
100
+ record_id: record_id.clone(),
101
+ fields: {
102
+ let mut m = HashMap::new();
103
+ m.insert("value".to_string(), serde_json::json!(value));
104
+ m
105
+ },
106
+ },
107
+ "fuzzer".to_string(),
108
+ );
109
+
110
+ // Randomly add read dependencies
111
+ if self.rng.gen_bool(0.3) {
112
+ let read_record = format!(
113
+ "rec_{}",
114
+ self.rng.gen_range(0..self.config.max_records)
115
+ );
116
+ op = op.with_read_set(vec![StateDependency::new(
117
+ "fuzzing".to_string(),
118
+ read_record.clone(),
119
+ Some("value".to_string()),
120
+ base_version.clone(),
121
+ )]);
122
+ read_set.insert(read_record);
123
+ }
124
+
125
+ op = op.with_write_set(vec![StateDependency::new(
126
+ "fuzzing".to_string(),
127
+ record_id.clone(),
128
+ Some("value".to_string()),
129
+ base_version.clone(),
130
+ )]);
131
+
132
+ write_set.insert(record_id);
133
+ operations.push(op);
134
+ }
135
+
136
+ FuzzedTransaction {
137
+ transaction_id: format!("fuzz_txn_{}", id),
138
+ operations,
139
+ write_set,
140
+ read_set,
141
+ }
142
+ }
143
+
144
+ /// Generate fuzzed transaction workload
145
+ pub fn generate_workload(&mut self) -> Vec<FuzzedTransaction> {
146
+ (0..self.config.num_transactions)
147
+ .map(|i| self.generate_transaction(i))
148
+ .collect()
149
+ }
150
+
151
+ /// Detect potential conflicts between two transactions
152
+ pub fn detect_conflict(tx1: &FuzzedTransaction, tx2: &FuzzedTransaction) -> bool {
153
+ // Write-Write conflict
154
+ if !tx1.write_set.is_disjoint(&tx2.write_set) {
155
+ return true;
156
+ }
157
+
158
+ // Read-Write conflict
159
+ if !tx1.read_set.is_disjoint(&tx2.write_set) {
160
+ return true;
161
+ }
162
+ if !tx1.write_set.is_disjoint(&tx2.read_set) {
163
+ return true;
164
+ }
165
+
166
+ false
167
+ }
168
+
169
+ /// Compute conflict graph for workload
170
+ pub fn compute_conflict_graph(workload: &[FuzzedTransaction]) -> HashMap<usize, Vec<usize>> {
171
+ let mut graph = HashMap::new();
172
+
173
+ for (i, tx1) in workload.iter().enumerate() {
174
+ let mut conflicts = Vec::new();
175
+ for (j, tx2) in workload.iter().enumerate() {
176
+ if i != j && Self::detect_conflict(tx1, tx2) {
177
+ conflicts.push(j);
178
+ }
179
+ }
180
+ graph.insert(i, conflicts);
181
+ }
182
+
183
+ graph
184
+ }
185
+
186
+ /// Compute statistics
187
+ pub fn compute_stats(workload: &[FuzzedTransaction]) -> FuzzingStats {
188
+ let total_transactions = workload.len();
189
+ let total_operations = workload.iter().map(|t| t.operations.len()).sum();
190
+ let conflict_graph = Self::compute_conflict_graph(workload);
191
+ let total_conflicts: usize = conflict_graph.iter().map(|(_, v)| v.len()).sum::<usize>() / 2; // Each edge counted twice
192
+
193
+ let concurrent_transactions = workload
194
+ .iter()
195
+ .filter(|t| t.operations.len() > 1)
196
+ .count();
197
+ let sequential_transactions = total_transactions - concurrent_transactions;
198
+
199
+ FuzzingStats {
200
+ total_transactions,
201
+ total_operations,
202
+ total_conflicts,
203
+ concurrent_transactions,
204
+ sequential_transactions,
205
+ }
206
+ }
207
+ }
208
+
209
+ #[cfg(test)]
210
+ mod tests {
211
+ use super::*;
212
+
213
+ #[test]
214
+ fn test_fuzzer_creation() {
215
+ let config = FuzzingConfig::default();
216
+ let fuzzer = ConcurrencyFuzzer::new(config.clone());
217
+
218
+ assert_eq!(fuzzer.config.num_transactions, 100);
219
+ }
220
+
221
+ #[test]
222
+ fn test_generate_single_transaction() {
223
+ let config = FuzzingConfig {
224
+ num_transactions: 1,
225
+ max_operations_per_transaction: 3,
226
+ max_records: 10,
227
+ conflict_probability: 0.5,
228
+ seed: 123,
229
+ };
230
+
231
+ let mut fuzzer = ConcurrencyFuzzer::new(config);
232
+ let tx = fuzzer.generate_transaction(0);
233
+
234
+ assert!(!tx.operations.is_empty());
235
+ assert!(tx.operations.len() <= 3);
236
+ }
237
+
238
+ #[test]
239
+ fn test_generate_workload() {
240
+ let config = FuzzingConfig {
241
+ num_transactions: 10,
242
+ max_operations_per_transaction: 2,
243
+ max_records: 5,
244
+ conflict_probability: 0.3,
245
+ seed: 456,
246
+ };
247
+
248
+ let mut fuzzer = ConcurrencyFuzzer::new(config);
249
+ let workload = fuzzer.generate_workload();
250
+
251
+ assert_eq!(workload.len(), 10);
252
+ for tx in &workload {
253
+ assert!(!tx.operations.is_empty());
254
+ }
255
+ }
256
+
257
+ #[test]
258
+ fn test_conflict_detection_no_conflict() {
259
+ let tx1 = FuzzedTransaction {
260
+ transaction_id: "tx1".to_string(),
261
+ operations: Vec::new(),
262
+ write_set: ["rec1".to_string()].iter().cloned().collect(),
263
+ read_set: HashSet::new(),
264
+ };
265
+
266
+ let tx2 = FuzzedTransaction {
267
+ transaction_id: "tx2".to_string(),
268
+ operations: Vec::new(),
269
+ write_set: ["rec2".to_string()].iter().cloned().collect(),
270
+ read_set: HashSet::new(),
271
+ };
272
+
273
+ assert!(!ConcurrencyFuzzer::detect_conflict(&tx1, &tx2));
274
+ }
275
+
276
+ #[test]
277
+ fn test_conflict_detection_write_write() {
278
+ let tx1 = FuzzedTransaction {
279
+ transaction_id: "tx1".to_string(),
280
+ operations: Vec::new(),
281
+ write_set: ["rec1".to_string()].iter().cloned().collect(),
282
+ read_set: HashSet::new(),
283
+ };
284
+
285
+ let tx2 = FuzzedTransaction {
286
+ transaction_id: "tx2".to_string(),
287
+ operations: Vec::new(),
288
+ write_set: ["rec1".to_string()].iter().cloned().collect(),
289
+ read_set: HashSet::new(),
290
+ };
291
+
292
+ assert!(ConcurrencyFuzzer::detect_conflict(&tx1, &tx2));
293
+ }
294
+
295
+ #[test]
296
+ fn test_conflict_detection_read_write() {
297
+ let tx1 = FuzzedTransaction {
298
+ transaction_id: "tx1".to_string(),
299
+ operations: Vec::new(),
300
+ write_set: HashSet::new(),
301
+ read_set: ["rec1".to_string()].iter().cloned().collect(),
302
+ };
303
+
304
+ let tx2 = FuzzedTransaction {
305
+ transaction_id: "tx2".to_string(),
306
+ operations: Vec::new(),
307
+ write_set: ["rec1".to_string()].iter().cloned().collect(),
308
+ read_set: HashSet::new(),
309
+ };
310
+
311
+ assert!(ConcurrencyFuzzer::detect_conflict(&tx1, &tx2));
312
+ }
313
+
314
+ #[test]
315
+ fn test_compute_conflict_graph() {
316
+ let tx1 = FuzzedTransaction {
317
+ transaction_id: "tx1".to_string(),
318
+ operations: Vec::new(),
319
+ write_set: ["rec1".to_string()].iter().cloned().collect(),
320
+ read_set: HashSet::new(),
321
+ };
322
+
323
+ let tx2 = FuzzedTransaction {
324
+ transaction_id: "tx2".to_string(),
325
+ operations: Vec::new(),
326
+ write_set: ["rec1".to_string()].iter().cloned().collect(),
327
+ read_set: HashSet::new(),
328
+ };
329
+
330
+ let tx3 = FuzzedTransaction {
331
+ transaction_id: "tx3".to_string(),
332
+ operations: Vec::new(),
333
+ write_set: ["rec2".to_string()].iter().cloned().collect(),
334
+ read_set: HashSet::new(),
335
+ };
336
+
337
+ let workload = vec![tx1, tx2, tx3];
338
+ let graph = ConcurrencyFuzzer::compute_conflict_graph(&workload);
339
+
340
+ assert_eq!(graph.len(), 3);
341
+ assert_eq!(graph[&0].len(), 1); // tx1 conflicts with tx2
342
+ assert_eq!(graph[&1].len(), 1); // tx2 conflicts with tx1
343
+ assert_eq!(graph[&2].len(), 0); // tx3 has no conflicts
344
+ }
345
+
346
+ #[test]
347
+ fn test_compute_stats() {
348
+ let config = FuzzingConfig {
349
+ num_transactions: 10,
350
+ max_operations_per_transaction: 2,
351
+ max_records: 5,
352
+ conflict_probability: 0.3,
353
+ seed: 789,
354
+ };
355
+
356
+ let mut fuzzer = ConcurrencyFuzzer::new(config);
357
+ let workload = fuzzer.generate_workload();
358
+ let stats = ConcurrencyFuzzer::compute_stats(&workload);
359
+
360
+ assert_eq!(stats.total_transactions, 10);
361
+ assert!(stats.total_operations >= 10);
362
+ assert!(stats.total_operations <= 20);
363
+ }
364
+
365
+ #[test]
366
+ fn test_workload_determinism_same_seed() {
367
+ let config1 = FuzzingConfig {
368
+ num_transactions: 5,
369
+ max_operations_per_transaction: 3,
370
+ max_records: 10,
371
+ conflict_probability: 0.3,
372
+ seed: 999,
373
+ };
374
+
375
+ let config2 = FuzzingConfig {
376
+ num_transactions: 5,
377
+ max_operations_per_transaction: 3,
378
+ max_records: 10,
379
+ conflict_probability: 0.3,
380
+ seed: 999,
381
+ };
382
+
383
+ let mut fuzzer1 = ConcurrencyFuzzer::new(config1);
384
+ let mut fuzzer2 = ConcurrencyFuzzer::new(config2);
385
+
386
+ let workload1 = fuzzer1.generate_workload();
387
+ let workload2 = fuzzer2.generate_workload();
388
+
389
+ assert_eq!(workload1.len(), workload2.len());
390
+ for (tx1, tx2) in workload1.iter().zip(workload2.iter()) {
391
+ assert_eq!(tx1.operations.len(), tx2.operations.len());
392
+ }
393
+ }
394
+
395
+ #[test]
396
+ fn test_workload_variation_different_seed() {
397
+ let config1 = FuzzingConfig {
398
+ num_transactions: 10,
399
+ max_operations_per_transaction: 3,
400
+ max_records: 10,
401
+ conflict_probability: 0.3,
402
+ seed: 111,
403
+ };
404
+
405
+ let config2 = FuzzingConfig {
406
+ num_transactions: 10,
407
+ max_operations_per_transaction: 3,
408
+ max_records: 10,
409
+ conflict_probability: 0.3,
410
+ seed: 222,
411
+ };
412
+
413
+ let mut fuzzer1 = ConcurrencyFuzzer::new(config1);
414
+ let mut fuzzer2 = ConcurrencyFuzzer::new(config2);
415
+
416
+ let workload1 = fuzzer1.generate_workload();
417
+ let workload2 = fuzzer2.generate_workload();
418
+
419
+ let diff_count = workload1
420
+ .iter()
421
+ .zip(workload2.iter())
422
+ .filter(|(tx1, tx2)| tx1.write_set != tx2.write_set)
423
+ .count();
424
+
425
+ assert!(diff_count > 0, "Different seeds should produce different workloads");
426
+ }
427
+ }