Skip to content

fix(cli): make mops install safe under parallel invocations - #543

Merged
Kamirus merged 3 commits into
mainfrom
kamillistopad/lang-1310-mops-make-mops-install-safe-under-parallel-invocations
May 22, 2026
Merged

fix(cli): make mops install safe under parallel invocations#543
Kamirus merged 3 commits into
mainfrom
kamillistopad/lang-1310-mops-make-mops-install-safe-under-parallel-invocations

Conversation

@Kamirus

@Kamirus Kamirus commented May 22, 2026

Copy link
Copy Markdown
Collaborator

Why

Two mops processes installing into the same project (editor watcher, vscode-motoko's fixture installer, CI matrix sharing a global cache) race on cache writes and leave zero-byte / truncated files in .mops/. Symptom downstream: missing completions, hover, type-check errors. v2.13.2's concurrent-invocations fix covered check/build/check-stable scratch dirs, not the install cache path.

Two race windows:

  • Global cache (install-mops-dep.ts): Promise.all(mkdir + writeFile) writes directly into cacheDir while isDepCached = fs.existsSync(cacheDir) — peer sees "cached" mid-write and copies a half-populated tree.
  • Local .mops/<pkg> (syncLocalCache): two concurrent installs both miss existsSync(dest) and interleave writes via ncp.

GitHub installs were strictly worse — mkdirSync(cacheDir) ran before the download, so peers saw an empty dir as cached. The shared .mops/_tmp/ zip dir was also clobbered by concurrent github installs.

What

Cache writes stage into a sibling .staging-* dir (mkdtemp, same filesystem) and atomically rename onto the canonical path. Race-loser catches ENOTEMPTY / EEXIST / EPERM (only when dest exists, to avoid masking real Windows AV / permission errors), drops its staging, and observes a complete cache. Applies to mops registry installs, github installs, local .mops/ sync, and the github zip download dir. Stale staging is swept on the next install with a 1h mtime cutoff.

Test plan

  • New test: 5 parallel mops install runs against an isolated XDG_CACHE_HOME produce no zero-byte files and no .staging-* leftovers (cold-cache path actually exercised).
  • Existing parallel builds of the same canister both succeed still passes.
  • Full CLI test suite: 104 passed.
  • npm run check + npm run lint clean.

Refs LANG-1310, caffeinelabs/vscode-motoko#461.

Concurrent `mops install` runs against the same project (editor watcher,
vscode-motoko fixture installer, CI matrix) used to race in two places —
global cache writes and local `.mops/<pkg>` copies — leaving zero-byte /
truncated files that surfaced later as missing completions or type-check
errors. GitHub installs were strictly worse because `mkdirSync(cacheDir)`
ran before the download, so a peer process saw an empty dir as cached.

Cache writes now stage into a sibling `.staging-*` dir and atomically
rename onto the canonical path; the loser of a race discards its staging
silently. `mops install`, `mops add`, and the local `.mops/` sync follow
the same pattern. The shared `.mops/_tmp/` zip dir used by GitHub installs
is also per-invocation (`mkdtemp`) now. Stale staging dirs from crashed
runs are swept on the next install (mtime threshold so a sibling that is
mid-staging is never clobbered).

Refs LANG-1310, caffeinelabs/vscode-motoko#461.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Kamirus
Kamirus requested a review from a team as a code owner May 22, 2026 07:40
Kamirus and others added 2 commits May 22, 2026 09:57
…github

- commitStagingDir only swallows EPERM/EEXIST/ENOTEMPTY when dest exists.
- Parallel-install test isolates the global cache via XDG_CACHE_HOME so
  the global-write code path actually executes (was a hot-cache no-op).
- Sweep covers <rootDir>/.mops/_github/ and the github zip download dir
  (renamed to .staging-github-dl-).
- Migrate dead-but-exported addCache to the staging pattern.
- SIGINT exits 130 (POSIX convention).
- Trim comments; CHANGELOG note about `mops cache clean` for upgrades.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@Kamirus
Kamirus merged commit 2033885 into main May 22, 2026
25 checks passed
@Kamirus
Kamirus deleted the kamillistopad/lang-1310-mops-make-mops-install-safe-under-parallel-invocations branch May 22, 2026 08:55
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