Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASM bytecode cache concurrent access #10065

Open
walkerburgin opened this issue Feb 20, 2025 · 0 comments
Open

WASM bytecode cache concurrent access #10065

walkerburgin opened this issue Feb 20, 2025 · 0 comments
Labels
Milestone

Comments

@walkerburgin
Copy link

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; aborting

And 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{}.tsx

Trying 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

Input code

See: https://github.com/walkerburgin/swc-plugin-cache-repro/tree/master

Config

{
  "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

@kdy1 kdy1 added this to the Planned milestone Feb 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants