@feltdb/core 0.15.0 → 0.16.1
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/agent-state.d.ts +20 -0
- package/dist/agent-state.d.ts.map +1 -1
- package/dist/agent-state.js +36 -0
- package/dist/build-identity.json +12 -4
- package/dist/cli/commands.js +187 -4
- package/dist/cli/publish-engine.js +116 -16
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +20 -2
- package/dist/create/build-identity.json +12 -4
- package/dist/create/managed-account.js +93 -35
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +6 -509
- package/dist/create/server-source/Cargo.toml +2 -13
- package/dist/create/server-source/crates/feltdb-client/Cargo.toml +1 -1
- package/dist/create/server-source/crates/feltdb-server/Cargo.toml +1 -1
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +356 -24
- package/dist/create/server-source/crates/feltdb-server/tests/rust_client.rs +10 -1
- package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +1 -1
- package/dist/db.d.ts +11 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +15 -2
- package/dist/error-codes.d.ts +5 -0
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +9 -1
- package/dist/http-db.d.ts +16 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +36 -6
- package/dist/managed-recovery.d.ts +6 -0
- package/dist/managed-recovery.d.ts.map +1 -1
- package/dist/managed-recovery.js +9 -0
- package/dist/studio-app/assets/{feltdb_wasm-BjM1ylL3.js → feltdb_wasm-BUIxCuTR.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-C3D6y6DK.wasm +0 -0
- package/dist/studio-app/assets/index-BhXYAhgS.js +37 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/wasm/package.json +1 -1
- package/dist/workload.d.ts +55 -3
- package/dist/workload.d.ts.map +1 -1
- package/dist/workload.js +23 -0
- package/dist/workspace/workspace-connection.d.ts.map +1 -1
- package/dist/workspace/workspace-connection.js +14 -3
- package/package.json +1 -1
- package/dist/create/server-source/crates/feltdb/Cargo.lock +0 -175
- package/dist/create/server-source/crates/feltdb/Cargo.toml +0 -102
- package/dist/create/server-source/crates/feltdb/LICENSE +0 -21
- package/dist/create/server-source/crates/feltdb/README.md +0 -75
- package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +0 -370
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +0 -589
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +0 -247
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +0 -440
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +0 -275
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +0 -381
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +0 -467
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +0 -489
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +0 -281
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +0 -606
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +0 -531
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +0 -462
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +0 -505
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +0 -484
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +0 -758
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +0 -421
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +0 -329
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +0 -1705
- package/dist/create/server-source/crates/feltdb/src/acquisition.rs +0 -286
- package/dist/create/server-source/crates/feltdb/src/admission.rs +0 -185
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +0 -507
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +0 -539
- package/dist/create/server-source/crates/feltdb/src/agent_state.rs +0 -320
- package/dist/create/server-source/crates/feltdb/src/agent_transition_contract.rs +0 -1545
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +0 -521
- package/dist/create/server-source/crates/feltdb/src/application.rs +0 -2674
- package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +0 -1070
- package/dist/create/server-source/crates/feltdb/src/authority.rs +0 -479
- package/dist/create/server-source/crates/feltdb/src/authority_failover.rs +0 -847
- package/dist/create/server-source/crates/feltdb/src/authorization.rs +0 -1030
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +0 -1122
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority-client.rs +0 -32
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority.rs +0 -19
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +0 -1474
- package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +0 -8
- package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +0 -418
- package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +0 -482
- package/dist/create/server-source/crates/feltdb/src/capability.rs +0 -903
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +0 -263
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -77
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +0 -496
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +0 -2574
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +0 -212
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +0 -422
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +0 -445
- package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +0 -465
- package/dist/create/server-source/crates/feltdb/src/convergence.rs +0 -634
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +0 -427
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +0 -376
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +0 -365
- package/dist/create/server-source/crates/feltdb/src/cron.rs +0 -294
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +0 -483
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +0 -477
- package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +0 -1003
- package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +0 -1334
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +0 -368
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +0 -231
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +0 -482
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +0 -203
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +0 -317
- package/dist/create/server-source/crates/feltdb/src/equality_index.rs +0 -595
- package/dist/create/server-source/crates/feltdb/src/execution.rs +0 -426
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +0 -214
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +0 -776
- package/dist/create/server-source/crates/feltdb/src/lib.rs +0 -7365
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +0 -234
- package/dist/create/server-source/crates/feltdb/src/materialization.rs +0 -184
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -266
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +0 -310
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +0 -736
- package/dist/create/server-source/crates/feltdb/src/observability.rs +0 -379
- package/dist/create/server-source/crates/feltdb/src/operation.rs +0 -290
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +0 -439
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +0 -349
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +0 -332
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +0 -718
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +0 -487
- package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +0 -166
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +0 -289
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +0 -566
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +0 -3391
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +0 -203
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +0 -261
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +0 -502
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +0 -216
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +0 -303
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +0 -323
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +0 -531
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +0 -406
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +0 -436
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +0 -387
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +0 -378
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +0 -359
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +0 -372
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +0 -404
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +0 -376
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +0 -461
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +0 -2125
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +0 -418
- package/dist/create/server-source/crates/feltdb/src/provenance.rs +0 -207
- package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +0 -126
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +0 -218
- package/dist/create/server-source/crates/feltdb/src/read_basis.rs +0 -257
- package/dist/create/server-source/crates/feltdb/src/references.rs +0 -404
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +0 -401
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +0 -501
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +0 -680
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +0 -163
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +0 -133
- package/dist/create/server-source/crates/feltdb/src/routing.rs +0 -409
- package/dist/create/server-source/crates/feltdb/src/semantic_decision.rs +0 -2029
- package/dist/create/server-source/crates/feltdb/src/semantic_query.rs +0 -599
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +0 -549
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +0 -516
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +0 -6003
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +0 -222
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +0 -320
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +0 -291
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +0 -4158
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +0 -379
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +0 -673
- package/dist/create/server-source/crates/feltdb/src/storage.rs +0 -273
- package/dist/create/server-source/crates/feltdb/src/submission.rs +0 -471
- package/dist/create/server-source/crates/feltdb/src/sync.rs +0 -495
- package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +0 -822
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +0 -433
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +0 -462
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +0 -965
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +0 -1120
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +0 -643
- package/dist/create/server-source/crates/feltdb/src/trigger.rs +0 -390
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +0 -749
- package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +0 -708
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +0 -1269
- package/dist/create/server-source/crates/feltdb/src/workflow.rs +0 -713
- package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +0 -510
- package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +0 -354
- package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +0 -594
- package/dist/create/server-source/crates/feltdb/src/workload.rs +0 -4222
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +0 -456
- package/dist/create/server-source/crates/feltdb/tests/agent_state_native.rs +0 -1188
- package/dist/create/server-source/crates/feltdb/tests/agent_transition_contract_conformance.rs +0 -2884
- package/dist/create/server-source/crates/feltdb/tests/authority_process.rs +0 -297
- package/dist/create/server-source/crates/feltdb/tests/authority_transaction_identity.rs +0 -534
- package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +0 -132
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/common/mod.rs +0 -96
- package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +0 -272
- package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +0 -467
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +0 -309
- package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +0 -445
- package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +0 -518
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +0 -392
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +0 -850
- package/dist/create/server-source/crates/feltdb/tests/fixtures/agent_transition_corpus.json +0 -23340
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +0 -1916
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +0 -1878
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +0 -1862
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +0 -123
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/local_process_ownership.rs +0 -69
- package/dist/create/server-source/crates/feltdb/tests/managed_incident_regression.rs +0 -191
- package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +0 -278
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +0 -234
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +0 -947
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +0 -404
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +0 -916
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +0 -1045
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +0 -347
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +0 -361
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +0 -330
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +0 -93
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +0 -622
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +0 -517
- package/dist/create/server-source/crates/feltdb/tests/production_certification.rs +0 -1153
- package/dist/create/server-source/crates/feltdb/tests/production_contract.rs +0 -771
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +0 -1383
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +0 -868
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +0 -221
- package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +0 -417
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +0 -338
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +0 -1039
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +0 -739
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +0 -452
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +0 -81
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +0 -438
- package/dist/create/server-source/crates/feltdb/tests/semantic_decision.rs +0 -1165
- package/dist/create/server-source/crates/feltdb/tests/semantic_query.rs +0 -767
- package/dist/create/server-source/crates/feltdb/tests/semantic_query_contract.rs +0 -363
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +0 -1799
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +0 -1316
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +0 -358
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +0 -365
- package/dist/create/server-source/crates/feltdb/tests/state_ref_authority.rs +0 -918
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +0 -328
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +0 -255
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +0 -249
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +0 -994
- package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +0 -484
- package/dist/studio-app/assets/feltdb_wasm_bg-TseJn2Ax.wasm +0 -0
- package/dist/studio-app/assets/index-1wd0uKua.js +0 -30
|
@@ -1,126 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
/// Query Performance Tracking & Latency Contracts
|
|
2
|
-
///
|
|
3
|
-
/// Manages query performance benchmarks and latency SLAs:
|
|
4
|
-
/// - Query latency tracking (p50, p95, p99)
|
|
5
|
-
/// - Latency contract enforcement
|
|
6
|
-
/// - Performance degradation detection
|
|
7
|
-
/// - Query optimization recommendations
|
|
8
|
-
use serde::{Deserialize, Serialize};
|
|
9
|
-
use std::collections::HashMap;
|
|
10
|
-
use std::time::{Duration, Instant};
|
|
11
|
-
|
|
12
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
13
|
-
pub struct LatencyContract {
|
|
14
|
-
pub query_type: String,
|
|
15
|
-
pub p50_target_us: u64,
|
|
16
|
-
pub p95_target_us: u64,
|
|
17
|
-
pub p99_target_us: u64,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
21
|
-
pub struct QueryMetric {
|
|
22
|
-
pub query_type: String,
|
|
23
|
-
pub duration_us: u64,
|
|
24
|
-
pub timestamp: u64,
|
|
25
|
-
pub index_name: String,
|
|
26
|
-
pub result_count: usize,
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
30
|
-
pub struct QueryLatencyStats {
|
|
31
|
-
pub query_type: String,
|
|
32
|
-
pub count: u64,
|
|
33
|
-
pub p50_us: u64,
|
|
34
|
-
pub p95_us: u64,
|
|
35
|
-
pub p99_us: u64,
|
|
36
|
-
pub min_us: u64,
|
|
37
|
-
pub max_us: u64,
|
|
38
|
-
pub avg_us: u64,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
#[derive(Debug, Clone)]
|
|
42
|
-
struct QueryTimer {
|
|
43
|
-
start: Instant,
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
impl QueryTimer {
|
|
47
|
-
fn new() -> Self {
|
|
48
|
-
QueryTimer {
|
|
49
|
-
start: Instant::now(),
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
fn elapsed_us(&self) -> u64 {
|
|
54
|
-
self.start.elapsed().as_micros() as u64
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
pub struct QueryPerformanceTracker {
|
|
59
|
-
contracts: HashMap<String, LatencyContract>,
|
|
60
|
-
metrics: Vec<QueryMetric>,
|
|
61
|
-
violations: Vec<String>,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
impl QueryPerformanceTracker {
|
|
65
|
-
pub fn new() -> Self {
|
|
66
|
-
QueryPerformanceTracker {
|
|
67
|
-
contracts: HashMap::new(),
|
|
68
|
-
metrics: Vec::new(),
|
|
69
|
-
violations: Vec::new(),
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
pub fn register_contract(&mut self, contract: LatencyContract) {
|
|
74
|
-
self.contracts.insert(contract.query_type.clone(), contract);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
pub fn start_timer(&self) -> QueryTimer {
|
|
78
|
-
QueryTimer::new()
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
pub fn record_query(
|
|
82
|
-
&mut self,
|
|
83
|
-
query_type: String,
|
|
84
|
-
index_name: String,
|
|
85
|
-
result_count: usize,
|
|
86
|
-
duration_us: u64,
|
|
87
|
-
) {
|
|
88
|
-
let metric = QueryMetric {
|
|
89
|
-
query_type: query_type.clone(),
|
|
90
|
-
duration_us,
|
|
91
|
-
timestamp: std::time::SystemTime::now()
|
|
92
|
-
.duration_since(std::time::UNIX_EPOCH)
|
|
93
|
-
.unwrap_or_default()
|
|
94
|
-
.as_secs(),
|
|
95
|
-
index_name,
|
|
96
|
-
result_count,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
if let Some(contract) = self.contracts.get(&query_type) {
|
|
100
|
-
if duration_us > contract.p99_target_us {
|
|
101
|
-
self.violations.push(format!(
|
|
102
|
-
"Query {} violated p99 contract: {} us > {} us",
|
|
103
|
-
query_type, duration_us, contract.p99_target_us
|
|
104
|
-
));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
self.metrics.push(metric);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
pub fn get_stats(&self, query_type: &str) -> Option<QueryLatencyStats> {
|
|
112
|
-
let relevant: Vec<&QueryMetric> = self
|
|
113
|
-
.metrics
|
|
114
|
-
.iter()
|
|
115
|
-
.filter(|m| m.query_type == query_type)
|
|
116
|
-
.collect();
|
|
117
|
-
|
|
118
|
-
if relevant.is_empty() {
|
|
119
|
-
return None;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
let mut durations: Vec<u64> = relevant.iter().map(|m| m.duration_us).collect();
|
|
123
|
-
durations.sort();
|
|
124
|
-
|
|
125
|
-
let count = durations.len() as u64;
|
|
126
|
-
let sum: u64 = durations.iter().sum();
|
|
127
|
-
let avg = sum / count;
|
|
128
|
-
|
|
129
|
-
let p50_idx = (count / 2) as usize;
|
|
130
|
-
let p95_idx = ((count * 95) / 100) as usize;
|
|
131
|
-
let p99_idx = ((count * 99) / 100) as usize;
|
|
132
|
-
|
|
133
|
-
Some(QueryLatencyStats {
|
|
134
|
-
query_type: query_type.to_string(),
|
|
135
|
-
count,
|
|
136
|
-
p50_us: durations.get(p50_idx).copied().unwrap_or(0),
|
|
137
|
-
p95_us: durations.get(p95_idx).copied().unwrap_or(0),
|
|
138
|
-
p99_us: durations.get(p99_idx).copied().unwrap_or(0),
|
|
139
|
-
min_us: *durations.first().unwrap_or(&0),
|
|
140
|
-
max_us: *durations.last().unwrap_or(&0),
|
|
141
|
-
avg_us: avg,
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
pub fn get_violations(&self) -> Vec<String> {
|
|
146
|
-
self.violations.clone()
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
pub fn clear_violations(&mut self) {
|
|
150
|
-
self.violations.clear();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
pub fn export_metrics_csv(&self) -> String {
|
|
154
|
-
let mut csv = String::from("timestamp,query_type,duration_us,index_name,result_count\n");
|
|
155
|
-
for metric in &self.metrics {
|
|
156
|
-
csv.push_str(&format!(
|
|
157
|
-
"{},{},{},{},{}\n",
|
|
158
|
-
metric.timestamp,
|
|
159
|
-
metric.query_type,
|
|
160
|
-
metric.duration_us,
|
|
161
|
-
metric.index_name,
|
|
162
|
-
metric.result_count
|
|
163
|
-
));
|
|
164
|
-
}
|
|
165
|
-
csv
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
impl Default for QueryPerformanceTracker {
|
|
170
|
-
fn default() -> Self {
|
|
171
|
-
Self::new()
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
#[cfg(test)]
|
|
176
|
-
mod tests {
|
|
177
|
-
use super::*;
|
|
178
|
-
|
|
179
|
-
#[test]
|
|
180
|
-
fn test_latency_contract() {
|
|
181
|
-
let mut tracker = QueryPerformanceTracker::new();
|
|
182
|
-
tracker.register_contract(LatencyContract {
|
|
183
|
-
query_type: "index_lookup".to_string(),
|
|
184
|
-
p50_target_us: 100,
|
|
185
|
-
p95_target_us: 500,
|
|
186
|
-
p99_target_us: 1000,
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
let timer = tracker.start_timer();
|
|
190
|
-
std::thread::sleep(Duration::from_micros(50));
|
|
191
|
-
let elapsed = timer.elapsed_us();
|
|
192
|
-
// A sleep lasts at least as long as asked, and on a loaded machine
|
|
193
|
-
// arbitrarily longer, so only the lower bound is a property.
|
|
194
|
-
assert!(elapsed >= 50, "timer measured {elapsed}us for a 50us sleep");
|
|
195
|
-
|
|
196
|
-
tracker.record_query("index_lookup".to_string(), "idx_1".to_string(), 10, elapsed);
|
|
197
|
-
|
|
198
|
-
let stats = tracker.get_stats("index_lookup").unwrap();
|
|
199
|
-
assert_eq!(stats.count, 1);
|
|
200
|
-
assert_eq!(stats.p50_us, elapsed);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
#[test]
|
|
204
|
-
fn test_violation_detection() {
|
|
205
|
-
let mut tracker = QueryPerformanceTracker::new();
|
|
206
|
-
tracker.register_contract(LatencyContract {
|
|
207
|
-
query_type: "slow_query".to_string(),
|
|
208
|
-
p50_target_us: 100,
|
|
209
|
-
p95_target_us: 200,
|
|
210
|
-
p99_target_us: 500,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
tracker.record_query("slow_query".to_string(), "idx_1".to_string(), 100, 600);
|
|
214
|
-
|
|
215
|
-
let violations = tracker.get_violations();
|
|
216
|
-
assert_eq!(violations.len(), 1);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
//! Read-basis fences: commit only if the state a decision read is unchanged.
|
|
2
|
-
//!
|
|
3
|
-
//! ```text
|
|
4
|
-
//! basis = read_basis(records, predicates) -- one token, under the lock
|
|
5
|
-
//! ... decide ...
|
|
6
|
-
//! commit fenced by basis -- re-read under the commit lock
|
|
7
|
-
//! unchanged -> commit
|
|
8
|
-
//! changed -> PreconditionFailed(Fence), ZERO writes
|
|
9
|
-
//! ```
|
|
10
|
-
//!
|
|
11
|
-
//! The fence reads stored rows, not bookkeeping a writer maintains, so it holds
|
|
12
|
-
//! whichever write path changed them. Only the named records and the named
|
|
13
|
-
//! collections' matching rows are read: everything else is outside the fence
|
|
14
|
-
//! and never conflicts.
|
|
15
|
-
|
|
16
|
-
#[cfg(test)]
|
|
17
|
-
mod tests {
|
|
18
|
-
use crate::{
|
|
19
|
-
AtomicMutation, FeltDb, FlowError, PreconditionFailure, ReadBasis, ReadCondition,
|
|
20
|
-
ReadLayout, ReadPredicate, ReadRecord, ReadSet,
|
|
21
|
-
};
|
|
22
|
-
use serde_json::json;
|
|
23
|
-
use tempfile::TempDir;
|
|
24
|
-
|
|
25
|
-
fn db(dir: &TempDir) -> FeltDb {
|
|
26
|
-
FeltDb::open(dir.path().join("app.log")).expect("open")
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
fn set(collection: &str, id: &str, value: serde_json::Value) -> AtomicMutation {
|
|
30
|
-
AtomicMutation {
|
|
31
|
-
capability: collection.to_string(),
|
|
32
|
-
key: format!("{collection}:{id}"),
|
|
33
|
-
value: Some(value),
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
fn delete(collection: &str, id: &str) -> AtomicMutation {
|
|
38
|
-
AtomicMutation {
|
|
39
|
-
capability: collection.to_string(),
|
|
40
|
-
key: format!("{collection}:{id}"),
|
|
41
|
-
value: None,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
fn write(db: &FeltDb, id: &str, mutations: &[AtomicMutation]) {
|
|
46
|
-
db.apply_atomic_transaction(id, None, &[], mutations, None)
|
|
47
|
-
.expect("write commits");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
fn reads() -> ReadSet {
|
|
51
|
-
ReadSet {
|
|
52
|
-
records: vec![ReadRecord {
|
|
53
|
-
collection: "A".into(),
|
|
54
|
-
id: "a1".into(),
|
|
55
|
-
}],
|
|
56
|
-
predicates: vec![ReadPredicate {
|
|
57
|
-
collection: "B".into(),
|
|
58
|
-
conditions: vec![ReadCondition {
|
|
59
|
-
field: "a".into(),
|
|
60
|
-
eq: json!("a1"),
|
|
61
|
-
}],
|
|
62
|
-
}],
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
fn seed(db: &FeltDb) {
|
|
67
|
-
write(
|
|
68
|
-
db,
|
|
69
|
-
"seed",
|
|
70
|
-
&[
|
|
71
|
-
set("A", "a1", json!({ "id": "a1", "n": 1 })),
|
|
72
|
-
set("B", "b1", json!({ "id": "b1", "a": "a1" })),
|
|
73
|
-
set("B", "b2", json!({ "id": "b2", "a": "a2" })),
|
|
74
|
-
],
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
fn fenced(db: &FeltDb, id: &str, basis: &ReadBasis) -> Result<crate::AtomicCommit, FlowError> {
|
|
79
|
-
db.apply_atomic_transaction_fenced(
|
|
80
|
-
id,
|
|
81
|
-
None,
|
|
82
|
-
None,
|
|
83
|
-
&[],
|
|
84
|
-
&[],
|
|
85
|
-
std::slice::from_ref(basis),
|
|
86
|
-
&[set("Z", id, json!({ "id": id }))],
|
|
87
|
-
None,
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
fn is_fence_conflict(result: Result<crate::AtomicCommit, FlowError>) -> bool {
|
|
92
|
-
matches!(result, Err(FlowError::PreconditionFailed(failure)) if matches!(*failure, PreconditionFailure::Fence { .. }))
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
#[test]
|
|
96
|
-
fn an_unchanged_basis_commits_and_a_changed_one_commits_nothing() {
|
|
97
|
-
let dir = TempDir::new().unwrap();
|
|
98
|
-
let db = db(&dir);
|
|
99
|
-
seed(&db);
|
|
100
|
-
let basis = db.read_basis(&reads()).unwrap();
|
|
101
|
-
assert!(fenced(&db, "z1", &basis).is_ok());
|
|
102
|
-
write(&db, "raw", &[set("A", "a1", json!({ "id": "a1", "n": 2 }))]);
|
|
103
|
-
assert!(is_fence_conflict(fenced(&db, "z2", &basis)));
|
|
104
|
-
assert!(
|
|
105
|
-
db.get_value("Z:z2").unwrap().is_none(),
|
|
106
|
-
"a refused transaction writes nothing"
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
#[test]
|
|
111
|
-
fn records_entering_or_leaving_a_predicate_conflict_and_others_do_not() {
|
|
112
|
-
let dir = TempDir::new().unwrap();
|
|
113
|
-
let db = db(&dir);
|
|
114
|
-
seed(&db);
|
|
115
|
-
let basis = db.read_basis(&reads()).unwrap();
|
|
116
|
-
write(
|
|
117
|
-
&db,
|
|
118
|
-
"outside",
|
|
119
|
-
&[
|
|
120
|
-
set("B", "b3", json!({ "id": "b3", "a": "a9" })),
|
|
121
|
-
set("C", "c1", json!({ "id": "c1" })),
|
|
122
|
-
],
|
|
123
|
-
);
|
|
124
|
-
assert!(
|
|
125
|
-
fenced(&db, "z1", &basis).is_ok(),
|
|
126
|
-
"writes outside the read set never conflict"
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
write(
|
|
130
|
-
&db,
|
|
131
|
-
"enter",
|
|
132
|
-
&[set("B", "b4", json!({ "id": "b4", "a": "a1" }))],
|
|
133
|
-
);
|
|
134
|
-
assert!(is_fence_conflict(fenced(&db, "z2", &basis)));
|
|
135
|
-
|
|
136
|
-
let basis = db.read_basis(&reads()).unwrap();
|
|
137
|
-
write(
|
|
138
|
-
&db,
|
|
139
|
-
"leave",
|
|
140
|
-
&[set("B", "b1", json!({ "id": "b1", "a": "a2" }))],
|
|
141
|
-
);
|
|
142
|
-
assert!(is_fence_conflict(fenced(&db, "z3", &basis)));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
#[test]
|
|
146
|
-
fn delete_and_recreate_with_different_content_conflicts() {
|
|
147
|
-
let dir = TempDir::new().unwrap();
|
|
148
|
-
let db = db(&dir);
|
|
149
|
-
seed(&db);
|
|
150
|
-
let basis = db.read_basis(&reads()).unwrap();
|
|
151
|
-
write(&db, "delete", &[delete("A", "a1")]);
|
|
152
|
-
write(
|
|
153
|
-
&db,
|
|
154
|
-
"recreate",
|
|
155
|
-
&[set("A", "a1", json!({ "id": "a1", "n": 7 }))],
|
|
156
|
-
);
|
|
157
|
-
assert!(is_fence_conflict(fenced(&db, "z1", &basis)));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
#[test]
|
|
161
|
-
fn tokens_are_deterministic_and_survive_restart() {
|
|
162
|
-
let dir = TempDir::new().unwrap();
|
|
163
|
-
let basis = {
|
|
164
|
-
let db = db(&dir);
|
|
165
|
-
seed(&db);
|
|
166
|
-
let first = db.read_basis(&reads()).unwrap();
|
|
167
|
-
let mut shuffled = reads();
|
|
168
|
-
shuffled.records.push(ReadRecord {
|
|
169
|
-
collection: "A".into(),
|
|
170
|
-
id: "a1".into(),
|
|
171
|
-
});
|
|
172
|
-
assert_eq!(
|
|
173
|
-
first,
|
|
174
|
-
db.read_basis(&shuffled).unwrap(),
|
|
175
|
-
"normalised: duplicates do not matter"
|
|
176
|
-
);
|
|
177
|
-
first
|
|
178
|
-
};
|
|
179
|
-
let db = db(&dir);
|
|
180
|
-
assert_eq!(db.read_basis(&reads()).unwrap().token, basis.token);
|
|
181
|
-
assert!(fenced(&db, "z1", &basis).is_ok());
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
#[test]
|
|
185
|
-
fn invalid_and_foreign_bases_are_refused_as_invalid_not_as_conflicts() {
|
|
186
|
-
let dir = TempDir::new().unwrap();
|
|
187
|
-
let db = db(&dir);
|
|
188
|
-
seed(&db);
|
|
189
|
-
assert!(db.read_basis(&ReadSet::default()).is_err());
|
|
190
|
-
let mut foreign = db.read_basis(&reads()).unwrap();
|
|
191
|
-
foreign.token = format!("fb1:{}", "0".repeat(64));
|
|
192
|
-
let result = fenced(&db, "z1", &foreign);
|
|
193
|
-
assert!(
|
|
194
|
-
matches!(result, Err(FlowError::CapabilityError(ref message)) if message.starts_with("INVALID_READ_SET:"))
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
#[test]
|
|
199
|
-
fn namespace_bases_read_namespace_rows_and_are_scoped_to_their_namespace() {
|
|
200
|
-
let dir = TempDir::new().unwrap();
|
|
201
|
-
let db = db(&dir);
|
|
202
|
-
let ns = ReadLayout::Namespace("tenant-a".into());
|
|
203
|
-
let row = |collection: &str, id: &str, value: serde_json::Value| AtomicMutation {
|
|
204
|
-
capability: format!("tenant-a:{collection}"),
|
|
205
|
-
key: id.to_string(),
|
|
206
|
-
value: Some(value),
|
|
207
|
-
};
|
|
208
|
-
write(
|
|
209
|
-
&db,
|
|
210
|
-
"seed",
|
|
211
|
-
&[
|
|
212
|
-
row("A", "a1", json!({ "id": "a1", "n": 1 })),
|
|
213
|
-
row("B", "b1", json!({ "id": "b1", "a": "a1" })),
|
|
214
|
-
],
|
|
215
|
-
);
|
|
216
|
-
let basis = db.read_basis_in(&reads(), &ns).unwrap();
|
|
217
|
-
assert!(basis.token.starts_with(&ns.token_prefix()));
|
|
218
|
-
let commit = |id: &str, layout: &ReadLayout, basis: &ReadBasis| {
|
|
219
|
-
db.apply_atomic_transaction_fenced_in(
|
|
220
|
-
id,
|
|
221
|
-
None,
|
|
222
|
-
None,
|
|
223
|
-
&[],
|
|
224
|
-
&[],
|
|
225
|
-
layout,
|
|
226
|
-
std::slice::from_ref(basis),
|
|
227
|
-
&[row("Z", id, json!({ "id": id }))],
|
|
228
|
-
None,
|
|
229
|
-
)
|
|
230
|
-
};
|
|
231
|
-
let invalid = |result: Result<crate::AtomicCommit, FlowError>| matches!(result, Err(FlowError::CapabilityError(ref message)) if message.starts_with("INVALID_READ_SET:"));
|
|
232
|
-
// A token from another scope is invalid, never compared.
|
|
233
|
-
assert!(invalid(commit("z0", &ReadLayout::Direct, &basis)));
|
|
234
|
-
assert!(invalid(commit(
|
|
235
|
-
"z0",
|
|
236
|
-
&ReadLayout::Namespace("tenant-b".into()),
|
|
237
|
-
&basis
|
|
238
|
-
)));
|
|
239
|
-
let direct = db.read_basis(&reads()).unwrap();
|
|
240
|
-
assert!(invalid(commit("z0", &ns, &direct)));
|
|
241
|
-
// Direct-layout writes to the same names are outside the namespace.
|
|
242
|
-
write(
|
|
243
|
-
&db,
|
|
244
|
-
"direct",
|
|
245
|
-
&[set("A", "a1", json!({ "id": "a1", "n": 9 }))],
|
|
246
|
-
);
|
|
247
|
-
assert!(commit("z1", &ns, &basis).is_ok());
|
|
248
|
-
// A namespace write the basis read refuses the commit.
|
|
249
|
-
write(
|
|
250
|
-
&db,
|
|
251
|
-
"raw",
|
|
252
|
-
&[row("B", "b9", json!({ "id": "b9", "a": "a1" }))],
|
|
253
|
-
);
|
|
254
|
-
let result = commit("z2", &ns, &basis);
|
|
255
|
-
assert!(is_fence_conflict(result));
|
|
256
|
-
}
|
|
257
|
-
}
|