From 793fc56151c8970ce3a671944357a21cb890a72f Mon Sep 17 00:00:00 2001 From: Joshua Frenchwood Date: Mon, 27 Jul 2026 14:48:54 -0500 Subject: [PATCH 1/2] Improve Guidance on when to use Sync/Async Nexus Operations --- .../Caller/HelloCallerWorkflow.workflow.cs | 13 ++++++++--- .../Handler/HelloHandlerWorkflow.workflow.cs | 13 ++++++++--- src/NexusSimple/README.md | 23 ++++++++++++++++--- tests/NexusSimple/CallerWorkflowTests.cs | 18 +++++++++++++++ 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/NexusSimple/Caller/HelloCallerWorkflow.workflow.cs b/src/NexusSimple/Caller/HelloCallerWorkflow.workflow.cs index d58e76f..b223a74 100644 --- a/src/NexusSimple/Caller/HelloCallerWorkflow.workflow.cs +++ b/src/NexusSimple/Caller/HelloCallerWorkflow.workflow.cs @@ -1,5 +1,6 @@ namespace TemporalioSamples.NexusSimple.Caller; +using Microsoft.Extensions.Logging; using Temporalio.Workflows; [Workflow] @@ -8,8 +9,14 @@ public class HelloCallerWorkflow [WorkflowRun] public async Task RunAsync(string name, IHelloService.HelloLanguage language) { - var output = await Workflow.CreateNexusWorkflowClient(NexusEndpoints.HelloService). - ExecuteNexusOperationAsync(svc => svc.SayHello(new(name, language))); + var client = Workflow.CreateNexusWorkflowClient(NexusEndpoints.HelloService); + var handle = await client.StartNexusOperationAsync(svc => svc.SayHello(new(name, language))); + + Workflow.Logger.LogInformation( + "Async SayHello operation started; waiting for the workflow-backed result"); + var output = await handle.GetResultAsync(); + Workflow.Logger.LogInformation("Async SayHello operation completed"); + return output.Message; } -} \ No newline at end of file +} diff --git a/src/NexusSimple/Handler/HelloHandlerWorkflow.workflow.cs b/src/NexusSimple/Handler/HelloHandlerWorkflow.workflow.cs index ce4349b..9cf811d 100644 --- a/src/NexusSimple/Handler/HelloHandlerWorkflow.workflow.cs +++ b/src/NexusSimple/Handler/HelloHandlerWorkflow.workflow.cs @@ -7,8 +7,14 @@ namespace TemporalioSamples.NexusSimple.Handler; public class HelloHandlerWorkflow { [WorkflowRun] - public async Task RunAsync(IHelloService.HelloInput input) => - input.Language switch + public async Task RunAsync(IHelloService.HelloInput input) + { + // This durable timer intentionally exceeds the 10-second Nexus request + // deadline in the runnable sample. The workflow-backed operation can continue after its + // start request returns an operation token. + await Workflow.DelayAsync(TimeSpan.FromSeconds(15)); + + return input.Language switch { IHelloService.HelloLanguage.En => new($"Hello {input.Name} 👋"), IHelloService.HelloLanguage.Fr => new($"Bonjour {input.Name} 👋"), @@ -18,4 +24,5 @@ public class HelloHandlerWorkflow _ => throw new ApplicationFailureException( $"Unsupported language: {input.Language}", errorType: "UNSUPPORTED_LANGUAGE"), }; -} \ No newline at end of file + } +} diff --git a/src/NexusSimple/README.md b/src/NexusSimple/README.md index 0e8ef77..f22a30f 100644 --- a/src/NexusSimple/README.md +++ b/src/NexusSimple/README.md @@ -1,8 +1,22 @@ # Nexus Simple -This sample demonstrates how to use Temporal for authoring a Nexus service and call it from a workflow. +This sample demonstrates how to author a Nexus service and call it from a Workflow. It intentionally +contrasts synchronous and asynchronous Nexus operations: -### Instructions +* `Echo` uses `OperationHandler.Sync` because it is a short, bounded, in-memory operation with no + external calls or side effects. +* `SayHello` is backed by a Workflow that waits on a durable 15-second timer before returning. The + delay intentionally exceeds the 10-second synchronous Nexus request deadline. + +Use `OperationHandler.Sync` only for highly reliable, low-latency, bounded operations that complete +well within that short request window. Prefer an asynchronous operation when latency or availability is uncertain, +the work might exceed the handler deadline, or execution depends on a potentially unreliable service or database. + +The asynchronous handler must still start the Workflow and return an operation token within +the request deadline. The Workflow can continue afterward, and the caller retrieves its +eventual result through the operation handle. + +## Instructions To run, first see [README.md](../../README.md) for prerequisites such as starting the Temporal server. @@ -35,4 +49,7 @@ In a third terminal, run the caller workflow: ``` dotnet run caller-workflow -``` \ No newline at end of file +``` + +The Echo result returns inline. For SayHello, the caller worker logs that the asynchronous operation +started, waits approximately 15 seconds, and then logs its completion. diff --git a/tests/NexusSimple/CallerWorkflowTests.cs b/tests/NexusSimple/CallerWorkflowTests.cs index 407c9f3..ca40137 100644 --- a/tests/NexusSimple/CallerWorkflowTests.cs +++ b/tests/NexusSimple/CallerWorkflowTests.cs @@ -80,4 +80,22 @@ await callerWorker.ExecuteAsync(async () => }); }); } + + [Fact] + public async Task RunAsync_HelloHandlerWorkflow_DurableDelayCompletes() + { + await using var env = await Temporalio.Testing.WorkflowEnvironment.StartTimeSkippingAsync(); + using var worker = new TemporalWorker( + env.Client, + new TemporalWorkerOptions($"tq-{Guid.NewGuid()}"). + AddWorkflow()); + await worker.ExecuteAsync(async () => + { + var result = await env.Client.ExecuteWorkflowAsync( + (HelloHandlerWorkflow wf) => + wf.RunAsync(new("some-name", IHelloService.HelloLanguage.Fr)), + new(id: $"wf-{Guid.NewGuid()}", taskQueue: worker.Options.TaskQueue!)); + Assert.Equal("Bonjour some-name 👋", result.Message); + }); + } } From 4a139cd8dcf80a5dc6f79dcd1f86ca2393f6f372 Mon Sep 17 00:00:00 2001 From: Joshua Frenchwood Date: Mon, 27 Jul 2026 15:24:50 -0500 Subject: [PATCH 2/2] Updating readme for sync vs async nexus operation changes --- src/NexusSimple/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/NexusSimple/README.md b/src/NexusSimple/README.md index f22a30f..7fef9d1 100644 --- a/src/NexusSimple/README.md +++ b/src/NexusSimple/README.md @@ -9,12 +9,13 @@ contrasts synchronous and asynchronous Nexus operations: delay intentionally exceeds the 10-second synchronous Nexus request deadline. Use `OperationHandler.Sync` only for highly reliable, low-latency, bounded operations that complete -well within that short request window. Prefer an asynchronous operation when latency or availability is uncertain, -the work might exceed the handler deadline, or execution depends on a potentially unreliable service or database. +well within that short request window. Use an asynchronous operation when latency or +availability is uncertain, the work might exceed the handler deadline, or execution depends on a potentially +unreliable service or database. -The asynchronous handler must still start the Workflow and return an operation token within -the request deadline. The Workflow can continue afterward, and the caller retrieves its -eventual result through the operation handle. +An asynchronous handler must still initiate or attach to the underlying work and return an operation +token within the request deadline. The work can continue afterward, and the caller retrieves its +eventual result through the operation handle. In this sample, that work is a Workflow. ## Instructions