Repository navigation
fix(storage): address sidecar review follow-ups - #330
Merged
Merged
Conversation
Storage's file backend keeps object content type, cache control and multipart part etags in extended attributes. Docker Desktop's macOS file sharing rejects them with ENOTSUP, so every upload into a bind-mounted uploads directory failed. Replace the bundled fs-xattr package with a wrapper that delegates to the real addon and, on ENOTSUP, stores values in a JSON sidecar under <storage root>/.slim-xattr. Native misses also consult the sidecar so a directory keeps its metadata when served by an engine with xattrs. The build fails if fs-xattr changes its entry point or exports. The image smoke forces the sidecar path, then re-serves the same volume with native attributes and checks the stored headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deletes, new object versions and multipart parts never go through fs-xattr, so their sidecars stayed forever. Docker Desktop dogfooding saw 9,026 sidecars for 492 live objects, and small operations slowed down as the single directory grew. Shard sidecars into 256 subdirectories and sweep entries whose file is gone. The sweep never competes with requests: it starts 30 s after the process loads (each wake from idle sleep is a fresh container), repeats hourly, uses async I/O one entry at a time, and pauses after every 50 entries, longer when the filesystem is slow. A cursor resumes a pass cut short by idle sleep, and a short-lived lock keeps one sweeper per directory. The smoke deletes an object in sidecar mode and checks that a restarted container sweeps its sidecar while live metadata survives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A successful native set drops the sidecar copy of that attribute, like remove already did. Multipart parts are rewritten in place, so a part uploaded without xattrs and re-uploaded with them no longer answers with its old ETag after switching back. - The sweep cursor records the last swept entry at every batch pause, so a pass cut short inside a large shard resumes there instead of restarting the shard. - The sweep lock holds a token, so a process only releases a lock it still owns. - Comments and REPORT.md no longer claim every path is fresh (the Iceberg S3 PutObject route is unversioned), and describe how a relative storage root behaves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r-sidecar-followups # Conflicts: # services/storage/REPORT.md # services/storage/fs-xattr-fallback.test.ts # services/storage/overlay/fs-xattr/index.js
jgoux
approved these changes
Sep 30, 2026
Refresh the sweep lock after each pause as well as before it, so a slow batch cannot let it go stale while the sweeper is still live. Record the remaining multipart etag limitation after two engine switches in REPORT.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
TFSebben
pushed a commit
to TFSebben/supabase_cli
that referenced
this pull request
Sep 30, 2026
…upabase#6921) ## Summary On Docker Desktop for macOS, every upload to a stack's Storage on the Docker runtime failed with 500 `ENOTSUP`. Storage's file backend keeps object metadata in extended attributes, and Docker Desktop's file sharing drops them on bind mounts. This pins the stack's Storage to slim `v1.79.28-r1`, whose `fs-xattr` wrapper falls back to sidecar metadata files when xattrs are rejected. The bind mount at `supabase/.temp/stack-uploads/<stack-id>` stays as it is. This replaces the engine-volume workaround in supabase#6908. ### Before ```mermaid flowchart LR A["Upload to stack Storage<br/>(Docker Desktop macOS)"] --> B["storage v1.73.0<br/>fs-xattr setAttributeSync"] B -->|ENOTSUP on bind mount| C["500 on every upload"] ``` ### After ```mermaid flowchart LR A["Upload to stack Storage<br/>(Docker Desktop macOS)"] --> B["storage v1.79.28-r1<br/>fs-xattr wrapper"] B -->|xattrs work| C["native attributes"] B -->|ENOTSUP| D["sidecar under<br/>stack-uploads/.slim-xattr"] ``` ### What changed - **Storage pin:** `packages/stack/src/Artifacts.ts` moves from `v1.73.0` to `v1.79.28-r1`, pinned by digest. This is the first catalog pin to a revisioned slim release, and the release tag, asset names, native OCI tags and image tag all follow the `v1.79.28-r1` form the catalog already derives. - **Fix location:** the fix lives in the slim build, in supabase/slim-services#329 and supabase/slim-services#330. - Native xattrs stay authoritative wherever they work: Linux, OrbStack and Windows Docker Desktop. - On `ENOTSUP`, metadata goes to `.slim-xattr/<2 hex>/<sha256>.json` under the uploads directory. - A throttled background sweep removes sidecars for deleted objects without blocking requests. - **`supabase services`:** in stack mode it reports catalog versions without the slim revision suffix. A linked project on the same upstream release, for example `v1.79.28`, therefore doesn't show a version mismatch. `SIDE_EFFECTS.md` is updated to match. ### Reviewer notes - The Storage version jump from `v1.73.0` to `v1.79.28` was compared on Docker Desktop against the pinned image: status codes and headers matched across a broad set of Storage API scenarios, both on native volumes and through the sidecar. - Known limitations of the fallback are recorded in slim-services `services/storage/REPORT.md`. - An object uploaded where xattrs work, then read under Docker Desktop, is served as `application/octet-stream`. - A multipart part rewritten across two engine switches within one upload keeps a stale etag. - The legacy `supabase start` Dockerfile pins (`supabase/storage-api:v1.77.0`) are unchanged. It mounts Storage from a named volume, so it isn't affected. ## Linked issue Closes supabase#6900 - [x] The linked issue is **open** and carries the `open-for-contribution` label (or I'm a Supabase maintainer). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
Follow-ups to the non-blocking review notes on #329 (merged). A native write now also clears the stale sidecar copy, the sweep resumes inside a large shard instead of restarting it, the sweep lock is only released by its owner, and two comments and REPORT.md statements are corrected.
Before
After
Why
These address jgoux's review comments on #329:
InvalidChecksum.finally.PutObjectroute is unversioned. They also misdescribed how a relative storage root behaves.What changed
setAttributeSync: after a successful native set it drops the sidecar copy of that attribute, sharing one helper withremoveAttributeSync.<shard>/<last swept entry>at every batch pause and resumes after that entry, walking each shard in sorted order.ENOTSUPremains.Reviewer notes
e668239(same tree as the head before the test commit), including the delete-then-sweep smoke step on both Linux targets: v1.79.27, v1.79.21. The head (7d4a761) also passed a v1.79.27 run.ENOENT, so only one extra syscall per attribute write where xattrs work.Part of supabase/cli#6900
🤖 Generated with Claude Code