feat: prune idle volume browser helper containers#2767
Merged
Conversation
Reused read-only helper containers leaked when a client vanished, holding the volume open and blocking deletes/prunes. Now track one helper per volume, stop it before DeleteVolume and on PruneVolumes, and add a reaper job (runs every minute) that removes helpers idle past volumeBrowserHelperIdleTimeout (minutes; default 10, 0 disables). Adds a POST browse/stop endpoint for explicit and page-unload teardown.
Stop the volume browser helper as soon as the user leaves the browser, instead of waiting on the backend reaper (now only a backstop). Stops on onDestroy (tab switch, in-app nav, back, opening another volume) and via a pagehide sendBeacon on hard close/refresh. Adds a Stop browsing button, the stopBrowsing service call, the settings type, and i18n messages.
Address review feedback: move the pagehide teardown beacon to a declarative <svelte:window onpagehide> handler instead of a manual onMount addEventListener pair, and show an error toast (new volumes_browser_stop_error message) when the explicit Stop button fails so it no longer fails silently.
Contributor
Author
Contributor
Author
|
greptileai please re-review |
Collaborator
Contributor
Author
|
@cabaucom376 do I have to do anything to get the E2E Test workflows to run? I ran all just tests on my local, is the report expected to upload or its running in a workflow? please let me know if any additional changes requested |
Contributor
Author
|
@kmendell please review |
kmendell
requested changes
May 31, 2026
kmendell
approved these changes
Jun 4, 2026
github-actions Bot
added a commit
to ShobuPrime/home-assistant-apps
that referenced
this pull request
Jun 5, 2026
## Arcane Docker Manager Update This automated PR updates Arcane from `1.19.5` to `1.20.0`. ### Changelog ### New features * add removeOrphans option to project deploy/redeploy ([#2785](getarcaneapp/arcane#2785) by `khanhx`) * prune idle volume browser helper containers ([#2767](getarcaneapp/arcane#2767) by `Zgrill2`) ### Bug fixes * slog-go nil pointer dereference ([#2781](getarcaneapp/arcane#2781) by `lohrbini`) * dashboard card buttons paddings overlaps([c1a0bda](getarcaneapp/arcane@c1a0bda) by `kmendell`) * disable schema display on text selection([058f062](getarcaneapp/arcane@058f062) by `kmendell`) * clear / check for default jwt secret([ae914bd](getarcaneapp/arcane@ae914bd) by `kmendell`) ### Dependencies * bump date-fns from 4.2.1 to 4.3.0 ([#2745](getarcaneapp/arcane#2745) by `dependabot`[bot]) * bump `sveltejs`/ki ### Changes - Updated `config.yaml` version - Updated `build.yaml` ARCANE_VERSION - Updated `Dockerfile` ARCANE_VERSION - Updated documentation files - Updated CHANGELOG.md ### Release Notes Full release notes: https://github.com/getarcaneapp/arcane/releases/tag/v1.20.0 --- This PR was automatically generated by the Update Arcane workflow Auto-merged by GitHub Actions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checklist
mainbranchWhat This PR Implements
Volume Browser Helper Reaper
Context / Motivation
The volume Browser tab (/volumes/[volumeName] → Browser) browses a volume's contents read-only. To do this the backend spins up a Docker helper
container that mounts the volume read-only at /volume
The problem: these helpers leak. If the user closes the tab, loses network, or the browser crashes, nothing tells the backend to tear the
helper down — it keeps running and holds the volume, which can block volume deletes/prunes and waste resources.
This PR adds layered cleanup: eager client-side teardown when the user leaves the browser, plus a backend reaper job as a guaranteed backstop that
removes any helper left idle past a configurable timeout.
Fixes: #2765
Changes Made
How it works
Helper lifecycle
Teardown paths (layered, intentional)
- onDestroy → stopBrowsing(volumeName) on tab switch, in-app nav, back button, and opening another volume (unmount).
- pagehide → navigator.sendBeacon(.../browse/stop) on hard close/refresh.
the client paths miss.
▎ Design note: teardown is eager, not lazy. The helper dies the moment you leave the browser; the 10-min idle timeout is the safety net, not the primary mechanism.
Testing Done
./scripts/development/dev.sh startjust lint all)just test backendAI Tool Used (if applicable)
AI Tool: Claude Code
Assistance Level: Moderate
What AI helped with: Design implementation plan, doing the actual code, running the linting and just tests.
I reviewed and edited all AI-generated output: Yes always. I had the design and all implementation plans were reviewed by me
I ran all required tests and manually verified changes: Yes, tested on my local laptop
Additional Context
My partner uses Arcane and really wants this feature
Disclaimer Greptiles Reviews use AI, make sure to check over its work.
To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike
To have Greptile Re-Review the changes, mention
greptileai.Greptile Summary
This PR introduces a layered cleanup mechanism for volume-browser helper containers: eager client-side teardown in
VolumeBrowser.svelte(onDestroy+pagehidebeacon), explicit "Stop browsing" button, pre-cleanup inDeleteVolume/PruneVolumesWithOptions, and a background reaper job (PruningVolumeHelperJob) as the final backstop.helperByVolumemap is promoted frommap[string]stringtomap[string]*volumeHelper, adding alastUsedAttimestamp.StopHelper,ReapIdleHelpers, andtouchHelperInternalare carefully split so all Docker calls happen outside the mutex, with map entries pre-deleted before container removal — concurrent browse requests get a clean inspect-fail path and re-create a fresh helper.VolumeBrowserHelperIdleTimeoutsetting (default 10 minutes, 0 disables) is wired through models, defaults, the TypeScript type, and the schema test; the reaper job runs on a fixed 5-minute schedule and reads this knob each invocation.collectStaleHelperIDsInternal,takeHelperIDInternal,touchHelperInternal) are tested in isolation without Docker, covering fresh/stale/edge/nil entries and idempotent absent cases.Confidence Score: 5/5
The change is safe to merge. All Docker calls are outside the mutex, force removal is used throughout, and concurrent browse/reap races result in a clean inspect-fail path rather than undefined behavior.
The concurrency design is sound: map entries are pre-deleted before container removal so concurrent requests transparently re-create a fresh helper, and force removal via
volumeHelperRemoveOptionsInternalensures running containers are stopped even when the timeout fires. DeleteVolume and PruneVolumes correctly stop helpers first to avoid "volume in use" conflicts. New unexported functions all carry the requiredInternalsuffix. Unit tests cover edge cases (exact-timeout boundary, nil entries, idempotent take) without requiring Docker. The new setting is consistently plumbed through models, defaults, TypeScript types, and schema tests.No files require special attention.
Reviews (7): Last reviewed commit: "rename" | Re-trigger Greptile