Skip to content

fix(aqua): remove unnecessary bin_paths disk cache#8383

Merged
jdx merged 1 commit intomainfrom
fix/remove-aqua-bin-path-cache
Feb 28, 2026
Merged

fix(aqua): remove unnecessary bin_paths disk cache#8383
jdx merged 1 commit intomainfrom
fix/remove-aqua-bin-path-cache

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 28, 2026

Summary

  • Remove bin_path_caches (DashMap + bin_paths.msgpack.z disk cache) from aqua backend
  • Remove now-unused PathExt::mount and PathExt::is_empty
  • list_bin_paths now computes directly every call

Context

The aqua backend cached list_bin_paths results to bin_paths.msgpack.z, but this cache provided negligible performance benefit — reading + decompressing a msgpack file is comparable to reading + parsing a small YAML registry file (or parsing from the baked-in registry which doesn't even hit disk).

Meanwhile, the cache was the root cause of #8372 where stale cached paths caused missing PATH entries after install. The fix attempted in #8374 was ineffective — it cleared the in-memory DashMap (only helps same process) and deleted the disk cache (immediately raceable by concurrent mise hook-env). The real protection was already in create_install_dirs() which wipes tv.cache_path() before install begins.

Removing the cache entirely eliminates this class of bugs with no meaningful performance impact.

Test plan

  • cargo check passes with no warnings
  • All lint checks pass
  • Existing e2e tests pass (CI)

🤖 Generated with Claude Code


Note

Medium Risk
Changes how list_bin_paths is computed by removing both in-memory and on-disk caching, which can affect PATH resolution behavior and performance (though intended to reduce stale-path bugs). Scope is limited to the Aqua backend and removal of unused path helpers.

Overview
Removes Aqua’s bin_paths caching layer (the per-version DashMap and bin_paths.msgpack.z file) and stops trying to invalidate it after installs.

list_bin_paths now recomputes binary directories on each call by reading the registry package definition and deriving the paths directly, eliminating stale cached PATH entries.

Cleans up PathExt by deleting the now-unused mount and is_empty helpers.

Written by Cursor Bugbot for commit 5e5c5db. This will update automatically on new commits. Configure here.

The aqua backend cached `list_bin_paths` results to
`bin_paths.msgpack.z`, but this cache provided negligible performance
benefit (reading + decompressing a msgpack file vs reading + parsing a
small YAML registry file) while introducing bugs like #8372 where
stale cached paths caused missing PATH entries after install.

The cache invalidation added in #8374 to address that bug was itself
ineffective — it couldn't prevent a concurrent `mise hook-env` process
from repopulating the cache during install.

Remove the cache entirely along with the now-unused `DashMap`
dependency, `PathExt::mount`, and `PathExt::is_empty`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the Aqua backend by removing an unnecessary bin path caching mechanism. The previous caching, which involved both an in-memory DashMap and a disk-based msgpack.z file, offered negligible performance benefits. Critically, it was identified as the root cause of issues where stale cached paths led to missing PATH entries after installations. By removing this cache entirely, the system eliminates a class of bugs related to stale data without impacting performance, simplifying the codebase and improving reliability.

Highlights

  • Bin Path Caching Removal: Eliminated the bin_path_caches (in-memory DashMap and bin_paths.msgpack.z disk cache) from the Aqua backend.
  • Unused Utility Removal: Removed the now-unused PathExt::mount and PathExt::is_empty methods.
  • Real-time Bin Path Computation: Modified list_bin_paths to compute bin paths directly on every call instead of relying on a cache.
Changelog
  • src/backend/aqua.rs
    • Removed PathExt and DashMap imports.
    • Removed the bin_path_caches field from the AquaBackend struct.
    • Removed logic for clearing bin_path_caches after installation.
    • Refactored list_bin_paths to directly compute and return paths, removing all cache-related operations.
    • Removed initialization of bin_path_caches in the AquaBackend::new function.
  • src/path.rs
    • Removed the mount and is_empty methods from the PathExt trait definition.
    • Removed the implementations of mount and is_empty from the impl PathExt for Path block.
Activity
  • The author confirmed cargo check passes with no warnings.
  • The author confirmed all lint checks pass.
  • The author noted that existing e2e tests pass in CI.
  • The pull request description was generated with Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively removes the bin_paths cache from the aqua backend, simplifying the code and addressing a bug concerning stale cache entries. The changes are logical and well-executed. I have one suggestion to enhance the list_bin_paths function by replacing a blocking I/O call with an asynchronous equivalent, which will improve performance and prevent potential runtime issues.

Comment on lines +480 to +487
let paths: Vec<PathBuf> = if srcs.is_empty() {
vec![install_path.clone()]
} else {
srcs.iter()
.map(|(_, dst)| dst.parent().unwrap().to_path_buf())
.collect()
};
Ok(paths.into_iter().unique().filter(|p| p.exists()).collect())
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The use of p.exists() within the filter closure performs synchronous I/O in an async function. This can block the async runtime and lead to performance issues, especially since this function is now called more frequently after removing the cache. It's recommended to use asynchronous file system operations to avoid blocking.

        let paths: Vec<PathBuf> = if srcs.is_empty() {
            vec![install_path.clone()]
        } else {
            srcs.iter()
                .map(|(_, dst)| dst.parent().unwrap().to_path_buf())
                .collect()
        };
        let mut existing_paths = Vec::new();
        for p in paths.into_iter().unique() {
            if tokio::fs::try_exists(&p).await.unwrap_or(false) {
                existing_paths.push(p);
            }
        }
        Ok(existing_paths)

@greptile-apps
Copy link

greptile-apps bot commented Feb 28, 2026

Greptile Summary

This PR eliminates the aqua backend's bin_paths caching mechanism (both in-memory DashMap and on-disk bin_paths.msgpack.z) that was causing stale PATH entries after installation (#8372). The previous fix attempt in #8374 was insufficient due to race conditions between cache clearing and concurrent mise hook-env processes.

Key changes:

  • Removed bin_path_caches: DashMap<String, CacheManager<Vec<PathBuf>>> field from AquaBackend
  • Simplified list_bin_paths to compute binary directories on every call by reading from AQUA_REGISTRY (which has its own package-level cache)
  • Removed cache invalidation logic after installation since cache no longer exists
  • Cleaned up unused PathExt::mount and PathExt::is_empty helper methods

The new implementation returns absolute paths directly instead of the previous strip-prefix-then-remount pattern, which is functionally equivalent but simpler. Performance impact is negligible since AQUA_REGISTRY.package() maintains its own static cache (line 86-96 in aqua_registry_wrapper.rs).

Confidence Score: 5/5

  • Safe to merge - eliminates race condition bugs with clean refactoring
  • The changes are straightforward deletions that remove problematic caching logic. The simplified list_bin_paths implementation is functionally equivalent to the old version (returns same absolute paths, just without the strip/remount roundtrip). AQUA_REGISTRY provides its own caching at the package level, so performance impact is negligible. No new logic or complexity introduced, and the change directly fixes the stale PATH issue.
  • No files require special attention

Important Files Changed

Filename Overview
src/backend/aqua.rs Removes bin_path_caches field and cache clearing logic; simplifies list_bin_paths to compute paths directly
src/path.rs Removes unused PathExt::mount and PathExt::is_empty helper methods

Last reviewed commit: 5e5c5db

@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.23 x -- echo 25.2 ± 0.6 24.3 27.7 1.00
mise x -- echo 28.6 ± 0.5 27.7 32.5 1.14 ± 0.03
⚠️ Warning: Performance variance for x -- echo is 14%

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.23 env 24.8 ± 0.8 23.9 33.1 1.00
mise env 28.1 ± 0.5 27.2 31.2 1.14 ± 0.04
⚠️ Warning: Performance variance for env is 14%

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.23 hook-env 25.4 ± 0.6 24.3 28.6 1.00
mise hook-env 29.8 ± 0.8 28.1 34.0 1.17 ± 0.04
⚠️ Warning: Performance variance for hook-env is 17%

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.23 ls 23.0 ± 0.8 22.1 27.5 1.00
mise ls 23.3 ± 0.7 22.1 28.1 1.01 ± 0.04

xtasks/test/perf

Command mise-2026.2.23 mise Variance
install (cached) 159ms 164ms -3%
ls (cached) 90ms 90ms +0%
bin-paths (cached) 94ms 97ms -3%
task-ls (cached) 838ms 848ms -1%

@jdx jdx merged commit c53dc29 into main Feb 28, 2026
37 of 38 checks passed
@jdx jdx deleted the fix/remove-aqua-bin-path-cache branch February 28, 2026 11:24
mise-en-dev added a commit that referenced this pull request Mar 1, 2026
### 🐛 Bug Fixes

- **(aqua)** remove unnecessary bin_paths disk cache by @jdx in
[#8383](#8383)
- **(hooks)** render tera templates and fix output masking by @jdx in
[#8385](#8385)
- **(install)** improve error when registry tool has no supported
backends by @jdx in [#8388](#8388)
- **(python)** remove deprecated venv_auto_create setting by @jdx in
[#8384](#8384)
jdx added a commit that referenced this pull request Mar 1, 2026
PR #8383 removed the aqua bin_paths disk cache to fix staleness bugs,
but this caused ~8-17% regression on mise env/x/hook-env. Restore the
cache using with_fresh_file(tv.install_path()) so it automatically
invalidates when a tool is installed (install_path mtime changes),
avoiding the original race condition without sacrificing performance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jdx added a commit that referenced this pull request Mar 1, 2026
…8398)

## Summary
- Restore the aqua `bin_paths.msgpack.z` disk cache that was removed in
#8383
- Use `with_fresh_file(tv.install_path())` for automatic cache
invalidation when a tool is installed/updated
- Restore `PathExt::mount` and `PathExt::is_empty` helpers needed for
relative path handling

## Context

PR #8383 removed the aqua bin_paths disk cache to fix staleness bugs
(#8372) where concurrent `mise hook-env` during install could cache
incomplete paths. However, this caused a ~8-17% performance regression
on `mise env`, `mise x -- echo`, and `mise hook-env` (confirmed in CI
hyperfine benchmarks on the 2026.2.24 release PR #8387).

The root cause of the regression: each mise command is a separate
process, so in-memory caches provide no benefit. The disk cache was what
persisted across invocations.

The original cache lacked `fresh_file` invalidation. This fix uses
`with_fresh_file(tv.install_path())` — the same pattern used by
`ExternalPluginCache` for asdf backends — so the cache automatically
invalidates when the install directory's mtime changes (i.e., after tool
installation). This avoids the race condition from #8372 without
sacrificing performance.

### Local benchmark results (hyperfine, 100 runs)

| Command | Baseline (2026.2.23) | No cache (2026.2.24) | **This fix** |
|---------|---------------------|---------------------|-------------|
| `mise env` | 24.5ms | 26.2ms (+7%) | **23.8ms** (restored) |
| `mise x -- echo` | 23.5ms | 26.0ms (+11%) | **23.7ms** (restored) |

## Test plan
- [x] `cargo check` passes
- [x] All lints pass (`mise run lint-fix`)
- [x] All 492 unit tests pass (`mise run test:unit`)
- [x] Local hyperfine benchmarks confirm regression is eliminated
- [ ] CI hyperfine benchmarks pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it changes `list_bin_paths` behavior and caching
semantics for the aqua backend; incorrect invalidation or path mounting
could lead to missing/incorrect PATH entries after installs/updates.
> 
> **Overview**
> Restores a persistent disk cache for aqua `list_bin_paths`
(`bin_paths.msgpack.z`) to avoid recomputing bin directories on each
invocation.
> 
> The cache now uses `with_fresh_file(tv.install_path())` (plus an
existing freshness duration) so it automatically invalidates when the
install directory changes, and it stores paths relative to
`install_path` then re-mounts them via newly reintroduced
`PathExt::mount`/`PathExt::is_empty` helpers.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8019d04. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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