@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.
- package/dist/cli/commands.js +5 -2
- package/dist/cli/index.js +1 -1
- package/dist/create/cli.js +16 -1
- package/dist/create/create.js +30 -16
- package/dist/create/docker-compose-generator.js +48 -9
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +2238 -0
- package/dist/create/server-source/Cargo.toml +7 -0
- package/dist/create/server-source/crates/feltdb/Cargo.lock +175 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +79 -0
- package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
- package/dist/create/server-source/crates/feltdb/src/acquisition.rs +286 -0
- package/dist/create/server-source/crates/feltdb/src/admission.rs +192 -0
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +475 -0
- package/dist/create/server-source/crates/feltdb/src/application.rs +2244 -0
- package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
- package/dist/create/server-source/crates/feltdb/src/authorization.rs +1030 -0
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
- package/dist/create/server-source/crates/feltdb/src/capability.rs +903 -0
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
- package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +465 -0
- package/dist/create/server-source/crates/feltdb/src/convergence.rs +618 -0
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +380 -0
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
- package/dist/create/server-source/crates/feltdb/src/cron.rs +294 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +316 -0
- package/dist/create/server-source/crates/feltdb/src/execution.rs +426 -0
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +779 -0
- package/dist/create/server-source/crates/feltdb/src/lib.rs +2838 -0
- package/dist/create/server-source/crates/feltdb/src/materialization.rs +184 -0
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +267 -0
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
- package/dist/create/server-source/crates/feltdb/src/observability.rs +366 -0
- package/dist/create/server-source/crates/feltdb/src/operation.rs +251 -0
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +344 -0
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +166 -0
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
- package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +400 -0
- package/dist/create/server-source/crates/feltdb/src/provenance.rs +207 -0
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +217 -0
- package/dist/create/server-source/crates/feltdb/src/references.rs +404 -0
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +160 -0
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
- package/dist/create/server-source/crates/feltdb/src/routing.rs +409 -0
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +523 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +3118 -0
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +291 -0
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
- package/dist/create/server-source/crates/feltdb/src/storage.rs +267 -0
- package/dist/create/server-source/crates/feltdb/src/submission.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/sync.rs +483 -0
- package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +822 -0
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +473 -0
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +646 -0
- package/dist/create/server-source/crates/feltdb/src/trigger.rs +390 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
- package/dist/create/server-source/crates/feltdb/src/workflow.rs +713 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +1310 -0
- package/dist/create/server-source/crates/feltdb-server/Cargo.toml +23 -0
- package/dist/create/server-source/crates/feltdb-server/README.md +79 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +73 -0
- package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
- package/dist/create/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
- package/dist/create/server-source/crates/feltdb-server/src/audit.rs +59 -0
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +308 -0
- package/dist/create/server-source/crates/feltdb-server/src/authorization.rs +228 -0
- package/dist/create/server-source/crates/feltdb-server/src/backup.rs +416 -0
- package/dist/create/server-source/crates/feltdb-server/src/causal.rs +218 -0
- package/dist/create/server-source/crates/feltdb-server/src/certification.rs +223 -0
- package/dist/create/server-source/crates/feltdb-server/src/clock.rs +132 -0
- package/dist/create/server-source/crates/feltdb-server/src/cluster.rs +165 -0
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +1044 -0
- package/dist/create/server-source/crates/feltdb-server/src/content.rs +111 -0
- package/dist/create/server-source/crates/feltdb-server/src/identity.rs +250 -0
- package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +78 -0
- package/dist/create/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
- package/dist/create/server-source/crates/feltdb-server/src/leases.rs +123 -0
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +25 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +8715 -0
- package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +97 -0
- package/dist/create/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
- package/dist/create/server-source/crates/feltdb-server/src/principals.rs +510 -0
- package/dist/create/server-source/crates/feltdb-server/src/providers.rs +269 -0
- package/dist/create/server-source/crates/feltdb-server/src/releases.rs +2563 -0
- package/dist/create/server-source/crates/feltdb-server/src/sessions.rs +156 -0
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
- package/dist/create/server-source/crates/feltdb-server/src/versions.rs +264 -0
- package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
- package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
- package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
- package/dist/flowspec.d.ts +2 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +28 -4
- package/dist/state-contract.d.ts +7 -1
- package/dist/state-contract.d.ts.map +1 -1
- package/dist/studio/components/ApplicationDesigner.d.ts.map +1 -1
- package/dist/studio/components/index.js +1 -1
- package/dist/studio/{components-Q0Nx6gLE.js → components-bHTARBin.js} +156 -46
- package/dist/studio/index.js +31 -28
- package/dist/studio-app/assets/{index-BHOmLZF-.js → index-BqKquLuU.js} +8 -8
- package/dist/studio-app/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/// Transaction API Layer
|
|
2
|
+
///
|
|
3
|
+
/// Public-facing transaction management API that integrates:
|
|
4
|
+
/// - TransactionExecutor for execution logic
|
|
5
|
+
/// - OperationLog for operation persistence
|
|
6
|
+
/// - StateTransitionStore for transition durability
|
|
7
|
+
/// - Precondition validation
|
|
8
|
+
/// - Read/write dependency tracking
|
|
9
|
+
|
|
10
|
+
use crate::convergence::VectorClock;
|
|
11
|
+
use crate::operation_log::{MemoryOperationLog, OperationLogEntry};
|
|
12
|
+
use crate::state_transition_store::{MemoryStateTransitionStore, StateTransitionRecord};
|
|
13
|
+
use crate::transactions::*;
|
|
14
|
+
use std::sync::{Arc, Mutex};
|
|
15
|
+
|
|
16
|
+
/// Result type for transaction API operations
|
|
17
|
+
pub type TxResult<T> = Result<T, TxError>;
|
|
18
|
+
|
|
19
|
+
/// Transaction API errors
|
|
20
|
+
#[derive(Clone, Debug)]
|
|
21
|
+
pub enum TxError {
|
|
22
|
+
PreconditionFailed(String),
|
|
23
|
+
ConflictDetected(String),
|
|
24
|
+
OperationDeduplication(String),
|
|
25
|
+
InvalidState(String),
|
|
26
|
+
PersistenceFailure(String),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
impl std::fmt::Display for TxError {
|
|
30
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
31
|
+
match self {
|
|
32
|
+
TxError::PreconditionFailed(msg) => write!(f, "Precondition failed: {}", msg),
|
|
33
|
+
TxError::ConflictDetected(msg) => write!(f, "Conflict detected: {}", msg),
|
|
34
|
+
TxError::OperationDeduplication(msg) => write!(f, "Operation already executed: {}", msg),
|
|
35
|
+
TxError::InvalidState(msg) => write!(f, "Invalid state: {}", msg),
|
|
36
|
+
TxError::PersistenceFailure(msg) => write!(f, "Persistence failure: {}", msg),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
impl std::error::Error for TxError {}
|
|
42
|
+
|
|
43
|
+
/// Transaction manager combining execution, persistence, and validation
|
|
44
|
+
pub struct TransactionManager {
|
|
45
|
+
node_id: String,
|
|
46
|
+
executor: TransactionExecutor,
|
|
47
|
+
operation_log: MemoryOperationLog,
|
|
48
|
+
transition_store: MemoryStateTransitionStore,
|
|
49
|
+
current_version: Arc<Mutex<StateVersion>>,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
impl TransactionManager {
|
|
53
|
+
/// Create a new transaction manager
|
|
54
|
+
pub fn new(node_id: String, initial_version: StateVersion) -> Self {
|
|
55
|
+
Self {
|
|
56
|
+
node_id: node_id.clone(),
|
|
57
|
+
executor: TransactionExecutor::new(node_id),
|
|
58
|
+
operation_log: MemoryOperationLog::new(),
|
|
59
|
+
transition_store: MemoryStateTransitionStore::new(),
|
|
60
|
+
current_version: Arc::new(Mutex::new(initial_version)),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Get the current state version
|
|
65
|
+
pub fn current_state_version(&self) -> StateVersion {
|
|
66
|
+
self.current_version.lock().unwrap().clone()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// Execute a transaction with full persistence integration
|
|
70
|
+
pub fn execute_and_commit(
|
|
71
|
+
&mut self,
|
|
72
|
+
transaction_id: String,
|
|
73
|
+
mut operations: Vec<Operation>,
|
|
74
|
+
consistency_contract: ConsistencyContract,
|
|
75
|
+
) -> TxResult<StateTransitionRecord> {
|
|
76
|
+
// 1. Validate all operations are new (not duplicated in operation log)
|
|
77
|
+
for op in &operations {
|
|
78
|
+
if self.operation_log.contains_operation_id(&op.operation_id) {
|
|
79
|
+
return Err(TxError::OperationDeduplication(format!(
|
|
80
|
+
"Operation {} already executed",
|
|
81
|
+
format!("{}:{}", op.operation_id.node_id, op.operation_id.sequence)
|
|
82
|
+
)));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 2. Set transaction_id on all operations
|
|
87
|
+
for op in &mut operations {
|
|
88
|
+
op.transaction_id = Some(transaction_id.clone());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 3. Execute transaction
|
|
92
|
+
let current_version = self.current_version.lock().unwrap().clone();
|
|
93
|
+
let transition = self.executor.execute_transaction(
|
|
94
|
+
transaction_id.clone(),
|
|
95
|
+
current_version,
|
|
96
|
+
operations.clone(),
|
|
97
|
+
consistency_contract,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// 4. Validate transition succeeded
|
|
101
|
+
if !transition.is_successful() {
|
|
102
|
+
return Err(TxError::InvalidState("Transaction execution failed".to_string()));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 5. Persist operations to operation log
|
|
106
|
+
for op in &transition.operations {
|
|
107
|
+
self.operation_log
|
|
108
|
+
.append(op.clone())
|
|
109
|
+
.map_err(|e| TxError::PersistenceFailure(e))?;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 6. Persist transition to state transition store
|
|
113
|
+
let store_index = self.transition_store
|
|
114
|
+
.record_transition(transition.clone())
|
|
115
|
+
.map_err(|e| TxError::PersistenceFailure(e))?;
|
|
116
|
+
|
|
117
|
+
// 7. Update current version
|
|
118
|
+
{
|
|
119
|
+
let mut current = self.current_version.lock().unwrap();
|
|
120
|
+
*current = transition.to_version.clone();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 8. Retrieve the recorded transition
|
|
124
|
+
let record = self.transition_store
|
|
125
|
+
.get_by_index(store_index)
|
|
126
|
+
.ok_or_else(|| TxError::PersistenceFailure("Failed to retrieve recorded transition".to_string()))?;
|
|
127
|
+
|
|
128
|
+
Ok(record)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Get a recorded operation by ID
|
|
132
|
+
pub fn get_operation(&self, operation_id: &OperationId) -> Option<OperationLogEntry> {
|
|
133
|
+
self.operation_log.get_by_id(operation_id)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Get a recorded transition by transaction ID
|
|
137
|
+
pub fn get_transition(&self, transaction_id: &str) -> Option<StateTransitionRecord> {
|
|
138
|
+
self.transition_store.get_by_transaction_id(transaction_id)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Get the operation log size
|
|
142
|
+
pub fn operation_log_size(&self) -> u64 {
|
|
143
|
+
self.operation_log.size()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// Get the transition store size
|
|
147
|
+
pub fn transition_store_size(&self) -> u64 {
|
|
148
|
+
self.transition_store.size()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// Rebuild state from operation log (for recovery)
|
|
152
|
+
/// Executes all operations in order to verify state integrity
|
|
153
|
+
pub fn rebuild_from_operation_log(&mut self) -> TxResult<StateVersion> {
|
|
154
|
+
let all_ops = self.operation_log.iter();
|
|
155
|
+
|
|
156
|
+
if all_ops.is_empty() {
|
|
157
|
+
return Ok(self.current_state_version());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let mut current_version = StateVersion::new(VectorClock::new(), "genesis".to_string());
|
|
161
|
+
|
|
162
|
+
for entry in all_ops {
|
|
163
|
+
let transition = self.executor.execute_transaction(
|
|
164
|
+
entry.operation.transaction_id.clone().unwrap_or_default(),
|
|
165
|
+
current_version.clone(),
|
|
166
|
+
vec![entry.operation.clone()],
|
|
167
|
+
ConsistencyContract::local(),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
if !transition.is_successful() {
|
|
171
|
+
return Err(TxError::InvalidState(
|
|
172
|
+
"Failed to rebuild state from operation log".to_string(),
|
|
173
|
+
));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
current_version = transition.to_version;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
{
|
|
180
|
+
let mut current = self.current_version.lock().unwrap();
|
|
181
|
+
*current = current_version.clone();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
Ok(current_version)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// Get the chain of state transitions from a starting version
|
|
188
|
+
pub fn get_transition_chain_from(&self, from_state_hash: &str) -> Vec<StateTransitionRecord> {
|
|
189
|
+
self.transition_store.get_transition_chain(from_state_hash)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Validate preconditions for an operation
|
|
193
|
+
/// (Simple string matching - production would have actual condition evaluation)
|
|
194
|
+
pub fn validate_preconditions(
|
|
195
|
+
&self,
|
|
196
|
+
operation: &Operation,
|
|
197
|
+
) -> TxResult<()> {
|
|
198
|
+
if operation.preconditions.is_empty() {
|
|
199
|
+
return Ok(());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Placeholder: In production, would evaluate actual state conditions
|
|
203
|
+
// For now, just verify preconditions are syntactically valid
|
|
204
|
+
for (condition_name, condition) in &operation.preconditions {
|
|
205
|
+
if condition.is_empty() {
|
|
206
|
+
return Err(TxError::PreconditionFailed(format!(
|
|
207
|
+
"Empty condition: {}",
|
|
208
|
+
condition_name
|
|
209
|
+
)));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
Ok(())
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#[cfg(test)]
|
|
218
|
+
mod tests {
|
|
219
|
+
use super::*;
|
|
220
|
+
use crate::transactions::OperationCommand;
|
|
221
|
+
use std::collections::HashMap;
|
|
222
|
+
|
|
223
|
+
#[test]
|
|
224
|
+
fn test_create_transaction_manager() {
|
|
225
|
+
let mut vc = VectorClock::new();
|
|
226
|
+
vc.increment("node1");
|
|
227
|
+
let version = StateVersion::new(vc, "hash0".to_string());
|
|
228
|
+
|
|
229
|
+
let manager = TransactionManager::new("node1".to_string(), version);
|
|
230
|
+
|
|
231
|
+
assert_eq!(manager.operation_log_size(), 0);
|
|
232
|
+
assert_eq!(manager.transition_store_size(), 0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
#[test]
|
|
236
|
+
fn test_execute_and_commit_single_operation() {
|
|
237
|
+
let mut vc = VectorClock::new();
|
|
238
|
+
vc.increment("node1");
|
|
239
|
+
let version = StateVersion::new(vc, "hash0".to_string());
|
|
240
|
+
|
|
241
|
+
let mut manager = TransactionManager::new("node1".to_string(), version);
|
|
242
|
+
|
|
243
|
+
let op = Operation::new(
|
|
244
|
+
OperationId::new("node1".to_string(), 1),
|
|
245
|
+
manager.current_state_version(),
|
|
246
|
+
"Update".to_string(),
|
|
247
|
+
OperationCommand {
|
|
248
|
+
op_type: "UpdateField".to_string(),
|
|
249
|
+
collection: "test".to_string(),
|
|
250
|
+
record_id: "rec1".to_string(),
|
|
251
|
+
fields: {
|
|
252
|
+
let mut m = HashMap::new();
|
|
253
|
+
m.insert("field".to_string(), serde_json::json!("value"));
|
|
254
|
+
m
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
"user".to_string(),
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
let result = manager.execute_and_commit(
|
|
261
|
+
"txn1".to_string(),
|
|
262
|
+
vec![op],
|
|
263
|
+
ConsistencyContract::local(),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
assert!(result.is_ok());
|
|
267
|
+
assert_eq!(manager.operation_log_size(), 1);
|
|
268
|
+
assert_eq!(manager.transition_store_size(), 1);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
#[test]
|
|
272
|
+
fn test_duplicate_operation_rejection() {
|
|
273
|
+
let mut vc = VectorClock::new();
|
|
274
|
+
vc.increment("node1");
|
|
275
|
+
let version = StateVersion::new(vc, "hash0".to_string());
|
|
276
|
+
|
|
277
|
+
let mut manager = TransactionManager::new("node1".to_string(), version);
|
|
278
|
+
|
|
279
|
+
let op = Operation::new(
|
|
280
|
+
OperationId::new("node1".to_string(), 1),
|
|
281
|
+
manager.current_state_version(),
|
|
282
|
+
"Update".to_string(),
|
|
283
|
+
OperationCommand {
|
|
284
|
+
op_type: "UpdateField".to_string(),
|
|
285
|
+
collection: "test".to_string(),
|
|
286
|
+
record_id: "rec1".to_string(),
|
|
287
|
+
fields: {
|
|
288
|
+
let mut m = HashMap::new();
|
|
289
|
+
m.insert("field".to_string(), serde_json::json!("value"));
|
|
290
|
+
m
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
"user".to_string(),
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
let _ = manager.execute_and_commit(
|
|
297
|
+
"txn1".to_string(),
|
|
298
|
+
vec![op.clone()],
|
|
299
|
+
ConsistencyContract::local(),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
let result = manager.execute_and_commit(
|
|
303
|
+
"txn2".to_string(),
|
|
304
|
+
vec![op],
|
|
305
|
+
ConsistencyContract::local(),
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
assert!(result.is_err());
|
|
309
|
+
assert!(result.unwrap_err().to_string().contains("already executed"));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
#[test]
|
|
313
|
+
fn test_get_operation_after_commit() {
|
|
314
|
+
let mut vc = VectorClock::new();
|
|
315
|
+
vc.increment("node1");
|
|
316
|
+
let version = StateVersion::new(vc, "hash0".to_string());
|
|
317
|
+
|
|
318
|
+
let mut manager = TransactionManager::new("node1".to_string(), version);
|
|
319
|
+
|
|
320
|
+
let op_id = OperationId::new("node1".to_string(), 1);
|
|
321
|
+
let op = Operation::new(
|
|
322
|
+
op_id.clone(),
|
|
323
|
+
manager.current_state_version(),
|
|
324
|
+
"Update".to_string(),
|
|
325
|
+
OperationCommand {
|
|
326
|
+
op_type: "UpdateField".to_string(),
|
|
327
|
+
collection: "test".to_string(),
|
|
328
|
+
record_id: "rec1".to_string(),
|
|
329
|
+
fields: {
|
|
330
|
+
let mut m = HashMap::new();
|
|
331
|
+
m.insert("field".to_string(), serde_json::json!("value"));
|
|
332
|
+
m
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
"user".to_string(),
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
let _ = manager.execute_and_commit(
|
|
339
|
+
"txn1".to_string(),
|
|
340
|
+
vec![op],
|
|
341
|
+
ConsistencyContract::local(),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
let retrieved = manager.get_operation(&op_id);
|
|
345
|
+
assert!(retrieved.is_some());
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#[test]
|
|
349
|
+
fn test_get_transition_after_commit() {
|
|
350
|
+
let mut vc = VectorClock::new();
|
|
351
|
+
vc.increment("node1");
|
|
352
|
+
let version = StateVersion::new(vc, "hash0".to_string());
|
|
353
|
+
|
|
354
|
+
let mut manager = TransactionManager::new("node1".to_string(), version);
|
|
355
|
+
|
|
356
|
+
let op = Operation::new(
|
|
357
|
+
OperationId::new("node1".to_string(), 1),
|
|
358
|
+
manager.current_state_version(),
|
|
359
|
+
"Update".to_string(),
|
|
360
|
+
OperationCommand {
|
|
361
|
+
op_type: "UpdateField".to_string(),
|
|
362
|
+
collection: "test".to_string(),
|
|
363
|
+
record_id: "rec1".to_string(),
|
|
364
|
+
fields: {
|
|
365
|
+
let mut m = HashMap::new();
|
|
366
|
+
m.insert("field".to_string(), serde_json::json!("value"));
|
|
367
|
+
m
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
"user".to_string(),
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
let _ = manager.execute_and_commit(
|
|
374
|
+
"txn1".to_string(),
|
|
375
|
+
vec![op],
|
|
376
|
+
ConsistencyContract::local(),
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
let transition = manager.get_transition("txn1");
|
|
380
|
+
assert!(transition.is_some());
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
#[test]
|
|
384
|
+
fn test_precondition_validation() {
|
|
385
|
+
let mut vc = VectorClock::new();
|
|
386
|
+
vc.increment("node1");
|
|
387
|
+
let version = StateVersion::new(vc, "hash0".to_string());
|
|
388
|
+
|
|
389
|
+
let manager = TransactionManager::new("node1".to_string(), version);
|
|
390
|
+
|
|
391
|
+
let mut op = Operation::new(
|
|
392
|
+
OperationId::new("node1".to_string(), 1),
|
|
393
|
+
manager.current_state_version(),
|
|
394
|
+
"Update".to_string(),
|
|
395
|
+
OperationCommand {
|
|
396
|
+
op_type: "UpdateField".to_string(),
|
|
397
|
+
collection: "test".to_string(),
|
|
398
|
+
record_id: "rec1".to_string(),
|
|
399
|
+
fields: {
|
|
400
|
+
let mut m = HashMap::new();
|
|
401
|
+
m.insert("field".to_string(), serde_json::json!("value"));
|
|
402
|
+
m
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
"user".to_string(),
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
op = op.with_preconditions(vec![("count".to_string(), "count >= 2".to_string())]);
|
|
409
|
+
|
|
410
|
+
let result = manager.validate_preconditions(&op);
|
|
411
|
+
assert!(result.is_ok());
|
|
412
|
+
|
|
413
|
+
let mut bad_op = Operation::new(
|
|
414
|
+
OperationId::new("node1".to_string(), 2),
|
|
415
|
+
manager.current_state_version(),
|
|
416
|
+
"Update".to_string(),
|
|
417
|
+
OperationCommand {
|
|
418
|
+
op_type: "UpdateField".to_string(),
|
|
419
|
+
collection: "test".to_string(),
|
|
420
|
+
record_id: "rec2".to_string(),
|
|
421
|
+
fields: {
|
|
422
|
+
let mut m = HashMap::new();
|
|
423
|
+
m.insert("field".to_string(), serde_json::json!("value"));
|
|
424
|
+
m
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
"user".to_string(),
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
bad_op = bad_op.with_preconditions(vec![("invalid".to_string(), "".to_string())]);
|
|
431
|
+
|
|
432
|
+
let bad_result = manager.validate_preconditions(&bad_op);
|
|
433
|
+
assert!(bad_result.is_err());
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
#[test]
|
|
437
|
+
fn test_current_state_version_tracking() {
|
|
438
|
+
let mut vc = VectorClock::new();
|
|
439
|
+
vc.increment("node1");
|
|
440
|
+
let initial_version = StateVersion::new(vc, "hash0".to_string());
|
|
441
|
+
let initial_hash = initial_version.state_hash.clone();
|
|
442
|
+
|
|
443
|
+
let mut manager = TransactionManager::new("node1".to_string(), initial_version);
|
|
444
|
+
|
|
445
|
+
assert_eq!(manager.current_state_version().state_hash, initial_hash);
|
|
446
|
+
|
|
447
|
+
let op = Operation::new(
|
|
448
|
+
OperationId::new("node1".to_string(), 1),
|
|
449
|
+
manager.current_state_version(),
|
|
450
|
+
"Update".to_string(),
|
|
451
|
+
OperationCommand {
|
|
452
|
+
op_type: "UpdateField".to_string(),
|
|
453
|
+
collection: "test".to_string(),
|
|
454
|
+
record_id: "rec1".to_string(),
|
|
455
|
+
fields: {
|
|
456
|
+
let mut m = HashMap::new();
|
|
457
|
+
m.insert("field".to_string(), serde_json::json!("value"));
|
|
458
|
+
m
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
"user".to_string(),
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
let _ = manager.execute_and_commit(
|
|
465
|
+
"txn1".to_string(),
|
|
466
|
+
vec![op],
|
|
467
|
+
ConsistencyContract::local(),
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
let new_version = manager.current_state_version();
|
|
471
|
+
assert_ne!(new_version.state_hash, initial_hash);
|
|
472
|
+
}
|
|
473
|
+
}
|