Persistence guard + CI path filters + resumable import#70
Merged
Conversation
…resumable import
The import hung at "123/195" then a refresh showed ZERO projects. Diagnosis: the
MCP container's project store is not on a mounted volume, and CI rebuilt the MCP
image on EVERY merge to main — so Watchtower restarted the MCP container minutes
after a web-only merge, killing the in-flight upload (fetch with no timeout =
infinite hang) and wiping the ephemeral store.
Three layers of fix:
1. Server persistence guard (REQUIRE_PERSISTENT_STORE=1, default in the Docker
image): at startup the server detects a store that is NOT on a mounted volume
(same st_dev as the container root) and refuses ALL writes with HTTP 507 and
an explicit French message, instead of accepting data the next redeploy would
destroy. /health reports it; startup log says FATAL. Dev/stdio unaffected
(flag absent). Verified: 507 on record+blob writes when ephemeral, 200 when
the guard is off.
2. CI path filters: docker-publish-mcp.yml only triggers on mcp-server/**
changes; docker-publish.yml only on web files. Web-only merges no longer
restart the MCP server (which used to kill uploads and SSE streams for
nothing).
3. Import robustness (app.js):
- fetchRetry: every import request now has a hard timeout (90s) + 3 attempts
with backoff — a stalled upload FAILS with a clear message instead of
hanging the overlay forever. 413/507 are not retried (permanent).
- Resumable: images are hashed first (bytes discarded, re-read from the ZIP
at upload time — still one image in memory at a time), then blobs/check
tells which blobs already exist: a re-import after a failure skips
everything already uploaded.
- Explicit error hints for 413/507 (storage not persistent, file too big).
https://claude.ai/code/session_01Q5Cd4M127ug84nhGjkiyLe
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.
Incident
Import hung at "Import des images… (123/195)"; after a refresh, zero projects. Live diagnosis (
list_projects→[]): the MCP container's project store is not on a mounted volume, and CI rebuilt the MCP image on every merge to main — so Watchtower restarted the MCP container minutes after a web-only merge, killing the in-flight upload (fetch with no timeout = infinite hang) and wiping the ephemeral store. Second occurrence of the same root cause.Three-layer fix
1. Server persistence guard (
REQUIRE_PERSISTENT_STORE=1, default in the Docker image)At startup the server detects a store that is not on a mounted volume (same
st_devas the container root — true for bind mounts and named volumes alike) and refuses all writes with HTTP 507 and an explicit message, instead of accepting data the next redeploy would destroy./healthreportspersistentStore:false; startup log saysFATAL. Dev/stdio unaffected (flag absent outside the image).Verified: 507 on record + blob writes when ephemeral (with the full French message), 200 when the guard is off.
2. CI path filters
docker-publish-mcp.yml→ onlymcp-server/**docker-publish.yml→ only web files (*.html/css/js,models/,img/,nginx.conf,Dockerfile,docker/)Web-only merges no longer restart the MCP server (which used to kill uploads and SSE streams for nothing).
3. Resumable, timeout-guarded import (app.js)
fetchRetry: hard timeout (90 s) + 3 attempts with backoff on every import request — a stalled upload fails with a clear message instead of hanging the overlay forever. 413/507 are permanent (no retry).blobs/checkreports which blobs already exist: a re-import after a failure skips everything already uploaded.https://claude.ai/code/session_01Q5Cd4M127ug84nhGjkiyLe
Generated by Claude Code