Repository navigation
fix(ollama-image): upload the weights once, through a registry build cache; publish by reference - #36
Merged
Merged
Conversation
…cache; publish by reference The first main run of the moved lane (40493629457) was cut at 45 minutes inside the push. The step log splits the job as: free disk space 4.6 min, build 4.5 min (bake.sh 3.4), layer export/compression 3.9 min, unpacking into the local store 3.9 min, then the push — the ~24 GB model layer still uploading after 28.1 min. Core's one green publish (40232022675, same runner type, same flags) pushed it in 880 s. The root: every publication rebuilt the image from nothing and re-uploaded the whole weights layer, because nothing was ever reused — the layer is not reproducible (file times) and no build cache existed (Deploy's BuildKit state starts cold; core's never exported one either). So a publish job's duration was bound to one ~24 GB upload at whatever rate the network gave that day. - New `cache` job: builds with --cache-from and --cache-to a REGISTRY cache (mode=max, zstd, in the image's own repository, `buildcache-<line>`), --output type=cacheonly (no image load, no unpack). Warm: every step CACHED, nothing uploaded. Cold: the weights go up once, in a job that does nothing else. - `publish` needs `cache`, builds --cache-from it and pushes by reference: unpack=false, and no force-compression (the cached layers are zstd; forcing would fetch every lazy blob to recompress). Proved locally (Docker 29.5, buildx 0.35, containerd store, registry:2, a 300 MB random layer): a FRESH builder with only --cache-from pushed in 1 s and the image's big layer IS the cache's blob; without --cache-from the same build produced a different blob (negative control). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.
…nstruction for a published commit Review of #36: 1. Overlapping main runs re-point the shared `buildcache-<line>` tag between one run's export and another's publish import, so publish could miss and re-upload ~24 GB. The cache job now writes the shared tag (warm start only) AND a run-specific tag, reads that manifest back and hands publish its DIGEST (`cache_ref`, from the served bytes); publish imports only that and refuses a hand-over that is not `@sha256:`. 2. An already-published commit now skips cache construction entirely: the existence check runs first in the cache job, and disk clean-up, the cache build and the hand-over are gated on it, so a rerun reaches converge-line even if the cache was pruned. `acr-data-plane.py check-cache-handover` (6 self-test cases, both review shapes as negative controls) runs in service-images-guard.yml on all four lanes; the previous head (b7ff076) is red under it on 6 counts. Proved locally against registry:2: importing the run cache by digest stays fully CACHED after the shared tag was overwritten; importing the overwritten shared tag rebuilt the layer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the pattern Review of #36: the guard passed whenever `*@sha256:*` appeared, so deleting the refusal arm or turning its `exit 1` into `true` stayed green while a non-digest hand-over would be built from. It now parses the `case "$CACHE_REF" in … esac`: the case must test $CACHE_REF, carry a `*@sha256:*)` arm, and its catch-all `*)` arm must exit with a literal non-zero status. Self-test adds both review mutations (arm deleted, `exit 1` -> `true`), `exit 0`, a case on another variable and a missing digest arm as negative controls, plus the real arm as a positive one. Applied to the real ollama-image.yml, both mutations are red. 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.


What & why
The first main run of the moved ollama lane (https://github.com/Systemorph/MeshWeaver.Deploy/actions/runs/40493629457) was cut at 45 minutes inside the push. ocr, whisper and s1-extract published on the same commit, so the identity, the data-plane login and the tag-list read are proven. Only ollama's upload didn't finish.
Where the time went (job
Build and push ollama to ACR, step log):bake.sh201 s / 182 s)Both runs used the same runner type (
ubuntu-latest), the same flags and the same registry. Neither had any build cache: core never exported one, and Deploy's BuildKit state starts cold. The other three lanes had finished pushing by 11:15:54Z, and ollama's push began at 11:23:39Z, so they did not compete. The logs do not show why this upload was at least twice as slow: BuildKit prints no per-blob progress. I did not establish that cause.The root. Every publication rebuilt the image from nothing and re-uploaded the whole ~24 GB weights layer. The layer is not reproducible (file times), and there was no cache to reuse it from. So the job's duration was bound to one huge upload at whatever rate that run got. Raising the bound would not change that, so this PR removes the repeated upload instead.
The change (
ollama-image.yml)cachejob. It runs--cache-from+--cache-toagainst a registry cache:type=registry,ref=<acr>/ollama:buildcache-<line>,mode=max, zstd,image-manifest=true, stored in the image's own repository. It uses--output type=cacheonly, so there is no image load and no unpack.publishnowneeds: cache. It builds--cache-fromthat cache and pushes by reference.unpack=false: a publish never runs the image, and unpacking cost 3.9 min of the cut run. There is noforce-compression: the cached layers are already zstd, and forcing it would fetch every lazy blob to recompress it.converge-line.check-workflowandcheck-line-convergenceare green, and so are timeouts (35 jobs), YAML keys and secret preflight. actionlint is clean.Proof (local, not CI)
Setup: Docker 29.5, buildx 0.35, the containerd image store (the lane's shape),
registry:2, and a 300 MB random layer standing in for the weights. Run against a fresh, empty registry:cachejob: rc 0, no image loaded or unpacked;RUNisCACHED;publishon a FRESH builder (cache pruned, no local image) with only--cache-from: pushed in 1 s, and the image's big layer is the cache's blob (sha256:13e24934815e…, 314,587,417 bytes,tar+zstd). The bytes are referenced, not re-uploaded;--cache-fromproduces a different blob.Is dispatching after the fix safe?
No dispatch is needed. The merge commit touches
ollama-image.yml, so the push trigger runs the lane on it. A dispatch would be safe anyway:e4cfec6never wrote its tag (cut before the manifest), so nothing exists to overwrite.converge-lineonly names published tags in main order.cachejob uploads the ~24 GB once, after about 12 min of setup, build and compression. That leaves about 33 min for the upload, against 14.7 min on core's run and more than 28.1 min on the cut run. If the upload is again that slow, thecachejob is what gets cut, and nothing is published or half-written. Every run after a successful cache upload (new commit, rerun, dispatch) skips the 24 GB.Not verified
registry:2, not ACR.buildcache-<line>(they are disabled or dry-run today). A pruned cache only costs a cold rebuild.🤖 Generated with Claude Code