Skip to content

feat(depot-client): wire sqlite optimization flags#4923

Closed
NathanFlurry wants to merge 1 commit intographite-base/4923from
sqlite-soak/wire-optimization-flags
Closed

feat(depot-client): wire sqlite optimization flags#4923
NathanFlurry wants to merge 1 commit intographite-base/4923from
sqlite-soak/wire-optimization-flags

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

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 would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copy link
Copy Markdown
Member Author

NathanFlurry commented May 4, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 4, 2026

Code Review: feat(depot-client): wire sqlite optimization flags

Overview

This PR wires the existing SqliteOptimizationFlags into the VFS layer to control page caching behavior more granularly. Key changes:

  • Introduces SqliteVfsPageCacheMode (Off / Target / Startup / Prefetch / All) to classify which pages are cached
  • Adds a protected_page_cache: HashMap<u32, Vec<u8>> alongside the moka LRU cache for eviction-immune pages (low-numbered pages)
  • Extends startup preload from single-page (page 1 only) to multi-page (startup_preload_first_page_count pages)
  • Adds PageCacheInsertKind (Target / Prefetch / Startup) to classify inserts
  • Strips read-pool metrics from the benchmark script (read pool feature deferred)

Issues

1. Possible logic bug in fetch_initial_pages_for_registration (vfs.rs)

The early-return guard falls back to fetching only page 1 if !config.preload_hints_on_open:

if !config.startup_preload_first_pages
    || !config.preload_hints_on_open   // suspicious
    || !config.page_cache_mode.caches_startup_preloaded_pages()
    || config.startup_preload_max_bytes < DEFAULT_PAGE_SIZE
{
    return fetch_initial_main_page_for_registration(...)

startup_preload_first_pages and preload_hints_on_open control independent features. Multi-page startup preloading warms the VFS cache directly and is useful regardless of whether actor-state preload hints are applied on open. Gating them together means disabling preload_hints_on_open silently also disables the multi-page cold-start fetch, which is non-obvious. Consider removing the !config.preload_hints_on_open arm or explicitly documenting why it belongs here.

2. Write lock held during page-cache insertion after transport call (vfs.rs)

Previously:

let page_cache = { self.state.read().page_cache.clone() };  // clone arc, drop lock
for fetched in ok.pages {
    page_cache.insert(...);  // moka: &self, no exclusive lock needed
}

Now:

let mut state = self.state.write();  // exclusive lock held for all inserts
for fetched in ok.pages { state.cache_page(...); }

The write lock is necessary because protected_page_cache is a plain HashMap. However, this serializes all concurrent VFS callers that were previously able to insert into moka without contention. Per the CLAUDE.md performance guidelines ("Never use Mutex... Use scc::HashMap"), consider whether protected_page_cache could be a scc::HashMap to restore concurrent inserts. The protected cache pages are a small, bounded set (default 512 entries), so a concurrent map is a natural fit.

3. page_cache_weighted_size metric adds entry count for protected cache (vfs.rs)

page_cache_weighted_size: state
    .page_cache
    .weighted_size()
    .saturating_add(state.protected_page_cache.len() as u64),

Without a custom moka weigher, page_cache.weighted_size() returns entry count (weight 1 per entry), so .len() is consistent. However, the metric name implies bytes and a future reader is likely to misinterpret the combined value. Consider renaming the metric to page_cache_entry_count, or adding a custom weigher that assigns actual page-byte weights so the metric reflects real memory pressure.

4. O(n^2) deduplication for early-page hints (vfs.rs)

for pgno in 1..=early_page_count {
    if !snapshot.ranges.iter().any(|range| range.contains(pgno))
        && !snapshot.pgnos.contains(&pgno)   // Vec::contains is O(n)
    {
        snapshot.pgnos.push(pgno);
    }
}
snapshot.pgnos.sort_unstable();

snapshot.pgnos.contains is O(existing hints) per iteration. With MAX_STARTUP_PRELOAD_FIRST_PAGE_COUNT = 256 and typical small hint sets this is fine in practice. A HashSet membership check or collecting into a BTreeSet before the loop would be cleaner and avoid the separate sort pass.

5. Minor: drain(..) in fetch_initial_main_page (vfs.rs)

.map(|mut pages| pages.drain(..).find(|(pgno, _)| *pgno == 1).map(|(_, bytes)| bytes))

Prefer .into_iter().find(...). drain(..) requires mut binding and is unconventional here.


Positive observations

  • Splitting the page cache into eviction-immune (protected_page_cache) and LRU (page_cache) tiers is the right architecture for frequently-accessed low-numbered pages.
  • Constructing VfsConfig before calling fetch_initial_pages_for_registration so the config drives the fetch is a clean improvement.
  • PageCacheInsertKind gives a clear audit trail for why a page entered the cache.
  • invalidate_page_cache() correctly clears both caches together, preventing the pre-existing bug where only the moka cache was invalidated on truncate.
  • should_cache_page always returning true for page 1 regardless of mode is correct, and can_read_cached_page is consistent with it.
  • The benchmark cleanup (removing read-pool metrics/scenarios) is clean.
  • Test coverage for VfsConfig::from_optimization_flags flag wiring is solid.

Summary

The two concerns worth resolving before merge are: (1) the spurious !config.preload_hints_on_open gate that silently disables an independent optimization; and (2) the HashMap behind a write lock for protected_page_cache, which conflicts with the project's concurrent-map guidelines and reduces write-path concurrency. The remaining items are low-priority.

@NathanFlurry NathanFlurry changed the base branch from sqlite-soak/sqlite-transport-trait to graphite-base/4923 May 4, 2026 02:59
@NathanFlurry NathanFlurry force-pushed the graphite-base/4923 branch from 2b8b055 to 48cc3bb Compare May 4, 2026 03:11
@NathanFlurry NathanFlurry force-pushed the sqlite-soak/wire-optimization-flags branch from 5a35d47 to 9155543 Compare May 4, 2026 03:11
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4923 to sqlite-soak/sqlite-transport-trait May 4, 2026 03:11
@NathanFlurry NathanFlurry marked this pull request as ready for review May 4, 2026 03:32
@NathanFlurry NathanFlurry changed the base branch from sqlite-soak/sqlite-transport-trait to graphite-base/4923 May 4, 2026 04:13
@NathanFlurry NathanFlurry force-pushed the graphite-base/4923 branch from 48cc3bb to 7835a88 Compare May 4, 2026 04:15
@NathanFlurry NathanFlurry force-pushed the sqlite-soak/wire-optimization-flags branch from 9155543 to e5462a4 Compare May 4, 2026 04:15
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4923 to sqlite-soak/depot-fault-decode-diagnostics May 4, 2026 04:15
@NathanFlurry NathanFlurry mentioned this pull request May 4, 2026
11 tasks
@NathanFlurry NathanFlurry changed the base branch from sqlite-soak/depot-fault-decode-diagnostics to graphite-base/4923 May 4, 2026 05:18
@NathanFlurry NathanFlurry force-pushed the graphite-base/4923 branch from 7835a88 to cb7241f Compare May 4, 2026 05:18
@NathanFlurry NathanFlurry force-pushed the sqlite-soak/wire-optimization-flags branch from e5462a4 to 8702157 Compare May 4, 2026 05:18
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4923 to mock-agentic-loop/gateway-bypass-client-overrides May 4, 2026 05:18
@NathanFlurry
Copy link
Copy Markdown
Member Author

Landed in main via stack-merge fast-forward push. Commits are in main; closing to match.

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.

1 participant