From 03985d9987c85e1ebde47a72f6e7792c8141d2ae Mon Sep 17 00:00:00 2001 From: Andrew Yuan Date: Mon, 9 Feb 2026 15:09:17 -0800 Subject: [PATCH 1/2] Increase timeout --- features/activity/shutdown/feature.cs | 15 ++++++++++----- features/activity/shutdown/feature.go | 16 +++++++++++----- features/activity/shutdown/feature.java | 17 ++++++++++++----- features/activity/shutdown/feature.py | 6 +++--- features/activity/shutdown/feature.ts | 20 +++++++++++++------- 5 files changed, 49 insertions(+), 25 deletions(-) diff --git a/features/activity/shutdown/feature.cs b/features/activity/shutdown/feature.cs index 38b014be..03ca14b2 100644 --- a/features/activity/shutdown/feature.cs +++ b/features/activity/shutdown/feature.cs @@ -38,15 +38,20 @@ class MyWorkflow [WorkflowRun] public async Task RunAsync() { - var options = new ActivityOptions + var gracefulOptions = new ActivityOptions { - ScheduleToCloseTimeout = TimeSpan.FromMilliseconds(300), + ScheduleToCloseTimeout = TimeSpan.FromSeconds(30), + RetryPolicy = new() { MaximumAttempts = 1 }, + }; + var ignoreOptions = new ActivityOptions + { + ScheduleToCloseTimeout = TimeSpan.FromSeconds(2), RetryPolicy = new() { MaximumAttempts = 1 }, }; - var fut = Workflow.ExecuteActivityAsync((MyActivities act) => act.CancelSuccess(), options); - var fut1 = Workflow.ExecuteActivityAsync((MyActivities act) => act.CancelFailure(), options); - var fut2 = Workflow.ExecuteActivityAsync((MyActivities act) => act.CancelIgnore(), options); + var fut = Workflow.ExecuteActivityAsync((MyActivities act) => act.CancelSuccess(), gracefulOptions); + var fut1 = Workflow.ExecuteActivityAsync((MyActivities act) => act.CancelFailure(), gracefulOptions); + var fut2 = Workflow.ExecuteActivityAsync((MyActivities act) => act.CancelIgnore(), ignoreOptions); await fut; diff --git a/features/activity/shutdown/feature.go b/features/activity/shutdown/feature.go index 3dc8604d..af6340d3 100644 --- a/features/activity/shutdown/feature.go +++ b/features/activity/shutdown/feature.go @@ -47,16 +47,22 @@ func Execute(ctx context.Context, r *harness.Runner) (client.WorkflowRun, error) } func Workflow(ctx workflow.Context) (string, error) { - ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ - ScheduleToCloseTimeout: 300 * time.Millisecond, + gracefulCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + ScheduleToCloseTimeout: 30 * time.Second, + RetryPolicy: &temporal.RetryPolicy{ + MaximumAttempts: 1, + }, + }) + ignoreCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + ScheduleToCloseTimeout: 2 * time.Second, RetryPolicy: &temporal.RetryPolicy{ MaximumAttempts: 1, }, }) - fut := workflow.ExecuteActivity(ctx, activities.CancelSuccess) - fut1 := workflow.ExecuteActivity(ctx, activities.CancelFailure) - fut2 := workflow.ExecuteActivity(ctx, activities.CancelIgnore) + fut := workflow.ExecuteActivity(gracefulCtx, activities.CancelSuccess) + fut1 := workflow.ExecuteActivity(gracefulCtx, activities.CancelFailure) + fut2 := workflow.ExecuteActivity(ignoreCtx, activities.CancelIgnore) err := fut.Get(ctx, nil) if err != nil { diff --git a/features/activity/shutdown/feature.java b/features/activity/shutdown/feature.java index 0689800b..64a1c90f 100644 --- a/features/activity/shutdown/feature.java +++ b/features/activity/shutdown/feature.java @@ -41,18 +41,25 @@ class Impl implements feature, ShutdownWorkflow { @Override public String workflow() { - var activities = + var gracefulActivities = activities( feature.class, builder -> builder - .setScheduleToCloseTimeout(Duration.ofMillis(300)) + .setScheduleToCloseTimeout(Duration.ofSeconds(30)) + .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())); + var ignoringActivities = + activities( + feature.class, + builder -> + builder + .setScheduleToCloseTimeout(Duration.ofSeconds(2)) .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())); - activities.cancelSuccess(); + gracefulActivities.cancelSuccess(); try { - activities.cancelFailure(); + gracefulActivities.cancelFailure(); throw ApplicationFailure.newFailure("expected failure", "NoError"); } catch (ActivityFailure e) { if (!(e.getCause() instanceof ApplicationFailure) @@ -62,7 +69,7 @@ public String workflow() { } try { - activities.cancelIgnore(); + ignoringActivities.cancelIgnore(); throw ApplicationFailure.newFailure("expected timeout", "NoError"); } catch (ActivityFailure e) { if (e.getCause() instanceof TimeoutFailure) { diff --git a/features/activity/shutdown/feature.py b/features/activity/shutdown/feature.py index 3df80f4e..8f0df9d1 100644 --- a/features/activity/shutdown/feature.py +++ b/features/activity/shutdown/feature.py @@ -20,17 +20,17 @@ class Workflow: async def run(self) -> str: handle = workflow.start_activity( cancel_success, - schedule_to_close_timeout=timedelta(milliseconds=300), + schedule_to_close_timeout=timedelta(seconds=30), retry_policy=RetryPolicy(maximum_attempts=1), ) handle1 = workflow.start_activity( cancel_failure, - schedule_to_close_timeout=timedelta(milliseconds=300), + schedule_to_close_timeout=timedelta(seconds=30), retry_policy=RetryPolicy(maximum_attempts=1), ) handle2 = workflow.start_activity( cancel_ignore, - schedule_to_close_timeout=timedelta(milliseconds=300), + schedule_to_close_timeout=timedelta(seconds=2), retry_policy=RetryPolicy(maximum_attempts=1), ) diff --git a/features/activity/shutdown/feature.ts b/features/activity/shutdown/feature.ts index 23cfa642..acf9df27 100644 --- a/features/activity/shutdown/feature.ts +++ b/features/activity/shutdown/feature.ts @@ -1,4 +1,3 @@ -import { Context } from '@temporalio/activity'; import { Feature } from '@temporalio/harness'; import * as wf from '@temporalio/workflow'; import { ApplicationFailure, TimeoutFailure, TimeoutType } from '@temporalio/common'; @@ -27,19 +26,26 @@ const activitiesImpl = { throw new Error('worker is shutting down'); }, async cancelIgnore(): Promise { - await Context.current().sleep(15000); + // Use a plain setTimeout that doesn't respond to activity cancellation, + // so the worker must abandon this activity on shutdown. + await new Promise((resolve) => setTimeout(resolve, 15000)); }, }; -const activities = wf.proxyActivities({ - scheduleToCloseTimeout: '300ms', +const gracefulActivities = wf.proxyActivities({ + scheduleToCloseTimeout: '30s', + retry: { maximumAttempts: 1 }, +}); + +const ignoringActivities = wf.proxyActivities({ + scheduleToCloseTimeout: '2s', retry: { maximumAttempts: 1 }, }); export async function workflow(): Promise { - const fut = activities.cancelSuccess(); - const fut1 = activities.cancelFailure(); - const fut2 = activities.cancelIgnore(); + const fut = gracefulActivities.cancelSuccess(); + const fut1 = gracefulActivities.cancelFailure(); + const fut2 = ignoringActivities.cancelIgnore(); await fut; From 6165a0abf5921cd05d850f8dd6b7adf0565674e7 Mon Sep 17 00:00:00 2001 From: Andrew Yuan Date: Tue, 10 Feb 2026 13:44:48 -0800 Subject: [PATCH 2/2] Keep ignore timeout the same 300ms, but passing/failing workflows up to 30s --- features/activity/shutdown/feature.cs | 2 +- features/activity/shutdown/feature.go | 2 +- features/activity/shutdown/feature.java | 2 +- features/activity/shutdown/feature.py | 2 +- features/activity/shutdown/feature.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/features/activity/shutdown/feature.cs b/features/activity/shutdown/feature.cs index 03ca14b2..5bc89181 100644 --- a/features/activity/shutdown/feature.cs +++ b/features/activity/shutdown/feature.cs @@ -45,7 +45,7 @@ public async Task RunAsync() }; var ignoreOptions = new ActivityOptions { - ScheduleToCloseTimeout = TimeSpan.FromSeconds(2), + ScheduleToCloseTimeout = TimeSpan.FromMilliseconds(300), RetryPolicy = new() { MaximumAttempts = 1 }, }; diff --git a/features/activity/shutdown/feature.go b/features/activity/shutdown/feature.go index af6340d3..3abce465 100644 --- a/features/activity/shutdown/feature.go +++ b/features/activity/shutdown/feature.go @@ -54,7 +54,7 @@ func Workflow(ctx workflow.Context) (string, error) { }, }) ignoreCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ - ScheduleToCloseTimeout: 2 * time.Second, + ScheduleToCloseTimeout: 300 * time.Millisecond, RetryPolicy: &temporal.RetryPolicy{ MaximumAttempts: 1, }, diff --git a/features/activity/shutdown/feature.java b/features/activity/shutdown/feature.java index 64a1c90f..776b4bc2 100644 --- a/features/activity/shutdown/feature.java +++ b/features/activity/shutdown/feature.java @@ -53,7 +53,7 @@ public String workflow() { feature.class, builder -> builder - .setScheduleToCloseTimeout(Duration.ofSeconds(2)) + .setScheduleToCloseTimeout(Duration.ofMillis(300)) .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())); gracefulActivities.cancelSuccess(); diff --git a/features/activity/shutdown/feature.py b/features/activity/shutdown/feature.py index 8f0df9d1..090bd60f 100644 --- a/features/activity/shutdown/feature.py +++ b/features/activity/shutdown/feature.py @@ -30,7 +30,7 @@ async def run(self) -> str: ) handle2 = workflow.start_activity( cancel_ignore, - schedule_to_close_timeout=timedelta(seconds=2), + schedule_to_close_timeout=timedelta(milliseconds=300), retry_policy=RetryPolicy(maximum_attempts=1), ) diff --git a/features/activity/shutdown/feature.ts b/features/activity/shutdown/feature.ts index acf9df27..623667e3 100644 --- a/features/activity/shutdown/feature.ts +++ b/features/activity/shutdown/feature.ts @@ -38,7 +38,7 @@ const gracefulActivities = wf.proxyActivities({ }); const ignoringActivities = wf.proxyActivities({ - scheduleToCloseTimeout: '2s', + scheduleToCloseTimeout: '300ms', retry: { maximumAttempts: 1 }, });