Skip to content

Commit 6895f0b

Browse files
authored
automatic time-skipping (#740)
<!-- Describe what has changed in this PR --> **What changed?** The new feature of automatic time-skipping is added. <!-- Are there any breaking changes on binary or code level? --> **Breaking changes** A new event type has been added that may break the WebUI if any workflows include this event. The feature should not be enabled until the WebUI is updated. For the SDK, this event will be skipped, so it will not affect older versions. <!-- If this breaks the Server, please provide the Server PR to merge right after this PR was merged. --> **Server PR** This change is not expected to break the server. No new RPC handlers have been introduced, and all new fields are optional.
1 parent 52ec420 commit 6895f0b

6 files changed

Lines changed: 224 additions & 2 deletions

File tree

openapi/openapiv2.json

Lines changed: 69 additions & 2 deletions
Large diffs are not rendered by default.

openapi/openapiv3.yaml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10658,6 +10658,7 @@ components:
1065810658
- EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED
1065910659
- EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED
1066010660
- EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED
10661+
- EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED
1066110662
type: string
1066210663
format: enum
1066310664
version:
@@ -10816,6 +10817,8 @@ components:
1081610817
$ref: '#/components/schemas/WorkflowExecutionPausedEventAttributes'
1081710818
workflowExecutionUnpausedEventAttributes:
1081810819
$ref: '#/components/schemas/WorkflowExecutionUnpausedEventAttributes'
10820+
workflowExecutionTimeSkippingTransitionedEventAttributes:
10821+
$ref: '#/components/schemas/WorkflowExecutionTimeSkippingTransitionedEventAttributes'
1081910822
description: |-
1082010823
History events are the method by which Temporal SDKs advance (or recreate) workflow state.
1082110824
See the `EventType` enum for more info about what each event is for.
@@ -11568,6 +11571,8 @@ components:
1156811571
Calls are retried internally by the server.
1156911572
(-- api-linter: core::0140::prepositions=disabled
1157011573
aip.dev/not-precedent: "to" is used to indicate interval. --)
11574+
(-- api-linter: core::0142::time-field-names=disabled
11575+
aip.dev/not-precedent: "timeout" is an acceptable suffix for duration fields in this API. --)
1157111576
nexusHeader:
1157211577
type: object
1157311578
additionalProperties:
@@ -12759,6 +12764,7 @@ components:
1275912764
- EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED
1276012765
- EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED
1276112766
- EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED
12767+
- EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED
1276212768
type: string
1276312769
description: The event type of the history event generated by the request.
1276412770
format: enum
@@ -14079,6 +14085,10 @@ components:
1407914085
allOf:
1408014086
- $ref: '#/components/schemas/Priority'
1408114087
description: Priority metadata
14088+
timeSkippingConfig:
14089+
allOf:
14090+
- $ref: '#/components/schemas/TimeSkippingConfig'
14091+
description: Time-skipping configuration. If not set, time skipping is disabled.
1408214092
SignalWithStartWorkflowExecutionResponse:
1408314093
type: object
1408414094
properties:
@@ -14541,6 +14551,10 @@ components:
1454114551
allOf:
1454214552
- $ref: '#/components/schemas/WorkerDeploymentOptions'
1454314553
description: Deployment Options of the worker who will process the eager task. Passed when `request_eager_execution=true`.
14554+
timeSkippingConfig:
14555+
allOf:
14556+
- $ref: '#/components/schemas/TimeSkippingConfig'
14557+
description: Time-skipping configuration. If not set, time skipping is disabled.
1454414558
StartWorkflowExecutionResponse:
1454514559
type: object
1454614560
properties:
@@ -14944,6 +14958,38 @@ components:
1494414958
TerminatedFailureInfo:
1494514959
type: object
1494614960
properties: {}
14961+
TimeSkippingConfig:
14962+
type: object
14963+
properties:
14964+
enabled:
14965+
type: boolean
14966+
description: "Enables or disables time skipping for this workflow execution.\n By default, this field is propagated to transitively related workflows (child workflows/start-as-new/reset) \n at the time they are started.\n Changes made after a transitively related workflow has started are not propagated."
14967+
disablePropagation:
14968+
type: boolean
14969+
description: If set, the enabled field is not propagated to transitively related workflows.
14970+
maxSkippedDuration:
14971+
pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$
14972+
type: string
14973+
description: Maximum total virtual time that can be skipped.
14974+
maxElapsedDuration:
14975+
pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$
14976+
type: string
14977+
description: |-
14978+
Maximum elapsed time since time skipping was enabled.
14979+
This includes both skipped time and real time elapsing.
14980+
maxTargetTime:
14981+
type: string
14982+
description: |-
14983+
Absolute virtual timestamp at which time skipping is disabled.
14984+
Time skipping will not advance beyond this point.
14985+
format: date-time
14986+
description: |-
14987+
Configuration for time skipping during a workflow execution.
14988+
When enabled, virtual time advances automatically whenever there is no in-flight work.
14989+
In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,
14990+
and possibly other features added in the future.
14991+
User timers are not classified as in-flight work and will be skipped over.
14992+
When time advances, it skips to the earlier of the next user timer or the configured bound, if either exists.
1494714993
TimeoutFailureInfo:
1494814994
type: object
1494914995
properties:
@@ -16458,6 +16504,7 @@ components:
1645816504
- EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED
1645916505
- EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED
1646016506
- EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED
16507+
- EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED
1646116508
type: string
1646216509
format: enum
1646316510
description: EventReference is a direct reference to a history event through the event ID.
@@ -16528,6 +16575,7 @@ components:
1652816575
- EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED
1652916576
- EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED
1653016577
- EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED
16578+
- EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED
1653116579
type: string
1653216580
format: enum
1653316581
description: RequestIdReference is a indirect reference to a history event through the request ID.
@@ -16877,6 +16925,10 @@ components:
1687716925
allOf:
1687816926
- $ref: '#/components/schemas/Priority'
1687916927
description: If set, overrides the workflow's priority sent by the SDK.
16928+
timeSkippingConfig:
16929+
allOf:
16930+
- $ref: '#/components/schemas/TimeSkippingConfig'
16931+
description: "Time-skipping configuration for this workflow execution.\n If not set, the time-skipping conf will not get updated upon request, \n i.e. the existing time-skipping conf will be preserved."
1688016932
WorkflowExecutionOptionsUpdatedEventAttributes:
1688116933
type: object
1688216934
properties:
@@ -16908,6 +16960,10 @@ components:
1690816960
description: |-
1690916961
Priority override upserted in this event. Represents the full priority; not just partial fields.
1691016962
Ignored if nil.
16963+
timeSkippingConfig:
16964+
allOf:
16965+
- $ref: '#/components/schemas/TimeSkippingConfig'
16966+
description: If set, the time-skipping configuration was changed. Contains the full updated configuration.
1691116967
WorkflowExecutionPauseInfo:
1691216968
type: object
1691316969
properties:
@@ -17213,6 +17269,27 @@ components:
1721317269
identity:
1721417270
type: string
1721517271
description: id of the client who requested termination
17272+
WorkflowExecutionTimeSkippingTransitionedEventAttributes:
17273+
type: object
17274+
properties:
17275+
targetTime:
17276+
type: string
17277+
description: The virtual time after time skipping was applied.
17278+
format: date-time
17279+
disabledAfterBound:
17280+
type: boolean
17281+
description: |-
17282+
when true, time skipping was disabled automatically due to a bound being reached.
17283+
(-- api-linter: core::0140::prepositions=disabled
17284+
aip.dev/not-precedent: "after" is used to indicate temporal ordering. --)
17285+
wallClockTime:
17286+
type: string
17287+
description: The wall-clock time when the time-skipping state changed event was generated.
17288+
format: date-time
17289+
description: |-
17290+
Attributes for an event indicating that time skipping state changed for a workflow execution,
17291+
either time was advanced or time skipping was disabled automatically due to a bound being reached.
17292+
The worker_may_ignore field in HistoryEvent should always be set true for this event.
1721617293
WorkflowExecutionTimedOutEventAttributes:
1721717294
type: object
1721817295
properties:

temporal/api/enums/v1/event_type.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,6 @@ enum EventType {
173173
EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED = 58;
174174
// An event that indicates that the previously paused workflow execution has been unpaused.
175175
EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED = 59;
176+
// An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.
177+
EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED = 60;
176178
}

temporal/api/history/v1/message.proto

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,8 @@ message WorkflowExecutionOptionsUpdatedEventAttributes {
873873
// Priority override upserted in this event. Represents the full priority; not just partial fields.
874874
// Ignored if nil.
875875
temporal.api.common.v1.Priority priority = 6;
876+
// If set, the time-skipping configuration was changed. Contains the full updated configuration.
877+
temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 7;
876878
}
877879

878880
// Not used anywhere. Use case is replaced by WorkflowExecutionOptionsUpdatedEventAttributes
@@ -965,6 +967,23 @@ message WorkflowExecutionUnpausedEventAttributes {
965967
string request_id = 3;
966968
}
967969

970+
// Attributes for an event indicating that time skipping state changed for a workflow execution,
971+
// either time was advanced or time skipping was disabled automatically due to a bound being reached.
972+
// The worker_may_ignore field in HistoryEvent should always be set true for this event.
973+
message WorkflowExecutionTimeSkippingTransitionedEventAttributes {
974+
975+
// The virtual time after time skipping was applied.
976+
google.protobuf.Timestamp target_time = 1;
977+
978+
// when true, time skipping was disabled automatically due to a bound being reached.
979+
// (-- api-linter: core::0140::prepositions=disabled
980+
// aip.dev/not-precedent: "after" is used to indicate temporal ordering. --)
981+
bool disabled_after_bound = 2;
982+
983+
// The wall-clock time when the time-skipping state changed event was generated.
984+
google.protobuf.Timestamp wall_clock_time = 3;
985+
}
986+
968987
// Event marking that an operation was scheduled by a workflow via the ScheduleNexusOperation command.
969988
message NexusOperationScheduledEventAttributes {
970989
// Endpoint name, must exist in the endpoint registry.
@@ -982,6 +1001,8 @@ message NexusOperationScheduledEventAttributes {
9821001
// Calls are retried internally by the server.
9831002
// (-- api-linter: core::0140::prepositions=disabled
9841003
// aip.dev/not-precedent: "to" is used to indicate interval. --)
1004+
// (-- api-linter: core::0142::time-field-names=disabled
1005+
// aip.dev/not-precedent: "timeout" is an acceptable suffix for duration fields in this API. --)
9851006
google.protobuf.Duration schedule_to_close_timeout = 5;
9861007
// Header to attach to the Nexus request. Note these headers are not the same as Temporal headers on internal
9871008
// activities and child workflows, these are transmitted to Nexus operations that may be external and are not
@@ -1199,6 +1220,7 @@ message HistoryEvent {
11991220
NexusOperationCancelRequestFailedEventAttributes nexus_operation_cancel_request_failed_event_attributes = 62;
12001221
WorkflowExecutionPausedEventAttributes workflow_execution_paused_event_attributes = 63;
12011222
WorkflowExecutionUnpausedEventAttributes workflow_execution_unpaused_event_attributes = 64;
1223+
WorkflowExecutionTimeSkippingTransitionedEventAttributes workflow_execution_time_skipping_transitioned_event_attributes = 65;
12021224
}
12031225
}
12041226

temporal/api/workflow/v1/message.proto

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,56 @@ message WorkflowExecutionOptions {
566566

567567
// If set, overrides the workflow's priority sent by the SDK.
568568
temporal.api.common.v1.Priority priority = 2;
569+
570+
// Time-skipping configuration for this workflow execution.
571+
// If not set, the time-skipping conf will not get updated upon request,
572+
// i.e. the existing time-skipping conf will be preserved.
573+
TimeSkippingConfig time_skipping_config = 3;
574+
}
575+
576+
// Configuration for time skipping during a workflow execution.
577+
// When enabled, virtual time advances automatically whenever there is no in-flight work.
578+
// In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,
579+
// and possibly other features added in the future.
580+
// User timers are not classified as in-flight work and will be skipped over.
581+
// When time advances, it skips to the earlier of the next user timer or the configured bound, if either exists.
582+
message TimeSkippingConfig {
583+
584+
// Enables or disables time skipping for this workflow execution.
585+
// By default, this field is propagated to transitively related workflows (child workflows/start-as-new/reset)
586+
// at the time they are started.
587+
// Changes made after a transitively related workflow has started are not propagated.
588+
bool enabled = 1;
589+
590+
// If set, the enabled field is not propagated to transitively related workflows.
591+
bool disable_propagation = 2;
592+
593+
// Optional bound that limits how long time skipping remains active.
594+
// Once the bound is reached, time skipping is automatically disabled.
595+
// It can later be re-enabled via UpdateWorkflowExecutionOptions.
596+
//
597+
// This is particularly useful in testing scenarios where workflows
598+
// are expected to receive signals, updates, or other events while
599+
// timers are in progress.
600+
//
601+
// This bound is not propagated to transitively related workflows.
602+
// When bound is also needed for transitively related workflows,
603+
// it is recommended to set disable_propagation to true
604+
// and configure TimeSkippingConfig explicitly for transitively related workflows.
605+
oneof bound {
606+
607+
// Maximum total virtual time that can be skipped.
608+
google.protobuf.Duration max_skipped_duration = 4;
609+
610+
// Maximum elapsed time since time skipping was enabled.
611+
// This includes both skipped time and real time elapsing.
612+
// (-- api-linter: core::0142::time-field-names=disabled --)
613+
google.protobuf.Duration max_elapsed_duration = 5;
614+
615+
// Absolute virtual timestamp at which time skipping is disabled.
616+
// Time skipping will not advance beyond this point.
617+
google.protobuf.Timestamp max_target_time = 6;
618+
}
569619
}
570620

571621
// Used to override the versioning behavior (and pinned deployment version, if applicable) of a

temporal/api/workflowservice/v1/request_response.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ message StartWorkflowExecutionRequest {
198198
temporal.api.common.v1.Priority priority = 27;
199199
// Deployment Options of the worker who will process the eager task. Passed when `request_eager_execution=true`.
200200
temporal.api.deployment.v1.WorkerDeploymentOptions eager_worker_deployment_options = 28;
201+
// Time-skipping configuration. If not set, time skipping is disabled.
202+
temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 29;
201203
}
202204

203205
message StartWorkflowExecutionResponse {
@@ -837,6 +839,8 @@ message SignalWithStartWorkflowExecutionRequest {
837839
temporal.api.workflow.v1.VersioningOverride versioning_override = 25;
838840
// Priority metadata
839841
temporal.api.common.v1.Priority priority = 26;
842+
// Time-skipping configuration. If not set, time skipping is disabled.
843+
temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 27;
840844
}
841845

842846
message SignalWithStartWorkflowExecutionResponse {

0 commit comments

Comments
 (0)