@feltdb/core 0.4.12 → 0.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands.js +42 -25
- package/dist/cli/index.js +1 -1
- package/dist/create/cli.js +39 -4
- package/dist/create/create.js +30 -18
- package/dist/create/docker-compose-generator.js +68 -11
- 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/ConflictExplorer.d.ts.map +1 -1
- package/dist/studio/components/index.js +1 -1
- package/dist/studio/{components-Q0Nx6gLE.js → components-9kDSWiGL.js} +163 -47
- package/dist/studio/index.js +31 -28
- package/dist/studio-app/assets/{index-BHOmLZF-.js → index-DVdvmf69.js} +9 -9
- package/dist/studio-app/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/// Crash Injection Testing Framework
|
|
2
|
+
///
|
|
3
|
+
/// Simulates crashes at persistence boundaries to verify recovery behavior.
|
|
4
|
+
/// Tests that recovery always produces valid state (pre-crash or post-crash, never partial).
|
|
5
|
+
|
|
6
|
+
use crate::convergence::VectorClock;
|
|
7
|
+
use crate::operation_log::MemoryOperationLog;
|
|
8
|
+
use crate::state_hash::{CanonicalState, StateHash};
|
|
9
|
+
use crate::state_transition_store::MemoryStateTransitionStore;
|
|
10
|
+
use crate::transactions::*;
|
|
11
|
+
use std::collections::HashMap;
|
|
12
|
+
|
|
13
|
+
/// Point in the transaction pipeline where crash can occur
|
|
14
|
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
15
|
+
pub enum CrashPoint {
|
|
16
|
+
BeforeOperationLogAppend,
|
|
17
|
+
AfterOperationLogAppend,
|
|
18
|
+
BeforeStateTransitionRecord,
|
|
19
|
+
AfterStateTransitionRecord,
|
|
20
|
+
BeforeStateHashUpdate,
|
|
21
|
+
AfterStateHashUpdate,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// Transaction execution checkpoint tracking
|
|
25
|
+
#[derive(Clone, Debug)]
|
|
26
|
+
pub struct ExecutionCheckpoint {
|
|
27
|
+
pub transaction_id: String,
|
|
28
|
+
pub operations_appended: bool,
|
|
29
|
+
pub transition_recorded: bool,
|
|
30
|
+
pub state_hash_updated: bool,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Result of crash recovery attempt
|
|
34
|
+
#[derive(Clone, Debug)]
|
|
35
|
+
pub enum RecoveryResult {
|
|
36
|
+
/// State is in a valid pre-transaction state
|
|
37
|
+
ValidPre {
|
|
38
|
+
checkpoint: ExecutionCheckpoint,
|
|
39
|
+
},
|
|
40
|
+
/// State is in a valid post-transaction state
|
|
41
|
+
ValidPost {
|
|
42
|
+
checkpoint: ExecutionCheckpoint,
|
|
43
|
+
},
|
|
44
|
+
/// State is corrupted or indeterminate
|
|
45
|
+
Invalid {
|
|
46
|
+
reason: String,
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Crash injection test harness
|
|
51
|
+
pub struct CrashInjectionTester {
|
|
52
|
+
operation_log: MemoryOperationLog,
|
|
53
|
+
transition_store: MemoryStateTransitionStore,
|
|
54
|
+
pre_crash_state: CanonicalState,
|
|
55
|
+
post_crash_state: CanonicalState,
|
|
56
|
+
checkpoints: Vec<ExecutionCheckpoint>,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
impl CrashInjectionTester {
|
|
60
|
+
pub fn new() -> Self {
|
|
61
|
+
Self {
|
|
62
|
+
operation_log: MemoryOperationLog::new(),
|
|
63
|
+
transition_store: MemoryStateTransitionStore::new(),
|
|
64
|
+
pre_crash_state: CanonicalState::new(),
|
|
65
|
+
post_crash_state: CanonicalState::new(),
|
|
66
|
+
checkpoints: Vec::new(),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Simulate crash before operation log append
|
|
71
|
+
pub fn crash_before_operation_append(&self, operation: &Operation) -> RecoveryResult {
|
|
72
|
+
assert_eq!(self.operation_log.size(), 0, "Operation should not be logged yet");
|
|
73
|
+
assert_eq!(
|
|
74
|
+
self.transition_store.size(),
|
|
75
|
+
0,
|
|
76
|
+
"Transition should not be recorded yet"
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
RecoveryResult::ValidPre {
|
|
80
|
+
checkpoint: ExecutionCheckpoint {
|
|
81
|
+
transaction_id: operation.transaction_id.clone().unwrap_or_default(),
|
|
82
|
+
operations_appended: false,
|
|
83
|
+
transition_recorded: false,
|
|
84
|
+
state_hash_updated: false,
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Simulate crash after operation log append (before transition)
|
|
90
|
+
pub fn crash_after_operation_append(&self, operation: &Operation) -> RecoveryResult {
|
|
91
|
+
let op_appended = self.operation_log.contains_operation_id(&operation.operation_id);
|
|
92
|
+
|
|
93
|
+
if !op_appended {
|
|
94
|
+
return RecoveryResult::Invalid {
|
|
95
|
+
reason: "Operation should be appended before crash".to_string(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
assert_eq!(
|
|
100
|
+
self.transition_store.size(),
|
|
101
|
+
0,
|
|
102
|
+
"Transition should not be recorded yet"
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
RecoveryResult::ValidPre {
|
|
106
|
+
checkpoint: ExecutionCheckpoint {
|
|
107
|
+
transaction_id: operation.transaction_id.clone().unwrap_or_default(),
|
|
108
|
+
operations_appended: true,
|
|
109
|
+
transition_recorded: false,
|
|
110
|
+
state_hash_updated: false,
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// Simulate crash after state transition recorded
|
|
116
|
+
pub fn crash_after_transition_recorded(&self, transaction_id: &str) -> RecoveryResult {
|
|
117
|
+
let transition = self
|
|
118
|
+
.transition_store
|
|
119
|
+
.get_by_transaction_id(transaction_id);
|
|
120
|
+
|
|
121
|
+
if transition.is_none() {
|
|
122
|
+
return RecoveryResult::Invalid {
|
|
123
|
+
reason: "Transition should be recorded before crash".to_string(),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
RecoveryResult::ValidPost {
|
|
128
|
+
checkpoint: ExecutionCheckpoint {
|
|
129
|
+
transaction_id: transaction_id.to_string(),
|
|
130
|
+
operations_appended: true,
|
|
131
|
+
transition_recorded: true,
|
|
132
|
+
state_hash_updated: true,
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// Record pre-crash state
|
|
138
|
+
pub fn set_pre_crash_state(&mut self, state: CanonicalState) {
|
|
139
|
+
self.pre_crash_state = state;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Record post-crash state (expected state after recovery)
|
|
143
|
+
pub fn set_post_crash_state(&mut self, state: CanonicalState) {
|
|
144
|
+
self.post_crash_state = state;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Verify recovery never produces invalid state
|
|
148
|
+
pub fn verify_no_partial_state(&self) -> bool {
|
|
149
|
+
// No partial operations should exist (all or nothing)
|
|
150
|
+
// No orphaned transitions (operation log and store should be consistent)
|
|
151
|
+
|
|
152
|
+
let operation_log_size = self.operation_log.size();
|
|
153
|
+
let transition_store_size = self.transition_store.size();
|
|
154
|
+
|
|
155
|
+
if operation_log_size > 0 && transition_store_size == 0 {
|
|
156
|
+
return true; // Valid partial: operations logged but transition pending
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if operation_log_size > 0 && transition_store_size > 0 {
|
|
160
|
+
return true; // Valid complete: both logged
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if operation_log_size == 0 && transition_store_size == 0 {
|
|
164
|
+
return true; // Valid empty: nothing executed
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
false // Invalid: orphaned state
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Verify no duplicate operations after recovery
|
|
171
|
+
pub fn verify_no_duplicates(&self) -> bool {
|
|
172
|
+
let all_ops = self.operation_log.iter();
|
|
173
|
+
let mut seen_ids = std::collections::HashSet::new();
|
|
174
|
+
|
|
175
|
+
for entry in all_ops {
|
|
176
|
+
let op_id_key = format!(
|
|
177
|
+
"{}:{}",
|
|
178
|
+
entry.operation_id.node_id, entry.operation_id.sequence
|
|
179
|
+
);
|
|
180
|
+
if seen_ids.contains(&op_id_key) {
|
|
181
|
+
return false; // Duplicate detected
|
|
182
|
+
}
|
|
183
|
+
seen_ids.insert(op_id_key);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
true
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// Verify state lineage is consistent
|
|
190
|
+
pub fn verify_state_lineage(&self) -> bool {
|
|
191
|
+
let all_transitions = self.transition_store.iter();
|
|
192
|
+
|
|
193
|
+
let mut current_hash = "genesis".to_string();
|
|
194
|
+
for record in all_transitions {
|
|
195
|
+
if record.transition.from_version.state_hash != current_hash {
|
|
196
|
+
return false; // Broken lineage
|
|
197
|
+
}
|
|
198
|
+
current_hash = record.transition.to_version.state_hash.clone();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
true
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
impl Default for CrashInjectionTester {
|
|
206
|
+
fn default() -> Self {
|
|
207
|
+
Self::new()
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#[cfg(test)]
|
|
212
|
+
mod tests {
|
|
213
|
+
use super::*;
|
|
214
|
+
|
|
215
|
+
fn create_test_operation(sequence: u64) -> Operation {
|
|
216
|
+
let mut vc = VectorClock::new();
|
|
217
|
+
vc.increment("node1");
|
|
218
|
+
let version = crate::transactions::StateVersion::new(vc, "hash0".to_string());
|
|
219
|
+
|
|
220
|
+
Operation::new(
|
|
221
|
+
OperationId::new("node1".to_string(), sequence),
|
|
222
|
+
version,
|
|
223
|
+
format!("Op{}", sequence),
|
|
224
|
+
OperationCommand {
|
|
225
|
+
op_type: "UpdateField".to_string(),
|
|
226
|
+
collection: "test".to_string(),
|
|
227
|
+
record_id: format!("rec{}", sequence),
|
|
228
|
+
fields: {
|
|
229
|
+
let mut m = HashMap::new();
|
|
230
|
+
m.insert("value".to_string(), serde_json::json!(sequence));
|
|
231
|
+
m
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
"user".to_string(),
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#[test]
|
|
239
|
+
fn test_crash_before_operation_append() {
|
|
240
|
+
let tester = CrashInjectionTester::new();
|
|
241
|
+
let op = create_test_operation(1);
|
|
242
|
+
|
|
243
|
+
let result = tester.crash_before_operation_append(&op);
|
|
244
|
+
|
|
245
|
+
match result {
|
|
246
|
+
RecoveryResult::ValidPre { checkpoint } => {
|
|
247
|
+
assert!(!checkpoint.operations_appended);
|
|
248
|
+
assert!(!checkpoint.transition_recorded);
|
|
249
|
+
}
|
|
250
|
+
_ => panic!("Expected ValidPre"),
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
#[test]
|
|
255
|
+
fn test_verify_no_duplicates() {
|
|
256
|
+
let tester = CrashInjectionTester::new();
|
|
257
|
+
|
|
258
|
+
assert!(tester.verify_no_duplicates());
|
|
259
|
+
|
|
260
|
+
let op = create_test_operation(1);
|
|
261
|
+
tester.operation_log.append(op).unwrap();
|
|
262
|
+
|
|
263
|
+
assert!(tester.verify_no_duplicates());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#[test]
|
|
267
|
+
fn test_verify_no_partial_state_empty() {
|
|
268
|
+
let tester = CrashInjectionTester::new();
|
|
269
|
+
|
|
270
|
+
assert!(tester.verify_no_partial_state());
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#[test]
|
|
274
|
+
fn test_verify_state_lineage_linear_chain() {
|
|
275
|
+
let mut tester = CrashInjectionTester::new();
|
|
276
|
+
|
|
277
|
+
let mut prev_hash = "genesis".to_string();
|
|
278
|
+
for i in 1..=3 {
|
|
279
|
+
let mut vc = VectorClock::new();
|
|
280
|
+
vc.increment("node1");
|
|
281
|
+
|
|
282
|
+
let from_version = crate::transactions::StateVersion::new(
|
|
283
|
+
vc.clone(),
|
|
284
|
+
prev_hash.clone(),
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
vc.increment("node1");
|
|
288
|
+
let to_version = crate::transactions::StateVersion::new(
|
|
289
|
+
vc,
|
|
290
|
+
format!("hash{}", i),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
let op = create_test_operation(i);
|
|
294
|
+
let transition = StateTransition::new(
|
|
295
|
+
from_version,
|
|
296
|
+
vec![op],
|
|
297
|
+
to_version.clone(),
|
|
298
|
+
ConsistencyContract::local(),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
tester.transition_store.record_transition(transition).unwrap();
|
|
302
|
+
prev_hash = format!("hash{}", i);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
assert!(tester.verify_state_lineage());
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
#[test]
|
|
309
|
+
fn test_verify_state_lineage_broken_chain() {
|
|
310
|
+
let mut tester = CrashInjectionTester::new();
|
|
311
|
+
|
|
312
|
+
let mut vc = VectorClock::new();
|
|
313
|
+
vc.increment("node1");
|
|
314
|
+
let from_version1 = crate::transactions::StateVersion::new(vc.clone(), "hash0".to_string());
|
|
315
|
+
|
|
316
|
+
vc.increment("node1");
|
|
317
|
+
let to_version1 = crate::transactions::StateVersion::new(vc.clone(), "hash1".to_string());
|
|
318
|
+
|
|
319
|
+
let op1 = create_test_operation(1);
|
|
320
|
+
let t1 = StateTransition::new(
|
|
321
|
+
from_version1,
|
|
322
|
+
vec![op1],
|
|
323
|
+
to_version1,
|
|
324
|
+
ConsistencyContract::local(),
|
|
325
|
+
);
|
|
326
|
+
tester.transition_store.record_transition(t1).unwrap();
|
|
327
|
+
|
|
328
|
+
let mut vc2 = VectorClock::new();
|
|
329
|
+
vc2.increment("node1");
|
|
330
|
+
let from_version2 = crate::transactions::StateVersion::new(vc2.clone(), "hash_broken".to_string());
|
|
331
|
+
|
|
332
|
+
vc2.increment("node1");
|
|
333
|
+
let to_version2 = crate::transactions::StateVersion::new(vc2, "hash2".to_string());
|
|
334
|
+
|
|
335
|
+
let op2 = create_test_operation(2);
|
|
336
|
+
let t2 = StateTransition::new(
|
|
337
|
+
from_version2,
|
|
338
|
+
vec![op2],
|
|
339
|
+
to_version2,
|
|
340
|
+
ConsistencyContract::local(),
|
|
341
|
+
);
|
|
342
|
+
tester.transition_store.record_transition(t2).unwrap();
|
|
343
|
+
|
|
344
|
+
assert!(!tester.verify_state_lineage());
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
#[test]
|
|
348
|
+
fn test_crash_recovery_idempotence() {
|
|
349
|
+
let mut tester = CrashInjectionTester::new();
|
|
350
|
+
let op = create_test_operation(1);
|
|
351
|
+
|
|
352
|
+
let result1 = tester.crash_before_operation_append(&op);
|
|
353
|
+
let result2 = tester.crash_before_operation_append(&op);
|
|
354
|
+
|
|
355
|
+
match (&result1, &result2) {
|
|
356
|
+
(RecoveryResult::ValidPre { .. }, RecoveryResult::ValidPre { .. }) => {
|
|
357
|
+
// Both should produce same recovery result
|
|
358
|
+
}
|
|
359
|
+
_ => panic!("Expected ValidPre for both"),
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
#[test]
|
|
364
|
+
fn test_crash_after_append_recovery() {
|
|
365
|
+
let tester = CrashInjectionTester::new();
|
|
366
|
+
let op = create_test_operation(1);
|
|
367
|
+
|
|
368
|
+
tester.operation_log.append(op.clone()).unwrap();
|
|
369
|
+
|
|
370
|
+
let result = tester.crash_after_operation_append(&op);
|
|
371
|
+
|
|
372
|
+
match result {
|
|
373
|
+
RecoveryResult::ValidPre { checkpoint } => {
|
|
374
|
+
assert!(checkpoint.operations_appended);
|
|
375
|
+
assert!(!checkpoint.transition_recorded);
|
|
376
|
+
}
|
|
377
|
+
_ => panic!("Expected ValidPre"),
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|