MILAB-6871: size analyze scratch space from the sample's reads - #213
Merged
Conversation
Analyze now asks the backend for disposable disk space instead of writing its temporary files into the working directory on shared storage. --use-local-temp is dropped, since TMPDIR points at that storage. The size is 11 x millionsOfReads x (averageReadLength / 1000) ^ 2 GiB, rounded up. One read end is measured with 'seqkit stats' and read back through 'csvtk'; read ends are symmetrical, so one describes the whole sample. The measurement and the formula live in the new read-stats library. The run moved to the mixcr-run template because a template cannot read the result of a command it started itself; mixcr-analyze keeps its name, inputs and outputs, so process.tpl.tengo is unchanged. Both templates carry fresh hash overrides: the old one pinned the template hash and would have served cached results from the previous code. Requires workflow-tengo 6.9.0.
…ng it mixcr-analyze keeps its original hash_override, its shape and its place as the template holding the run, so commands that already finished still deduplicate and recover. It gains three things: the read-stats import, the scratch size read back from its new readStats input, and that size in the resources it already built. The measuring wrapper moves out to measure-and-analyze, which process.tpl.tengo now renders. It carries its own fresh hash_override, being a new template.
…enders The previous commit put the wrapper under a new name, which changed the template pframes.processColumn renders and so changed the dedup key for every group. An analysis that had already finished would have run again. Swap the two: mixcr-analyze is now the wrapper that measures and renders the run, keeping the hash override it has always carried and the inputs processColumn has always passed, so a finished analysis still resolves to its result. That is safe because scratch space changes only where MiXCR writes its temporary files, never what it produces. The run moves to run-mixcr with a freshly generated override - never the one mixcr-analyze uses, since a shared override declares two templates interchangeable. process.tpl.tengo goes back to its original text. The inner render passes the CPU and memory overrides as meta inputs, matching how process.tpl.tengo passes them in, so a run can still be recovered across a change of either.
… terms
S = 2.0 x millionsOfReads x (lengthOfR1 + lengthOfR2) / 1000
C = 11 x millionsOfReads x (lengthOfTheLongestRead / 1000) ^ 2
GiB = max(S, C)
S is two bytes per base of the pair, so it tracks the volume of the input. C is
quadratic in read length and overtakes S only on long reads. The previous
formula was C alone with the average read length, which under-sized every short
read sample: the reference 72.6M x 151bp pair asked for 18GiB where the volume
term wants 44GiB.
Both read ends are measured now, in one seqkit pass: the volume term needs their
lengths separately and the longest read may sit in either. Bases are summed
rather than averaged, which is exact and needs no weighting when a sample spans
lanes, and the read count comes from R1 alone, since it counts pairs.
read-stats.test.tengo covers which term wins, single-end input, multi-lane
summing, the longest read crossing ends, the round up to a whole GiB, and the
cases that must ask for nothing.
seqkit parallelises across files, not within one, so the request follows their number and no core idles. Restores the rationale the STATS_CPU comment carried.
600M single-end reads of 1000bp ask for 6600GiB, well under the 16Ti that AWS deployments configure as --k8s-max-scratch-request. The quadratic term decides at that shape, so this is where a runaway would show first; the test fails if a change to the formula pushes a plausible sample into the clamp, where it would silently get less space than it was sized for.
DenKoren
force-pushed
the
MILAB-6871_mixcr-scratch-space
branch
2 times, most recently
from
September 8, 2026 07:32
76aff3c to
b1f69ad
Compare
MILAB-6721 rotated the hash override because the memory request formula changed. That was a mistake - a resource formula changes what a run asks for, never what it produces - and it never shipped: every released block up to v2.22.0 carries D70EDB25, and AD2092AB exists only on main. Left alone it would have re-analysed every sample in every project on the next block update. Reverted to D70EDB25 and documented what the value means above it, in both templates that carry one.
DenKoren
force-pushed
the
MILAB-6871_mixcr-scratch-space
branch
from
September 8, 2026 07:36
b1f69ad to
78f93ee
Compare
PaulNewling
approved these changes
Sep 8, 2026
Dropping the flag assumed TMPDIR points at scratch storage. A backend that does not understand the request leaves it pointing at the small temporary directory every job shares, which is not enough for MiXCR to sort in, so the flag is restored there and the block behaves exactly as it did before this change. Gated on feats.hasScratchSpace, which is false both when the backend does not report the flag and when it is too old to answer at all. The same flag stops the seqkit pass: a measurement no backend can use is not worth a full decompression of every sample.
DenKoren
enabled auto-merge
September 8, 2026 07:54
PaulNewling
approved these changes
Sep 8, 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.
What
mixcr analyzenow asks the backend for disposable disk space sized from the sample's own reads, instead of writing its temporary files into the working directory on shared storage.--use-local-tempis dropped, sinceTMPDIRpoints at that storage.Size
Sis two bytes per base of the pair, so it tracks the volume of the input.Cis quadratic in read length and overtakesSonly on long reads.None of those numbers is in the column metadata, so both read ends are measured in one pass before the run:
--threadsreads the granted core count back from{system.cpu}, so it tracks the allocation rather than repeating a literal. Bases are summed rather than averaged — exact, and needs no weighting when a sample spans lanes. The read count comes from R1 alone, since it counts pairs.Shape
workflow/src/read-stats.lib.tengoworkflow/src/run-mixcr.tpl.tengomixcr-analyzebody, plus areadStatsinputworkflow/src/mixcr-analyze.tpl.tengorun-mixcrworkflow/src/process.tpl.tengoThe split is forced, not cosmetic: a template cannot read the result of a command it started itself, and
exec.formulaonly knowssize/lineCounton files.Deduplication is preserved
pframes.processColumnstill rendersmixcr-analyzewith the same inputs, andmixcr-analyzecarries thehash_overrideevery released block carries. The per-group dedup key is therefore what it has always been, so an analysis that already finished resolves to its existing result instead of running again. That is safe because scratch space changes only where MiXCR writes its temporary files, never what it produces.run-mixcrcarries a freshly generated override — never the onemixcr-analyzeuses, since a shared override declares two templates interchangeable to the backend.The inner render passes the CPU and memory overrides as meta inputs, matching how
process.tpl.tengopasses them in viametaExtra, so a run can still be recovered across a change of either.Also reverts an unreleased override rotation
86d727d(MILAB-6721) rotatedmixcr-analyze's override fromD70EDB25toAD2092ABbecause the memory request formula changed. That was a mistake — a resource formula changes what a run asks for, never what it produces — and it never shipped: every release up tov2.22.0carriesD70EDB25, andgit tag --contains 86d727dis empty. Left in place it would have re-analysed every sample in every project on the next block update.Verified on
lab.research.platforma.bio: a dev build offmainre-ran samples the released block had already processed, and the DB showedAD2092ABappearing for the first time on that instance the moment the dev block loaded. This PR reverts the value and documents above it, in both templates that carry one, that changing it means "re-run every sample on the next block update".Failure behaviour
The request is an optimisation, never a precondition. A deployment that cannot serve the size shrinks or grows it, one with no scratch storage ignores it, and a sample with no read end to measure asks for nothing. Analyze runs either way.
Tests
workflow/src/read-stats.test.tengoruns on the tengo VM viapl-tengo testand covers which term wins, single-end input, multi-lane summing, the longest read crossing ends, the round up to a whole GiB, and every case that must ask for nothing.Verified on a live cluster
Ten samples through
denkoren-test(EKS, EBS gp3 scratch class), against the earlier single-term formula. Requests were read from the backend'scompute request was registeredlog line and came out per-sample distinct — 20, 22, 23, 23, 24, 24, 24, 25, 26 GiB — each then raised to the deployment's--k8s-min-scratch-request=32Gifloor, which is the EBS gp3 IOPS-ratio minimum for that storage class. The formula in this PR sizes those same samples at roughly twice that, so most will now clear the floor on their own.Dependencies
@platforma-sdk/workflow-tengo6.8.2 -> 6.9.0 — first release carryingscratchFreeSpace@platforma-open/milaboratories.software-binary-collection1.0.12 — new, suppliesseqkitandcsvtk