Skip to content

Commit 0234aba

Browse files
authored
Update Scheduler WF ID (#360)
Previously, scheduler used ZoneDateTime string format when creating workflow IDs, but this embeds the time zone ID in the workflow id. Example: `sched-run_every_min-2026-04-20T14:40-07:00[America/Los_Angeles]`. This PR uses OffsetDateTime in the workflow ID, which keeps the offset but drops the zone ID. Example: `sched-run_every_min-2026-04-20T14:40-07:00`.
1 parent 3f691c9 commit 0234aba

3 files changed

Lines changed: 82 additions & 2 deletions

File tree

transact/src/main/java/dev/dbos/transact/execution/SchedulerService.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,9 @@ public void run() {
248248
return;
249249
}
250250
var args = new Object[] {nextTime.toInstant(), wfSchedule.context()};
251-
var workflowId = "sched-%s-%s".formatted(wfSchedule.scheduleName(), nextTime);
251+
var workflowId =
252+
"sched-%s-%s"
253+
.formatted(wfSchedule.scheduleName(), nextTime.toOffsetDateTime());
252254
logger.debug(
253255
"Queuing scheduled workflow {} schedule {} workflowId {}",
254256
regWorkflow.fullyQualifiedName(),
@@ -320,7 +322,8 @@ public void run() {
320322
return;
321323
}
322324
var args = new Object[] {scheduledTime.toInstant(), Instant.now()};
323-
var workflowId = "sched-%s-%s".formatted(workflowName, scheduledTime);
325+
var workflowId =
326+
"sched-%s-%s".formatted(workflowName, scheduledTime.toOffsetDateTime());
324327
logger.debug(
325328
"Triggering annotated workflow {} at {} workflowId {}",
326329
workflowName,

transact/src/test/java/dev/dbos/transact/scheduled/AnnotatedWorkflowScheduleTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import dev.dbos.transact.workflow.Queue;
1212

1313
import java.time.Duration;
14+
import java.time.OffsetDateTime;
1415

1516
import com.zaxxer.hikari.HikariDataSource;
1617
import org.junit.jupiter.api.AutoClose;
@@ -106,6 +107,32 @@ public void simpleScheduledWorkflow() throws Exception {
106107
assertTrue(count1dimb <= 14);
107108
}
108109

110+
@Test
111+
public void annotatedWorkflowIdUsesOffsetDateTimeFormat() throws Exception {
112+
// Annotated scheduler IDs must use OffsetDateTime, not ZonedDateTime with zone brackets.
113+
var q = new Queue("q2").withConcurrency(1);
114+
dbos.registerQueue(q);
115+
var impl = new AnnotatedScheduledServiceImpl(dbos);
116+
dbos.registerProxy(AnnotatedScheduledService.class, impl);
117+
dbos.launch();
118+
119+
Thread.sleep(3000);
120+
DBOSTestAccess.getSchedulerService(dbos).close();
121+
122+
var workflows = dbos.listWorkflows(new ListWorkflowsInput().withWorkflowName("everySecond"));
123+
assertFalse(workflows.isEmpty(), "Expected at least one everySecond execution");
124+
for (var wf : workflows) {
125+
var id = wf.workflowId();
126+
assertFalse(id.contains("["), "Annotated workflow ID must not contain zone brackets: " + id);
127+
// ID format: sched-{name}/{class}/-{offsetDateTime}
128+
// The fully-qualified name ends with '/', so the timestamp starts two chars after the
129+
// last '/' (skipping the trailing '-' separator).
130+
var suffix = id.substring(id.lastIndexOf('/') + 2);
131+
assertDoesNotThrow(
132+
() -> OffsetDateTime.parse(suffix), "Date suffix must be a valid OffsetDateTime: " + id);
133+
}
134+
}
135+
109136
@Test
110137
public void invalidSignature() {
111138
var e =

transact/src/test/java/dev/dbos/transact/scheduled/WorkflowScheduleTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
import dev.dbos.transact.DBOSTestAccess;
77
import dev.dbos.transact.config.DBOSConfig;
88
import dev.dbos.transact.utils.PgContainer;
9+
import dev.dbos.transact.workflow.ListWorkflowsInput;
910
import dev.dbos.transact.workflow.ScheduleStatus;
1011
import dev.dbos.transact.workflow.WorkflowSchedule;
1112

1213
import java.time.Duration;
1314
import java.time.Instant;
15+
import java.time.OffsetDateTime;
1416
import java.time.ZoneId;
1517
import java.time.temporal.ChronoUnit;
1618
import java.util.List;
@@ -597,6 +599,54 @@ public void triggerScheduleWithContext() throws Exception {
597599
assertEquals("test-context", impl.lastContext);
598600
}
599601

602+
// ── Workflow ID format ────────────────────────────────────────────────────
603+
604+
@Test
605+
public void workflowIdUsesOffsetDateTimeFormat() throws Exception {
606+
// Scheduler-generated IDs must use OffsetDateTime (e.g. 2026-04-20T10:15:30+00:00),
607+
// not ZonedDateTime which appends zone name in brackets (e.g. [UTC]).
608+
var impl = registerAndLaunch();
609+
dbos.createSchedule(
610+
new WorkflowSchedule("id-fmt-sched", "latchedRun", className(), "0/1 * * * * *"));
611+
612+
assertTrue(impl.latch.await(15, TimeUnit.SECONDS));
613+
614+
var prefix = "sched-id-fmt-sched-";
615+
var workflows = dbos.listWorkflows(new ListWorkflowsInput().withWorkflowIdPrefix(prefix));
616+
assertFalse(workflows.isEmpty());
617+
for (var wf : workflows) {
618+
var id = wf.workflowId();
619+
assertFalse(id.contains("["), "Workflow ID must not contain zone name brackets: " + id);
620+
assertDoesNotThrow(
621+
() -> OffsetDateTime.parse(id.substring(prefix.length())),
622+
"Date suffix must be a valid OffsetDateTime: " + id);
623+
}
624+
}
625+
626+
@Test
627+
public void workflowIdIncludesTimezoneOffsetNotZoneName() throws Exception {
628+
// When cronTimezone is a named zone (e.g. America/New_York), the ID must include the
629+
// numeric offset (e.g. -04:00) but not the zone name in brackets.
630+
var impl = registerAndLaunch();
631+
dbos.createSchedule(
632+
new WorkflowSchedule("tz-sched", "latchedRun", className(), "0/1 * * * * *")
633+
.withCronTimezone(ZoneId.of("America/New_York")));
634+
635+
assertTrue(impl.latch.await(15, TimeUnit.SECONDS));
636+
637+
var prefix = "sched-tz-sched-";
638+
var workflows = dbos.listWorkflows(new ListWorkflowsInput().withWorkflowIdPrefix(prefix));
639+
assertFalse(workflows.isEmpty());
640+
for (var wf : workflows) {
641+
var id = wf.workflowId();
642+
assertFalse(id.contains("["), "Workflow ID must not contain zone name brackets: " + id);
643+
assertFalse(id.contains("America"), "Workflow ID must not contain zone name: " + id);
644+
assertDoesNotThrow(
645+
() -> OffsetDateTime.parse(id.substring(prefix.length())),
646+
"Date suffix must be a valid OffsetDateTime: " + id);
647+
}
648+
}
649+
600650
// ── End-to-end ────────────────────────────────────────────────────────────
601651

602652
@Test

0 commit comments

Comments
 (0)