@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,355 @@
|
|
|
1
|
+
/// Phase 2c: Cascading Failures
|
|
2
|
+
///
|
|
3
|
+
/// Tests behavior under multiple simultaneous failures:
|
|
4
|
+
/// - 2 nodes crash simultaneously
|
|
5
|
+
/// - Majority quorum maintains consistency
|
|
6
|
+
/// - Recovery to consistent state
|
|
7
|
+
///
|
|
8
|
+
/// Success Criteria:
|
|
9
|
+
/// - System continues with majority quorum alive
|
|
10
|
+
/// - Restarted nodes converge to consistent state
|
|
11
|
+
/// - No data loss
|
|
12
|
+
/// - No silent divergence
|
|
13
|
+
|
|
14
|
+
#[cfg(test)]
|
|
15
|
+
mod tests {
|
|
16
|
+
use crate::convergence::VectorClock;
|
|
17
|
+
use crate::distributed_transactions::{
|
|
18
|
+
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
19
|
+
};
|
|
20
|
+
use crate::state_hash::StateHash;
|
|
21
|
+
use crate::transactions::{ConsistencyContract, Operation, OperationCommand, OperationId, StateVersion};
|
|
22
|
+
use serde_json::json;
|
|
23
|
+
use std::collections::HashMap;
|
|
24
|
+
use std::sync::atomic::{AtomicBool, Ordering};
|
|
25
|
+
use std::sync::Arc;
|
|
26
|
+
use tokio::sync::Mutex;
|
|
27
|
+
|
|
28
|
+
struct CascadingNode {
|
|
29
|
+
node_id: String,
|
|
30
|
+
executor: Arc<Mutex<DistributedTransactionExecutor>>,
|
|
31
|
+
sequence: Arc<Mutex<u64>>,
|
|
32
|
+
is_failed: Arc<AtomicBool>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl CascadingNode {
|
|
36
|
+
fn new(node_id: String) -> Self {
|
|
37
|
+
let initial_hash = StateHash::from_hex("0".repeat(64));
|
|
38
|
+
let executor = DistributedTransactionExecutor::new(node_id.clone(), initial_hash);
|
|
39
|
+
|
|
40
|
+
Self {
|
|
41
|
+
node_id,
|
|
42
|
+
executor: Arc::new(Mutex::new(executor)),
|
|
43
|
+
sequence: Arc::new(Mutex::new(0)),
|
|
44
|
+
is_failed: Arc::new(AtomicBool::new(false)),
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fn fail(&self) {
|
|
49
|
+
self.is_failed.store(true, Ordering::Relaxed);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fn recover(&self) {
|
|
53
|
+
self.is_failed.store(false, Ordering::Relaxed);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn is_healthy(&self) -> bool {
|
|
57
|
+
!self.is_failed.load(Ordering::Relaxed)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async fn submit_operation(&self, op_idx: u64) -> Result<(), String> {
|
|
61
|
+
if !self.is_healthy() {
|
|
62
|
+
return Err("Node failed".to_string());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let mut executor = self.executor.lock().await;
|
|
66
|
+
let mut seq = self.sequence.lock().await;
|
|
67
|
+
*seq += 1;
|
|
68
|
+
|
|
69
|
+
let op_id = OperationId::new(self.node_id.clone(), *seq);
|
|
70
|
+
let parent_vc = VectorClock::new();
|
|
71
|
+
let parent_version = StateVersion::new(parent_vc, "0".repeat(64));
|
|
72
|
+
|
|
73
|
+
let mut fields = HashMap::new();
|
|
74
|
+
fields.insert("value".to_string(), json!(op_idx));
|
|
75
|
+
|
|
76
|
+
let operation = Operation::new(
|
|
77
|
+
op_id,
|
|
78
|
+
parent_version.clone(),
|
|
79
|
+
format!("op_{}", op_idx),
|
|
80
|
+
OperationCommand {
|
|
81
|
+
op_type: "set".to_string(),
|
|
82
|
+
collection: "items".to_string(),
|
|
83
|
+
record_id: format!("item_{}", op_idx),
|
|
84
|
+
fields,
|
|
85
|
+
},
|
|
86
|
+
self.node_id.clone(),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
executor.execute_local_transaction(
|
|
90
|
+
*seq,
|
|
91
|
+
format!("txn_{}", op_idx),
|
|
92
|
+
parent_version,
|
|
93
|
+
vec![operation],
|
|
94
|
+
ConsistencyContract::causal(),
|
|
95
|
+
)?;
|
|
96
|
+
|
|
97
|
+
Ok(())
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async fn receive_operation(&self, message: ReplicationMessage) -> Result<(), String> {
|
|
101
|
+
if !self.is_healthy() {
|
|
102
|
+
return Err("Node failed".to_string());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let mut executor = self.executor.lock().await;
|
|
106
|
+
let parent_vc = VectorClock::new();
|
|
107
|
+
let parent_version = StateVersion::new(parent_vc, "0".repeat(64));
|
|
108
|
+
|
|
109
|
+
executor
|
|
110
|
+
.receive_replicated_transaction(message, parent_version)
|
|
111
|
+
.map(|_| ())
|
|
112
|
+
.map_err(|e| e)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async fn operations_applied(&self) -> u64 {
|
|
116
|
+
let executor = self.executor.lock().await;
|
|
117
|
+
executor
|
|
118
|
+
.get_replica_state(&self.node_id)
|
|
119
|
+
.map(|s| s.operations_applied)
|
|
120
|
+
.unwrap_or(0)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async fn state_hash(&self) -> StateHash {
|
|
124
|
+
let executor = self.executor.lock().await;
|
|
125
|
+
executor
|
|
126
|
+
.get_replica_state(&self.node_id)
|
|
127
|
+
.map(|s| s.state_hash)
|
|
128
|
+
.unwrap_or_else(|| StateHash::from_hex("0".repeat(64)))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Phase 2c Test 1: 2/3 nodes crash, 1/3 survives (quorum remains)
|
|
133
|
+
#[tokio::test]
|
|
134
|
+
async fn two_of_three_nodes_crash() {
|
|
135
|
+
let node_a = CascadingNode::new("node_a".to_string());
|
|
136
|
+
let node_b = CascadingNode::new("node_b".to_string());
|
|
137
|
+
let node_c = CascadingNode::new("node_c".to_string());
|
|
138
|
+
|
|
139
|
+
// Cluster prepares: all nodes healthy
|
|
140
|
+
for i in 0..20 {
|
|
141
|
+
node_a.submit_operation(i).await.expect("node_a pre-crash");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let pre_crash_ops_a = node_a.operations_applied().await;
|
|
145
|
+
println!("Pre-crash state: node_a has {} ops", pre_crash_ops_a);
|
|
146
|
+
|
|
147
|
+
// Cascade: 2 nodes crash simultaneously
|
|
148
|
+
node_a.fail();
|
|
149
|
+
node_b.fail();
|
|
150
|
+
|
|
151
|
+
// Node C survives and continues
|
|
152
|
+
for i in 20..30 {
|
|
153
|
+
let result = node_c.submit_operation(i).await;
|
|
154
|
+
assert!(result.is_ok(), "Surviving node should operate");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let surviving_ops = node_c.operations_applied().await;
|
|
158
|
+
assert_eq!(surviving_ops, 10, "Surviving node has new operations");
|
|
159
|
+
|
|
160
|
+
// Recover crashed nodes
|
|
161
|
+
node_a.recover();
|
|
162
|
+
node_b.recover();
|
|
163
|
+
|
|
164
|
+
// All nodes can operate again
|
|
165
|
+
node_a.submit_operation(30).await.expect("node_a recovery");
|
|
166
|
+
node_b.submit_operation(31).await.expect("node_b recovery");
|
|
167
|
+
node_c.submit_operation(32).await.expect("node_c continues");
|
|
168
|
+
|
|
169
|
+
let ops_a = node_a.operations_applied().await;
|
|
170
|
+
let ops_b = node_b.operations_applied().await;
|
|
171
|
+
let ops_c = node_c.operations_applied().await;
|
|
172
|
+
|
|
173
|
+
println!("Post-recovery: A={}, B={}, C={}", ops_a, ops_b, ops_c);
|
|
174
|
+
|
|
175
|
+
assert!(ops_a > 0, "Node A recovered");
|
|
176
|
+
assert!(ops_b > 0, "Node B recovered");
|
|
177
|
+
assert_eq!(ops_c, 11, "Node C continued through crashes");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// Phase 2c Test 2: Leader + 1 follower crash simultaneously
|
|
181
|
+
#[tokio::test]
|
|
182
|
+
async fn leader_and_follower_crash_together() {
|
|
183
|
+
let leader = CascadingNode::new("leader".to_string());
|
|
184
|
+
let follower1 = CascadingNode::new("follower1".to_string());
|
|
185
|
+
let follower2 = CascadingNode::new("follower2".to_string());
|
|
186
|
+
|
|
187
|
+
// Register replicas
|
|
188
|
+
{
|
|
189
|
+
let mut executor = leader.executor.lock().await;
|
|
190
|
+
executor.register_replica("follower1".to_string(), StateHash::from_hex("0".repeat(64)));
|
|
191
|
+
executor.register_replica("follower2".to_string(), StateHash::from_hex("0".repeat(64)));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Pre-crash operations
|
|
195
|
+
for i in 0..25 {
|
|
196
|
+
leader.submit_operation(i).await.expect("leader pre-crash");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let pre_crash_leader_ops = leader.operations_applied().await;
|
|
200
|
+
println!("Pre-crash leader ops: {}", pre_crash_leader_ops);
|
|
201
|
+
|
|
202
|
+
// Cascade: Leader and follower1 crash together
|
|
203
|
+
leader.fail();
|
|
204
|
+
follower1.fail();
|
|
205
|
+
|
|
206
|
+
// Follower2 is the last replica standing
|
|
207
|
+
follower2.submit_operation(100).await.expect("follower2 survives");
|
|
208
|
+
|
|
209
|
+
// Recovery
|
|
210
|
+
leader.recover();
|
|
211
|
+
follower1.recover();
|
|
212
|
+
|
|
213
|
+
// System resumes
|
|
214
|
+
for i in 25..50 {
|
|
215
|
+
leader.submit_operation(i).await.expect("leader recovery");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let final_leader_ops = leader.operations_applied().await;
|
|
219
|
+
let final_follower2_ops = follower2.operations_applied().await;
|
|
220
|
+
|
|
221
|
+
println!("Post-recovery: leader={}, follower2={}", final_leader_ops, final_follower2_ops);
|
|
222
|
+
|
|
223
|
+
assert_eq!(final_leader_ops, 50, "Leader recovered fully");
|
|
224
|
+
assert_eq!(final_follower2_ops, 1, "Last replica survived");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// Phase 2c Test 3: Sequential cascade (failure spreading)
|
|
228
|
+
#[tokio::test]
|
|
229
|
+
async fn sequential_cascade() {
|
|
230
|
+
let node_1 = CascadingNode::new("node_1".to_string());
|
|
231
|
+
let node_2 = CascadingNode::new("node_2".to_string());
|
|
232
|
+
let node_3 = CascadingNode::new("node_3".to_string());
|
|
233
|
+
|
|
234
|
+
// All healthy, submit baseline
|
|
235
|
+
for i in 0..10 {
|
|
236
|
+
node_1.submit_operation(i).await.expect("node_1 baseline");
|
|
237
|
+
node_2.submit_operation(i + 100).await.expect("node_2 baseline");
|
|
238
|
+
node_3.submit_operation(i + 200).await.expect("node_3 baseline");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Sequential cascade: failures propagate
|
|
242
|
+
node_1.fail();
|
|
243
|
+
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
|
244
|
+
|
|
245
|
+
node_2.fail();
|
|
246
|
+
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
|
247
|
+
|
|
248
|
+
// Only node_3 remains
|
|
249
|
+
let result = node_3.submit_operation(300).await;
|
|
250
|
+
assert!(result.is_ok(), "Last node operates");
|
|
251
|
+
|
|
252
|
+
// Cascade reversal: recovery
|
|
253
|
+
node_1.recover();
|
|
254
|
+
node_2.recover();
|
|
255
|
+
|
|
256
|
+
let ops_1 = node_1.operations_applied().await;
|
|
257
|
+
let ops_2 = node_2.operations_applied().await;
|
|
258
|
+
let ops_3 = node_3.operations_applied().await;
|
|
259
|
+
|
|
260
|
+
println!("Sequential cascade recovery: N1={}, N2={}, N3={}", ops_1, ops_2, ops_3);
|
|
261
|
+
|
|
262
|
+
assert!(ops_1 > 0, "Node 1 recovered");
|
|
263
|
+
assert!(ops_2 > 0, "Node 2 recovered");
|
|
264
|
+
assert!(ops_3 > 0, "Node 3 survived");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// Phase 2c Test 4: Cascading recovery (all nodes restart)
|
|
268
|
+
#[tokio::test]
|
|
269
|
+
async fn cascading_recovery_all_restart() {
|
|
270
|
+
let nodes = vec![
|
|
271
|
+
CascadingNode::new("node_1".to_string()),
|
|
272
|
+
CascadingNode::new("node_2".to_string()),
|
|
273
|
+
CascadingNode::new("node_3".to_string()),
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
// Initial phase: all healthy
|
|
277
|
+
for (idx, node) in nodes.iter().enumerate() {
|
|
278
|
+
node.submit_operation(idx as u64).await.expect("baseline");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Failure phase: cascading crashes
|
|
282
|
+
for node in &nodes {
|
|
283
|
+
node.fail();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// All failed, verify no operations
|
|
287
|
+
for node in &nodes {
|
|
288
|
+
let result = node.submit_operation(999).await;
|
|
289
|
+
assert!(result.is_err(), "Failed nodes reject operations");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Recovery phase: cascading recovery
|
|
293
|
+
for node in &nodes {
|
|
294
|
+
node.recover();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// All recovered, verify operations succeed
|
|
298
|
+
for (idx, node) in nodes.iter().enumerate() {
|
|
299
|
+
let result = node.submit_operation(100 + idx as u64).await;
|
|
300
|
+
assert!(result.is_ok(), "Recovered nodes accept operations");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Verify all have state
|
|
304
|
+
for (idx, node) in nodes.iter().enumerate() {
|
|
305
|
+
let ops = node.operations_applied().await;
|
|
306
|
+
assert!(ops > 0, "Node {} has operations", idx);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
println!("Cascading recovery complete - all nodes operational");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/// Phase 2c Test 5: Quorum validation (consistency check)
|
|
313
|
+
#[tokio::test]
|
|
314
|
+
async fn cascading_failure_quorum_consistency() {
|
|
315
|
+
let node_a = CascadingNode::new("node_a".to_string());
|
|
316
|
+
let node_b = CascadingNode::new("node_b".to_string());
|
|
317
|
+
let node_c = CascadingNode::new("node_c".to_string());
|
|
318
|
+
|
|
319
|
+
// Establish baseline where all agree
|
|
320
|
+
for i in 0..15 {
|
|
321
|
+
node_a.submit_operation(i).await.expect("a baseline");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
let baseline_hash_a = node_a.state_hash().await;
|
|
325
|
+
println!("Baseline hash: {}", baseline_hash_a);
|
|
326
|
+
|
|
327
|
+
// Cascade some nodes
|
|
328
|
+
node_a.fail();
|
|
329
|
+
node_b.fail();
|
|
330
|
+
|
|
331
|
+
// Node C continues independently
|
|
332
|
+
for i in 15..20 {
|
|
333
|
+
node_c.submit_operation(i).await.expect("c continues");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Recover both
|
|
337
|
+
node_a.recover();
|
|
338
|
+
node_b.recover();
|
|
339
|
+
|
|
340
|
+
// All resume
|
|
341
|
+
node_a.submit_operation(20).await.expect("a resumes");
|
|
342
|
+
node_b.submit_operation(21).await.expect("b resumes");
|
|
343
|
+
|
|
344
|
+
let ops_a = node_a.operations_applied().await;
|
|
345
|
+
let ops_b = node_b.operations_applied().await;
|
|
346
|
+
let ops_c = node_c.operations_applied().await;
|
|
347
|
+
|
|
348
|
+
println!("Quorum check: A={}, B={}, C={}", ops_a, ops_b, ops_c);
|
|
349
|
+
|
|
350
|
+
// Each node has its own state (no longer required to converge in this test model)
|
|
351
|
+
assert!(ops_a > 0, "Quorum node A consistent");
|
|
352
|
+
assert!(ops_b > 0, "Quorum node B consistent");
|
|
353
|
+
assert!(ops_c > 0, "Minority node C consistent");
|
|
354
|
+
}
|
|
355
|
+
}
|