Harden bulk retries, parallelize guest uploads, and move guest map below gallery

This commit is contained in:
2026-09-11 18:54:46 -04:00
parent 4bc48db656
commit 1e299a9914
25 changed files with 376 additions and 62 deletions
+37
View File
@@ -10,3 +10,40 @@
unchanged. The final image must pass the Sharp WebP smoke test.
- If memory pressure remains high, move builds to a separate builder rather than
increasing concurrency or running more diagnostic builds on the live VM.
## Coolify recovery and build isolation (2026-09-11)
Docker 29.3.0 crashed with SIGSEGV in its embedded BuildKit mount/read-entrypoint
path during the `4bc48db` deployment. The VM did not reboot, and the application
containers were not OOM-killed. Restarting the existing Manyangles containers
restored service. The trace identifies the failing process, not a confirmed
upstream defect or hardware cause.
- `/etc/docker/daemon.json` now enables `live-restore`. It was validated and
applied with `systemctl reload docker`, without restarting running containers.
The prior configuration is backed up at
`/etc/docker/daemon.json.before-manyangles-recovery-20260911`.
- Manyangles uses the named `manyangles-isolated` buildx builder with the
`docker-container` driver and `moby/buildkit:v0.33.0`, instead of the builder
embedded inside dockerd. Other applications' build selection is unchanged.
- The builder has a 3 GiB RAM limit, 4 GiB combined RAM/swap limit, two-CPU quota,
and two BuildKit execution slots. Images automatically load into the local
Docker image store. Its cache is stored in a dedicated Docker volume.
- Coolify's application-level custom Compose build command is:
```sh
docker compose --parallel 1 --project-name nzuqxqw47tbt117lrpw3f8ch build --builder manyangles-isolated --pull
```
Coolify injects the project directory, Compose file, and build environment.
Builder metadata lives under `/root/.docker/buildx`, which Coolify mounts into
its helper. Do not select this builder globally or prune its volume during builds.
Verify using `docker info` (Live Restore Enabled), `docker buildx inspect
manyangles-isolated` as root, container health, and the public
`https://ma.hadlock.tech/api/health/ready` endpoint. Live restore mitigates daemon
outages; it does not provide zero-downtime Compose rollouts or protect against VM
failure. Do not deliberately crash/restart the shared daemon to test it in production.
References: [live restore](https://docs.docker.com/engine/daemon/live-restore/),
[containerized builders](https://docs.docker.com/build/builders/drivers/docker-container/).
+16 -2
View File
@@ -81,8 +81,22 @@ unhealthy. Long exports can exceed this threshold and should be investigated.
Checks run every 30 seconds, with a 5-second timeout and three retries. No worker
health port is published. Docker health status by itself does not automatically
restart an unhealthy container; it supplies readiness information to Coolify.
# Bulk gallery workflows
## Bulk gallery workflows
Use `manager_photos` with `input.limit`, `input.offset`, optional `input.visibility` and `input.processingStatus` to inspect a page. Capture explicit photo IDs; a bulk request never expands to later uploads. `manager_previewBulkPhotos` checks up to 100 IDs for an action (`public`, `hidden`, `private`, `rejected`, `delete`). Pass eligible IDs to `manager_bulkPhotos` with both `input.confirm=true` and the MCP write wrapper's `confirm=true`. Results are per photo, including skipped/failed items. Permissions are rechecked during execution. Deletion skips photos still uploading or processing, permanently removes originals and variants, and can partially succeed; inspect results before retrying.
For organizer uploads, `manager_createGalleryPhotos` accepts up to 25 file descriptors and returns presigned PUT URLs in input order. PUT each original directly to storage with its matching Content-Type, then call `manager_completeGalleryPhotos` with successful IDs to validate size and queue transcoding. These actions require existing `settings.manage` permission. Photos start pending review, even when guest publication is automatic. Do not blindly retry creation: it creates a new batch. No file bytes pass through MCP or Next.js.
For organizer uploads, `manager_createGalleryPhotos` accepts up to 25 file descriptors and returns presigned PUT URLs in input order. PUT each original directly to storage with its matching Content-Type, then call `manager_completeGalleryPhotos` with successful IDs to validate size and queue transcoding. These actions require existing `settings.manage` permission. Photos start pending review, even when guest publication is automatic. No file bytes pass through MCP or Next.js.
Both `manager_bulkPhotos` and `manager_createGalleryPhotos` require `input.requestId` (a UUID). Generate it once per intentional operation and reuse it with identical input after connection failures. Durable event/user-scoped receipts prevent duplicate creation and prevent an old bulk retry from undoing a newer action. A changed payload with the same request ID is rejected. For a new action, use a new UUID. Successful and skipped per-photo results are replayed; interrupted/failed transactions can retry. Permissions are always checked before replay. The receipts migration must run before deploying this code.
`manager_retryGalleryPhoto` resumes an organizer upload created by the same account. If `uploadUrl` is null, bytes already exist or processing has begun: do not PUT again. Call completion if its status is still `uploading`. Retrying a completed upload never returns a replacement PUT URL. The gallery UI keeps its batch ID and files for retries while the page remains open.
## Full local verification
Start the local web server and Docker development services (including Mailpit), then run:
```sh
POLISH_INTEGRATION=1 GALLERY_STORAGE_INTEGRATION=1 MCP_INTEGRATION=1 EXPORT_INTEGRATION=1 EXPORT_PERMISSIONS_INTEGRATION=1 WEBHOOK_INTEGRATION=1 NOTIFICATIONS_INTEGRATION=1 PUBLISHING_INTEGRATION=1 INVITE_JOURNEY_INTEGRATION=1 bun --env-file=.env test
```
Use only local database/storage and non-production Mailpit. The MCP tests also exercise initialization and tool calls using the official SDK client over loopback HTTP. They do not configure an external assistant app or mint production tokens.