template: every -disk worker at Lambda's 10240 MB /tmp ceiling (issue #536) - #537
Merged
Conversation
espg
marked this pull request as ready for review
August 26, 2026 03:54
This was referenced Aug 26, 2026
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.
Closes #536.
Raises
WorkerDiskTmpto Lambda's 10240 MBEphemeralStorageceiling at every memory size, superseding issue #235'smemory + 2048(under which only the 8192 variant reached the ceiling).Why the rule changed
The thing
/tmpgates does not scale with memory.zagg.processing.spill._default_block_bytesismin(0.2 x memory x K, 0.45 x free_tmp)withK = 4 ** (group_order - parent_order), and K is 64 on the production HEALPix grids:Disk bound the block by 6-23x at every tier. Memory never chose the block size.
The GEDI waveform reducers (
build_waveform_digest,single_shot_value,shot_count,shot_number) have no cross-block fold law, so that config is exact-single-block-only: crossing the threshold raisesSpillOverflowErrorrather than emit a combined-but-wrong result. 249 of 2,726 shards on the CA GEDI build died exactly there, all of them dense (0% failures below 50 granules, 74% in the 80-100 band).Measured, not assumed
The 10 densest failed shards — 98-103 granules, including all four >100-granule shards in the map — re-run on
process-shard-8192-disk(10240 MB/tmp):10/10 succeeded,
spill_blocks_closed = 0throughout (single-block, exact — the correctness condition), peak 3.56 GiB against the 4.50 GiB ceiling: 21% headroom. Since 10240 MB is Lambda's ceiling, 4.50 GiB is the largest spill block obtainable on any Lambda, and the measurement says it covers the whole CA map.Cost
Ephemeral storage above 512 MB bills at $0.0000000309/GB-s. Over a CA-sized run (~681,500 invocation-seconds) the extra 4 GB is $0.08, against $48.65 of compute — 0.2%.
What this does NOT claim
That memory can now be dropped. The spill threshold is memory-independent; the 900 s timeout is not — vCPU follows memory, the config runs
shard_workers: 4, and the dense band measured 673-732 s at 8192 MB, only 19-25% under the wall. Whether 2048 or 4096 clears that band in time is a separate timing test. Both the template comment and the changelog say so explicitly, because "disk parity means capability parity" is the wrong lesson to take from this PR.Phases
Single phase — a three-value mapping change plus the prose and tests that pin the old rule. Splitting it would be artificial file-splitting (CLAUDE.md section 2).
WorkerMemorySizesdescription andMappingscomment rewritten;test_lambda_build.pyassertions updated.How it was tested
pytest -q tests/test_lambda_build.py-> 39 passed, 1 skipped, plus the one failure below.test_foreach_expands_to_six_variantsnow assertstmp_mb == _DISK_TMP[size] == _LAMBDA_EPHEMERAL_CEILING_MBfor all three sizes._DISK_TMPis derived asdict.fromkeys(_SIZES, _LAMBDA_EPHEMERAL_CEILING_MB)rather than restated, so it cannot drift from the constant — and because a derived table would silently agree with a template that regressed, the test also asserts the mapping as literally written in the YAML.test_size_list_and_disk_mapping_stay_in_syncis unchanged and still passes (keys still coverWorkerMemorySizes).ruff check --select=E,F,W,I --ignore=E501 src testsandruff format --check src testsclean.test_lambda_build.py::TestFunctionBuild::test_function_build_succeedsfails locally withNo matching distribution found for zarr>=3.1.5— a pre-existing offline-sandbox failure, unrelated, and green in CI on #535.Deploy notes for whoever runs this — please read before the stack update
Two things found while checking that this needs no release. Neither is part of the diff; both bite at deploy time.
The live stack has drifted.
zagg-backenddeclaresLayerS3Key/FunctionS3Keyat 0.49, while the fleet runs 0.52 code on layer:61, updated outside CloudFormation at 02:25 UTC. Any stack update reconciles that, so this deploy will also re-point code and publish a new layer version. Harmless if the resolved minor is 0.52 — it is what is already running — but it is not the config-only update it looks like.stand_up.shunpinned can downgrade the fleet. It builds version-scoped keys (dist_key-><prefix>/<minor>/<zip>) and resolves the minor fromLAMBDA_VERSION=latest-> explicitLAMBDA_VERSION->git describe --tags-> installed zagg, then passes every parameter explicitly (no "use previous value"). From a feature branchgit describereturns the newest tag reachable from HEAD: onclaude/328-demo-07-minimalthat is 0.47.0, which is staged and would pass every check in the script while silently reverting the fleet past the issue fleet cannot write large objects to source.coop: x-amz-acl is rejected on UploadPart (400 InvalidArgument) — MVP blocker #534 ACL fix.Deploy as
LAMBDA_VERSION=0.52 ./deployment/aws/stand_up.sh, or from a checkout sitting on the 0.52.0 tag.Correctly, no new release is needed: this touches no Lambda code.
Questions for review
2048be at the ceiling too? Included here on the "disk is free, memory is what costs" reasoning, and it makes the mapping uniform. The counter-argument is that a 2048 worker cannot use a 4.50 GiB block within 900 s anyway, so the entry is aspirational. Left uniform because a non-uniform table needs a rule, and the old rule is what this PR removes.