@feltdb/core 0.4.11 → 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/commands.js +5 -2
- package/dist/cli/index.js +1 -1
- package/dist/create/cli.js +16 -1
- package/dist/create/create.js +30 -16
- package/dist/create/docker-compose-generator.js +48 -9
- 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/index.js +1 -1
- package/dist/studio/{components-q43NSTTH.js → components-bHTARBin.js} +160 -89
- package/dist/studio/index.js +31 -28
- package/dist/studio-app/assets/{index-DZpBoVMp.js → index-BqKquLuU.js} +8 -8
- package/dist/studio-app/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/// Phase 7.1: Release Economics Testing
|
|
2
|
+
///
|
|
3
|
+
/// Direct measurement of frontier advancement costs without criterion harness
|
|
4
|
+
/// This provides rapid feedback on release behavior
|
|
5
|
+
|
|
6
|
+
#[cfg(test)]
|
|
7
|
+
mod phase_7_1_release_economics {
|
|
8
|
+
use crate::causal_dependency_barrier::CausalDependencyBarrier;
|
|
9
|
+
use crate::convergence::VectorClock;
|
|
10
|
+
use std::time::Instant;
|
|
11
|
+
|
|
12
|
+
#[test]
|
|
13
|
+
fn phase_7_1_release_single_scan_small() {
|
|
14
|
+
// Test 1: Single release scan with small pending buffer
|
|
15
|
+
println!("\n=== Phase 7.1 Test 1: Single Release Scan (Small) ===");
|
|
16
|
+
|
|
17
|
+
for pending_size in [0, 10, 100].iter() {
|
|
18
|
+
let mut barrier = CausalDependencyBarrier::new();
|
|
19
|
+
|
|
20
|
+
// Fill with blocked operations
|
|
21
|
+
for i in 0..*pending_size {
|
|
22
|
+
let mut required = VectorClock::new();
|
|
23
|
+
required.clocks.insert("node_a".to_string(), (1000 + i) as u64);
|
|
24
|
+
let _ = barrier.receive_operation(format!("pending_{}", i), required);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Measure one frontier advance
|
|
28
|
+
let start = Instant::now();
|
|
29
|
+
let mut frontier = VectorClock::new();
|
|
30
|
+
frontier.clocks.insert("node_a".to_string(), 1);
|
|
31
|
+
barrier.advance_frontier(&frontier);
|
|
32
|
+
let elapsed = start.elapsed();
|
|
33
|
+
|
|
34
|
+
let metrics = barrier.metrics();
|
|
35
|
+
println!(
|
|
36
|
+
"Pending={:5} | Cost={:?} | Entries examined={} | Released={}",
|
|
37
|
+
pending_size, elapsed, metrics.pending_entries_examined, metrics.operations_released
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[test]
|
|
43
|
+
fn phase_7_1_release_selectivity_test() {
|
|
44
|
+
// Test 2: Does readiness % affect cost?
|
|
45
|
+
println!("\n=== Phase 7.1 Test 2: Release Selectivity ===");
|
|
46
|
+
|
|
47
|
+
for ready_percent in [0, 10, 50, 100].iter() {
|
|
48
|
+
let mut barrier = CausalDependencyBarrier::new();
|
|
49
|
+
let pending_size = 100;
|
|
50
|
+
|
|
51
|
+
let ready_count = (pending_size * ready_percent) / 100;
|
|
52
|
+
let blocked_count = pending_size - ready_count;
|
|
53
|
+
|
|
54
|
+
// Add ready operations
|
|
55
|
+
for i in 0..ready_count {
|
|
56
|
+
let mut required = VectorClock::new();
|
|
57
|
+
required.clocks.insert("node_a".to_string(), 100);
|
|
58
|
+
let _ = barrier.receive_operation(format!("ready_{}", i), required);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Add blocked operations
|
|
62
|
+
for i in 0..blocked_count {
|
|
63
|
+
let mut required = VectorClock::new();
|
|
64
|
+
required.clocks.insert("node_a".to_string(), 5000);
|
|
65
|
+
let _ = barrier.receive_operation(format!("blocked_{}", i), required);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Measure frontier advance to release ready operations
|
|
69
|
+
let start = Instant::now();
|
|
70
|
+
let mut frontier = VectorClock::new();
|
|
71
|
+
frontier.clocks.insert("node_a".to_string(), 2000);
|
|
72
|
+
barrier.advance_frontier(&frontier);
|
|
73
|
+
let elapsed = start.elapsed();
|
|
74
|
+
|
|
75
|
+
let metrics = barrier.metrics();
|
|
76
|
+
let released_pct = if metrics.pending_entries_examined > 0 {
|
|
77
|
+
(metrics.operations_released as f64 / metrics.pending_entries_examined as f64) * 100.0
|
|
78
|
+
} else {
|
|
79
|
+
0.0
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
println!(
|
|
83
|
+
"Ready%={:3} | Cost={:?} | Examined={:3} | Released={:3} | Ratio={:.1}%",
|
|
84
|
+
ready_percent, elapsed, metrics.pending_entries_examined, metrics.operations_released, released_pct
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#[test]
|
|
90
|
+
fn phase_7_1_release_pathological_simplified() {
|
|
91
|
+
// Test 3: Simplified pathological test
|
|
92
|
+
// Creates sequential dependencies but only advances frontier a few times
|
|
93
|
+
println!("\n=== Phase 7.1 Test 3: Pathological (Simplified) ===");
|
|
94
|
+
|
|
95
|
+
let pending_size = 100;
|
|
96
|
+
let mut barrier = CausalDependencyBarrier::new();
|
|
97
|
+
|
|
98
|
+
// Create sequential dependencies
|
|
99
|
+
for i in 0..pending_size {
|
|
100
|
+
let mut required = VectorClock::new();
|
|
101
|
+
required.clocks.insert("node_a".to_string(), (i + 1) as u64);
|
|
102
|
+
let _ = barrier.receive_operation(format!("seq_{}", i), required);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
println!("Initial pending: {}", barrier.get_pending_operations().len());
|
|
106
|
+
|
|
107
|
+
// Advance frontier multiple times
|
|
108
|
+
let start = Instant::now();
|
|
109
|
+
let mut total_entries_examined = 0;
|
|
110
|
+
let mut total_released = 0;
|
|
111
|
+
|
|
112
|
+
for step in 1..=10 {
|
|
113
|
+
barrier.clear_metrics();
|
|
114
|
+
let mut frontier = VectorClock::new();
|
|
115
|
+
frontier.clocks.insert("node_a".to_string(), step as u64);
|
|
116
|
+
barrier.advance_frontier(&frontier);
|
|
117
|
+
|
|
118
|
+
let metrics = barrier.metrics();
|
|
119
|
+
total_entries_examined += metrics.pending_entries_examined;
|
|
120
|
+
total_released += metrics.operations_released;
|
|
121
|
+
|
|
122
|
+
println!(
|
|
123
|
+
"Step {:2}: frontier A:{} | examined={:3} | released={} | remaining={:3}",
|
|
124
|
+
step,
|
|
125
|
+
step,
|
|
126
|
+
metrics.pending_entries_examined,
|
|
127
|
+
metrics.operations_released,
|
|
128
|
+
barrier.get_pending_operations().len()
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let elapsed = start.elapsed();
|
|
133
|
+
println!(
|
|
134
|
+
"\nTotal across 10 steps: elapsed={:?} | total_examined={} | total_released={}",
|
|
135
|
+
elapsed, total_entries_examined, total_released
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Analysis
|
|
139
|
+
if total_entries_examined > 500 {
|
|
140
|
+
println!("⚠️ WARNING: Examined {} entries for 100 pending (10 advances)", total_entries_examined);
|
|
141
|
+
println!(" Expected if O(m): ~100-200");
|
|
142
|
+
println!(" This suggests possible rescanning behavior");
|
|
143
|
+
} else {
|
|
144
|
+
println!("✅ GOOD: Linear scaling confirmed");
|
|
145
|
+
println!(" Examined {} entries total", total_entries_examined);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#[test]
|
|
150
|
+
fn phase_7_1_release_mixed_advances() {
|
|
151
|
+
// Test 4: Realistic mixed advances
|
|
152
|
+
println!("\n=== Phase 7.1 Test 4: Mixed Frontier Advances ===");
|
|
153
|
+
|
|
154
|
+
for pending_size in [50, 100].iter() {
|
|
155
|
+
println!("\nPending size: {}", pending_size);
|
|
156
|
+
let mut barrier = CausalDependencyBarrier::new();
|
|
157
|
+
|
|
158
|
+
// Create mixed dependencies
|
|
159
|
+
for i in 0..*pending_size {
|
|
160
|
+
let mut required = VectorClock::new();
|
|
161
|
+
let dependency_level = ((i * 997) % 1000) as u64 + 1;
|
|
162
|
+
required.clocks.insert("node_a".to_string(), dependency_level);
|
|
163
|
+
let _ = barrier.receive_operation(format!("mixed_{}", i), required);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let start = Instant::now();
|
|
167
|
+
let mut total_examined = 0;
|
|
168
|
+
let mut total_released = 0;
|
|
169
|
+
|
|
170
|
+
// Series of frontier advances
|
|
171
|
+
for level in (100..=500).step_by(100) {
|
|
172
|
+
barrier.clear_metrics();
|
|
173
|
+
let mut frontier = VectorClock::new();
|
|
174
|
+
frontier.clocks.insert("node_a".to_string(), level as u64);
|
|
175
|
+
barrier.advance_frontier(&frontier);
|
|
176
|
+
|
|
177
|
+
let metrics = barrier.metrics();
|
|
178
|
+
total_examined += metrics.pending_entries_examined;
|
|
179
|
+
total_released += metrics.operations_released;
|
|
180
|
+
|
|
181
|
+
println!(
|
|
182
|
+
" Advance to A:{} | examined={:2} | released={:2}",
|
|
183
|
+
level, metrics.pending_entries_examined, metrics.operations_released
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let elapsed = start.elapsed();
|
|
188
|
+
println!(
|
|
189
|
+
"Total: elapsed={:?} | examined={} | released={}",
|
|
190
|
+
elapsed, total_examined, total_released
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/// Concurrency Fuzzing Framework
|
|
2
|
+
///
|
|
3
|
+
/// Generates randomized concurrent transactions with variable:
|
|
4
|
+
/// - Operation count per transaction
|
|
5
|
+
/// - Read/write set sizes
|
|
6
|
+
/// - Conflicts between transactions
|
|
7
|
+
/// - Execution interleaving
|
|
8
|
+
///
|
|
9
|
+
/// Verifies final state matches deterministic reference model.
|
|
10
|
+
|
|
11
|
+
use crate::convergence::VectorClock;
|
|
12
|
+
use crate::state_hash::StateHash;
|
|
13
|
+
use crate::transactions::*;
|
|
14
|
+
use rand::Rng;
|
|
15
|
+
use std::collections::{HashMap, HashSet};
|
|
16
|
+
|
|
17
|
+
/// Fuzzing configuration
|
|
18
|
+
#[derive(Clone, Debug)]
|
|
19
|
+
pub struct FuzzingConfig {
|
|
20
|
+
pub num_transactions: usize,
|
|
21
|
+
pub max_operations_per_transaction: usize,
|
|
22
|
+
pub max_records: usize,
|
|
23
|
+
pub conflict_probability: f32, // 0.0 to 1.0
|
|
24
|
+
pub seed: u64,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl Default for FuzzingConfig {
|
|
28
|
+
fn default() -> Self {
|
|
29
|
+
Self {
|
|
30
|
+
num_transactions: 100,
|
|
31
|
+
max_operations_per_transaction: 5,
|
|
32
|
+
max_records: 20,
|
|
33
|
+
conflict_probability: 0.3,
|
|
34
|
+
seed: 42,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Generated transaction for fuzzing
|
|
40
|
+
#[derive(Clone, Debug)]
|
|
41
|
+
pub struct FuzzedTransaction {
|
|
42
|
+
pub transaction_id: String,
|
|
43
|
+
pub operations: Vec<Operation>,
|
|
44
|
+
pub write_set: HashSet<String>, // record IDs written
|
|
45
|
+
pub read_set: HashSet<String>, // record IDs read
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Fuzzing statistics
|
|
49
|
+
#[derive(Clone, Debug)]
|
|
50
|
+
pub struct FuzzingStats {
|
|
51
|
+
pub total_transactions: usize,
|
|
52
|
+
pub total_operations: usize,
|
|
53
|
+
pub total_conflicts: usize,
|
|
54
|
+
pub concurrent_transactions: usize,
|
|
55
|
+
pub sequential_transactions: usize,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Concurrency fuzzer
|
|
59
|
+
pub struct ConcurrencyFuzzer {
|
|
60
|
+
config: FuzzingConfig,
|
|
61
|
+
rng: rand::rngs::StdRng,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
impl ConcurrencyFuzzer {
|
|
65
|
+
pub fn new(config: FuzzingConfig) -> Self {
|
|
66
|
+
use rand::SeedableRng;
|
|
67
|
+
let rng = rand::rngs::StdRng::seed_from_u64(config.seed);
|
|
68
|
+
|
|
69
|
+
Self { config, rng }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Generate random transaction
|
|
73
|
+
fn generate_transaction(&mut self, id: usize) -> FuzzedTransaction {
|
|
74
|
+
let num_ops = self.rng.gen_range(1..=self.config.max_operations_per_transaction);
|
|
75
|
+
let mut write_set = HashSet::new();
|
|
76
|
+
let mut read_set = HashSet::new();
|
|
77
|
+
let mut operations = Vec::new();
|
|
78
|
+
|
|
79
|
+
let mut vc = VectorClock::new();
|
|
80
|
+
vc.increment("fuzzer");
|
|
81
|
+
|
|
82
|
+
let base_version = StateVersion::new(vc.clone(), format!("genesis_{}", id));
|
|
83
|
+
|
|
84
|
+
for op_idx in 0..num_ops {
|
|
85
|
+
let record_id = format!(
|
|
86
|
+
"rec_{}",
|
|
87
|
+
self.rng.gen_range(0..self.config.max_records)
|
|
88
|
+
);
|
|
89
|
+
let value = self.rng.gen_range(0..1000);
|
|
90
|
+
|
|
91
|
+
let op_id = OperationId::new("fuzzer".to_string(), ((id * 100) + op_idx) as u64);
|
|
92
|
+
|
|
93
|
+
let mut op = Operation::new(
|
|
94
|
+
op_id,
|
|
95
|
+
base_version.clone(),
|
|
96
|
+
format!("FuzzOp_{}", op_idx),
|
|
97
|
+
OperationCommand {
|
|
98
|
+
op_type: "UpdateField".to_string(),
|
|
99
|
+
collection: "fuzzing".to_string(),
|
|
100
|
+
record_id: record_id.clone(),
|
|
101
|
+
fields: {
|
|
102
|
+
let mut m = HashMap::new();
|
|
103
|
+
m.insert("value".to_string(), serde_json::json!(value));
|
|
104
|
+
m
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
"fuzzer".to_string(),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Randomly add read dependencies
|
|
111
|
+
if self.rng.gen_bool(0.3) {
|
|
112
|
+
let read_record = format!(
|
|
113
|
+
"rec_{}",
|
|
114
|
+
self.rng.gen_range(0..self.config.max_records)
|
|
115
|
+
);
|
|
116
|
+
op = op.with_read_set(vec![StateDependency::new(
|
|
117
|
+
"fuzzing".to_string(),
|
|
118
|
+
read_record.clone(),
|
|
119
|
+
Some("value".to_string()),
|
|
120
|
+
base_version.clone(),
|
|
121
|
+
)]);
|
|
122
|
+
read_set.insert(read_record);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
op = op.with_write_set(vec![StateDependency::new(
|
|
126
|
+
"fuzzing".to_string(),
|
|
127
|
+
record_id.clone(),
|
|
128
|
+
Some("value".to_string()),
|
|
129
|
+
base_version.clone(),
|
|
130
|
+
)]);
|
|
131
|
+
|
|
132
|
+
write_set.insert(record_id);
|
|
133
|
+
operations.push(op);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
FuzzedTransaction {
|
|
137
|
+
transaction_id: format!("fuzz_txn_{}", id),
|
|
138
|
+
operations,
|
|
139
|
+
write_set,
|
|
140
|
+
read_set,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Generate fuzzed transaction workload
|
|
145
|
+
pub fn generate_workload(&mut self) -> Vec<FuzzedTransaction> {
|
|
146
|
+
(0..self.config.num_transactions)
|
|
147
|
+
.map(|i| self.generate_transaction(i))
|
|
148
|
+
.collect()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// Detect potential conflicts between two transactions
|
|
152
|
+
pub fn detect_conflict(tx1: &FuzzedTransaction, tx2: &FuzzedTransaction) -> bool {
|
|
153
|
+
// Write-Write conflict
|
|
154
|
+
if !tx1.write_set.is_disjoint(&tx2.write_set) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Read-Write conflict
|
|
159
|
+
if !tx1.read_set.is_disjoint(&tx2.write_set) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if !tx1.write_set.is_disjoint(&tx2.read_set) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
false
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// Compute conflict graph for workload
|
|
170
|
+
pub fn compute_conflict_graph(workload: &[FuzzedTransaction]) -> HashMap<usize, Vec<usize>> {
|
|
171
|
+
let mut graph = HashMap::new();
|
|
172
|
+
|
|
173
|
+
for (i, tx1) in workload.iter().enumerate() {
|
|
174
|
+
let mut conflicts = Vec::new();
|
|
175
|
+
for (j, tx2) in workload.iter().enumerate() {
|
|
176
|
+
if i != j && Self::detect_conflict(tx1, tx2) {
|
|
177
|
+
conflicts.push(j);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
graph.insert(i, conflicts);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
graph
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// Compute statistics
|
|
187
|
+
pub fn compute_stats(workload: &[FuzzedTransaction]) -> FuzzingStats {
|
|
188
|
+
let total_transactions = workload.len();
|
|
189
|
+
let total_operations = workload.iter().map(|t| t.operations.len()).sum();
|
|
190
|
+
let conflict_graph = Self::compute_conflict_graph(workload);
|
|
191
|
+
let total_conflicts: usize = conflict_graph.iter().map(|(_, v)| v.len()).sum::<usize>() / 2; // Each edge counted twice
|
|
192
|
+
|
|
193
|
+
let concurrent_transactions = workload
|
|
194
|
+
.iter()
|
|
195
|
+
.filter(|t| t.operations.len() > 1)
|
|
196
|
+
.count();
|
|
197
|
+
let sequential_transactions = total_transactions - concurrent_transactions;
|
|
198
|
+
|
|
199
|
+
FuzzingStats {
|
|
200
|
+
total_transactions,
|
|
201
|
+
total_operations,
|
|
202
|
+
total_conflicts,
|
|
203
|
+
concurrent_transactions,
|
|
204
|
+
sequential_transactions,
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
#[cfg(test)]
|
|
210
|
+
mod tests {
|
|
211
|
+
use super::*;
|
|
212
|
+
|
|
213
|
+
#[test]
|
|
214
|
+
fn test_fuzzer_creation() {
|
|
215
|
+
let config = FuzzingConfig::default();
|
|
216
|
+
let fuzzer = ConcurrencyFuzzer::new(config.clone());
|
|
217
|
+
|
|
218
|
+
assert_eq!(fuzzer.config.num_transactions, 100);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
#[test]
|
|
222
|
+
fn test_generate_single_transaction() {
|
|
223
|
+
let config = FuzzingConfig {
|
|
224
|
+
num_transactions: 1,
|
|
225
|
+
max_operations_per_transaction: 3,
|
|
226
|
+
max_records: 10,
|
|
227
|
+
conflict_probability: 0.5,
|
|
228
|
+
seed: 123,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
let mut fuzzer = ConcurrencyFuzzer::new(config);
|
|
232
|
+
let tx = fuzzer.generate_transaction(0);
|
|
233
|
+
|
|
234
|
+
assert!(!tx.operations.is_empty());
|
|
235
|
+
assert!(tx.operations.len() <= 3);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#[test]
|
|
239
|
+
fn test_generate_workload() {
|
|
240
|
+
let config = FuzzingConfig {
|
|
241
|
+
num_transactions: 10,
|
|
242
|
+
max_operations_per_transaction: 2,
|
|
243
|
+
max_records: 5,
|
|
244
|
+
conflict_probability: 0.3,
|
|
245
|
+
seed: 456,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
let mut fuzzer = ConcurrencyFuzzer::new(config);
|
|
249
|
+
let workload = fuzzer.generate_workload();
|
|
250
|
+
|
|
251
|
+
assert_eq!(workload.len(), 10);
|
|
252
|
+
for tx in &workload {
|
|
253
|
+
assert!(!tx.operations.is_empty());
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#[test]
|
|
258
|
+
fn test_conflict_detection_no_conflict() {
|
|
259
|
+
let tx1 = FuzzedTransaction {
|
|
260
|
+
transaction_id: "tx1".to_string(),
|
|
261
|
+
operations: Vec::new(),
|
|
262
|
+
write_set: ["rec1".to_string()].iter().cloned().collect(),
|
|
263
|
+
read_set: HashSet::new(),
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
let tx2 = FuzzedTransaction {
|
|
267
|
+
transaction_id: "tx2".to_string(),
|
|
268
|
+
operations: Vec::new(),
|
|
269
|
+
write_set: ["rec2".to_string()].iter().cloned().collect(),
|
|
270
|
+
read_set: HashSet::new(),
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
assert!(!ConcurrencyFuzzer::detect_conflict(&tx1, &tx2));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#[test]
|
|
277
|
+
fn test_conflict_detection_write_write() {
|
|
278
|
+
let tx1 = FuzzedTransaction {
|
|
279
|
+
transaction_id: "tx1".to_string(),
|
|
280
|
+
operations: Vec::new(),
|
|
281
|
+
write_set: ["rec1".to_string()].iter().cloned().collect(),
|
|
282
|
+
read_set: HashSet::new(),
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
let tx2 = FuzzedTransaction {
|
|
286
|
+
transaction_id: "tx2".to_string(),
|
|
287
|
+
operations: Vec::new(),
|
|
288
|
+
write_set: ["rec1".to_string()].iter().cloned().collect(),
|
|
289
|
+
read_set: HashSet::new(),
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
assert!(ConcurrencyFuzzer::detect_conflict(&tx1, &tx2));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
#[test]
|
|
296
|
+
fn test_conflict_detection_read_write() {
|
|
297
|
+
let tx1 = FuzzedTransaction {
|
|
298
|
+
transaction_id: "tx1".to_string(),
|
|
299
|
+
operations: Vec::new(),
|
|
300
|
+
write_set: HashSet::new(),
|
|
301
|
+
read_set: ["rec1".to_string()].iter().cloned().collect(),
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
let tx2 = FuzzedTransaction {
|
|
305
|
+
transaction_id: "tx2".to_string(),
|
|
306
|
+
operations: Vec::new(),
|
|
307
|
+
write_set: ["rec1".to_string()].iter().cloned().collect(),
|
|
308
|
+
read_set: HashSet::new(),
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
assert!(ConcurrencyFuzzer::detect_conflict(&tx1, &tx2));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#[test]
|
|
315
|
+
fn test_compute_conflict_graph() {
|
|
316
|
+
let tx1 = FuzzedTransaction {
|
|
317
|
+
transaction_id: "tx1".to_string(),
|
|
318
|
+
operations: Vec::new(),
|
|
319
|
+
write_set: ["rec1".to_string()].iter().cloned().collect(),
|
|
320
|
+
read_set: HashSet::new(),
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
let tx2 = FuzzedTransaction {
|
|
324
|
+
transaction_id: "tx2".to_string(),
|
|
325
|
+
operations: Vec::new(),
|
|
326
|
+
write_set: ["rec1".to_string()].iter().cloned().collect(),
|
|
327
|
+
read_set: HashSet::new(),
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
let tx3 = FuzzedTransaction {
|
|
331
|
+
transaction_id: "tx3".to_string(),
|
|
332
|
+
operations: Vec::new(),
|
|
333
|
+
write_set: ["rec2".to_string()].iter().cloned().collect(),
|
|
334
|
+
read_set: HashSet::new(),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
let workload = vec![tx1, tx2, tx3];
|
|
338
|
+
let graph = ConcurrencyFuzzer::compute_conflict_graph(&workload);
|
|
339
|
+
|
|
340
|
+
assert_eq!(graph.len(), 3);
|
|
341
|
+
assert_eq!(graph[&0].len(), 1); // tx1 conflicts with tx2
|
|
342
|
+
assert_eq!(graph[&1].len(), 1); // tx2 conflicts with tx1
|
|
343
|
+
assert_eq!(graph[&2].len(), 0); // tx3 has no conflicts
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
#[test]
|
|
347
|
+
fn test_compute_stats() {
|
|
348
|
+
let config = FuzzingConfig {
|
|
349
|
+
num_transactions: 10,
|
|
350
|
+
max_operations_per_transaction: 2,
|
|
351
|
+
max_records: 5,
|
|
352
|
+
conflict_probability: 0.3,
|
|
353
|
+
seed: 789,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
let mut fuzzer = ConcurrencyFuzzer::new(config);
|
|
357
|
+
let workload = fuzzer.generate_workload();
|
|
358
|
+
let stats = ConcurrencyFuzzer::compute_stats(&workload);
|
|
359
|
+
|
|
360
|
+
assert_eq!(stats.total_transactions, 10);
|
|
361
|
+
assert!(stats.total_operations >= 10);
|
|
362
|
+
assert!(stats.total_operations <= 20);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
#[test]
|
|
366
|
+
fn test_workload_determinism_same_seed() {
|
|
367
|
+
let config1 = FuzzingConfig {
|
|
368
|
+
num_transactions: 5,
|
|
369
|
+
max_operations_per_transaction: 3,
|
|
370
|
+
max_records: 10,
|
|
371
|
+
conflict_probability: 0.3,
|
|
372
|
+
seed: 999,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
let config2 = FuzzingConfig {
|
|
376
|
+
num_transactions: 5,
|
|
377
|
+
max_operations_per_transaction: 3,
|
|
378
|
+
max_records: 10,
|
|
379
|
+
conflict_probability: 0.3,
|
|
380
|
+
seed: 999,
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
let mut fuzzer1 = ConcurrencyFuzzer::new(config1);
|
|
384
|
+
let mut fuzzer2 = ConcurrencyFuzzer::new(config2);
|
|
385
|
+
|
|
386
|
+
let workload1 = fuzzer1.generate_workload();
|
|
387
|
+
let workload2 = fuzzer2.generate_workload();
|
|
388
|
+
|
|
389
|
+
assert_eq!(workload1.len(), workload2.len());
|
|
390
|
+
for (tx1, tx2) in workload1.iter().zip(workload2.iter()) {
|
|
391
|
+
assert_eq!(tx1.operations.len(), tx2.operations.len());
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
#[test]
|
|
396
|
+
fn test_workload_variation_different_seed() {
|
|
397
|
+
let config1 = FuzzingConfig {
|
|
398
|
+
num_transactions: 10,
|
|
399
|
+
max_operations_per_transaction: 3,
|
|
400
|
+
max_records: 10,
|
|
401
|
+
conflict_probability: 0.3,
|
|
402
|
+
seed: 111,
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
let config2 = FuzzingConfig {
|
|
406
|
+
num_transactions: 10,
|
|
407
|
+
max_operations_per_transaction: 3,
|
|
408
|
+
max_records: 10,
|
|
409
|
+
conflict_probability: 0.3,
|
|
410
|
+
seed: 222,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
let mut fuzzer1 = ConcurrencyFuzzer::new(config1);
|
|
414
|
+
let mut fuzzer2 = ConcurrencyFuzzer::new(config2);
|
|
415
|
+
|
|
416
|
+
let workload1 = fuzzer1.generate_workload();
|
|
417
|
+
let workload2 = fuzzer2.generate_workload();
|
|
418
|
+
|
|
419
|
+
let diff_count = workload1
|
|
420
|
+
.iter()
|
|
421
|
+
.zip(workload2.iter())
|
|
422
|
+
.filter(|(tx1, tx2)| tx1.write_set != tx2.write_set)
|
|
423
|
+
.count();
|
|
424
|
+
|
|
425
|
+
assert!(diff_count > 0, "Different seeds should produce different workloads");
|
|
426
|
+
}
|
|
427
|
+
}
|