use std::sync::Arc;
use fathomdb_embedder_api::{Embedder, EmbedderError, EmbedderIdentity, Vector};
use fathomdb_engine::{
clock_reads_for_test, Engine, InitialState, PreparedWrite, SearchResult, SourceId,
SEARCH_RERANK_LIMIT,
};
use fathomdb_schema::SQLITE_SUFFIX;
use tempfile::TempDir;
#[derive(Clone, Debug)]
struct RankedEmbedder;
fn rank_of(text: &str) -> f32 {
let bytes = text.as_bytes();
if bytes.len() >= 3
&& bytes[0] == b'r'
&& bytes[1].is_ascii_digit()
&& bytes[2].is_ascii_digit()
{
return text[1..3].parse::<f32>().unwrap_or(0.0);
}
0.0
}
impl Embedder for RankedEmbedder {
fn identity(&self) -> EmbedderIdentity {
EmbedderIdentity::new("ranked", "rev-a", 8)
}
fn embed(&self, text: &str) -> Result<Vector, EmbedderError> {
let mut v = vec![0.0_f32; 8];
v[0] = 1.0;
v[1] = rank_of(text) * 0.01;
Ok(v)
}
}
const FAR_PAST_UNTIL: i64 = 2_000;
static CLOCK_METER: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn clock_meter_guard() -> std::sync::MutexGuard<'static, ()> {
CLOCK_METER.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn node_win(
logical_id: &str,
body: &str,
valid_from: Option<i64>,
valid_until: Option<i64>,
) -> PreparedWrite {
PreparedWrite::Node {
kind: "doc".to_string(),
body: body.to_string(),
source_id: SourceId::new("test:s15b-fix3").expect("test source id"),
logical_id: Some(logical_id.to_string()),
state: InitialState::Active,
reason: None,
valid_from,
valid_until,
}
}
fn bodies(result: &SearchResult) -> Vec<String> {
let mut out: Vec<String> = result.results.iter().map(|h| h.body.clone()).collect();
out.sort();
out
}
fn vector_row_count(path: &std::path::Path) -> i64 {
let conn = rusqlite::Connection::open_with_flags(
path,
rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_URI,
)
.expect("open read-only");
conn.query_row("SELECT count(*) FROM vector_default", [], |r| r.get::<_, i64>(0))
.expect("count vector rows")
}
fn expired_row_count(path: &std::path::Path) -> i64 {
let conn = rusqlite::Connection::open_with_flags(
path,
rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_URI,
)
.expect("open read-only");
conn.query_row(
"SELECT count(*) FROM canonical_nodes
WHERE superseded_at IS NULL AND valid_until IS NOT NULL AND valid_until <= ?1",
[FAR_PAST_UNTIL],
|r| r.get::<_, i64>(0),
)
.expect("count expired rows")
}
#[test]
fn expired_nearest_neighbours_do_not_starve_valid_vector_hits() {
let _meter = clock_meter_guard();
let dir = TempDir::new().unwrap();
let path = dir.path().join(format!("f1_cutoff{SQLITE_SUFFIX}"));
assert_eq!(SEARCH_RERANK_LIMIT, 10, "this fixture is sized to the production cutoff");
let mut batch: Vec<PreparedWrite> = Vec::new();
for i in 1..=10 {
batch.push(node_win(
&format!("EXPIRED{i:02}"),
&format!("r{i:02} telemetry rollup"),
None,
Some(FAR_PAST_UNTIL),
));
}
batch.push(node_win("VALID11", "r11 telemetry rollup", None, None));
batch.push(node_win("VALID12", "r12 telemetry rollup", None, None));
{
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(RankedEmbedder))
.expect("open for seed");
opened.engine.configure_vector_kind_for_test("doc").expect("vector kind doc");
opened.engine.write(&batch).expect("seed write");
opened.engine.drain(10_000).expect("drain");
opened.engine.close().expect("close");
}
assert_eq!(vector_row_count(&path), 12, "every seeded row must be a vector candidate");
assert_eq!(expired_row_count(&path), 10, "ten expired windows must be on disk");
let opened =
Engine::open_with_embedder_for_test(&path, Arc::new(RankedEmbedder)).expect("reopen");
let engine = &opened.engine;
engine.set_vector_stage_only_for_test(true);
let hits = engine.search("telemetry").expect("search");
assert_eq!(
bodies(&hits),
vec!["r11 telemetry rollup".to_string(), "r12 telemetry rollup".to_string()],
"valid rows ranked just below the cutoff must not be starved by expired \
nearer neighbours — validity has to participate BEFORE the LIMIT"
);
opened.engine.close().unwrap();
}
#[test]
fn default_view_search_reads_the_clock_once_per_query() {
let _meter = clock_meter_guard();
let dir = TempDir::new().unwrap();
let path = dir.path().join(format!("f2_instant{SQLITE_SUFFIX}"));
{
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(RankedEmbedder))
.expect("open for seed");
opened.engine.configure_vector_kind_for_test("doc").expect("vector kind doc");
opened
.engine
.write(&[
node_win("A", "r01 telemetry rollup", None, None),
node_win("B", "r02 telemetry digest", None, None),
])
.expect("seed write");
opened.engine.drain(10_000).expect("drain");
opened.engine.close().expect("close");
}
let opened =
Engine::open_with_embedder_for_test(&path, Arc::new(RankedEmbedder)).expect("reopen");
let engine = &opened.engine;
let before = clock_reads_for_test();
engine.search_reranked("telemetry", None, 0, true, 0.3, 0).expect("graph-arm search");
let after = clock_reads_for_test();
assert_eq!(
after - before,
1,
"a default-view query must resolve its validity instant ONCE and share it \
with every arm (text, vector, graph); {} reads means the arms can \
disagree across a validity boundary",
after - before
);
opened.engine.close().unwrap();
}
#[test]
fn default_view_search_without_graph_arm_also_reads_the_clock_once() {
let _meter = clock_meter_guard();
let dir = TempDir::new().unwrap();
let path = dir.path().join(format!("f2_control{SQLITE_SUFFIX}"));
{
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(RankedEmbedder))
.expect("open for seed");
opened.engine.configure_vector_kind_for_test("doc").expect("vector kind doc");
opened
.engine
.write(&[node_win("A", "r01 telemetry rollup", None, None)])
.expect("seed write");
opened.engine.drain(10_000).expect("drain");
opened.engine.close().expect("close");
}
let opened =
Engine::open_with_embedder_for_test(&path, Arc::new(RankedEmbedder)).expect("reopen");
let engine = &opened.engine;
let before = clock_reads_for_test();
engine.search("telemetry").expect("search");
let after = clock_reads_for_test();
assert_eq!(after - before, 1, "the two-arm path also resolves the instant exactly once");
opened.engine.close().unwrap();
}