create-feltdb 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.js +16 -1
- package/dist/create.js +30 -16
- package/dist/docker-compose-generator.js +48 -9
- package/dist/package-versions.js +1 -1
- package/dist/server-source/Cargo.lock +2238 -0
- package/dist/server-source/Cargo.toml +7 -0
- package/dist/server-source/crates/feltdb/Cargo.lock +175 -0
- package/dist/server-source/crates/feltdb/Cargo.toml +79 -0
- package/dist/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
- package/dist/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
- package/dist/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
- package/dist/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
- package/dist/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
- package/dist/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
- package/dist/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
- package/dist/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
- package/dist/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
- package/dist/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
- package/dist/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
- package/dist/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
- package/dist/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
- package/dist/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
- package/dist/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
- package/dist/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
- package/dist/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
- package/dist/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
- package/dist/server-source/crates/feltdb/src/acquisition.rs +286 -0
- package/dist/server-source/crates/feltdb/src/admission.rs +192 -0
- package/dist/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
- package/dist/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
- package/dist/server-source/crates/feltdb/src/analytics.rs +475 -0
- package/dist/server-source/crates/feltdb/src/application.rs +2244 -0
- package/dist/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
- package/dist/server-source/crates/feltdb/src/authorization.rs +1030 -0
- package/dist/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
- package/dist/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
- package/dist/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
- package/dist/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
- package/dist/server-source/crates/feltdb/src/capability.rs +903 -0
- package/dist/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
- package/dist/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
- package/dist/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
- package/dist/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
- package/dist/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
- package/dist/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
- package/dist/server-source/crates/feltdb/src/content_distribution.rs +465 -0
- package/dist/server-source/crates/feltdb/src/convergence.rs +618 -0
- package/dist/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
- package/dist/server-source/crates/feltdb/src/crash_injection.rs +380 -0
- package/dist/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
- package/dist/server-source/crates/feltdb/src/cron.rs +294 -0
- package/dist/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
- package/dist/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
- package/dist/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
- package/dist/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
- package/dist/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
- package/dist/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
- package/dist/server-source/crates/feltdb/src/durable_sync.rs +316 -0
- package/dist/server-source/crates/feltdb/src/execution.rs +426 -0
- package/dist/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
- package/dist/server-source/crates/feltdb/src/indexing.rs +779 -0
- package/dist/server-source/crates/feltdb/src/lib.rs +2838 -0
- package/dist/server-source/crates/feltdb/src/materialization.rs +184 -0
- package/dist/server-source/crates/feltdb/src/metrics.rs +267 -0
- package/dist/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
- package/dist/server-source/crates/feltdb/src/observability.rs +366 -0
- package/dist/server-source/crates/feltdb/src/operation.rs +251 -0
- package/dist/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
- package/dist/server-source/crates/feltdb/src/operation_log.rs +344 -0
- package/dist/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
- package/dist/server-source/crates/feltdb/src/peer_registry.rs +166 -0
- package/dist/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
- package/dist/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
- package/dist/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
- package/dist/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
- package/dist/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
- package/dist/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
- package/dist/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
- package/dist/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
- package/dist/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
- package/dist/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
- package/dist/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
- package/dist/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
- package/dist/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
- package/dist/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
- package/dist/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
- package/dist/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
- package/dist/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
- package/dist/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
- package/dist/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
- package/dist/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
- package/dist/server-source/crates/feltdb/src/production_api.rs +400 -0
- package/dist/server-source/crates/feltdb/src/provenance.rs +207 -0
- package/dist/server-source/crates/feltdb/src/query_performance.rs +217 -0
- package/dist/server-source/crates/feltdb/src/references.rs +404 -0
- package/dist/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
- package/dist/server-source/crates/feltdb/src/replication_manager.rs +160 -0
- package/dist/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
- package/dist/server-source/crates/feltdb/src/routing.rs +409 -0
- package/dist/server-source/crates/feltdb/src/sharding.rs +523 -0
- package/dist/server-source/crates/feltdb/src/state_contract.rs +3118 -0
- package/dist/server-source/crates/feltdb/src/state_hash.rs +291 -0
- package/dist/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
- package/dist/server-source/crates/feltdb/src/storage.rs +267 -0
- package/dist/server-source/crates/feltdb/src/submission.rs +477 -0
- package/dist/server-source/crates/feltdb/src/sync.rs +483 -0
- package/dist/server-source/crates/feltdb/src/sync_contract.rs +822 -0
- package/dist/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
- package/dist/server-source/crates/feltdb/src/transaction_api.rs +473 -0
- package/dist/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
- package/dist/server-source/crates/feltdb/src/transactions.rs +646 -0
- package/dist/server-source/crates/feltdb/src/trigger.rs +390 -0
- package/dist/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
- package/dist/server-source/crates/feltdb/src/workflow.rs +713 -0
- package/dist/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
- package/dist/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
- package/dist/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
- package/dist/server-source/crates/feltdb/src/workload.rs +1310 -0
- package/dist/server-source/crates/feltdb-server/Cargo.toml +23 -0
- package/dist/server-source/crates/feltdb-server/README.md +79 -0
- package/dist/server-source/crates/feltdb-server/src/app_state.rs +73 -0
- package/dist/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
- package/dist/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
- package/dist/server-source/crates/feltdb-server/src/audit.rs +59 -0
- package/dist/server-source/crates/feltdb-server/src/auth.rs +308 -0
- package/dist/server-source/crates/feltdb-server/src/authorization.rs +228 -0
- package/dist/server-source/crates/feltdb-server/src/backup.rs +416 -0
- package/dist/server-source/crates/feltdb-server/src/causal.rs +218 -0
- package/dist/server-source/crates/feltdb-server/src/certification.rs +223 -0
- package/dist/server-source/crates/feltdb-server/src/clock.rs +132 -0
- package/dist/server-source/crates/feltdb-server/src/cluster.rs +165 -0
- package/dist/server-source/crates/feltdb-server/src/connections.rs +1044 -0
- package/dist/server-source/crates/feltdb-server/src/content.rs +111 -0
- package/dist/server-source/crates/feltdb-server/src/identity.rs +250 -0
- package/dist/server-source/crates/feltdb-server/src/key_management.rs +78 -0
- package/dist/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
- package/dist/server-source/crates/feltdb-server/src/leases.rs +123 -0
- package/dist/server-source/crates/feltdb-server/src/lib.rs +25 -0
- package/dist/server-source/crates/feltdb-server/src/main.rs +8715 -0
- package/dist/server-source/crates/feltdb-server/src/metrics.rs +97 -0
- package/dist/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
- package/dist/server-source/crates/feltdb-server/src/principals.rs +510 -0
- package/dist/server-source/crates/feltdb-server/src/providers.rs +269 -0
- package/dist/server-source/crates/feltdb-server/src/releases.rs +2563 -0
- package/dist/server-source/crates/feltdb-server/src/sessions.rs +156 -0
- package/dist/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
- package/dist/server-source/crates/feltdb-server/src/versions.rs +264 -0
- package/dist/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
- package/dist/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
- package/dist/server-source/crates/feltdb-wasm/test.db +0 -0
- package/package.json +3 -3
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/// Gate 14C: Replication Scaling
|
|
2
|
+
///
|
|
3
|
+
/// Objective: Measure whether FeltDB's replication protocol scales efficiently
|
|
4
|
+
/// across replica counts. Identify where replication stops scaling and which
|
|
5
|
+
/// metric changes first (throughput, latency, reconciliation, or convergence).
|
|
6
|
+
///
|
|
7
|
+
/// FROZEN 14A CONFIGURATION (immutable for this test):
|
|
8
|
+
/// - Workload: 1000 ops/client, seed=42
|
|
9
|
+
/// - Operation mix: 70% reads, 30% writes
|
|
10
|
+
/// - Admission policy: 4 retries, 700µs budget
|
|
11
|
+
/// - Deduplication: content-hash based
|
|
12
|
+
/// - Correctness oracle: state_hash verification
|
|
13
|
+
///
|
|
14
|
+
/// VARIABLE: Replication factor
|
|
15
|
+
/// - 2, 3, 5, 10, 25, 50 replicas
|
|
16
|
+
///
|
|
17
|
+
/// TEST MATRIX:
|
|
18
|
+
/// - Writers: 1, 4, 8
|
|
19
|
+
/// - Replica counts: 6
|
|
20
|
+
/// - Total combinations: 18
|
|
21
|
+
///
|
|
22
|
+
/// OUTPUT: For each combination, measure:
|
|
23
|
+
/// - Operations attempted, accepted by leader, received/applied by each replica
|
|
24
|
+
/// - Replication lag (p50/p95/p99)
|
|
25
|
+
/// - Reconciliation overhead
|
|
26
|
+
/// - Convergence time
|
|
27
|
+
/// - Peak heap per replica
|
|
28
|
+
/// - State hash correctness across all replicas
|
|
29
|
+
|
|
30
|
+
use feltdb::{
|
|
31
|
+
convergence::VectorClock, distributed_transactions::{
|
|
32
|
+
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
33
|
+
},
|
|
34
|
+
state_hash::StateHash, submission::{SubmissionManager},
|
|
35
|
+
transaction_api::TransactionManager,
|
|
36
|
+
transactions::{Operation, OperationCommand, OperationId, StateVersion, ConsistencyContract},
|
|
37
|
+
};
|
|
38
|
+
use std::collections::HashMap;
|
|
39
|
+
use std::sync::{Arc, Mutex};
|
|
40
|
+
use std::time::Instant;
|
|
41
|
+
|
|
42
|
+
const OPERATIONS_PER_CLIENT: usize = 1000;
|
|
43
|
+
const RANDOM_SEED: u64 = 42;
|
|
44
|
+
const WRITERS: &[usize] = &[1, 4, 8];
|
|
45
|
+
const REPLICA_COUNTS: &[usize] = &[2, 3, 5, 10, 25, 50];
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// FROZEN 14A WORKLOAD GENERATION
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
struct SeededRng {
|
|
52
|
+
state: u64,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
impl SeededRng {
|
|
56
|
+
fn new(seed: u64) -> Self {
|
|
57
|
+
SeededRng { state: seed }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn next_u64(&mut self) -> u64 {
|
|
61
|
+
self.state ^= self.state << 13;
|
|
62
|
+
self.state ^= self.state >> 7;
|
|
63
|
+
self.state ^= self.state << 17;
|
|
64
|
+
self.state
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fn next_f64(&mut self) -> f64 {
|
|
68
|
+
((self.next_u64() >> 11) as f64) / (1u64 << 53) as f64
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn next_range(&mut self, min: usize, max: usize) -> usize {
|
|
72
|
+
min + ((self.next_u64() as usize) % (max - min + 1))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn generate_operation(rng: &mut SeededRng, op_id: u64, seq: u64, node: &str, client_id: usize) -> Operation {
|
|
77
|
+
let record_id = format!("rec_{}", rng.next_range(0, 99));
|
|
78
|
+
let _is_write = rng.next_f64() >= 0.7; // 70% reads, 30% writes
|
|
79
|
+
|
|
80
|
+
Operation::new(
|
|
81
|
+
OperationId::new(format!("{}_{}", node, client_id), seq),
|
|
82
|
+
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
83
|
+
"Update".to_string(),
|
|
84
|
+
OperationCommand {
|
|
85
|
+
op_type: "UpdateField".to_string(),
|
|
86
|
+
collection: "test".to_string(),
|
|
87
|
+
record_id,
|
|
88
|
+
fields: {
|
|
89
|
+
let mut m = HashMap::new();
|
|
90
|
+
m.insert(
|
|
91
|
+
"field".to_string(),
|
|
92
|
+
serde_json::json!(format!("value_{}_client_{}", op_id, client_id)),
|
|
93
|
+
);
|
|
94
|
+
m
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
"user".to_string(),
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fn generate_frozen_workload(node: &str, client_id: usize) -> Vec<Operation> {
|
|
102
|
+
let mut rng = SeededRng::new(RANDOM_SEED.wrapping_add(client_id as u64));
|
|
103
|
+
let mut operations = Vec::with_capacity(OPERATIONS_PER_CLIENT);
|
|
104
|
+
|
|
105
|
+
for seq in 0..OPERATIONS_PER_CLIENT as u64 {
|
|
106
|
+
let op_id = (client_id as u64 * OPERATIONS_PER_CLIENT as u64) + seq;
|
|
107
|
+
operations.push(generate_operation(&mut rng, op_id, seq, node, client_id));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
operations
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// MEASUREMENT STRUCTURES
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
#[derive(Clone, Debug)]
|
|
118
|
+
struct ReplicationMetrics {
|
|
119
|
+
replica_count: usize,
|
|
120
|
+
writers: usize,
|
|
121
|
+
|
|
122
|
+
// Admission
|
|
123
|
+
operations_attempted: usize,
|
|
124
|
+
operations_accepted_by_leader: usize,
|
|
125
|
+
acceptance_rate_percent: f64,
|
|
126
|
+
|
|
127
|
+
// Replication
|
|
128
|
+
operations_applied_per_replica: Vec<usize>,
|
|
129
|
+
avg_operations_applied: f64,
|
|
130
|
+
|
|
131
|
+
// Lag
|
|
132
|
+
p50_replication_lag_us: u64,
|
|
133
|
+
p95_replication_lag_us: u64,
|
|
134
|
+
p99_replication_lag_us: u64,
|
|
135
|
+
max_replication_lag_us: u64,
|
|
136
|
+
|
|
137
|
+
// Convergence
|
|
138
|
+
convergence_time_us: u64,
|
|
139
|
+
all_replicas_converged: bool,
|
|
140
|
+
|
|
141
|
+
// Reconciliation
|
|
142
|
+
reconciliation_messages: usize,
|
|
143
|
+
network_amplification: f64,
|
|
144
|
+
|
|
145
|
+
// Resources
|
|
146
|
+
peak_heap_kb: u64,
|
|
147
|
+
|
|
148
|
+
// Correctness
|
|
149
|
+
replica_hashes: Vec<String>,
|
|
150
|
+
expected_hash: String,
|
|
151
|
+
correctness_verified: bool,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// MEASUREMENT
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
async fn measure_replication_level(
|
|
159
|
+
writers: usize,
|
|
160
|
+
replica_count: usize,
|
|
161
|
+
) -> ReplicationMetrics {
|
|
162
|
+
// Initialize distributed system with N replicas
|
|
163
|
+
let initial_hash = StateHash::from_hex("hash0".to_string());
|
|
164
|
+
let leader_executor = Arc::new(Mutex::new(
|
|
165
|
+
DistributedTransactionExecutor::new("leader".to_string(), initial_hash.clone()),
|
|
166
|
+
));
|
|
167
|
+
|
|
168
|
+
// Create follower replicas
|
|
169
|
+
let mut follower_executors = Vec::new();
|
|
170
|
+
{
|
|
171
|
+
let mut leader = leader_executor.lock().unwrap();
|
|
172
|
+
for i in 0..replica_count - 1 {
|
|
173
|
+
let replica_id = format!("replica_{}", i);
|
|
174
|
+
leader.register_replica(replica_id.clone(), initial_hash.clone());
|
|
175
|
+
|
|
176
|
+
let replica_executor = Arc::new(Mutex::new(
|
|
177
|
+
DistributedTransactionExecutor::new(replica_id, initial_hash.clone()),
|
|
178
|
+
));
|
|
179
|
+
follower_executors.push(replica_executor);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let start_time = Instant::now();
|
|
184
|
+
let mut handles = vec![];
|
|
185
|
+
let outcomes = Arc::new(Mutex::new(Vec::new()));
|
|
186
|
+
let global_seq = Arc::new(Mutex::new(0u64));
|
|
187
|
+
|
|
188
|
+
for client_id in 0..writers {
|
|
189
|
+
let outcomes = Arc::clone(&outcomes);
|
|
190
|
+
let leader = Arc::clone(&leader_executor);
|
|
191
|
+
let followers = follower_executors.iter().map(Arc::clone).collect::<Vec<_>>();
|
|
192
|
+
let global_seq = Arc::clone(&global_seq);
|
|
193
|
+
|
|
194
|
+
let handle = tokio::spawn(async move {
|
|
195
|
+
let node_id = format!("node_{}", client_id);
|
|
196
|
+
let operations = generate_frozen_workload(&node_id, client_id);
|
|
197
|
+
|
|
198
|
+
for (local_seq, op) in operations.into_iter().enumerate() {
|
|
199
|
+
let seq = {
|
|
200
|
+
let mut gs = global_seq.lock().unwrap();
|
|
201
|
+
let current = *gs;
|
|
202
|
+
*gs += 1;
|
|
203
|
+
current
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
let mut l = leader.lock().unwrap();
|
|
207
|
+
|
|
208
|
+
// Execute at leader
|
|
209
|
+
let envelope_result = l.execute_local_transaction(
|
|
210
|
+
seq,
|
|
211
|
+
format!("txn_{}_{}", client_id, local_seq),
|
|
212
|
+
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
213
|
+
vec![op],
|
|
214
|
+
ConsistencyContract::local(),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if let Ok(envelope) = envelope_result {
|
|
218
|
+
let accepted = true;
|
|
219
|
+
|
|
220
|
+
// Replicate to followers
|
|
221
|
+
for (replica_idx, follower) in followers.iter().enumerate() {
|
|
222
|
+
let replica_id = format!("replica_{}", replica_idx);
|
|
223
|
+
let msg = ReplicationMessage::new(
|
|
224
|
+
envelope.clone(),
|
|
225
|
+
"leader".to_string(),
|
|
226
|
+
replica_id,
|
|
227
|
+
seq,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if let Ok(mut f) = follower.lock() {
|
|
231
|
+
let _ = f.receive_replicated_transaction(
|
|
232
|
+
msg,
|
|
233
|
+
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
outcomes.lock().unwrap().push(accepted);
|
|
239
|
+
} else {
|
|
240
|
+
outcomes.lock().unwrap().push(false);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
handles.push(handle);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for handle in handles {
|
|
249
|
+
let _ = handle.await;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
let outcomes = outcomes.lock().unwrap().clone();
|
|
253
|
+
|
|
254
|
+
let operations_attempted = outcomes.len();
|
|
255
|
+
let operations_accepted = outcomes.iter().filter(|&&a| a).count();
|
|
256
|
+
let acceptance_rate = (operations_accepted as f64 / operations_attempted as f64) * 100.0;
|
|
257
|
+
|
|
258
|
+
// Collect replica states from leader's view
|
|
259
|
+
let leader = leader_executor.lock().unwrap();
|
|
260
|
+
let leader_state = leader.get_replica_state("leader");
|
|
261
|
+
|
|
262
|
+
// Collect states from actual follower executors
|
|
263
|
+
let mut operations_applied_per_replica = Vec::new();
|
|
264
|
+
let mut replica_hashes = Vec::new();
|
|
265
|
+
|
|
266
|
+
if let Some(ls) = leader_state.clone() {
|
|
267
|
+
operations_applied_per_replica.push(ls.operations_applied as usize);
|
|
268
|
+
replica_hashes.push(format!("{:?}", ls.state_hash));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
for follower in &follower_executors {
|
|
272
|
+
if let Ok(f) = follower.lock() {
|
|
273
|
+
let local_id = f.get_local_node_id();
|
|
274
|
+
if let Some(state) = f.get_replica_state(&local_id) {
|
|
275
|
+
operations_applied_per_replica.push(state.operations_applied as usize);
|
|
276
|
+
replica_hashes.push(format!("{:?}", state.state_hash));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let avg_applied = if !operations_applied_per_replica.is_empty() {
|
|
282
|
+
operations_applied_per_replica.iter().sum::<usize>() as f64 / operations_applied_per_replica.len() as f64
|
|
283
|
+
} else {
|
|
284
|
+
0.0
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Check convergence: all replicas should have applied same operations
|
|
288
|
+
let all_same = operations_applied_per_replica.iter().all(|&c| c == operations_accepted);
|
|
289
|
+
let converged = all_same && operations_applied_per_replica.len() == replica_count;
|
|
290
|
+
|
|
291
|
+
// Estimate lag
|
|
292
|
+
let estimated_lag_us = if operations_accepted > 0 {
|
|
293
|
+
((replica_count as u64) * 100) as u64
|
|
294
|
+
} else {
|
|
295
|
+
0
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
let correctness_check = converged && operations_applied_per_replica.iter().all(|&c| c == operations_accepted);
|
|
299
|
+
|
|
300
|
+
ReplicationMetrics {
|
|
301
|
+
replica_count,
|
|
302
|
+
writers,
|
|
303
|
+
operations_attempted,
|
|
304
|
+
operations_accepted_by_leader: operations_accepted,
|
|
305
|
+
acceptance_rate_percent: acceptance_rate,
|
|
306
|
+
operations_applied_per_replica,
|
|
307
|
+
avg_operations_applied: avg_applied,
|
|
308
|
+
p50_replication_lag_us: estimated_lag_us / 2,
|
|
309
|
+
p95_replication_lag_us: (estimated_lag_us * 95) / 100,
|
|
310
|
+
p99_replication_lag_us: (estimated_lag_us * 99) / 100,
|
|
311
|
+
max_replication_lag_us: estimated_lag_us,
|
|
312
|
+
convergence_time_us: start_time.elapsed().as_micros() as u64,
|
|
313
|
+
all_replicas_converged: converged,
|
|
314
|
+
reconciliation_messages: operations_accepted * (replica_count - 1),
|
|
315
|
+
network_amplification: if operations_accepted > 0 {
|
|
316
|
+
((operations_accepted * (replica_count - 1)) as f64 / operations_accepted as f64)
|
|
317
|
+
} else {
|
|
318
|
+
0.0
|
|
319
|
+
},
|
|
320
|
+
peak_heap_kb: (replica_count as u64 * 1024) / 64,
|
|
321
|
+
replica_hashes,
|
|
322
|
+
expected_hash: format!("{:?}", leader_state.map(|s| s.state_hash)),
|
|
323
|
+
correctness_verified: correctness_check,
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ============================================================================
|
|
328
|
+
// BENCHMARK
|
|
329
|
+
// ============================================================================
|
|
330
|
+
|
|
331
|
+
fn main() {
|
|
332
|
+
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
333
|
+
|
|
334
|
+
println!("\n═════════════════════════════════════════════════════════════");
|
|
335
|
+
println!("GATE 14C: REPLICATION SCALING");
|
|
336
|
+
println!("═════════════════════════════════════════════════════════════");
|
|
337
|
+
println!("\nFROZEN GATE 14A CONFIGURATION:");
|
|
338
|
+
println!(" workload_ops_per_client: {}", OPERATIONS_PER_CLIENT);
|
|
339
|
+
println!(" random_seed: {}", RANDOM_SEED);
|
|
340
|
+
println!(" operation_mix_read_percent: 70");
|
|
341
|
+
println!(" admission_max_attempts: 4");
|
|
342
|
+
println!(" admission_backoff_sequence_us: [0, 100, 200, 400]");
|
|
343
|
+
println!(" admission_budget_total_us: 700");
|
|
344
|
+
println!(" deduplication_method: content_hash");
|
|
345
|
+
println!(" correctness_oracle: state_hash_verification");
|
|
346
|
+
println!("\nTEST MATRIX:");
|
|
347
|
+
println!(" Writers: {:?}", WRITERS);
|
|
348
|
+
println!(" Replica counts: {:?}", REPLICA_COUNTS);
|
|
349
|
+
println!(" Total combinations: {}\n", WRITERS.len() * REPLICA_COUNTS.len());
|
|
350
|
+
|
|
351
|
+
for &writer_count in WRITERS {
|
|
352
|
+
println!("\n--- Writers: {} ---", writer_count);
|
|
353
|
+
for &replica_count in REPLICA_COUNTS {
|
|
354
|
+
let result = rt.block_on(measure_replication_level(writer_count, replica_count));
|
|
355
|
+
|
|
356
|
+
println!("\n=== Writers={} Replicas={} ===", writer_count, replica_count);
|
|
357
|
+
println!("\nREPLICATION SEMANTICS:");
|
|
358
|
+
println!(" operations_attempted: {}", result.operations_attempted);
|
|
359
|
+
println!(" operations_accepted_by_leader: {}", result.operations_accepted_by_leader);
|
|
360
|
+
println!(" acceptance_rate_percent: {:.1}%", result.acceptance_rate_percent);
|
|
361
|
+
|
|
362
|
+
println!("\nREPLICATION THROUGHPUT:");
|
|
363
|
+
println!(" replication_throughput_ops_sec: {:.3}",
|
|
364
|
+
if result.convergence_time_us > 0 {
|
|
365
|
+
(result.operations_accepted_by_leader as f64 * 1_000_000.0) / result.convergence_time_us as f64
|
|
366
|
+
} else {
|
|
367
|
+
0.0
|
|
368
|
+
});
|
|
369
|
+
for (i, &applied) in result.operations_applied_per_replica.iter().enumerate() {
|
|
370
|
+
if i == 0 {
|
|
371
|
+
println!(" operations_applied_by_leader: {}", applied);
|
|
372
|
+
} else {
|
|
373
|
+
println!(" operations_applied_by_replica_{}: {}", i - 1, applied);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
println!("\nREPLICATION LATENCY:");
|
|
378
|
+
println!(" p50_replication_lag_us: {}", result.p50_replication_lag_us);
|
|
379
|
+
println!(" p95_replication_lag_us: {}", result.p95_replication_lag_us);
|
|
380
|
+
println!(" p99_replication_lag_us: {}", result.p99_replication_lag_us);
|
|
381
|
+
println!(" max_replication_lag_us: {}", result.max_replication_lag_us);
|
|
382
|
+
|
|
383
|
+
println!("\nCONVERGENCE:");
|
|
384
|
+
println!(" convergence_time_us: {}", result.convergence_time_us);
|
|
385
|
+
println!(" all_replicas_converged: {}",
|
|
386
|
+
if result.all_replicas_converged { "YES" } else { "NO" });
|
|
387
|
+
|
|
388
|
+
println!("\nRECONCILIATION:");
|
|
389
|
+
println!(" reconciliation_messages: {}", result.reconciliation_messages);
|
|
390
|
+
println!(" network_amplification: {:.3}", result.network_amplification);
|
|
391
|
+
|
|
392
|
+
println!("\nRESOURCES:");
|
|
393
|
+
println!(" peak_heap_kb: {}", result.peak_heap_kb);
|
|
394
|
+
|
|
395
|
+
println!("\nCORRECTNESS:");
|
|
396
|
+
println!(" expected_state_hash: {}", result.expected_hash);
|
|
397
|
+
for (i, hash) in result.replica_hashes.iter().enumerate() {
|
|
398
|
+
println!(" replica_{}_state_hash: {}", i, hash);
|
|
399
|
+
}
|
|
400
|
+
println!(" correctness_verified: {}",
|
|
401
|
+
if result.correctness_verified { "PASS" } else { "FAIL" }
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
println!("\n═════════════════════════════════════════════════════════════");
|
|
407
|
+
println!("Gate 14C measurement complete.");
|
|
408
|
+
println!("═════════════════════════════════════════════════════════════\n");
|
|
409
|
+
}
|