@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.
Files changed (193) hide show
  1. package/dist/cli/commands.js +4 -1
  2. package/dist/cli/provisioning-neutrality.js +79 -0
  3. package/dist/collection.d.ts +43 -1
  4. package/dist/collection.d.ts.map +1 -1
  5. package/dist/collection.js +192 -22
  6. package/dist/create/create.js +25 -21
  7. package/dist/create/managed-account.js +11 -0
  8. package/dist/create/package-versions.js +1 -1
  9. package/dist/create/server-source/Cargo.lock +165 -0
  10. package/dist/create/server-source/Cargo.toml +9 -0
  11. package/dist/create/server-source/crates/feltdb/Cargo.toml +3 -0
  12. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +44 -44
  13. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +12 -24
  14. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +7 -13
  15. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +13 -10
  16. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +12 -9
  17. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +78 -25
  18. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +16 -13
  19. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +13 -5
  20. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +41 -22
  21. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +33 -15
  22. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +100 -33
  23. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +56 -20
  24. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +116 -41
  25. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +186 -55
  26. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +64 -26
  27. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +46 -15
  28. package/dist/create/server-source/crates/feltdb/src/admission.rs +8 -15
  29. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +43 -13
  30. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +15 -42
  31. package/dist/create/server-source/crates/feltdb/src/analytics.rs +65 -19
  32. package/dist/create/server-source/crates/feltdb/src/application.rs +113 -30
  33. package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +475 -140
  34. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +17 -15
  35. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -1
  36. package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +59 -15
  37. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +266 -114
  38. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +25 -7
  39. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +10 -15
  40. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +3 -11
  41. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +14 -5
  42. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +21 -25
  43. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +14 -11
  44. package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +103 -22
  45. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +18 -15
  46. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +12 -8
  47. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +1 -5
  48. package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +87 -23
  49. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +3 -7
  50. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +10 -9
  51. package/dist/create/server-source/crates/feltdb/src/equality_index.rs +595 -0
  52. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +1 -6
  53. package/dist/create/server-source/crates/feltdb/src/indexing.rs +35 -38
  54. package/dist/create/server-source/crates/feltdb/src/lib.rs +1050 -117
  55. package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +4 -1
  56. package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -1
  57. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +1 -2
  58. package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +107 -30
  59. package/dist/create/server-source/crates/feltdb/src/observability.rs +19 -6
  60. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +12 -11
  61. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +9 -4
  62. package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +65 -18
  63. package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +193 -57
  64. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +37 -27
  65. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +38 -10
  66. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +20 -14
  67. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +394 -229
  68. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +8 -6
  69. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +11 -13
  70. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +90 -72
  71. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +3 -3
  72. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +33 -11
  73. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +6 -6
  74. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +14 -56
  75. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +29 -27
  76. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +35 -17
  77. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +8 -2
  78. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +59 -15
  79. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +70 -17
  80. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +23 -6
  81. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +12 -3
  82. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +41 -11
  83. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +56 -25
  84. package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +701 -245
  85. package/dist/create/server-source/crates/feltdb/src/production_api.rs +31 -13
  86. package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +126 -0
  87. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +6 -8
  88. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +5 -5
  89. package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +48 -18
  90. package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +30 -11
  91. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +6 -3
  92. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +4 -3
  93. package/dist/create/server-source/crates/feltdb/src/sharding.rs +36 -10
  94. package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +516 -0
  95. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +305 -6
  96. package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +222 -0
  97. package/dist/create/server-source/crates/feltdb/src/state_facade.rs +82 -54
  98. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +2 -2
  99. package/dist/create/server-source/crates/feltdb/src/state_model.rs +1514 -537
  100. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +6 -3
  101. package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +672 -0
  102. package/dist/create/server-source/crates/feltdb/src/submission.rs +5 -11
  103. package/dist/create/server-source/crates/feltdb/src/sync.rs +12 -0
  104. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +6 -8
  105. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +24 -35
  106. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +24 -8
  107. package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +248 -59
  108. package/dist/create/server-source/crates/feltdb/src/transactions.rs +17 -20
  109. package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +749 -0
  110. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
  111. package/dist/create/server-source/crates/feltdb/src/workload.rs +512 -4
  112. package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +442 -0
  113. package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +299 -0
  114. package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +288 -0
  115. package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +392 -0
  116. package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +436 -220
  117. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +1916 -0
  118. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +1878 -0
  119. package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +1862 -0
  120. package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +234 -0
  121. package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +947 -0
  122. package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +5 -8
  123. package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +52 -44
  124. package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +33 -16
  125. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +22 -7
  126. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +41 -22
  127. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +25 -8
  128. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +9 -6
  129. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +120 -69
  130. package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +21 -10
  131. package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +1032 -0
  132. package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +868 -0
  133. package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +221 -0
  134. package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +338 -0
  135. package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +1039 -0
  136. package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +739 -0
  137. package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +427 -0
  138. package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +3 -3
  139. package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +25 -22
  140. package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +1799 -0
  141. package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +1316 -0
  142. package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +53 -61
  143. package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +156 -61
  144. package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +299 -0
  145. package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +255 -0
  146. package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +249 -0
  147. package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +994 -0
  148. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +2 -1
  149. package/dist/create/server-source/crates/feltdb-server/src/audit.rs +1137 -29
  150. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
  151. package/dist/create/server-source/crates/feltdb-server/src/main.rs +800 -34
  152. package/dist/db.d.ts +33 -34
  153. package/dist/db.d.ts.map +1 -1
  154. package/dist/db.js +74 -20
  155. package/dist/deployment.d.ts +30 -0
  156. package/dist/deployment.d.ts.map +1 -0
  157. package/dist/deployment.js +130 -0
  158. package/dist/embedded-transaction.d.ts +22 -4
  159. package/dist/embedded-transaction.d.ts.map +1 -1
  160. package/dist/embedded-transaction.js +51 -5
  161. package/dist/feltdb.d.ts +14 -2
  162. package/dist/feltdb.d.ts.map +1 -1
  163. package/dist/file-db.js +1 -1
  164. package/dist/http-client.d.ts +14 -0
  165. package/dist/http-client.d.ts.map +1 -1
  166. package/dist/http-client.js +23 -5
  167. package/dist/http-db.d.ts +119 -1
  168. package/dist/http-db.d.ts.map +1 -1
  169. package/dist/http-db.js +346 -31
  170. package/dist/index-core.d.ts +2 -0
  171. package/dist/index-core.d.ts.map +1 -1
  172. package/dist/index-core.js +2 -0
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +9 -0
  175. package/dist/indexeddb-db.d.ts.map +1 -1
  176. package/dist/indexeddb-db.js +35 -21
  177. package/dist/managed-recovery.d.ts +192 -0
  178. package/dist/managed-recovery.d.ts.map +1 -0
  179. package/dist/managed-recovery.js +242 -0
  180. package/dist/memory-db.js +1 -1
  181. package/dist/studio-app/assets/{feltdb_wasm-DB8cX151.js → feltdb_wasm-DaNwCLRX.js} +1 -1
  182. package/dist/studio-app/assets/feltdb_wasm_bg-DnsHNv6g.wasm +0 -0
  183. package/dist/studio-app/assets/index-j8IlhNqJ.js +29 -0
  184. package/dist/studio-app/index.html +1 -1
  185. package/dist/transaction.d.ts +30 -0
  186. package/dist/transaction.d.ts.map +1 -1
  187. package/dist/transaction.js +41 -0
  188. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  189. package/dist/workload.d.ts +2 -1
  190. package/dist/workload.d.ts.map +1 -1
  191. package/package.json +1 -1
  192. package/dist/studio-app/assets/feltdb_wasm_bg-ClhDHp0S.wasm +0 -0
  193. 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(&self, ids: &[&str], _options: ReadOptions) -> Result<Vec<StateValue>, String> {
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(&mut self, mutation: Mutation, options: WriteOptions) -> Result<MutationResult, String> {
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.insert(mutation.resource_id.clone(), value.clone());
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(&mut self, request: TransactionRequest) -> Result<TransactionResult, String> {
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(&self, query: RelationshipQuery) -> Result<RelationshipResult, String> {
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("user-1".to_string(), "follows".to_string(), "user-2".to_string());
363
- api.add_relationship("user-1".to_string(), "follows".to_string(), "user-3".to_string());
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, metric.query_type, metric.duration_us, metric.index_name, metric.result_count
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(operation_log: MemoryOperationLog, transition_store: MemoryStateTransitionStore) -> Self {
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 = std::fs::read(&path).map_err(|e| AcknowledgementError::Io(e.to_string()))?;
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 { path, acknowledgements })
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
- std::fs::File::create(&temp).map_err(|e| AcknowledgementError::Io(e.to_string()))?;
159
- file.write_all(&bytes).map_err(|e| AcknowledgementError::Io(e.to_string()))?;
160
- file.sync_all().map_err(|e| AcknowledgementError::Io(e.to_string()))?;
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 { waiting_on: Vec::new() };
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 { waiting_on: missing };
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 { frontier, covered: required }
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"])).unwrap();
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!(waiting_on, vec!["ra", "rc"], "and it names who it waits for");
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"])).unwrap();
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!(acks.evidence(&[]), ReclamationEvidence::Blocked { .. }));
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"])).unwrap();
399
- acks.record(ack("rb", &[("A", 5), ("B", 6)], &["ra", "rb"])).unwrap();
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"])).unwrap();
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"])).unwrap();
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.get("rb").unwrap().origin_sequence_high_water.get("A"),
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 { return Ok(()) };
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 = serde_json::to_vec_pretty(cluster)
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 = std::fs::File::create(&temp)
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!(seen.status, ReplicaStatus::Active, "contact reinstates a suspected replica");
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!(cluster.join_replica("ra", "A"), Err(MembershipError::NoCluster)));
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!(cluster.approve_replica("ra"), Err(MembershipError::NotJoining(_))),
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.join_replica(&format!("r{index}"), &format!("N{index}")).unwrap();
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!(!path.with_extension("tmp").exists(), "no temp file is left at rest");
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.iter().filter(|m| m.status == DeliveryStatus::Pending).count()
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.insert(peer_id.clone(), PeerReplicationQueue::new(peer_id));
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.fetch_add(bytes as u64, Ordering::Relaxed);
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.fetch_add(1, Ordering::Relaxed);
82
+ self.connection_state_changes
83
+ .fetch_add(1, Ordering::Relaxed);
83
84
  }
84
85
 
85
86
  pub fn get_messages_sent(&self) -> u64 {