Skip to content

refactor(cache): kind-based cache store; migrate all caches - #20899

Draft
bohutang wants to merge 42 commits into
mainfrom
refactor/cache-infra
Draft

bohutang wants to merge 42 commits into
mainfrom
refactor/cache-infra

Conversation

@bohutang

@bohutang bohutang commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

I hereby agree to the terms of the CLA available at: https://docs.databend.com/dev/policies/cla/

Summary

Refactors the cache subsystem onto one kind-based infrastructure, migrates every cache onto it, and adds a shared-memory backend behind cache.shared_memory_enabled (off by default). The default backend keeps main's capacity rules and accounting.

Why. Each of the 24 CacheManager slots is its own LRU whose get takes a write lock; capacities are per cache and can sum past node memory; memory pressure never reclaims cache; IO and caching are mixed in one reader.

Changes

  • Infrastructure (common/cache, storages/common/cache): CacheKind registry (23 kinds, 3 pools, legacy names kept), MemCache<T> (single-flight shared per kind, read-only view, optional disk tier), Reader (IO + decode only), CacheRef (values held by readers stay charged), mem_bytes for every cached type.
  • LegacyStore (default): one LRU per kind with main's capacity rules and byte formulas.
  • SharedStore (shared_memory_enabled): three S3-FIFO pools (meta / index / data, 10 / 40 / 10 % of max_server_memory_usage), queues in a linked slab, ghost bounded by 0.9× the pool cap. Values are cached on first read; a kind with [cache.kind_max_bytes] evicts its own coldest entries. Under memory pressure, caches shrink before a query is killed, also for allocations outside queries. Requires max_memory_limit_enabled and max_server_memory_usage, and refuses to start without them.
  • Every cache migrated; removed the old slots and getters, HybridCache, CacheReader / Loader, the per-cache type aliases and the per-block BlockMeta cache.
  • CI: standalone_shared_cache runs base, query and standalone sqllogictests on the shared backend.

Known differences vs main (default backend)

  • memory_cache_block_meta is gone: set_cache_capacity on it errors; RowFetch and replace into reuse segment_block_metas.
  • system.caches reports real sizes for snapshots, and for byte-bounded caches counts values evicted but still held; a cache set to 0 at runtime has no row.
  • Concurrent misses on one key read storage once; every cache's capacity can be set by name.

Performance (db0: 16 vCPU aarch64, 30 GiB, release, local fs; shared backend vs main)

workload main cold / hot (s) shared cold / hot (s) hot geomean vs main
TPC-DS SF100, 8 GiB limit 378.5 / 271.8 378.1 / 283.0 * 1.004
TPC-DS SF100, 16 GiB, disk tiers on 299.8 / 249.5 297.2 / 246.3 0.996
TPC-H SF10, 256 MiB column data cache 13.03 / 5.12 12.56 / 5.08 0.995

* Q78 sits at the 8 GiB limit and succeeded once on each side (22 s vs 32 s); the other queries are within 0.5 %.

Hit path per lookup, 10k keys: 1 thread 194 ns (main) / 228 ns (legacy) / 304 ns (shared); 64 threads 48.1 µs / 55.3 µs / 2.39 µs.

Correctness and stability (db0)

  • Results identical to main: TPC-H 23/23; TPC-DS 99/99 for shared at 8 GiB and for main and shared with disk tiers; hits 43/43 (8 queries whose LIMIT cuts through ties compared with all columns as tie-breakers).
  • Long runs, shared, 16 GiB, 8 clients, 20 min: TPC-DS 284 queries ok, 39 over the memory limit; hits 0 failures. RSS flat over the second half (15 / 16 GiB), server up, no panics other than the memory-limit ones main also has.
  • 30-minute soak of the shared store under random caps, limits, held values and reclaims: no accounting or bound violation, 2 MiB left after.

Tests: unit and integration tests for both backends over all 23 kinds, S3-FIFO against a reference model, kind turnover under 16 threads, scan resistance of a shared pool, reclaim / scrape / traffic deadlock tests, the soak; storages::fuse suites; sqllogictests base, query, no_table_meta_cache, standalone (both backends).

