@@ -105,6 +105,8 @@ private interface UpdateProcedure {
105105 private final Map <String , Long > activityById = new HashMap <>();
106106 private final Map <Long , StateMachine <ChildWorkflowData >> childWorkflows = new HashMap <>();
107107 private final Map <Long , StateMachine <NexusOperationData >> nexusOperations = new HashMap <>();
108+ // Tracks cancelRequestedEventId by scheduledEventId, persists after operation removal.
109+ private final Map <Long , Long > nexusCancelRequestedEventIds = new HashMap <>();
108110 private final Map <String , StateMachine <TimerData >> timers = new HashMap <>();
109111 private final Map <String , StateMachine <SignalExternalData >> externalSignals = new HashMap <>();
110112 private final Map <String , StateMachine <CancelExternalData >> externalCancellations =
@@ -899,6 +901,11 @@ private void processRequestCancelNexusOperation(
899901 ctx .setNeedWorkflowTask (true );
900902 } else {
901903 operation .action (Action .REQUEST_CANCELLATION , ctx , null , workflowTaskCompletedId );
904+ ctx .onCommit (
905+ historySize -> {
906+ nexusCancelRequestedEventIds .put (
907+ scheduleEventId , operation .getData ().cancelRequestedEventId );
908+ });
902909 ctx .addTimer (
903910 ProtobufTimeUtils .toJavaDuration (operation .getData ().requestTimeout ),
904911 () ->
@@ -2339,6 +2346,10 @@ public void startNexusOperation(
23392346 update (
23402347 ctx -> {
23412348 StateMachine <NexusOperationData > operation = getPendingNexusOperation (scheduledEventId );
2349+ if (operation .getState () == State .STARTED ) {
2350+ // Operation was already started (e.g. from a previous attempt before retry).
2351+ return ;
2352+ }
23422353 operation .action (StateMachines .Action .START , ctx , resp , 0 );
23432354 operation .getData ().identity = clientIdentity ;
23442355
@@ -2378,13 +2389,30 @@ public void cancelNexusOperation(NexusOperationRef ref, Failure failure) {
23782389 });
23792390 }
23802391
2392+ /**
2393+ * Resolves the cancelRequestedEventId for a nexus operation, checking both the active operations
2394+ * map and the persisted cancel request IDs (for operations that have already completed/removed).
2395+ */
2396+ private long resolveCancelRequestedEventId (long scheduledEventId ) {
2397+ StateMachine <NexusOperationData > operation = nexusOperations .get (scheduledEventId );
2398+ if (operation != null ) {
2399+ return operation .getData ().cancelRequestedEventId ;
2400+ }
2401+ Long stored = nexusCancelRequestedEventIds .get (scheduledEventId );
2402+ return stored != null ? stored : 0 ;
2403+ }
2404+
23812405 @ Override
23822406 public void cancelNexusOperationRequestAcknowledge (NexusOperationRef ref ) {
23832407 update (
23842408 ctx -> {
23852409 StateMachine <NexusOperationData > operation =
2386- getPendingNexusOperation (ref .getScheduledEventId ());
2387- if (!operationInFlight (operation .getState ())) {
2410+ nexusOperations .get (ref .getScheduledEventId ());
2411+ if (operation != null && !operationInFlight (operation .getState ())) {
2412+ return ;
2413+ }
2414+ long cancelRequestedEventId = resolveCancelRequestedEventId (ref .getScheduledEventId ());
2415+ if (cancelRequestedEventId == 0 ) {
23882416 return ;
23892417 }
23902418 ctx .addEvent (
@@ -2393,12 +2421,35 @@ public void cancelNexusOperationRequestAcknowledge(NexusOperationRef ref) {
23932421 .setNexusOperationCancelRequestCompletedEventAttributes (
23942422 NexusOperationCancelRequestCompletedEventAttributes .newBuilder ()
23952423 .setScheduledEventId (ref .getScheduledEventId ())
2396- .setRequestedEventId (operation . getData (). cancelRequestedEventId ))
2424+ .setRequestedEventId (cancelRequestedEventId ))
23972425 .build ());
2426+ scheduleWorkflowTask (ctx );
23982427 ctx .unlockTimer ("cancelNexusOperationRequestAcknowledge" );
23992428 });
24002429 }
24012430
2431+ @ Override
2432+ public void failNexusOperationCancelRequest (NexusOperationRef ref , Failure failure ) {
2433+ update (
2434+ ctx -> {
2435+ long cancelRequestedEventId = resolveCancelRequestedEventId (ref .getScheduledEventId ());
2436+ if (cancelRequestedEventId == 0 ) {
2437+ return ;
2438+ }
2439+ ctx .addEvent (
2440+ HistoryEvent .newBuilder ()
2441+ .setEventType (EventType .EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED )
2442+ .setNexusOperationCancelRequestFailedEventAttributes (
2443+ NexusOperationCancelRequestFailedEventAttributes .newBuilder ()
2444+ .setScheduledEventId (ref .getScheduledEventId ())
2445+ .setRequestedEventId (cancelRequestedEventId )
2446+ .setFailure (failure ))
2447+ .build ());
2448+ scheduleWorkflowTask (ctx );
2449+ ctx .unlockTimer ("failNexusOperationCancelRequest" );
2450+ });
2451+ }
2452+
24022453 @ Override
24032454 public void completeNexusOperation (NexusOperationRef ref , Payload result ) {
24042455 update (
@@ -2496,7 +2547,8 @@ private void timeoutNexusRequest(long scheduledEventId, String requestMethod, in
24962547 ctx -> {
24972548 StateMachine <NexusOperationData > operation = getPendingNexusOperation (scheduledEventId );
24982549 if (attempt != operation .getData ().getAttempt ()
2499- || isTerminalState (operation .getState ())) {
2550+ || isTerminalState (operation .getState ())
2551+ || operation .getState () == State .STARTED ) {
25002552 throw Status .NOT_FOUND .withDescription ("Timer fired earlier" ).asRuntimeException ();
25012553 }
25022554
0 commit comments