From dc355502067d9a925608ccf0182192051b46d466 Mon Sep 17 00:00:00 2001 From: dawn <90008@klbr.net> Date: Sat, 11 Jul 2026 20:21:44 +0300 Subject: [PATCH] [db] poison-check point ops, unify key separator Ks gains inherent get/insert/remove/contains_key mirroring Keyspace's signatures with the fatal poison check built in; inherent methods win over the deref bridge, so every db..() callsite is covered without churn. the dead async insert/remove/contains_key helpers on Db are gone; Db::get checks poison before erasing the error. db/filter.rs loses its private SEP copy in favor of keys::SEP (v9 migration updated to match). part of hydrant-9xa (phase 2) --- .beads/interactions.jsonl | 1 + src/db/filter.rs | 2 +- src/db/migration/v9.rs | 4 ++-- src/db/mod.rs | 39 +++++++-------------------------------- src/db/schema.rs | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index bb434cb..31fca89 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -38,3 +38,4 @@ {"id":"int-673a873b685f9bb3289d8f60a3543353","kind":"field_change","created_at":"2026-07-11T12:21:57.79772Z","actor":"dawn","issue_id":"hydrant-9ka.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"tests/feature_matrix.nu checks 13 supported combos error+warning-free; fixed bare-indexer, featureless, firehose-diagnostics, and relay combo breakage; documented in AGENTS.md; lib tests pass on default (69), relay (48), relay+jetstream (51)"}} {"id":"int-98c8017fe132cfedeb794583f35d7136","kind":"field_change","created_at":"2026-07-11T15:50:21.796448Z","actor":"dawn","issue_id":"hydrant-9ka.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"all four seams landed: (1) Config::default mode_defaults modules, (2) ingest per-mode EventSink (indexer/relay/none) replacing inline cfg blocks in worker/context/handlers + diagnostics ZST facade removing ~100 hot-path gates, (3) db keyspaces grouped into IndexerDb/StreamDb/JetstreamDb/RelayDb declared in db/keyspaces.rs incl. keyspace_by_name table, (4) types/event split per mode + ops::record_events emitter + backlink_ops hooks making apply_commit unconditional. cfg mentions 720->551; remaining sites are mod decls, struct groups, route registration, and composition roots (run.rs, sink.rs, keyspaces.rs, firehose_stats.rs) plus mode-internal sub-feature coupling (shard.rs/process.rs — dedup follow-up filed). metric note: order-of-magnitude numeric target not hit; the shared-logic confetti the epic complains about is gone. verified: 13-combo matrix warning-free, lib tests 69/60/51/93 across modes, live full-network indexer smoke (live creates w/ payloads over /stream), relay subscribeRepos CBOR frames E2E, debug/crawler/ttl/repos integration tests."}} {"id":"int-4076df0c4f5aa199da3c2b00104151a6","kind":"field_change","created_at":"2026-07-11T15:50:22.329086Z","actor":"dawn","issue_id":"hydrant-9ka","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"epic complete: matrix check (9ka.1) + composable mode seams (9ka.2); runtime-mode child superseded per dawn's direction (keep compile-time gates)"}} +{"id":"int-8eba29aa89c70bffe53d29a8ac491e16","kind":"field_change","created_at":"2026-07-11T17:15:31.961561Z","actor":"dawn","issue_id":"hydrant-9xa.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"landed: schema table + registry derivation + open-time completeness assert; matrix 13/13, lib tests 69/69, integration suite green minus known-env websocat + preexisting hydrant-580"}} diff --git a/src/db/filter.rs b/src/db/filter.rs index f5829fc..41af0f0 100644 --- a/src/db/filter.rs +++ b/src/db/filter.rs @@ -2,6 +2,7 @@ use fjall::{Keyspace, OwnedWriteBatch}; use jacquard_common::types::string::Did; use miette::{IntoDiagnostic, Result}; +use crate::db::keys::SEP; use crate::db::types::TrimmedDid; use crate::filter::{FilterConfig, FilterMode}; use crate::patch::SetUpdate; @@ -10,7 +11,6 @@ pub const MODE_KEY: &[u8] = b"m"; pub const SIGNAL_PREFIX: u8 = b's'; pub const COLLECTION_PREFIX: u8 = b'c'; pub const EXCLUDE_PREFIX: u8 = b'x'; -pub const SEP: u8 = b'|'; pub fn signal_key(val: &str) -> Result> { let mut key = Vec::with_capacity(2 + val.len()); diff --git a/src/db/migration/v9.rs b/src/db/migration/v9.rs index 2d4461c..a4a8b21 100644 --- a/src/db/migration/v9.rs +++ b/src/db/migration/v9.rs @@ -9,7 +9,7 @@ use {crate::db::types::TrimmedDid, jacquard_common::types::did::Did, miette::Int #[cfg(feature = "indexer")] pub(crate) fn migrate_v9(db: &Db, batch: &mut OwnedWriteBatch) -> Result<()> { // 1. Migrate excludes - let exclude_prefix = [crate::db::filter::EXCLUDE_PREFIX, crate::db::filter::SEP]; + let exclude_prefix = [crate::db::filter::EXCLUDE_PREFIX, crate::db::keys::SEP]; for guard in db.filter.prefix(exclude_prefix) { let (k, _) = guard.into_inner().into_diagnostic()?; let val_bytes = &k[exclude_prefix.len()..]; @@ -19,7 +19,7 @@ pub(crate) fn migrate_v9(db: &Db, batch: &mut OwnedWriteBatch) -> Result<()> { let trimmed = TrimmedDid::from(&did); let mut new_key = Vec::with_capacity(2 + trimmed.len()); new_key.push(crate::db::filter::EXCLUDE_PREFIX); - new_key.push(crate::db::filter::SEP); + new_key.push(crate::db::keys::SEP); trimmed.write_to_vec(&mut new_key); batch.insert(&db.filter, new_key, []); diff --git a/src/db/mod.rs b/src/db/mod.rs index 690e74f..8bad2d5 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -122,38 +122,13 @@ impl Db { pub async fn get(ks: Keyspace, key: impl Into) -> Result> { let key = key.into(); - tokio::task::spawn_blocking(move || ks.get(key).into_diagnostic()) - .await - .into_diagnostic()? - } - - #[allow(dead_code)] - pub async fn insert( - ks: Keyspace, - key: impl Into, - value: impl Into, - ) -> Result<()> { - let key = key.into(); - let value = value.into(); - tokio::task::spawn_blocking(move || ks.insert(key, value).into_diagnostic()) - .await - .into_diagnostic()? - } - - #[allow(dead_code)] - pub async fn remove(ks: Keyspace, key: impl Into) -> Result<()> { - let key = key.into(); - tokio::task::spawn_blocking(move || ks.remove(key).into_diagnostic()) - .await - .into_diagnostic()? - } - - #[allow(dead_code)] - pub async fn contains_key(ks: Keyspace, key: impl Into) -> Result { - let key = key.into(); - tokio::task::spawn_blocking(move || ks.contains_key(key).into_diagnostic()) - .await - .into_diagnostic()? + tokio::task::spawn_blocking(move || { + ks.get(key) + .inspect_err(check_poisoned) + .into_diagnostic() + }) + .await + .into_diagnostic()? } } diff --git a/src/db/schema.rs b/src/db/schema.rs index c8145e0..eee48ae 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -112,6 +112,38 @@ impl Ks { pub fn raw(&self) -> &Keyspace { &self.inner } + + /// point reads/writes mirror [`Keyspace`]'s signatures exactly, adding + /// the fatal poison check every callsite previously had to remember. + /// inherent methods win over the deref bridge, so all `db..()` + /// callsites route through these. + #[inline] + pub fn get>(&self, key: K) -> fjall::Result> { + self.inner.get(key).inspect_err(super::check_poisoned) + } + + #[inline] + pub fn contains_key>(&self, key: K) -> fjall::Result { + self.inner + .contains_key(key) + .inspect_err(super::check_poisoned) + } + + #[inline] + pub fn insert, V: Into>( + &self, + key: K, + value: V, + ) -> fjall::Result<()> { + self.inner + .insert(key, value) + .inspect_err(super::check_poisoned) + } + + #[inline] + pub fn remove>(&self, key: K) -> fjall::Result<()> { + self.inner.remove(key).inspect_err(super::check_poisoned) + } } /// phase-1 migration bridge: existing callsites use raw `Keyspace` methods -- 2.51.2