Not in this PR: turning the shared backend on by default; system.caches columns for pools and eviction counters (Prometheus metrics for now).

Tests

  • Unit Test
  • Logic Test
  • Benchmark Test
  • No Test - Explain why

Type of change

  • Bug Fix (non-breaking change which fixes an issue)
  • New Feature (non-breaking change which adds functionality)
  • Breaking Change (fix or feature that could cause existing functionality not to work as expected)
  • Documentation Update
  • Refactoring
  • Performance Improvement
  • Other (please describe):

AI assistance

  • AI usage: An AI coding agent drafted the design, implementation, tests and benches under the author's direction; the author reviewed the design decisions and directed the scope and validation
  • Responsible human: @bohutang
  • The responsible human has read every line of this diff and can explain each change

This change is Reviewable

Infrastructure for the cache refactor, plus the first family migrated.

- `common/cache`: S3-FIFO with ghost queue.
- `storages/common/cache`: `CacheKind` (23 kinds, 3 pools), `CachedItem`,
  `CacheRef`/`Reservation`, `MemCache` (single-flight, read-only view,
  optional disk tier), `KindStore` with `LegacyStore` (pre-refactor
  capacity rules) and `SharedStore` (pooled S3-FIFO), `CacheManager` with
  `system.caches` rows and `set_capacity_by_name`.
- `mem_bytes` for all cached types; the nine that reported 0 now report
  real sizes, verified against a counting allocator.
- Snapshot family moves to `FuseCaches::snapshots` + `SnapshotReader` /
  `SnapshotStatsReader`. The old `CacheManager` builds the new manager for
  migrated kinds and routes `set_cache_capacity` and `system.caches` to it.
- Benches: hit path (current vs legacy vs shared backend), pressure shrink,
  S3-FIFO vs LRU hit rate.
@github-actions github-actions Bot added the pr-refactor this PR changes the code base without new features or bugfix label Oct 30, 2026
@bohutang bohutang added the ci-benchmark-cloud Benchmark: run only cloud tests for tpch/hits label Oct 30, 2026
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

⏱ CI timing · Dev

run 41219395591 · attempt 2 (wall time includes re-run gap) · 58.6 min (main p50 63.3 min, −4.6 min)

stage this run main p50 Δ
total 58.6 min 63.3 min −4.6 min
build (aarch64, ci profile) 29.9 min 23.4 min +6.5 min ⚠️
├ deps + crates (parallel) 13.1 min — —
├ databend-query codegen 3.0 min — —
├ databend-binaries link 11.8 min — —
└ sccache hit rate 97.2 % — —
test_unit 17.4 min 17.6 min −0.2 min
mac_check — 13.1 min —
tests after build (52 jobs) 28.3 min 30.4 min −2.1 min
├ queue p50 / max 1.6 min / 19.4 min 4.9 min / 26.9 min −3.3 min
├ sqllogic standalone (8 jobs, max) 4.5 min 4.3 min +0.3 min
├ sqllogic cluster (8 jobs, max) 7.1 min 7.2 min −0.1 min
├ sqllogic stage (2 jobs, max) 5.3 min 5.0 min +0.3 min
├ sqllogic other (13 jobs, max) 5.8 min 5.7 min +0.1 min
├ stateless (2 jobs, max) 6.5 min 6.7 min −0.2 min
├ stateful (4 jobs, max) 4.7 min 4.8 min −0.1 min
├ compat (6 jobs, max) 5.9 min 5.7 min +0.3 min
├ meta / logs / lineage / udf (5 jobs, max) 4.7 min 4.4 min +0.2 min
├ ee (2 jobs, max) 1.9 min 1.3 min +0.6 min
├ private_tasks (1 jobs, max) 8.8 min 8.8 min −0.0 min
└ fuzz (1 jobs, max) 1.4 min 1.1 min +0.3 min

Minutes unless stated; durations exclude queue time unless stated; "max" = longest job in the group. Baseline = median of the last 10 successful runs (Dev: Main on main; Cloud: recent Cloud runs). Build breakdown has no baseline. Updated on each push.

@github-actions

Copy link
Copy Markdown
Contributor

Docker Image for PR

  • tag: pr-20899-8b24e74-1793336950

note: this image tag is only available for internal use.

Move segments, column-oriented segments, segment block metas and segment
statistics onto the kind-based cache, still on the legacy capacity rules.

- `SegmentCache` in `FuseCaches` + `SegmentInfoReader` / `SegmentStatsReader`
  replace `MetaReaders::segment_*`; call sites hold the cache and reader.
- The two duplicated `extract_block_metas` paths share `SegmentCache::block_metas`.
- RowFetch and replace into look up `segment_block_metas` before decoding a
  segment; the per-block `BlockMeta` cache is removed (disabled by default,
  `block_meta_count` no longer has an effect).
- The old `CacheManager` drops these slots; `set_cache_capacity` and
  `system.caches` for them are served by the new manager.
@bohutang bohutang changed the title refactor(cache): kind-based cache store and snapshot cache migration refactor(cache): kind-based cache store; migrate snapshot and segment caches Oct 30, 2026
@bohutang bohutang added ci-benchmark-cloud Benchmark: run only cloud tests for tpch/hits and removed ci-benchmark-cloud Benchmark: run only cloud tests for tpch/hits labels Oct 30, 2026
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

⏱ CI timing · Cloud (PR image)

run 41009288689 · to image 35.4 min (recent p50 48.4 min, −13.0 min)

stage this run recent p50 Δ
build aarch64 (release) 26.9 min 44.1 min −17.2 min
├ deps + crates (parallel) 11.1 min — —
├ databend-query codegen 4.5 min — —
├ databend-binaries link 8.6 min — —
└ sccache hit rate 98.6 % — —
build x86_64 (release) 28.9 min 37.8 min −8.9 min
├ deps + crates (parallel) 10.8 min — —
├ databend-query codegen 3.4 min — —
├ databend-binaries link 12.2 min — —
└ sccache hit rate 98.6 % — —
docker (multi-arch) 2.9 min 2.8 min +0.1 min

Minutes unless stated; durations exclude queue time unless stated; "max" = longest job in the group. Baseline = median of the last 10 successful runs (Dev: Main on main; Cloud: recent Cloud runs). Build breakdown has no baseline. Updated on each push.

@github-actions

Copy link
Copy Markdown
Contributor

Docker Image for PR

  • tag: pr-20899-32f2f95-1793340507

note: this image tag is only available for internal use.

…e and prune result caches

Move BloomIndexMeta, BloomFilter, VectorIndexMeta, SpatialIndexMeta,
VirtualColumnMeta, SpatialIndex, VectorRefine and PruneResult to the
kind-based store. IndexMetaReader<T> replaces MetaReaders. Disk tiers
keep their directories and sizes; the license toggle also hides them
from system.caches.
@bohutang bohutang changed the title refactor(cache): kind-based cache store; migrate snapshot and segment caches refactor(cache): kind-based cache store; migrate snapshot, segment and index meta caches Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

…g caches

Move the remaining kinds to the kind-based store: inverted index
meta / lookup / payload / full payload, decoded HNSW indexes, column
data and arrays, parquet file metas and iceberg tables. The old
CacheManager now only assembles the store and the disk tiers;
HybridCache, the CacheReader / Loader readers, CachedObject and
VectorIndexCache are removed. A kind with its memory tier off keeps
using its disk tier, as the hybrid caches did.
@bohutang bohutang changed the title refactor(cache): kind-based cache store; migrate snapshot, segment and index meta caches refactor(cache): kind-based cache store; migrate all caches Oct 30, 2026
Add shared_memory_enabled (off by default) and the meta / index / data
pool ratios. With it on, caches live in three S3-FIFO pools sized by
ratio of the memory limit, the legacy capacity options are ignored with
a warning, and set_cache_capacity accepts pool names.

QueriesMemoryManager::set_reclaim_handle lets a waiting allocation
shrink caches before a query is killed when the global limit is
exceeded. It is registered only for the shared backend, so the legacy
backend behaves as before.

CI runs the base, query and standalone sqllogictests on the shared
backend.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-31T16:25:26.934733Z 288e228 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83daa30fd8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/common/cache/src/s3fifo.rs Resolved
# Conflicts:
#	src/query/storages/fuse/src/operations/read/parquet_rows_fetcher.rs
…tale queue positions

- LegacyStore: replacing an entry counts the old value as room, so growing
  the least recently used entry stays within the byte capacity; a value
  larger than the whole capacity is not cached.
- SingleFlight: a cancelled load with no other waiter removes its entry.
- S3Fifo: removals compact stale queue positions once they outnumber live
  entries.
- Vector index reader tests write separate files, so cached index metas of
  one test are not read by the other.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9a22b8fbdc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…ntries

- SharedStore reserves the kind limit and the pool capacity with
  compare-and-swap before inserting, and gives them back when the insert
  is rejected, so concurrent fills cannot together exceed either.
- TTLReader: a background refresh replaces the entry with put; if the
  cache rejects the new value, or the refresh fails, the stale entry is
  dropped so a later read reloads it instead of serving it forever.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9c60d9c5f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…rejected victims

- LegacyStore charges an admitted value's reservation before releasing
  the kind's LRU lock, so concurrent VectorIndex inserts, which check
  resident bytes under that lock, cannot both pass the check.
- Entries evicted by an insert that is then rejected are accounted for,
  instead of staying in the item and in-cache counters.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54fdbbbbfd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff6de590de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +229 to +231
shard.pop_evictable(Entry::is_shared, CHECKS_PER_POP)
else {
break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Continue scanning past a batch of pinned entries

With shared caching enabled, a shard whose main queue starts with 32 pinned entries followed by unpinned entries still stops eviction prematurely: pop_evictable returns None after keeping 32 entries, and this break abandons the remaining entries. If the other shards cannot yield a victim, pressure reclamation stops despite reclaimable memory, and capacity-driven insertion incorrectly returns PoolFullPinned. Fresh evidence beyond the earlier aging-only case: a reproduction using the current S3Fifo with 32 pinned entries followed by one unpinned entry returns None on its first scan and evicts the unpinned entry on its second. Distinguish scan-budget exhaustion from a completed search before abandoning the shard.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9540010: evict_batch no longer abandons a shard after one empty pop_evictable. Kept entries rotate to the back, so it keeps scanning until the checks reach the shard's entry count (a full turn, as quick_cache does), bumping the lock between scans. Test: test_eviction_scans_past_held_entries (96 held entries in front of one free entry, one shard).

- evict_batch gave up a shard once pop_evictable had kept 32 held
  entries, so held entries at the front of a shard hid evictable ones
  behind them: pressure reclamation stopped and inserts were refused as
  PoolFullPinned with memory to free. Kept entries rotate to the back,
  so the shard is now done only once as many checks as it has entries
  came back empty, as quick_cache gives up only after a full turn of
  its ring; the lock is bumped between two scans.
- pop_evictable_where restarted from the queue front for each victim
  and left passed entries in place: a victim cost a scan, and entries
  at the front lost an access on every eviction. evict_where evicts a
  batch in one scan and moves aged and kept entries to the back of the
  main queue, as pop_evictable does; other kinds keep their place.

Tests: test_eviction_scans_past_held_entries, evict_where order,
target, kept entries, clock rotation matching pop_evictable, bounded
queues.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 95400101da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The queues were VecDeques of (key, sequence number): a removal or a
move left a stale position behind, skipped when reached and dropped by
compaction, and a scan over one kind could not take an entry out of
the middle. Entries now live in a slab of slots linked into the small
and the main queue by index, as quick_cache does (foyer uses an
intrusive list): removing or moving an entry is O(1) and leaves
nothing behind, and the key is stored once, found through a HashTable
of slot indexes. Free slots are reused; once they outnumber the
entries the slab is rebuilt in queue order and released, so it stays
within twice the entries plus a constant after the entries shrink.

Eviction order and every result are unchanged. PoolStructure reports
slots instead of queue positions.

Tests, in s3fifo.rs: a plain reference S3-FIFO (a map and two lists of
keys) run against the slab on random gets, inserts, admissions,
removals, pop_evictable with held values and check budgets,
evict_where, remove_if and bulk fills that make the slab compact; each
result, both queues in order, the byte counts and the slab's links,
table and free list are compared after every operation, with
admission on first and on second access. test_compaction_keeps_order
checks the order after a rebuild against the reference.
The ghost of each shard remembered a fixed 16Ki keys whatever the pool
size: for a data pool of large values that is many times the data the
pool holds, so a value read twice within a long window was admitted
and the second-read admission let scans through; for small values it
could be too short. As libCacheSim (ghost-size-ratio 0.9) and foyer
size ghosts in bytes, a pool's ghosts now also stop at 0.9 times its
cap in bytes, split over the shards, and follow the cap when it
changes; a pool turned off forgets them. The record bound stays, so
the ghost's memory does not grow. A shard keeps its newest record even
when larger than its share, so that any admissible value can still be
admitted on a prompt second read.

S3Fifo::admission takes the value's bytes; PoolStructure reports ghost
bytes and their bound.

Tests: ghost byte bound, oldest dropped first, newest kept when large,
size updates and bound changes, byte count against the live records
on random operations; pool ghosts following the cap and a large value
admitted on its second read; the model comparison in s3fifo.rs now
runs with a byte-bounded ghost and compares ghost counts and bytes;
the soak checks ghost bytes against the bound.
16 threads fill a kind limited to 50 values: it never passes its
limit, refuses nothing, and every value but the last 50 is counted as
evicted for capacity.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 288ff3ca92

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

break;
}
// Let readers in between two scans of a shard full of held entries.
RwLockWriteGuard::bump(&mut shard);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep nonblocking reclamation from waiting in bump

