Experiment 100: Bounded stream re-query scheduler

Date: 2026-04-25

Status: Rejected

Problem

Experiment 083 fixed the worst stream invalidation backlog by coalescing

re-runs before they enter the reader pool. The remaining issue is high fan-out:

when many active streams are invalidated by the same write, the stream engine

can still hand one re-query per stream to ReaderPool.

That is useful when only a few streams are dirty and readers are idle. It is

less useful when dozens of streams are invalidated at once, because stream

re-query work can occupy every reader slot and delay unrelated reads.

Hypothesis

Bound stream re-query dispatch to roughly the reader-pool width for large

fan-out invalidations, while preserving the legacy eager path for small

fan-out invalidations where readers are immediately available.

This should improve unrelated read availability during high fan-out stream

invalidations without regressing the common one-stream path or existing stream

drain benchmarks.

Approach

Added a small scheduler in StreamEngine:

dispatching every time an invalidation arrives.

are idle.

ReaderPool.workerCount stream re-queries into the pool at once.

stream can run.

Added a focused benchmark harness at

benchmark/experiments/stream_scheduler.dart that measures one row update

feeding 1, 8, 32, and 64 distinct active streams.

The candidate also included a correctness stress test covering 64 distinct

streams invalidated by a single write.

Results

Artifacts:

Focused benchmark command:

 dart \ -DRESQLITE_STREAM_SCHEDULER_ROUNDS=3000 \ -DRESQLITE_STREAM_SCHEDULER_REPEATS=5 \ run benchmark/experiments/stream_scheduler.dart 

Baseline was run through the baseline worktree package config, using the same

benchmark script.

The table uses the final adjacent baseline/candidate rerun. Each cell is the

median of five full benchmark runs; each full run contains 3,000 write +

stream-drain cycles per workload.

WorkloadBaseline p50Candidate p50Baseline p95Candidate p95Baseline p99Candidate p99Baseline updates/secCandidate updates/secRead
1 stream0.033 ms0.033 ms0.053 ms0.055 ms0.096 ms0.093 ms26.7k26.0kneutral
8 streams0.117 ms0.103 ms0.228 ms0.170 ms0.339 ms0.250 ms7.3k8.7kwin
32 streams0.454 ms0.357 ms0.711 ms0.814 ms1.094 ms1.501 ms2.1k2.4kmixed
64 streams0.958 ms0.804 ms1.577 ms1.492 ms2.307 ms2.311 ms1.0k1.1kwin

This looked promising in isolation, but it did not measure the main reason to

bound re-queries: protecting unrelated reads while stream re-query work is

backlogged.

After adding that probe workload, the hypothesis did not hold:

WorkloadBaseline p50Candidate p50Baseline p95Candidate p95Baseline p99Candidate p99Baseline cycles/secCandidate cycles/sec
Probe read during 64-stream fan-out0.141 ms0.151 ms0.204 ms0.287 ms0.338 ms0.724 ms1.1k1.1k

The release suite was worse for the existing app-shaped high-fanout benchmark:

MetricFresh baselineCandidateResult
High-Cardinality Stream Fan-out (100 streams × 200 writes)236.54 ms479.42 ms103% slower
Keyed PK Subscriptions (50 streams × 200 random-PK writes)216.78 ms215.35 msneutral
Reactive feed with 100 concurrent writes108.43 ms105.89 msneutral

The high-cardinality result is the deciding signal. This scheduler can improve

some synthetic fan-out timings, but the standard benchmark that most closely

resembles high-cardinality reactive apps regresses materially.

Correctness checks passed:

Full dart test was not used for this experiment because a clean worktree is

missing gitignored Drift generated files under benchmark/drift/*.g.dart.

Decision

Reject.

The implementation is correct, but the performance trade-off is wrong. Bounding

stream re-query admission helps only some synthetic drain shapes, does not

improve the targeted unrelated-read-under-fanout scenario, and more than

doubles the standard high-cardinality stream fan-out runtime.

The broader lesson is useful: a stream scheduler must optimize the app-shaped

mix, not just the number of re-query tasks admitted to the reader pool. If this

area is revisited, the benchmark should start from competing normal reads

during reactive fan-out and from A11b, not from isolated all-stream drain time.