Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/fixed/relay-racing-raccoon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Setting `Worker.client` no longer raises "New client is not on the same runtime as the existing client" when both clients connected without a `runtime`, since both use the default runtime. The check now compares the runtime each client actually connected with.
3 changes: 3 additions & 0 deletions temporalio/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ def __init__(self, config: ConnectConfig) -> None:
self._bridge_config = config._to_bridge_config()
self._bridge_client: temporalio.bridge.client.Client | None = None
self._bridge_client_connect_lock = asyncio.Lock()
# Runtime we connected with, so the default one if none was set
self._runtime: temporalio.runtime.Runtime | None = None

async def _connected_client(self) -> temporalio.bridge.client.Client:
# Fast path avoids touching the lock once connected. This keeps the
Expand All @@ -433,6 +435,7 @@ async def _connected_client(self) -> temporalio.bridge.client.Client:
runtime._core_runtime,
self._bridge_config,
)
self._runtime = runtime
return self._bridge_client

@property
Expand Down
6 changes: 4 additions & 2 deletions temporalio/worker/_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,9 @@ def _init_from_config(self, client: temporalio.client.Client, config: WorkerConf

self._activity_worker: _ActivityWorker | None = None
self._runtime = (
bridge_client.config.runtime or temporalio.runtime.Runtime.default()
bridge_client._runtime
or bridge_client.config.runtime
or temporalio.runtime.Runtime.default()
)
activities = config.get("activities")
if activities:
Expand Down Expand Up @@ -743,7 +745,7 @@ def client(self, value: temporalio.client.Client) -> None:
must be using the same runtime as the current client.
"""
bridge_client = _extract_bridge_client_for_worker(value)
if self._runtime is not bridge_client.config.runtime:
if self._runtime is not bridge_client._runtime:
raise ValueError(
"New client is not on the same runtime as the existing client"
)
Expand Down
27 changes: 27 additions & 0 deletions tests/worker/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6669,6 +6669,33 @@ async def test_workflow_replace_worker_client_diff_runtimes_fail(
worker.client = other_client


async def test_workflow_replace_worker_client_implicit_default_runtime(
env: WorkflowEnvironment,
):
# Neither client sets a runtime, so both are on the default one
worker_client = await env.connect_client(runtime=None)
new_client = await env.connect_client(runtime=None)
async with new_worker(worker_client, HelloWorkflow) as worker:
worker.client = new_client
assert worker.client is new_client


async def test_workflow_replace_worker_client_implicit_default_runtime_mismatch_fail(
env: WorkflowEnvironment,
):
# No runtime means the default one, which isn't the worker's
worker_client = await env.connect_client(
runtime=Runtime(telemetry=TelemetryConfig())
)
default_runtime_client = await env.connect_client(runtime=None)
async with new_worker(worker_client, HelloWorkflow) as worker:
with pytest.raises(
ValueError,
match="New client is not on the same runtime as the existing client",
):
worker.client = default_runtime_client


@activity.defn(dynamic=True)
async def return_name_activity(_args: Sequence[RawValue]) -> str:
return activity.info().activity_type
Expand Down
Loading