Skip to content

Stand alone operations for Nexus#2872

Open
Evanthx wants to merge 13 commits intomasterfrom
sano
Open

Stand alone operations for Nexus#2872
Evanthx wants to merge 13 commits intomasterfrom
sano

Conversation

@Evanthx
Copy link
Copy Markdown

@Evanthx Evanthx commented May 7, 2026

What was changed

Why?

Checklist

  1. Closes

  2. How was this tested:

  1. Any docs updates needed?

@Evanthx Evanthx requested a review from a team as a code owner May 7, 2026 22:53
<T> NexusServiceClient<T> newNexusServiceClient(Class<T> serviceInterface, String endpoint);

/** Start a new standalone Nexus operation execution. */
StartNexusOperationExecutionOutput startNexusOperationExecution(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This method should not be on the NexusClient

StartNexusOperationExecutionInput input);

/** List standalone Nexus operation executions matching a query. */
ListNexusOperationExecutionsOutput listNexusOperationExecutions(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Client should not be returning interceptor types here, Look at other clients like https://github.com/temporalio/sdk-java/blob/master/temporal-sdk/src/main/java/io/temporal/client/ActivityClient.java#L37

ListNexusOperationExecutionsOutput listNexusOperationExecutions(
ListNexusOperationExecutionsInput input);

/** Count standalone Nexus operation executions matching a query. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

* NexusClientHandle}; obtain a handle via {@link #getHandle}.
*/
@Experimental
public interface NexusClient {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

* type binding to an {@link UntypedNexusClientHandle} (returned by {@link
* NexusClient#getHandle(String)}) by calling one of the {@link #fromUntyped} factories.
*/
public interface NexusClientHandle<R> extends UntypedNexusClientHandle {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
public interface NexusClientHandle<R> extends UntypedNexusClientHandle {
public interface NexusOperationHandle<R> extends UntypedNexusOperationHandle {

Please keep consistent naming with other Handles like https://github.com/temporalio/sdk-java/blob/master/temporal-sdk/src/main/java/io/temporal/client/ActivityHandle.java

import java.util.concurrent.TimeoutException;
import javax.annotation.Nullable;

public interface UntypedNexusClientHandle {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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


void terminate(@Nullable String reason);

void delete();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Delete shouldn't be included here

* List operation executions whose service matches the service this client targets. Any
* user-supplied query is ANDed with a {@code Service="..."} filter.
*/
ListNexusOperationExecutionsOutput listNexusOperationExecutions(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Listing should be on the NexusClient client not the service client, it is strictly for stating operations, pleas remove

* Count operation executions whose service matches the service this client targets. Any
* user-supplied query is ANDed with a {@code Service="..."} filter.
*/
CountNexusOperationExecutionsOutput countNexusOperationExecutions(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

private final String endpoint;
private final String service;
private final String operation;
private final @Nullable Duration scheduleToCloseTimeout;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Don't need to duplicate all the options in NexusClientOperationOptions see ActivityClientCallsInterceptor

import java.util.List;
import javax.annotation.Nullable;

public class NexusClientOperationOptions {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Docs

import java.util.Objects;

/**
* NexusOperationOptions is used to specify the options for starting a Nexus operation from a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This shouldn't be renamed

* Async variant of {@link #start(BiFunction, Object)}. Returns a {@link CompletableFuture} that
* completes with the typed handle once the start RPC has acknowledged the operation.
*/
<U, R> CompletableFuture<NexusClientHandle<R>> startAsync(BiFunction<T, U, R> operation, U input);
Copy link
Copy Markdown
Contributor

@Quinn-With-Two-Ns Quinn-With-Two-Ns May 8, 2026

Choose a reason for hiding this comment

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

These variants are not part of proposed API, please remove not every API should have an async variant

@Override
public <U, R> java.util.concurrent.CompletableFuture<R> executeAsync(
BiFunction<T, U, R> operation, U input, NexusOperationOptions options) {
return startAsync(operation, input, options).thenCompose(NexusClientHandle::getResultAsync);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Look at how executeAsync is implemented on ActivityClient

@Override
public <U, R> NexusClientHandle<R> start(
BiFunction<T, U, R> operation, U input, NexusOperationOptions options) {
OperationCapture<R> capture = captureOperation(operation, input);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Look at how stand alone activities code and the newly added https://github.com/temporalio/sdk-java/blob/master/temporal-sdk/src/main/java/io/temporal/internal/util/MethodExtractor.java we should reuse the utility and not re-implment the same logic

public void applyOnConflictOptions(@Nonnull StartWorkflowExecutionRequest request) {
update(
ctx -> {
OnConflictOptions options = request.getOnConflictOptions();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unnecessary change

.build());
}

private Endpoint createEndpoint(String name) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You shouldn't need to create endpoint or delete them per test, the SDKTestWorkflowRule already creates the endpoint for you, this applies to multiple test files from what I can tell

.setTestTimeoutSeconds(120)
.build();

private NexusClient createNexusClient() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see multiple tests already need this, I would consider moving it to the SDKTestWorkflowRule

SDKTestWorkflowRule.newBuilder()
.setWorkflowTypes(NexusClientTest.PlaceholderWorkflowImpl.class)
.setNexusServiceImplementation(new TestNexusServiceImpl())
// Default is 10s; standalone Nexus dispatch + worker poll can take longer.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It shouldn't take over 120s , the default timeout should be fine since we use the same timeout for in workflow nexus operations

* NexusClient#getHandle(String)}: {@code describe()}, {@code cancel()}/{@code cancel(reason)}, and
* {@code terminate()}/{@code terminate(reason)}.
*/
public class NexusClientHandleTest {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This applies to all your tests, but since the time skipping test server doesn't support stand alone nexus operations you need to skip these tests if we are using the time skipping test server using assumeTrue(SDKTestWorkflowRule.useExternalService) on each test file

private final DataConverter dataConverter;
private final @Nullable SearchAttributes searchAttributes;
private final @Nullable String summary;
private final @Nullable NexusOperationIdReusePolicy idReusePolicy;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The options here are not correct, searchAttributes, summary, idReusePolicy, idReusePolicy should be on the per operation options

import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Name should be NexusClientOptions


import io.temporal.common.Experimental;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.workflow.NexusOperationOptions;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not be reusing the workflow NexusOperationOptions here, nothing in workflow should really be mentioned outside that package. See how stand alone activities redefined StartActivityOptions

* proto requests and delegates the actual gRPC calls to {@link GenericWorkflowClient}.
*/
@Experimental
public class RootNexusClientInvoker implements NexusClientCallsInterceptor {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe you are missing Identity here, it should be set on most requests.

@Nonnull DescribeNexusOperationExecutionRequest request, @Nonnull Deadline deadline);

// ---- Standalone Activity RPCs ----
CompletableFuture<DescribeNexusOperationExecutionResponse> describeNexusOperationExecutionAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can remove describeNexusOperationExecutionAsync we should only have APIs here that are actually used by the high level clients/handles

@Quinn-With-Two-Ns
Copy link
Copy Markdown
Contributor

Reviewed most of the public API, didn't get to into the tests or implementation for now since some stuff will likely change.

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.

2 participants