deploy: make the cold-boot hydrate survive the container healthcheck - #113
Conversation
The root Dockerfile already builds and deploys q2-cockpit, so no new deploy
surface was needed — but the S3 hydrate I added would have failed the deploy it
was meant to enable.
THE DEFECT, MINE. ensure_slab_local().await (main.rs:194) blocks
TcpListener::bind (main.rs:346). On a cold boot that is ~60-90s of transfer
before the port opens, and HEALTHCHECK had `--interval=30s --timeout=3s` with NO
`--start-period` — so Docker probes immediately and the default 3 retries mark
the container unhealthy inside ~90s. The unhealthy window overlapped the hydrate
window exactly: the deploy would have failed while doing precisely what it was
supposed to do.
Fixed with `--start-period=600s`. Failures during the start period do not count,
so this only widens the window for a legitimately slow FIRST start, and the cold
path runs once per VOLUME rather than once per deploy — persisting across
rebuilds is the volume's whole purpose.
It is not masking a hang, and that is verified rather than asserted. Pointed at
a nonexistent prefix:
11:25:12 INFO osm slab: resolving from S3 (cold boot transfers ~1.35 GB ...)
bucket=... prefix=q2/bakes/does-not-exist dir=.../probevol/osm
11:25:13 ERROR osm slab: SHA256SUMS not readable - 404 ... in 765.929422ms
(server still starts and serves)
A misconfiguration costs 0.8s and names the exact URL. Only a real transfer is
slow.
Also added the boot line ABOVE, emitted BEFORE the transfer. Without it a cold
start logs nothing for 60-90s, which is indistinguishable from a hang for
whoever is watching a deploy — the same observability gap as the q2_cockpit
filter bug, one layer up.
CHECKED RATHER THAN ASSUMED, on the existing Dockerfile:
toolchain it pins 1.94.0, but rust-toolchain.toml pins nightly-2026-04-28
and rustup honours the toml — so the build already resolves to a
toolchain satisfying ogar-vocab's >=1.95 floor. Not a mismatch.
TLS object_store resolves to rustls (hyper-rustls), not native-tls, so
the S3 leg adds no OpenSSL requirement to the runtime image.
features deploy builds --features embed-cockpit,planner; planner is already
default and embed-cockpit only gates include_dir for static assets.
Neither touches the hydrate path.
Railway needs the five AWS_* vars (already set), RAILWAY_VOL, and a volume of
>=2 GB — 1.35 GB of artifacts plus headroom for a .part during re-fetch, which
transiently doubles one file.
90 passed, 0 failed, 3 ignored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe runtime Docker health check now waits 600 seconds before counting failures. ChangesCold-start hydration readiness
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: Poem
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_5a26b3a5-1733-4a42-9cad-9db7144b5be1) |
The root
Dockerfilealready builds and deploysq2-cockpit— no new deploy surface was needed. But the S3 hydrate merged in #112 would have failed the very deploy it exists to enable.⚠ The defect, mine
ensure_slab_local().await(main.rs:194) blocksTcpListener::bind(main.rs:346). On a cold boot that is ~60–90 s of transfer before the port opens — andHEALTHCHECKhad--interval=30s --timeout=3swith no--start-period, so Docker probes immediately and the default 3 retries mark the container unhealthy inside ~90 s.The unhealthy window overlapped the hydrate window exactly: the deploy would have failed while doing precisely what it was supposed to do.
Fixed with
--start-period=600s. Failures during the start period don't count, so this only widens the window for a legitimately slow first start — and the cold path runs once per volume, not once per deploy, since persisting across rebuilds is the volume's whole purpose.It is not masking a hang — verified, not asserted
Pointed at a nonexistent prefix:
A misconfiguration costs 0.8 s and names the exact URL. Only a real transfer is slow.
The boot line above is also new, and emitted before the transfer. Without it a cold start logs nothing for 60–90 s, which is indistinguishable from a hang for whoever is watching a deploy — the same observability gap as the
q2_cockpitfilter bug, one layer up.Checked rather than assumed
1.94.0, butrust-toolchain.tomlpinsnightly-2026-04-28and rustup honours the toml — the build already resolves to a toolchain satisfyingogar-vocab's ≥1.95 floor. The apparent mismatch is not one.object_storeresolves to rustls (hyper-rustls), not native-tls — the S3 leg adds no OpenSSL requirement to the runtime image.--features embed-cockpit,planner.planneris alreadydefault;embed-cockpitonly gatesinclude_dirfor static assets. Neither touches the hydrate path.What Railway needs
AWS_ENDPOINT_URLAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_DEFAULT_REGIONAmazonS3Builder::from_env()AWS_S3_BUCKET_NAMEwith_bucket_nameRAILWAY_VOL<vol>/osm/holds the cacheVolume needs ≥ 2 GB — 1.35 GB of artifacts plus headroom for a
.partduring re-fetch, which transiently doubles one file.OSM_SLAB_S3_PREFIXoverrides the defaultq2/bakes/berlin-v1;OSM_SLAB_CACHE_DIRoverrides the volume root and is what made this testable off-Railway.Verification
cargo test --bin q2-cockpit— 90 passed, 0 failed, 3 ignored.Still unverified: the literal
/volume01mount and a real container build (no Docker daemon here). First deploy tests both — and thanks to the tracing fix and the new boot line, a failure will now say why instead of producing silent 503s.🤖 Generated with Claude Code
https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
Generated by Claude Code
Summary by CodeRabbit
Bug Fixes
Documentation