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..7fef9d1 100644 --- a/src/NexusSimple/README.md +++ b/src/NexusSimple/README.md @@ -1,8 +1,23 @@ # 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. 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. + +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 To run, first see [README.md](../../README.md) for prerequisites such as starting the Temporal server. @@ -35,4 +50,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); + }); + } }