@feltdb/core 0.8.4 → 0.8.6
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/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +165 -0
- package/dist/create/server-source/Cargo.toml +9 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +3 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +44 -44
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +12 -24
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +7 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +13 -10
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +12 -9
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +78 -25
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +16 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +13 -5
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +41 -22
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +33 -15
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +100 -33
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +56 -20
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +116 -41
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +186 -55
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +64 -26
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +46 -15
- package/dist/create/server-source/crates/feltdb/src/admission.rs +8 -15
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +43 -13
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +15 -42
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +65 -19
- package/dist/create/server-source/crates/feltdb/src/application.rs +113 -30
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +475 -140
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +17 -15
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +266 -114
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +25 -7
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +10 -15
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +3 -11
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +14 -5
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +21 -25
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +14 -11
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +103 -22
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +18 -15
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +12 -8
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +1 -5
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +87 -23
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +3 -7
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +10 -9
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +1 -6
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +35 -38
- package/dist/create/server-source/crates/feltdb/src/lib.rs +1648 -46
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +4 -1
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +1 -2
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +107 -30
- package/dist/create/server-source/crates/feltdb/src/observability.rs +19 -6
- package/dist/create/server-source/crates/feltdb/src/operation.rs +39 -0
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +12 -11
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +9 -4
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +65 -18
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +193 -57
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +37 -27
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +38 -10
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +20 -14
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +394 -229
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +8 -6
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +11 -13
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +79 -70
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +3 -3
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +33 -11
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +6 -6
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +14 -56
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +29 -27
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +35 -17
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +8 -2
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +70 -17
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +23 -6
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +12 -3
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +41 -11
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +56 -25
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +701 -245
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +31 -13
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +5 -5
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +48 -18
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +30 -11
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +4 -3
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +36 -10
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +516 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +13 -4
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +222 -0
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +82 -54
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +2 -2
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +1565 -536
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +672 -0
- package/dist/create/server-source/crates/feltdb/src/storage.rs +9 -3
- package/dist/create/server-source/crates/feltdb/src/submission.rs +5 -11
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +24 -35
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +24 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +248 -59
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +17 -20
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +749 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +512 -4
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +3 -4
- package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +132 -0
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +272 -0
- package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +467 -0
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +309 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +445 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +518 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +392 -0
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +436 -220
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +1916 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +1878 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +1862 -0
- package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +278 -0
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +2 -1
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +80 -25
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +5 -8
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +52 -44
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +33 -16
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +22 -7
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +41 -22
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +25 -8
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +9 -6
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +120 -69
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +21 -10
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +1365 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +868 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +221 -0
- package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +417 -0
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +338 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +1039 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +739 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +427 -0
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +3 -3
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +25 -22
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +1799 -0
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +1316 -0
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +53 -61
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +156 -61
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +255 -0
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +249 -0
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +994 -0
- package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +442 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +16 -1
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +695 -47
- package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +21 -0
- package/dist/studio-app/assets/{feltdb_wasm-CVQWgXO-.js → feltdb_wasm-C1VhI-U5.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-C8HXbAXb.wasm +0 -0
- package/dist/studio-app/assets/{index-DwgNAIIX.js → index-Bbos1m2U.js} +1 -1
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workload.d.ts +2 -1
- package/dist/workload.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-CNVpvaZV.wasm +0 -0
|
@@ -61,9 +61,17 @@ mod tests {
|
|
|
61
61
|
/// Convergence validation result
|
|
62
62
|
#[derive(Debug, PartialEq)]
|
|
63
63
|
enum ConvergenceResult {
|
|
64
|
-
Converged {
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
Converged {
|
|
65
|
+
common_hash: String,
|
|
66
|
+
rounds_needed: usize,
|
|
67
|
+
},
|
|
68
|
+
PartialConvergence {
|
|
69
|
+
converged_nodes: Vec<NodeId>,
|
|
70
|
+
diverged_nodes: Vec<NodeId>,
|
|
71
|
+
},
|
|
72
|
+
Diverged {
|
|
73
|
+
hashes: HashMap<NodeId, String>,
|
|
74
|
+
},
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
/// Execute operations on a replica set under adversarial transport
|
|
@@ -80,7 +88,10 @@ mod tests {
|
|
|
80
88
|
for (op_idx, op) in operations.iter().enumerate() {
|
|
81
89
|
// Originating node applies operation
|
|
82
90
|
let origin = nodes[op_idx % nodes.len()];
|
|
83
|
-
replicas
|
|
91
|
+
replicas
|
|
92
|
+
.get_mut(&origin)
|
|
93
|
+
.unwrap()
|
|
94
|
+
.apply_operation(op.clone());
|
|
84
95
|
|
|
85
96
|
// Broadcast to other nodes
|
|
86
97
|
for &target in nodes.iter().filter(|n| **n != origin) {
|
|
@@ -106,8 +117,10 @@ mod tests {
|
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
// Check convergence: all nodes should have same hash
|
|
109
|
-
let hashes: HashMap<NodeId, String> =
|
|
110
|
-
|
|
120
|
+
let hashes: HashMap<NodeId, String> = replicas
|
|
121
|
+
.iter()
|
|
122
|
+
.map(|(n, r)| (*n, r.hash().to_string()))
|
|
123
|
+
.collect();
|
|
111
124
|
|
|
112
125
|
let unique_hashes: HashSet<&String> = hashes.values().collect();
|
|
113
126
|
|
|
@@ -124,15 +137,16 @@ mod tests {
|
|
|
124
137
|
#[test]
|
|
125
138
|
fn test_convergence_validator_commutative_ops_mesh() {
|
|
126
139
|
let nodes = vec![NodeId(1), NodeId(2), NodeId(3)];
|
|
127
|
-
let ops = vec![
|
|
140
|
+
let ops = vec![
|
|
141
|
+
"add:1".to_string(),
|
|
142
|
+
"add:2".to_string(),
|
|
143
|
+
"add:3".to_string(),
|
|
144
|
+
];
|
|
128
145
|
let faults = vec![];
|
|
129
146
|
|
|
130
147
|
let result = validate_convergence(nodes, ops, faults);
|
|
131
148
|
|
|
132
|
-
assert!(matches!(
|
|
133
|
-
result,
|
|
134
|
-
ConvergenceResult::Converged { .. }
|
|
135
|
-
));
|
|
149
|
+
assert!(matches!(result, ConvergenceResult::Converged { .. }));
|
|
136
150
|
println!("✅ Test 1: Commutative operations converged");
|
|
137
151
|
}
|
|
138
152
|
|
|
@@ -150,10 +164,7 @@ mod tests {
|
|
|
150
164
|
let result = validate_convergence(nodes, ops, faults);
|
|
151
165
|
|
|
152
166
|
// Last write wins should be deterministic
|
|
153
|
-
assert!(matches!(
|
|
154
|
-
result,
|
|
155
|
-
ConvergenceResult::Converged { .. }
|
|
156
|
-
));
|
|
167
|
+
assert!(matches!(result, ConvergenceResult::Converged { .. }));
|
|
157
168
|
println!("✅ Test 2: Deterministic merge operations converged");
|
|
158
169
|
}
|
|
159
170
|
|
|
@@ -190,10 +201,7 @@ mod tests {
|
|
|
190
201
|
|
|
191
202
|
let result = validate_convergence(nodes, ops, faults);
|
|
192
203
|
|
|
193
|
-
assert!(matches!(
|
|
194
|
-
result,
|
|
195
|
-
ConvergenceResult::Converged { .. }
|
|
196
|
-
));
|
|
204
|
+
assert!(matches!(result, ConvergenceResult::Converged { .. }));
|
|
197
205
|
println!("✅ Test 3: Convergence after partition healing verified");
|
|
198
206
|
}
|
|
199
207
|
|
|
@@ -212,10 +220,7 @@ mod tests {
|
|
|
212
220
|
let result = validate_convergence(nodes, ops, faults);
|
|
213
221
|
|
|
214
222
|
// In correct implementation, should converge
|
|
215
|
-
assert!(matches!(
|
|
216
|
-
result,
|
|
217
|
-
ConvergenceResult::Converged { .. }
|
|
218
|
-
));
|
|
223
|
+
assert!(matches!(result, ConvergenceResult::Converged { .. }));
|
|
219
224
|
println!("✅ Test 4: Convergence validator detection verified");
|
|
220
225
|
}
|
|
221
226
|
|
|
@@ -306,10 +311,7 @@ mod tests {
|
|
|
306
311
|
let result = validate_convergence(nodes, operations, faults);
|
|
307
312
|
|
|
308
313
|
// Should converge despite many faults
|
|
309
|
-
assert!(matches!(
|
|
310
|
-
result,
|
|
311
|
-
ConvergenceResult::Converged { .. }
|
|
312
|
-
));
|
|
314
|
+
assert!(matches!(result, ConvergenceResult::Converged { .. }));
|
|
313
315
|
println!("✅ Test 7: No silent divergence under faults verified");
|
|
314
316
|
}
|
|
315
317
|
|
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
#[cfg(test)]
|
|
7
7
|
mod tests {
|
|
8
8
|
use crate::causal_dependency_barrier::CausalDependencyBarrier;
|
|
9
|
-
use crate::consistency_contract::{
|
|
9
|
+
use crate::consistency_contract::{CommutativeVerificationResult, ConsistencyContractEnforcer};
|
|
10
10
|
use crate::convergence::VectorClock;
|
|
11
11
|
use crate::persistence_reality::{
|
|
12
|
-
DurablePersistenceLayer, PersistenceCrashPoint,
|
|
13
|
-
RecoveryResult,
|
|
12
|
+
DurablePersistenceLayer, PersistenceContractVerifier, PersistenceCrashPoint, RecoveryResult,
|
|
14
13
|
};
|
|
15
14
|
|
|
16
15
|
#[test]
|
|
@@ -102,11 +101,15 @@ mod tests {
|
|
|
102
101
|
let recovery = wal.recover();
|
|
103
102
|
|
|
104
103
|
// Partition A's operation should survive
|
|
105
|
-
assert!(recovery
|
|
104
|
+
assert!(recovery
|
|
105
|
+
.recovered_operations
|
|
106
|
+
.contains(&"partition_a_op1".to_string()));
|
|
106
107
|
|
|
107
108
|
// Partition B's operation was corrupted during crash
|
|
108
109
|
// Should not silently appear in recovery
|
|
109
|
-
assert!(!recovery
|
|
110
|
+
assert!(!recovery
|
|
111
|
+
.recovered_operations
|
|
112
|
+
.contains(&"partition_b_op1".to_string()));
|
|
110
113
|
|
|
111
114
|
println!("✅ Gates 5+7: Partition persistence verified");
|
|
112
115
|
}
|
|
@@ -196,7 +199,9 @@ mod tests {
|
|
|
196
199
|
);
|
|
197
200
|
|
|
198
201
|
let recovery = wal.recover();
|
|
199
|
-
assert!(recovery
|
|
202
|
+
assert!(recovery
|
|
203
|
+
.recovered_operations
|
|
204
|
+
.contains(&"exactly_once_op".to_string()));
|
|
200
205
|
|
|
201
206
|
// Critical: After recovery, duplicate application must be prevented
|
|
202
207
|
let result = executor.apply_with_crash("exactly_once_op", "rec1", 99);
|
|
@@ -213,7 +218,10 @@ mod tests {
|
|
|
213
218
|
|
|
214
219
|
// Gate 6: Record commutative operation
|
|
215
220
|
let result = enforcer.verify_commutative("Increment", &[5, 10, 15]);
|
|
216
|
-
assert!(matches!(
|
|
221
|
+
assert!(matches!(
|
|
222
|
+
result,
|
|
223
|
+
CommutativeVerificationResult::Satisfied { .. }
|
|
224
|
+
));
|
|
217
225
|
|
|
218
226
|
// Gate 7: Simulate persistence
|
|
219
227
|
let mut wal = DurablePersistenceLayer::new();
|
|
@@ -306,27 +314,37 @@ mod tests {
|
|
|
306
314
|
|
|
307
315
|
let crash_points = vec![
|
|
308
316
|
("BeforeWrite", PersistenceCrashPoint::BeforeWrite),
|
|
309
|
-
(
|
|
317
|
+
(
|
|
318
|
+
"AfterBufferBeforeFsync",
|
|
319
|
+
PersistenceCrashPoint::AfterBufferBeforeFsync,
|
|
320
|
+
),
|
|
310
321
|
("DuringFsync", PersistenceCrashPoint::DuringFsync),
|
|
311
|
-
(
|
|
312
|
-
|
|
322
|
+
(
|
|
323
|
+
"AfterFsyncBeforeReturn",
|
|
324
|
+
PersistenceCrashPoint::AfterFsyncBeforeReturn,
|
|
325
|
+
),
|
|
326
|
+
(
|
|
327
|
+
"AfterWriteReturns",
|
|
328
|
+
PersistenceCrashPoint::AfterWriteReturns,
|
|
329
|
+
),
|
|
313
330
|
];
|
|
314
331
|
|
|
315
332
|
let mut verifier = PersistenceContractVerifier::new();
|
|
316
333
|
|
|
317
334
|
for (name, crash_point) in crash_points {
|
|
318
335
|
let mut wal = DurablePersistenceLayer::new();
|
|
319
|
-
let result = wal.write_with_crash(
|
|
320
|
-
format!("op_{}", name),
|
|
321
|
-
vec![1, 2, 3],
|
|
322
|
-
crash_point,
|
|
323
|
-
);
|
|
336
|
+
let result = wal.write_with_crash(format!("op_{}", name), vec![1, 2, 3], crash_point);
|
|
324
337
|
|
|
325
338
|
let recovery = wal.recover();
|
|
326
339
|
|
|
327
340
|
// Only count as "written" if actually fsynced
|
|
328
|
-
let was_fsynced = matches!(
|
|
329
|
-
|
|
341
|
+
let was_fsynced = matches!(
|
|
342
|
+
result,
|
|
343
|
+
crate::persistence_reality::WriteResult::Success { .. }
|
|
344
|
+
) || matches!(
|
|
345
|
+
result,
|
|
346
|
+
crate::persistence_reality::WriteResult::Crashed { fsynced: true, .. }
|
|
347
|
+
);
|
|
330
348
|
|
|
331
349
|
let written_count = if was_fsynced { 1 } else { 0 };
|
|
332
350
|
let recovered_count = recovery.recovered_operations.len();
|
|
@@ -10,7 +10,9 @@ mod tests {
|
|
|
10
10
|
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
11
11
|
};
|
|
12
12
|
use crate::state_hash::StateHash;
|
|
13
|
-
use crate::transactions::{
|
|
13
|
+
use crate::transactions::{
|
|
14
|
+
ConsistencyContract, Operation, OperationCommand, OperationId, StateVersion,
|
|
15
|
+
};
|
|
14
16
|
use serde_json::json;
|
|
15
17
|
use std::collections::HashMap;
|
|
16
18
|
use std::sync::Arc;
|
|
@@ -131,7 +133,11 @@ mod tests {
|
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
/// Replicate a single operation from leader to all followers
|
|
134
|
-
fn replicate_operation(
|
|
136
|
+
fn replicate_operation(
|
|
137
|
+
&self,
|
|
138
|
+
envelope: TransactionEnvelope,
|
|
139
|
+
seq: u64,
|
|
140
|
+
) -> Result<(), String> {
|
|
135
141
|
// Create replication message
|
|
136
142
|
let message = ReplicationMessage::new(
|
|
137
143
|
envelope,
|
|
@@ -19,7 +19,9 @@ mod tests {
|
|
|
19
19
|
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
20
20
|
};
|
|
21
21
|
use crate::state_hash::StateHash;
|
|
22
|
-
use crate::transactions::{
|
|
22
|
+
use crate::transactions::{
|
|
23
|
+
ConsistencyContract, Operation, OperationCommand, OperationId, StateVersion,
|
|
24
|
+
};
|
|
23
25
|
use serde_json::json;
|
|
24
26
|
use std::collections::HashMap;
|
|
25
27
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
@@ -161,15 +163,24 @@ mod tests {
|
|
|
161
163
|
follower1.recover();
|
|
162
164
|
|
|
163
165
|
// Follower1 can submit operations again
|
|
164
|
-
follower1
|
|
166
|
+
follower1
|
|
167
|
+
.submit_operation(50)
|
|
168
|
+
.await
|
|
169
|
+
.expect("recovery submit");
|
|
165
170
|
|
|
166
171
|
let leader_ops = leader.operations_applied().await;
|
|
167
172
|
let f1_ops = follower1.operations_applied().await;
|
|
168
173
|
let f2_ops = follower2.operations_applied().await;
|
|
169
174
|
|
|
170
|
-
println!(
|
|
175
|
+
println!(
|
|
176
|
+
"Leader: {}, Follower1: {}, Follower2: {}",
|
|
177
|
+
leader_ops, f1_ops, f2_ops
|
|
178
|
+
);
|
|
171
179
|
assert_eq!(leader_ops, 50, "Leader should have 50 operations");
|
|
172
|
-
assert_eq!(
|
|
180
|
+
assert_eq!(
|
|
181
|
+
f1_ops, 1,
|
|
182
|
+
"Follower1 should have 1 operation after recovery"
|
|
183
|
+
);
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
/// Phase 2a Test 2: Leader crash and recovery
|
|
@@ -192,7 +203,10 @@ mod tests {
|
|
|
192
203
|
}
|
|
193
204
|
|
|
194
205
|
let pre_crash_leader_ops = leader.operations_applied().await;
|
|
195
|
-
assert_eq!(
|
|
206
|
+
assert_eq!(
|
|
207
|
+
pre_crash_leader_ops, 25,
|
|
208
|
+
"Leader should have 25 pre-crash ops"
|
|
209
|
+
);
|
|
196
210
|
|
|
197
211
|
// Crash leader
|
|
198
212
|
leader.crash();
|
|
@@ -204,12 +218,18 @@ mod tests {
|
|
|
204
218
|
|
|
205
219
|
// Leader can submit operations again
|
|
206
220
|
for i in 25..50 {
|
|
207
|
-
leader
|
|
221
|
+
leader
|
|
222
|
+
.submit_operation(i)
|
|
223
|
+
.await
|
|
224
|
+
.expect("leader post-recovery submit");
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
let post_recovery_leader_ops = leader.operations_applied().await;
|
|
211
228
|
println!("Post-recovery leader ops: {}", post_recovery_leader_ops);
|
|
212
|
-
assert_eq!(
|
|
229
|
+
assert_eq!(
|
|
230
|
+
post_recovery_leader_ops, 50,
|
|
231
|
+
"Leader should recover and accept new ops"
|
|
232
|
+
);
|
|
213
233
|
}
|
|
214
234
|
|
|
215
235
|
/// Phase 2a Test 3: Concurrent crashes (leader + 1 follower)
|
|
@@ -236,7 +256,10 @@ mod tests {
|
|
|
236
256
|
follower1.crash();
|
|
237
257
|
|
|
238
258
|
// Only follower2 can operate
|
|
239
|
-
follower2
|
|
259
|
+
follower2
|
|
260
|
+
.submit_operation(30)
|
|
261
|
+
.await
|
|
262
|
+
.expect("follower2 operates during dual crash");
|
|
240
263
|
|
|
241
264
|
// Recover both
|
|
242
265
|
leader.recover();
|
|
@@ -244,14 +267,23 @@ mod tests {
|
|
|
244
267
|
|
|
245
268
|
// All can operate again
|
|
246
269
|
leader.submit_operation(30).await.expect("leader recovery");
|
|
247
|
-
follower1
|
|
248
|
-
|
|
270
|
+
follower1
|
|
271
|
+
.submit_operation(31)
|
|
272
|
+
.await
|
|
273
|
+
.expect("follower1 recovery");
|
|
274
|
+
follower2
|
|
275
|
+
.submit_operation(32)
|
|
276
|
+
.await
|
|
277
|
+
.expect("follower2 continues");
|
|
249
278
|
|
|
250
279
|
let leader_ops = leader.operations_applied().await;
|
|
251
280
|
let f1_ops = follower1.operations_applied().await;
|
|
252
281
|
let f2_ops = follower2.operations_applied().await;
|
|
253
282
|
|
|
254
|
-
println!(
|
|
283
|
+
println!(
|
|
284
|
+
"Post dual-crash - Leader: {}, F1: {}, F2: {}",
|
|
285
|
+
leader_ops, f1_ops, f2_ops
|
|
286
|
+
);
|
|
255
287
|
assert_eq!(leader_ops, 31, "Leader should recover");
|
|
256
288
|
assert_eq!(f1_ops, 1, "Follower1 should recover");
|
|
257
289
|
assert_eq!(f2_ops, 2, "Follower2 should continue");
|
|
@@ -281,13 +313,22 @@ mod tests {
|
|
|
281
313
|
|
|
282
314
|
// Recover
|
|
283
315
|
leader.recover();
|
|
284
|
-
leader
|
|
316
|
+
leader
|
|
317
|
+
.submit_operation(cycle * 100)
|
|
318
|
+
.await
|
|
319
|
+
.expect("recovery submit");
|
|
285
320
|
|
|
286
321
|
let post_recovery_ops = leader.operations_applied().await;
|
|
287
|
-
println!(
|
|
322
|
+
println!(
|
|
323
|
+
"Cycle {}: pre={}, post={}",
|
|
324
|
+
cycle, pre_crash_ops, post_recovery_ops
|
|
325
|
+
);
|
|
288
326
|
|
|
289
327
|
// Should maintain state across crashes
|
|
290
|
-
assert!(
|
|
328
|
+
assert!(
|
|
329
|
+
post_recovery_ops > 0,
|
|
330
|
+
"Should have operations after recovery"
|
|
331
|
+
);
|
|
291
332
|
}
|
|
292
333
|
}
|
|
293
334
|
|
|
@@ -322,7 +363,10 @@ mod tests {
|
|
|
322
363
|
|
|
323
364
|
// Finish submitting
|
|
324
365
|
for i in 10..20 {
|
|
325
|
-
leader
|
|
366
|
+
leader
|
|
367
|
+
.submit_operation(i)
|
|
368
|
+
.await
|
|
369
|
+
.expect("post-recovery submit");
|
|
326
370
|
}
|
|
327
371
|
|
|
328
372
|
let final_ops = leader.operations_applied().await;
|
|
@@ -20,7 +20,9 @@ mod tests {
|
|
|
20
20
|
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
21
21
|
};
|
|
22
22
|
use crate::state_hash::StateHash;
|
|
23
|
-
use crate::transactions::{
|
|
23
|
+
use crate::transactions::{
|
|
24
|
+
ConsistencyContract, Operation, OperationCommand, OperationId, StateVersion,
|
|
25
|
+
};
|
|
24
26
|
use serde_json::json;
|
|
25
27
|
use std::collections::HashMap;
|
|
26
28
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
@@ -136,25 +138,49 @@ mod tests {
|
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
// Normal operation before partition
|
|
139
|
-
leader
|
|
140
|
-
|
|
141
|
+
leader
|
|
142
|
+
.submit_operation(0)
|
|
143
|
+
.await
|
|
144
|
+
.expect("pre-partition submit");
|
|
145
|
+
assert_eq!(
|
|
146
|
+
leader.operations_applied().await,
|
|
147
|
+
1,
|
|
148
|
+
"Leader has 1 op pre-partition"
|
|
149
|
+
);
|
|
141
150
|
|
|
142
151
|
// Partition leader
|
|
143
152
|
leader.partition();
|
|
144
153
|
|
|
145
154
|
// Followers can still operate independently
|
|
146
|
-
follower1
|
|
147
|
-
|
|
155
|
+
follower1
|
|
156
|
+
.submit_operation(100)
|
|
157
|
+
.await
|
|
158
|
+
.expect("follower1 operates");
|
|
159
|
+
follower2
|
|
160
|
+
.submit_operation(200)
|
|
161
|
+
.await
|
|
162
|
+
.expect("follower2 operates");
|
|
148
163
|
|
|
149
164
|
// Leader isolated cannot reach followers
|
|
150
|
-
assert_eq!(
|
|
165
|
+
assert_eq!(
|
|
166
|
+
leader.operations_applied().await,
|
|
167
|
+
1,
|
|
168
|
+
"Leader still has 1 op"
|
|
169
|
+
);
|
|
151
170
|
|
|
152
171
|
// Heal partition
|
|
153
172
|
leader.heal_partition();
|
|
154
173
|
|
|
155
174
|
// Leader can operate again
|
|
156
|
-
leader
|
|
157
|
-
|
|
175
|
+
leader
|
|
176
|
+
.submit_operation(1)
|
|
177
|
+
.await
|
|
178
|
+
.expect("post-partition submit");
|
|
179
|
+
assert_eq!(
|
|
180
|
+
leader.operations_applied().await,
|
|
181
|
+
2,
|
|
182
|
+
"Leader resumed after partition"
|
|
183
|
+
);
|
|
158
184
|
|
|
159
185
|
println!("Partition recovery successful");
|
|
160
186
|
}
|
|
@@ -187,8 +213,14 @@ mod tests {
|
|
|
187
213
|
}
|
|
188
214
|
}
|
|
189
215
|
|
|
190
|
-
println!(
|
|
191
|
-
|
|
216
|
+
println!(
|
|
217
|
+
"Sent {} operations, {} successful deliveries",
|
|
218
|
+
envelope_count, successful
|
|
219
|
+
);
|
|
220
|
+
assert_eq!(
|
|
221
|
+
successful, envelope_count,
|
|
222
|
+
"Leader submits should all succeed locally"
|
|
223
|
+
);
|
|
192
224
|
|
|
193
225
|
// Test that operations persist despite loss
|
|
194
226
|
let ops = leader.operations_applied().await;
|
|
@@ -231,8 +263,14 @@ mod tests {
|
|
|
231
263
|
|
|
232
264
|
{
|
|
233
265
|
let mut executor = leader.executor.lock().await;
|
|
234
|
-
executor.register_replica(
|
|
235
|
-
|
|
266
|
+
executor.register_replica(
|
|
267
|
+
"slow_follower".to_string(),
|
|
268
|
+
StateHash::from_hex("0".repeat(64)),
|
|
269
|
+
);
|
|
270
|
+
executor.register_replica(
|
|
271
|
+
"fast_follower".to_string(),
|
|
272
|
+
StateHash::from_hex("0".repeat(64)),
|
|
273
|
+
);
|
|
236
274
|
}
|
|
237
275
|
|
|
238
276
|
// Leader submits 50 operations rapidly
|
|
@@ -245,11 +283,17 @@ mod tests {
|
|
|
245
283
|
|
|
246
284
|
// Simulate slow node with delay (in production, this would be network latency)
|
|
247
285
|
// Fast node catches up quickly
|
|
248
|
-
fast_follower
|
|
286
|
+
fast_follower
|
|
287
|
+
.submit_operation(0)
|
|
288
|
+
.await
|
|
289
|
+
.expect("fast follower");
|
|
249
290
|
|
|
250
291
|
// Slow node with simulated 1s delay would eventually catch up
|
|
251
292
|
// For testing, we verify the mechanism works
|
|
252
|
-
slow_follower
|
|
293
|
+
slow_follower
|
|
294
|
+
.submit_operation(0)
|
|
295
|
+
.await
|
|
296
|
+
.expect("slow follower");
|
|
253
297
|
|
|
254
298
|
// Both followers should be able to process
|
|
255
299
|
let fast_ops = fast_follower.operations_applied().await;
|
|
@@ -286,18 +330,27 @@ mod tests {
|
|
|
286
330
|
|
|
287
331
|
// Follower1 (slow) eventually catches up
|
|
288
332
|
for i in 0..30 {
|
|
289
|
-
follower1
|
|
333
|
+
follower1
|
|
334
|
+
.submit_operation(1000 + i)
|
|
335
|
+
.await
|
|
336
|
+
.expect("slow follower processes");
|
|
290
337
|
}
|
|
291
338
|
|
|
292
339
|
// Heal follower2
|
|
293
340
|
follower2.heal_partition();
|
|
294
|
-
follower2
|
|
341
|
+
follower2
|
|
342
|
+
.submit_operation(2000)
|
|
343
|
+
.await
|
|
344
|
+
.expect("follower2 recovers");
|
|
295
345
|
|
|
296
346
|
let leader_ops = leader.operations_applied().await;
|
|
297
347
|
let f1_ops = follower1.operations_applied().await;
|
|
298
348
|
let f2_ops = follower2.operations_applied().await;
|
|
299
349
|
|
|
300
|
-
println!(
|
|
350
|
+
println!(
|
|
351
|
+
"Combined issues - Leader: {}, F1: {}, F2: {}",
|
|
352
|
+
leader_ops, f1_ops, f2_ops
|
|
353
|
+
);
|
|
301
354
|
|
|
302
355
|
assert_eq!(leader_ops, 30, "Leader not affected by follower issues");
|
|
303
356
|
assert_eq!(f1_ops, 30, "Slow follower recovered");
|
|
@@ -18,7 +18,9 @@ mod tests {
|
|
|
18
18
|
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
19
19
|
};
|
|
20
20
|
use crate::state_hash::StateHash;
|
|
21
|
-
use crate::transactions::{
|
|
21
|
+
use crate::transactions::{
|
|
22
|
+
ConsistencyContract, Operation, OperationCommand, OperationId, StateVersion,
|
|
23
|
+
};
|
|
22
24
|
use serde_json::json;
|
|
23
25
|
use std::collections::HashMap;
|
|
24
26
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
@@ -204,7 +206,10 @@ mod tests {
|
|
|
204
206
|
follower1.fail();
|
|
205
207
|
|
|
206
208
|
// Follower2 is the last replica standing
|
|
207
|
-
follower2
|
|
209
|
+
follower2
|
|
210
|
+
.submit_operation(100)
|
|
211
|
+
.await
|
|
212
|
+
.expect("follower2 survives");
|
|
208
213
|
|
|
209
214
|
// Recovery
|
|
210
215
|
leader.recover();
|
|
@@ -218,7 +223,10 @@ mod tests {
|
|
|
218
223
|
let final_leader_ops = leader.operations_applied().await;
|
|
219
224
|
let final_follower2_ops = follower2.operations_applied().await;
|
|
220
225
|
|
|
221
|
-
println!(
|
|
226
|
+
println!(
|
|
227
|
+
"Post-recovery: leader={}, follower2={}",
|
|
228
|
+
final_leader_ops, final_follower2_ops
|
|
229
|
+
);
|
|
222
230
|
|
|
223
231
|
assert_eq!(final_leader_ops, 50, "Leader recovered fully");
|
|
224
232
|
assert_eq!(final_follower2_ops, 1, "Last replica survived");
|
|
@@ -234,8 +242,14 @@ mod tests {
|
|
|
234
242
|
// All healthy, submit baseline
|
|
235
243
|
for i in 0..10 {
|
|
236
244
|
node_1.submit_operation(i).await.expect("node_1 baseline");
|
|
237
|
-
node_2
|
|
238
|
-
|
|
245
|
+
node_2
|
|
246
|
+
.submit_operation(i + 100)
|
|
247
|
+
.await
|
|
248
|
+
.expect("node_2 baseline");
|
|
249
|
+
node_3
|
|
250
|
+
.submit_operation(i + 200)
|
|
251
|
+
.await
|
|
252
|
+
.expect("node_3 baseline");
|
|
239
253
|
}
|
|
240
254
|
|
|
241
255
|
// Sequential cascade: failures propagate
|
|
@@ -257,7 +271,10 @@ mod tests {
|
|
|
257
271
|
let ops_2 = node_2.operations_applied().await;
|
|
258
272
|
let ops_3 = node_3.operations_applied().await;
|
|
259
273
|
|
|
260
|
-
println!(
|
|
274
|
+
println!(
|
|
275
|
+
"Sequential cascade recovery: N1={}, N2={}, N3={}",
|
|
276
|
+
ops_1, ops_2, ops_3
|
|
277
|
+
);
|
|
261
278
|
|
|
262
279
|
assert!(ops_1 > 0, "Node 1 recovered");
|
|
263
280
|
assert!(ops_2 > 0, "Node 2 recovered");
|
|
@@ -16,7 +16,9 @@ mod tests {
|
|
|
16
16
|
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
17
17
|
};
|
|
18
18
|
use crate::state_hash::StateHash;
|
|
19
|
-
use crate::transactions::{
|
|
19
|
+
use crate::transactions::{
|
|
20
|
+
ConsistencyContract, Operation, OperationCommand, OperationId, StateVersion,
|
|
21
|
+
};
|
|
20
22
|
use serde_json::json;
|
|
21
23
|
use std::collections::HashMap;
|
|
22
24
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
@@ -78,7 +80,11 @@ mod tests {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
/// Submit operation if not crashed
|
|
81
|
-
fn submit_operation(
|
|
83
|
+
fn submit_operation(
|
|
84
|
+
&self,
|
|
85
|
+
op_idx: u64,
|
|
86
|
+
crash_point: CrashPoint,
|
|
87
|
+
) -> Result<TransactionEnvelope, String> {
|
|
82
88
|
if self.is_crashed.load(Ordering::Relaxed) {
|
|
83
89
|
return Err("Node is crashed".to_string());
|
|
84
90
|
}
|
|
@@ -375,7 +381,10 @@ mod tests {
|
|
|
375
381
|
assert!(!result.data_loss);
|
|
376
382
|
assert!(!result.divergence);
|
|
377
383
|
|
|
378
|
-
println!(
|
|
384
|
+
println!(
|
|
385
|
+
"✓ {:?}: {} operations, {} crashes",
|
|
386
|
+
point, result.operations_tested, result.crashes
|
|
387
|
+
);
|
|
379
388
|
}
|
|
380
389
|
}
|
|
381
390
|
|