Skip to content

feat: On disk cache for split footers#78

Merged
Darkheir merged 6 commits into
sekoiafrom
feat/split_footer_on_disk_cache
Jul 10, 2026
Merged

feat: On disk cache for split footers#78
Darkheir merged 6 commits into
sekoiafrom
feat/split_footer_on_disk_cache

Conversation

@Darkheir

@Darkheir Darkheir commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Description

Describe the proposed changes made in this PR.

How was this PR tested?

Describe how you tested this PR.

Copilot AI review requested due to automatic review settings July 8, 2026 15:27

Copilot AI 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.

Pull request overview

This PR introduces an optional persistent (on-disk) cache layer for split footers, backing the existing in-memory “hotcache” footer cache so footers can survive searcher restarts and avoid refetching from object storage.

Changes:

  • Add a disk-backed size-bounded cache (DiskSizedCache) and a two-tier cache wrapper (TieredSizedCache) to combine memory + optional disk tiers.
  • Wire the new disk footer cache into SearcherContext construction and searcher startup via a new split_footer_disk_cache_capacity config option.
  • Extend metrics and documentation/config examples to reflect the new persistent cache.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
quickwit/quickwit-storage/src/metrics.rs Adds metrics bucket for the new disk footer cache.
quickwit/quickwit-storage/src/lib.rs Re-exports new cache types for downstream crates.
quickwit/quickwit-storage/src/cache/mod.rs Registers and re-exports the new cache modules.
quickwit/quickwit-storage/src/cache/disk_sized_cache.rs Implements the on-disk, LRU, size-bounded cache with tests.
quickwit/quickwit-storage/src/cache/tiered_sized_cache.rs Implements the memory+disk tiered cache wrapper with tests.
quickwit/quickwit-config/src/node_config/mod.rs Adds split_footer_disk_cache_capacity to SearcherConfig.
quickwit/quickwit-config/src/node_config/serialize.rs Updates config serialization tests/defaults for new field.
quickwit/quickwit-serve/src/lib.rs Opens disk cache under data dir and passes it into SearcherContext.
quickwit/quickwit-search/src/service.rs Updates SearcherContext to use TieredSizedCache and new constructor arg.
quickwit/quickwit-search/src/leaf.rs Switches footer cache parameter type to the tiered cache.
quickwit/quickwit-search/src/lib.rs Updates context construction to new SearcherContext::new signature.
quickwit/quickwit-search/src/tests.rs Updates tests to new SearcherContext::new signature.
docs/overview/concepts/querying.md Documents the new split footer disk cache conceptually.
docs/configuration/node-config.md Documents the new searcher configuration key.
config/quickwit.yaml Adds commented example for enabling the new setting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread quickwit/quickwit-search/src/service.rs

/// Returns the cached payload for the given key, if present on disk.
pub fn get(&self, key: &K) -> Option<OwnedBytes> {
let file_name = key.to_string();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We are not concerned by this issue in the current PR. Should I still add this defensive check in case the cache is re-used later ?

/// recency is refreshed.
pub fn put(&self, key: K, bytes: OwnedBytes) {
let num_bytes = bytes.len() as u64;
let file_name = key.to_string();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We are not concerned by this issue in the current PR. Should I still add this defensive check in case the cache is re-used later ?

Comment thread docs/overview/concepts/querying.md Outdated
Comment thread quickwit/quickwit-search/src/leaf.rs Outdated
@Darkheir
Darkheir force-pushed the feat/split_footer_on_disk_cache branch from c6e7050 to 7440c8f Compare July 8, 2026 15:35
Signed-off-by: Darkheir <raphael.cohen@sekoia.io>
@Darkheir
Darkheir force-pushed the feat/split_footer_on_disk_cache branch from 7440c8f to ecd01e9 Compare July 8, 2026 15:38
Signed-off-by: Darkheir <raphael.cohen@sekoia.io>

Copilot AI 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.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Comment thread quickwit/quickwit-storage/src/cache/disk_sized_cache.rs
Comment thread quickwit/quickwit-storage/src/cache/disk_sized_cache.rs
Comment thread quickwit/quickwit-storage/src/cache/tiered_sized_cache.rs
Comment on lines +110 to +112
for dir_entry_res in std::fs::read_dir(&root_path)? {
let dir_entry = dir_entry_res?;
let metadata = match dir_entry.metadata() {

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.

This is calling stat on all files. It should be fast on an ssd (tried it locally, 150ms). I do have some concerns on EBS. Also this will also hold up the actor loop (never yelds). I assume it's okay on a fast disk as it's called only once. I think it can have bad side effects if the disk is low.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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.

The test I performed was on 100k files. Actually looking a little bit further back in time, the number of entries in the current cache goes up to 220k per searcher. Given that this cache could be configured bigger and that it will never get wiped until the limit is reached, I think we can expect it to go up to millions.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

To avoid blocking the loop we could make open async and iterate inside spawn_blocking.

Regarding all the metadata calls I'm not sure we should remove them. The alternative of having a single manifest file feels a bit tricky because it adds write overhead and is problematic for crash consistency while the metadata calls are made only on startup

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If we want to avoid making all the stat calls we could store the size of the files in their name so we would have all the required info.

We would also lose the seed order for the LRUCache that is using mtime, but this is flawed anyway because it doesn't track the last time the footer was accessed, only the time it was created.

@Darkheir
Darkheir force-pushed the feat/split_footer_on_disk_cache branch from 6e10f50 to bf2d309 Compare July 8, 2026 16:27
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@Darkheir
Darkheir force-pushed the feat/split_footer_on_disk_cache branch from bf2d309 to aa723c2 Compare July 8, 2026 17:02
Darkheir added 2 commits July 9, 2026 10:08
Signed-off-by: Darkheir <raphael.cohen@sekoia.io>
Signed-off-by: Darkheir <raphael.cohen@sekoia.io>
@Darkheir
Darkheir requested a review from rdettai-sk July 9, 2026 12:44

@rdettai-sk rdettai-sk left a comment

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.

Looks good! The tiered approach is just a bit unfortunate in that it uses 2 locks / 2 states when theoretically only 1 should be enough. But I think contention should remain small as the runtime doesn't have many threads. Also eviction on a two tier state would not be easy.

Comment thread quickwit/quickwit-storage/src/cache/disk_sized_cache.rs Outdated
Comment thread quickwit/quickwit-storage/src/cache/disk_sized_cache.rs
Comment thread quickwit/quickwit-storage/src/cache/disk_sized_cache.rs
Comment thread quickwit/quickwit-storage/src/cache/disk_sized_cache.rs
};
if !victims.is_empty() {
let root_path = self.root_path.clone();
let _ = tokio::task::spawn_blocking(move || remove_files(&root_path, &victims)).await;

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 think we don't need to await this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think we could end in a strange state if the key is added again before being removed

Signed-off-by: Darkheir <raphael.cohen@sekoia.io>
@Darkheir
Darkheir merged commit 1f226bd into sekoia Jul 10, 2026
3 checks 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.

3 participants