Tags: cloudflare/workspace
Tags
wsd: ship auto_cache and one-second metadata timeouts by default Previous commits added WSD_FUSE_AUTO_CACHE, WSD_FUSE_ATTR_TIMEOUT, WSD_FUSE_ENTRY_TIMEOUT, WSD_FUSE_NEGATIVE_TIMEOUT, and WSD_FUSE_AC_ATTR_TIMEOUT as opt-in knobs. The default option string matched the historical big_writes plus 128 KiB max_read and max_write so existing deployments saw no change. The mtime contract that auto_cache rests on is now pinned from both sides. Two driver tests confirm the FUSE driver's getattr surfaces fresh mtime and size after an external VFS write and after a buffered local write. Four sync-apply tests confirm the apply path propagates the source mtime onto the destination row, including the same-size-bytes-change case where mtime is the only signal the kernel sees. With both contracts locked, the safer default is also the better default. Flip the defaults. auto_cache is on; attr_timeout, entry_timeout, and ac_attr_timeout sit at one second; negative_timeout is zero so a just-written file shows up immediately. Operators who hit a correctness issue can opt out by setting the matching env var to "0", "false", "no", "off", or "". The kernel_cache fast profile needs an explicit WSD_FUSE_AUTO_CACHE=0 alongside\nWSD_FUSE_KERNEL_CACHE=1 to override the auto_cache default, and a test pins the working incantation. The handoff report measured a single-rep pure read of 44 ms on the old default; the new default lands at 45 ms in this run with the same harness, and at 50 ms with cache turned off. The new default is either neutral or a 10 percent win on pure read in this shape, and a no-op on the write-bound scenarios. The bigger wins land on workloads that reopen the same file across many opens \u2014 the case the bench harness can't measure today. parseBoolWithDefault and pushTimeout now distinguish unset env vars\nfrom explicit empty strings. An unset var takes the default; an\nempty var (what becomes in a shell) turns the\noption off. The asymmetry matters: operators reach for the empty\nform when they want to disable a default without rebuilding.
wsd, rpc: fix shim reverse-direction race via beforeFetch hook Under FUSE_MOUNT=shim, a write inside an exec'd process didn't make it back to the DO unless something later triggered a fresh push/pull cycle. The shim's disk\u2192VFS path is a 250ms poll; the worker's post-exec pullOnce races against that poll, and on a fast exec the pull beats the poll \u2014 fetchChanges streams the empty pre-write state and the file stays stranded in wsd's VFS forever (or until the next exec drives another bracket). The push direction already had a symmetric escape hatch: afterApply on the SyncRPC fires after a peer batch commits and the shim flushes VFS\u2192disk before the push returns. Add the matching beforeFetch hook in the rpc server. wsd wires it to shim.reconcileNow(), a new public method on ShimMount that runs the same disk\u2192VFS reconcile the poll loop runs, serialised through the same internal mutex. With the hook in place, fetchChanges awaits the shim's reconcile before computing the change set, so a Workspace.pull issued right after shell.exec sees every file the spawned process wrote. Verified end-to-end against examples/wsd-container under wrangler dev (FUSE_MOUNT=auto resolves to shim with no /dev/fuse): the smoke script's step 4 now passes with SETTLE_SECONDS=0, and the previously-stranded single-exec scenario reads back the written file immediately. beforeFetch is documented to fire on every fetch, including ones that would otherwise stream zero entries \u2014 the hook is what produces the entries in the first place. Errors are caught and logged so a wedged shim can't take down the wire. Test coverage mirrors the afterApply set: fires once per pull, surfaces hook- materialised writes, swallows thrown hooks. The example script drops its post-exec sleep now that the contract is synchronous.
ci, examples: mirror wsd image to GHCR; examples consume the GHCR copy The release workflow now pushes the wsd-linux-x64 binary image to two registries in a single buildx invocation: ghcr.io as the canonical public source the example Dockerfiles reference, and registry.cloudflare.com/library as a Cloudflare-platform-native copy for consumers who prefer to stay on the in-platform registry. Both registries get the version-specific tag on every release; the :latest tag still only moves on stable (non-prerelease) versions. GHCR was picked for the example Dockerfiles because its public namespace allows anonymous pulls without an account hop, so a developer running wrangler dev against the examples needs no Cloudflare credentials. The CF registry's library namespace requires Cloudflare-internal library_push to publish into and is not generally pullable from outside the platform. GHCR push uses the workflow's GITHUB_TOKEN via packages: write; no repository secrets needed for that half. The CF registry push still uses CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID via the existing .github/cf-registry-login.sh helper. One manual GHCR step is needed once, after the first push of the package: flip its visibility to Public in the package settings; subsequent pushes inherit it. scripts/set-versions.mjs picks up the GHCR-rooted regex so the example Dockerfiles stay in lockstep with the npm version on every tag bump.
PreviousNext