Skip to content

perf(sipflow): scope query bucket enumeration to the requested window - #271

Merged
yeoleobun merged 1 commit into
restsend:mainfrom
ftong2010:sipflow-query-fanout
Sep 3, 2026
Merged

perf(sipflow): scope query bucket enumeration to the requested window#271
yeoleobun merged 1 commit into
restsend:mainfrom
ftong2010:sipflow-query-fanout

Conversation

@ftong2010

Copy link
Copy Markdown

Motivation

Every recording/flow query enumerated all historical buckets: one
fresh sqlite connection plus one — almost always empty — SELECT per
bucket (discover_data_dirs deliberately appends out-of-range buckets,
"scanning extra directories is safe"). With 13 accumulated hour buckets
and 3 query types per call, that measured ~77 SELECT statements per
recording
(194,530 selects / 2,527 recordings, via the pipeline
metrics from #270), and per-query read IO grew linearly with retention.

What this PR changes

StorageManager::get_folders_in_range — the query hot path — now uses
discover_data_dirs_in_range, which enumerates only buckets overlapping
the requested window, widened by one bucket on each side to absorb
flush lag and writer/querier clock skew. Ingest buckets by wall-clock
capture time, so per-query read IO is now constant instead of growing
with history.

The legacy whole-tree scan is preserved behind
StorageManager::with_scan_out_of_range(true) for replayed or
mis-bucketed data (e.g. a bucket written by a subdirs = none writer
rooted elsewhere). Diagnostics (run_diag) opt in, as does the existing
test_daily_dir_containing_other_days_data_is_still_queryable.

discover_data_dirs itself is unchanged.

Verification

  • New unit tests: hourly scoping (in-range + margin only; a far-history
    bucket is never touched) and daily scoping.
  • 10 cps × 60 s load, 600 calls: 600/600 recordings, answered-missing-wav 0
    — correctness intact.
  • Device reads during load (cgroup io.stat): 22.4 MB/s / 132 rIOPS →
    2.7 MB/s / 29 rIOPS (~8×)
    . Write path untouched (~211 wIOPS).

Every recording/flow query walked ALL historical buckets — one fresh
sqlite connection plus one (almost always empty) SELECT per bucket —
so per-query read IO grew linearly with retention (13 buckets = ~77
SELECTs per recording at 3 query types).

Queries now enumerate only buckets overlapping the requested window
widened by one bucket on each side (absorbs flush lag and writer/
querier clock skew), keeping per-query read IO constant. The legacy
whole-tree scan stays available via StorageManager::with_scan_out_
of_range(true) for replayed or mis-bucketed data; diagnostics use it,
as does the existing cross-day-bucket test.
@yeoleobun
yeoleobun merged commit 7b74214 into restsend:main Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants