Skip to content

feat(storage): add split range disk cache - #6781

Open
congx4 wants to merge 9 commits into
quickwit-oss:mainfrom
congx4:congxie/finalDiskCache
Open

congx4 wants to merge 9 commits into
quickwit-oss:mainfrom
congx4:congxie/finalDiskCache

Conversation

@congx4

@congx4 congx4 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a instance-level Foyer cache for split footer and body byte ranges including all kinds of data types(Fast fields, term dict, posting list, etc)
  • configure cache capacity, admission, recovery, compression, reclaiming, and write throttling
  • integrate the cache into searcher startup, shutdown, footer reads, and body reads
  • export cache request, admission, and Foyer device metrics
  • disable the long-lived fast-field RAM cache by default when Foyer is configured

Test plan

  • make fmt
  • cargo test -p quickwit-config --lib node_config::tests::test_split_range_disk_cache -- --nocapture
  • cargo test -p quickwit-storage --all-features --lib split_range_cache -- --nocapture
  • cargo test -p quickwit-search --all-features --lib split_range_cache_layer -- --nocapture
  • cargo clippy -p quickwit-config -p quickwit-storage -p quickwit-search --all-features --tests -- -D warnings

Made with Cursor

congx4 and others added 5 commits September 8, 2026 10:27
Based on quickwit-oss#6700. Pin Foyer and add the disabled-by-default cache configuration, including clean-block and write-throughput defaults.

Co-authored-by: Cursor <cursoragent@cursor.com>
Based on quickwit-oss#6701. Encode cache keys portably while rejecting only I/O failures and non-UTF-8 URIs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Based on quickwit-oss#6702. Build and recover the process-wide cache, flush write-on-eviction entries on close, and apply configured reclaim and write throttling.

Co-authored-by: Cursor <cursoragent@cursor.com>
Based on quickwit-oss#6707. Add the storage decorator, manage cache lifecycle in searchers, and route footer and body range reads through Foyer without changing whole-split cache behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Based on quickwit-oss#6709. Export cache metrics, document production sizing, and disable the long-lived fast-field RAM cache by default when Foyer is configured.

Co-authored-by: Cursor <cursoragent@cursor.com>
@congx4
congx4 requested a review from a team as a code owner September 8, 2026 14:45
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 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-09-16T20:28:59.031340Z 81cf59d New commits
ℹ️ 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: 053cb917b3

ℹ️ 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 +61 to +62
let uri_len = read_u64(reader)? as usize;
let mut uri_bytes = vec![0; uri_len];

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 Reject oversized URI lengths before allocating

When recovering a truncated or corrupted cache image, the first eight key bytes can decode to an arbitrarily large u64; casting that value and immediately allocating vec![0; uri_len] can exhaust memory or abort the process instead of returning a Foyer decode error so recovery can reject the entry. Validate the conversion and bound the URI length against the encoded entry size before allocating.

AGENTS.md reference: AGENTS.md:L21-L22

Useful? React with 👍 / 👎.

Comment on lines +171 to +173
_buckets: Vec<f64>,
) -> BoxedHistogramVec {
self.register_histogram_vec(name, desc, label_names)

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 Preserve Foyer's explicit histogram buckets

When Foyer registers a histogram through register_histogram_vec_with_buckets, this implementation discards the supplied boundaries and creates an ordinary recorder histogram instead. The Prometheus exporter will consequently aggregate these observations using its default buckets rather than Foyer's requested buckets, making the resulting latency and operation distributions inaccurate; propagate the boundaries into the recorder configuration rather than ignoring them.

Useful? React with 👍 / 👎.

Comment thread config/quickwit.yaml
# compression: lz4
# recover_mode: quiet
# block_size: 64M
# max_entry_size: 60M

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

a full float field will use 8byte per element

We target 10M docs, but we overshoot sometimes, so we may have 12M docs.
So the max size is closer to 100MB

what happens if the size is larger than max_entry_size?

Copy link
Copy Markdown
Contributor Author

Comment thread docs/configuration/node-config.md Outdated
Comment thread docs/configuration/node-config.md Outdated
/// Disabled-by-default searcher disk cache for exact split byte ranges.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SplitRangeDiskCacheConfig {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would avoid putting to many foyer specific parameters in the config. We can have defaults and override them via env parameters instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sounds good, once I verify some settings, I will remove them from the config.

fn default() -> Self {
SearcherConfig {
fast_field_cache: CacheConfig::default_with_capacity(ByteSize::gb(1)),
fast_field_cache: None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why did you change the default here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So the whole logic for changing the fast field cache is that we want to keep the existing fast field configuration when there is no disk cache defined. But when disk cache is defined, we want to remove fast field.
so the fast field cache looks like below after the changes:
omitted: disable it when Foyer is configured
explicitly configured: preserve the user’s value
omitted without Foyer: retain the effective 1 GB default

Comment thread quickwit/quickwit-config/src/node_config/mod.rs Outdated
Comment thread quickwit/quickwit-search/src/leaf.rs Outdated
Comment thread quickwit/quickwit-search/src/leaf.rs
Comment thread quickwit/quickwit-search/src/service.rs Outdated
Comment thread quickwit/quickwit-search/src/service.rs Outdated
Comment thread quickwit/quickwit-serve/src/lib.rs
Comment thread quickwit/quickwit-storage/src/split_range_cache/key.rs Outdated
Comment thread quickwit/quickwit-storage/src/split_range_cache/mod.rs Outdated
Clarify cache naming and documentation, centralize zero-capacity handling in QuickwitCache, and document the manual key codec accurately.

@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: 47b0b6743e

ℹ️ 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 +175 to +176
#[async_trait]
impl Storage for FoyerSplitRangeStorage {

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 Delegate object listing to the wrapped storage

When a caller invokes list on the returned Arc<dyn Storage>, this implementation inherits Storage::list's default unsupported-operation error instead of listing through inner. Since this public read-only decorator otherwise forwards non-cached reads and metadata operations, wrapping a storage that supports listing unexpectedly removes that capability; add a list implementation that delegates to self.inner.list(prefix).

Useful? React with 👍 / 👎.

Comment on lines +170 to +174
let storage_with_split_range_cache: Arc<dyn Storage> = match &searcher_context
.split_range_disk_cache_opt
{
Some(cache) => wrap_storage_with_split_range_cache(cache.clone(), index_storage.clone()),
None => index_storage.clone(),

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 Apply the storage timeout outside cache reads

When both storage_timeout_policy and the split-range disk cache are enabled, configure_storage_retries wraps only index_storage, and this newly added outer wrapper performs Foyer lookups before reaching it. A stalled Foyer disk read therefore bypasses the configured per-read timeout and retries, so searches wait until the broader leaf-request timeout instead of failing open to remote storage at the configured deadline; enforce the timeout around the cache lookup as well.

Useful? React with 👍 / 👎.

@congx4
congx4 force-pushed the congxie/finalDiskCache branch from 25063ec to df68cb7 Compare September 14, 2026 20:42
@congx4
congx4 requested a review from PSeitz September 16, 2026 14:20
Comment thread quickwit/quickwit-storage/src/split_range_cache/metrics.rs Outdated
subsystem: "storage",
);

fn foyer_histogram_buckets(name: &str) -> Option<Vec<f64>> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't understand this at all. Why use a string as the key?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

str here is just the metric name. Foyer’s RegistryOps API provides only: name: Cow<'static, str>. Therefore, we must inspect a string to identify the histogram. We could use some helper functions to replace this but we couldn't completely eliminate the string.

Comment thread quickwit/quickwit-storage/src/split_range_cache/metrics.rs Outdated
Comment thread quickwit/quickwit-storage/src/split_range_cache/metrics.rs Outdated
Comment thread quickwit/quickwit-storage/src/split_range_cache/storage.rs Outdated
Comment thread quickwit/quickwit-storage/src/split_range_cache/storage.rs Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>

@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: 4f94f458af

ℹ️ 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 quickwit/quickwit-storage/src/cache/quickwit_cache.rs Outdated
Comment thread quickwit/quickwit-storage/src/split_range_cache/storage.rs

@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

https://github.com/quickwit-oss/quickwit/blob/81cf59d054cf151bed135668cbeb77d2039f6ebc/quickwit-storage/src/split_range_cache/storage.rs#L69-L70
P2 Badge Reject entries whose encoded form exceeds the block

When max_entry_size is configured close to block_size, an incompressible payload can pass this value-length check while its encoded Foyer entry cannot fit: Foyer 0.22.3 also reserves a 4 KiB blob index and page-aligns the header, key, and value. Such entries are sent to the disk tier but rejected there, causing fail-open reads or repeated remote misses despite being advertised as cacheable; account for the full encoded size and mark non-fitting values InMem, and document this format constraint.

AGENTS.md reference: AGENTS.md:L126-L126

ℹ️ 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 config/quickwit.yaml
# disk_capacity: 1500G
# memory_capacity: 15G
# memory_eviction_policy: s3-fifo
# compression: lz4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this effective as default for this kind of data? I would assume we will mostly store data that has already some kind of compression and we will just waste CPU for very small size gains.

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.

4 participants