Skip to content

Auto-enroll pollers into autoscaling on PollerAutoscalingAutoEnroll (Rust SDK) #1406

Closed
veeral-patel wants to merge 1 commit into
mainfrom
feat/poller-autoscaling-auto-enroll
Closed

Auto-enroll pollers into autoscaling on PollerAutoscalingAutoEnroll (Rust SDK) #1406
veeral-patel wants to merge 1 commit into
mainfrom
feat/poller-autoscaling-auto-enroll

Conversation

@veeral-patel

@veeral-patel veeral-patel commented Jul 15, 2026

Copy link
Copy Markdown

Ports temporalio/sdk-go#2442 to sdk-core, so it's shared by every SDK on top of it (Rust directly; TS/Python/.NET/Ruby via the c-bridge).

What

When a namespace advertises the poller_autoscaling_auto_enroll capability, workers automatically switch their workflow, activity, and nexus pollers to autoscaling — but only for poller types the user left at their default. Pollers with an explicitly configured count or behavior are left alone.

Why it's implemented this way

The capability is only known after Worker::validate() (an async namespace lookup), but pollers are built earlier, synchronously. Instead of reordering that, the PollScaler resolves its behavior lazily, when polling starts — which always happens after validate(). Alternatives (blocking network I/O during construction, or rebuilding pollers afterward) were more invasive.

Key changes

  • Worker::validate() reads the new capability and also enables poller_autoscaling so enrolled pollers can scale down.
  • WorkerConfig gains three per-type "eligibility" flags (default false — nothing auto-enrolls unless a layer above opts in).
  • PollScaler defers its setup into activate() and, if the poller is eligible and the capability is present, uses Autoscaling { min: 1, max: 100, initial: 5 } (matching Go).
  • High-level SDK / c-bridge signal eligibility by leaving the poller behavior unset (SDK: Option::None; c-bridge: both FFI pointers null).

Notes

  • Two intentional differences from the Go PR: no session-worker case and no heartbeat change — neither exists in sdk-core.
  • The SDK WorkerOptions poller fields become Option<PollerBehavior> (minor pre-1.0 break; existing .foo(x) calls still compile).
  • Other lang SDKs need a small follow-up to pass the unset signal through the FFI.

Testing

New unit tests for behavior resolution, validate() plumbing, and the c-bridge conversion. Full sdk-core lib suite passes (368 tests); fmt + clippy clean.

Automatically switch workflow, activity, and nexus pollers to autoscaling
when the namespace advertises the `poller_autoscaling_auto_enroll`
capability. This only applies to poller types left at their default (the
worker set neither a fixed poller count nor a poller behavior);
explicitly configured pollers are left unchanged.

Ports temporalio/sdk-go#2442 to sdk-core so it is shared by every SDK
built on top of it.

- Parse the capability in `Worker::validate` and force-enable
  `poller_autoscaling` (so auto-enrolled pollers may scale down), tracked
  on `NamespaceCapabilities`.
- Add per-type auto-enroll eligibility to `WorkerConfig`.
- `PollScaler` now resolves its effective behavior lazily, when polling
  starts (guaranteed after `validate`), deferring the report handle,
  ingestor task, and `no_retry` so a default poller can be enrolled into
  autoscaling without changing construction ordering.
- Surface eligibility through the high-level SDK `WorkerOptions`
  (poller fields are now `Option`) and the c-bridge (unset == both FFI
  pointers null).

Note: unlike the Go PR there is no session-worker case and no heartbeat
change, as neither concept exists in sdk-core.
@veeral-patel
veeral-patel requested a review from a team as a code owner July 15, 2026 21:40
/// Converts an FFI poller behavior into an optional core poller behavior. Both fields null means
/// the poller was left unset by lang: the caller should apply the default behavior and treat the
/// poller as eligible for automatic enrollment into poller autoscaling.
fn ffi_poller_behavior_opt(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably just be an Into impl

Comment on lines +543 to +559
// The watch channel is created eagerly because `WFTPollerShared` needs `active_rx` before the
// poller task starts.
active_tx: watch::Sender<usize>,
active_rx: watch::Receiver<usize>,
num_pollers_handler: Option<Arc<F>>,
// Inputs captured so the effective behavior can be resolved lazily in `activate`, once the
// namespace capabilities discovered by `Worker::validate` are known (which is guaranteed to be
// before polling begins).
configured_behavior: PollerBehavior,
auto_enroll_eligible: bool,
capabilities: Arc<NamespaceCapabilities>,
last_successful_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
shutdown: CancellationToken,
/// Shared with the poll function so `no_retry` tracks the resolved (post-`activate`) behavior.
autoscaling_active: Arc<AtomicBool>,
// Resolved by `activate`, before the poll loop issues any poll.
report_handle: Option<Arc<PollScalerReportHandle>>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of additional complexity. I think things could overall become much, much simpler, if we just pass in the right PollerBehavior based on namespace capabilities rather than switching it dynamically.

We aren't expecting to dynamically flip between enabled and not enabled while the worker is running, right? I don't think the Go PR did that.

So IMO we should just delay spinning up pollers until after the validate call has happened (which might already be true)? And then this file doesn't need any changes at all, everything should just work as is.

@veeral-patel veeral-patel changed the title feat(sdk): auto-enroll default pollers into autoscaling Auto-enroll pollers into autoscaling on PollerAutoscalingAutoEnroll (Rust SDK) Jul 20, 2026
self.capabilities
.poller_autoscaling_auto_enroll
.store(true, Ordering::Relaxed);
// Auto-enroll implies full autoscaling support (including scaling down on

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this. We can decouple these two capabilities.

if auto_enroll_eligible && capabilities.poller_autoscaling_auto_enroll() {
// Mirrors the Go SDK's auto-enroll autoscaling defaults.
PollerBehavior::Autoscaling {
minimum: 1,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See if we can not pass in the min, max, initial so the existing default values are used. If we have to, see if we can reuse a shared constant instead of hard-coding

@veeral-patel

Copy link
Copy Markdown
Author

Closing in favor of this PR: #1425

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.

2 participants