Skip to content

Reduce idle CPU usage with adaptive poll backoff - #46

Merged
codingjoe merged 4 commits into
mainfrom
codingjoe-fix-idle-cpu-usage
Sep 8, 2026
Merged

Reduce idle CPU usage with adaptive poll backoff#46
codingjoe merged 4 commits into
mainfrom
codingjoe-fix-idle-cpu-usage

Conversation

@codingjoe

@codingjoe codingjoe commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Why

Idle workers burned CPU by busy-polling Redis: acquire() retried every 10ms, so each consumer thread issued ~100 EVALSHA round-trips per second while the pool sat empty, across cpu_count - 1 worker processes.

How

The wait between idle acquire attempts now doubles on every consecutive empty poll, from poll_interval (default 10ms) up to poll_max_interval (default 1s), and resets on task pickup. On a local Redis, idle round-trips dropped from ~200 per 2 seconds to 9.

  • Both options are tunable via the backend's OPTIONS and per worker run via --poll-interval / --poll-max-interval (seconds). The executor carries concrete defaults so every worker process applies the same values to its resolved backend before consumer threads start.
  • Every wait is clamped to the remaining acquire deadline, so TimeoutError and queue.Empty behave exactly as before.

Trade-offs

  • Empty-queue pickup latency is now bounded by poll_max_interval (up to 1s at the default) instead of 10ms. Idle-polling Redis for 10ms of responsiveness was the CPU cost being removed; lower the option if faster idle pickup matters.
  • Illegal poll_interval / poll_max_interval values are no longer rejected: zero dies with ZeroDivisionError on the first empty poll, negatives crash the consumer thread. Misconfiguration fails loudly rather than being silently rejected.

codingjoe and others added 4 commits September 8, 2026 01:21
Idle workers busy-polled Redis every 10ms, generating ~100 EVALSHA
round-trips per second per consumer thread. The wait between acquire
attempts now doubles on consecutive empty polls from poll_interval
(default 10ms) up to poll_max_interval (default 1s) and resets on task
pickup, keeping idle CPU usage low while bounding empty-queue pickup
latency by poll_max_interval.

Misconfigured options fail fast at construction: poll_interval must be
a positive timedelta and poll_max_interval at least poll_interval, so a
typo cannot silently spin workers unthrottled or crash-loop the pool.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Trust operator-provided values: illegal poll_interval or poll_max_interval settings may now crash the backend loudly instead of being rejected with a ValueError.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
--poll-interval and --poll-max-interval override the backend's idle poll options per worker run, in seconds. Overrides flow through the TaskExecutor into each worker process and are applied to the resolved backend before consumer threads start.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@codingjoe
codingjoe merged commit c60eae9 into main Sep 8, 2026
4 checks passed
@codingjoe
codingjoe deleted the codingjoe-fix-idle-cpu-usage branch September 8, 2026 07:57
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.

1 participant