Skip to content

Follow-ons to #183: dispatcher-side shard-aware granule_workers, and function-size variants gated on utilization #184

Description

@espg

🤖 from Claude

Follow-up to PR #183 (granule-level read concurrency, Closes #180 / #179), filed at espg's direction. Two items were deliberately left out of that PR; this issue captures them so they don't live only in the PR's "Questions for review". Both are gated on the fleet A/B at K∈{1,2,4,6} (issue #180 phase 3, espg-side): the per-invocation cpu_seconds telemetry that PR #183 adds is what produces the utilization data these decisions key on.

1. Dispatcher-side shard-aware K (per-shard granule_workers override)

PR #183 ships data_source.granule_workers as a config-level knob only: one K for the whole run. The spec (issue #180 phase 2) sketched the finer version:

Shard-aware K (free 80% of "shardmap-scoped workers"): the dispatcher can thread a per-shard granule_workers override into the cell event from the shardmap's granule counts — precedent: the aoi_payload per-cell extra in runner.py's _cell_work.

The worker side already accepts event-injected values (the plumbing seam exists in PR #183); nothing sets it yet. The point: shardmaps record granule counts per shard, so the dispatcher can clamp K to min(K_config, n_granules) — a 5-granule shard shouldn't spin up K=6 threads — and could go further (scale K down on the densest shards where RSS headroom at 2 GB is the binding constraint, per issue #117).

Scope sketch: runner.py _cell_work threads granule_workers into the cell event alongside aoi_payload; simplest policy first (min(K, n_granules)), anything cleverer only if the A/B shows shard heterogeneity matters. Tests: dispatcher unit test on the event payload; worker already validates event values.

Gate: worth doing at all only if the A/B lands on a default K > 1.

2. Function-size variants (different-size Lambdas) — separate, harder-gated decision

From issue #180's follow-on section, espg's criterion verbatim:

Lambda bills GB-seconds → bigger workers (vCPU ∝ memory, ~1/1769 MB, max 6) are cost-neutral whenever wall drops proportionally. Scale up only once K-in-flight work demonstrably saturates 2 vCPUs ("I don't want a 16-core lambda unless we hammer the CPUs" — espg). If shard heterogeneity warrants, function-size variants (2/4/7 GB) + dispatch routing by granule count.

Decision tree from the A/B's cpu_seconds/wall/RSS data:

  1. Wall keeps dropping with K, utilization stays low (latency-bound reads) → raise K, keep 2 GB, close this item as not-needed.
  2. RSS caps K before CPU does (memory-bound; K multiplies in-flight granule footprint ~20–40 MB decoded + digest state, and dense shards are already marginal at 2 GB per Order-10 single-shard processing OOMs at 2 GB (gain_bias) — 2 GB is marginal for order-10 #117) → one probe run at 3–4 GB: the memory bump buys K headroom and vCPU roughly for free if wall follows.
  3. Utilization approaches the vCPU count (CPU-bound decode/aggregation) → the real design discussion: 2/4/7 GB function variants + dispatch routing by granule count (which composes with item 1 — the same shardmap granule counts drive both the K override and the size routing).

Gate: hard-gated on the A/B utilization data crossing the hammer-the-CPUs bar; do not start this on speculation. Touches deployment/aws/template.yaml (new function resources) — per repo convention that work needs an issue naming it, which this issue does, but the CloudFormation/deploy execution stays espg-side.

Refs

(Left unlabeled for espg to triage — add plan/implement when the A/B data is in.)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions