Skip to content

feat(launch): --max-concurrent-auto derives sweep concurrency from real quota - #493

Merged
scttfrdmn merged 2 commits into
mainfrom
feat/492-quota-derived-max-concurrent
Aug 10, 2026
Merged

feat(launch): --max-concurrent-auto derives sweep concurrency from real quota#493
scttfrdmn merged 2 commits into
mainfrom
feat/492-quota-derived-max-concurrent

Conversation

@scttfrdmn

@scttfrdmn scttfrdmn commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New --max-concurrent-auto flag: derives the parameter-sweep concurrency ceiling from the account's real AWS quota headroom instead of a user-typed number.
  • The wave/staged-launch mechanism this feeds already exists (pkg/sweep + lambda/sweep-orchestrator's runPollingLoop, which polls active-instance count and launches min(available, remaining)) — this closes the actual gap, which was that the ceiling was never derived from anything real.
  • Queries truffle's quota client for headroom (quota − current usage) per instance family in the sweep's resolved launch region, converts vCPU headroom → instance count via Capabilities.VCPUs (truffle v0.49.0).
  • A heterogeneous sweep's ceiling is the minimum across every distinct (instance type, spot/on-demand) combination present — verified via TestResolveAutoMaxConcurrent_TightestFamilyWins, which found real behavior different from what I initially assumed: an unseeded/zero-quota family correctly drags the WHOLE sweep's derived ceiling to 0 (and thus an error) rather than being silently outvoted by a roomier family. That's the safety-correct behavior — a sweep shouldn't launch instances of a type whose quota headroom was never actually verified.
  • Mutually exclusive with --max-concurrent (validated, matching the existing --detach/--no-detach pattern).
  • Wired in before the detached/non-detached branch point, since a sweep auto-enables --detach and that branch needs a concrete maxConcurrent > 0 to take the Lambda-orchestrated path.

Fixes #492.

Not in scope (follow-up)

spawn resume's --max-concurrent override has the same class of gap. Didn't extend this PR to cover it — resume already has a recorded region from the original launch (an easier retrofit than the initial-launch path this PR touches), so it's a smaller, separate change.

Test plan

  • TestSweepQuotaCombos_* (4 tests) — pure combo-extraction logic: dedup, base-config fallback, base-Spot inheritance, missing-instance-type/empty-params errors
  • TestResolveAutoMaxConcurrent_* (3 tests, Substrate-backed): derives the correct ceiling from a real seeded quota (32 vCPU Standard / 4 vCPU per c5.xlarge = 8), the tightest-family-wins safety behavior described above, and the region-must-be-resolved-first invariant
  • Full go build ./... / go test ./... (repo-wide) verified against the REAL released truffle v0.49.0 (not the local checkout the draft version used) — zero FAILs, and TestCatalogValid (previously blocked by a local-overlay artifact unrelated to this PR, see spawn#489/libs#38) now passes clean
  • golangci-lint run ./cmd/... — 19 pre-existing issues, all confirmed present in unmodified code, zero in the new files
  • make check-docs — caught the new flag correctly, docs-gen/launch.md regenerated and committed

…al quota

The sweep orchestrator's wave mechanism (pkg/sweep +
lambda/sweep-orchestrator) already polls active-instance count and
launches min(available, remaining) -- a real batch/wait-for-room
primitive. But its ceiling (state.MaxConcurrent) was always a number
the caller had to already know, via --max-concurrent or the arbitrary
default-10 fallback when a sweep auto-enables --detach.

--max-concurrent-auto queries truffle's quota client for headroom
(quota - current usage) per instance family, in the region the sweep
is about to launch in, and converts vCPU headroom to an instance
count via the real per-type vCPU count (truffle#134's new
Capabilities.VCPUs, not a guessed size suffix). A heterogeneous
sweep's derived ceiling is the MINIMUM across every distinct
(instance type, spot/on-demand) combination present in the sweep, so
a scarce family can't be silently outvoted by a roomier one.

Real-world motivation: a 10-shard fleet launch with no concurrency
guardrail hit an account's real ceiling (a G/VT Spot quota of 64
vCPUs, saturated by 8 running g7e.2xlarge instances) with zero prior
warning; the actual launches then failed with
MaxSpotInstanceCountExceeded.

Not yet wired into `spawn resume`'s equivalent override -- tracked as
a follow-up, since resume already has a recorded region and is a
smaller retrofit.

BLOCKED: go.mod carries a TEMPORARY local replace directive for
truffle, since this code depends on truffle#133 (QuotaInfo.SpotUsage)
and truffle#134 (Capabilities.VCPUs), both unreleased. Must be
resolved to a real tag before merge.

Fixes #492.
truffle v0.49.0 (carrying #133 QuotaInfo.SpotUsage and #134
Capabilities.VCPUs) is now released, so this PR no longer needs the
local-path replace directive. go.mod now pins the real tag; verified
the full build/test suite (including TestCatalogValid and the new
sweep-quota tests) against it, not the local checkout.
@scttfrdmn
scttfrdmn marked this pull request as ready for review August 10, 2026 22:38
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 48.75000% with 41 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/launch_sweep.go 0.00% 23 Missing ⚠️
cmd/launch_sweep_quota.go 67.85% 9 Missing and 9 partials ⚠️

📢 Thoughts on this report? Let us know!

@scttfrdmn
scttfrdmn merged commit 4d6acf7 into main Aug 10, 2026
7 checks passed
@scttfrdmn
scttfrdmn deleted the feat/492-quota-derived-max-concurrent branch August 10, 2026 22:48
scttfrdmn added a commit that referenced this pull request Aug 11, 2026
--max-concurrent-auto (#492/#493) is a backward-compatible feature
addition with no breaking changes, so this is a MINOR bump per the
pre-1.0 SemVer policy.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sweep orchestrator's --max-concurrent is user-typed, not quota-derived — no automatic wave sizing to fit a real account ceiling

1 participant