Skip to content

Use seekable Sieve for frame caching#39

Merged
jtarchie merged 7 commits into
jtarchie:mainfrom
SaveTheRbtz:use-seekable-sieve-frame-cache
Jun 5, 2026
Merged

Use seekable Sieve for frame caching#39
jtarchie merged 7 commits into
jtarchie:mainfrom
SaveTheRbtz:use-seekable-sieve-frame-cache

Conversation

@SaveTheRbtz

@SaveTheRbtz SaveTheRbtz commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

This switches sqlitezstd to the Reader-owned decoded-frame cache added in zstd-seekable-format-go v0.10.0. The configured policy is framecache.NewSieve, and the existing WithFrameCacheSize option continues to control the number of decoded frames cached per opened file.

The important design choice is that sqlitezstd now has one persistent frame cache, not several partially overlapping caches. The cache lives at the seekable-reader layer, is keyed by seek-table frame IDs, and stores decoded frame data directly. That avoids hashing compressed payloads, duplicated frame-cache lifetimes, and confusing memory accounting where one frame-cache knob could imply multiple resident caches.

Cache Model

Before this PR, an opened compressed database could keep separate frame-level state in sqlitezstd:

  • a decoded-frame LRU wrapper around the zstd decoder;
  • a compressed-frame LRU in frameReader, keyed by compressed offset;
  • an optional HTTP byte/page cache for remote reads.

After this PR, ZstdVFS.open passes framecache.NewSieve(framecache.Limits{MaxFrames: frameCacheSize}) to seekable.NewReader via seekable.WithReaderFrameCache. The local frameReader is reduced to the positional io.ReaderAt/io.ReadSeeker adapter needed by the seekable reader, and the process-wide shared decoder remains unchanged.

This makes WithFrameCacheSize(64) mean one thing: cache up to 64 decoded zstd frames for that opened file.

API And HTTP Path

WithFrameCacheSize remains the public cache knob.

This intentionally removes the HTTP byte-cache API added on the upgrade branch: WithHTTPCacheSize, WithHTTPPageSize, and DefaultHTTPPageSize. The HTTP path now uses the range reader directly and relies on the decoded-frame Sieve cache as the only persistent cache layer.

Benchmarks

Fresh run after rebasing onto main; values are median ± relative MAD across 5 runs. Lower is better; the better raw value in each before/after pair is bolded.

Benchmark CPU before (ns/op) CPU after (ns/op) CPU change Mem before (B/op) Mem after (B/op) Mem change Allocs before (allocs/op) Allocs after (allocs/op)
Point read 9,965 ±2.0% 9,222 ±1.2% -7.5% 18,061 ±1.9% 19,286 ±1.4% +6.8% 15 ±0.0% 15 ±0.0%
HTTP point read 11,006 ±5.3% 10,682 ±1.3% -2.9% 11,719 ±4.0% 12,930 ±2.6% +10.3% 17 ±0.0% 17 ±0.0%
RTree 13,265 ±4.6% 13,074 ±1.2% -1.4% 17,188 ±5.3% 18,400 ±1.2% +7.1% 13 ±0.0% 13 ±0.0%
FTS5 3,300,782 ±0.6% 3,169,517 ±0.3% -4.0% 159,157 ±3.5% 140,199 ±0.9% -11.9% 24 ±0.0% 20 ±0.0%
FTS5, min cache 3,768,040 ±5.8% 3,154,699 ±0.2% -16.3% 185,281 ±3.8% 147,282 ±0.3% -20.5% 26 ±3.8% 21 ±0.0%

The strongest signal is the compressed FTS5/min-cache path, where moving to the seekable Sieve cache reduces both CPU and allocation pressure. The small point-read deltas should be read cautiously because the uncompressed controls also moved between runs.

The removed HTTP-cache benchmark existed only for the old second cache layer. Its baseline median was 10,420 ns/op, 18,963 B/op, and 17 allocs/op; the regular HTTP compressed path after this PR is 10,682 ns/op, 12,930 B/op, and 17 allocs/op, without a separate resident HTTP cache.

Testing

  • env GOCACHE=/tmp/codex-go-build-cache go test ./...
  • env GOCACHE=/tmp/codex-go-build-cache go test -tags fts5
  • env GOWORK=off TMPDIR=/home/rbtz/tmp/bench/... GOCACHE=/home/rbtz/tmp/bench/go-build-cache GOMODCACHE=/tmp/codex-gomodcache GODEBUG=randautoseed=0 GOMAXPROCS=8 go test -tags fts5 -run '^$' -bench '^BenchmarkRead' -benchmem -count=5 -timeout=90m

@SaveTheRbtz

Copy link
Copy Markdown
Contributor Author

(Also note that the seekable zstd cache also supports byte-size limits which you can also utilize if you want)

@jtarchie jtarchie merged commit 568da00 into jtarchie:main Jun 5, 2026
1 check passed
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