Skip to content

fix(resolver): publish cached absolute paths once - #94

Merged
steipete merged 1 commit into
mainfrom
claude/resolver-churn-w115
Oct 3, 2026
Merged

steipete merged 1 commit into
mainfrom
claude/resolver-churn-w115

Conversation

@steipete

@steipete steipete commented Oct 3, 2026 •

Copy link
Copy Markdown

What does this PR do?

Concurrent worker resolution and filesystem-router reloads can expose a truncated symlink target such as /fixture/node_modules/, causing EISDIR or ERR_MODULE_NOT_FOUND. Entry.abs_path was a plain, multiword Interned, filled independently with either the shorter alias path or the longer canonical path. Linux instrumentation captured an alias-length abs_path snapshot ending at node_modules, while the guarded symlink cache held the complete filename and the cached fd was invalid.

Adapts oven-sh/bun#40258 by @robobun. OnceLock<Interned> publishes the complete value once without manual Send/Sync implementations. Initialization retains the per-entry mutex for cached-fd access and leaves failed fills retryable. Resolver, router, test-scanner, and hot-reloader callers use the same API. The mutable entry cache remains guarded separately.

How did you verify your code works?

The unpatched Linux workload reproduced directory-only paths, and the native diagnostic identified the malformed absolute-path snapshot. Miri reports a data race in the original set_abs_path assignment versus the original getter. The baseline probe leaves production code unchanged and uses a test-only adapter to map the new initializer calls to the former lazy-fill getter/setter sequence. The committed resolver regression tests check concurrent publication and failed-fill retry; patched Miri passes four tests with three existing native-SIMD exclusions.

Final head fce7fbde02bcee756ed9e76c1bced8dc5f503b9a passed 68,616,192 resolves, imports, and requires each, with 67,008 worker lifetimes, 104,958 build/reload iterations, and zero resolution errors across ten approximately 90-second processes. This exceeds the earlier failing workload's six complete pre-failure runs: 29,163,520 of each operation, 28,480 workers, and 39,878 build/reload iterations. The original failure occurred in its seventh process.

Exposure is nondeterministic: the initial main CI artifact failed twice; an instrumented main build failed in its third process and supplied the native capture; a clean matched source baseline also completed 57,933,824 of each operation without error. The Miri failure and native capture establish the publication defect independently of that variability.

Matched no-LTO release builds of main ba02f43295 and this head used the same Linux machine/toolchain, no other test load, CPUs 16–31, and eight samples per arm in ABBA order:

Warm workload Baseline median Patched median Change
300,000 plain resolutions 169.80 ms 170.14 ms +0.20%
300,000 symlink resolutions 375.56 ms 381.35 ms +1.54%
400,000 symlink resolutions across eight workers 1,235.07 ms 1,241.06 ms +0.49%

The final-head surrounding resolver, filesystem-router, worker, bundler-resolver, and source-lint run passed 598 tests, with two existing skips. All 12 Rust targets passed; resolver all-target clippy, router library clippy, and formatting pass. An extra router all-target lint probe found four unchanged debug-print macros in existing tests; those are outside this change.

The OpenClaw consumer pair passed all 23 tests on the final binary with OPENCLAW_VITEST_RUNTIME=bun and private HOME/state/tmp directories. Ten paired runs on the same resolver change before the unrelated main rebase also passed all 230 test cases; baseline Bun and Node controls passed 23 each. A Node 24 control resolved and required all 128 symlinked page fixtures correctly. The import override to bun:test row passes after #87; no W100 change is duplicated here.

Independent Codex P2 branch review is scoped-clean. Both native lanes, Linux x64 and Darwin arm64, are green on this exact head in run 37126588182. Rust CI 37126588216 is also green, including Miri and clippy. The rebase preserved both append-only changelog entries and left every reviewed resolver/router source blob unchanged.

The advisory Mordant job remains red with the existing bare_bool_args warning for resolve_target(..., false, false) in unchanged src/resolver/package_json.rs, counted across three targets, as already reported by the upstream-sync parent in run 37121766296. That job is explicitly configured as non-blocking; the native lanes, Miri, clippy, formatting, and source checks pass.

@steipete
steipete marked this pull request as ready for review October 3, 2026 13:29
Prevent worker resolution and filesystem-router reloads from observing a
canonical path pointer with the shorter alias length, which can resolve a
symlinked file to a directory-only node_modules path.

Adapts oven-sh#40258 by @robobun. Use OnceLock for safe publication and
retain the entry mutex around fallible initialization and cached-fd access.
Add resolver tests for concurrent first-fill publication and failed-fill retry.
@steipete
steipete force-pushed the claude/resolver-churn-w115 branch from 401a563 to fce7fbd Compare October 3, 2026 13:33
@steipete
steipete merged commit 61e5bf3 into main Oct 3, 2026
10 of 11 checks passed
@steipete
steipete deleted the claude/resolver-churn-w115 branch October 3, 2026 14:17
steipete added a commit that referenced this pull request Oct 5, 2026
Bound the macOS runtime transpiler cache to one write transaction at a time. Parallel file creation and rename contend inside APFS, spending kernel CPU across Bun Pool workers. Parsing, resolution, cache reads, cache bytes and atomic publication remain unchanged; resolver synchronization from #89/#94 is preserved.

On the loaded arm64 Mac, 12 samples per arm in ABBA order reduce median system CPU from 5.340 to 1.505 seconds (71.8%) and hardware cycles from 24.838 to 11.343 billion (54.3%). Both bootstrap intervals exclude no change. Wall time is 1.805 to 1.890 seconds, a difference within the wide shared-host noise interval. All 352 SDK imports succeed and all 2,093 cache entries are byte-identical. Quiet Linux timings remain unchanged within run variation.

Add a cold/warm module-import benchmark and concurrent symlink/cache-reuse coverage. Align stale hook-cache assertions with format 36 while preserving current-hook, changed-target and removed-target checks.

Validation: 28 cache tests; 141 resolver/cache tests with two existing skips; all 12 Rust targets; native Linux and Darwin CI; scoped-clean Codex P2 review. CI: https://github.com/openclaw/bun/actions/runs/37246173137
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