Skip to content

Commit 0eb804e

Browse files
kaibocaiclaude
andcommitted
feat: add CreateTimerAt for absolute-time durable timers
Add CreateTimerAt(fireAt time.Time) to OrchestrationContext, allowing orchestrators to schedule durable timers at an absolute UTC time. This aligns the Go SDK with the .NET SDK's CreateTimer(DateTime) API. The existing CreateTimer(Duration) now delegates to the new internal createTimerAtInternal method, keeping behavior identical for all existing callers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 812d373 commit 0eb804e

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

task/orchestrator.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,17 @@ func (ctx *OrchestrationContext) CreateTimer(delay time.Duration) Task {
366366
return ctx.createTimerInternal(delay)
367367
}
368368

369+
// CreateTimerAt schedules a durable timer that fires at the specified absolute UTC time.
370+
func (ctx *OrchestrationContext) CreateTimerAt(fireAt time.Time) Task {
371+
return ctx.createTimerAtInternal(fireAt)
372+
}
373+
369374
func (ctx *OrchestrationContext) createTimerInternal(delay time.Duration) *completableTask {
370375
fireAt := ctx.CurrentTimeUtc.Add(delay)
376+
return ctx.createTimerAtInternal(fireAt)
377+
}
378+
379+
func (ctx *OrchestrationContext) createTimerAtInternal(fireAt time.Time) *completableTask {
371380
timerAction := helpers.NewCreateTimerAction(ctx.getNextSequenceNumber(), fireAt)
372381
ctx.pendingActions[timerAction.Id] = timerAction
373382

tests/orchestrations_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,45 @@ func Test_SingleTimer(t *testing.T) {
8888
)
8989
}
9090

91+
func Test_SingleTimerAt(t *testing.T) {
92+
// Registration
93+
r := task.NewTaskRegistry()
94+
require.NoError(t, r.AddOrchestratorN("SingleTimerAt", func(ctx *task.OrchestrationContext) (any, error) {
95+
// Schedule a timer that fires 1 second in the future using an absolute time
96+
fireAt := ctx.CurrentTimeUtc.Add(1 * time.Second)
97+
err := ctx.CreateTimerAt(fireAt).Await(nil)
98+
return nil, err
99+
}))
100+
101+
// Initialization
102+
ctx := context.Background()
103+
exporter := initTracing()
104+
client, worker := initTaskHubWorker(ctx, r)
105+
defer func() {
106+
if err := worker.Shutdown(ctx); err != nil {
107+
t.Logf("shutdown: %v", err)
108+
}
109+
}()
110+
111+
// Run the orchestration
112+
id, err := client.ScheduleNewOrchestration(ctx, "SingleTimerAt")
113+
if assert.NoError(t, err) {
114+
metadata, err := client.WaitForOrchestrationCompletion(ctx, id)
115+
if assert.NoError(t, err) {
116+
assert.Equal(t, protos.OrchestrationStatus_ORCHESTRATION_STATUS_COMPLETED, metadata.RuntimeStatus)
117+
assert.GreaterOrEqual(t, metadata.LastUpdatedAt, metadata.CreatedAt)
118+
}
119+
}
120+
121+
// Validate the exported OTel traces
122+
spans := exporter.GetSpans().Snapshots()
123+
assertSpanSequence(t, spans,
124+
assertOrchestratorCreated("SingleTimerAt", id),
125+
assertTimer(id),
126+
assertOrchestratorExecuted("SingleTimerAt", id, "COMPLETED"),
127+
)
128+
}
129+
91130
func Test_ConcurrentTimers(t *testing.T) {
92131
// Registration
93132
r := task.NewTaskRegistry()

0 commit comments

Comments
 (0)