With shared caching enabled, scanning more than 32 pinned entries can reach this call during pressure reclamation, even when blocking is false. RwLockWriteGuard::bump yields the lock and then reacquires it with a blocking write. A queued cache writer can acquire the yielded shard, allocate during insertion, and enter QueriesMemoryManager::reclaim, where it waits on the reclaiming mutex held by the original reclaimer. The reclaimer then waits for that writer's shard lock inside bump, permanently deadlocking both threads under memory pressure. Avoid bump on the nonblocking path, or release the guard and reacquire only with try_write.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 10dbeee: evict_batch bumps the shard lock only when blocking. Non-blocking eviction (reclaim) takes each shard with try_write once and keeps it until its scan is done, so it never waits for a shard lock. No deterministic test: the hang needs a writer to take the yielded lock and allocate inside it; the property is stated on evict_batch.

Scanning a shard past held entries bumped its lock between two scans,
also when reclaiming. bump takes the lock back by waiting, and the
writer it let in could allocate, enter reclaim and wait for the
reclaim in progress: both threads hung. Only blocking eviction bumps
now; reclaim tries each shard once and keeps it until its scan ends.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 10dbeee2ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

… memory limit

- Data on first read. The data pool cached a column value only on its
  second read; caching data once read matters more than filtering
  scans out, which probation in the small queue still does. Every pool
  now admits on first read, the ghost still sends a key evicted from
  probation and read again to the main queue. Deferral is gone with it:
  InsertMode, InsertOutcome::Deferred, PoolStore::admission and the
  cache_admission_deferred_count metric.
- Loads shared per kind. A MemCache made its own single-flight, so the
  parquet meta, parquet column data and iceberg table caches, which
  make a handle per call, loaded one key once per concurrent caller.
  CacheManager keeps one single-flight per kind for every handle.
- A memory limit for the shared backend. Its pools were sized from 80%
  of system memory when max_server_memory_usage was 0, and only
  reclaimed when an enforced limit was reached, which
  max_memory_limit_enabled (off by default) decides: up to 48% of
  memory as cache that nothing would shrink. Startup now fails unless
  both are set; the legacy backend is unchanged. CI's
  standalone_shared_cache job sets them to 80% of the runner memory.
