Skip to content

fix(devbox): reconcile runx bin dir in place to avoid concurrent races - #3019

Merged
mikeland73 merged 1 commit into
mainfrom
mikeland73/specs/bugs.md-fix-remove
Oct 26, 2026
Merged

mikeland73 merged 1 commit into
mainfrom
mikeland73/specs/bugs.md-fix-remove

Conversation

@mikeland73

Copy link
Copy Markdown
Collaborator

Summary

RunXPaths deleted and recreated .devbox/virtenv/runx/bin on every computeEnv, so concurrent devbox commands in the same project could hit ENOENT/EEXIST, and running shells lost their runx binaries while it was rebuilt. It now builds the desired name → target map and reconciles the directory in place: only stale or unwanted entries are removed, missing links are created, and links that a concurrent process already created correctly are accepted. This also removes specs/bugs.md, which only tracked this bug.

How was it tested?

Added TestSyncSymlinks (directory with correct, stale, unwanted links and a regular file) and TestSyncSymlinksConcurrent (8 goroutines syncing the same directory); both pass with go test -race -count=3.

Community Contribution License

All community contributions in this pull request are licensed to the project
maintainers under the terms of the
Apache 2 License.

By creating this pull request, I represent that I have the right to license the
contributions to the project maintainers under the Apache 2 License as stated in
the
Community Contribution License.

🤖 Generated with Claude Code

RunXPaths used to RemoveAll the runx bin directory and recreate it on every
computeEnv, so parallel devbox commands could delete it out from under each
other. Now the directory is reconciled in place: stale entries are removed,
missing links are created, and links created concurrently by another process
are accepted.

Also removes specs/bugs.md now that the deferred bug is fixed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Oct 25, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@mikeland73

Copy link
Copy Markdown
Collaborator Author

@claude ptal

@claude

claude Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Claude finished @mikeland73's task in 17s —— View job


Review of #3019

I read the diff and found no blocking issues. I didn't run the tests, and I didn't check the range 50 / range cap(errs) loops against the Go version in go.mod. Those loops need Go 1.22 or later.

What looks good

  • RunXPaths now builds the name → target map first and then calls syncSymlinks. It no longer deletes and recreates the directory, so running shells keep their runx binaries.
  • Correct links are left alone. Stale links, unwanted entries and regular files are removed. Missing links are created.
  • ErrNotExist on removal and ErrExist with a matching target on creation are both tolerated. That covers the common concurrent case, where several processes reconcile the same set of links.
  • Keeping the first package's binary when names collide matches the old behavior, where Symlink hit ErrExist and was ignored.
  • TestSyncSymlinks covers each reconcile case. TestSyncSymlinksConcurrent covers the race.

Minor, non-blocking

  1. Remaining race window (internal/devbox/devbox.go, syncSymlinks). Say process A and process B both see a stale foo link. A removes it and creates the correct one. B then removes A's new link, because B's decision came from its earlier ReadDir. B then recreates it. The end state is correct, but foo is briefly missing. This is much narrower than before and only matters when targets change, so I don't think it's worth fixing.
  2. Error on a wrong-target link (syncSymlinks). If Symlink returns ErrExist and the existing link points somewhere else, the function returns the error. That can happen if two processes disagree about the target at the same moment. It's rare, and retrying once after a remove would make it robust. This is optional.
  3. Concurrent test (TestSyncSymlinksConcurrent). It starts with an empty directory, so it only exercises the create-and-accept-ErrExist path. Pre-seeding it with a few stale links would also exercise the removal path under -race.

Deleting specs/bugs.md is fine, since it only tracked this bug.

@mikeland73
mikeland73 merged commit 0623e2b into main Oct 26, 2026
29 checks passed
@mikeland73
mikeland73 deleted the mikeland73/specs/bugs.md-fix-remove branch October 26, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant