diff --git a/nexus_standalone_operations/README.md b/nexus_standalone_operations/README.md index ddddadf45..63a04b5ac 100644 --- a/nexus_standalone_operations/README.md +++ b/nexus_standalone_operations/README.md @@ -2,12 +2,17 @@ This sample demonstrates how to execute Nexus operations directly from client co without wrapping them in a workflow. It shows both synchronous and asynchronous (workflow-backed) operations, plus listing and counting operations. +The starter and worker connect to two different namespaces (a "caller" namespace and a "handler" +namespace) — this mirrors how Nexus is typically used to cross namespace boundaries. The client is +configured via the SDK's [environment configuration](https://docs.temporal.io/develop/environment-configuration) +support (`ClientConfig.load_client_connect_config()`), which reads `TEMPORAL_NAMESPACE`, +`TEMPORAL_ADDRESS`, etc. from the environment (and optionally profiles from `temporal.toml`). ### Temporal Python SDK support for Standalone Nexus Operations is at [Pre-release](https://docs.temporal.io/evaluate/development-production-features/release-stages#pre-release). All APIs are experimental and may be subject to backwards-incompatible changes. -Standalone Nexus operations require a server version that supports this feature. Use the dev server build at https://github.com/temporalio/cli/releases/tag/v1.7.3-standalone-nexus-operations. +Standalone Nexus operations require a server version that supports this feature. Use the dev server build at https://github.com/temporalio/cli/releases/tag/v1.7.4-standalone-nexus-operations. ### Sample directory structure @@ -16,38 +21,36 @@ Standalone Nexus operations require a server version that supports this feature. - [worker.py](./worker.py) - Temporal worker that hosts the Nexus service - [starter.py](./starter.py) - Client that executes standalone Nexus operations +## Run locally against a dev server -### Instructions +1. Start the [Temporal dev server build that supports standalone Nexus operations](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support) with the required namespaces pre-created: -Run the [Temporal dev server build that supports standalone Nexus operations](https://github.com/temporalio/cli/releases/tag/v1.7.3-standalone-nexus-operations). -(If you are going to run locally, you will want to start it in another terminal; this command is blocking and runs until it receives a SIGINT (Ctrl + C) command.) + ```bash + ./temporal server start-dev \ + --namespace my-caller-namespace \ + --namespace my-handler-namespace + ``` -Start a Temporal dev server with the dynamic config flags required for standalone Nexus operations: +2. Create a Nexus endpoint that routes to the handler namespace and the worker's task queue: -```bash -temporal server start-dev \ - --dynamic-config-value "nexusoperation.enableStandalone=true" \ - --dynamic-config-value "history.enableChasmCallbacks=true" -``` + ```bash + ./temporal operator nexus endpoint create \ + --name my-nexus-endpoint \ + --target-namespace my-handler-namespace \ + --target-task-queue nexus-handler-queue + ``` -Create the Nexus endpoint: +3. In a second terminal, start the worker in the handler namespace: -``` -temporal operator nexus endpoint create \ - --name nexus-standalone-operations-endpoint \ - --target-namespace default \ - --target-task-queue nexus-standalone-operations -``` + ```bash + TEMPORAL_NAMESPACE=my-handler-namespace uv run nexus_standalone_operations/worker.py + ``` -In one terminal, start the worker: -``` -uv run nexus_standalone_operations/worker.py -``` +4. In a third terminal, run the starter in the caller namespace: -In another terminal, run the starter: -``` -uv run nexus_standalone_operations/starter.py -``` + ```bash + TEMPORAL_NAMESPACE=my-caller-namespace uv run nexus_standalone_operations/starter.py + ``` ### Expected output @@ -66,4 +69,48 @@ Total Nexus operations: 2 ``` If you run the starter code multiple times, you should see additional operations in the listing results, as more operations are run. -The same goes for the total number of operations. \ No newline at end of file +The same goes for the total number of operations. + +## Run against Temporal Cloud + +1. Create two namespaces in Temporal Cloud (for example `my-caller-namespace.` and + `my-handler-namespace.`) and generate an API key (or mTLS cert) that can access both. + +2. Create a Nexus endpoint that targets the handler namespace and the worker's task queue. See the + Temporal Cloud instructions at https://docs.temporal.io/nexus/registry#create-a-nexus-endpoint. + Use: + - Endpoint name: `my-nexus-endpoint` + - Target namespace: `my-handler-namespace.` + - Target task queue: `nexus-handler-queue` + - Allowed caller namespaces: include `my-caller-namespace.` (endpoints reject callers + that are not on this list) + +3. Add two profiles to your [environment configuration file](https://docs.temporal.io/develop/environment-configuration), + one per namespace. Using API keys: + + ```toml + [profile.handler] + address = "..api.temporal.io:7233" + namespace = "my-handler-namespace." + api_key = "" + + [profile.caller] + address = "..api.temporal.io:7233" + namespace = "my-caller-namespace." + api_key = "" + ``` + + For mTLS instead of API keys, set `tls.client_cert_path` and `tls.client_key_path` on each profile + (see the [docs](https://docs.temporal.io/develop/environment-configuration) for the full schema). + +4. Run the worker and starter in separate terminals, selecting the appropriate profile in each: + + ```bash + # terminal 1 (worker, handler namespace) + TEMPORAL_PROFILE=handler uv run nexus_standalone_operations/worker.py + ``` + + ```bash + # terminal 2 (starter, caller namespace) + TEMPORAL_PROFILE=caller uv run nexus_standalone_operations/starter.py + ``` diff --git a/nexus_standalone_operations/starter.py b/nexus_standalone_operations/starter.py index 8bb235b2d..df001cab7 100644 --- a/nexus_standalone_operations/starter.py +++ b/nexus_standalone_operations/starter.py @@ -19,7 +19,7 @@ MyNexusService, ) -ENDPOINT_NAME = "nexus-standalone-operations-endpoint" +ENDPOINT_NAME = "my-nexus-endpoint" async def main() -> None: diff --git a/nexus_standalone_operations/worker.py b/nexus_standalone_operations/worker.py index 0de4ac3b1..2adcbdd90 100644 --- a/nexus_standalone_operations/worker.py +++ b/nexus_standalone_operations/worker.py @@ -11,7 +11,7 @@ interrupt_event = asyncio.Event() -TASK_QUEUE = "nexus-standalone-operations" +TASK_QUEUE = "nexus-handler-queue" async def main() -> None: diff --git a/tests/conftest.py b/tests/conftest.py index 8009ac6b9..b857e0d96 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -42,17 +42,7 @@ async def env(request) -> AsyncGenerator[WorkflowEnvironment, None]: env_type = request.config.getoption("--workflow-environment") if env_type == "local": env = await WorkflowEnvironment.start_local( - dev_server_download_version="v1.7.3-standalone-nexus-operations", - dev_server_extra_args=[ - "--dynamic-config-value", - "frontend.enableExecuteMultiOperation=true", - "--dynamic-config-value", - "system.enableEagerWorkflowStart=true", - "--dynamic-config-value", - "nexusoperation.enableStandalone=true", - "--dynamic-config-value", - "history.enableChasmCallbacks=true", - ], + dev_server_download_version="v1.7.4-standalone-nexus-operations", ) elif env_type == "time-skipping": env = await WorkflowEnvironment.start_time_skipping()