@feltdb/core 0.4.11 → 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-q43NSTTH.js → components-bHTARBin.js} +160 -89
  161. package/dist/studio/index.js +31 -28
  162. package/dist/studio-app/assets/{index-DZpBoVMp.js → index-BqKquLuU.js} +8 -8
  163. package/dist/studio-app/index.html +1 -1
  164. package/package.json +1 -1
@@ -0,0 +1,646 @@
1
+ /// Transaction system - Verified state transitions with atomicity guarantees
2
+ ///
3
+ /// Implements the STATE_MODEL.md specification:
4
+ /// - Optimistic concurrency with commit-time conflict detection
5
+ /// - Deterministic operation execution
6
+ /// - Read/write dependency tracking
7
+ /// - Configurable consistency contracts
8
+ /// - Authoritative operation log with derived indexes
9
+
10
+ use std::collections::{HashMap, HashSet};
11
+ use crate::convergence::VectorClock;
12
+ use serde::{Deserialize, Serialize};
13
+
14
+ #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
15
+ pub struct OperationId {
16
+ pub node_id: String,
17
+ pub sequence: u64,
18
+ }
19
+
20
+ impl OperationId {
21
+ pub fn new(node_id: String, sequence: u64) -> Self {
22
+ OperationId { node_id, sequence }
23
+ }
24
+ }
25
+
26
+ #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
27
+ pub struct StateVersion {
28
+ pub vector_clock: VectorClock,
29
+ pub state_hash: String,
30
+ }
31
+
32
+ impl StateVersion {
33
+ pub fn new(vector_clock: VectorClock, state_hash: String) -> Self {
34
+ StateVersion {
35
+ vector_clock,
36
+ state_hash,
37
+ }
38
+ }
39
+
40
+ pub fn dominates(&self, other: &StateVersion) -> bool {
41
+ self.vector_clock != other.vector_clock
42
+ && other.vector_clock.happens_before(&self.vector_clock)
43
+ }
44
+
45
+ pub fn concurrent_with(&self, other: &StateVersion) -> bool {
46
+ self.vector_clock.is_concurrent(&other.vector_clock)
47
+ }
48
+
49
+ pub fn equal_to(&self, other: &StateVersion) -> bool {
50
+ self.vector_clock == other.vector_clock && self.state_hash == other.state_hash
51
+ }
52
+ }
53
+
54
+ #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
55
+ pub struct CommitId {
56
+ pub consensus_group_id: String,
57
+ pub commit_index: u64,
58
+ pub transaction_id: String,
59
+ }
60
+
61
+ impl CommitId {
62
+ pub fn new(consensus_group_id: String, commit_index: u64, transaction_id: String) -> Self {
63
+ CommitId {
64
+ consensus_group_id,
65
+ commit_index,
66
+ transaction_id,
67
+ }
68
+ }
69
+ }
70
+
71
+ #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
72
+ pub enum ConsistencyLevel {
73
+ Local,
74
+ Eventual,
75
+ Causal,
76
+ Linearizable,
77
+ }
78
+
79
+ #[derive(Clone, Debug, Serialize, Deserialize)]
80
+ pub struct ConsistencyContract {
81
+ pub level: ConsistencyLevel,
82
+ pub timeout_ms: Option<u64>,
83
+ pub required_acks: Option<usize>,
84
+ }
85
+
86
+ impl ConsistencyContract {
87
+ pub fn local() -> Self {
88
+ ConsistencyContract {
89
+ level: ConsistencyLevel::Local,
90
+ timeout_ms: None,
91
+ required_acks: None,
92
+ }
93
+ }
94
+
95
+ pub fn eventual() -> Self {
96
+ ConsistencyContract {
97
+ level: ConsistencyLevel::Eventual,
98
+ timeout_ms: Some(5000),
99
+ required_acks: None,
100
+ }
101
+ }
102
+
103
+ pub fn causal() -> Self {
104
+ ConsistencyContract {
105
+ level: ConsistencyLevel::Causal,
106
+ timeout_ms: Some(1000),
107
+ required_acks: None,
108
+ }
109
+ }
110
+
111
+ pub fn linearizable(required_acks: usize) -> Self {
112
+ ConsistencyContract {
113
+ level: ConsistencyLevel::Linearizable,
114
+ timeout_ms: Some(10000),
115
+ required_acks: Some(required_acks),
116
+ }
117
+ }
118
+ }
119
+
120
+ #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
121
+ pub struct StateDependency {
122
+ pub collection: String,
123
+ pub record_id: String,
124
+ pub field: Option<String>,
125
+ pub version: StateVersion,
126
+ }
127
+
128
+ impl StateDependency {
129
+ pub fn new(
130
+ collection: String,
131
+ record_id: String,
132
+ field: Option<String>,
133
+ version: StateVersion,
134
+ ) -> Self {
135
+ StateDependency {
136
+ collection,
137
+ record_id,
138
+ field,
139
+ version,
140
+ }
141
+ }
142
+ }
143
+
144
+ #[derive(Clone, Debug, Serialize, Deserialize)]
145
+ pub struct OperationCommand {
146
+ pub op_type: String,
147
+ pub collection: String,
148
+ pub record_id: String,
149
+ pub fields: HashMap<String, serde_json::Value>,
150
+ }
151
+
152
+ #[derive(Clone, Debug, Serialize, Deserialize)]
153
+ pub struct Operation {
154
+ pub operation_id: OperationId,
155
+ pub parent_version: StateVersion,
156
+ pub transaction_id: Option<String>,
157
+ pub intent: String,
158
+ pub preconditions: Vec<(String, String)>,
159
+ pub read_set: Vec<StateDependency>,
160
+ pub write_set: Vec<StateDependency>,
161
+ pub command: OperationCommand,
162
+ pub timestamp: u64,
163
+ pub initiator: String,
164
+ pub source: String,
165
+ pub evidence: Option<HashMap<String, serde_json::Value>>,
166
+ }
167
+
168
+ impl Operation {
169
+ pub fn new(
170
+ operation_id: OperationId,
171
+ parent_version: StateVersion,
172
+ intent: String,
173
+ command: OperationCommand,
174
+ initiator: String,
175
+ ) -> Self {
176
+ Operation {
177
+ operation_id,
178
+ parent_version,
179
+ transaction_id: None,
180
+ intent,
181
+ preconditions: Vec::new(),
182
+ read_set: Vec::new(),
183
+ write_set: Vec::new(),
184
+ command,
185
+ timestamp: std::time::SystemTime::now()
186
+ .duration_since(std::time::UNIX_EPOCH)
187
+ .unwrap()
188
+ .as_millis() as u64,
189
+ initiator,
190
+ source: "local".to_string(),
191
+ evidence: None,
192
+ }
193
+ }
194
+
195
+ pub fn with_preconditions(mut self, preconditions: Vec<(String, String)>) -> Self {
196
+ self.preconditions = preconditions;
197
+ self
198
+ }
199
+
200
+ pub fn with_read_set(mut self, read_set: Vec<StateDependency>) -> Self {
201
+ self.read_set = read_set;
202
+ self
203
+ }
204
+
205
+ pub fn with_write_set(mut self, write_set: Vec<StateDependency>) -> Self {
206
+ self.write_set = write_set;
207
+ self
208
+ }
209
+
210
+ pub fn with_transaction_id(mut self, transaction_id: String) -> Self {
211
+ self.transaction_id = Some(transaction_id);
212
+ self
213
+ }
214
+ }
215
+
216
+ #[derive(Clone, Debug, PartialEq, Eq)]
217
+ pub enum TransitionResult {
218
+ Success,
219
+ Aborted { reason: String },
220
+ Failed { reason: String },
221
+ }
222
+
223
+ #[derive(Clone, Debug)]
224
+ pub struct StateTransition {
225
+ pub from_version: StateVersion,
226
+ pub operations: Vec<Operation>,
227
+ pub transaction_id: Option<String>,
228
+ pub result: TransitionResult,
229
+ pub to_version: StateVersion,
230
+ pub commit_id: Option<CommitId>,
231
+ pub consistency_contract: ConsistencyContract,
232
+ }
233
+
234
+ impl StateTransition {
235
+ pub fn new(
236
+ from_version: StateVersion,
237
+ operations: Vec<Operation>,
238
+ to_version: StateVersion,
239
+ consistency_contract: ConsistencyContract,
240
+ ) -> Self {
241
+ StateTransition {
242
+ from_version,
243
+ transaction_id: operations.first().and_then(|o| o.transaction_id.clone()),
244
+ result: TransitionResult::Success,
245
+ operations,
246
+ to_version,
247
+ commit_id: None,
248
+ consistency_contract,
249
+ }
250
+ }
251
+
252
+ pub fn aborted(
253
+ from_version: StateVersion,
254
+ reason: String,
255
+ consistency_contract: ConsistencyContract,
256
+ ) -> Self {
257
+ StateTransition {
258
+ from_version: from_version.clone(),
259
+ operations: Vec::new(),
260
+ transaction_id: None,
261
+ result: TransitionResult::Aborted { reason },
262
+ to_version: from_version,
263
+ commit_id: None,
264
+ consistency_contract,
265
+ }
266
+ }
267
+
268
+ pub fn is_successful(&self) -> bool {
269
+ matches!(self.result, TransitionResult::Success)
270
+ }
271
+ }
272
+
273
+ pub struct TransactionExecutor {
274
+ node_id: String,
275
+ local_clock: u64,
276
+ applied_operations: HashSet<OperationId>,
277
+ }
278
+
279
+ impl TransactionExecutor {
280
+ pub fn new(node_id: String) -> Self {
281
+ TransactionExecutor {
282
+ node_id,
283
+ local_clock: 0,
284
+ applied_operations: HashSet::new(),
285
+ }
286
+ }
287
+
288
+ pub fn execute_transaction(
289
+ &mut self,
290
+ _transaction_id: String,
291
+ parent_version: StateVersion,
292
+ operations: Vec<Operation>,
293
+ consistency_contract: ConsistencyContract,
294
+ ) -> StateTransition {
295
+ for op in &operations {
296
+ if self.applied_operations.contains(&op.operation_id) {
297
+ return StateTransition::aborted(
298
+ parent_version,
299
+ format!("Operation {:?} already applied", op.operation_id),
300
+ consistency_contract,
301
+ );
302
+ }
303
+ }
304
+
305
+ let mut to_version = parent_version.clone();
306
+ self.local_clock += 1;
307
+
308
+ for op in &operations {
309
+ self.applied_operations.insert(op.operation_id.clone());
310
+
311
+ to_version.vector_clock.increment(&self.node_id);
312
+
313
+ let mut hasher = std::collections::hash_map::DefaultHasher::new();
314
+ use std::hash::{Hash, Hasher};
315
+ format!("{}{:?}", to_version.state_hash, op.command).hash(&mut hasher);
316
+ to_version.state_hash = format!("{:x}", hasher.finish());
317
+ }
318
+
319
+ StateTransition::new(
320
+ parent_version,
321
+ operations,
322
+ to_version,
323
+ consistency_contract,
324
+ )
325
+ }
326
+
327
+ pub fn mark_applied(&mut self, operation_id: &OperationId) {
328
+ self.applied_operations.insert(operation_id.clone());
329
+ }
330
+
331
+ pub fn is_applied(&self, operation_id: &OperationId) -> bool {
332
+ self.applied_operations.contains(operation_id)
333
+ }
334
+ }
335
+
336
+ pub struct CommitValidator;
337
+
338
+ impl CommitValidator {
339
+ pub fn validate_transition(
340
+ transition: &StateTransition,
341
+ current_state: &StateVersion,
342
+ ) -> Result<(), String> {
343
+ match transition.result {
344
+ TransitionResult::Success => {
345
+ if transition.from_version.equal_to(current_state)
346
+ || transition.from_version.dominates(current_state)
347
+ {
348
+ Ok(())
349
+ } else if transition.from_version.concurrent_with(current_state) {
350
+ Err(format!(
351
+ "State version mismatch: concurrent states detected"
352
+ ))
353
+ } else {
354
+ Err(format!(
355
+ "State version stale: transition from {:?} but current is {:?}",
356
+ transition.from_version, current_state
357
+ ))
358
+ }
359
+ }
360
+ TransitionResult::Aborted { ref reason } => Err(format!("Transaction aborted: {}", reason)),
361
+ TransitionResult::Failed { ref reason } => Err(format!("Transaction failed: {}", reason)),
362
+ }
363
+ }
364
+
365
+ pub fn detect_conflicts(
366
+ transition: &StateTransition,
367
+ concurrent_transitions: &[StateTransition],
368
+ ) -> Vec<String> {
369
+ let mut conflicts = Vec::new();
370
+
371
+ for other in concurrent_transitions {
372
+ if !transition.is_successful() || !other.is_successful() {
373
+ continue;
374
+ }
375
+
376
+ for op in &transition.operations {
377
+ for other_op in &other.operations {
378
+ if op.operation_id == other_op.operation_id {
379
+ conflicts.push(format!(
380
+ "Duplicate operation: {:?}",
381
+ op.operation_id
382
+ ));
383
+ continue;
384
+ }
385
+
386
+ let op_writes: HashSet<_> = op
387
+ .write_set
388
+ .iter()
389
+ .map(|d| (d.collection.clone(), d.record_id.clone()))
390
+ .collect();
391
+ let other_writes: HashSet<_> = other_op
392
+ .write_set
393
+ .iter()
394
+ .map(|d| (d.collection.clone(), d.record_id.clone()))
395
+ .collect();
396
+
397
+ if !op_writes.is_disjoint(&other_writes) {
398
+ conflicts.push(format!(
399
+ "Write-write conflict: {:?} and {:?}",
400
+ op.intent, other_op.intent
401
+ ));
402
+ }
403
+
404
+ let op_reads: HashSet<_> = op
405
+ .read_set
406
+ .iter()
407
+ .map(|d| (d.collection.clone(), d.record_id.clone()))
408
+ .collect();
409
+ let other_reads: HashSet<_> = other_op
410
+ .read_set
411
+ .iter()
412
+ .map(|d| (d.collection.clone(), d.record_id.clone()))
413
+ .collect();
414
+
415
+ if !op_reads.is_disjoint(&other_writes) {
416
+ conflicts.push(format!(
417
+ "Read-after-write conflict: {:?} reads what {:?} writes",
418
+ op.intent, other_op.intent
419
+ ));
420
+ }
421
+
422
+ if !op_writes.is_disjoint(&other_reads) {
423
+ conflicts.push(format!(
424
+ "Write-after-read conflict: {:?} writes what {:?} reads",
425
+ op.intent, other_op.intent
426
+ ));
427
+ }
428
+ }
429
+ }
430
+ }
431
+
432
+ conflicts
433
+ }
434
+ }
435
+
436
+ #[cfg(test)]
437
+ mod tests {
438
+ use super::*;
439
+
440
+ #[test]
441
+ fn test_operation_id_deduplication() {
442
+ let mut executor = TransactionExecutor::new("node1".to_string());
443
+ let op_id = OperationId::new("node1".to_string(), 1);
444
+
445
+ assert!(!executor.is_applied(&op_id));
446
+ executor.mark_applied(&op_id);
447
+ assert!(executor.is_applied(&op_id));
448
+ }
449
+
450
+ #[test]
451
+ fn test_state_version_domination() {
452
+ let mut vc1 = VectorClock::new();
453
+ vc1.increment("node1");
454
+ vc1.increment("node1");
455
+ let v1 = StateVersion::new(vc1, "hash1".to_string());
456
+
457
+ let mut vc2 = VectorClock::new();
458
+ vc2.increment("node1");
459
+ let v2 = StateVersion::new(vc2, "hash2".to_string());
460
+
461
+ assert!(v1.dominates(&v2));
462
+ assert!(!v2.dominates(&v1));
463
+ }
464
+
465
+ #[test]
466
+ fn test_concurrent_state_versions() {
467
+ let mut vc1 = VectorClock::new();
468
+ vc1.increment("node1");
469
+ let v1 = StateVersion::new(vc1, "hash1".to_string());
470
+
471
+ let mut vc2 = VectorClock::new();
472
+ vc2.increment("node2");
473
+ let v2 = StateVersion::new(vc2, "hash2".to_string());
474
+
475
+ assert!(v1.concurrent_with(&v2));
476
+ assert!(v2.concurrent_with(&v1));
477
+ }
478
+
479
+ #[test]
480
+ fn test_exactly_once_semantics() {
481
+ let mut executor = TransactionExecutor::new("node1".to_string());
482
+ let mut vc = VectorClock::new();
483
+ vc.increment("node1");
484
+ let version = StateVersion::new(vc, "initial_hash".to_string());
485
+
486
+ let op_id = OperationId::new("node1".to_string(), 1);
487
+ let command = OperationCommand {
488
+ op_type: "SetField".to_string(),
489
+ collection: "orders".to_string(),
490
+ record_id: "123".to_string(),
491
+ fields: {
492
+ let mut m = HashMap::new();
493
+ m.insert("status".to_string(), serde_json::json!("paid"));
494
+ m
495
+ },
496
+ };
497
+
498
+ let op = Operation::new(
499
+ op_id.clone(),
500
+ version.clone(),
501
+ "MarkOrderPaid".to_string(),
502
+ command,
503
+ "system".to_string(),
504
+ );
505
+
506
+ let transition1 =
507
+ executor.execute_transaction("tx1".to_string(), version.clone(), vec![op.clone()], ConsistencyContract::local());
508
+
509
+ assert!(transition1.is_successful());
510
+ assert_eq!(transition1.operations.len(), 1);
511
+
512
+ let transition2 = executor.execute_transaction(
513
+ "tx2".to_string(),
514
+ version,
515
+ vec![op],
516
+ ConsistencyContract::local(),
517
+ );
518
+
519
+ assert!(!transition2.is_successful());
520
+ }
521
+
522
+ #[test]
523
+ fn test_conflict_detection() {
524
+ let mut vc = VectorClock::new();
525
+ vc.increment("node1");
526
+ let version = StateVersion::new(vc, "initial_hash".to_string());
527
+
528
+ let mut cmd1 = HashMap::new();
529
+ cmd1.insert("amount".to_string(), serde_json::json!(100));
530
+
531
+ let op1 = Operation::new(
532
+ OperationId::new("node1".to_string(), 1),
533
+ version.clone(),
534
+ "Debit".to_string(),
535
+ OperationCommand {
536
+ op_type: "UpdateField".to_string(),
537
+ collection: "accounts".to_string(),
538
+ record_id: "acc1".to_string(),
539
+ fields: cmd1,
540
+ },
541
+ "system".to_string(),
542
+ )
543
+ .with_write_set(vec![StateDependency::new(
544
+ "accounts".to_string(),
545
+ "acc1".to_string(),
546
+ Some("amount".to_string()),
547
+ version.clone(),
548
+ )]);
549
+
550
+ let mut cmd2 = HashMap::new();
551
+ cmd2.insert("amount".to_string(), serde_json::json!(50));
552
+
553
+ let op2 = Operation::new(
554
+ OperationId::new("node1".to_string(), 2),
555
+ version.clone(),
556
+ "Credit".to_string(),
557
+ OperationCommand {
558
+ op_type: "UpdateField".to_string(),
559
+ collection: "accounts".to_string(),
560
+ record_id: "acc1".to_string(),
561
+ fields: cmd2,
562
+ },
563
+ "system".to_string(),
564
+ )
565
+ .with_write_set(vec![StateDependency::new(
566
+ "accounts".to_string(),
567
+ "acc1".to_string(),
568
+ Some("amount".to_string()),
569
+ version.clone(),
570
+ )]);
571
+
572
+ let mut to_vc = version.vector_clock.clone();
573
+ to_vc.increment("node1");
574
+ let to_version = StateVersion::new(to_vc, "new_hash".to_string());
575
+
576
+ let transition1 = StateTransition::new(
577
+ version.clone(),
578
+ vec![op1],
579
+ to_version.clone(),
580
+ ConsistencyContract::causal(),
581
+ );
582
+
583
+ let transition2 = StateTransition::new(
584
+ version,
585
+ vec![op2],
586
+ to_version,
587
+ ConsistencyContract::causal(),
588
+ );
589
+
590
+ let conflicts = CommitValidator::detect_conflicts(&transition1, &[transition2]);
591
+ assert!(!conflicts.is_empty());
592
+ assert!(conflicts
593
+ .iter()
594
+ .any(|c| c.contains("Write-write conflict")));
595
+ }
596
+
597
+ #[test]
598
+ fn test_consistency_contract_creation() {
599
+ let local = ConsistencyContract::local();
600
+ assert_eq!(local.level, ConsistencyLevel::Local);
601
+ assert_eq!(local.timeout_ms, None);
602
+
603
+ let causal = ConsistencyContract::causal();
604
+ assert_eq!(causal.level, ConsistencyLevel::Causal);
605
+ assert_eq!(causal.timeout_ms, Some(1000));
606
+
607
+ let linearizable = ConsistencyContract::linearizable(3);
608
+ assert_eq!(linearizable.level, ConsistencyLevel::Linearizable);
609
+ assert_eq!(linearizable.required_acks, Some(3));
610
+ }
611
+
612
+ #[test]
613
+ fn test_transition_validation() {
614
+ let mut vc = VectorClock::new();
615
+ vc.increment("node1");
616
+ let version = StateVersion::new(vc, "hash".to_string());
617
+
618
+ let command = OperationCommand {
619
+ op_type: "SetField".to_string(),
620
+ collection: "test".to_string(),
621
+ record_id: "1".to_string(),
622
+ fields: HashMap::new(),
623
+ };
624
+
625
+ let op = Operation::new(
626
+ OperationId::new("node1".to_string(), 1),
627
+ version.clone(),
628
+ "Test".to_string(),
629
+ command,
630
+ "system".to_string(),
631
+ );
632
+
633
+ let mut to_vc = version.vector_clock.clone();
634
+ to_vc.increment("node1");
635
+ let to_version = StateVersion::new(to_vc, "new_hash".to_string());
636
+
637
+ let transition = StateTransition::new(
638
+ version.clone(),
639
+ vec![op],
640
+ to_version,
641
+ ConsistencyContract::local(),
642
+ );
643
+
644
+ assert!(CommitValidator::validate_transition(&transition, &version).is_ok());
645
+ }
646
+ }