fix(assets-sync): split commit_batch to fit ingress message limit#68
Merged
Conversation
raymondk
approved these changes
Jun 1, 2026
719f27a to
b092222
Compare
Large deploys (e.g. ~1700 files with a multi-kilobyte `Content-Security-Policy` declared in `_headers`) build a single `commit_batch` payload that exceeds the IC's 2 MiB per-message ingress limit on application subnets, surfacing as a 413 Payload Too Large from the replica. Split `commit_batch` into multiple ingress calls when the operation set would exceed the limit. Per-group caps: - 500 operations — bounds certified-tree work per call and limits the blast radius of a mid-deploy failure. - 1.5 MiB of inlined header bytes — leaves ~500 KiB of headroom under the 2 MiB cap for fixed per-op overhead (keys, chunk_ids, sha256s, variant tags, request envelope). Header bytes are the only variable-sized per-op field and are where real-world overruns come from. Intermediate splits use a placeholder `batch_id = 0`; a trailing empty-ops call on the real batch_id releases the canister-side batch entry. The canister's `commit_batch` does not validate batch existence and consumes chunk_ids regardless of which batch created them, so the chunks uploaded once under the real batch_id remain reachable across all splits — only `create_batch` GCs orphaned chunks. Small deploys still use a single `commit_batch` with the real batch_id; the placeholder dance only kicks in when splitting is actually needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddc9077 to
0cc61e0
Compare
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.
Problem
Large deploys hit the IC's per-message ingress size limit on
commit_batch. A real example from a downstream project: 1713 files with a multi-kilobyteContent-Security-Policydeclared in_headers, producing 4843 batch operations. The boundary node rejects the call with:The 4 MiB number is the local boundary node's HTTP body cap; the real limit on production application subnets is 2 MiB per ingress message (
MAX_INGRESS_BYTES_PER_MESSAGE_APP_SUBNETindfinity/ic/rs/limits/src/lib.rs). Most of the payload bytes come from per-asset headers that_headersresolves to (a 1.5 KiB CSP × 1713CreateAssetops ≈ 2.9 MiB on its own).Fix
Split
commit_batchinto multiple ingress calls when the operation set would exceed the limit.Per-group caps:
Intermediate splits use a placeholder
batch_id = 0; a trailing empty-ops call on the real batch_id releases the canister-side batch entry. The canister'scommit_batchdoes not validate batch existence and consumes chunk_ids regardless of which batch created them, so chunks uploaded once under the real batch_id remain reachable across all splits — onlycreate_batchGCs orphaned chunks.Small deploys still use a single
commit_batchwith the real batch_id; the placeholder dance only kicks in when splitting is actually needed.Same shape of fix that
dfx'sic-asset/src/sync.rsuses for the same problem.Trade-off
Splitting forfeits cross-batch atomicity. A failure mid-deploy leaves the canister with the operations from previously-successful calls applied; the next sync run diffs against the canister and resumes.
Test plan
cargo test -p assets-sync --lib— 177 tests pass, 10 newcargo build -p plugin --target wasm32-wasip2 --release— clean buildicp deployagainst the downstream project that hit the original 413 and confirm it now goes through🤖 Generated with Claude Code