- Reclaim outside queries. An allocation over the global limit on a
  thread outside any query (background work, cache fills) was let
  through without reclaiming; it now reclaims cache memory first, and
  is still never refused. Without a reclaim handler (legacy) nothing
  is done, not even flushing the stat buffers. An empty pool is not
  scanned by a reclaim.

Tests: every kind through both backends loads once for concurrent
misses through two handles (test_handles_share_loads) and is cached on
its first read; an allocation outside queries over the limit reclaims,
under it or without a handler does not
(test_allocation_outside_queries_reclaims); the shared backend refuses
a config without a memory limit, the legacy one accepts it; a hot set
of vector indexes keeps a hit rate of at least 0.95 while bloom
filters scan a quarter of their shared pool every round
(test_hot_index_survives_scans_of_another_kind); the pool's ghost
bytes follow its cap with first-read admission.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 1453dd6ff5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Every pool caches a value on its first read since 1453dd6, so the
admit_after knob of S3Fifo was always 1 and nothing called admit or
admission any more. Removed: admit_after, Admission, Placement, admit,
admission and insert_at; insert is the one way in, sending a key the
ghost remembers to the main queue as before. The ghost no longer keeps
access counts, only which keys were evicted from probation and their
bytes. Behavior is unchanged.

Tests: the reference model in s3fifo.rs drops admission too and runs
with the default and with a byte-bounded ghost; the it tests use
insert.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 3d9db9baae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

09_0043 expected one row from system.caches for
memory_cache_parquet_meta_data; in a cluster there is one per node,
and only the nodes that read the stage files cache their footers, so
cluster (base, 2c, 2) got 0, 0, 1. Sum hits and items over the nodes.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: f7741b9ccd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- S3-FIFO allocates before it mutates; removals and evictions do not
  allocate, and the ghost drops a record rather than fail. Per-class
  lists let a kind evict or be removed without walking other kinds.
- Budgets are RAII charges taken one at a time; in-cache counts come
  from the queues (shared) or the LRU (legacy) instead of atomics.
- Memory-pressure reclaim looks at a bounded number of entries over all
  pools, skips busy shards, never compacts, and no longer serializes
  reclaiming threads behind a mutex.
- A per-pool bounds lock orders inserts against cap and limit changes;
  kind limits are enforced on resident bytes.
- Loads are shared only for kinds whose files are written once
  (CacheDef::SHARE_LOADS); a joined flight checks memory first.
- Pruning results size parts by PartInfo::mem_bytes; iceberg values
  count their engine options.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3d1831be3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +233 to +237
let state = self.kind(kind);
let capacity = *state.capacity.lock();
if capacity == LegacyCapacity::Disabled {
return InsertOutcome::Rejected(RejectReason::Disabled);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize legacy capacity changes with admission

On the default legacy backend, a concurrent capacity reduction can be bypassed because insertion snapshots capacity before acquiring the LRU lock. An insertion can read the old nonzero capacity, pause while set_limit updates the capacity and finishes evicting entries, and then insert using the old budget. This also allows an entry to remain resident after the cache has been disabled, since subsequent fills are rejected without removing it. Previously, InMemoryLruCache changed capacity under the same lock used for insertion. Protect the capacity check and publication with the same synchronization as resizing so an insertion cannot publish against bounds that have already been replaced.

Useful? React with 👍 / 👎.

An insert read the capacity before locking the LRU, so it could insert
against a capacity that set_limit had already lowered and evicted down
to, leaving an entry in a disabled kind. set_limit now changes the
capacity under the LRU lock and inserts read it there.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 284767177e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

format! grows its string by doubling, so the sample's JSON allocation
was about twice its length; the estimate counts the allocation and
passed the test's bound. Shrink the sample to its length.
@bohutang

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 288e228aa1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-benchmark-cloud Benchmark: run only cloud tests for tpch/hits pr-refactor this PR changes the code base without new features or bugfix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant