Skip to content

Comments

Cancel timer when Workflow.await condition is satisfied#2799

Open
mfateev wants to merge 3 commits intotemporalio:masterfrom
mfateev:await-cancel-timer-clean
Open

Cancel timer when Workflow.await condition is satisfied#2799
mfateev wants to merge 3 commits intotemporalio:masterfrom
mfateev:await-cancel-timer-clean

Conversation

@mfateev
Copy link
Member

@mfateev mfateev commented Feb 24, 2026

Summary

  • Workflow.await(duration, condition) now cancels the timer when the condition is satisfied before the timeout expires
  • Prevents unnecessary workflow tasks caused by timer fired events
  • If condition is already true when called, returns immediately without creating a timer

Background

Fixes #2312. Port of the same fix from the Go SDK: temporalio/sdk-go#2153

Implementation

  • Added CANCEL_AWAIT_TIMER_ON_CONDITION(4) SDK flag for replay compatibility
  • When the flag is enabled, the timer is created inside a CancellationScope so it can be cancelled when the condition resolves
  • Uses checkSdkFlag (not tryUseSdkFlag) 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 policy
  • TODO comment marks where to switch to tryUseSdkFlag in the next release

Test plan

  • testTimerCancelledWhenFlagEnabled — explicitly enables the flag, verifies TIMER_CANCELED in history
  • testTimerNotCancelledWhenFlagDisabled — default (flag off), verifies timer is NOT cancelled (old behavior)
  • testNoTimerWhenConditionImmediatelySatisfiedWithFlag — verifies no timer created when condition is already true
  • testAwaitReturnValue — verifies return value semantics (true=condition met, false=timeout)
  • testReplayOldHistoryWithoutFlag — replay compatibility with old workflow histories recorded without the flag

…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).
@mfateev mfateev requested a review from a team as a code owner February 24, 2026 16:42
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()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I'd move this block into the first branch of the if (cancelTimerOnCondition) { below

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

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.

Workflow.await(duration, condition) does not automatically cancel the timer if the condition is resolved

2 participants