Cancel timer when Workflow.await condition is satisfied#2799
Cancel timer when Workflow.await condition is satisfied#2799mjameswh merged 6 commits intotemporalio:masterfrom
Conversation
…tisfied This change addresses GitHub issue temporalio#2312 by ensuring that Workflow.await(duration, condition) cancels the timer when the condition is satisfied before the timeout expires. Changes: - Add CANCEL_AWAIT_TIMER_ON_CONDITION SDK flag for backward compatibility - Modify SyncWorkflowContext.await() to use a CancellationScope to cancel the timer when condition is satisfied before timeout - Skip timer creation entirely if condition is already satisfied - Add comprehensive tests including replay compatibility test The new behavior is enabled by default for new workflows via the SDK flag mechanism. Old workflows replay correctly with the original behavior.
Follow the Go SDK pattern (PR temporalio#2153) per reviewer feedback: 1. Use checkSdkFlag instead of tryUseSdkFlag so the flag is NOT auto-enabled for new workflows. Add TODO to switch to tryUseSdkFlag in the next release. 2. Remove CANCEL_AWAIT_TIMER_ON_CONDITION from initialFlags. 3. Tests explicitly toggle the flag to verify both old behavior (timer NOT cancelled) and new behavior (timer cancelled).
| replayContext.checkSdkFlag(SdkFlag.CANCEL_AWAIT_TIMER_ON_CONDITION); | ||
|
|
||
| // If new behavior is enabled and condition is already satisfied, skip creating timer | ||
| if (cancelTimerOnCondition && unblockCondition.get()) { |
There was a problem hiding this comment.
nit: I'd move this block into the first branch of the if (cancelTimerOnCondition) { below
|
|
||
| boolean conditionSatisfied = !timer.isCompleted(); | ||
| if (conditionSatisfied) { | ||
| timerScope.cancel("await condition resolved"); |
There was a problem hiding this comment.
I'm not sure about providing a reason message here. AFAICS, we're not currently providing reason messages in similar context anywhere in the Java SDK, and we don't do that either in the Go SDK, which was used as reference for this PR.
But no strong opinion either way.
| } | ||
|
|
||
| @WorkflowInterface | ||
| public interface TestAwaitWorkflow { |
There was a problem hiding this comment.
nit: I believe we generally try to deduplicate workflow interfacess in our tests.
mjameswh
left a comment
There was a problem hiding this comment.
Two very minor comments, but overall looks correct to me.
|
Hi, I see in the description: If condition is already true when called, returns immediately without creating a timer Does it mean we break determinism if the condition is initially false (timer started event present in history) and later on we change the workflow definition in a way that makes the condition already true when What would be the way to make such a change without breaking determinism? I don't think we can call |
Yes, that's correct, and that has always been the case. The change you describe necessarily implies a change in the relative order of events in the workflow history, and would therefore cause an NDE, independently of the present PR. Do you have a specific case in mind? |
I don't think that has always been the case, I am using Java Temporal SDK 1.32.1 and I see the timer is created by adding following: Doesn't that mean that upgrading to the new version might cause NDE in existing workflows even without applying any change to the workflow definition? Am I missing something? In my case, I created an helper to do similar things (cancelling the timer when needed and also adding a summary to it) and noticed the difference with Another question, would it make sense to have a version of |
No, the SDK itself has its own mechanism, "SDK flags", to determine whether it needs to replay using the legacy logic or the fixed logic. That's a bit similar to |
It would certainly make sense. See #2751. |
Summary
Workflow.await(duration, condition)now cancels the timer when the condition is satisfied before the timeout expiresBackground
Fixes #2312. Port of the same fix from the Go SDK: temporalio/sdk-go#2153
Implementation
CANCEL_AWAIT_TIMER_ON_CONDITION(4)SDK flag for replay compatibilityCancellationScopeso it can be cancelled when the condition resolvescheckSdkFlag(nottryUseSdkFlag) so the flag is not auto-enabled for new workflows in this release — it must wait at least 1 release before being enabled by default, per SDK flag rollout policytryUseSdkFlagin the next releaseTest plan
testTimerCancelledWhenFlagEnabled— explicitly enables the flag, verifiesTIMER_CANCELEDin historytestTimerNotCancelledWhenFlagDisabled— default (flag off), verifies timer is NOT cancelled (old behavior)testNoTimerWhenConditionImmediatelySatisfiedWithFlag— verifies no timer created when condition is already truetestAwaitReturnValue— verifies return value semantics (true=condition met, false=timeout)testReplayOldHistoryWithoutFlag— replay compatibility with old workflow histories recorded without the flag