@feltdb/core 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +165 -0
- package/dist/create/server-source/Cargo.toml +9 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +3 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +44 -44
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +12 -24
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +7 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +13 -10
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +12 -9
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +78 -25
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +16 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +13 -5
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +41 -22
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +33 -15
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +100 -33
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +56 -20
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +116 -41
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +186 -55
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +64 -26
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +46 -15
- package/dist/create/server-source/crates/feltdb/src/admission.rs +8 -15
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +43 -13
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +15 -42
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +65 -19
- package/dist/create/server-source/crates/feltdb/src/application.rs +113 -30
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +475 -140
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +17 -15
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +266 -114
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +25 -7
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +10 -15
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +3 -11
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +14 -5
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +21 -25
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +14 -11
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +103 -22
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +18 -15
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +12 -8
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +1 -5
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +87 -23
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +3 -7
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +10 -9
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +1 -6
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +35 -38
- package/dist/create/server-source/crates/feltdb/src/lib.rs +1648 -46
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +4 -1
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +1 -2
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +107 -30
- package/dist/create/server-source/crates/feltdb/src/observability.rs +19 -6
- package/dist/create/server-source/crates/feltdb/src/operation.rs +39 -0
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +12 -11
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +9 -4
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +65 -18
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +193 -57
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +37 -27
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +38 -10
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +20 -14
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +394 -229
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +8 -6
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +11 -13
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +79 -70
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +3 -3
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +33 -11
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +6 -6
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +14 -56
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +29 -27
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +35 -17
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +8 -2
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +70 -17
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +23 -6
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +12 -3
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +41 -11
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +56 -25
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +701 -245
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +31 -13
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +5 -5
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +48 -18
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +30 -11
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +4 -3
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +36 -10
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +516 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +13 -4
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +222 -0
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +82 -54
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +2 -2
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +1565 -536
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +672 -0
- package/dist/create/server-source/crates/feltdb/src/storage.rs +9 -3
- package/dist/create/server-source/crates/feltdb/src/submission.rs +5 -11
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +24 -35
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +24 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +248 -59
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +17 -20
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +749 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +512 -4
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +3 -4
- package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +132 -0
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +272 -0
- package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +467 -0
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +309 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +445 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +518 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +392 -0
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +436 -220
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +1916 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +1878 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +1862 -0
- package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +278 -0
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +2 -1
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +80 -25
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +5 -8
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +52 -44
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +33 -16
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +22 -7
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +41 -22
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +25 -8
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +9 -6
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +120 -69
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +21 -10
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +1365 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +868 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +221 -0
- package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +417 -0
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +338 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +1039 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +739 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +427 -0
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +3 -3
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +25 -22
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +1799 -0
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +1316 -0
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +53 -61
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +156 -61
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +255 -0
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +249 -0
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +994 -0
- package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +442 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +16 -1
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +695 -47
- package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +21 -0
- package/dist/studio-app/assets/{feltdb_wasm-CVQWgXO-.js → feltdb_wasm-C1VhI-U5.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-C8HXbAXb.wasm +0 -0
- package/dist/studio-app/assets/{index-DwgNAIIX.js → index-Bbos1m2U.js} +1 -1
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workload.d.ts +2 -1
- package/dist/workload.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-CNVpvaZV.wasm +0 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
//! Compaction must not stop the world on a timer.
|
|
2
|
+
//!
|
|
3
|
+
//! An incident on the managed deployment showed `/health` returning `200` while
|
|
4
|
+
//! every application-scoped operation timed out or returned `502`, across
|
|
5
|
+
//! several minutes, on four unrelated collections at once.
|
|
6
|
+
//!
|
|
7
|
+
//! Four collections failing together is the signature of a **global stall**,
|
|
8
|
+
//! not of four collection-specific faults. The mechanism was here:
|
|
9
|
+
//!
|
|
10
|
+
//! > `compact_operation_log` takes the one lock that serializes every read and
|
|
11
|
+
//! > every write, then serializes **every row in the database** to a temp file
|
|
12
|
+
//! > and `fsync`s it — and a background task called it **every two seconds**.
|
|
13
|
+
//!
|
|
14
|
+
//! Pruning acknowledged operations from memory is cheap. Rewriting the log is
|
|
15
|
+
//! not. This file holds them apart: pruning still happens whenever it can, and
|
|
16
|
+
//! the rewrite waits until enough has accumulated to be worth the stall.
|
|
17
|
+
//!
|
|
18
|
+
//! See `docs/architecture/compaction-stall.md`.
|
|
19
|
+
|
|
20
|
+
use feltdb::state_model::StateStore;
|
|
21
|
+
use feltdb::{CompactionOutcome, CompactionPolicy, FeltDb};
|
|
22
|
+
use serde_json::json;
|
|
23
|
+
use std::sync::Arc;
|
|
24
|
+
use std::time::Instant;
|
|
25
|
+
use tempfile::TempDir;
|
|
26
|
+
|
|
27
|
+
/// Acknowledge everything a database has produced, so a compaction has work.
|
|
28
|
+
fn acknowledge_all(db: &Arc<FeltDb>) {
|
|
29
|
+
let versions = db.operation_versions().unwrap();
|
|
30
|
+
db.acknowledge_peer_versions("peer-1".to_string(), versions)
|
|
31
|
+
.unwrap();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn write_n(db: &Arc<FeltDb>, n: usize) {
|
|
35
|
+
for index in 0..n {
|
|
36
|
+
db.insert(&format!("tasks:{index}"), json!({ "n": index }))
|
|
37
|
+
.unwrap();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// The fix
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/// **A timer-driven compaction prunes without rewriting the log.**
|
|
46
|
+
///
|
|
47
|
+
/// This is the change that stops the stall. The in-memory operation log still
|
|
48
|
+
/// shrinks on every tick; the durable rewrite — the expensive half — waits.
|
|
49
|
+
#[test]
|
|
50
|
+
fn a_policy_driven_compaction_defers_the_rewrite() {
|
|
51
|
+
let directory = TempDir::new().unwrap();
|
|
52
|
+
let path = directory.path().join("defer.log");
|
|
53
|
+
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
54
|
+
db.set_compaction_policy(CompactionPolicy {
|
|
55
|
+
rewrite_after_pruned: 1000,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
write_n(&db, 20);
|
|
59
|
+
acknowledge_all(&db);
|
|
60
|
+
let before = std::fs::metadata(&path).unwrap().len();
|
|
61
|
+
|
|
62
|
+
let outcome = db
|
|
63
|
+
.maybe_compact_operation_log(&["peer-1".to_string()])
|
|
64
|
+
.unwrap();
|
|
65
|
+
|
|
66
|
+
match outcome {
|
|
67
|
+
CompactionOutcome::Deferred {
|
|
68
|
+
pruned,
|
|
69
|
+
pending_since_rewrite,
|
|
70
|
+
} => {
|
|
71
|
+
assert!(pruned > 0, "the cheap half still happened");
|
|
72
|
+
assert_eq!(pending_since_rewrite, pruned);
|
|
73
|
+
}
|
|
74
|
+
other => panic!("expected the rewrite to be deferred, got {other:?}"),
|
|
75
|
+
}
|
|
76
|
+
assert_eq!(
|
|
77
|
+
std::fs::metadata(&path).unwrap().len(),
|
|
78
|
+
before,
|
|
79
|
+
"the durable log was not rewritten"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// And the database is entirely usable.
|
|
83
|
+
let value: serde_json::Value = db.get("tasks:0").unwrap().unwrap();
|
|
84
|
+
assert_eq!(value["n"], json!(0));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// **The rewrite happens once the accumulated gain earns it**, and the log
|
|
88
|
+
/// actually shrinks when it does.
|
|
89
|
+
#[test]
|
|
90
|
+
fn the_rewrite_happens_once_the_threshold_is_reached() {
|
|
91
|
+
let directory = TempDir::new().unwrap();
|
|
92
|
+
let path = directory.path().join("threshold.log");
|
|
93
|
+
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
94
|
+
db.set_compaction_policy(CompactionPolicy {
|
|
95
|
+
rewrite_after_pruned: 25,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
let mut rewrites = 0;
|
|
99
|
+
let mut defers = 0;
|
|
100
|
+
for round in 0..6 {
|
|
101
|
+
for index in 0..10 {
|
|
102
|
+
db.update(
|
|
103
|
+
&format!("tasks:{}", round * 10 + index),
|
|
104
|
+
json!({ "n": index }),
|
|
105
|
+
)
|
|
106
|
+
.unwrap();
|
|
107
|
+
}
|
|
108
|
+
acknowledge_all(&db);
|
|
109
|
+
match db
|
|
110
|
+
.maybe_compact_operation_log(&["peer-1".to_string()])
|
|
111
|
+
.unwrap()
|
|
112
|
+
{
|
|
113
|
+
CompactionOutcome::Rewritten { .. } => rewrites += 1,
|
|
114
|
+
CompactionOutcome::Deferred { .. } => defers += 1,
|
|
115
|
+
CompactionOutcome::Idle => {}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
assert!(defers > 0, "most ticks defer");
|
|
120
|
+
assert!(rewrites > 0, "and the rewrite still happens");
|
|
121
|
+
assert!(
|
|
122
|
+
defers > rewrites,
|
|
123
|
+
"the point is that deferring is the common case: {defers} deferred, {rewrites} rewritten"
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// **An explicit compaction still rewrites immediately.**
|
|
128
|
+
///
|
|
129
|
+
/// The policy governs the timer, not a caller who is asking for a rewrite now.
|
|
130
|
+
/// Every existing caller of `compact_operation_log` keeps its behaviour.
|
|
131
|
+
#[test]
|
|
132
|
+
fn an_explicit_compaction_still_rewrites_now() {
|
|
133
|
+
let directory = TempDir::new().unwrap();
|
|
134
|
+
let path = directory.path().join("explicit.log");
|
|
135
|
+
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
136
|
+
db.set_compaction_policy(CompactionPolicy {
|
|
137
|
+
rewrite_after_pruned: usize::MAX,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
write_n(&db, 20);
|
|
141
|
+
for index in 0..20 {
|
|
142
|
+
db.update(&format!("tasks:{index}"), json!({"n": "updated"}))
|
|
143
|
+
.unwrap();
|
|
144
|
+
}
|
|
145
|
+
acknowledge_all(&db);
|
|
146
|
+
let before = std::fs::metadata(&path).unwrap().len();
|
|
147
|
+
|
|
148
|
+
let pruned = db.compact_operation_log(&["peer-1".to_string()]).unwrap();
|
|
149
|
+
assert!(pruned > 0);
|
|
150
|
+
assert!(
|
|
151
|
+
std::fs::metadata(&path).unwrap().len() < before,
|
|
152
|
+
"an explicit compaction rewrote and shrank the log"
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// **The deferred rewrite costs nothing in correctness.**
|
|
157
|
+
///
|
|
158
|
+
/// Pruning without rewriting leaves the durable log longer than it needs to be,
|
|
159
|
+
/// and nothing else: the database reopens to the same state and the same
|
|
160
|
+
/// history.
|
|
161
|
+
#[test]
|
|
162
|
+
fn deferring_the_rewrite_preserves_state_and_history() {
|
|
163
|
+
let directory = TempDir::new().unwrap();
|
|
164
|
+
let path = directory.path().join("preserve.log");
|
|
165
|
+
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
166
|
+
db.set_compaction_policy(CompactionPolicy {
|
|
167
|
+
rewrite_after_pruned: usize::MAX,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
db.insert("tasks:1", json!({"n": 0})).unwrap();
|
|
171
|
+
for n in 1..10 {
|
|
172
|
+
db.update("tasks:1", json!({ "n": n })).unwrap();
|
|
173
|
+
}
|
|
174
|
+
acknowledge_all(&db);
|
|
175
|
+
assert!(matches!(
|
|
176
|
+
db.maybe_compact_operation_log(&["peer-1".to_string()])
|
|
177
|
+
.unwrap(),
|
|
178
|
+
CompactionOutcome::Deferred { .. }
|
|
179
|
+
));
|
|
180
|
+
|
|
181
|
+
let store = StateStore::with_feltdb(db.clone()).unwrap();
|
|
182
|
+
let history_before = store.history_digest();
|
|
183
|
+
let state_before = db.state_digest().unwrap();
|
|
184
|
+
drop(store);
|
|
185
|
+
drop(db);
|
|
186
|
+
|
|
187
|
+
let reopened = Arc::new(FeltDb::open(&path).unwrap());
|
|
188
|
+
let store = StateStore::with_feltdb(reopened.clone()).unwrap();
|
|
189
|
+
assert_eq!(reopened.state_digest().unwrap(), state_before);
|
|
190
|
+
assert_eq!(store.history_digest(), history_before);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Nothing to prune is not a rewrite either — the pre-existing early return.
|
|
194
|
+
#[test]
|
|
195
|
+
fn an_idle_compaction_does_no_work() {
|
|
196
|
+
let directory = TempDir::new().unwrap();
|
|
197
|
+
let path = directory.path().join("idle.log");
|
|
198
|
+
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
199
|
+
write_n(&db, 5);
|
|
200
|
+
let before = std::fs::read(&path).unwrap();
|
|
201
|
+
|
|
202
|
+
// No peer has acknowledged anything.
|
|
203
|
+
let outcome = db.maybe_compact_operation_log(&[]).unwrap();
|
|
204
|
+
assert_eq!(outcome, CompactionOutcome::Idle);
|
|
205
|
+
assert_eq!(outcome.pruned(), 0);
|
|
206
|
+
assert!(!outcome.rewrote_log());
|
|
207
|
+
assert_eq!(std::fs::read(&path).unwrap(), before);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// The stall itself
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
/// **The rewrite is what stalls, and it grows with the database.**
|
|
215
|
+
///
|
|
216
|
+
/// Measured as an ordering rather than a number: a rewrite over a larger
|
|
217
|
+
/// database holds the lock longer. That is why running it on a two-second timer
|
|
218
|
+
/// degraded as the deployment grew, and why the default policy now waits.
|
|
219
|
+
#[test]
|
|
220
|
+
fn the_rewrite_cost_grows_with_the_database() {
|
|
221
|
+
let cost = |rows: usize| {
|
|
222
|
+
let directory = TempDir::new().unwrap();
|
|
223
|
+
let path = directory.path().join("cost.log");
|
|
224
|
+
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
225
|
+
write_n(&db, rows);
|
|
226
|
+
for index in 0..rows {
|
|
227
|
+
db.update(&format!("tasks:{index}"), json!({"n": "updated"}))
|
|
228
|
+
.unwrap();
|
|
229
|
+
}
|
|
230
|
+
acknowledge_all(&db);
|
|
231
|
+
let started = Instant::now();
|
|
232
|
+
db.compact_operation_log(&["peer-1".to_string()]).unwrap();
|
|
233
|
+
started.elapsed()
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
let small = cost(20);
|
|
237
|
+
let large = cost(400);
|
|
238
|
+
assert!(
|
|
239
|
+
large > small,
|
|
240
|
+
"a rewrite over more rows holds the lock longer: {large:?} vs {small:?}"
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/// **The default policy does not rewrite on every tick.**
|
|
245
|
+
///
|
|
246
|
+
/// The regression this whole file exists to prevent. With the shipped default,
|
|
247
|
+
/// a handful of acknowledged operations must not trigger a full log rewrite.
|
|
248
|
+
#[test]
|
|
249
|
+
fn the_default_policy_does_not_rewrite_on_a_handful_of_operations() {
|
|
250
|
+
assert_eq!(CompactionPolicy::default().rewrite_after_pruned, 1000);
|
|
251
|
+
|
|
252
|
+
let directory = TempDir::new().unwrap();
|
|
253
|
+
let path = directory.path().join("default.log");
|
|
254
|
+
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
255
|
+
assert_eq!(db.compaction_policy(), CompactionPolicy::default());
|
|
256
|
+
|
|
257
|
+
write_n(&db, 30);
|
|
258
|
+
acknowledge_all(&db);
|
|
259
|
+
let before = std::fs::metadata(&path).unwrap().len();
|
|
260
|
+
|
|
261
|
+
// Ten ticks of the timer.
|
|
262
|
+
for _ in 0..10 {
|
|
263
|
+
db.maybe_compact_operation_log(&["peer-1".to_string()])
|
|
264
|
+
.unwrap();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
assert_eq!(
|
|
268
|
+
std::fs::metadata(&path).unwrap().len(),
|
|
269
|
+
before,
|
|
270
|
+
"ten timer ticks over a small database rewrote the log zero times"
|
|
271
|
+
);
|
|
272
|
+
}
|