Skip to content

Conversation

@yuandrew
Copy link
Contributor

@yuandrew yuandrew commented Oct 14, 2025

What was changed

  • Send plugin names over to core for worker heartbeating.
  • Updated Core to latest main, 45b1d7e.
  • Added logic to pass newly required WorkerTaskTypes to core, based on workflows, activities, and nexus handlers registered to the worker, as well as no_remote_activities configuration.
  • Generated new protos

Why?

Worker heartbeating

Checklist

  1. Closes [Feature Request] Enable Worker Heartbeating #1196

  2. How was this tested:

  • Added tests for plugin name propagation, and runtime options configuration
  • Updated test to validate replacing clients with a client from a different runtime is invalid.
  1. Any docs updates needed?

Note

Upgrades to latest Core and protos, adds RuntimeOptions (incl. worker heartbeat), sends worker task types and plugin names to Core, exposes new service RPCs (e.g., DescribeWorker) and cloud endpoints, and tightens client/worker runtime compatibility with accompanying tests.

  • Runtime/Bridge:
    • Add RuntimeOptions (incl. worker_heartbeat_interval_millis) and switch Runtime init to use options.
    • Send worker plugin names to Core for heartbeating.
    • Migrate bridge to new temporalio-* crates; update imports and Cargo deps.
  • Worker:
    • Introduce WorkerTaskTypes and pass enabled types (workflows/local/remote activities/nexus) to Core.
    • Include plugin names in worker config sent to Core.
    • Enforce same-runtime when replacing a worker’s client; expose error on mismatch.
  • APIs/Protos:
    • Add WorkflowService DescribeWorker and SetWorkerDeploymentManager; extend requests (e.g., allow_no_pollers, eager worker options).
    • Add CloudService SetServiceAccountNamespaceAccess and ValidateAccountAuditLogSink; introduce AuditLogSinkSpec, KinesisSpec, PubSubSpec.
    • Expand namespace/deployment messages (capabilities, manager_identity) and various fields.
  • Tests/CI:
    • New/updated tests for plugin propagation, runtime options, local activities with no_remote_activities, and client replacement runtime validation.
    • Minor CI step reorder and script path/rename fixes.

Written by Cursor Bugbot for commit e0d0a25. This will update automatically on new commits. Configure here.

@yuandrew yuandrew marked this pull request as ready for review October 21, 2025 01:04
@yuandrew yuandrew requested a review from a team as a code owner October 21, 2025 01:04
Copy link
Member

@Sushisource Sushisource left a comment

Choose a reason for hiding this comment

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

This is looking good to me, only thing is the default interval

Copy link
Member

@Sushisource Sushisource left a comment

Choose a reason for hiding this comment

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

Nice!

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Runtime Comparison Needs Proper Instance Resolution

The runtime comparison uses identity (is not) but doesn't properly handle when bridge_client.config.runtime is None. When the original client uses the default runtime (via None), self._runtime stores the actual default runtime object, but if a new client also has runtime=None in its config, the comparison self._runtime is not None incorrectly raises an error even though both clients use the same default runtime. The comparison should resolve both sides to actual runtime instances before comparing.

temporalio/worker/_worker.py#L648-L652

bridge_client = _extract_bridge_client_for_worker(value)
if self._runtime is not bridge_client.config.runtime:
raise ValueError(
"New client is not on the same runtime as the existing client"
)

Fix in Cursor Fix in Web


"temporalio_sdk",
]
parts = [self.other_level]
parts.extend(f"{target}={self.core_level}" for target in targets)
Copy link

Choose a reason for hiding this comment

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

Bug: Outdated Telemetry Target Breaks Rust Log Filtering

The TelemetryFilter.formatted() method includes "temporalio_sdk" as a target, but this doesn't match any actual Rust crate name. The crates were renamed from temporal_sdk_core, temporal_client, etc. to temporalio_sdk_core, temporalio_client, and temporalio_common. The target "temporalio_sdk" will never match any log output from the Rust code, preventing those logs from being filtered at the configured level. This should likely be "temporalio_sdk_core" (which is already in the list) or removed entirely.

Fix in Cursor Fix in Web

@yuandrew yuandrew changed the title Send plugin names to Core Update Core, configure worker types, send plugin names to Core Nov 19, 2025
nonsticky_to_sticky_poll_ratio: f32,
activity_task_poller_behavior: PollerBehavior,
no_remote_activities: bool,
task_types: WorkerTaskTypes,
Copy link
Member

Choose a reason for hiding this comment

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

Not sure it deserves a whole new single-use Python type here vs just three booleans inlined, but not a big deal

# Terminate both
await handle1.terminate()
await handle2.terminate()
async def test_workflow_replace_worker_client(client: Client):
Copy link
Member

Choose a reason for hiding this comment

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

Can you provide some background why the previous test was removed and replaced with this? We want to test that replacing a client works, not that it raises when a runtime is different (if we want the latter, that's a different test).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously thought the error I was hitting was due to using different runtimes, but just took another look into things and found the real fix, enter_sync!(self.runtime);, which is now needed bc replacing the client sometimes creates a new workflow worker, which spawns a tokio task.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing out

Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

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

Nothing blocking, but did notice some two things we should fix (maybe separately) about the plugin impl while reviewing:

  • We should not make a public attribute, plugins, on our worker IMO
  • Client.config() and Worker.config() need to represent immutable copies of the exact kwargs passed in to constructor or at least be sure they can be passed in to another constructor

f"The same plugin type {type(client_plugin)} is present from both client and worker. It may run twice and may not be the intended behavior."
)
plugins = plugins_from_client + list(plugins)
config["plugins"] = plugins
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I think this may be a problem we need to address at some point. The purpose of config was to match kwargs so someone could easily re-initialize a worker by splatting them.

@yuandrew yuandrew merged commit b5001f9 into temporalio:main Nov 25, 2025
16 checks passed
@yuandrew yuandrew deleted the plugins_to_core branch November 25, 2025 18:17
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.

[Feature Request] Enable Worker Heartbeating

4 participants