Skip to content

runner.py lambda orchestrator: default max_workers exhausts client file descriptors, silently dropping cells #28

Description

@espg

Problem

_run_lambda (src/zagg/runner.py:452) fans out via ThreadPoolExecutor(max_workers=max_workers), where each worker holds an open socket to the Lambda endpoint for a synchronous invoke. The lambda-backend default is max_workers=1700 (and the CLI accepts arbitrary values).

On stock macOS / many Linux login shells the open-file limit (ulimit -n) defaults to 256. Once concurrent workers exceed it, invocations fail with:

[Errno 24] Too many open files
Could not connect to the endpoint URL: ".../functions/process-shard/invocations"

This is a client-side failure — AWS never receives those cells, so they're dropped from the output while the run still "completes." In practice --max-workers 900 lost ~430/1330 cells; even --max-workers 500 silently caps at ~246 concurrent (≈ the 256 FD limit) if ulimit isn't raised.

Why it's a footgun

  • The default (1700) is far above any common ulimit -n, so the default invocation can fail on a fresh machine.
  • The error reads like an AWS/network fault, not a local FD limit.
  • Wall-clock is gated by the slowest single cell anyway, so high concurrency has little upside — the risky default buys nothing.

Proposed fixes (some combination)

  1. Saner default — clamp default max_workers to something safe on a 256 FD limit (e.g. 128–200), or derive from resource.getrlimit(RLIMIT_NOFILE) minus headroom.
  2. Bounded connection reuse — share one boto3 client with botocore.config.Config(max_pool_connections=...) across threads so FD use is bounded regardless of worker count.
  3. Pre-flight — read ulimit -n at startup; if max_workers exceeds it, raise the soft limit via resource.setrlimit (up to hard) or clamp + warn clearly.
  4. Clear error — catch OSError: [Errno 24] and surface "raise ulimit -n or lower --max-workers" instead of the raw connection error.

Workaround (documented)

ulimit -n 8192 before running; keep --max-workers ≤ min(ulimit -n − headroom, account concurrency). See docs/deployment/lambda.md → "Concurrency, workers, and file descriptors".

Metadata

Metadata

Assignees

No one assigned

    Labels

    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