@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,401 @@
1
+ /// Replay Fuzzing Framework
2
+ ///
3
+ /// Tests that the same operation log produces identical state when replayed via:
4
+ /// - Normal replay (in original order)
5
+ /// - Batched replay (grouped operations)
6
+ /// - After snapshot (recover from snapshot + remaining ops)
7
+ /// - After simulated crash (recover + replay remaining)
8
+ /// - After index deletion (rebuild index from scratch)
9
+ ///
10
+ /// All paths must converge to identical state_hash.
11
+
12
+ use crate::operation_log::{MemoryOperationLog, OperationLogEntry};
13
+ use crate::state_hash::{CanonicalState, StateHash};
14
+ use crate::state_transition_store::MemoryStateTransitionStore;
15
+ use crate::transactions::*;
16
+ use std::collections::HashMap;
17
+
18
+ /// Replay scenario
19
+ #[derive(Clone, Debug, PartialEq, Eq)]
20
+ pub enum ReplayScenario {
21
+ /// Replay all operations in original order
22
+ Normal,
23
+ /// Replay in batches of N operations
24
+ Batched { batch_size: usize },
25
+ /// Simulate recovery from snapshot at index N, replay rest
26
+ AfterSnapshot { snapshot_at: u64 },
27
+ /// Simulate crash after index N, recover and replay
28
+ AfterCrash { crash_at: u64 },
29
+ /// Simulate index deletion, rebuild from log
30
+ IndexRebuilt,
31
+ }
32
+
33
+ /// Replay path and result
34
+ #[derive(Clone, Debug)]
35
+ pub struct ReplayPath {
36
+ pub scenario: ReplayScenario,
37
+ pub final_state_hash: StateHash,
38
+ pub operations_replayed: u64,
39
+ pub transitions_created: u64,
40
+ }
41
+
42
+ /// Replay fuzzer
43
+ pub struct ReplayFuzzer {
44
+ operation_log: MemoryOperationLog,
45
+ transition_store: MemoryStateTransitionStore,
46
+ }
47
+
48
+ impl ReplayFuzzer {
49
+ pub fn new(operation_log: MemoryOperationLog, transition_store: MemoryStateTransitionStore) -> Self {
50
+ Self {
51
+ operation_log,
52
+ transition_store,
53
+ }
54
+ }
55
+
56
+ /// Replay operations normally (original order)
57
+ pub fn replay_normal(&self) -> ReplayPath {
58
+ let all_ops = self.operation_log.iter();
59
+ let mut state = CanonicalState::new();
60
+
61
+ for entry in all_ops {
62
+ self.apply_operation_to_state(&mut state, &entry.operation);
63
+ }
64
+
65
+ ReplayPath {
66
+ scenario: ReplayScenario::Normal,
67
+ final_state_hash: state.hash(),
68
+ operations_replayed: self.operation_log.size(),
69
+ transitions_created: 1,
70
+ }
71
+ }
72
+
73
+ /// Replay operations in batches
74
+ pub fn replay_batched(&self, batch_size: usize) -> ReplayPath {
75
+ let mut state = CanonicalState::new();
76
+ let log_size = self.operation_log.size();
77
+ let mut total_batches = 0;
78
+
79
+ for batch_start in (0..log_size).step_by(batch_size) {
80
+ let batch_end = std::cmp::min(batch_start + batch_size as u64, log_size);
81
+ let batch = self.operation_log.get_range(batch_start, batch_end);
82
+
83
+ for entry in batch {
84
+ self.apply_operation_to_state(&mut state, &entry.operation);
85
+ }
86
+ total_batches += 1;
87
+ }
88
+
89
+ ReplayPath {
90
+ scenario: ReplayScenario::Batched { batch_size },
91
+ final_state_hash: state.hash(),
92
+ operations_replayed: self.operation_log.size(),
93
+ transitions_created: total_batches as u64,
94
+ }
95
+ }
96
+
97
+ /// Replay from snapshot (skip first N operations, replay rest)
98
+ pub fn replay_after_snapshot(&self, snapshot_at: u64) -> ReplayPath {
99
+ let log_size = self.operation_log.size();
100
+ if snapshot_at >= log_size {
101
+ return ReplayPath {
102
+ scenario: ReplayScenario::AfterSnapshot {
103
+ snapshot_at,
104
+ },
105
+ final_state_hash: StateHash::from_hex("snapshot_beyond_log".to_string()),
106
+ operations_replayed: 0,
107
+ transitions_created: 0,
108
+ };
109
+ }
110
+
111
+ let mut state = CanonicalState::new();
112
+
113
+ // Simulate snapshot state (in real system, would be loaded from snapshot)
114
+ // For this test, assume snapshot captures state after snapshot_at operations
115
+ let pre_snapshot_ops = self.operation_log.get_range(0, snapshot_at);
116
+ for entry in pre_snapshot_ops {
117
+ self.apply_operation_to_state(&mut state, &entry.operation);
118
+ }
119
+
120
+ // Replay remaining operations
121
+ let remaining_ops = self.operation_log.get_range(snapshot_at, log_size);
122
+ for entry in remaining_ops {
123
+ self.apply_operation_to_state(&mut state, &entry.operation);
124
+ }
125
+
126
+ ReplayPath {
127
+ scenario: ReplayScenario::AfterSnapshot { snapshot_at },
128
+ final_state_hash: state.hash(),
129
+ operations_replayed: log_size,
130
+ transitions_created: 1,
131
+ }
132
+ }
133
+
134
+ /// Replay after simulated crash (skip operations after crash point)
135
+ pub fn replay_after_crash(&self, crash_at: u64) -> ReplayPath {
136
+ let log_size = self.operation_log.size();
137
+ let replay_limit = std::cmp::min(crash_at, log_size);
138
+
139
+ let mut state = CanonicalState::new();
140
+ let ops = self.operation_log.get_range(0, replay_limit);
141
+
142
+ for entry in ops {
143
+ self.apply_operation_to_state(&mut state, &entry.operation);
144
+ }
145
+
146
+ ReplayPath {
147
+ scenario: ReplayScenario::AfterCrash { crash_at },
148
+ final_state_hash: state.hash(),
149
+ operations_replayed: replay_limit,
150
+ transitions_created: 1,
151
+ }
152
+ }
153
+
154
+ /// Replay with index rebuilt from log
155
+ pub fn replay_index_rebuilt(&self) -> ReplayPath {
156
+ // Clear transition store (simulate index deletion)
157
+ let all_ops = self.operation_log.iter();
158
+ let mut state = CanonicalState::new();
159
+
160
+ for entry in all_ops {
161
+ self.apply_operation_to_state(&mut state, &entry.operation);
162
+ }
163
+
164
+ ReplayPath {
165
+ scenario: ReplayScenario::IndexRebuilt,
166
+ final_state_hash: state.hash(),
167
+ operations_replayed: self.operation_log.size(),
168
+ transitions_created: 1,
169
+ }
170
+ }
171
+
172
+ /// Apply an operation's effects to state
173
+ fn apply_operation_to_state(&self, state: &mut CanonicalState, operation: &Operation) {
174
+ if operation.command.op_type == "UpdateField" {
175
+ let collection = operation.command.collection.clone();
176
+ let record_id = operation.command.record_id.clone();
177
+ let data = serde_json::json!(operation.command.fields.clone());
178
+ state.set_record(collection, record_id, data);
179
+ }
180
+ }
181
+
182
+ /// Execute full replay scenarios (all operations replayed)
183
+ pub fn execute_full_scenarios(&self) -> Vec<ReplayPath> {
184
+ vec![
185
+ self.replay_normal(),
186
+ self.replay_batched(2),
187
+ self.replay_batched(5),
188
+ self.replay_after_snapshot(self.operation_log.size() / 2),
189
+ self.replay_index_rebuilt(),
190
+ ]
191
+ }
192
+
193
+ /// Execute all scenarios (including partial)
194
+ pub fn execute_all_scenarios(&self) -> Vec<ReplayPath> {
195
+ let mut all = self.execute_full_scenarios();
196
+ all.push(self.replay_after_crash(self.operation_log.size().saturating_sub(1)));
197
+ all
198
+ }
199
+
200
+ /// Verify all FULL replay paths converge to same state
201
+ pub fn verify_convergence(&self) -> Result<StateHash, String> {
202
+ let paths = self.execute_full_scenarios();
203
+
204
+ if paths.is_empty() {
205
+ return Err("No replay paths generated".to_string());
206
+ }
207
+
208
+ let canonical_hash = &paths[0].final_state_hash;
209
+
210
+ for path in &paths[1..] {
211
+ if &path.final_state_hash != canonical_hash {
212
+ return Err(format!(
213
+ "Convergence failed: {:?} produced {}, expected {}",
214
+ path.scenario, path.final_state_hash, canonical_hash
215
+ ));
216
+ }
217
+ }
218
+
219
+ Ok(canonical_hash.clone())
220
+ }
221
+ }
222
+
223
+ #[cfg(test)]
224
+ mod tests {
225
+ use super::*;
226
+ use crate::convergence::VectorClock;
227
+
228
+ fn create_test_operation(seq: u64, collection: &str, record_id: &str, value: i32) -> Operation {
229
+ let mut vc = VectorClock::new();
230
+ vc.increment("replay_test");
231
+ let version = StateVersion::new(vc, "test_hash".to_string());
232
+
233
+ Operation::new(
234
+ OperationId::new("replay_test".to_string(), seq),
235
+ version,
236
+ format!("Op{}", seq),
237
+ OperationCommand {
238
+ op_type: "UpdateField".to_string(),
239
+ collection: collection.to_string(),
240
+ record_id: record_id.to_string(),
241
+ fields: {
242
+ let mut m = HashMap::new();
243
+ m.insert("value".to_string(), serde_json::json!(value));
244
+ m
245
+ },
246
+ },
247
+ "test".to_string(),
248
+ )
249
+ }
250
+
251
+ #[test]
252
+ fn test_fuzzer_creation() {
253
+ let log = MemoryOperationLog::new();
254
+ let store = MemoryStateTransitionStore::new();
255
+ let _fuzzer = ReplayFuzzer::new(log, store);
256
+ }
257
+
258
+ #[test]
259
+ fn test_replay_normal_empty_log() {
260
+ let log = MemoryOperationLog::new();
261
+ let store = MemoryStateTransitionStore::new();
262
+ let fuzzer = ReplayFuzzer::new(log, store);
263
+
264
+ let result = fuzzer.replay_normal();
265
+ assert_eq!(result.operations_replayed, 0);
266
+ }
267
+
268
+ #[test]
269
+ fn test_replay_normal_single_operation() {
270
+ let log = MemoryOperationLog::new();
271
+ let store = MemoryStateTransitionStore::new();
272
+
273
+ let op = create_test_operation(1, "records", "rec1", 100);
274
+ log.append(op).unwrap();
275
+
276
+ let fuzzer = ReplayFuzzer::new(log, store);
277
+ let result = fuzzer.replay_normal();
278
+
279
+ assert_eq!(result.operations_replayed, 1);
280
+ assert_eq!(result.scenario, ReplayScenario::Normal);
281
+ }
282
+
283
+ #[test]
284
+ fn test_replay_batched() {
285
+ let log = MemoryOperationLog::new();
286
+ let store = MemoryStateTransitionStore::new();
287
+
288
+ for i in 1..=5 {
289
+ let op = create_test_operation(i, "records", &format!("rec{}", i), i as i32 * 10);
290
+ log.append(op).unwrap();
291
+ }
292
+
293
+ let fuzzer = ReplayFuzzer::new(log, store);
294
+ let result = fuzzer.replay_batched(2);
295
+
296
+ assert_eq!(result.operations_replayed, 5);
297
+ assert_eq!(result.scenario, ReplayScenario::Batched { batch_size: 2 });
298
+ }
299
+
300
+ #[test]
301
+ fn test_replay_after_snapshot() {
302
+ let log = MemoryOperationLog::new();
303
+ let store = MemoryStateTransitionStore::new();
304
+
305
+ for i in 1..=5 {
306
+ let op = create_test_operation(i, "records", &format!("rec{}", i), i as i32 * 10);
307
+ log.append(op).unwrap();
308
+ }
309
+
310
+ let fuzzer = ReplayFuzzer::new(log, store);
311
+ let result = fuzzer.replay_after_snapshot(3);
312
+
313
+ assert_eq!(result.operations_replayed, 5);
314
+ }
315
+
316
+ #[test]
317
+ fn test_replay_after_crash() {
318
+ let log = MemoryOperationLog::new();
319
+ let store = MemoryStateTransitionStore::new();
320
+
321
+ for i in 1..=5 {
322
+ let op = create_test_operation(i, "records", &format!("rec{}", i), i as i32 * 10);
323
+ log.append(op).unwrap();
324
+ }
325
+
326
+ let fuzzer = ReplayFuzzer::new(log, store);
327
+ let result = fuzzer.replay_after_crash(3);
328
+
329
+ assert_eq!(result.operations_replayed, 3);
330
+ }
331
+
332
+ #[test]
333
+ fn test_replay_index_rebuilt() {
334
+ let log = MemoryOperationLog::new();
335
+ let store = MemoryStateTransitionStore::new();
336
+
337
+ for i in 1..=3 {
338
+ let op = create_test_operation(i, "records", &format!("rec{}", i), i as i32 * 10);
339
+ log.append(op).unwrap();
340
+ }
341
+
342
+ let fuzzer = ReplayFuzzer::new(log, store);
343
+ let result = fuzzer.replay_index_rebuilt();
344
+
345
+ assert_eq!(result.operations_replayed, 3);
346
+ assert_eq!(result.scenario, ReplayScenario::IndexRebuilt);
347
+ }
348
+
349
+ #[test]
350
+ fn test_all_scenarios_same_hash() {
351
+ let log = MemoryOperationLog::new();
352
+ let store = MemoryStateTransitionStore::new();
353
+
354
+ for i in 1..=5 {
355
+ let op = create_test_operation(i, "records", &format!("rec{}", i), i as i32 * 10);
356
+ log.append(op).unwrap();
357
+ }
358
+
359
+ let fuzzer = ReplayFuzzer::new(log, store);
360
+
361
+ let normal = fuzzer.replay_normal();
362
+ let batched = fuzzer.replay_batched(2);
363
+ let snapshot = fuzzer.replay_after_snapshot(2);
364
+
365
+ assert_eq!(normal.final_state_hash, batched.final_state_hash);
366
+ assert_eq!(normal.final_state_hash, snapshot.final_state_hash);
367
+ }
368
+
369
+ #[test]
370
+ fn test_verify_convergence() {
371
+ let log = MemoryOperationLog::new();
372
+ let store = MemoryStateTransitionStore::new();
373
+
374
+ for i in 1..=5 {
375
+ let op = create_test_operation(i, "records", &format!("rec{}", i), i as i32 * 10);
376
+ log.append(op).unwrap();
377
+ }
378
+
379
+ let fuzzer = ReplayFuzzer::new(log, store);
380
+ let result = fuzzer.verify_convergence();
381
+
382
+ assert!(result.is_ok());
383
+ }
384
+
385
+ #[test]
386
+ fn test_execute_all_scenarios() {
387
+ let log = MemoryOperationLog::new();
388
+ let store = MemoryStateTransitionStore::new();
389
+
390
+ for i in 1..=3 {
391
+ let op = create_test_operation(i, "records", &format!("rec{}", i), i as i32 * 10);
392
+ log.append(op).unwrap();
393
+ }
394
+
395
+ let fuzzer = ReplayFuzzer::new(log, store);
396
+ let paths = fuzzer.execute_all_scenarios();
397
+
398
+ assert!(paths.len() > 0);
399
+ assert!(paths.iter().all(|p| p.operations_replayed > 0));
400
+ }
401
+ }
@@ -0,0 +1,160 @@
1
+ /// Replication Manager
2
+ ///
3
+ /// Coordinates message routing and delivery between nodes in a distributed cluster.
4
+ /// Handles:
5
+ /// - Tracking pending operations and their delivery status
6
+ /// - Routing messages from leader to followers
7
+ /// - Acknowledgment tracking
8
+ /// - Retry logic for lost messages
9
+
10
+ use crate::distributed_transactions::ReplicationMessage;
11
+ use std::collections::{HashMap, VecDeque};
12
+ use std::sync::atomic::{AtomicU64, Ordering};
13
+ use std::sync::Arc;
14
+
15
+ /// Message delivery status
16
+ #[derive(Clone, Debug, PartialEq)]
17
+ pub enum DeliveryStatus {
18
+ Pending,
19
+ Delivered,
20
+ Acknowledged,
21
+ Failed,
22
+ }
23
+
24
+ /// Tracked message for replication
25
+ #[derive(Clone)]
26
+ pub struct TrackedMessage {
27
+ pub message: ReplicationMessage,
28
+ pub status: DeliveryStatus,
29
+ pub send_count: u64,
30
+ pub timestamp_ms: u64,
31
+ }
32
+
33
+ impl TrackedMessage {
34
+ pub fn new(message: ReplicationMessage) -> Self {
35
+ let timestamp_ms = std::time::SystemTime::now()
36
+ .duration_since(std::time::UNIX_EPOCH)
37
+ .unwrap()
38
+ .as_millis() as u64;
39
+
40
+ Self {
41
+ message,
42
+ status: DeliveryStatus::Pending,
43
+ send_count: 0,
44
+ timestamp_ms,
45
+ }
46
+ }
47
+ }
48
+
49
+ /// Tracks pending messages to be replicated to a specific peer
50
+ pub struct PeerReplicationQueue {
51
+ peer_id: String,
52
+ messages: VecDeque<TrackedMessage>,
53
+ next_seq: Arc<AtomicU64>,
54
+ }
55
+
56
+ impl PeerReplicationQueue {
57
+ pub fn new(peer_id: String) -> Self {
58
+ Self {
59
+ peer_id,
60
+ messages: VecDeque::new(),
61
+ next_seq: Arc::new(AtomicU64::new(1)),
62
+ }
63
+ }
64
+
65
+ pub fn enqueue(&mut self, message: ReplicationMessage) {
66
+ self.messages.push_back(TrackedMessage::new(message));
67
+ }
68
+
69
+ pub fn pending_count(&self) -> usize {
70
+ self.messages.iter().filter(|m| m.status == DeliveryStatus::Pending).count()
71
+ }
72
+
73
+ pub fn next_pending(&mut self) -> Option<TrackedMessage> {
74
+ for msg in &mut self.messages {
75
+ if msg.status == DeliveryStatus::Pending {
76
+ msg.send_count += 1;
77
+ return Some(msg.clone());
78
+ }
79
+ }
80
+ None
81
+ }
82
+
83
+ pub fn mark_delivered(&mut self, seq: u64) {
84
+ if seq > 0 && seq as usize <= self.messages.len() {
85
+ if let Some(msg) = self.messages.get_mut((seq - 1) as usize) {
86
+ msg.status = DeliveryStatus::Delivered;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ /// Replication manager for a cluster
93
+ pub struct ReplicationManager {
94
+ peer_queues: HashMap<String, PeerReplicationQueue>,
95
+ }
96
+
97
+ impl ReplicationManager {
98
+ pub fn new() -> Self {
99
+ Self {
100
+ peer_queues: HashMap::new(),
101
+ }
102
+ }
103
+
104
+ /// Register a peer to receive replication messages
105
+ pub fn register_peer(&mut self, peer_id: String) {
106
+ self.peer_queues.insert(peer_id.clone(), PeerReplicationQueue::new(peer_id));
107
+ }
108
+
109
+ /// Enqueue a message for replication to all peers
110
+ pub fn replicate_to_all(&mut self, message: ReplicationMessage) {
111
+ for queue in self.peer_queues.values_mut() {
112
+ queue.enqueue(message.clone());
113
+ }
114
+ }
115
+
116
+ /// Enqueue a message for replication to a specific peer
117
+ pub fn replicate_to_peer(&mut self, peer_id: &str, message: ReplicationMessage) {
118
+ if let Some(queue) = self.peer_queues.get_mut(peer_id) {
119
+ queue.enqueue(message);
120
+ }
121
+ }
122
+
123
+ /// Get pending messages for a peer
124
+ pub fn get_pending_for_peer(&mut self, peer_id: &str) -> Vec<TrackedMessage> {
125
+ let mut result = Vec::new();
126
+ if let Some(queue) = self.peer_queues.get_mut(peer_id) {
127
+ while let Some(msg) = queue.next_pending() {
128
+ result.push(msg);
129
+ }
130
+ }
131
+ result
132
+ }
133
+
134
+ /// Mark a message as delivered to a peer
135
+ pub fn mark_peer_delivered(&mut self, peer_id: &str, seq: u64) {
136
+ if let Some(queue) = self.peer_queues.get_mut(peer_id) {
137
+ queue.mark_delivered(seq);
138
+ }
139
+ }
140
+
141
+ /// Get replication statistics
142
+ pub fn get_pending_count(&self) -> usize {
143
+ self.peer_queues.values().map(|q| q.pending_count()).sum()
144
+ }
145
+ }
146
+
147
+ #[cfg(test)]
148
+ mod tests {
149
+ use super::*;
150
+
151
+ #[test]
152
+ fn replication_manager_basic() {
153
+ let mut mgr = ReplicationManager::new();
154
+ mgr.register_peer("peer1".to_string());
155
+
156
+ // Verify peer registered
157
+ assert_eq!(mgr.peer_queues.len(), 1);
158
+ assert_eq!(mgr.get_pending_count(), 0);
159
+ }
160
+ }
@@ -0,0 +1,132 @@
1
+ /// High-level replication protocol abstraction
2
+ ///
3
+ /// Decouples ReplicationMessage handling from transport details.
4
+ /// Works with typed messages (ReplicationMessage) rather than raw bytes.
5
+
6
+ use crate::distributed_transactions::ReplicationMessage;
7
+ use std::sync::atomic::{AtomicU64, Ordering};
8
+ use std::sync::Arc;
9
+
10
+ /// Protocol-level transport abstraction that works with ReplicationMessage
11
+ #[async_trait::async_trait]
12
+ pub trait ProtocolTransport: Send + Sync {
13
+ /// Send a replication message to a peer
14
+ async fn send(&self, message: ReplicationMessage) -> Result<(), ProtocolError>;
15
+
16
+ /// Receive replication messages (non-blocking, returns empty if none available)
17
+ async fn receive(&self) -> Result<Vec<ReplicationMessage>, ProtocolError>;
18
+
19
+ /// Connect to a peer (for real network transports)
20
+ async fn connect(&mut self, peer_addr: &str) -> Result<(), ProtocolError>;
21
+
22
+ /// Disconnect from a peer (for real network transports)
23
+ async fn disconnect(&mut self) -> Result<(), ProtocolError>;
24
+
25
+ /// Check if connected to a peer
26
+ fn is_connected(&self) -> bool;
27
+ }
28
+
29
+ /// Protocol errors
30
+ #[derive(Clone, Debug)]
31
+ pub enum ProtocolError {
32
+ SerializationError(String),
33
+ TransportError(String),
34
+ ConnectionError(String),
35
+ Timeout,
36
+ }
37
+
38
+ impl std::fmt::Display for ProtocolError {
39
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40
+ match self {
41
+ Self::SerializationError(s) => write!(f, "serialization error: {}", s),
42
+ Self::TransportError(s) => write!(f, "transport error: {}", s),
43
+ Self::ConnectionError(s) => write!(f, "connection error: {}", s),
44
+ Self::Timeout => write!(f, "timeout"),
45
+ }
46
+ }
47
+ }
48
+
49
+ impl std::error::Error for ProtocolError {}
50
+
51
+ /// Metrics for monitoring replication protocol
52
+ #[derive(Clone, Debug, Default)]
53
+ pub struct ProtocolMetrics {
54
+ messages_sent: Arc<AtomicU64>,
55
+ messages_received: Arc<AtomicU64>,
56
+ bytes_sent: Arc<AtomicU64>,
57
+ bytes_received: Arc<AtomicU64>,
58
+ errors: Arc<AtomicU64>,
59
+ connection_state_changes: Arc<AtomicU64>,
60
+ }
61
+
62
+ impl ProtocolMetrics {
63
+ pub fn new() -> Self {
64
+ Self::default()
65
+ }
66
+
67
+ pub fn record_send(&self, bytes: usize) {
68
+ self.messages_sent.fetch_add(1, Ordering::Relaxed);
69
+ self.bytes_sent.fetch_add(bytes as u64, Ordering::Relaxed);
70
+ }
71
+
72
+ pub fn record_receive(&self, bytes: usize) {
73
+ self.messages_received.fetch_add(1, Ordering::Relaxed);
74
+ self.bytes_received.fetch_add(bytes as u64, Ordering::Relaxed);
75
+ }
76
+
77
+ pub fn record_error(&self) {
78
+ self.errors.fetch_add(1, Ordering::Relaxed);
79
+ }
80
+
81
+ pub fn record_connection_state_change(&self) {
82
+ self.connection_state_changes.fetch_add(1, Ordering::Relaxed);
83
+ }
84
+
85
+ pub fn get_messages_sent(&self) -> u64 {
86
+ self.messages_sent.load(Ordering::Relaxed)
87
+ }
88
+
89
+ pub fn get_messages_received(&self) -> u64 {
90
+ self.messages_received.load(Ordering::Relaxed)
91
+ }
92
+
93
+ pub fn get_bytes_sent(&self) -> u64 {
94
+ self.bytes_sent.load(Ordering::Relaxed)
95
+ }
96
+
97
+ pub fn get_bytes_received(&self) -> u64 {
98
+ self.bytes_received.load(Ordering::Relaxed)
99
+ }
100
+
101
+ pub fn get_errors(&self) -> u64 {
102
+ self.errors.load(Ordering::Relaxed)
103
+ }
104
+
105
+ pub fn get_connection_state_changes(&self) -> u64 {
106
+ self.connection_state_changes.load(Ordering::Relaxed)
107
+ }
108
+ }
109
+
110
+ #[cfg(test)]
111
+ mod tests {
112
+ use super::*;
113
+
114
+ #[test]
115
+ fn metrics_track_messages() {
116
+ let metrics = ProtocolMetrics::new();
117
+ metrics.record_send(1024);
118
+ metrics.record_send(2048);
119
+
120
+ assert_eq!(metrics.get_messages_sent(), 2);
121
+ assert_eq!(metrics.get_bytes_sent(), 3072);
122
+ }
123
+
124
+ #[test]
125
+ fn metrics_track_errors() {
126
+ let metrics = ProtocolMetrics::new();
127
+ metrics.record_error();
128
+ metrics.record_error();
129
+
130
+ assert_eq!(metrics.get_errors(), 2);
131
+ }
132
+ }