@feltdb/core 0.8.4 → 0.8.5
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 +507 -6
- 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_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 +1514 -537
- 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/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/branching_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +288 -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/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 +1032 -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/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-server/src/app_state.rs +2 -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 +326 -6
- package/dist/studio-app/assets/{feltdb_wasm-CVQWgXO-.js → feltdb_wasm-DaNwCLRX.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-DnsHNv6g.wasm +0 -0
- package/dist/studio-app/assets/{index-DwgNAIIX.js → index-j8IlhNqJ.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
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
/// - Resource usage (memory, CPU)
|
|
34
34
|
/// - Rejection behavior
|
|
35
35
|
/// - Correctness verification
|
|
36
|
-
|
|
37
36
|
use criterion::{criterion_group, criterion_main, Criterion};
|
|
38
37
|
use feltdb::{
|
|
39
38
|
causal_dependency_barrier::CausalDependencyBarrier,
|
|
40
|
-
convergence::VectorClock,
|
|
39
|
+
convergence::VectorClock,
|
|
40
|
+
submission::{SubmissionManager, SubmissionMetrics},
|
|
41
41
|
transactions::{Operation, OperationCommand, OperationId, StateVersion},
|
|
42
42
|
};
|
|
43
43
|
use std::collections::HashMap;
|
|
@@ -78,7 +78,13 @@ impl SeededRng {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
fn generate_operation(
|
|
81
|
+
fn generate_operation(
|
|
82
|
+
rng: &mut SeededRng,
|
|
83
|
+
op_id: u64,
|
|
84
|
+
seq: u64,
|
|
85
|
+
node: &str,
|
|
86
|
+
client_id: usize,
|
|
87
|
+
) -> Operation {
|
|
82
88
|
let record_id = format!("rec_{}", rng.next_range(0, 99));
|
|
83
89
|
let _is_write = rng.next_f64() >= 0.7; // 70% reads, 30% writes
|
|
84
90
|
|
|
@@ -163,10 +169,7 @@ struct BacklogMetrics {
|
|
|
163
169
|
// MEASUREMENT
|
|
164
170
|
// ============================================================================
|
|
165
171
|
|
|
166
|
-
async fn measure_backlog_level(
|
|
167
|
-
writers: usize,
|
|
168
|
-
backlog_depth: usize,
|
|
169
|
-
) -> BacklogMetrics {
|
|
172
|
+
async fn measure_backlog_level(writers: usize, backlog_depth: usize) -> BacklogMetrics {
|
|
170
173
|
// Create causal dependency barrier for this measurement
|
|
171
174
|
let mut barrier = CausalDependencyBarrier::new();
|
|
172
175
|
let mut frontier = VectorClock::new();
|
|
@@ -180,7 +183,9 @@ async fn measure_backlog_level(
|
|
|
180
183
|
let mut required_deps = VectorClock::new();
|
|
181
184
|
// Create dependencies on future state (far beyond current frontier)
|
|
182
185
|
// This ensures the operation will be causally blocked
|
|
183
|
-
required_deps
|
|
186
|
+
required_deps
|
|
187
|
+
.clocks
|
|
188
|
+
.insert("backlog_producer".to_string(), (10000 + i) as u64);
|
|
184
189
|
let op_id = format!("pending_backlog_{}", i);
|
|
185
190
|
let _ = barrier.receive_operation(op_id, required_deps);
|
|
186
191
|
}
|
|
@@ -198,8 +203,10 @@ async fn measure_backlog_level(
|
|
|
198
203
|
|
|
199
204
|
// Now measure submission performance while backlog exists
|
|
200
205
|
let tx_manager = Arc::new(Mutex::new(
|
|
201
|
-
feltdb::transaction_api::TransactionManager::new(
|
|
202
|
-
|
|
206
|
+
feltdb::transaction_api::TransactionManager::new(
|
|
207
|
+
"node1".to_string(),
|
|
208
|
+
StateVersion::new(frontier.clone(), "hash0".to_string()),
|
|
209
|
+
),
|
|
203
210
|
));
|
|
204
211
|
|
|
205
212
|
let submission_manager = SubmissionManager::new(Arc::clone(&tx_manager));
|
|
@@ -238,11 +245,13 @@ async fn measure_backlog_level(
|
|
|
238
245
|
let outcomes = outcomes.lock().unwrap().clone();
|
|
239
246
|
|
|
240
247
|
let initial_attempted = outcomes.len();
|
|
241
|
-
let initial_accepted = outcomes
|
|
248
|
+
let initial_accepted = outcomes
|
|
249
|
+
.iter()
|
|
242
250
|
.filter(|(eventually, first_lat, _)| *first_lat > 0 && *eventually)
|
|
243
251
|
.count();
|
|
244
252
|
let initial_rejected = initial_attempted - initial_accepted;
|
|
245
|
-
let final_accepted = outcomes
|
|
253
|
+
let final_accepted = outcomes
|
|
254
|
+
.iter()
|
|
246
255
|
.filter(|(eventually, _, _)| *eventually)
|
|
247
256
|
.count();
|
|
248
257
|
let final_permanently_rejected = initial_attempted - final_accepted;
|
|
@@ -287,7 +296,10 @@ async fn measure_backlog_level(
|
|
|
287
296
|
// State hash: for causal barrier measurement, hash reflects frontier state
|
|
288
297
|
let frontier_hash = format!("{:?}", frontier.clocks);
|
|
289
298
|
let expected_hash = format!("{:08x}", frontier_hash.len() as u32);
|
|
290
|
-
let actual_hash = format!(
|
|
299
|
+
let actual_hash = format!(
|
|
300
|
+
"{:08x}",
|
|
301
|
+
(backlog_depth ^ writers ^ final_accepted as usize) as u32
|
|
302
|
+
);
|
|
291
303
|
|
|
292
304
|
let correctness_verified = actual_pending_count == backlog_depth;
|
|
293
305
|
BacklogMetrics {
|
|
@@ -333,28 +345,51 @@ fn gate_14b_causal_backlog_scaling(c: &mut Criterion) {
|
|
|
333
345
|
println!("\nTEST MATRIX:");
|
|
334
346
|
println!(" Writers: {:?}", WRITERS);
|
|
335
347
|
println!(" Backlog levels: {:?}", BACKLOG_LEVELS);
|
|
336
|
-
println!(
|
|
348
|
+
println!(
|
|
349
|
+
" Total combinations: {}\n",
|
|
350
|
+
WRITERS.len() * BACKLOG_LEVELS.len()
|
|
351
|
+
);
|
|
337
352
|
|
|
338
353
|
for &writer_count in WRITERS {
|
|
339
354
|
println!("\n--- Writers: {} ---", writer_count);
|
|
340
355
|
for &backlog_level in BACKLOG_LEVELS {
|
|
341
356
|
let result = rt.block_on(measure_backlog_level(writer_count, backlog_level));
|
|
342
357
|
|
|
343
|
-
println!(
|
|
358
|
+
println!(
|
|
359
|
+
"\n=== Writers={} Backlog={} ===",
|
|
360
|
+
writer_count, backlog_level
|
|
361
|
+
);
|
|
344
362
|
println!("\nADMISSION SEMANTICS (14A preserved):");
|
|
345
|
-
println!(
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
363
|
+
println!(
|
|
364
|
+
" initial_admission_attempted: {}",
|
|
365
|
+
result.initial_attempted
|
|
366
|
+
);
|
|
367
|
+
println!(
|
|
368
|
+
" initial_admission_accepted: {}",
|
|
369
|
+
result.initial_accepted
|
|
370
|
+
);
|
|
371
|
+
println!(
|
|
372
|
+
" initial_admission_rejected: {}",
|
|
373
|
+
result.initial_rejected
|
|
374
|
+
);
|
|
375
|
+
println!(
|
|
376
|
+
" initial_acceptance_rate_percent: {:.1}%",
|
|
349
377
|
if result.initial_attempted > 0 {
|
|
350
378
|
(result.initial_accepted as f64 / result.initial_attempted as f64) * 100.0
|
|
351
379
|
} else {
|
|
352
380
|
0.0
|
|
353
381
|
}
|
|
354
382
|
);
|
|
355
|
-
println!(
|
|
356
|
-
|
|
357
|
-
|
|
383
|
+
println!(
|
|
384
|
+
" final_outcome_accepted: {}",
|
|
385
|
+
result.final_accepted
|
|
386
|
+
);
|
|
387
|
+
println!(
|
|
388
|
+
" final_outcome_permanently_rejected: {}",
|
|
389
|
+
result.final_permanently_rejected
|
|
390
|
+
);
|
|
391
|
+
println!(
|
|
392
|
+
" eventual_acceptance_rate_percent: {:.1}%",
|
|
358
393
|
if result.initial_attempted > 0 {
|
|
359
394
|
(result.final_accepted as f64 / result.initial_attempted as f64) * 100.0
|
|
360
395
|
} else {
|
|
@@ -363,21 +398,53 @@ fn gate_14b_causal_backlog_scaling(c: &mut Criterion) {
|
|
|
363
398
|
);
|
|
364
399
|
|
|
365
400
|
println!("\nPERFORMANCE:");
|
|
366
|
-
println!(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
println!(
|
|
371
|
-
|
|
401
|
+
println!(
|
|
402
|
+
" throughput_ops_sec: {:.3}",
|
|
403
|
+
result.throughput_ops_sec
|
|
404
|
+
);
|
|
405
|
+
println!(
|
|
406
|
+
" admission_rate_ops_sec: {:.3}",
|
|
407
|
+
result.admission_rate_ops_sec
|
|
408
|
+
);
|
|
409
|
+
println!(
|
|
410
|
+
" backlog_drain_rate_ops_sec: {:.3}",
|
|
411
|
+
result.backlog_drain_rate_ops_sec
|
|
412
|
+
);
|
|
413
|
+
println!(
|
|
414
|
+
" causal_readiness_p50_us: {}",
|
|
415
|
+
result.p50_causal_readiness_us
|
|
416
|
+
);
|
|
417
|
+
println!(
|
|
418
|
+
" causal_readiness_p95_us: {}",
|
|
419
|
+
result.p95_causal_readiness_us
|
|
420
|
+
);
|
|
421
|
+
println!(
|
|
422
|
+
" causal_readiness_p99_us: {}",
|
|
423
|
+
result.p99_causal_readiness_us
|
|
424
|
+
);
|
|
372
425
|
|
|
373
426
|
println!("\nRESOURCES:");
|
|
374
|
-
println!(
|
|
427
|
+
println!(
|
|
428
|
+
" peak_total_heap_kb: {}",
|
|
429
|
+
result.peak_heap_kb
|
|
430
|
+
);
|
|
375
431
|
|
|
376
432
|
println!("\nCORRECTNESS:");
|
|
377
|
-
println!(
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
433
|
+
println!(
|
|
434
|
+
" expected_state_hash: {}",
|
|
435
|
+
result.expected_state_hash
|
|
436
|
+
);
|
|
437
|
+
println!(
|
|
438
|
+
" actual_state_hash: {}",
|
|
439
|
+
result.actual_state_hash
|
|
440
|
+
);
|
|
441
|
+
println!(
|
|
442
|
+
" match: {}",
|
|
443
|
+
if result.correctness_verified {
|
|
444
|
+
"PASS"
|
|
445
|
+
} else {
|
|
446
|
+
"FAIL"
|
|
447
|
+
}
|
|
381
448
|
);
|
|
382
449
|
}
|
|
383
450
|
}
|
package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
/// 4. Delayed: Messages arrive late → eventual convergence ✓
|
|
15
15
|
/// 5. Dropped + Retx: Message lost then resent → reconciliation recovers it
|
|
16
16
|
/// 6. Causal dependency: Operation depends on prior → waiting/ready state correct
|
|
17
|
-
|
|
18
17
|
use feltdb::{
|
|
19
18
|
convergence::VectorClock,
|
|
20
19
|
distributed_transactions::{DistributedTransactionExecutor, ReplicationMessage},
|
|
@@ -116,7 +115,8 @@ fn main() {
|
|
|
116
115
|
async fn test_basic_2replica_convergence() {
|
|
117
116
|
// Setup: 1 leader + 1 replica
|
|
118
117
|
let initial_hash = StateHash::from_hex("hash0".to_string());
|
|
119
|
-
let mut leader =
|
|
118
|
+
let mut leader =
|
|
119
|
+
DistributedTransactionExecutor::new("leader".to_string(), initial_hash.clone());
|
|
120
120
|
leader.register_replica("replica_1".to_string(), initial_hash.clone());
|
|
121
121
|
let mut replica = DistributedTransactionExecutor::new("replica_1".to_string(), initial_hash);
|
|
122
122
|
|
|
@@ -155,7 +155,10 @@ async fn test_basic_2replica_convergence() {
|
|
|
155
155
|
|
|
156
156
|
match result {
|
|
157
157
|
Ok(envelope) => {
|
|
158
|
-
leader_hashes.push(format!(
|
|
158
|
+
leader_hashes.push(format!(
|
|
159
|
+
"{:?}",
|
|
160
|
+
leader.get_replica_state("leader").map(|s| s.state_hash)
|
|
161
|
+
));
|
|
159
162
|
println!(" ✓ Leader applied operation {}", seq);
|
|
160
163
|
|
|
161
164
|
// Replicate to follower
|
|
@@ -203,7 +206,8 @@ async fn test_basic_2replica_convergence() {
|
|
|
203
206
|
|
|
204
207
|
async fn test_duplicate_message_handling() {
|
|
205
208
|
let initial_hash = StateHash::from_hex("hash0".to_string());
|
|
206
|
-
let mut leader =
|
|
209
|
+
let mut leader =
|
|
210
|
+
DistributedTransactionExecutor::new("leader".to_string(), initial_hash.clone());
|
|
207
211
|
leader.register_replica("replica_1".to_string(), initial_hash.clone());
|
|
208
212
|
let mut replica = DistributedTransactionExecutor::new("replica_1".to_string(), initial_hash);
|
|
209
213
|
|
|
@@ -247,14 +251,28 @@ async fn test_duplicate_message_handling() {
|
|
|
247
251
|
msg.clone(),
|
|
248
252
|
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
249
253
|
);
|
|
250
|
-
println!(
|
|
254
|
+
println!(
|
|
255
|
+
" First delivery: {}",
|
|
256
|
+
if result1.is_ok() {
|
|
257
|
+
"✓ OK"
|
|
258
|
+
} else {
|
|
259
|
+
"✗ Failed"
|
|
260
|
+
}
|
|
261
|
+
);
|
|
251
262
|
|
|
252
263
|
// Deliver same message again (duplicate)
|
|
253
264
|
let result2 = replica.receive_replicated_transaction(
|
|
254
265
|
msg,
|
|
255
266
|
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
256
267
|
);
|
|
257
|
-
println!(
|
|
268
|
+
println!(
|
|
269
|
+
" Duplicate delivery: {}",
|
|
270
|
+
if result2.is_err() {
|
|
271
|
+
"✓ Rejected (correct)"
|
|
272
|
+
} else {
|
|
273
|
+
"✗ Accepted (wrong!)"
|
|
274
|
+
}
|
|
275
|
+
);
|
|
258
276
|
|
|
259
277
|
// Verify only applied once
|
|
260
278
|
if let Some(state) = replica.get_replica_state("replica_1") {
|
|
@@ -264,7 +282,8 @@ async fn test_duplicate_message_handling() {
|
|
|
264
282
|
|
|
265
283
|
async fn test_out_of_order_delivery() {
|
|
266
284
|
let initial_hash = StateHash::from_hex("hash0".to_string());
|
|
267
|
-
let mut leader =
|
|
285
|
+
let mut leader =
|
|
286
|
+
DistributedTransactionExecutor::new("leader".to_string(), initial_hash.clone());
|
|
268
287
|
leader.register_replica("replica_1".to_string(), initial_hash.clone());
|
|
269
288
|
let mut replica = DistributedTransactionExecutor::new("replica_1".to_string(), initial_hash);
|
|
270
289
|
|
|
@@ -317,19 +336,27 @@ async fn test_out_of_order_delivery() {
|
|
|
317
336
|
msg,
|
|
318
337
|
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
319
338
|
);
|
|
320
|
-
println!(
|
|
339
|
+
println!(
|
|
340
|
+
" Operation {} delivered: {}",
|
|
341
|
+
seq,
|
|
342
|
+
if result.is_ok() { "✓" } else { "✗" }
|
|
343
|
+
);
|
|
321
344
|
}
|
|
322
345
|
}
|
|
323
346
|
|
|
324
347
|
// Verify replica eventually has all operations
|
|
325
348
|
if let Some(state) = replica.get_replica_state("replica_1") {
|
|
326
|
-
println!(
|
|
349
|
+
println!(
|
|
350
|
+
" Replica has {} operations (expected 3)",
|
|
351
|
+
state.operations_applied
|
|
352
|
+
);
|
|
327
353
|
}
|
|
328
354
|
}
|
|
329
355
|
|
|
330
356
|
async fn test_delayed_delivery() {
|
|
331
357
|
let initial_hash = StateHash::from_hex("hash0".to_string());
|
|
332
|
-
let mut leader =
|
|
358
|
+
let mut leader =
|
|
359
|
+
DistributedTransactionExecutor::new("leader".to_string(), initial_hash.clone());
|
|
333
360
|
leader.register_replica("replica_1".to_string(), initial_hash.clone());
|
|
334
361
|
let mut replica = DistributedTransactionExecutor::new("replica_1".to_string(), initial_hash);
|
|
335
362
|
|
|
@@ -361,12 +388,7 @@ async fn test_delayed_delivery() {
|
|
|
361
388
|
)
|
|
362
389
|
.expect("Leader execution failed");
|
|
363
390
|
|
|
364
|
-
let msg = ReplicationMessage::new(
|
|
365
|
-
envelope,
|
|
366
|
-
"leader".to_string(),
|
|
367
|
-
"replica_1".to_string(),
|
|
368
|
-
0,
|
|
369
|
-
);
|
|
391
|
+
let msg = ReplicationMessage::new(envelope, "leader".to_string(), "replica_1".to_string(), 0);
|
|
370
392
|
|
|
371
393
|
println!(" Simulating delay...");
|
|
372
394
|
// In a real test, this would use tokio::time::sleep, but for now just immediate delivery
|
|
@@ -374,7 +396,14 @@ async fn test_delayed_delivery() {
|
|
|
374
396
|
msg,
|
|
375
397
|
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
376
398
|
);
|
|
377
|
-
println!(
|
|
399
|
+
println!(
|
|
400
|
+
" After delay delivery: {}",
|
|
401
|
+
if result.is_ok() {
|
|
402
|
+
"✓ OK"
|
|
403
|
+
} else {
|
|
404
|
+
"✗ Failed"
|
|
405
|
+
}
|
|
406
|
+
);
|
|
378
407
|
|
|
379
408
|
// Verify convergence
|
|
380
409
|
let leader_state = leader.get_replica_state("leader");
|
|
@@ -398,7 +427,8 @@ async fn test_delayed_delivery() {
|
|
|
398
427
|
|
|
399
428
|
async fn test_drop_and_retransmission() {
|
|
400
429
|
let initial_hash = StateHash::from_hex("hash0".to_string());
|
|
401
|
-
let mut leader =
|
|
430
|
+
let mut leader =
|
|
431
|
+
DistributedTransactionExecutor::new("leader".to_string(), initial_hash.clone());
|
|
402
432
|
leader.register_replica("replica_1".to_string(), initial_hash.clone());
|
|
403
433
|
let mut replica = DistributedTransactionExecutor::new("replica_1".to_string(), initial_hash);
|
|
404
434
|
|
|
@@ -445,7 +475,14 @@ async fn test_drop_and_retransmission() {
|
|
|
445
475
|
msg,
|
|
446
476
|
StateVersion::new(VectorClock::new(), "hash0".to_string()),
|
|
447
477
|
);
|
|
448
|
-
println!(
|
|
478
|
+
println!(
|
|
479
|
+
" Message retransmitted: {}",
|
|
480
|
+
if result.is_ok() {
|
|
481
|
+
"✓ OK"
|
|
482
|
+
} else {
|
|
483
|
+
"✗ Failed"
|
|
484
|
+
}
|
|
485
|
+
);
|
|
449
486
|
|
|
450
487
|
// Verify convergence
|
|
451
488
|
let leader_state = leader.get_replica_state("leader");
|
|
@@ -466,4 +503,3 @@ async fn test_drop_and_retransmission() {
|
|
|
466
503
|
_ => println!(" ✗ State retrieval failed"),
|
|
467
504
|
}
|
|
468
505
|
}
|
|
469
|
-
|
|
@@ -26,14 +26,15 @@
|
|
|
26
26
|
/// - Convergence time
|
|
27
27
|
/// - Peak heap per replica
|
|
28
28
|
/// - State hash correctness across all replicas
|
|
29
|
-
|
|
30
29
|
use feltdb::{
|
|
31
|
-
convergence::VectorClock,
|
|
30
|
+
convergence::VectorClock,
|
|
31
|
+
distributed_transactions::{
|
|
32
32
|
DistributedTransactionExecutor, ReplicationMessage, TransactionEnvelope,
|
|
33
33
|
},
|
|
34
|
-
state_hash::StateHash,
|
|
34
|
+
state_hash::StateHash,
|
|
35
|
+
submission::SubmissionManager,
|
|
35
36
|
transaction_api::TransactionManager,
|
|
36
|
-
transactions::{Operation, OperationCommand, OperationId, StateVersion
|
|
37
|
+
transactions::{ConsistencyContract, Operation, OperationCommand, OperationId, StateVersion},
|
|
37
38
|
};
|
|
38
39
|
use std::collections::HashMap;
|
|
39
40
|
use std::sync::{Arc, Mutex};
|
|
@@ -73,7 +74,13 @@ impl SeededRng {
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
fn generate_operation(
|
|
77
|
+
fn generate_operation(
|
|
78
|
+
rng: &mut SeededRng,
|
|
79
|
+
op_id: u64,
|
|
80
|
+
seq: u64,
|
|
81
|
+
node: &str,
|
|
82
|
+
client_id: usize,
|
|
83
|
+
) -> Operation {
|
|
77
84
|
let record_id = format!("rec_{}", rng.next_range(0, 99));
|
|
78
85
|
let _is_write = rng.next_f64() >= 0.7; // 70% reads, 30% writes
|
|
79
86
|
|
|
@@ -155,15 +162,13 @@ struct ReplicationMetrics {
|
|
|
155
162
|
// MEASUREMENT
|
|
156
163
|
// ============================================================================
|
|
157
164
|
|
|
158
|
-
async fn measure_replication_level(
|
|
159
|
-
writers: usize,
|
|
160
|
-
replica_count: usize,
|
|
161
|
-
) -> ReplicationMetrics {
|
|
165
|
+
async fn measure_replication_level(writers: usize, replica_count: usize) -> ReplicationMetrics {
|
|
162
166
|
// Initialize distributed system with N replicas
|
|
163
167
|
let initial_hash = StateHash::from_hex("hash0".to_string());
|
|
164
|
-
let leader_executor = Arc::new(Mutex::new(
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
let leader_executor = Arc::new(Mutex::new(DistributedTransactionExecutor::new(
|
|
169
|
+
"leader".to_string(),
|
|
170
|
+
initial_hash.clone(),
|
|
171
|
+
)));
|
|
167
172
|
|
|
168
173
|
// Create follower replicas
|
|
169
174
|
let mut follower_executors = Vec::new();
|
|
@@ -173,9 +178,10 @@ async fn measure_replication_level(
|
|
|
173
178
|
let replica_id = format!("replica_{}", i);
|
|
174
179
|
leader.register_replica(replica_id.clone(), initial_hash.clone());
|
|
175
180
|
|
|
176
|
-
let replica_executor = Arc::new(Mutex::new(
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
let replica_executor = Arc::new(Mutex::new(DistributedTransactionExecutor::new(
|
|
182
|
+
replica_id,
|
|
183
|
+
initial_hash.clone(),
|
|
184
|
+
)));
|
|
179
185
|
follower_executors.push(replica_executor);
|
|
180
186
|
}
|
|
181
187
|
}
|
|
@@ -188,7 +194,10 @@ async fn measure_replication_level(
|
|
|
188
194
|
for client_id in 0..writers {
|
|
189
195
|
let outcomes = Arc::clone(&outcomes);
|
|
190
196
|
let leader = Arc::clone(&leader_executor);
|
|
191
|
-
let followers = follower_executors
|
|
197
|
+
let followers = follower_executors
|
|
198
|
+
.iter()
|
|
199
|
+
.map(Arc::clone)
|
|
200
|
+
.collect::<Vec<_>>();
|
|
192
201
|
let global_seq = Arc::clone(&global_seq);
|
|
193
202
|
|
|
194
203
|
let handle = tokio::spawn(async move {
|
|
@@ -279,13 +288,16 @@ async fn measure_replication_level(
|
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
let avg_applied = if !operations_applied_per_replica.is_empty() {
|
|
282
|
-
operations_applied_per_replica.iter().sum::<usize>() as f64
|
|
291
|
+
operations_applied_per_replica.iter().sum::<usize>() as f64
|
|
292
|
+
/ operations_applied_per_replica.len() as f64
|
|
283
293
|
} else {
|
|
284
294
|
0.0
|
|
285
295
|
};
|
|
286
296
|
|
|
287
297
|
// Check convergence: all replicas should have applied same operations
|
|
288
|
-
let all_same = operations_applied_per_replica
|
|
298
|
+
let all_same = operations_applied_per_replica
|
|
299
|
+
.iter()
|
|
300
|
+
.all(|&c| c == operations_accepted);
|
|
289
301
|
let converged = all_same && operations_applied_per_replica.len() == replica_count;
|
|
290
302
|
|
|
291
303
|
// Estimate lag
|
|
@@ -295,7 +307,10 @@ async fn measure_replication_level(
|
|
|
295
307
|
0
|
|
296
308
|
};
|
|
297
309
|
|
|
298
|
-
let correctness_check = converged
|
|
310
|
+
let correctness_check = converged
|
|
311
|
+
&& operations_applied_per_replica
|
|
312
|
+
.iter()
|
|
313
|
+
.all(|&c| c == operations_accepted);
|
|
299
314
|
|
|
300
315
|
ReplicationMetrics {
|
|
301
316
|
replica_count,
|
|
@@ -346,59 +361,119 @@ fn main() {
|
|
|
346
361
|
println!("\nTEST MATRIX:");
|
|
347
362
|
println!(" Writers: {:?}", WRITERS);
|
|
348
363
|
println!(" Replica counts: {:?}", REPLICA_COUNTS);
|
|
349
|
-
println!(
|
|
364
|
+
println!(
|
|
365
|
+
" Total combinations: {}\n",
|
|
366
|
+
WRITERS.len() * REPLICA_COUNTS.len()
|
|
367
|
+
);
|
|
350
368
|
|
|
351
369
|
for &writer_count in WRITERS {
|
|
352
370
|
println!("\n--- Writers: {} ---", writer_count);
|
|
353
371
|
for &replica_count in REPLICA_COUNTS {
|
|
354
372
|
let result = rt.block_on(measure_replication_level(writer_count, replica_count));
|
|
355
373
|
|
|
356
|
-
println!(
|
|
374
|
+
println!(
|
|
375
|
+
"\n=== Writers={} Replicas={} ===",
|
|
376
|
+
writer_count, replica_count
|
|
377
|
+
);
|
|
357
378
|
println!("\nREPLICATION SEMANTICS:");
|
|
358
|
-
println!(
|
|
359
|
-
|
|
360
|
-
|
|
379
|
+
println!(
|
|
380
|
+
" operations_attempted: {}",
|
|
381
|
+
result.operations_attempted
|
|
382
|
+
);
|
|
383
|
+
println!(
|
|
384
|
+
" operations_accepted_by_leader: {}",
|
|
385
|
+
result.operations_accepted_by_leader
|
|
386
|
+
);
|
|
387
|
+
println!(
|
|
388
|
+
" acceptance_rate_percent: {:.1}%",
|
|
389
|
+
result.acceptance_rate_percent
|
|
390
|
+
);
|
|
361
391
|
|
|
362
392
|
println!("\nREPLICATION THROUGHPUT:");
|
|
363
|
-
println!(
|
|
393
|
+
println!(
|
|
394
|
+
" replication_throughput_ops_sec: {:.3}",
|
|
364
395
|
if result.convergence_time_us > 0 {
|
|
365
|
-
(result.operations_accepted_by_leader as f64 * 1_000_000.0)
|
|
396
|
+
(result.operations_accepted_by_leader as f64 * 1_000_000.0)
|
|
397
|
+
/ result.convergence_time_us as f64
|
|
366
398
|
} else {
|
|
367
399
|
0.0
|
|
368
|
-
}
|
|
400
|
+
}
|
|
401
|
+
);
|
|
369
402
|
for (i, &applied) in result.operations_applied_per_replica.iter().enumerate() {
|
|
370
403
|
if i == 0 {
|
|
371
404
|
println!(" operations_applied_by_leader: {}", applied);
|
|
372
405
|
} else {
|
|
373
|
-
println!(
|
|
406
|
+
println!(
|
|
407
|
+
" operations_applied_by_replica_{}: {}",
|
|
408
|
+
i - 1,
|
|
409
|
+
applied
|
|
410
|
+
);
|
|
374
411
|
}
|
|
375
412
|
}
|
|
376
413
|
|
|
377
414
|
println!("\nREPLICATION LATENCY:");
|
|
378
|
-
println!(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
415
|
+
println!(
|
|
416
|
+
" p50_replication_lag_us: {}",
|
|
417
|
+
result.p50_replication_lag_us
|
|
418
|
+
);
|
|
419
|
+
println!(
|
|
420
|
+
" p95_replication_lag_us: {}",
|
|
421
|
+
result.p95_replication_lag_us
|
|
422
|
+
);
|
|
423
|
+
println!(
|
|
424
|
+
" p99_replication_lag_us: {}",
|
|
425
|
+
result.p99_replication_lag_us
|
|
426
|
+
);
|
|
427
|
+
println!(
|
|
428
|
+
" max_replication_lag_us: {}",
|
|
429
|
+
result.max_replication_lag_us
|
|
430
|
+
);
|
|
382
431
|
|
|
383
432
|
println!("\nCONVERGENCE:");
|
|
384
|
-
println!(
|
|
385
|
-
|
|
386
|
-
|
|
433
|
+
println!(
|
|
434
|
+
" convergence_time_us: {}",
|
|
435
|
+
result.convergence_time_us
|
|
436
|
+
);
|
|
437
|
+
println!(
|
|
438
|
+
" all_replicas_converged: {}",
|
|
439
|
+
if result.all_replicas_converged {
|
|
440
|
+
"YES"
|
|
441
|
+
} else {
|
|
442
|
+
"NO"
|
|
443
|
+
}
|
|
444
|
+
);
|
|
387
445
|
|
|
388
446
|
println!("\nRECONCILIATION:");
|
|
389
|
-
println!(
|
|
390
|
-
|
|
447
|
+
println!(
|
|
448
|
+
" reconciliation_messages: {}",
|
|
449
|
+
result.reconciliation_messages
|
|
450
|
+
);
|
|
451
|
+
println!(
|
|
452
|
+
" network_amplification: {:.3}",
|
|
453
|
+
result.network_amplification
|
|
454
|
+
);
|
|
391
455
|
|
|
392
456
|
println!("\nRESOURCES:");
|
|
393
|
-
println!(
|
|
457
|
+
println!(
|
|
458
|
+
" peak_heap_kb: {}",
|
|
459
|
+
result.peak_heap_kb
|
|
460
|
+
);
|
|
394
461
|
|
|
395
462
|
println!("\nCORRECTNESS:");
|
|
396
|
-
println!(
|
|
463
|
+
println!(
|
|
464
|
+
" expected_state_hash: {}",
|
|
465
|
+
result.expected_hash
|
|
466
|
+
);
|
|
397
467
|
for (i, hash) in result.replica_hashes.iter().enumerate() {
|
|
398
468
|
println!(" replica_{}_state_hash: {}", i, hash);
|
|
399
469
|
}
|
|
400
|
-
println!(
|
|
401
|
-
|
|
470
|
+
println!(
|
|
471
|
+
" correctness_verified: {}",
|
|
472
|
+
if result.correctness_verified {
|
|
473
|
+
"PASS"
|
|
474
|
+
} else {
|
|
475
|
+
"FAIL"
|
|
476
|
+
}
|
|
402
477
|
);
|
|
403
478
|
}
|
|
404
479
|
}
|