Skip to content

Reduce idle polling CPU for 0.13.1 - #5

Merged
cardmagic merged 1 commit into
mainfrom
agent/idle-polling-backoff
Aug 16, 2026
Merged

Reduce idle polling CPU for 0.13.1#5
cardmagic merged 1 commit into
mainfrom
agent/idle-polling-backoff

Conversation

@cardmagic

@cardmagic cardmagic commented Aug 16, 2026

Copy link
Copy Markdown
Owner

What changed

Coordinated Ruby parity: solid_objects#39

  • back actor, effect, reminder, and broadcast polling off exponentially from the configured fast interval to a one-second idle ceiling
  • reset the interval on processed work and wake-up notifications, while clamping actor waits to the lease-renewal interval
  • expose the current interval and emit transition instrumentation
  • warn once when live processes share the database without a configured cross-process wake-up adapter
  • make in-process, PostgreSQL, and Redis waits distinguish notifications from timeouts while preserving legacy void adapters
  • add a reproducible four-role SQLite idle benchmark and prepare version 0.13.1

Why

Each runtime role previously polled at the configured interval even when the application had no work. Four default roles therefore turned a 100 ms setting into roughly 40 SQLite passes per second, and tighter application settings multiplied the idle CPU cost.

The default wake-up already interrupts waits inside one Node process, so a fixed short interval buys no local delivery latency. Separate processes still need PostgreSQL notifications or optional Redis Pub/Sub for prompt delivery. The new warning and documentation make that topology visible.

Measured impact

Apple M5, Node 26.7.0, in-memory SQLite, four roles, three-second warmup and ten-second sample:

Fast interval Before After
20 ms 188.78 polls/s, 3.254% CPU 4.000 polls/s, 0.129% CPU
100 ms 39.596 polls/s, 0.906% CPU 3.999 polls/s, 0.121% CPU
500 ms 7.999 polls/s, 0.251% CPU 3.999 polls/s, 0.104% CPU

Five post-idle SQLite samples kept one-process p50 latency effectively flat: 2.589 ms before and 2.662 ms after. A deliberately worst-case polling-only second process moved from 107.945 ms to 1,006.232 ms p50; this is the documented reason to configure a cross-process adapter when low latency matters.

Compatibility

pollingIntervalMilliseconds is now the fast interval after activity. Existing explicit values back off to idlePollingIntervalMilliseconds, which defaults to 1,000 ms. Set both values equal to retain a fixed cadence. Legacy custom wake-up adapters that return void remain at the fast cadence.

Validation

  • pnpm run check
  • pnpm run test:coverage — 226 passed, 11 skipped
  • pnpm run build
  • pnpm run pack:check
  • pnpm run test:package
  • pnpm run test:recovery
  • pnpm run test:browser — 3 passed
  • PostgreSQL integration — 11 passed
  • MySQL integration — 5 passed
  • Redis integration — 3 passed
  • pnpm audit --audit-level=high — no known vulnerabilities

Idle runtime roles currently query the database at a fixed cadence even when no work exists. Back empty passes off to a bounded ceiling while keeping work and wake-up paths prompt, observable, and lease-safe.

Warn when separate processes rely on polling alone, document the resulting latency tradeoff, add reproducible measurements, and prepare 0.13.1.
@greptile-apps

greptile-apps Bot commented Aug 16, 2026

Copy link
Copy Markdown

Greptile Summary

The PR reduces idle CPU usage by adding adaptive polling backoff while preserving prompt wake-ups after work or notifications.

  • Adds a reusable polling-backoff state machine and integrates it across actor, effect, reminder, and broadcast roles.
  • Extends wake-up adapters to distinguish notifications from timeouts while retaining legacy void compatibility.
  • Adds cross-process polling warnings, transition instrumentation, configuration, documentation, tests, and an idle benchmark.
  • Prepares the package and release documentation for version 0.13.1.

Confidence Score: 5/5

The PR appears safe to merge with no concrete blocking or independently actionable non-blocking issue identified.

The adaptive polling transitions are bounded, notification races remain protected by generation watches, legacy adapters retain their prior cadence, and instrumentation failures are isolated from role execution.

Important Files Changed

Filename Overview
src/polling-backoff.ts Introduces bounded exponential polling backoff with reset and transition callbacks.
src/worker.ts Applies adaptive waits to actor polling while clamping intervals to lease renewal.
src/effect-worker.ts Applies adaptive polling and wake-up-aware resets to effect processing.
src/reminder-scheduler.ts Applies adaptive polling and wake-up-aware resets to reminder scheduling.
src/broadcast-worker.ts Applies adaptive polling and wake-up-aware resets to broadcast processing.
src/wake-up.ts Makes in-process waits distinguish notifications from timeouts, cancellation, and closure.
src/wake-up/postgresql.ts Propagates notification status through PostgreSQL waiters and treats connection-wide wake-ups as non-notifications.
src/runtime.ts Adds deduplicated polling-only topology warnings and transition instrumentation integration.
src/repository.ts Adds a live-process query used to detect database sharing outside the current host process.
src/configuration.ts Adds and validates the idle polling ceiling and records whether a wake-up adapter was explicitly configured.
test/polling-loop.test.ts Exercises role backoff, reset behavior, warning emission, instrumentation, and legacy adapter compatibility.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Create wake-up watch] --> B[Poll durable work]
    B -->|Work processed| C[Reset to fast interval]
    C --> A
    B -->|No work| D[Wait at current interval]
    D -->|Notification| E[Reset to fast interval]
    E --> A
    D -->|Timeout| F[Double interval up to idle ceiling]
    F --> A
    D -->|Abort| G[Stop role]
Loading

Reviews (1): Last reviewed commit: "fix: back off idle polling" | Re-trigger Greptile

@cardmagic
cardmagic marked this pull request as ready for review August 16, 2026 20:37
@cardmagic
cardmagic merged commit 5c52ea8 into main Aug 16, 2026
17 checks passed
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