-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Describe the bug
Context
We're using the @swc/plugin-formatjs plugin as part of our internationalization system.
Our build system (Bazel + aspect_rules_swc) spawns many, many SWC processes concurrently. Under most circumstances this works great! SWC starts up so fast.
We're currently seeing two different issues related to the WASM bytecode cache.
Issue 1)
SWC won't use the bytecode cache if the directory is read only.
aspect_rules_swc tries to pre-populate the bytecode cache and pass it as as an explicit input to SWC actions. Unfortunately the build system marks this directory as read-only when it's used as an input. From what I can tell SWC is using wasmer's FileSystemCache (here), which fails if the directory is read only (here) and falls back to an in-memory cache every time (which doesn't help us when we're spawning many small, granular actions). The net effect is that we re-prepare the plugin bytecode many thousands of times. The cache is pre-populated, so we know that it won't be written to. It would be nice in this scenario if the cache failed on write instead of failing to initialize altogether.
We've tried to mitigate this by setting jsc.experimental.cacheRoot to a writable dirctory outside of the build system's control (e.g. /tmp/swc) but…
Issue 2)
SWC fails intermittently when multiple processes try to write to the SWC cache at the same time.
It looks like the wasmer FileSystemCache isn't safe for many processes to run concurrently. I put together a standalone repro here that runs swc directly here: https://github.com/walkerburgin/swc-plugin-cache-repro
This fails reliably on my machine:
➜ rm -rf .swc && rm -rf build && seq 0 499 | xargs -P 25 -I{} ./bin/swc-darwin-arm64-v1.10.18 compile --config-json '{"jsc":{"experimental":{ "cacheRoot": ".swc", "plugins":[["./node_modules/@swc/plugin-formatjs", { }]]}}}' --out-file ./build/Component{}.js src/Component{}.tsx
xargs: ./bin/swc-darwin-arm64: terminated with signal 10; abortingAnd this succeeds:
# Pre-populate the plugin bytecode cache
➜ rm -rf .swc && ./bin/swc-darwin-arm64-v1.10.18 compile --config-json '{"jsc":{"experimental":{ "cacheRoot": ".swc", "plugins":[["./node_modules/@swc/plugin-formatjs", { }]]}}}' --out-file /dev/null /dev/null
# Then we can run many actions in parallel successfully (and fast!)
➜ rm -rf build && seq 0 499 | xargs -P 25 -I{} ./bin/swc-darwin-arm64-v1.10.18 compile --config-json '{"jsc":{"experimental":{ "cacheRoot": ".swc", "plugins":[["./node_modules/@swc/plugin-formatjs", { }]]}}}' --out-file ./build/Component{}.js src/Component{}.tsxTrying to watch the filesystem access to the .swc cache directory while the failing case is running:
➜ fswatch -x .swc
.swc Created IsDir AttributeModified
.swc/plugins Created IsDir AttributeModified
.swc/plugins/v7_macos_aarch64_7.0.0 Created IsDir AttributeModified
.swc/plugins/v7_macos_aarch64_7.0.0/73d7dfbc154ced25d4c677fe2162e5aa0280fb947e891799719e0f23d786d2dc Created AttributeModified IsFile Updated Removed AttributeModified
.swc/plugins/v7_macos_aarch64_7.0.0/73d7dfbc154ced25d4c677fe2162e5aa0280fb947e891799719e0f23d786d2dc Created IsFile Updated Removed AttributeModified
.swc/plugins/v7_macos_aarch64_7.0.0/73d7dfbc154ced25d4c677fe2162e5aa0280fb947e891799719e0f23d786d2dc Created AttributeModified IsFile Updated AttributeModified
Misc
- Related issue in
aspect_rules_swc: [Bug]:jsc.experimental.cacheRootdestroys SWC performance aspect-build/rules_swc#293
Input code
See: https://github.com/walkerburgin/swc-plugin-cache-repro/tree/masterConfig
{
"sourceMaps": "inline",
"module": {
"type": "es6",
"strictMode": true,
"noInterop": false
},
"jsc": {
"externalHelpers": false,
"target": "es2022",
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": false,
"dynamicImport": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": false,
"react": {
"throwIfNamespace": false,
"useBuiltins": false,
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
"importSource": "react"
}
},
"keepClassNames": true
}
}Playground link (or link to the minimal reproduction)
https://github.com/walkerburgin/swc-plugin-cache-repro
SWC Info output
Operating System:
Platform: darwin
Arch: arm64
Machine Type: arm64
Version: Darwin Kernel Version 24.2.0: Fri Dec 6 19:02:41 PST 2024; root:xnu-11215.61.5~2/RELEASE_ARM64_T6030
CPU: (12 cores)
Models: Apple M3 Pro
Binaries:
Node: 22.14.0
npm: 10.9.2
Yarn: N/A
pnpm: 9.10.0
Relevant Packages:
@swc/core: 1.10.18
@swc/helpers: N/A
@swc/types: N/A
SWC Config:
output: N/A
.swcrc path: N/A
Next.js info:
output: N/A
Expected behavior
- SWC can read from a read-only plugin cache directory
- Concurrent SWC processes can write safely to a shared plugin cache directory
Actual behavior
- SWC does not read from plugin cache directories that are read only
- Concurrent writes to a shared plugin cache directory fail intermittently
Version
v1.10.18
Additional context
No response