@feltdb/core 0.8.3 → 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/cli/commands.js +4 -1
- package/dist/cli/provisioning-neutrality.js +79 -0
- package/dist/collection.d.ts +43 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +192 -22
- package/dist/create/create.js +25 -21
- package/dist/create/managed-account.js +11 -0
- 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/equality_index.rs +595 -0
- 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 +1050 -117
- 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 +90 -72
- 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_execution_diagnostics.rs +126 -0
- 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 +305 -6
- 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/sync.rs +12 -0
- 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 +442 -0
- 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 +234 -0
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +947 -0
- 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/audit.rs +1137 -29
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +800 -34
- package/dist/db.d.ts +33 -34
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +74 -20
- package/dist/deployment.d.ts +30 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +130 -0
- package/dist/embedded-transaction.d.ts +22 -4
- package/dist/embedded-transaction.d.ts.map +1 -1
- package/dist/embedded-transaction.js +51 -5
- package/dist/feltdb.d.ts +14 -2
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/file-db.js +1 -1
- package/dist/http-client.d.ts +14 -0
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +23 -5
- package/dist/http-db.d.ts +119 -1
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +346 -31
- package/dist/index-core.d.ts +2 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/indexeddb-db.d.ts.map +1 -1
- package/dist/indexeddb-db.js +35 -21
- package/dist/managed-recovery.d.ts +192 -0
- package/dist/managed-recovery.d.ts.map +1 -0
- package/dist/managed-recovery.js +242 -0
- package/dist/memory-db.js +1 -1
- package/dist/studio-app/assets/{feltdb_wasm-DB8cX151.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-j8IlhNqJ.js +29 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/transaction.d.ts +30 -0
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +41 -0
- 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-ClhDHp0S.wasm +0 -0
- package/dist/studio-app/assets/index-B0k4UAlI.js +0 -29
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
/// - Relationship access patterns
|
|
8
8
|
/// - Materialized views and projections
|
|
9
9
|
/// - Multi-key operations
|
|
10
|
-
|
|
11
10
|
use serde::{Deserialize, Serialize};
|
|
12
11
|
use std::collections::HashMap;
|
|
13
12
|
|
|
@@ -142,7 +141,11 @@ impl ProductionAPI {
|
|
|
142
141
|
.ok_or_else(|| format!("Resource not found: {}", id))
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
pub fn read_many(
|
|
144
|
+
pub fn read_many(
|
|
145
|
+
&self,
|
|
146
|
+
ids: &[&str],
|
|
147
|
+
_options: ReadOptions,
|
|
148
|
+
) -> Result<Vec<StateValue>, String> {
|
|
146
149
|
let mut results = Vec::new();
|
|
147
150
|
for id in ids {
|
|
148
151
|
if let Some(value) = self.state_store.get(*id) {
|
|
@@ -152,7 +155,11 @@ impl ProductionAPI {
|
|
|
152
155
|
Ok(results)
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
pub fn write(
|
|
158
|
+
pub fn write(
|
|
159
|
+
&mut self,
|
|
160
|
+
mutation: Mutation,
|
|
161
|
+
options: WriteOptions,
|
|
162
|
+
) -> Result<MutationResult, String> {
|
|
156
163
|
if let Some(ref key) = options.idempotency_key {
|
|
157
164
|
if let Some(existing) = self.state_store.get(&mutation.resource_id) {
|
|
158
165
|
return Ok(MutationResult {
|
|
@@ -177,7 +184,8 @@ impl ProductionAPI {
|
|
|
177
184
|
updated_at: now,
|
|
178
185
|
};
|
|
179
186
|
|
|
180
|
-
self.state_store
|
|
187
|
+
self.state_store
|
|
188
|
+
.insert(mutation.resource_id.clone(), value.clone());
|
|
181
189
|
|
|
182
190
|
Ok(MutationResult {
|
|
183
191
|
success: true,
|
|
@@ -209,7 +217,10 @@ impl ProductionAPI {
|
|
|
209
217
|
Ok(results)
|
|
210
218
|
}
|
|
211
219
|
|
|
212
|
-
pub fn transaction(
|
|
220
|
+
pub fn transaction(
|
|
221
|
+
&mut self,
|
|
222
|
+
request: TransactionRequest,
|
|
223
|
+
) -> Result<TransactionResult, String> {
|
|
213
224
|
self.transaction_id_counter += 1;
|
|
214
225
|
let transaction_id = format!("tx-{}", self.transaction_id_counter);
|
|
215
226
|
|
|
@@ -246,13 +257,12 @@ impl ProductionAPI {
|
|
|
246
257
|
.push(target);
|
|
247
258
|
}
|
|
248
259
|
|
|
249
|
-
pub fn get_relationships(
|
|
260
|
+
pub fn get_relationships(
|
|
261
|
+
&self,
|
|
262
|
+
query: RelationshipQuery,
|
|
263
|
+
) -> Result<RelationshipResult, String> {
|
|
250
264
|
let key = format!("{}:{}", query.source_id, query.relationship);
|
|
251
|
-
let targets = self
|
|
252
|
-
.relationships
|
|
253
|
-
.get(&key)
|
|
254
|
-
.cloned()
|
|
255
|
-
.unwrap_or_default();
|
|
265
|
+
let targets = self.relationships.get(&key).cloned().unwrap_or_default();
|
|
256
266
|
|
|
257
267
|
Ok(RelationshipResult {
|
|
258
268
|
source_id: query.source_id,
|
|
@@ -359,8 +369,16 @@ mod tests {
|
|
|
359
369
|
#[test]
|
|
360
370
|
fn test_relationships() {
|
|
361
371
|
let mut api = ProductionAPI::new();
|
|
362
|
-
api.add_relationship(
|
|
363
|
-
|
|
372
|
+
api.add_relationship(
|
|
373
|
+
"user-1".to_string(),
|
|
374
|
+
"follows".to_string(),
|
|
375
|
+
"user-2".to_string(),
|
|
376
|
+
);
|
|
377
|
+
api.add_relationship(
|
|
378
|
+
"user-1".to_string(),
|
|
379
|
+
"follows".to_string(),
|
|
380
|
+
"user-3".to_string(),
|
|
381
|
+
);
|
|
364
382
|
|
|
365
383
|
let result = api
|
|
366
384
|
.get_relationships(RelationshipQuery {
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
//! Test instrumentation for the collection read paths a bounded query can take.
|
|
2
|
+
//!
|
|
3
|
+
//! PR33 attributed sustained-workload degradation to bounded queries cloning an
|
|
4
|
+
//! entire collection under the state mutex before predicate, ordering and limit
|
|
5
|
+
//! were applied. PR34 removes that clone, and a benchmark alone cannot prove a
|
|
6
|
+
//! clone is gone: a faster number is evidence of speed, not of mechanism.
|
|
7
|
+
//!
|
|
8
|
+
//! These counters therefore record *which traversal a read used*, so a test can
|
|
9
|
+
//! assert that a bounded query never entered `FeltDb::list_collection`. They
|
|
10
|
+
//! are process-global, monotonic, carry no record data, and are not part of the
|
|
11
|
+
//! query contract: nothing in the query path reads them, and no query result
|
|
12
|
+
//! depends on them. The HTTP surface only exposes them when a server is started
|
|
13
|
+
//! with `FELTDB_QUERY_DIAGNOSTICS=1`.
|
|
14
|
+
//!
|
|
15
|
+
//! PR35 extends them with execution attribution. An index is an optimization, so
|
|
16
|
+
//! "the query got faster" is not evidence that it was used; these counters say
|
|
17
|
+
//! which execution a query actually took, how many candidates the index
|
|
18
|
+
//! produced, and how many records the predicate had to evaluate. That last
|
|
19
|
+
//! figure is the one the PR35 claim rests on: for a selective indexed query it
|
|
20
|
+
//! must track the candidate count rather than the collection size.
|
|
21
|
+
|
|
22
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
23
|
+
|
|
24
|
+
static FULL_COLLECTION_MATERIALIZATIONS: AtomicU64 = AtomicU64::new(0);
|
|
25
|
+
static FULL_COLLECTION_RECORDS_CLONED: AtomicU64 = AtomicU64::new(0);
|
|
26
|
+
static COLLECTION_SCANS: AtomicU64 = AtomicU64::new(0);
|
|
27
|
+
static SCAN_RECORDS_VISITED: AtomicU64 = AtomicU64::new(0);
|
|
28
|
+
static SCAN_RECORDS_MATERIALIZED: AtomicU64 = AtomicU64::new(0);
|
|
29
|
+
static QUERIES_TOTAL: AtomicU64 = AtomicU64::new(0);
|
|
30
|
+
static QUERIES_SCAN: AtomicU64 = AtomicU64::new(0);
|
|
31
|
+
static QUERIES_INDEXED: AtomicU64 = AtomicU64::new(0);
|
|
32
|
+
static INDEX_HITS: AtomicU64 = AtomicU64::new(0);
|
|
33
|
+
static INDEX_MISSES: AtomicU64 = AtomicU64::new(0);
|
|
34
|
+
static INDEX_CANDIDATES_EXAMINED: AtomicU64 = AtomicU64::new(0);
|
|
35
|
+
static RECORDS_PREDICATE_EVALUATED: AtomicU64 = AtomicU64::new(0);
|
|
36
|
+
|
|
37
|
+
/// A point-in-time reading of the collection read counters.
|
|
38
|
+
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
|
39
|
+
pub struct QueryExecutionCounters {
|
|
40
|
+
/// Calls to `FeltDb::list_collection`, which clones a whole collection.
|
|
41
|
+
pub full_collection_materializations: u64,
|
|
42
|
+
/// Records cloned by those calls.
|
|
43
|
+
pub full_collection_records_cloned: u64,
|
|
44
|
+
/// Calls to `FeltDb::query_collection`, which evaluates borrowed records.
|
|
45
|
+
pub collection_scans: u64,
|
|
46
|
+
/// Records a scan predicate observed, borrowed rather than cloned.
|
|
47
|
+
pub scan_records_visited: u64,
|
|
48
|
+
/// Records a scan actually cloned, which is at most the matching set.
|
|
49
|
+
pub scan_records_materialized: u64,
|
|
50
|
+
/// Collection reads that executed a query: `queries_scan + queries_indexed`.
|
|
51
|
+
pub queries_total: u64,
|
|
52
|
+
/// Queries answered by evaluating the predicate over the whole collection.
|
|
53
|
+
pub queries_scan: u64,
|
|
54
|
+
/// Queries answered from equality-index candidates.
|
|
55
|
+
pub queries_indexed: u64,
|
|
56
|
+
/// Queries for which at least one condition had an applicable index.
|
|
57
|
+
pub index_hits: u64,
|
|
58
|
+
/// Queries for which no condition had an applicable index, which fall back.
|
|
59
|
+
pub index_misses: u64,
|
|
60
|
+
/// Candidate record keys the index produced, after intersection.
|
|
61
|
+
pub index_candidates_examined: u64,
|
|
62
|
+
/// Records the predicate was actually evaluated against, on either path.
|
|
63
|
+
/// For a selective indexed query this tracks the candidate count; for a scan
|
|
64
|
+
/// it is the collection size.
|
|
65
|
+
pub records_predicate_evaluated: u64,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
pub(crate) fn record_full_materialization(records: usize) {
|
|
69
|
+
FULL_COLLECTION_MATERIALIZATIONS.fetch_add(1, Ordering::Relaxed);
|
|
70
|
+
FULL_COLLECTION_RECORDS_CLONED.fetch_add(records as u64, Ordering::Relaxed);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pub(crate) fn record_scan(visited: usize, materialized: usize) {
|
|
74
|
+
COLLECTION_SCANS.fetch_add(1, Ordering::Relaxed);
|
|
75
|
+
SCAN_RECORDS_VISITED.fetch_add(visited as u64, Ordering::Relaxed);
|
|
76
|
+
SCAN_RECORDS_MATERIALIZED.fetch_add(materialized as u64, Ordering::Relaxed);
|
|
77
|
+
QUERIES_TOTAL.fetch_add(1, Ordering::Relaxed);
|
|
78
|
+
QUERIES_SCAN.fetch_add(1, Ordering::Relaxed);
|
|
79
|
+
// A scan evaluates the predicate against every record it visits. This is
|
|
80
|
+
// the O(N) figure PR35 exists to reduce, recorded on the same counter the
|
|
81
|
+
// indexed path reports to, so the two are directly comparable.
|
|
82
|
+
RECORDS_PREDICATE_EVALUATED.fetch_add(visited as u64, Ordering::Relaxed);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// A query for which no condition had an applicable index, so it falls back.
|
|
86
|
+
///
|
|
87
|
+
/// This deliberately does not advance `queries_total`: the scan the caller is
|
|
88
|
+
/// about to run records the query itself, and counting it here would report
|
|
89
|
+
/// every fallback twice.
|
|
90
|
+
pub(crate) fn record_index_miss() {
|
|
91
|
+
INDEX_MISSES.fetch_add(1, Ordering::Relaxed);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// A query answered from index candidates.
|
|
95
|
+
///
|
|
96
|
+
/// `examined` is the candidate set after intersection, `evaluated` the records
|
|
97
|
+
/// the predicate actually ran against, and `materialized` the matching set that
|
|
98
|
+
/// was cloned. `evaluated` can be below `examined` only if an index entry has no
|
|
99
|
+
/// record behind it, which would be a maintenance defect; the gap is visible
|
|
100
|
+
/// here rather than hidden.
|
|
101
|
+
pub(crate) fn record_indexed_query(examined: usize, evaluated: usize, materialized: usize) {
|
|
102
|
+
QUERIES_TOTAL.fetch_add(1, Ordering::Relaxed);
|
|
103
|
+
QUERIES_INDEXED.fetch_add(1, Ordering::Relaxed);
|
|
104
|
+
INDEX_HITS.fetch_add(1, Ordering::Relaxed);
|
|
105
|
+
INDEX_CANDIDATES_EXAMINED.fetch_add(examined as u64, Ordering::Relaxed);
|
|
106
|
+
RECORDS_PREDICATE_EVALUATED.fetch_add(evaluated as u64, Ordering::Relaxed);
|
|
107
|
+
SCAN_RECORDS_MATERIALIZED.fetch_add(materialized as u64, Ordering::Relaxed);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Read the counters. Monotonic since process start, so tests compare deltas.
|
|
111
|
+
pub fn counters() -> QueryExecutionCounters {
|
|
112
|
+
QueryExecutionCounters {
|
|
113
|
+
full_collection_materializations: FULL_COLLECTION_MATERIALIZATIONS.load(Ordering::Relaxed),
|
|
114
|
+
full_collection_records_cloned: FULL_COLLECTION_RECORDS_CLONED.load(Ordering::Relaxed),
|
|
115
|
+
collection_scans: COLLECTION_SCANS.load(Ordering::Relaxed),
|
|
116
|
+
scan_records_visited: SCAN_RECORDS_VISITED.load(Ordering::Relaxed),
|
|
117
|
+
scan_records_materialized: SCAN_RECORDS_MATERIALIZED.load(Ordering::Relaxed),
|
|
118
|
+
queries_total: QUERIES_TOTAL.load(Ordering::Relaxed),
|
|
119
|
+
queries_scan: QUERIES_SCAN.load(Ordering::Relaxed),
|
|
120
|
+
queries_indexed: QUERIES_INDEXED.load(Ordering::Relaxed),
|
|
121
|
+
index_hits: INDEX_HITS.load(Ordering::Relaxed),
|
|
122
|
+
index_misses: INDEX_MISSES.load(Ordering::Relaxed),
|
|
123
|
+
index_candidates_examined: INDEX_CANDIDATES_EXAMINED.load(Ordering::Relaxed),
|
|
124
|
+
records_predicate_evaluated: RECORDS_PREDICATE_EVALUATED.load(Ordering::Relaxed),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/// - Latency contract enforcement
|
|
6
6
|
/// - Performance degradation detection
|
|
7
7
|
/// - Query optimization recommendations
|
|
8
|
-
|
|
9
8
|
use serde::{Deserialize, Serialize};
|
|
10
9
|
use std::collections::HashMap;
|
|
11
10
|
use std::time::{Duration, Instant};
|
|
@@ -156,7 +155,11 @@ impl QueryPerformanceTracker {
|
|
|
156
155
|
for metric in &self.metrics {
|
|
157
156
|
csv.push_str(&format!(
|
|
158
157
|
"{},{},{},{},{}\n",
|
|
159
|
-
metric.timestamp,
|
|
158
|
+
metric.timestamp,
|
|
159
|
+
metric.query_type,
|
|
160
|
+
metric.duration_us,
|
|
161
|
+
metric.index_name,
|
|
162
|
+
metric.result_count
|
|
160
163
|
));
|
|
161
164
|
}
|
|
162
165
|
csv
|
|
@@ -187,12 +190,7 @@ mod tests {
|
|
|
187
190
|
std::thread::sleep(Duration::from_micros(50));
|
|
188
191
|
let elapsed = timer.elapsed_us();
|
|
189
192
|
|
|
190
|
-
tracker.record_query(
|
|
191
|
-
"index_lookup".to_string(),
|
|
192
|
-
"idx_1".to_string(),
|
|
193
|
-
10,
|
|
194
|
-
elapsed,
|
|
195
|
-
);
|
|
193
|
+
tracker.record_query("index_lookup".to_string(), "idx_1".to_string(), 10, elapsed);
|
|
196
194
|
|
|
197
195
|
let stats = tracker.get_stats("index_lookup").unwrap();
|
|
198
196
|
assert_eq!(stats.count, 1);
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
/// - After index deletion (rebuild index from scratch)
|
|
9
9
|
///
|
|
10
10
|
/// All paths must converge to identical state_hash.
|
|
11
|
-
|
|
12
11
|
use crate::operation_log::{MemoryOperationLog, OperationLogEntry};
|
|
13
12
|
use crate::state_hash::{CanonicalState, StateHash};
|
|
14
13
|
use crate::state_transition_store::MemoryStateTransitionStore;
|
|
@@ -46,7 +45,10 @@ pub struct ReplayFuzzer {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
impl ReplayFuzzer {
|
|
49
|
-
pub fn new(
|
|
48
|
+
pub fn new(
|
|
49
|
+
operation_log: MemoryOperationLog,
|
|
50
|
+
transition_store: MemoryStateTransitionStore,
|
|
51
|
+
) -> Self {
|
|
50
52
|
Self {
|
|
51
53
|
operation_log,
|
|
52
54
|
transition_store,
|
|
@@ -99,9 +101,7 @@ impl ReplayFuzzer {
|
|
|
99
101
|
let log_size = self.operation_log.size();
|
|
100
102
|
if snapshot_at >= log_size {
|
|
101
103
|
return ReplayPath {
|
|
102
|
-
scenario: ReplayScenario::AfterSnapshot {
|
|
103
|
-
snapshot_at,
|
|
104
|
-
},
|
|
104
|
+
scenario: ReplayScenario::AfterSnapshot { snapshot_at },
|
|
105
105
|
final_state_hash: StateHash::from_hex("snapshot_beyond_log".to_string()),
|
|
106
106
|
operations_replayed: 0,
|
|
107
107
|
transitions_created: 0,
|
|
@@ -137,12 +137,16 @@ impl AcknowledgementStore {
|
|
|
137
137
|
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, AcknowledgementError> {
|
|
138
138
|
let path = path.as_ref().to_path_buf();
|
|
139
139
|
let acknowledgements = if path.exists() {
|
|
140
|
-
let bytes =
|
|
140
|
+
let bytes =
|
|
141
|
+
std::fs::read(&path).map_err(|e| AcknowledgementError::Io(e.to_string()))?;
|
|
141
142
|
serde_json::from_slice(&bytes).map_err(|e| AcknowledgementError::Io(e.to_string()))?
|
|
142
143
|
} else {
|
|
143
144
|
BTreeMap::new()
|
|
144
145
|
};
|
|
145
|
-
Ok(Self {
|
|
146
|
+
Ok(Self {
|
|
147
|
+
path,
|
|
148
|
+
acknowledgements,
|
|
149
|
+
})
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
fn persist(&self) -> Result<(), AcknowledgementError> {
|
|
@@ -154,10 +158,12 @@ impl AcknowledgementStore {
|
|
|
154
158
|
.map_err(|e| AcknowledgementError::Io(e.to_string()))?;
|
|
155
159
|
{
|
|
156
160
|
use std::io::Write;
|
|
157
|
-
let mut file =
|
|
158
|
-
|
|
159
|
-
file.write_all(&bytes)
|
|
160
|
-
|
|
161
|
+
let mut file = std::fs::File::create(&temp)
|
|
162
|
+
.map_err(|e| AcknowledgementError::Io(e.to_string()))?;
|
|
163
|
+
file.write_all(&bytes)
|
|
164
|
+
.map_err(|e| AcknowledgementError::Io(e.to_string()))?;
|
|
165
|
+
file.sync_all()
|
|
166
|
+
.map_err(|e| AcknowledgementError::Io(e.to_string()))?;
|
|
161
167
|
}
|
|
162
168
|
std::fs::rename(&temp, &self.path).map_err(|e| AcknowledgementError::Io(e.to_string()))?;
|
|
163
169
|
if let Some(parent) = self.path.parent() {
|
|
@@ -227,7 +233,9 @@ impl AcknowledgementStore {
|
|
|
227
233
|
// No required replicas at all. Still not a licence: a node with no
|
|
228
234
|
// membership has not established that it is alone, it has
|
|
229
235
|
// established nothing.
|
|
230
|
-
return ReclamationEvidence::Blocked {
|
|
236
|
+
return ReclamationEvidence::Blocked {
|
|
237
|
+
waiting_on: Vec::new(),
|
|
238
|
+
};
|
|
231
239
|
}
|
|
232
240
|
|
|
233
241
|
let missing: Vec<String> = required
|
|
@@ -236,7 +244,9 @@ impl AcknowledgementStore {
|
|
|
236
244
|
.cloned()
|
|
237
245
|
.collect();
|
|
238
246
|
if !missing.is_empty() {
|
|
239
|
-
return ReclamationEvidence::Blocked {
|
|
247
|
+
return ReclamationEvidence::Blocked {
|
|
248
|
+
waiting_on: missing,
|
|
249
|
+
};
|
|
240
250
|
}
|
|
241
251
|
|
|
242
252
|
// The minimum over every required replica, component-wise. A frontier
|
|
@@ -259,7 +269,10 @@ impl AcknowledgementStore {
|
|
|
259
269
|
frontier.clocks.insert(origin, lowest);
|
|
260
270
|
}
|
|
261
271
|
}
|
|
262
|
-
ReclamationEvidence::SafeThrough {
|
|
272
|
+
ReclamationEvidence::SafeThrough {
|
|
273
|
+
frontier,
|
|
274
|
+
covered: required,
|
|
275
|
+
}
|
|
263
276
|
}
|
|
264
277
|
}
|
|
265
278
|
|
|
@@ -328,12 +341,17 @@ mod tests {
|
|
|
328
341
|
fn a_required_replica_with_no_acknowledgement_blocks() {
|
|
329
342
|
let dir = tempfile::tempdir().unwrap();
|
|
330
343
|
let mut acks = store(&dir);
|
|
331
|
-
acks.record(ack("rb", &[("A", 3)], &["ra", "rb", "rc"]))
|
|
344
|
+
acks.record(ack("rb", &[("A", 3)], &["ra", "rb", "rc"]))
|
|
345
|
+
.unwrap();
|
|
332
346
|
|
|
333
347
|
let evidence = acks.evidence(&["ra".into(), "rb".into(), "rc".into()]);
|
|
334
348
|
match evidence {
|
|
335
349
|
ReclamationEvidence::Blocked { waiting_on } => {
|
|
336
|
-
assert_eq!(
|
|
350
|
+
assert_eq!(
|
|
351
|
+
waiting_on,
|
|
352
|
+
vec!["ra", "rc"],
|
|
353
|
+
"and it names who it waits for"
|
|
354
|
+
);
|
|
337
355
|
}
|
|
338
356
|
other => panic!("missing acknowledgement must block, got {other:?}"),
|
|
339
357
|
}
|
|
@@ -347,7 +365,8 @@ mod tests {
|
|
|
347
365
|
let dir = tempfile::tempdir().unwrap();
|
|
348
366
|
let mut acks = store(&dir);
|
|
349
367
|
acks.record(ack("ra", &[("A", 3)], &["ra", "rb"])).unwrap();
|
|
350
|
-
acks.record(ack("rb", &[("A", 3)], &["ra", "rb", "rc"]))
|
|
368
|
+
acks.record(ack("rb", &[("A", 3)], &["ra", "rb", "rc"]))
|
|
369
|
+
.unwrap();
|
|
351
370
|
|
|
352
371
|
let local = vec!["ra".to_string(), "rb".to_string()];
|
|
353
372
|
assert_eq!(acks.required_union(&local), vec!["ra", "rb", "rc"]);
|
|
@@ -363,7 +382,10 @@ mod tests {
|
|
|
363
382
|
// alone. It has established nothing, and nothing is what it may act on.
|
|
364
383
|
let dir = tempfile::tempdir().unwrap();
|
|
365
384
|
let acks = store(&dir);
|
|
366
|
-
assert!(matches!(
|
|
385
|
+
assert!(matches!(
|
|
386
|
+
acks.evidence(&[]),
|
|
387
|
+
ReclamationEvidence::Blocked { .. }
|
|
388
|
+
));
|
|
367
389
|
}
|
|
368
390
|
|
|
369
391
|
#[test]
|
|
@@ -395,8 +417,10 @@ mod tests {
|
|
|
395
417
|
fn the_safe_frontier_is_the_minimum_over_every_required_replica() {
|
|
396
418
|
let dir = tempfile::tempdir().unwrap();
|
|
397
419
|
let mut acks = store(&dir);
|
|
398
|
-
acks.record(ack("ra", &[("A", 9), ("B", 4)], &["ra", "rb"]))
|
|
399
|
-
|
|
420
|
+
acks.record(ack("ra", &[("A", 9), ("B", 4)], &["ra", "rb"]))
|
|
421
|
+
.unwrap();
|
|
422
|
+
acks.record(ack("rb", &[("A", 5), ("B", 6)], &["ra", "rb"]))
|
|
423
|
+
.unwrap();
|
|
400
424
|
|
|
401
425
|
match acks.evidence(&["ra".into(), "rb".into()]) {
|
|
402
426
|
ReclamationEvidence::SafeThrough { frontier, covered } => {
|
|
@@ -415,7 +439,8 @@ mod tests {
|
|
|
415
439
|
// anything is safe through.
|
|
416
440
|
let dir = tempfile::tempdir().unwrap();
|
|
417
441
|
let mut acks = store(&dir);
|
|
418
|
-
acks.record(ack("ra", &[("A", 4), ("C", 9)], &["ra", "rb"]))
|
|
442
|
+
acks.record(ack("ra", &[("A", 4), ("C", 9)], &["ra", "rb"]))
|
|
443
|
+
.unwrap();
|
|
419
444
|
acks.record(ack("rb", &[("A", 4)], &["ra", "rb"])).unwrap();
|
|
420
445
|
|
|
421
446
|
match acks.evidence(&["ra".into(), "rb".into()]) {
|
|
@@ -437,7 +462,8 @@ mod tests {
|
|
|
437
462
|
let dir = tempfile::tempdir().unwrap();
|
|
438
463
|
{
|
|
439
464
|
let mut acks = store(&dir);
|
|
440
|
-
acks.record(ack("rb", &[("A", 3)], &["ra", "rb", "rc"]))
|
|
465
|
+
acks.record(ack("rb", &[("A", 3)], &["ra", "rb", "rc"]))
|
|
466
|
+
.unwrap();
|
|
441
467
|
}
|
|
442
468
|
let recovered = store(&dir);
|
|
443
469
|
match recovered.evidence(&["ra".into(), "rb".into()]) {
|
|
@@ -464,7 +490,11 @@ mod tests {
|
|
|
464
490
|
|
|
465
491
|
let recovered = store(&dir);
|
|
466
492
|
assert_eq!(
|
|
467
|
-
recovered
|
|
493
|
+
recovered
|
|
494
|
+
.get("rb")
|
|
495
|
+
.unwrap()
|
|
496
|
+
.origin_sequence_high_water
|
|
497
|
+
.get("A"),
|
|
468
498
|
Some(&7),
|
|
469
499
|
);
|
|
470
500
|
}
|
|
@@ -146,19 +146,23 @@ impl MembershipStore {
|
|
|
146
146
|
/// directory. Membership is what will later authorise deleting history, so
|
|
147
147
|
/// it is written with the same care as the log it will authorise deleting.
|
|
148
148
|
fn persist(&self) -> Result<(), MembershipError> {
|
|
149
|
-
let Some(cluster) = &self.cluster else {
|
|
149
|
+
let Some(cluster) = &self.cluster else {
|
|
150
|
+
return Ok(());
|
|
151
|
+
};
|
|
150
152
|
if let Some(parent) = self.path.parent() {
|
|
151
153
|
std::fs::create_dir_all(parent).map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
152
154
|
}
|
|
153
155
|
let temp = self.path.with_extension("tmp");
|
|
154
|
-
let bytes =
|
|
155
|
-
.map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
156
|
+
let bytes =
|
|
157
|
+
serde_json::to_vec_pretty(cluster).map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
156
158
|
{
|
|
157
159
|
use std::io::Write;
|
|
158
|
-
let mut file =
|
|
160
|
+
let mut file =
|
|
161
|
+
std::fs::File::create(&temp).map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
162
|
+
file.write_all(&bytes)
|
|
163
|
+
.map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
164
|
+
file.sync_all()
|
|
159
165
|
.map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
160
|
-
file.write_all(&bytes).map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
161
|
-
file.sync_all().map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
162
166
|
}
|
|
163
167
|
std::fs::rename(&temp, &self.path).map_err(|e| MembershipError::Io(e.to_string()))?;
|
|
164
168
|
if let Some(parent) = self.path.parent() {
|
|
@@ -435,7 +439,11 @@ mod tests {
|
|
|
435
439
|
cluster.suspect_replica("rc").unwrap();
|
|
436
440
|
cluster.mark_seen("rc").unwrap();
|
|
437
441
|
let seen = cluster.get_members().into_iter().next().unwrap();
|
|
438
|
-
assert_eq!(
|
|
442
|
+
assert_eq!(
|
|
443
|
+
seen.status,
|
|
444
|
+
ReplicaStatus::Active,
|
|
445
|
+
"contact reinstates a suspected replica"
|
|
446
|
+
);
|
|
439
447
|
assert!(seen.last_seen_at_ms.is_some());
|
|
440
448
|
|
|
441
449
|
cluster.remove_replica("rc").unwrap();
|
|
@@ -503,7 +511,10 @@ mod tests {
|
|
|
503
511
|
fn membership_operations_need_a_cluster() {
|
|
504
512
|
let dir = tempfile::tempdir().unwrap();
|
|
505
513
|
let mut cluster = store(&dir);
|
|
506
|
-
assert!(matches!(
|
|
514
|
+
assert!(matches!(
|
|
515
|
+
cluster.join_replica("ra", "A"),
|
|
516
|
+
Err(MembershipError::NoCluster)
|
|
517
|
+
));
|
|
507
518
|
cluster.create_cluster("c1").unwrap();
|
|
508
519
|
assert!(matches!(
|
|
509
520
|
cluster.create_cluster("c2"),
|
|
@@ -523,7 +534,10 @@ mod tests {
|
|
|
523
534
|
cluster.join_replica("ra", "A").unwrap();
|
|
524
535
|
cluster.approve_replica("ra").unwrap();
|
|
525
536
|
assert!(
|
|
526
|
-
matches!(
|
|
537
|
+
matches!(
|
|
538
|
+
cluster.approve_replica("ra"),
|
|
539
|
+
Err(MembershipError::NotJoining(_))
|
|
540
|
+
),
|
|
527
541
|
"approving twice is not a silent no-op"
|
|
528
542
|
);
|
|
529
543
|
}
|
|
@@ -648,7 +662,9 @@ mod tests {
|
|
|
648
662
|
let mut cluster = MembershipStore::open(&path).unwrap();
|
|
649
663
|
cluster.create_cluster("c1").unwrap();
|
|
650
664
|
for index in 0..20 {
|
|
651
|
-
cluster
|
|
665
|
+
cluster
|
|
666
|
+
.join_replica(&format!("r{index}"), &format!("N{index}"))
|
|
667
|
+
.unwrap();
|
|
652
668
|
let observed = MembershipStore::open(&path).unwrap();
|
|
653
669
|
assert_eq!(
|
|
654
670
|
observed.get_members().len(),
|
|
@@ -656,6 +672,9 @@ mod tests {
|
|
|
656
672
|
"every observation is of a complete cluster"
|
|
657
673
|
);
|
|
658
674
|
}
|
|
659
|
-
assert!(
|
|
675
|
+
assert!(
|
|
676
|
+
!path.with_extension("tmp").exists(),
|
|
677
|
+
"no temp file is left at rest"
|
|
678
|
+
);
|
|
660
679
|
}
|
|
661
680
|
}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
/// - Routing messages from leader to followers
|
|
7
7
|
/// - Acknowledgment tracking
|
|
8
8
|
/// - Retry logic for lost messages
|
|
9
|
-
|
|
10
9
|
use crate::distributed_transactions::ReplicationMessage;
|
|
11
10
|
use std::collections::{HashMap, VecDeque};
|
|
12
11
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
@@ -67,7 +66,10 @@ impl PeerReplicationQueue {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
pub fn pending_count(&self) -> usize {
|
|
70
|
-
self.messages
|
|
69
|
+
self.messages
|
|
70
|
+
.iter()
|
|
71
|
+
.filter(|m| m.status == DeliveryStatus::Pending)
|
|
72
|
+
.count()
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
pub fn next_pending(&mut self) -> Option<TrackedMessage> {
|
|
@@ -103,7 +105,8 @@ impl ReplicationManager {
|
|
|
103
105
|
|
|
104
106
|
/// Register a peer to receive replication messages
|
|
105
107
|
pub fn register_peer(&mut self, peer_id: String) {
|
|
106
|
-
self.peer_queues
|
|
108
|
+
self.peer_queues
|
|
109
|
+
.insert(peer_id.clone(), PeerReplicationQueue::new(peer_id));
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
/// Enqueue a message for replication to all peers
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
///
|
|
3
3
|
/// Decouples ReplicationMessage handling from transport details.
|
|
4
4
|
/// Works with typed messages (ReplicationMessage) rather than raw bytes.
|
|
5
|
-
|
|
6
5
|
use crate::distributed_transactions::ReplicationMessage;
|
|
7
6
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
8
7
|
use std::sync::Arc;
|
|
@@ -71,7 +70,8 @@ impl ProtocolMetrics {
|
|
|
71
70
|
|
|
72
71
|
pub fn record_receive(&self, bytes: usize) {
|
|
73
72
|
self.messages_received.fetch_add(1, Ordering::Relaxed);
|
|
74
|
-
self.bytes_received
|
|
73
|
+
self.bytes_received
|
|
74
|
+
.fetch_add(bytes as u64, Ordering::Relaxed);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
pub fn record_error(&self) {
|
|
@@ -79,7 +79,8 @@ impl ProtocolMetrics {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
pub fn record_connection_state_change(&self) {
|
|
82
|
-
self.connection_state_changes
|
|
82
|
+
self.connection_state_changes
|
|
83
|
+
.fetch_add(1, Ordering::Relaxed);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
pub fn get_messages_sent(&self) -> u64 {
|