feat(rulake): per-shard over-request for federated top-K (SOTA folklore rule)

Closes the data-skew recall gap the 2026-04-23 cache/federation SOTA
agent flagged. Weaviate/Elasticsearch default to k-per-shard which
under-recalls when the true top-K is concentrated in one shard.

Policy:  k' = k + ceil(sqrt(k * ln(S))), clamped to [k, 4k]

Examples:
  k=10, S=2  → k'=13
  k=10, S=4  → k'=14
  k=10, S=16 → k'=16
  k=10, S=64 → k'=17

At k=10 the over-request is ~30-70% of k — cheap insurance against
skew. Formula is the folklore rule cited in SPIRE (arxiv 2512.17264),
HARMONY (SIGMOD'25), and the OpenSearch recall guide. Extra cost per
shard is O(k' × rerank) — negligible vs the scan cost at rerank=20.

Single-shard (S=1) returns k unchanged. Callers can still override
via search_federated_with_rerank to get exact parity.

21 federation tests passing. Clippy -D warnings clean.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruvnet 2026-04-23 22:04:58 -04:00
parent 4f458cd837
commit a0fdd4d9b0

View file

@ -289,10 +289,23 @@ impl RuLake {
Some((global / shards).max(Self::MIN_PER_SHARD_RERANK))
}
});
// Per-shard over-request (2026-04-23 SOTA-federation agent):
// k' = k + ceil(sqrt(k * ln(S)))
// Closes the data-skew gap Weaviate / Elasticsearch leave with
// naive k-per-shard fan-out. At k=10, S=4 this gives k'=13;
// at k=10, S=16, k'=16. Cheap insurance against one shard
// holding disproportionately more of the true top-K.
let k_per_shard = Self::over_request_k(k, shards);
let shard_hits: Result<Vec<Vec<SearchResult>>> = targets
.par_iter()
.map(|(backend, collection)| {
self.search_one_with_rerank(backend, collection, query, k, rerank_override)
self.search_one_with_rerank(
backend,
collection,
query,
k_per_shard,
rerank_override,
)
})
.collect();
let mut merged: Vec<SearchResult> = shard_hits?.into_iter().flatten().collect();
@ -301,6 +314,23 @@ impl RuLake {
Ok(merged)
}
/// Per-shard over-request for federated search:
/// `k' = k + ceil(sqrt(k * ln(S)))`, clamped to `[k, 4k]`.
///
/// The formula is the folklore rule cited in the 2024-2025
/// federated-ANN literature (see SPIRE, HARMONY, OpenSearch
/// recall guide). Over-requests enough to cover data-skew across
/// shards without pulling so many rows that rerank cost explodes.
/// Single-shard (S=1) returns k unchanged.
#[inline]
fn over_request_k(k: usize, shards: usize) -> usize {
if shards <= 1 || k == 0 {
return k;
}
let bonus = ((k as f64) * (shards as f64).ln()).sqrt().ceil() as usize;
(k + bonus).min(k.saturating_mul(4))
}
/// Like [`search_one`] but with an optional per-call rerank override.
/// The federated path uses this to fan out with a reduced rerank
/// budget per shard.