From 02743678958c6a10dde55f99e53398e66ce6b22d Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Fri, 22 May 2026 15:41:41 -0700 Subject: [PATCH 1/9] refactor time skipping config and event --- Makefile | 2 +- openapi/openapiv2.json | 25 +++++---- openapi/openapiv3.yaml | 46 +++++++++++----- temporal/api/history/v1/message.proto | 9 ++-- temporal/api/workflow/v1/message.proto | 54 +++++++------------ .../workflowservice/v1/request_response.proto | 4 ++ 6 files changed, 78 insertions(+), 62 deletions(-) diff --git a/Makefile b/Makefile index dc8ab5025..02aaeeb41 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ $(PROTO_OUT): mkdir $(PROTO_OUT) ##### Compile proto files for go ##### -grpc: buf-lint api-linter buf-breaking clean go-grpc fix-path +grpc: buf-lint api-linter clean go-grpc fix-path go-grpc: clean $(PROTO_OUT) printf $(COLOR) "Compile for go-gRPC..." diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 40bd92a3c..c304fc9f4 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -19678,16 +19678,16 @@ "type": "boolean", "description": "Enables or disables time skipping for this workflow execution." }, - "maxSkippedDuration": { + "sleep": { "type": "string", - "description": "Maximum total virtual time that can be skipped." + "description": "Optionally register a sleep for the current workflow execution.\nWhen the sleep completes, time skipping is disabled and this action is recorded in\nthe WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this sleep won't affect them." }, - "maxElapsedDuration": { - "type": "string", - "description": "Maximum elapsed time since time skipping was enabled.\nThis includes both skipped time and real time elapsing." + "disableChildPropagation": { + "type": "boolean", + "description": "By default, child workflows inherit the \"enabled\" flag when they are started.\nThis flag disables that inheritance." } }, - "description": "Configuration for time skipping during a workflow execution.\nWhen enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over.\nWhen time advances, it skips to the earlier of the next user timer or the configured bound, if either exists.\n\nPropagation behavior of time skipping:\nThe enabled flag, bound fields, and accumulated skipped duration are propagated to related executions as follows:\n(1) Child workflows and continue-as-new: both the configuration and the accumulated skipped duration are\n inherited from the current execution. The configured bound is shared between the inherited skipped\n duration and any additional duration skipped by the new run.\n(2) Retry and cron: the configuration and accumulated skipped duration are inherited as recorded when the\n current workflow started; the accumulated skipped duration of the current run is not propagated.\n(3) Reset: the new run retains the time-skipping configuration of the current execution. Because reset replays\n all events up to the reset point and re-applies any UpdateWorkflowExecutionOptions changes made after that\n point, the resulting run ends up with the same final time-skipping configuration as the previous run." + "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered sleep when there is no in-flight work.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut the parent's sleep won't affect the child execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." }, "v1TimeoutFailureInfo": { "type": "object", @@ -19984,6 +19984,11 @@ "workflowExecutionOptions": { "$ref": "#/definitions/v1WorkflowExecutionOptions", "description": "Workflow Execution options after update." + }, + "updateTime": { + "type": "string", + "format": "date-time", + "description": "The Workflow Execution time when the options were updated. When time skipping is\nenabled, this is the workflow's virtual time rather than wall-clock time." } } }, @@ -21313,11 +21318,11 @@ "targetTime": { "type": "string", "format": "date-time", - "description": "The virtual time after time skipping was applied." + "description": "The virtual time point that time skipping advanced to." }, - "disabledAfterBound": { + "disabledAfterSleep": { "type": "boolean", - "description": "when true, time skipping was disabled automatically due to a bound being reached." + "description": "When true, time skipping has been disabled automatically due to the sleep completing." }, "wallClockTime": { "type": "string", @@ -21325,7 +21330,7 @@ "description": "The wall-clock time when the time-skipping state changed event was generated." } }, - "description": "Attributes for an event indicating that time skipping state changed for a workflow execution,\neither time was advanced or time skipping was disabled automatically due to a bound being reached.\nThe worker_may_ignore field in HistoryEvent should always be set true for this event." + "description": "Attributes for an event indicating that time skipping state changed for a workflow execution,\neither time was advanced or time skipping was disabled automatically due to the sleep completing.\nThe worker_may_ignore field in HistoryEvent should always be set true for this event." }, "v1WorkflowExecutionTimedOutEventAttributes": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 378cc5d80..157102997 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17430,17 +17430,33 @@ components: enabled: type: boolean description: Enables or disables time skipping for this workflow execution. - maxSkippedDuration: - pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ - type: string - description: Maximum total virtual time that can be skipped. - maxElapsedDuration: + sleep: pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ type: string description: |- - Maximum elapsed time since time skipping was enabled. - This includes both skipped time and real time elapsing. - description: "Configuration for time skipping during a workflow execution.\n When enabled, virtual time advances automatically whenever there is no in-flight work.\n In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\n and possibly other features added in the future.\n User timers are not classified as in-flight work and will be skipped over.\n When time advances, it skips to the earlier of the next user timer or the configured bound, if either exists.\n \n Propagation behavior of time skipping:\n The enabled flag, bound fields, and accumulated skipped duration are propagated to related executions as follows:\n (1) Child workflows and continue-as-new: both the configuration and the accumulated skipped duration are\n inherited from the current execution. The configured bound is shared between the inherited skipped\n duration and any additional duration skipped by the new run.\n (2) Retry and cron: the configuration and accumulated skipped duration are inherited as recorded when the\n current workflow started; the accumulated skipped duration of the current run is not propagated.\n (3) Reset: the new run retains the time-skipping configuration of the current execution. Because reset replays\n all events up to the reset point and re-applies any UpdateWorkflowExecutionOptions changes made after that\n point, the resulting run ends up with the same final time-skipping configuration as the previous run." + Optionally register a sleep for the current workflow execution. + When the sleep completes, time skipping is disabled and this action is recorded in + the WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by + setting `enabled` to true via UpdateWorkflowExecutionOptions. + The current workflow execution is a chain of runs (retries, cron, continue-as-new); + child workflows are separate executions, so this sleep won't affect them. + disableChildPropagation: + type: boolean + description: |- + By default, child workflows inherit the "enabled" flag when they are started. + This flag disables that inheritance. + description: |- + The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new). + When time skipping is enabled, virtual time advances automatically whenever there is no in-flight work. + In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, + and possibly other features added in the future. + User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the + time point of the registered sleep when there is no in-flight work. + + For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, + but the parent's sleep won't affect the child execution. A flag is provided to disable propagation of the + "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the + parent execution as its start time. TimeoutFailureInfo: type: object properties: @@ -18105,6 +18121,12 @@ components: allOf: - $ref: '#/components/schemas/WorkflowExecutionOptions' description: Workflow Execution options after update. + updateTime: + type: string + description: |- + The Workflow Execution time when the options were updated. When time skipping is + enabled, this is the workflow's virtual time rather than wall-clock time. + format: date-time UpdateWorkflowExecutionRequest: type: object properties: @@ -19875,12 +19897,12 @@ components: properties: targetTime: type: string - description: The virtual time after time skipping was applied. + description: The virtual time point that time skipping advanced to. format: date-time - disabledAfterBound: + disabledAfterSleep: type: boolean description: |- - when true, time skipping was disabled automatically due to a bound being reached. + When true, time skipping has been disabled automatically due to the sleep completing. (-- api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: "after" is used to indicate temporal ordering. --) wallClockTime: @@ -19889,7 +19911,7 @@ components: format: date-time description: |- Attributes for an event indicating that time skipping state changed for a workflow execution, - either time was advanced or time skipping was disabled automatically due to a bound being reached. + either time was advanced or time skipping was disabled automatically due to the sleep completing. The worker_may_ignore field in HistoryEvent should always be set true for this event. WorkflowExecutionTimedOutEventAttributes: type: object diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 32f4f83cc..6a498611e 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -1003,17 +1003,16 @@ message WorkflowExecutionUnpausedEventAttributes { } // Attributes for an event indicating that time skipping state changed for a workflow execution, -// either time was advanced or time skipping was disabled automatically due to a bound being reached. +// either time was advanced or time skipping was disabled automatically due to the sleep completing. // The worker_may_ignore field in HistoryEvent should always be set true for this event. message WorkflowExecutionTimeSkippingTransitionedEventAttributes { - - // The virtual time after time skipping was applied. + // The virtual time point that time skipping advanced to. google.protobuf.Timestamp target_time = 1; - // when true, time skipping was disabled automatically due to a bound being reached. + // When true, time skipping has been disabled automatically due to the sleep completing. // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "after" is used to indicate temporal ordering. --) - bool disabled_after_bound = 2; + bool disabled_after_sleep = 2; // The wall-clock time when the time-skipping state changed event was generated. google.protobuf.Timestamp wall_clock_time = 3; diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index fdca1df4d..aa514d4bb 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -591,46 +591,32 @@ message WorkflowExecutionOptions { TimeSkippingConfig time_skipping_config = 3; } -// Configuration for time skipping during a workflow execution. -// When enabled, virtual time advances automatically whenever there is no in-flight work. +// The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new). +// When time skipping is enabled, virtual time advances automatically whenever there is no in-flight work. // In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, // and possibly other features added in the future. -// User timers are not classified as in-flight work and will be skipped over. -// When time advances, it skips to the earlier of the next user timer or the configured bound, if either exists. -// -// Propagation behavior of time skipping: -// The enabled flag, bound fields, and accumulated skipped duration are propagated to related executions as follows: -// (1) Child workflows and continue-as-new: both the configuration and the accumulated skipped duration are -// inherited from the current execution. The configured bound is shared between the inherited skipped -// duration and any additional duration skipped by the new run. -// (2) Retry and cron: the configuration and accumulated skipped duration are inherited as recorded when the -// current workflow started; the accumulated skipped duration of the current run is not propagated. -// (3) Reset: the new run retains the time-skipping configuration of the current execution. Because reset replays -// all events up to the reset point and re-applies any UpdateWorkflowExecutionOptions changes made after that -// point, the resulting run ends up with the same final time-skipping configuration as the previous run. +// User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the +// time point of the registered sleep when there is no in-flight work. +// +// For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, +// but the parent's sleep won't affect the child execution. A flag is provided to disable propagation of the +// "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the +// parent execution as its start time. message TimeSkippingConfig { - reserved 2, 6; - reserved "disable_propagation", "max_target_time"; - // Enables or disables time skipping for this workflow execution. bool enabled = 1; - // Optional bound that limits the gap between the virtual time of this execution and wall-clock time. - // Once the bound is reached, time skipping is automatically disabled, - // but can be re-enabled by setting `enabled` to true via UpdateWorkflowExecutionOptions. - // This bound cannot be set to a value smaller than the execution's currently skipped duration. - // - // This is useful in testing scenarios where a workflow is expected to receive - // signals, updates, or other external events while timers are in progress. - oneof bound { - // Maximum total virtual time that can be skipped. - google.protobuf.Duration max_skipped_duration = 4; - - // Maximum elapsed time since time skipping was enabled. - // This includes both skipped time and real time elapsing. - // (-- api-linter: core::0142::time-field-names=disabled --) - google.protobuf.Duration max_elapsed_duration = 5; - } + // Optionally register a sleep for the current workflow execution. + // When the sleep completes, time skipping is disabled and this action is recorded in + // the WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by + // setting `enabled` to true via UpdateWorkflowExecutionOptions. + // The current workflow execution is a chain of runs (retries, cron, continue-as-new); + // child workflows are separate executions, so this sleep won't affect them. + google.protobuf.Duration sleep = 2; + + // By default, child workflows inherit the "enabled" flag when they are started. + // This flag disables that inheritance. + bool disable_child_propagation = 3; } // Used to override the versioning behavior (and pinned deployment version, if applicable) of a diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index b19ca0001..588a92d91 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -2387,6 +2387,10 @@ message UpdateWorkflowExecutionOptionsRequest { message UpdateWorkflowExecutionOptionsResponse { // Workflow Execution options after update. temporal.api.workflow.v1.WorkflowExecutionOptions workflow_execution_options = 1; + + // The Workflow Execution time when the options were updated. When time skipping is + // enabled, this is the workflow's virtual time rather than wall-clock time. + google.protobuf.Timestamp update_time = 2; } // [cleanup-wv-pre-release] Pre-release deployment APIs, clean up later From 807db8641428008701133437162bdb911e9ba1f4 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Fri, 5 Jun 2026 12:50:18 -0700 Subject: [PATCH 2/9] change sleep to fast forward --- openapi/openapiv2.json | 12 ++++++------ openapi/openapiv3.yaml | 18 +++++++++--------- temporal/api/history/v1/message.proto | 6 +++--- temporal/api/workflow/v1/message.proto | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index c304fc9f4..388801256 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -19678,16 +19678,16 @@ "type": "boolean", "description": "Enables or disables time skipping for this workflow execution." }, - "sleep": { + "fastForward": { "type": "string", - "description": "Optionally register a sleep for the current workflow execution.\nWhen the sleep completes, time skipping is disabled and this action is recorded in\nthe WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this sleep won't affect them." + "description": "Optionally fast-forward the current workflow execution to this point in time.\nWhen the fast-forward completes, time skipping is disabled and this action is recorded in\nthe WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this fast_forward won't affect them." }, "disableChildPropagation": { "type": "boolean", "description": "By default, child workflows inherit the \"enabled\" flag when they are started.\nThis flag disables that inheritance." } }, - "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered sleep when there is no in-flight work.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut the parent's sleep won't affect the child execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." + "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered fast_forward when there is no in-flight work.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut the parent's fast_forward won't affect the child execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." }, "v1TimeoutFailureInfo": { "type": "object", @@ -21320,9 +21320,9 @@ "format": "date-time", "description": "The virtual time point that time skipping advanced to." }, - "disabledAfterSleep": { + "disabledAfterFastForward": { "type": "boolean", - "description": "When true, time skipping has been disabled automatically due to the sleep completing." + "description": "When true, time skipping has been disabled automatically due to the fast_forward completing." }, "wallClockTime": { "type": "string", @@ -21330,7 +21330,7 @@ "description": "The wall-clock time when the time-skipping state changed event was generated." } }, - "description": "Attributes for an event indicating that time skipping state changed for a workflow execution,\neither time was advanced or time skipping was disabled automatically due to the sleep completing.\nThe worker_may_ignore field in HistoryEvent should always be set true for this event." + "description": "Attributes for an event indicating that time skipping state changed for a workflow execution,\neither time was advanced or time skipping was disabled automatically due to the fast_forward completing.\nThe worker_may_ignore field in HistoryEvent should always be set true for this event." }, "v1WorkflowExecutionTimedOutEventAttributes": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 157102997..21441ec24 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17430,16 +17430,16 @@ components: enabled: type: boolean description: Enables or disables time skipping for this workflow execution. - sleep: + fastForward: pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ type: string description: |- - Optionally register a sleep for the current workflow execution. - When the sleep completes, time skipping is disabled and this action is recorded in + Optionally fast-forward the current workflow execution to this point in time. + When the fast-forward completes, time skipping is disabled and this action is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by setting `enabled` to true via UpdateWorkflowExecutionOptions. The current workflow execution is a chain of runs (retries, cron, continue-as-new); - child workflows are separate executions, so this sleep won't affect them. + child workflows are separate executions, so this fast_forward won't affect them. disableChildPropagation: type: boolean description: |- @@ -17451,10 +17451,10 @@ components: In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, and possibly other features added in the future. User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the - time point of the registered sleep when there is no in-flight work. + time point of the registered fast_forward when there is no in-flight work. For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, - but the parent's sleep won't affect the child execution. A flag is provided to disable propagation of the + but the parent's fast_forward won't affect the child execution. A flag is provided to disable propagation of the "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the parent execution as its start time. TimeoutFailureInfo: @@ -19899,10 +19899,10 @@ components: type: string description: The virtual time point that time skipping advanced to. format: date-time - disabledAfterSleep: + disabledAfterFastForward: type: boolean description: |- - When true, time skipping has been disabled automatically due to the sleep completing. + When true, time skipping has been disabled automatically due to the fast_forward completing. (-- api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: "after" is used to indicate temporal ordering. --) wallClockTime: @@ -19911,7 +19911,7 @@ components: format: date-time description: |- Attributes for an event indicating that time skipping state changed for a workflow execution, - either time was advanced or time skipping was disabled automatically due to the sleep completing. + either time was advanced or time skipping was disabled automatically due to the fast_forward completing. The worker_may_ignore field in HistoryEvent should always be set true for this event. WorkflowExecutionTimedOutEventAttributes: type: object diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 6a498611e..afc6be391 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -1003,16 +1003,16 @@ message WorkflowExecutionUnpausedEventAttributes { } // Attributes for an event indicating that time skipping state changed for a workflow execution, -// either time was advanced or time skipping was disabled automatically due to the sleep completing. +// either time was advanced or time skipping was disabled automatically due to the fast_forward completing. // The worker_may_ignore field in HistoryEvent should always be set true for this event. message WorkflowExecutionTimeSkippingTransitionedEventAttributes { // The virtual time point that time skipping advanced to. google.protobuf.Timestamp target_time = 1; - // When true, time skipping has been disabled automatically due to the sleep completing. + // When true, time skipping has been disabled automatically due to the fast_forward completing. // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "after" is used to indicate temporal ordering. --) - bool disabled_after_sleep = 2; + bool disabled_after_fast_forward = 2; // The wall-clock time when the time-skipping state changed event was generated. google.protobuf.Timestamp wall_clock_time = 3; diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index aa514d4bb..c293efaeb 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -596,23 +596,23 @@ message WorkflowExecutionOptions { // In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, // and possibly other features added in the future. // User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the -// time point of the registered sleep when there is no in-flight work. +// time point of the registered fast_forward when there is no in-flight work. // // For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, -// but the parent's sleep won't affect the child execution. A flag is provided to disable propagation of the +// but the parent's fast_forward won't affect the child execution. A flag is provided to disable propagation of the // "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the // parent execution as its start time. message TimeSkippingConfig { // Enables or disables time skipping for this workflow execution. bool enabled = 1; - // Optionally register a sleep for the current workflow execution. - // When the sleep completes, time skipping is disabled and this action is recorded in + // Optionally fast-forward the current workflow execution to this point in time. + // When the fast-forward completes, time skipping is disabled and this action is recorded in // the WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by // setting `enabled` to true via UpdateWorkflowExecutionOptions. // The current workflow execution is a chain of runs (retries, cron, continue-as-new); - // child workflows are separate executions, so this sleep won't affect them. - google.protobuf.Duration sleep = 2; + // child workflows are separate executions, so this fast_forward won't affect them. + google.protobuf.Duration fast_forward = 2; // By default, child workflows inherit the "enabled" flag when they are started. // This flag disables that inheritance. From c0965cbdc23750739c337fa3b1c72fa2e4786055 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Mon, 8 Jun 2026 12:02:53 -0700 Subject: [PATCH 3/9] comment refinements Co-authored-by: Spencer Judge --- openapi/openapiv2.json | 6 +++--- openapi/openapiv3.yaml | 10 +++++----- temporal/api/history/v1/message.proto | 2 +- temporal/api/workflow/v1/message.proto | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 388801256..52427c166 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -19680,14 +19680,14 @@ }, "fastForward": { "type": "string", - "description": "Optionally fast-forward the current workflow execution to this point in time.\nWhen the fast-forward completes, time skipping is disabled and this action is recorded in\nthe WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this fast_forward won't affect them." + "description": "Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time.\nWhen the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this\naction is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this fast_forward won't affect them." }, "disableChildPropagation": { "type": "boolean", "description": "By default, child workflows inherit the \"enabled\" flag when they are started.\nThis flag disables that inheritance." } }, - "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered fast_forward when there is no in-flight work.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut the parent's fast_forward won't affect the child execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." + "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered fast_forward when there is no in-flight work.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." }, "v1TimeoutFailureInfo": { "type": "object", @@ -21322,7 +21322,7 @@ }, "disabledAfterFastForward": { "type": "boolean", - "description": "When true, time skipping has been disabled automatically due to the fast_forward completing." + "description": "When true, time skipping has been disabled automatically due to a call to fast_forward completing." }, "wallClockTime": { "type": "string", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 21441ec24..763b78c23 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17434,9 +17434,9 @@ components: pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ type: string description: |- - Optionally fast-forward the current workflow execution to this point in time. - When the fast-forward completes, time skipping is disabled and this action is recorded in - the WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by + Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time. + When the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this + action is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by setting `enabled` to true via UpdateWorkflowExecutionOptions. The current workflow execution is a chain of runs (retries, cron, continue-as-new); child workflows are separate executions, so this fast_forward won't affect them. @@ -17454,7 +17454,7 @@ components: time point of the registered fast_forward when there is no in-flight work. For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, - but the parent's fast_forward won't affect the child execution. A flag is provided to disable propagation of the + but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the parent execution as its start time. TimeoutFailureInfo: @@ -19902,7 +19902,7 @@ components: disabledAfterFastForward: type: boolean description: |- - When true, time skipping has been disabled automatically due to the fast_forward completing. + When true, time skipping has been disabled automatically due to a call to fast_forward completing. (-- api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: "after" is used to indicate temporal ordering. --) wallClockTime: diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index afc6be391..d17317b30 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -1009,7 +1009,7 @@ message WorkflowExecutionTimeSkippingTransitionedEventAttributes { // The virtual time point that time skipping advanced to. google.protobuf.Timestamp target_time = 1; - // When true, time skipping has been disabled automatically due to the fast_forward completing. + // When true, time skipping has been disabled automatically due to a call to fast_forward completing. // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "after" is used to indicate temporal ordering. --) bool disabled_after_fast_forward = 2; diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index c293efaeb..492308b5b 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -599,16 +599,16 @@ message WorkflowExecutionOptions { // time point of the registered fast_forward when there is no in-flight work. // // For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, -// but the parent's fast_forward won't affect the child execution. A flag is provided to disable propagation of the +// but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the // "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the // parent execution as its start time. message TimeSkippingConfig { // Enables or disables time skipping for this workflow execution. bool enabled = 1; - // Optionally fast-forward the current workflow execution to this point in time. - // When the fast-forward completes, time skipping is disabled and this action is recorded in - // the WorkflowExecutionTimeSkippingTransitionedEvent, but it can be re-enabled by + // Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time. + // When the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this + // action is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by // setting `enabled` to true via UpdateWorkflowExecutionOptions. // The current workflow execution is a chain of runs (retries, cron, continue-as-new); // child workflows are separate executions, so this fast_forward won't affect them. From 3187a627945db0eb18e4bd5451befaca6604cc14 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Tue, 9 Jun 2026 20:51:30 -0700 Subject: [PATCH 4/9] add TimeSkippingStatePropagation --- openapi/openapiv2.json | 27 ++++++++++++++---- openapi/openapiv3.yaml | 38 ++++++++++++++++++++------ temporal/api/history/v1/message.proto | 19 +++++++++---- temporal/api/workflow/v1/message.proto | 14 ++++++++++ 4 files changed, 78 insertions(+), 20 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 52427c166..2e09d56a5 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -19285,9 +19285,9 @@ "$ref": "#/definitions/v1TimeSkippingConfig", "description": "The propagated time-skipping configuration for the child workflow." }, - "initialSkippedDuration": { - "type": "string", - "description": "Propagate the duration skipped to the child workflow." + "timeSkippingStatePropagation": { + "$ref": "#/definitions/v1TimeSkippingStatePropagation", + "description": "The time-skipping state propagated from the parent workflow. This can be nil if no time skipping\nhas occurred or there is no previous run." } } }, @@ -19689,6 +19689,21 @@ }, "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered fast_forward when there is no in-flight work.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." }, + "v1TimeSkippingStatePropagation": { + "type": "object", + "properties": { + "initialSkippedDuration": { + "type": "string", + "description": "The time skipped by the previous execution that started this workflow.\nIt can happen in child workflows and a chain of runs (CaN, cron, retry)." + }, + "fastForwardTargetTime": { + "type": "string", + "format": "date-time", + "description": "If there is a fast-forward action set for the previous run in a chain of runs,\nthe target time should be propagated to the next run as well." + } + }, + "description": "The time-skipping state that needs to be propagated from a parent workflow to a child workflow,\nor through a chain of runs." + }, "v1TimeoutFailureInfo": { "type": "object", "properties": { @@ -21273,9 +21288,9 @@ "$ref": "#/definitions/v1TimeSkippingConfig", "description": "Initial time-skipping configuration for this workflow execution, recorded at start time.\nThis may have been set explicitly via the start workflow request, or propagated from a\nparent/previous execution.\n\nThe configuration may be updated after start via UpdateWorkflowExecutionOptions, which\nwill be reflected in the WorkflowExecutionOptionsUpdatedEvent." }, - "initialSkippedDuration": { - "type": "string", - "description": "The time skipped by the previous execution that started this workflow.\nIt can happen in cases of child workflows and continue-as-new workflows." + "timeSkippingStatePropagation": { + "$ref": "#/definitions/v1TimeSkippingStatePropagation", + "description": "The time-skipping state propagated from a previous run of this workflow. This can be nil\nif no time skipping has occurred or there is no previous run." } }, "title": "Always the first event in workflow history" diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 763b78c23..fad1d059d 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -16739,10 +16739,12 @@ components: allOf: - $ref: '#/components/schemas/TimeSkippingConfig' description: The propagated time-skipping configuration for the child workflow. - initialSkippedDuration: - pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ - type: string - description: Propagate the duration skipped to the child workflow. + timeSkippingStatePropagation: + allOf: + - $ref: '#/components/schemas/TimeSkippingStatePropagation' + description: |- + The time-skipping state propagated from the parent workflow. This can be nil if no time skipping + has occurred or there is no previous run. StartNexusOperationExecutionRequest: type: object properties: @@ -17457,6 +17459,24 @@ components: but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the parent execution as its start time. + TimeSkippingStatePropagation: + type: object + properties: + initialSkippedDuration: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: |- + The time skipped by the previous execution that started this workflow. + It can happen in child workflows and a chain of runs (CaN, cron, retry). + fastForwardTargetTime: + type: string + description: |- + If there is a fast-forward action set for the previous run in a chain of runs, + the target time should be propagated to the next run as well. + format: date-time + description: |- + The time-skipping state that needs to be propagated from a parent workflow to a child workflow, + or through a chain of runs. TimeoutFailureInfo: type: object properties: @@ -19874,12 +19894,12 @@ components: The configuration may be updated after start via UpdateWorkflowExecutionOptions, which will be reflected in the WorkflowExecutionOptionsUpdatedEvent. - initialSkippedDuration: - pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ - type: string + timeSkippingStatePropagation: + allOf: + - $ref: '#/components/schemas/TimeSkippingStatePropagation' description: |- - The time skipped by the previous execution that started this workflow. - It can happen in cases of child workflows and continue-as-new workflows. + The time-skipping state propagated from a previous run of this workflow. This can be nil + if no time skipping has occurred or there is no previous run. description: Always the first event in workflow history WorkflowExecutionTerminatedEventAttributes: type: object diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index d17317b30..803d87346 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -206,11 +206,16 @@ message WorkflowExecutionStartedEventAttributes { // will be reflected in the WorkflowExecutionOptionsUpdatedEvent. temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 41; - // The time skipped by the previous execution that started this workflow. - // It can happen in cases of child workflows and continue-as-new workflows. - google.protobuf.Duration initial_skipped_duration = 42; + reserved 42; + reserved "initial_skipped_duration"; + + // The time-skipping state propagated from a previous run of this workflow. This can be nil + // if no time skipping has occurred or there is no previous run. + temporal.api.workflow.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 43; + } + // Wrapper for a target deployment version that the SDK declined to upgrade to. // See declined_target_version_upgrade on WorkflowExecutionStartedEventAttributes. message DeclinedTargetVersionUpgrade { @@ -779,8 +784,12 @@ message StartChildWorkflowExecutionInitiatedEventAttributes { // The propagated time-skipping configuration for the child workflow. temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 21; - // Propagate the duration skipped to the child workflow. - google.protobuf.Duration initial_skipped_duration = 30; + reserved 22; + reserved "initial_skipped_duration"; + + // The time-skipping state propagated from the parent workflow. This can be nil if no time skipping + // has occurred or there is no previous run. + temporal.api.workflow.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 23; } message StartChildWorkflowExecutionFailedEventAttributes { diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index 492308b5b..3530183cd 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -619,6 +619,20 @@ message TimeSkippingConfig { bool disable_child_propagation = 3; } +// The time-skipping state that needs to be propagated from a parent workflow to a child workflow, +// or through a chain of runs. +message TimeSkippingStatePropagation { + + // The time skipped by the previous execution that started this workflow. + // It can happen in child workflows and a chain of runs (CaN, cron, retry). + google.protobuf.Duration initial_skipped_duration = 1; + + // If there is a fast-forward action set for the previous run in a chain of runs, + // the target time should be propagated to the next run as well. + google.protobuf.Timestamp fast_forward_target_time = 2; +} + + // Used to override the versioning behavior (and pinned deployment version, if applicable) of a // specific workflow execution. If set, this override takes precedence over worker-sent values. // See `WorkflowExecutionInfo.VersioningInfo` for more information. From 45c407e320479013e434a5067e2a1f3eb32f4df5 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Wed, 10 Jun 2026 19:21:46 -0700 Subject: [PATCH 5/9] add explanation for fast-forward override --- openapi/openapiv2.json | 4 ++-- openapi/openapiv3.yaml | 10 +++++++++- temporal/api/history/v1/message.proto | 4 ++-- temporal/api/workflow/v1/message.proto | 10 +++++++++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 2e09d56a5..866ab6915 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -19680,14 +19680,14 @@ }, "fastForward": { "type": "string", - "description": "Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time.\nWhen the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this\naction is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this fast_forward won't affect them." + "description": "Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time.\nWhen the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this\naction is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this fast_forward won't affect them.\n\nFor a given workflow execution, only one active fast-forward is allowed at a time.\nIf a new fast-forward is set via UpdateWorkflowExecutionOptions before the previous\none completes, the new one will override the previous one.\nIf the fast-forward duration exceeds the remaining execution timeout, time will only\nbe fast-forwarded up to the end of the execution." }, "disableChildPropagation": { "type": "boolean", "description": "By default, child workflows inherit the \"enabled\" flag when they are started.\nThis flag disables that inheritance." } }, - "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered fast_forward when there is no in-flight work.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." + "description": "The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new).\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations,\nand possibly other features added in the future.\nUser timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the\ntime point of the registered fast forward when there is no in-flight work.\nWhen time is skipped, a WorkflowExecutionTimeSkippingTransitionedEvent will be\nadded to the workflow history to capture the state changes.\n\nFor child workflows, by default, if the parent execution is skipping time, the child execution will also skip time,\nbut a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the\n\"enabled\" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the\nparent execution as its start time." }, "v1TimeSkippingStatePropagation": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index fad1d059d..42383f133 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17442,6 +17442,12 @@ components: setting `enabled` to true via UpdateWorkflowExecutionOptions. The current workflow execution is a chain of runs (retries, cron, continue-as-new); child workflows are separate executions, so this fast_forward won't affect them. + + For a given workflow execution, only one active fast-forward is allowed at a time. + If a new fast-forward is set via UpdateWorkflowExecutionOptions before the previous + one completes, the new one will override the previous one. + If the fast-forward duration exceeds the remaining execution timeout, time will only + be fast-forwarded up to the end of the execution. disableChildPropagation: type: boolean description: |- @@ -17453,7 +17459,9 @@ components: In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, and possibly other features added in the future. User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the - time point of the registered fast_forward when there is no in-flight work. + time point of the registered fast forward when there is no in-flight work. + When time is skipped, a WorkflowExecutionTimeSkippingTransitionedEvent will be + added to the workflow history to capture the state changes. For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 803d87346..051854893 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -209,8 +209,8 @@ message WorkflowExecutionStartedEventAttributes { reserved 42; reserved "initial_skipped_duration"; - // The time-skipping state propagated from a previous run of this workflow. This can be nil - // if no time skipping has occurred or there is no previous run. + // The time-skipping state propagated from a previous run of this workflow. This can be nil + // if no time skipping has occurred or there is no previous run. temporal.api.workflow.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 43; } diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index 3530183cd..c1b1a9cad 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -596,7 +596,9 @@ message WorkflowExecutionOptions { // In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, // and possibly other features added in the future. // User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the -// time point of the registered fast_forward when there is no in-flight work. +// time point of the registered fast forward when there is no in-flight work. +// When time is skipped, a WorkflowExecutionTimeSkippingTransitionedEvent will be +// added to the workflow history to capture the state changes. // // For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, // but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the @@ -612,6 +614,12 @@ message TimeSkippingConfig { // setting `enabled` to true via UpdateWorkflowExecutionOptions. // The current workflow execution is a chain of runs (retries, cron, continue-as-new); // child workflows are separate executions, so this fast_forward won't affect them. + // + // For a given workflow execution, only one active fast-forward is allowed at a time. + // If a new fast-forward is set via UpdateWorkflowExecutionOptions before the previous + // one completes, the new one will override the previous one. + // If the fast-forward duration exceeds the remaining execution timeout, time will only + // be fast-forwarded up to the end of the execution. google.protobuf.Duration fast_forward = 2; // By default, child workflows inherit the "enabled" flag when they are started. From a93ee408e209cc61d684d43bb933dd2eb0757012 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Thu, 11 Jun 2026 14:00:14 -0700 Subject: [PATCH 6/9] add description to fast forward --- openapi/openapiv2.json | 2 +- openapi/openapiv3.yaml | 5 +++++ temporal/api/workflow/v1/message.proto | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 866ab6915..a460d63ad 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -21009,7 +21009,7 @@ }, "timeSkippingConfig": { "$ref": "#/definitions/v1TimeSkippingConfig", - "description": "Time-skipping configuration for this workflow execution.\nIf not set, the time-skipping configuration is not updated by this request;\nthe existing configuration is preserved." + "description": "Time-skipping configuration for this workflow execution.\nIf not set, the time-skipping configuration is not updated by this request;\nthe existing configuration is preserved.\n\nWhen `fast_forward` is set, time will be fast-forwarded to a future point relative\nto the current workflow timestamp. Each call takes effect, even if\n`fast_forward` is set to the same duration, since the target time is recalculated\nfrom the current timestamp on every call." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 42383f133..61ec71347 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -19538,6 +19538,11 @@ components: Time-skipping configuration for this workflow execution. If not set, the time-skipping configuration is not updated by this request; the existing configuration is preserved. + + When `fast_forward` is set, time will be fast-forwarded to a future point relative + to the current workflow timestamp. Each call takes effect, even if + `fast_forward` is set to the same duration, since the target time is recalculated + from the current timestamp on every call. WorkflowExecutionOptionsUpdatedEventAttributes: type: object properties: diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index c1b1a9cad..58b595d3b 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -588,6 +588,11 @@ message WorkflowExecutionOptions { // Time-skipping configuration for this workflow execution. // If not set, the time-skipping configuration is not updated by this request; // the existing configuration is preserved. + // + // When `fast_forward` is set, time will be fast-forwarded to a future point relative + // to the current workflow timestamp. Each call takes effect, even if + // `fast_forward` is set to the same duration, since the target time is recalculated + // from the current timestamp on every call. TimeSkippingConfig time_skipping_config = 3; } From 1f73c307e3f026aeb6319d9824e484eb0ac7252d Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Fri, 12 Jun 2026 10:37:52 -0700 Subject: [PATCH 7/9] move time skipping config from workflow folder to the common folder --- temporal/api/common/v1/message.proto | 50 ++++++++++++++++++ temporal/api/history/v1/message.proto | 10 ++-- temporal/api/workflow/v1/message.proto | 52 +------------------ .../workflowservice/v1/request_response.proto | 4 +- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 98908cf89..43ca7664d 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -11,6 +11,7 @@ option csharp_namespace = "Temporalio.Api.Common.V1"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; import "temporal/api/enums/v1/common.proto"; import "temporal/api/enums/v1/event_type.proto"; @@ -393,3 +394,52 @@ message OnConflictOptions { // Attaches the links to the running execution. bool attach_links = 3; } + +// The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new). +// When time skipping is enabled, virtual time advances automatically whenever there is no in-flight work. +// In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, +// and possibly other features added in the future. +// User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the +// time point of the registered fast forward when there is no in-flight work. +// When time is skipped, a WorkflowExecutionTimeSkippingTransitionedEvent will be +// added to the workflow history to capture the state changes. +// +// For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, +// but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the +// "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the +// parent execution as its start time. +message TimeSkippingConfig { + // Enables or disables time skipping for this workflow execution. + bool enabled = 1; + + // Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time. + // When the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this + // action is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by + // setting `enabled` to true via UpdateWorkflowExecutionOptions. + // The current workflow execution is a chain of runs (retries, cron, continue-as-new); + // child workflows are separate executions, so this fast_forward won't affect them. + // + // For a given workflow execution, only one active fast-forward is allowed at a time. + // If a new fast-forward is set via UpdateWorkflowExecutionOptions before the previous + // one completes, the new one will override the previous one. + // If the fast-forward duration exceeds the remaining execution timeout, time will only + // be fast-forwarded up to the end of the execution. + google.protobuf.Duration fast_forward = 2; + + // By default, child workflows inherit the "enabled" flag when they are started. + // This flag disables that inheritance. + bool disable_child_propagation = 3; +} + +// The time-skipping state that needs to be propagated from a parent workflow to a child workflow, +// or through a chain of runs. +message TimeSkippingStatePropagation { + + // The time skipped by the previous execution that started this workflow. + // It can happen in child workflows and a chain of runs (CaN, cron, retry). + google.protobuf.Duration initial_skipped_duration = 1; + + // If there is a fast-forward action set for the previous run in a chain of runs, + // the target time should be propagated to the next run as well. + google.protobuf.Timestamp fast_forward_target_time = 2; +} diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 051854893..d98f05002 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -204,14 +204,14 @@ message WorkflowExecutionStartedEventAttributes { // // The configuration may be updated after start via UpdateWorkflowExecutionOptions, which // will be reflected in the WorkflowExecutionOptionsUpdatedEvent. - temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 41; + temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 41; reserved 42; reserved "initial_skipped_duration"; // The time-skipping state propagated from a previous run of this workflow. This can be nil // if no time skipping has occurred or there is no previous run. - temporal.api.workflow.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 43; + temporal.api.common.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 43; } @@ -782,14 +782,14 @@ message StartChildWorkflowExecutionInitiatedEventAttributes { temporal.api.common.v1.Priority priority = 20; // The propagated time-skipping configuration for the child workflow. - temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 21; + temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 21; reserved 22; reserved "initial_skipped_duration"; // The time-skipping state propagated from the parent workflow. This can be nil if no time skipping // has occurred or there is no previous run. - temporal.api.workflow.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 23; + temporal.api.common.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 23; } message StartChildWorkflowExecutionFailedEventAttributes { @@ -916,7 +916,7 @@ message WorkflowExecutionOptionsUpdatedEventAttributes { // Ignored if nil. temporal.api.common.v1.Priority priority = 6; // If set, the time-skipping configuration was changed. Contains the full updated configuration. - temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 7; + temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 7; // Updates to workflow updates options. repeated WorkflowUpdateOptionsUpdate workflow_update_options = 8; } diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index 58b595d3b..c98796924 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -593,59 +593,9 @@ message WorkflowExecutionOptions { // to the current workflow timestamp. Each call takes effect, even if // `fast_forward` is set to the same duration, since the target time is recalculated // from the current timestamp on every call. - TimeSkippingConfig time_skipping_config = 3; + temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 3; } -// The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new). -// When time skipping is enabled, virtual time advances automatically whenever there is no in-flight work. -// In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, -// and possibly other features added in the future. -// User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the -// time point of the registered fast forward when there is no in-flight work. -// When time is skipped, a WorkflowExecutionTimeSkippingTransitionedEvent will be -// added to the workflow history to capture the state changes. -// -// For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, -// but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the -// "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the -// parent execution as its start time. -message TimeSkippingConfig { - // Enables or disables time skipping for this workflow execution. - bool enabled = 1; - - // Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time. - // When the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this - // action is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by - // setting `enabled` to true via UpdateWorkflowExecutionOptions. - // The current workflow execution is a chain of runs (retries, cron, continue-as-new); - // child workflows are separate executions, so this fast_forward won't affect them. - // - // For a given workflow execution, only one active fast-forward is allowed at a time. - // If a new fast-forward is set via UpdateWorkflowExecutionOptions before the previous - // one completes, the new one will override the previous one. - // If the fast-forward duration exceeds the remaining execution timeout, time will only - // be fast-forwarded up to the end of the execution. - google.protobuf.Duration fast_forward = 2; - - // By default, child workflows inherit the "enabled" flag when they are started. - // This flag disables that inheritance. - bool disable_child_propagation = 3; -} - -// The time-skipping state that needs to be propagated from a parent workflow to a child workflow, -// or through a chain of runs. -message TimeSkippingStatePropagation { - - // The time skipped by the previous execution that started this workflow. - // It can happen in child workflows and a chain of runs (CaN, cron, retry). - google.protobuf.Duration initial_skipped_duration = 1; - - // If there is a fast-forward action set for the previous run in a chain of runs, - // the target time should be propagated to the next run as well. - google.protobuf.Timestamp fast_forward_target_time = 2; -} - - // Used to override the versioning behavior (and pinned deployment version, if applicable) of a // specific workflow execution. If set, this override takes precedence over worker-sent values. // See `WorkflowExecutionInfo.VersioningInfo` for more information. diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 588a92d91..263c3c8a0 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -210,7 +210,7 @@ message StartWorkflowExecutionRequest { temporal.api.deployment.v1.WorkerDeploymentOptions eager_worker_deployment_options = 28; // Time-skipping configuration. If not set, time skipping is disabled. - temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 29; + temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 29; } message StartWorkflowExecutionResponse { @@ -899,7 +899,7 @@ message SignalWithStartWorkflowExecutionRequest { // Priority metadata temporal.api.common.v1.Priority priority = 26; // Time-skipping configuration. If not set, time skipping is disabled. - temporal.api.workflow.v1.TimeSkippingConfig time_skipping_config = 27; + temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 27; } message SignalWithStartWorkflowExecutionResponse { From 1bc64c9f0cfda717580c6272773d10ad683976f6 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Fri, 12 Jun 2026 22:02:40 -0700 Subject: [PATCH 8/9] refined the comment for WorkflowExecutionOptions --- openapi/openapiv2.json | 8 ++++++-- openapi/openapiv3.yaml | 17 +++++++---------- temporal/api/history/v1/message.proto | 8 +++++++- temporal/api/workflow/v1/message.proto | 9 +++++---- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index a460d63ad..bda99d70f 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -21009,7 +21009,7 @@ }, "timeSkippingConfig": { "$ref": "#/definitions/v1TimeSkippingConfig", - "description": "Time-skipping configuration for this workflow execution.\nIf not set, the time-skipping configuration is not updated by this request;\nthe existing configuration is preserved.\n\nWhen `fast_forward` is set, time will be fast-forwarded to a future point relative\nto the current workflow timestamp. Each call takes effect, even if\n`fast_forward` is set to the same duration, since the target time is recalculated\nfrom the current timestamp on every call." + "description": "The time-skipping configuration for this workflow execution.\nWhen `fast_forward` is set, time will be fast-forwarded to a future point relative\nto the current workflow timestamp. Each call takes effect, even if\n`fast_forward` is set to the same duration, since the target time is recalculated\nfrom the current timestamp on every call.\n\nThis field must be updated as a whole; updating individual sub-fields is not supported.\nWhen setting the update mask in `UpdateWorkflowExecutionOptionsRequest`, \n`BatchOperationUpdateWorkflowExecutionOptions`, etc., use a mask that covers the entire field." } } }, @@ -21046,7 +21046,11 @@ }, "timeSkippingConfig": { "$ref": "#/definitions/v1TimeSkippingConfig", - "description": "If set, the time-skipping configuration was changed. Contains the full updated configuration." + "description": "TimeSkippingConfig override upserted in this event. Represents the full config." + }, + "timeSkippingConfigUpdated": { + "type": "boolean", + "description": "Indicates the time skipping config was updated by the recent call to update\nworkflow execution options." }, "workflowUpdateOptions": { "type": "array", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 61ec71347..454080f3a 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -19534,15 +19534,7 @@ components: timeSkippingConfig: allOf: - $ref: '#/components/schemas/TimeSkippingConfig' - description: |- - Time-skipping configuration for this workflow execution. - If not set, the time-skipping configuration is not updated by this request; - the existing configuration is preserved. - - When `fast_forward` is set, time will be fast-forwarded to a future point relative - to the current workflow timestamp. Each call takes effect, even if - `fast_forward` is set to the same duration, since the target time is recalculated - from the current timestamp on every call. + description: "The time-skipping configuration for this workflow execution.\n When `fast_forward` is set, time will be fast-forwarded to a future point relative\n to the current workflow timestamp. Each call takes effect, even if\n `fast_forward` is set to the same duration, since the target time is recalculated\n from the current timestamp on every call.\n\n This field must be updated as a whole; updating individual sub-fields is not supported.\n When setting the update mask in `UpdateWorkflowExecutionOptionsRequest`, \n `BatchOperationUpdateWorkflowExecutionOptions`, etc., use a mask that covers the entire field." WorkflowExecutionOptionsUpdatedEventAttributes: type: object properties: @@ -19577,7 +19569,12 @@ components: timeSkippingConfig: allOf: - $ref: '#/components/schemas/TimeSkippingConfig' - description: If set, the time-skipping configuration was changed. Contains the full updated configuration. + description: TimeSkippingConfig override upserted in this event. Represents the full config. + timeSkippingConfigUpdated: + type: boolean + description: |- + Indicates the time skipping config was updated by the recent call to update + workflow execution options. workflowUpdateOptions: type: array items: diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index d98f05002..2fdb678bc 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -915,8 +915,14 @@ message WorkflowExecutionOptionsUpdatedEventAttributes { // Priority override upserted in this event. Represents the full priority; not just partial fields. // Ignored if nil. temporal.api.common.v1.Priority priority = 6; - // If set, the time-skipping configuration was changed. Contains the full updated configuration. + + // TimeSkippingConfig override upserted in this event. Represents the full config. temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 7; + + // Indicates the time skipping config was updated by the recent call to update + // workflow execution options. + bool time_skipping_config_updated = 9; + // Updates to workflow updates options. repeated WorkflowUpdateOptionsUpdate workflow_update_options = 8; } diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index c98796924..4d35cb74b 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -585,14 +585,15 @@ message WorkflowExecutionOptions { // If set, overrides the workflow's priority sent by the SDK. temporal.api.common.v1.Priority priority = 2; - // Time-skipping configuration for this workflow execution. - // If not set, the time-skipping configuration is not updated by this request; - // the existing configuration is preserved. - // + // The time-skipping configuration for this workflow execution. // When `fast_forward` is set, time will be fast-forwarded to a future point relative // to the current workflow timestamp. Each call takes effect, even if // `fast_forward` is set to the same duration, since the target time is recalculated // from the current timestamp on every call. + // + // This field must be updated as a whole; updating individual sub-fields is not supported. + // When setting the update mask in `UpdateWorkflowExecutionOptionsRequest`, + // `BatchOperationUpdateWorkflowExecutionOptions`, etc., use a mask that covers the entire field. temporal.api.common.v1.TimeSkippingConfig time_skipping_config = 3; } From 9fb069b8c124aab6b5e50424de8bd58eff8a366d Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Mon, 15 Jun 2026 13:38:50 -0700 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: Spencer Judge --- openapi/openapiv2.json | 2 +- openapi/openapiv3.yaml | 4 ++-- temporal/api/common/v1/message.proto | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index bda99d70f..ea37162a0 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -19680,7 +19680,7 @@ }, "fastForward": { "type": "string", - "description": "Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time.\nWhen the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this\naction is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by\nsetting `enabled` to true via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this fast_forward won't affect them.\n\nFor a given workflow execution, only one active fast-forward is allowed at a time.\nIf a new fast-forward is set via UpdateWorkflowExecutionOptions before the previous\none completes, the new one will override the previous one.\nIf the fast-forward duration exceeds the remaining execution timeout, time will only\nbe fast-forwarded up to the end of the execution." + "description": "Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time.\nAfter the fast-forward completes, time skipping is disabled, and this\naction is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by\nsetting `enabled` to true or setting `fast_forward` again via UpdateWorkflowExecutionOptions.\nThe current workflow execution is a chain of runs (retries, cron, continue-as-new);\nchild workflows are separate executions, so this fast_forward won't affect them.\n\nFor a given workflow execution, only one active fast-forward is allowed at a time.\nIf a new fast-forward is set via UpdateWorkflowExecutionOptions before the previous\none completes, the new one will override the previous one.\nIf the fast-forward duration exceeds the remaining execution timeout, time will only\nbe fast-forwarded up to the end of the execution." }, "disableChildPropagation": { "type": "boolean", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 454080f3a..a2ec27cdd 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17437,9 +17437,9 @@ components: type: string description: |- Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time. - When the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this + After the fast-forward completes, time skipping is disabled, and this action is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by - setting `enabled` to true via UpdateWorkflowExecutionOptions. + setting `enabled` to true or setting `fast_forward` again via UpdateWorkflowExecutionOptions. The current workflow execution is a chain of runs (retries, cron, continue-as-new); child workflows are separate executions, so this fast_forward won't affect them. diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 43ca7664d..a0525ab47 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -413,9 +413,9 @@ message TimeSkippingConfig { bool enabled = 1; // Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time. - // When the fast-forward completes, time skipping is disabled by the call that initiated the fast-forward, and this + // After the fast-forward completes, time skipping is disabled, and this // action is recorded in the WorkflowExecutionTimeSkippingTransitionedEvent. It can be re-enabled by - // setting `enabled` to true via UpdateWorkflowExecutionOptions. + // setting `enabled` to true or setting `fast_forward` again via UpdateWorkflowExecutionOptions. // The current workflow execution is a chain of runs (retries, cron, continue-as-new); // child workflows are separate executions, so this fast_forward won't affect them. //