@feltdb/core 0.4.12 → 0.4.13

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 (164) hide show
  1. package/dist/cli/commands.js +5 -2
  2. package/dist/cli/index.js +1 -1
  3. package/dist/create/cli.js +16 -1
  4. package/dist/create/create.js +30 -16
  5. package/dist/create/docker-compose-generator.js +48 -9
  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/index.js +1 -1
  160. package/dist/studio/{components-Q0Nx6gLE.js → components-bHTARBin.js} +156 -46
  161. package/dist/studio/index.js +31 -28
  162. package/dist/studio-app/assets/{index-BHOmLZF-.js → index-BqKquLuU.js} +8 -8
  163. package/dist/studio-app/index.html +1 -1
  164. package/package.json +1 -1
@@ -0,0 +1,483 @@
1
+ use crate::Operation;
2
+ /// Sync Engine module for peer-to-peer synchronization
3
+ use serde::{Deserialize, Serialize};
4
+ use std::collections::{HashMap, VecDeque};
5
+
6
+ /// A change log that tracks pending operations for synchronization
7
+ #[derive(Debug, Clone, Default, Serialize, Deserialize)]
8
+ pub struct ChangeLog {
9
+ /// Operations that have been applied locally but not yet acknowledged by peers
10
+ pub pending: VecDeque<Operation>,
11
+ /// Last sequence number we've applied from each peer
12
+ pub peer_versions: HashMap<String, u64>,
13
+ /// Contiguous high-water mark observed from every operation origin.
14
+ #[serde(default)]
15
+ pub observed_versions: HashMap<String, u64>,
16
+ /// Per-peer acknowledgement vector, keyed by operation origin.
17
+ #[serde(default)]
18
+ pub peer_acknowledgements: HashMap<String, HashMap<String, u64>>,
19
+ }
20
+
21
+ impl ChangeLog {
22
+ /// Create a new empty change log
23
+ pub fn new() -> Self {
24
+ Self::default()
25
+ }
26
+
27
+ /// Add an operation to the pending queue
28
+ pub fn add_operation(&mut self, op: Operation) {
29
+ if !self.has_operation(&op.instance_id, op.sequence) {
30
+ self.observed_versions
31
+ .entry(op.instance_id.clone())
32
+ .and_modify(|sequence| *sequence = (*sequence).max(op.sequence))
33
+ .or_insert(op.sequence);
34
+ self.pending.push_back(op);
35
+ }
36
+ }
37
+
38
+ pub fn has_operation(&self, instance_id: &str, sequence: u64) -> bool {
39
+ self.observed_versions
40
+ .get(instance_id)
41
+ .copied()
42
+ .unwrap_or(0)
43
+ >= sequence
44
+ }
45
+
46
+ /// Return the operations missing from a caller's per-origin version vector.
47
+ pub fn operations_since(&self, versions: &HashMap<String, u64>) -> Vec<Operation> {
48
+ self.pending
49
+ .iter()
50
+ .filter(|op| op.sequence > versions.get(&op.instance_id).copied().unwrap_or(0))
51
+ .cloned()
52
+ .collect()
53
+ }
54
+
55
+ /// Highest operation sequence observed for every originating instance.
56
+ pub fn versions(&self) -> HashMap<String, u64> {
57
+ self.observed_versions.clone()
58
+ }
59
+
60
+ /// Get all pending operations
61
+ pub fn pending_operations(&self) -> Vec<Operation> {
62
+ self.pending.iter().cloned().collect()
63
+ }
64
+
65
+ /// Acknowledge operations up to a given sequence number from a peer
66
+ pub fn acknowledge_peer(&mut self, peer_id: String, sequence: u64) {
67
+ self.peer_versions.insert(peer_id, sequence);
68
+ }
69
+
70
+ pub fn acknowledge_versions(&mut self, peer_id: String, versions: HashMap<String, u64>) {
71
+ let acknowledgements = self.peer_acknowledgements.entry(peer_id).or_default();
72
+ for (origin, sequence) in versions {
73
+ acknowledgements
74
+ .entry(origin)
75
+ .and_modify(|current| *current = (*current).max(sequence))
76
+ .or_insert(sequence);
77
+ }
78
+ }
79
+
80
+ pub fn acknowledged_versions(&self, peer_id: &str) -> HashMap<String, u64> {
81
+ self.peer_acknowledgements
82
+ .get(peer_id)
83
+ .cloned()
84
+ .unwrap_or_default()
85
+ }
86
+
87
+ pub fn acknowledged_operation_count(&self, active_peers: &[String]) -> usize {
88
+ if active_peers.is_empty() {
89
+ return 0;
90
+ }
91
+ self.pending
92
+ .iter()
93
+ .filter(|operation| {
94
+ active_peers.iter().all(|peer| {
95
+ self.peer_acknowledgements
96
+ .get(peer)
97
+ .and_then(|versions| versions.get(&operation.instance_id))
98
+ .copied()
99
+ .unwrap_or(0)
100
+ >= operation.sequence
101
+ })
102
+ })
103
+ .count()
104
+ }
105
+
106
+ pub fn prune_acknowledged(&mut self, active_peers: &[String]) -> usize {
107
+ if active_peers.is_empty() {
108
+ return 0;
109
+ }
110
+ let acknowledgements = &self.peer_acknowledgements;
111
+ let before = self.pending.len();
112
+ self.pending.retain(|operation| {
113
+ !active_peers.iter().all(|peer| {
114
+ acknowledgements
115
+ .get(peer)
116
+ .and_then(|versions| versions.get(&operation.instance_id))
117
+ .copied()
118
+ .unwrap_or(0)
119
+ >= operation.sequence
120
+ })
121
+ });
122
+ before - self.pending.len()
123
+ }
124
+
125
+ /// Get the last acknowledged sequence for a peer
126
+ pub fn last_peer_sequence(&self, peer_id: &str) -> u64 {
127
+ *self.peer_versions.get(peer_id).unwrap_or(&0)
128
+ }
129
+
130
+ /// Remove operations that have been acknowledged by all peers
131
+ pub fn cleanup_acknowledged(&mut self, _active_peers: &[String]) {
132
+ // For now, we keep all operations for replay
133
+ // In production, we'd remove operations that all peers have acknowledged
134
+ }
135
+ }
136
+
137
+ /// State of a peer's synchronization
138
+ #[derive(Debug, Clone, Serialize, Deserialize)]
139
+ pub struct PeerState {
140
+ /// Unique peer identifier
141
+ pub peer_id: String,
142
+ /// Last version we received from this peer
143
+ pub last_received_version: u64,
144
+ /// Operations pending delivery to this peer
145
+ pub pending_for_delivery: VecDeque<Operation>,
146
+ /// Whether we're currently syncing with this peer
147
+ pub is_syncing: bool,
148
+ /// Last sync timestamp
149
+ pub last_sync_ms: u128,
150
+ }
151
+
152
+ impl PeerState {
153
+ /// Create a new peer state
154
+ pub fn new(peer_id: String) -> Self {
155
+ Self {
156
+ peer_id,
157
+ last_received_version: 0,
158
+ pending_for_delivery: VecDeque::new(),
159
+ is_syncing: false,
160
+ last_sync_ms: 0,
161
+ }
162
+ }
163
+
164
+ /// Mark operations for delivery to this peer
165
+ pub fn add_pending(&mut self, op: Operation) {
166
+ self.pending_for_delivery.push_back(op);
167
+ }
168
+
169
+ /// Get operations pending delivery
170
+ pub fn pending_for_delivery(&self) -> Vec<Operation> {
171
+ self.pending_for_delivery.iter().cloned().collect()
172
+ }
173
+
174
+ /// Clear pending deliveries after successful sync
175
+ pub fn clear_pending(&mut self) {
176
+ self.pending_for_delivery.clear();
177
+ }
178
+ }
179
+
180
+ /// Represents a conflict between local and remote operations
181
+ #[derive(Debug, Clone, Serialize, Deserialize)]
182
+ pub struct Conflict {
183
+ /// The entity that has conflicting changes
184
+ pub entity_key: String,
185
+ /// The local operation
186
+ pub local_op: Operation,
187
+ /// The remote operation
188
+ pub remote_op: Operation,
189
+ /// Timestamp when conflict was detected
190
+ pub detected_at_ms: u128,
191
+ /// Whether this conflict has been resolved
192
+ pub resolved: bool,
193
+ }
194
+
195
+ impl Conflict {
196
+ /// Create a new conflict
197
+ pub fn new(entity_key: String, local_op: Operation, remote_op: Operation) -> Self {
198
+ use std::time::{SystemTime, UNIX_EPOCH};
199
+ let now_ms = SystemTime::now()
200
+ .duration_since(UNIX_EPOCH)
201
+ .map(|d| d.as_millis())
202
+ .unwrap_or(0);
203
+
204
+ Self {
205
+ entity_key,
206
+ local_op,
207
+ remote_op,
208
+ detected_at_ms: now_ms,
209
+ resolved: false,
210
+ }
211
+ }
212
+ }
213
+
214
+ /// Conflict detector for identifying conflicts between operations
215
+ #[derive(Debug, Default)]
216
+ pub struct ConflictDetector {
217
+ /// Detected conflicts
218
+ conflicts: Vec<Conflict>,
219
+ }
220
+
221
+ impl ConflictDetector {
222
+ /// Create a new conflict detector
223
+ pub fn new() -> Self {
224
+ Self::default()
225
+ }
226
+
227
+ /// Detect conflicts between local and remote operations
228
+ pub fn detect_conflict(
229
+ &mut self,
230
+ local_op: &Operation,
231
+ remote_op: &Operation,
232
+ ) -> Option<Conflict> {
233
+ // Detect write-write conflicts (both operations modify the same entity)
234
+ if local_op.key == remote_op.key
235
+ && local_op.capability == remote_op.capability
236
+ && local_op.op_type != crate::operation::OperationType::Delete
237
+ && remote_op.op_type != crate::operation::OperationType::Delete
238
+ {
239
+ let conflict = Conflict::new(local_op.key.clone(), local_op.clone(), remote_op.clone());
240
+ self.conflicts.push(conflict.clone());
241
+ return Some(conflict);
242
+ }
243
+
244
+ None
245
+ }
246
+
247
+ /// Get all detected conflicts
248
+ pub fn get_conflicts(&self) -> Vec<Conflict> {
249
+ self.conflicts.clone()
250
+ }
251
+
252
+ /// Clear resolved conflicts
253
+ pub fn clear_resolved(&mut self) {
254
+ self.conflicts.retain(|c| !c.resolved);
255
+ }
256
+ }
257
+
258
+ /// Sync protocol messages for peer-to-peer synchronization
259
+ #[derive(Debug, Clone, Serialize, Deserialize)]
260
+ pub enum SyncMessage {
261
+ /// Handshake to establish connection
262
+ Handshake {
263
+ instance_id: String,
264
+ version: String,
265
+ },
266
+ /// Advertise current state version
267
+ StateVersion { instance_id: String, sequence: u64 },
268
+ /// Request missing operations
269
+ MissingOperations {
270
+ instance_id: String,
271
+ since_sequence: u64,
272
+ },
273
+ /// Transfer operations to peer
274
+ OperationTransfer {
275
+ instance_id: String,
276
+ operations: Vec<Operation>,
277
+ },
278
+ /// Acknowledge receipt of operations
279
+ Acknowledgement {
280
+ instance_id: String,
281
+ up_to_sequence: u64,
282
+ },
283
+ }
284
+
285
+ /// Overall sync state and metrics
286
+ #[derive(Debug, Clone, Default, Serialize, Deserialize)]
287
+ pub struct SyncState {
288
+ /// This instance's ID
289
+ pub instance_id: String,
290
+ /// Current sequence number for this instance
291
+ pub sequence: u64,
292
+ /// Connected peers
293
+ pub connected_peers: Vec<String>,
294
+ /// Pending operations waiting to be synced
295
+ pub pending_operations: u64,
296
+ /// Operations sent to peers
297
+ pub operations_sent: u64,
298
+ /// Operations received from peers
299
+ pub operations_received: u64,
300
+ /// Conflicts detected
301
+ pub conflicts_detected: u64,
302
+ /// Last sync timestamp
303
+ pub last_sync_ms: u128,
304
+ /// Is currently connected to network
305
+ pub is_connected: bool,
306
+ /// Vector clock for causal tracking (optional for backward compatibility)
307
+ #[serde(default)]
308
+ pub vector_clock: Option<std::collections::HashMap<String, u64>>,
309
+ }
310
+
311
+ impl SyncState {
312
+ /// Create a new sync state
313
+ pub fn new(instance_id: String) -> Self {
314
+ Self {
315
+ instance_id,
316
+ sequence: 0,
317
+ connected_peers: Vec::new(),
318
+ pending_operations: 0,
319
+ operations_sent: 0,
320
+ operations_received: 0,
321
+ conflicts_detected: 0,
322
+ last_sync_ms: 0,
323
+ is_connected: false,
324
+ vector_clock: None,
325
+ }
326
+ }
327
+
328
+ /// Add a connected peer
329
+ pub fn add_peer(&mut self, peer_id: String) {
330
+ if !self.connected_peers.contains(&peer_id) {
331
+ self.connected_peers.push(peer_id);
332
+ self.is_connected = true;
333
+ }
334
+ }
335
+
336
+ /// Remove a connected peer
337
+ pub fn remove_peer(&mut self, peer_id: &str) {
338
+ self.connected_peers.retain(|p| p != peer_id);
339
+ if self.connected_peers.is_empty() {
340
+ self.is_connected = false;
341
+ }
342
+ }
343
+
344
+ /// Increment operations sent
345
+ pub fn increment_sent(&mut self, count: u64) {
346
+ self.operations_sent += count;
347
+ }
348
+
349
+ /// Increment operations received
350
+ pub fn increment_received(&mut self, count: u64) {
351
+ self.operations_received += count;
352
+ }
353
+
354
+ /// Increment conflicts detected
355
+ pub fn increment_conflicts(&mut self) {
356
+ self.conflicts_detected += 1;
357
+ }
358
+
359
+ /// Initialize or get the vector clock for this sync state
360
+ pub fn get_or_init_vector_clock(&mut self) -> &mut std::collections::HashMap<String, u64> {
361
+ if self.vector_clock.is_none() {
362
+ self.vector_clock = Some(std::collections::HashMap::new());
363
+ }
364
+ self.vector_clock.as_mut().unwrap()
365
+ }
366
+
367
+ /// Increment the vector clock for this instance
368
+ pub fn increment_vector_clock(&mut self) {
369
+ let instance_id = self.instance_id.clone();
370
+ let clock = self.get_or_init_vector_clock();
371
+ let entry = clock.entry(instance_id).or_insert(0);
372
+ *entry += 1;
373
+ }
374
+
375
+ /// Merge another vector clock into ours (takes maximum)
376
+ pub fn merge_vector_clock(&mut self, other: &std::collections::HashMap<String, u64>) {
377
+ let clock = self.get_or_init_vector_clock();
378
+ for (instance_id, other_value) in other {
379
+ let entry = clock.entry(instance_id.clone()).or_insert(0);
380
+ *entry = (*entry).max(*other_value);
381
+ }
382
+ }
383
+ }
384
+
385
+ #[cfg(test)]
386
+ mod tests {
387
+ use super::*;
388
+
389
+ #[test]
390
+ fn change_log_tracks_operations() {
391
+ let mut log = ChangeLog::new();
392
+ let op = Operation::insert(
393
+ 1,
394
+ "inst1".to_string(),
395
+ 1,
396
+ "users:123".to_string(),
397
+ serde_json::json!({"name": "Alice"}),
398
+ "User".to_string(),
399
+ "users".to_string(),
400
+ );
401
+
402
+ log.add_operation(op.clone());
403
+ assert_eq!(log.pending_operations().len(), 1);
404
+ }
405
+
406
+ #[test]
407
+ fn peer_state_tracks_pending() {
408
+ let mut peer = PeerState::new("peer1".to_string());
409
+ let op = Operation::insert(
410
+ 1,
411
+ "inst1".to_string(),
412
+ 1,
413
+ "users:123".to_string(),
414
+ serde_json::json!({"name": "Alice"}),
415
+ "User".to_string(),
416
+ "users".to_string(),
417
+ );
418
+
419
+ peer.add_pending(op);
420
+ assert_eq!(peer.pending_for_delivery().len(), 1);
421
+ peer.clear_pending();
422
+ assert_eq!(peer.pending_for_delivery().len(), 0);
423
+ }
424
+
425
+ #[test]
426
+ fn conflict_detector_finds_write_conflicts() {
427
+ let mut detector = ConflictDetector::new();
428
+ let local_op = Operation::update(
429
+ 1,
430
+ "inst1".to_string(),
431
+ 1,
432
+ "tasks:123".to_string(),
433
+ serde_json::json!({"status": "completed"}),
434
+ "Task".to_string(),
435
+ "tasks".to_string(),
436
+ );
437
+
438
+ let remote_op = Operation::update(
439
+ 2,
440
+ "inst2".to_string(),
441
+ 1,
442
+ "tasks:123".to_string(),
443
+ serde_json::json!({"status": "blocked"}),
444
+ "Task".to_string(),
445
+ "tasks".to_string(),
446
+ );
447
+
448
+ let conflict = detector.detect_conflict(&local_op, &remote_op);
449
+ assert!(conflict.is_some());
450
+ assert_eq!(detector.get_conflicts().len(), 1);
451
+ }
452
+
453
+ #[test]
454
+ fn sync_state_tracks_peers() {
455
+ let mut state = SyncState::new("inst1".to_string());
456
+ assert!(!state.is_connected);
457
+
458
+ state.add_peer("peer1".to_string());
459
+ assert!(state.is_connected);
460
+ assert_eq!(state.connected_peers.len(), 1);
461
+
462
+ state.add_peer("peer2".to_string());
463
+ assert_eq!(state.connected_peers.len(), 2);
464
+
465
+ state.remove_peer("peer1");
466
+ assert_eq!(state.connected_peers.len(), 1);
467
+ assert!(state.is_connected);
468
+
469
+ state.remove_peer("peer2");
470
+ assert!(!state.is_connected);
471
+ }
472
+
473
+ #[test]
474
+ fn sync_message_serializes() {
475
+ let msg = SyncMessage::Handshake {
476
+ instance_id: "inst1".to_string(),
477
+ version: "0.1.0".to_string(),
478
+ };
479
+
480
+ let json = serde_json::to_string(&msg).expect("serialize");
481
+ let _deserialized: SyncMessage = serde_json::from_str(&json).expect("deserialize");
482
+ }
483
+ }