From cbcf90d8309c39c145e2e6c2b37dfb0ac65b2e9e Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Sun, 19 Jul 2026 14:01:32 -0700 Subject: [PATCH 01/15] add max_skip_count and change comments of TimeSkippingConfig --- openapi/openapiv2.json | 9 ++++-- openapi/openapiv3.yaml | 43 +++++++++++++++++----------- temporal/api/common/v1/message.proto | 41 +++++++++++++++----------- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 97ff181c9..f5632647c 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -19894,14 +19894,19 @@ }, "fastForward": { "type": "string", - "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." + "description": "Optionally fast-forward the current execution by this duration ahead of current workflow time.\nAfter the fast-forward completes, time skipping is disabled. And it can be re-enabled by calling\nan update API with the time-skipping opt-in.\n\nFor a given execution, only one active fast-forward is allowed at a time.\nIf a new fast-forward is set via an update call 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 not\npass beyond the end of the execution, and fast_forward won't have a chance to complete." }, "disablePropagation": { "type": "boolean", "description": "By default, executions started by another execution (e.g. a child workflow of a parent workflow or\na schedule with the timeskipping policy enabled), inherit the \"enabled\" flag and skip time when possible.\nThis flag disables that inheritance." + }, + "maxSkipCount": { + "type": "integer", + "format": "int32", + "description": "The maximum skip count allowed after time skipping is enabled. It is used to protect the current execution\nfrom unlimited retries when backoff is skipped. Every time the execution skips some time,\nthe skip count is incremented by one, and when it reaches max_skip_count, time skipping will be disabled.\n\nIf the field is not set, a large default value (e.g. 100) will be set by the server.\nThe default value can be changed through dynamic config, and can be overridden by this field if set." } }, - "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." + "description": "The configuration for time skipping of an execution.\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nOptions like fast_forward, disable_propagation, and max_skip_count are provided for granular\ncontrol of the execution's time skipping behavior. See each field's comment for a detailed explanation.\n\nAn example of workflows with time skipping:\nFor workflows, an execution is a chain of runs including retries, cron, and continue-as-new.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc.\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.\nEvery time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled.\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 f2081bd9b..0101c0202 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17660,34 +17660,43 @@ components: pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ type: string description: |- - Optionally fast-forward the current workflow execution by this duration ahead of current workflow execution time. - 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 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. + Optionally fast-forward the current execution by this duration ahead of current workflow time. + After the fast-forward completes, time skipping is disabled. And it can be re-enabled by calling + an update API with the time-skipping opt-in. - 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 + For a given execution, only one active fast-forward is allowed at a time. + If a new fast-forward is set via an update call 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. + If the fast-forward duration exceeds the remaining execution timeout, time will not + pass beyond the end of the execution, and fast_forward won't have a chance to complete. disablePropagation: type: boolean description: |- By default, executions started by another execution (e.g. a child workflow of a parent workflow or a schedule with the timeskipping policy enabled), inherit the "enabled" flag and skip time when possible. This flag disables that inheritance. + maxSkipCount: + type: integer + description: |- + The maximum skip count allowed after time skipping is enabled. It is used to protect the current execution + from unlimited retries when backoff is skipped. Every time the execution skips some time, + the skip count is incremented by one, and when it reaches max_skip_count, time skipping will be disabled. + + If the field is not set, a large default value (e.g. 100) will be set by the server. + The default value can be changed through dynamic config, and can be overridden by this field if set. + format: int32 description: |- - The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new). + The configuration for time skipping of an execution. 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. + Options like fast_forward, disable_propagation, and max_skip_count are provided for granular + control of the execution's time skipping behavior. See each field's comment for a detailed explanation. + An example of workflows with time skipping: + For workflows, an execution is a chain of runs including retries, cron, and continue-as-new. + In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc. + 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. + Every time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled. 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 diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 1af5b4acc..07d904025 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -403,15 +403,17 @@ message OnConflictOptions { bool attach_links = 3; } -// The configuration for time skipping of a workflow execution (a chain of runs including retries, cron, continue-as-new). +// The configuration for time skipping of an execution. // 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. +// Options like fast_forward, disable_propagation, and max_skip_count are provided for granular +// control of the execution's time skipping behavior. See each field's comment for a detailed explanation. // +// An example of workflows with time skipping: +// For workflows, an execution is a chain of runs including retries, cron, and continue-as-new. +// In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc. +// 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. +// Every time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled. // 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 @@ -420,24 +422,29 @@ 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. - // 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 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. + // Optionally fast-forward the current execution by this duration ahead of current workflow time. + // After the fast-forward completes, time skipping is disabled. And it can be re-enabled by calling + // an update API with the time-skipping opt-in. // - // 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 + // For a given execution, only one active fast-forward is allowed at a time. + // If a new fast-forward is set via an update call 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. + // If the fast-forward duration exceeds the remaining execution timeout, time will not + // pass beyond the end of the execution, and fast_forward won't have a chance to complete. google.protobuf.Duration fast_forward = 2; // By default, executions started by another execution (e.g. a child workflow of a parent workflow or // a schedule with the timeskipping policy enabled), inherit the "enabled" flag and skip time when possible. // This flag disables that inheritance. bool disable_propagation = 3; + + // The maximum skip count allowed after time skipping is enabled. It is used to protect the current execution + // from unlimited retries when backoff is skipped. Every time the execution skips some time, + // the skip count is incremented by one, and when it reaches max_skip_count, time skipping will be disabled. + // + // If the field is not set, a large default value (e.g. 100) will be set by the server. + // The default value can be changed through dynamic config, and can be overridden by this field if set. + int32 max_skip_count = 4; } // The time-skipping state that needs to be propagated from a parent workflow to a child workflow, From 310f7086a37de8edfb8b86e8b4387922b6077f22 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Sun, 19 Jul 2026 14:47:06 -0700 Subject: [PATCH 02/15] add TimeSkippingInfo to WorkflowExecutionExtendedInfo, add poll api --- openapi/openapiv2.json | 147 ++++++++++++++++++ openapi/openapiv3.yaml | 135 ++++++++++++++++ temporal/api/common/v1/message.proto | 24 +++ temporal/api/workflow/v1/message.proto | 4 + .../workflowservice/v1/request_response.proto | 25 +++ temporal/api/workflowservice/v1/service.proto | 17 ++ 6 files changed, 352 insertions(+) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index f5632647c..85c6b12a5 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -4427,6 +4427,49 @@ ] } }, + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/time-skipping/poll": { + "get": { + "summary": "PollWorkflowExecutionTimeSkipping long-polls until the time-skipping state of a workflow\nexecution changes (its time-skipping config is updated or an active fast-forward completes),\nthen returns the reason and the current fast-forward info. It lets callers observe fast-forward\nprogress without busy-polling.", + "operationId": "PollWorkflowExecutionTimeSkipping2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1PollWorkflowExecutionTimeSkippingResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/update-options": { "post": { "summary": "UpdateWorkflowExecutionOptions partially updates the WorkflowExecutionOptions of an existing workflow execution.", @@ -9961,6 +10004,49 @@ ] } }, + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/time-skipping/poll": { + "get": { + "summary": "PollWorkflowExecutionTimeSkipping long-polls until the time-skipping state of a workflow\nexecution changes (its time-skipping config is updated or an active fast-forward completes),\nthen returns the reason and the current fast-forward info. It lets callers observe fast-forward\nprogress without busy-polling.", + "operationId": "PollWorkflowExecutionTimeSkipping", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1PollWorkflowExecutionTimeSkippingResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/update-options": { "post": { "summary": "UpdateWorkflowExecutionOptions partially updates the WorkflowExecutionOptions of an existing workflow execution.", @@ -11059,6 +11145,16 @@ } } }, + "PollWorkflowExecutionTimeSkippingResponseReturnReason": { + "type": "string", + "enum": [ + "RETURN_REASON_UNSPECIFIED", + "RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED", + "RETURN_REASON_FAST_FORWARD_COMPLETED" + ], + "default": "RETURN_REASON_UNSPECIFIED", + "description": "Why the poll returned.\n\n - RETURN_REASON_UNSPECIFIED: The poll returned because its server-side timeout elapsed without any change. The caller\nmay poll again.\n - RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED: The execution's time-skipping config was updated.\n - RETURN_REASON_FAST_FORWARD_COMPLETED: The active fast-forward reached its target time and completed." + }, "PostResetOperationSignalWorkflow": { "type": "object", "properties": { @@ -17971,6 +18067,18 @@ } } }, + "v1PollWorkflowExecutionTimeSkippingResponse": { + "type": "object", + "properties": { + "reason": { + "$ref": "#/definitions/PollWorkflowExecutionTimeSkippingResponseReturnReason" + }, + "fastForwardInfo": { + "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", + "description": "The current fast-forward on the execution, if any." + } + } + }, "v1PollWorkflowTaskQueueResponse": { "type": "object", "properties": { @@ -19908,6 +20016,41 @@ }, "description": "The configuration for time skipping of an execution.\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nOptions like fast_forward, disable_propagation, and max_skip_count are provided for granular\ncontrol of the execution's time skipping behavior. See each field's comment for a detailed explanation.\n\nAn example of workflows with time skipping:\nFor workflows, an execution is a chain of runs including retries, cron, and continue-as-new.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc.\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.\nEvery time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled.\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." }, + "v1TimeSkippingFastForwardInfo": { + "type": "object", + "properties": { + "createTime": { + "type": "string", + "format": "date-time", + "description": "The virtual time at which the fast-forward was created." + }, + "targetTime": { + "type": "string", + "format": "date-time", + "description": "The target virtual time at which the fast-forward completes." + }, + "hasCompleted": { + "type": "boolean", + "description": "True once `target_time` has been reached." + } + }, + "description": "TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution." + }, + "v1TimeSkippingInfo": { + "type": "object", + "properties": { + "currentTime": { + "type": "string", + "format": "date-time", + "description": "Current virtual time of the execution. If the execution hasn't skipped\nany time yet, it will be the same as wall clock time." + }, + "isRunning": { + "type": "boolean", + "description": "If the execution is actively trying to skip time automatically when there is a chance,\nthis field will be set to true. If time has stopped skipping either by fast-forward completion,\nmax skip allowed checking, or user configuration, it will be false." + } + }, + "description": "Describes the current time-skipping state of a workflow execution." + }, "v1TimeSkippingStatePropagation": { "type": "object", "properties": { @@ -21091,6 +21234,10 @@ "pauseInfo": { "$ref": "#/definitions/v1WorkflowExecutionPauseInfo", "description": "Information about the workflow execution pause operation." + }, + "timeSkippingInfo": { + "$ref": "#/definitions/v1TimeSkippingInfo", + "description": "Information about time skipping of the workflow execution.\nIf the execution has never enabled time skipping, it will be nil." } }, "description": "Holds all the extra information about workflow execution that is not part of Visibility." diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 0101c0202..08b3266d5 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -4572,6 +4572,48 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll: + get: + tags: + - WorkflowService + description: |- + PollWorkflowExecutionTimeSkipping long-polls until the time-skipping state of a workflow + execution changes (its time-skipping config is updated or an active fast-forward completes), + then returns the reason and the current fast-forward info. It lets callers observe fast-forward + progress without busy-polling. + operationId: PollWorkflowExecutionTimeSkipping + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PollWorkflowExecutionTimeSkippingResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/update-options: post: tags: @@ -9568,6 +9610,48 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll: + get: + tags: + - WorkflowService + description: |- + PollWorkflowExecutionTimeSkipping long-polls until the time-skipping state of a workflow + execution changes (its time-skipping config is updated or an active fast-forward completes), + then returns the reason and the current fast-forward info. It lets callers observe fast-forward + progress without busy-polling. + operationId: PollWorkflowExecutionTimeSkipping + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PollWorkflowExecutionTimeSkippingResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/update-options: post: tags: @@ -14396,6 +14480,20 @@ components: allOf: - $ref: '#/components/schemas/Failure' description: The failure if the operation completed unsuccessfully. + PollWorkflowExecutionTimeSkippingResponse: + type: object + properties: + reason: + enum: + - RETURN_REASON_UNSPECIFIED + - RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED + - RETURN_REASON_FAST_FORWARD_COMPLETED + type: string + format: enum + fastForwardInfo: + allOf: + - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' + description: The current fast-forward on the execution, if any. PollWorkflowTaskQueueResponse: type: object properties: @@ -17701,6 +17799,37 @@ 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. + TimeSkippingFastForwardInfo: + type: object + properties: + createTime: + type: string + description: The virtual time at which the fast-forward was created. + format: date-time + targetTime: + type: string + description: The target virtual time at which the fast-forward completes. + format: date-time + hasCompleted: + type: boolean + description: True once `target_time` has been reached. + description: TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution. + TimeSkippingInfo: + type: object + properties: + currentTime: + type: string + description: |- + Current virtual time of the execution. If the execution hasn't skipped + any time yet, it will be the same as wall clock time. + format: date-time + isRunning: + type: boolean + description: |- + If the execution is actively trying to skip time automatically when there is a chance, + this field will be set to true. If time has stopped skipping either by fast-forward completion, + max skip allowed checking, or user configuration, it will be false. + description: Describes the current time-skipping state of a workflow execution. TimeSkippingStatePropagation: type: object properties: @@ -19644,6 +19773,12 @@ components: allOf: - $ref: '#/components/schemas/WorkflowExecutionPauseInfo' description: Information about the workflow execution pause operation. + timeSkippingInfo: + allOf: + - $ref: '#/components/schemas/TimeSkippingInfo' + description: |- + Information about time skipping of the workflow execution. + If the execution has never enabled time skipping, it will be nil. description: Holds all the extra information about workflow execution that is not part of Visibility. WorkflowExecutionFailedEventAttributes: type: object diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 07d904025..dd9c66457 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -459,3 +459,27 @@ message TimeSkippingStatePropagation { // the target time should be propagated to the next run as well. google.protobuf.Timestamp fast_forward_target_time = 2; } + + +// Describes the current time-skipping state of a workflow execution. +message TimeSkippingInfo { + // Current virtual time of the execution. If the execution hasn't skipped + // any time yet, it will be the same as wall clock time. + google.protobuf.Timestamp current_time = 1; + + // If the execution is actively trying to skip time automatically when there is a chance, + // this field will be set to true. If time has stopped skipping either by fast-forward completion, + // max skip allowed checking, or user configuration, it will be false. + bool is_running = 2; +} + + +// TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution. +message TimeSkippingFastForwardInfo { + // The virtual time at which the fast-forward was created. + google.protobuf.Timestamp create_time = 1; + // The target virtual time at which the fast-forward completes. + google.protobuf.Timestamp target_time = 2; + // True once `target_time` has been reached. + bool has_completed = 3; +} diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index c09370f7c..1ed33fa4c 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -134,6 +134,10 @@ message WorkflowExecutionExtendedInfo { // Information about the workflow execution pause operation. WorkflowExecutionPauseInfo pause_info = 8; + + // Information about time skipping of the workflow execution. + // If the execution has never enabled time skipping, it will be nil. + temporal.api.common.v1.TimeSkippingInfo time_skipping_info = 9; } // Holds all the information about worker versioning for a particular workflow execution. diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 66f478c5c..bf58201af 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -3634,3 +3634,28 @@ message DeleteNexusOperationExecutionRequest { message DeleteNexusOperationExecutionResponse { } + + +// A long-poll request that blocks until the time-skipping state of an execution changes, so callers +// can observe fast-forward progress and time-skipping config updates without busy-polling. +message PollWorkflowExecutionTimeSkippingRequest { + string namespace = 1; + temporal.api.common.v1.WorkflowExecution workflow_execution = 2; +} + +message PollWorkflowExecutionTimeSkippingResponse { + // Why the poll returned. + enum ReturnReason { + // The poll returned because its server-side timeout elapsed without any change. The caller + // may poll again. + RETURN_REASON_UNSPECIFIED = 0; + // The execution's time-skipping config was updated. + RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED = 1; + // The active fast-forward reached its target time and completed. + RETURN_REASON_FAST_FORWARD_COMPLETED = 2; + } + ReturnReason reason = 1; + + // The current fast-forward on the execution, if any. + temporal.api.common.v1.TimeSkippingFastForwardInfo fast_forward_info = 2; +} diff --git a/temporal/api/workflowservice/v1/service.proto b/temporal/api/workflowservice/v1/service.proto index bec02b9e5..97a26e3d7 100644 --- a/temporal/api/workflowservice/v1/service.proto +++ b/temporal/api/workflowservice/v1/service.proto @@ -2039,4 +2039,21 @@ service WorkflowService { // (-- api-linter: core::0127::http-annotation=disabled // aip.dev/not-precedent: Nexus operation deletion not exposed to HTTP, users should use cancel or terminate. --) rpc DeleteNexusOperationExecution (DeleteNexusOperationExecutionRequest) returns (DeleteNexusOperationExecutionResponse) {} + + // PollWorkflowExecutionTimeSkipping long-polls until the time-skipping state of a workflow + // execution changes (its time-skipping config is updated or an active fast-forward completes), + // then returns the reason and the current fast-forward info. It lets callers observe fast-forward + // progress without busy-polling. + rpc PollWorkflowExecutionTimeSkipping (PollWorkflowExecutionTimeSkippingRequest) returns (PollWorkflowExecutionTimeSkippingResponse) { + option (google.api.http) = { + get: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll" + additional_bindings { + get: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll" + } + }; + option (temporal.api.protometa.v1.request_header) = { + header: "temporal-resource-id" + value: "workflow:{workflow_execution.workflow_id}" + }; + } } From 9bba2939c424b9b8ed411b4a255771f41656172e Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Sun, 19 Jul 2026 16:46:36 -0700 Subject: [PATCH 03/15] add initial_skip_count to TimeSkippingStatePropagation and refine comments --- openapi/openapiv2.json | 13 +++++++++---- openapi/openapiv3.yaml | 24 ++++++++++++++---------- temporal/api/common/v1/message.proto | 22 +++++++++++++--------- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 85c6b12a5..a19d69bf6 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20011,7 +20011,7 @@ "maxSkipCount": { "type": "integer", "format": "int32", - "description": "The maximum skip count allowed after time skipping is enabled. It is used to protect the current execution\nfrom unlimited retries when backoff is skipped. Every time the execution skips some time,\nthe skip count is incremented by one, and when it reaches max_skip_count, time skipping will be disabled.\n\nIf the field is not set, a large default value (e.g. 100) will be set by the server.\nThe default value can be changed through dynamic config, and can be overridden by this field if set." + "description": "The maximum skip count allowed after time skipping is enabled for an execution.\nIt protects the current execution from unlimited retries when backoff is skipped.\nEvery time the execution skips some time, the skip count is incremented by one,\nand when it reaches max_skip_count, time skipping is disabled.\nFor an execution with a chain of runs, the count is accumulated across all runs.\n\nIf the field is not set, a large default value (e.g. 100) will be set by the server.\nThe default value can be changed through dynamic config, and can be overridden by this field if set." } }, "description": "The configuration for time skipping of an execution.\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nOptions like fast_forward, disable_propagation, and max_skip_count are provided for granular\ncontrol of the execution's time skipping behavior. See each field's comment for a detailed explanation.\n\nAn example of workflows with time skipping:\nFor workflows, an execution is a chain of runs including retries, cron, and continue-as-new.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc.\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.\nEvery time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled.\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." @@ -20056,15 +20056,20 @@ "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)." + "description": "The time skipped by the previous run. It is propagated both to executions started by the\ncurrent execution and through 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 fast-forward target time. It only propagates across a chain of runs within the same execution." + }, + "initialSkipCount": { + "type": "integer", + "format": "int32", + "description": "The initial skip count. It only propagates across a chain of runs within the same execution." } }, - "description": "The time-skipping state that needs to be propagated from a parent workflow to a child workflow,\nor through a chain of runs." + "description": "The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs\nwithin the same execution." }, "v1TimeoutFailureInfo": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 08b3266d5..2472393b1 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17776,9 +17776,11 @@ components: maxSkipCount: type: integer description: |- - The maximum skip count allowed after time skipping is enabled. It is used to protect the current execution - from unlimited retries when backoff is skipped. Every time the execution skips some time, - the skip count is incremented by one, and when it reaches max_skip_count, time skipping will be disabled. + The maximum skip count allowed after time skipping is enabled for an execution. + It protects the current execution from unlimited retries when backoff is skipped. + Every time the execution skips some time, the skip count is incremented by one, + and when it reaches max_skip_count, time skipping is disabled. + For an execution with a chain of runs, the count is accumulated across all runs. If the field is not set, a large default value (e.g. 100) will be set by the server. The default value can be changed through dynamic config, and can be overridden by this field if set. @@ -17837,17 +17839,19 @@ components: 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). + The time skipped by the previous run. It is propagated both to executions started by the + current execution and through 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. + description: The fast-forward target time. It only propagates across a chain of runs within the same execution. format: date-time + initialSkipCount: + type: integer + description: The initial skip count. It only propagates across a chain of runs within the same execution. + format: int32 description: |- - The time-skipping state that needs to be propagated from a parent workflow to a child workflow, - or through a chain of runs. + The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs + within the same execution. TimeoutFailureInfo: type: object properties: diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index dd9c66457..091546956 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -438,26 +438,30 @@ message TimeSkippingConfig { // This flag disables that inheritance. bool disable_propagation = 3; - // The maximum skip count allowed after time skipping is enabled. It is used to protect the current execution - // from unlimited retries when backoff is skipped. Every time the execution skips some time, - // the skip count is incremented by one, and when it reaches max_skip_count, time skipping will be disabled. + // The maximum skip count allowed after time skipping is enabled for an execution. + // It protects the current execution from unlimited retries when backoff is skipped. + // Every time the execution skips some time, the skip count is incremented by one, + // and when it reaches max_skip_count, time skipping is disabled. + // For an execution with a chain of runs, the count is accumulated across all runs. // // If the field is not set, a large default value (e.g. 100) will be set by the server. // The default value can be changed through dynamic config, and can be overridden by this field if set. int32 max_skip_count = 4; } -// The time-skipping state that needs to be propagated from a parent workflow to a child workflow, -// or through a chain of runs. +// The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs +// within the same execution. 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). + // The time skipped by the previous run. It is propagated both to executions started by the + // current execution and through 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. + // The fast-forward target time. It only propagates across a chain of runs within the same execution. google.protobuf.Timestamp fast_forward_target_time = 2; + + // The initial skip count. It only propagates across a chain of runs within the same execution. + int32 initial_skip_count = 3; } From 746b497ccd0195b58f1696c6da09e9843811d437 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Sun, 19 Jul 2026 18:19:28 -0700 Subject: [PATCH 04/15] rename PollWorkflowExecutionTimeSkippingResponse fields --- openapi/openapiv2.json | 20 ++++++++------- openapi/openapiv3.yaml | 14 +++++++---- .../workflowservice/v1/request_response.proto | 25 +++++++++++-------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index a19d69bf6..91c8a31f5 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -11145,15 +11145,16 @@ } } }, - "PollWorkflowExecutionTimeSkippingResponseReturnReason": { + "PollWorkflowExecutionTimeSkippingResponseResult": { "type": "string", "enum": [ - "RETURN_REASON_UNSPECIFIED", - "RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED", - "RETURN_REASON_FAST_FORWARD_COMPLETED" + "RESULT_UNSPECIFIED", + "RESULT_TIMESKIPPING_CONFIG_UPDATED", + "RESULT_FAST_FORWARD_COMPLETED", + "RESULT_NO_PENDING_FAST_FORWARD" ], - "default": "RETURN_REASON_UNSPECIFIED", - "description": "Why the poll returned.\n\n - RETURN_REASON_UNSPECIFIED: The poll returned because its server-side timeout elapsed without any change. The caller\nmay poll again.\n - RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED: The execution's time-skipping config was updated.\n - RETURN_REASON_FAST_FORWARD_COMPLETED: The active fast-forward reached its target time and completed." + "default": "RESULT_UNSPECIFIED", + "description": " - RESULT_UNSPECIFIED: The poll timed out server-side before any time-skipping state change occurred to fast forward.\nThe caller may poll again.\n - RESULT_TIMESKIPPING_CONFIG_UPDATED: The execution's time-skipping config changed while the poll was waiting.\n - RESULT_FAST_FORWARD_COMPLETED: The fast-forward that was active when the poll started reached its target time and completed.\n - RESULT_NO_PENDING_FAST_FORWARD: No fast-forward was in progress when the poll started, so there was nothing to wait for." }, "PostResetOperationSignalWorkflow": { "type": "object", @@ -18070,12 +18071,13 @@ "v1PollWorkflowExecutionTimeSkippingResponse": { "type": "object", "properties": { - "reason": { - "$ref": "#/definitions/PollWorkflowExecutionTimeSkippingResponseReturnReason" + "result": { + "$ref": "#/definitions/PollWorkflowExecutionTimeSkippingResponseResult", + "description": "The result of this poll." }, "fastForwardInfo": { "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", - "description": "The current fast-forward on the execution, if any." + "description": "The execution's most recent fast-forward, whether it is still pending or already completed.\nUnset only if the execution has never had a fast-forward." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 2472393b1..1a592b23f 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -14483,17 +14483,21 @@ components: PollWorkflowExecutionTimeSkippingResponse: type: object properties: - reason: + result: enum: - - RETURN_REASON_UNSPECIFIED - - RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED - - RETURN_REASON_FAST_FORWARD_COMPLETED + - RESULT_UNSPECIFIED + - RESULT_TIMESKIPPING_CONFIG_UPDATED + - RESULT_FAST_FORWARD_COMPLETED + - RESULT_NO_PENDING_FAST_FORWARD type: string + description: The result of this poll. format: enum fastForwardInfo: allOf: - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' - description: The current fast-forward on the execution, if any. + description: |- + The execution's most recent fast-forward, whether it is still pending or already completed. + Unset only if the execution has never had a fast-forward. PollWorkflowTaskQueueResponse: type: object properties: diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index bf58201af..3c2d48b01 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -3644,18 +3644,21 @@ message PollWorkflowExecutionTimeSkippingRequest { } message PollWorkflowExecutionTimeSkippingResponse { - // Why the poll returned. - enum ReturnReason { - // The poll returned because its server-side timeout elapsed without any change. The caller - // may poll again. - RETURN_REASON_UNSPECIFIED = 0; - // The execution's time-skipping config was updated. - RETURN_REASON_TIMESKIPPING_CONFIG_UPDATED = 1; - // The active fast-forward reached its target time and completed. - RETURN_REASON_FAST_FORWARD_COMPLETED = 2; + enum Result { + // The poll timed out server-side before any time-skipping state change occurred to fast forward. + // The caller may poll again. + RESULT_UNSPECIFIED = 0; + // The execution's time-skipping config changed while the poll was waiting. + RESULT_TIMESKIPPING_CONFIG_UPDATED = 1; + // The fast-forward that was active when the poll started reached its target time and completed. + RESULT_FAST_FORWARD_COMPLETED = 2; + // No fast-forward was in progress when the poll started, so there was nothing to wait for. + RESULT_NO_PENDING_FAST_FORWARD = 3; } - ReturnReason reason = 1; + // The result of this poll. + Result result = 1; - // The current fast-forward on the execution, if any. + // The execution's most recent fast-forward, whether it is still pending or already completed. + // Unset only if the execution has never had a fast-forward. temporal.api.common.v1.TimeSkippingFastForwardInfo fast_forward_info = 2; } From 63300850d24ad75d733a0429be29e1a901b245f7 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Sun, 19 Jul 2026 18:37:20 -0700 Subject: [PATCH 05/15] rename max_skip_per_session, add RESULT_WORKFLOW_ENDED --- openapi/openapiv2.json | 6 ++--- openapi/openapiv3.yaml | 23 +++++++++++-------- temporal/api/common/v1/message.proto | 23 +++++++++++-------- .../workflowservice/v1/request_response.proto | 3 +++ 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 91c8a31f5..2b6d2cf2e 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20010,13 +20010,13 @@ "type": "boolean", "description": "By default, executions started by another execution (e.g. a child workflow of a parent workflow or\na schedule with the timeskipping policy enabled), inherit the \"enabled\" flag and skip time when possible.\nThis flag disables that inheritance." }, - "maxSkipCount": { + "maxSkipPerSession": { "type": "integer", "format": "int32", - "description": "The maximum skip count allowed after time skipping is enabled for an execution.\nIt protects the current execution from unlimited retries when backoff is skipped.\nEvery time the execution skips some time, the skip count is incremented by one,\nand when it reaches max_skip_count, time skipping is disabled.\nFor an execution with a chain of runs, the count is accumulated across all runs.\n\nIf the field is not set, a large default value (e.g. 100) will be set by the server.\nThe default value can be changed through dynamic config, and can be overridden by this field if set." + "description": "The maximum number of skips allowed within a single time-skipping session, where a session\nruns from when time skipping is enabled until it is disabled. It protects the execution from\nunlimited retries when backoff is skipped.\nEvery time the execution skips time, the skip count is incremented by one, and when it reaches\nmax_skip_per_session, time skipping is disabled.\nFor an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated\nacross all runs within the same session. The count resets to 0 at the start of each new session,\ni.e. each time this config is updated to re-enable time skipping.\n\nIf this field is not set, the server applies a large default value (e.g. 100). The default can\nbe changed through dynamic config, and is overridden by this field when set." } }, - "description": "The configuration for time skipping of an execution.\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nOptions like fast_forward, disable_propagation, and max_skip_count are provided for granular\ncontrol of the execution's time skipping behavior. See each field's comment for a detailed explanation.\n\nAn example of workflows with time skipping:\nFor workflows, an execution is a chain of runs including retries, cron, and continue-as-new.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc.\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.\nEvery time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled.\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 an execution.\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nOptions like fast_forward, disable_propagation, and max_skip_per_session are provided for granular\ncontrol of the execution's time skipping behavior. See each field's comment for a detailed explanation.\n\nAn example of workflows with time skipping:\nFor workflows, an execution is a chain of runs including retries, cron, and continue-as-new.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc.\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.\nEvery time time is skipped, the skip count is incremented by one; max_skip_per_session bounds the number of skips allowed within a single time-skipping session.\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." }, "v1TimeSkippingFastForwardInfo": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 1a592b23f..1b5ef565f 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17777,22 +17777,25 @@ components: By default, executions started by another execution (e.g. a child workflow of a parent workflow or a schedule with the timeskipping policy enabled), inherit the "enabled" flag and skip time when possible. This flag disables that inheritance. - maxSkipCount: + maxSkipPerSession: type: integer description: |- - The maximum skip count allowed after time skipping is enabled for an execution. - It protects the current execution from unlimited retries when backoff is skipped. - Every time the execution skips some time, the skip count is incremented by one, - and when it reaches max_skip_count, time skipping is disabled. - For an execution with a chain of runs, the count is accumulated across all runs. + The maximum number of skips allowed within a single time-skipping session, where a session + runs from when time skipping is enabled until it is disabled. It protects the execution from + unlimited retries when backoff is skipped. + Every time the execution skips time, the skip count is incremented by one, and when it reaches + max_skip_per_session, time skipping is disabled. + For an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated + across all runs within the same session. The count resets to 0 at the start of each new session, + i.e. each time this config is updated to re-enable time skipping. - If the field is not set, a large default value (e.g. 100) will be set by the server. - The default value can be changed through dynamic config, and can be overridden by this field if set. + If this field is not set, the server applies a large default value (e.g. 100). The default can + be changed through dynamic config, and is overridden by this field when set. format: int32 description: |- The configuration for time skipping of an execution. When time skipping is enabled, virtual time advances automatically whenever there is no in-flight work. - Options like fast_forward, disable_propagation, and max_skip_count are provided for granular + Options like fast_forward, disable_propagation, and max_skip_per_session are provided for granular control of the execution's time skipping behavior. See each field's comment for a detailed explanation. An example of workflows with time skipping: @@ -17800,7 +17803,7 @@ components: In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc. 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. - Every time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled. + Every time time is skipped, the skip count is incremented by one; max_skip_per_session bounds the number of skips allowed within a single time-skipping session. 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 diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 091546956..04dba0f46 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -405,7 +405,7 @@ message OnConflictOptions { // The configuration for time skipping of an execution. // When time skipping is enabled, virtual time advances automatically whenever there is no in-flight work. -// Options like fast_forward, disable_propagation, and max_skip_count are provided for granular +// Options like fast_forward, disable_propagation, and max_skip_per_session are provided for granular // control of the execution's time skipping behavior. See each field's comment for a detailed explanation. // // An example of workflows with time skipping: @@ -413,7 +413,7 @@ message OnConflictOptions { // In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc. // 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. -// Every time time is skipped, the skip count is incremented by one, and a maximum skip count is allowed each time time skipping is enabled. +// Every time time is skipped, the skip count is incremented by one; max_skip_per_session bounds the number of skips allowed within a single time-skipping session. // 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 @@ -438,15 +438,18 @@ message TimeSkippingConfig { // This flag disables that inheritance. bool disable_propagation = 3; - // The maximum skip count allowed after time skipping is enabled for an execution. - // It protects the current execution from unlimited retries when backoff is skipped. - // Every time the execution skips some time, the skip count is incremented by one, - // and when it reaches max_skip_count, time skipping is disabled. - // For an execution with a chain of runs, the count is accumulated across all runs. + // The maximum number of skips allowed within a single time-skipping session, where a session + // runs from when time skipping is enabled until it is disabled. It protects the execution from + // unlimited retries when backoff is skipped. + // Every time the execution skips time, the skip count is incremented by one, and when it reaches + // max_skip_per_session, time skipping is disabled. + // For an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated + // across all runs within the same session. The count resets to 0 at the start of each new session, + // i.e. each time this config is updated to re-enable time skipping. // - // If the field is not set, a large default value (e.g. 100) will be set by the server. - // The default value can be changed through dynamic config, and can be overridden by this field if set. - int32 max_skip_count = 4; + // If this field is not set, the server applies a large default value (e.g. 100). The default can + // be changed through dynamic config, and is overridden by this field when set. + int32 max_skip_per_session = 4; } // The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 3c2d48b01..c91ef1ed5 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -3654,6 +3654,9 @@ message PollWorkflowExecutionTimeSkippingResponse { RESULT_FAST_FORWARD_COMPLETED = 2; // No fast-forward was in progress when the poll started, so there was nothing to wait for. RESULT_NO_PENDING_FAST_FORWARD = 3; + // The workflow has ended, where "workflow" refers to the whole chain of runs (so a + // continue-as-new does not count as ended) and "ended" means it reached a terminal status. + RESULT_WORKFLOW_ENDED = 4; } // The result of this poll. Result result = 1; From fe738889b3136efe89745ba141bb6bf723d046f7 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Mon, 20 Jul 2026 16:46:39 -0700 Subject: [PATCH 06/15] add fast_forward_id, and other runtime fields to TimeSkippingInfo --- openapi/openapiv2.json | 36 ++++++++++++++++-- openapi/openapiv3.yaml | 37 ++++++++++++++++--- temporal/api/common/v1/message.proto | 10 +++++ .../workflowservice/v1/request_response.proto | 32 ++++++++++------ 4 files changed, 94 insertions(+), 21 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 2b6d2cf2e..eeaee35bc 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -4463,6 +4463,13 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "fastForwardId", + "description": "Required. Identifies the fast-forward whose completion the caller wants to wait for.\nMust match the `fast_forward_id` set in the execution's TimeSkippingConfig.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -10040,6 +10047,13 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "fastForwardId", + "description": "Required. Identifies the fast-forward whose completion the caller wants to wait for.\nMust match the `fast_forward_id` set in the execution's TimeSkippingConfig.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -11149,12 +11163,14 @@ "type": "string", "enum": [ "RESULT_UNSPECIFIED", - "RESULT_TIMESKIPPING_CONFIG_UPDATED", + "RESULT_POLL_TIMEOUT", "RESULT_FAST_FORWARD_COMPLETED", - "RESULT_NO_PENDING_FAST_FORWARD" + "RESULT_FAST_FORWARD_NOT_FOUND", + "RESULT_FAST_FORWARD_OVERRIDDEN", + "RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION" ], "default": "RESULT_UNSPECIFIED", - "description": " - RESULT_UNSPECIFIED: The poll timed out server-side before any time-skipping state change occurred to fast forward.\nThe caller may poll again.\n - RESULT_TIMESKIPPING_CONFIG_UPDATED: The execution's time-skipping config changed while the poll was waiting.\n - RESULT_FAST_FORWARD_COMPLETED: The fast-forward that was active when the poll started reached its target time and completed.\n - RESULT_NO_PENDING_FAST_FORWARD: No fast-forward was in progress when the poll started, so there was nothing to wait for." + "description": " - RESULT_UNSPECIFIED: Never returned; guards against an unset result.\n - RESULT_POLL_TIMEOUT: The poll timed out server-side before the fast-forward completed. The caller may poll again.\n - RESULT_FAST_FORWARD_COMPLETED: The fast-forward identified by the request's `fast_forward_id` reached its target time and completed.\n - RESULT_FAST_FORWARD_NOT_FOUND: The request's `fast_forward_id` does not match the execution's current `fast_forward_id`.\nThe API returns immediately with this result, and `fast_forward_info` carries the execution's\ncurrent fast-forward info.\n - RESULT_FAST_FORWARD_OVERRIDDEN: The request's `fast_forward_id` matched the execution's current `fast_forward_id`, but while\nwaiting for it to complete the fast-forward was overridden by another call that changed the\nTimeSkippingConfig.\n - RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION: The request's `fast_forward_id` matched the execution's current `fast_forward_id`,\nbut the workflow execution (the whole chain of runs) has completed before fast forward has a chance to complete." }, "PostResetOperationSignalWorkflow": { "type": "object", @@ -18077,7 +18093,7 @@ }, "fastForwardInfo": { "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", - "description": "The execution's most recent fast-forward, whether it is still pending or already completed.\nUnset only if the execution has never had a fast-forward." + "description": "The execution's most recent fast forward. Unset if time skipping was enabled without a fast-forward." } } }, @@ -20006,6 +20022,10 @@ "type": "string", "description": "Optionally fast-forward the current execution by this duration ahead of current workflow time.\nAfter the fast-forward completes, time skipping is disabled. And it can be re-enabled by calling\nan update API with the time-skipping opt-in.\n\nFor a given execution, only one active fast-forward is allowed at a time.\nIf a new fast-forward is set via an update call 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 not\npass beyond the end of the execution, and fast_forward won't have a chance to complete." }, + "fastForwardId": { + "type": "string", + "description": "A client-supplied ID that must be set together with `fast_forward`. It is used to poll for\nfast-forward completion via PollWorkflowExecutionTimeSkipping.\nThe server performs no idempotency check on this ID; the client is responsible for managing it." + }, "disablePropagation": { "type": "boolean", "description": "By default, executions started by another execution (e.g. a child workflow of a parent workflow or\na schedule with the timeskipping policy enabled), inherit the \"enabled\" flag and skip time when possible.\nThis flag disables that inheritance." @@ -20034,6 +20054,10 @@ "hasCompleted": { "type": "boolean", "description": "True once `target_time` has been reached." + }, + "fastForwardId": { + "type": "string", + "description": "The client-supplied ID set alongside `fast_forward` when this fast-forward was created." } }, "description": "TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution." @@ -20049,6 +20073,10 @@ "isRunning": { "type": "boolean", "description": "If the execution is actively trying to skip time automatically when there is a chance,\nthis field will be set to true. If time has stopped skipping either by fast-forward completion,\nmax skip allowed checking, or user configuration, it will be false." + }, + "fastForwardInfo": { + "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", + "description": "The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward." } }, "description": "Describes the current time-skipping state of a workflow execution." diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 1b5ef565f..b09a50f0b 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -4601,6 +4601,13 @@ paths: in: query schema: type: string + - name: fastForwardId + in: query + description: |- + Required. Identifies the fast-forward whose completion the caller wants to wait for. + Must match the `fast_forward_id` set in the execution's TimeSkippingConfig. + schema: + type: string responses: "200": description: OK @@ -9639,6 +9646,13 @@ paths: in: query schema: type: string + - name: fastForwardId + in: query + description: |- + Required. Identifies the fast-forward whose completion the caller wants to wait for. + Must match the `fast_forward_id` set in the execution's TimeSkippingConfig. + schema: + type: string responses: "200": description: OK @@ -14486,18 +14500,18 @@ components: result: enum: - RESULT_UNSPECIFIED - - RESULT_TIMESKIPPING_CONFIG_UPDATED + - RESULT_POLL_TIMEOUT - RESULT_FAST_FORWARD_COMPLETED - - RESULT_NO_PENDING_FAST_FORWARD + - RESULT_FAST_FORWARD_NOT_FOUND + - RESULT_FAST_FORWARD_OVERRIDDEN + - RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION type: string description: The result of this poll. format: enum fastForwardInfo: allOf: - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' - description: |- - The execution's most recent fast-forward, whether it is still pending or already completed. - Unset only if the execution has never had a fast-forward. + description: The execution's most recent fast forward. Unset if time skipping was enabled without a fast-forward. PollWorkflowTaskQueueResponse: type: object properties: @@ -17771,6 +17785,12 @@ components: one completes, the new one will override the previous one. If the fast-forward duration exceeds the remaining execution timeout, time will not pass beyond the end of the execution, and fast_forward won't have a chance to complete. + fastForwardId: + type: string + description: |- + A client-supplied ID that must be set together with `fast_forward`. It is used to poll for + fast-forward completion via PollWorkflowExecutionTimeSkipping. + The server performs no idempotency check on this ID; the client is responsible for managing it. disablePropagation: type: boolean description: |- @@ -17822,6 +17842,9 @@ components: hasCompleted: type: boolean description: True once `target_time` has been reached. + fastForwardId: + type: string + description: The client-supplied ID set alongside `fast_forward` when this fast-forward was created. description: TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution. TimeSkippingInfo: type: object @@ -17838,6 +17861,10 @@ components: If the execution is actively trying to skip time automatically when there is a chance, this field will be set to true. If time has stopped skipping either by fast-forward completion, max skip allowed checking, or user configuration, it will be false. + fastForwardInfo: + allOf: + - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' + description: The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. description: Describes the current time-skipping state of a workflow execution. TimeSkippingStatePropagation: type: object diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 04dba0f46..6f446f02f 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -433,6 +433,11 @@ message TimeSkippingConfig { // pass beyond the end of the execution, and fast_forward won't have a chance to complete. google.protobuf.Duration fast_forward = 2; + // A client-supplied ID that must be set together with `fast_forward`. It is used to poll for + // fast-forward completion via PollWorkflowExecutionTimeSkipping. + // The server performs no idempotency check on this ID; the client is responsible for managing it. + string fast_forward_id = 5; + // By default, executions started by another execution (e.g. a child workflow of a parent workflow or // a schedule with the timeskipping policy enabled), inherit the "enabled" flag and skip time when possible. // This flag disables that inheritance. @@ -478,6 +483,9 @@ message TimeSkippingInfo { // this field will be set to true. If time has stopped skipping either by fast-forward completion, // max skip allowed checking, or user configuration, it will be false. bool is_running = 2; + + // The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. + TimeSkippingFastForwardInfo fast_forward_info = 3; } @@ -489,4 +497,6 @@ message TimeSkippingFastForwardInfo { google.protobuf.Timestamp target_time = 2; // True once `target_time` has been reached. bool has_completed = 3; + // The client-supplied ID set alongside `fast_forward` when this fast-forward was created. + string fast_forward_id = 4; } diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index c91ef1ed5..91a01d582 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -3641,27 +3641,35 @@ message DeleteNexusOperationExecutionResponse { message PollWorkflowExecutionTimeSkippingRequest { string namespace = 1; temporal.api.common.v1.WorkflowExecution workflow_execution = 2; + // Required. Identifies the fast-forward whose completion the caller wants to wait for. + // Must match the `fast_forward_id` set in the execution's TimeSkippingConfig. + string fast_forward_id = 3; } message PollWorkflowExecutionTimeSkippingResponse { enum Result { - // The poll timed out server-side before any time-skipping state change occurred to fast forward. - // The caller may poll again. + // Never returned; guards against an unset result. RESULT_UNSPECIFIED = 0; - // The execution's time-skipping config changed while the poll was waiting. - RESULT_TIMESKIPPING_CONFIG_UPDATED = 1; - // The fast-forward that was active when the poll started reached its target time and completed. + // The poll timed out server-side before the fast-forward completed. The caller may poll again. + RESULT_POLL_TIMEOUT = 1; + // The fast-forward identified by the request's `fast_forward_id` reached its target time and completed. RESULT_FAST_FORWARD_COMPLETED = 2; - // No fast-forward was in progress when the poll started, so there was nothing to wait for. - RESULT_NO_PENDING_FAST_FORWARD = 3; - // The workflow has ended, where "workflow" refers to the whole chain of runs (so a - // continue-as-new does not count as ended) and "ended" means it reached a terminal status. - RESULT_WORKFLOW_ENDED = 4; + // The request's `fast_forward_id` does not match the execution's current `fast_forward_id`. + // The API returns immediately with this result, and `fast_forward_info` carries the execution's + // current fast-forward info. + RESULT_FAST_FORWARD_NOT_FOUND = 3; + // The request's `fast_forward_id` matched the execution's current `fast_forward_id`, but while + // waiting for it to complete the fast-forward was overridden by another call that changed the + // TimeSkippingConfig. + RESULT_FAST_FORWARD_OVERRIDDEN = 4; + // The request's `fast_forward_id` matched the execution's current `fast_forward_id`, + // but the workflow execution (the whole chain of runs) has completed before fast forward has a chance to complete. + RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION = 5; } + // The result of this poll. Result result = 1; - // The execution's most recent fast-forward, whether it is still pending or already completed. - // Unset only if the execution has never had a fast-forward. + // The execution's most recent fast forward. Unset if time skipping was enabled without a fast-forward. temporal.api.common.v1.TimeSkippingFastForwardInfo fast_forward_info = 2; } From 186760c9b278071d6f177b1ff13cbe60429028c3 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Wed, 22 Jul 2026 16:00:20 -0700 Subject: [PATCH 07/15] delete update_time so that we don't mislead users to use time for fast-forward verification --- temporal/api/common/v1/message.proto | 12 ++++++------ .../api/workflowservice/v1/request_response.proto | 6 ++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 6f446f02f..2d5a6b28a 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -491,12 +491,12 @@ message TimeSkippingInfo { // TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution. message TimeSkippingFastForwardInfo { - // The virtual time at which the fast-forward was created. - google.protobuf.Timestamp create_time = 1; + // The client-supplied `fast_forward` duration. + google.protobub.Duration fast_forward_duration = 1; + // The client-supplied ID set alongside `fast_forward` duration. + string fast_forward_id = 2; // The target virtual time at which the fast-forward completes. - google.protobuf.Timestamp target_time = 2; + google.protobuf.Timestamp target_time = 3; // True once `target_time` has been reached. - bool has_completed = 3; - // The client-supplied ID set alongside `fast_forward` when this fast-forward was created. - string fast_forward_id = 4; + bool has_completed = 4; } diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 91a01d582..070b1227c 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -2445,10 +2445,8 @@ 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; + reserved 2; + reserved "update_time"; } // [cleanup-wv-pre-release] Pre-release deployment APIs, clean up later From e08d56431449b00c25302a498c600c0ed1b04503 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Wed, 22 Jul 2026 16:04:13 -0700 Subject: [PATCH 08/15] change comments of update time in update wf options --- openapi/openapiv2.json | 15 +++++++-------- openapi/openapiv3.yaml | 15 +++++++++------ temporal/api/common/v1/message.proto | 2 +- .../api/workflowservice/v1/request_response.proto | 9 +++++++-- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index eeaee35bc..dd14eb361 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20041,10 +20041,13 @@ "v1TimeSkippingFastForwardInfo": { "type": "object", "properties": { - "createTime": { + "fastForwardDuration": { "type": "string", - "format": "date-time", - "description": "The virtual time at which the fast-forward was created." + "description": "The client-supplied `fast_forward` duration." + }, + "fastForwardId": { + "type": "string", + "description": "The client-supplied ID set alongside `fast_forward` duration." }, "targetTime": { "type": "string", @@ -20054,10 +20057,6 @@ "hasCompleted": { "type": "boolean", "description": "True once `target_time` has been reached." - }, - "fastForwardId": { - "type": "string", - "description": "The client-supplied ID set alongside `fast_forward` when this fast-forward was created." } }, "description": "TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution." @@ -20400,7 +20399,7 @@ "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." + "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.\n\nThis timestamp cannot be used for time-skipping fast-forward info verification,\nuse `fast_forward_id` instead." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index b09a50f0b..d23317105 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17831,10 +17831,13 @@ components: TimeSkippingFastForwardInfo: type: object properties: - createTime: + fastForwardDuration: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ type: string - description: The virtual time at which the fast-forward was created. - format: date-time + description: The client-supplied `fast_forward` duration. + fastForwardId: + type: string + description: The client-supplied ID set alongside `fast_forward` duration. targetTime: type: string description: The target virtual time at which the fast-forward completes. @@ -17842,9 +17845,6 @@ components: hasCompleted: type: boolean description: True once `target_time` has been reached. - fastForwardId: - type: string - description: The client-supplied ID set alongside `fast_forward` when this fast-forward was created. description: TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution. TimeSkippingInfo: type: object @@ -18555,6 +18555,9 @@ components: 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. + + This timestamp cannot be used for time-skipping fast-forward info verification, + use `fast_forward_id` instead. format: date-time UpdateWorkflowExecutionRequest: type: object diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 2d5a6b28a..455647b12 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -492,7 +492,7 @@ message TimeSkippingInfo { // TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution. message TimeSkippingFastForwardInfo { // The client-supplied `fast_forward` duration. - google.protobub.Duration fast_forward_duration = 1; + google.protobuf.Duration fast_forward_duration = 1; // The client-supplied ID set alongside `fast_forward` duration. string fast_forward_id = 2; // The target virtual time at which the fast-forward completes. diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 070b1227c..28c5dbbb1 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -2445,8 +2445,13 @@ message UpdateWorkflowExecutionOptionsRequest { message UpdateWorkflowExecutionOptionsResponse { // Workflow Execution options after update. temporal.api.workflow.v1.WorkflowExecutionOptions workflow_execution_options = 1; - reserved 2; - reserved "update_time"; + + // 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. + // + // This timestamp cannot be used for time-skipping fast-forward info verification, + // use `fast_forward_id` instead. + google.protobuf.Timestamp update_time = 2; } // [cleanup-wv-pre-release] Pre-release deployment APIs, clean up later From bea40da4c5a28e848816074034cc6c447efa27c7 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Wed, 22 Jul 2026 18:30:06 -0700 Subject: [PATCH 09/15] =?UTF-8?q?consolidated=20the=20Result=20types=20?= =?UTF-8?q?=E2=80=94=20no=20need=20to=20maintain=20overly=20fine-grained?= =?UTF-8?q?=20distinctions=20between=20types,=20which=20reduces=20complexi?= =?UTF-8?q?ty.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflowservice/v1/request_response.proto | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 28c5dbbb1..1a23e4102 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -3652,22 +3652,17 @@ message PollWorkflowExecutionTimeSkippingRequest { message PollWorkflowExecutionTimeSkippingResponse { enum Result { // Never returned; guards against an unset result. - RESULT_UNSPECIFIED = 0; + POLL_FAST_FORWARD_RESULT_UNSPECIFIED = 0; // The poll timed out server-side before the fast-forward completed. The caller may poll again. - RESULT_POLL_TIMEOUT = 1; + POLL_FAST_FORWARD_RESULT_TIMEOUT = 1; // The fast-forward identified by the request's `fast_forward_id` reached its target time and completed. - RESULT_FAST_FORWARD_COMPLETED = 2; + POLL_FAST_FORWARD_RESULT_COMPLETED = 2; // The request's `fast_forward_id` does not match the execution's current `fast_forward_id`. - // The API returns immediately with this result, and `fast_forward_info` carries the execution's - // current fast-forward info. - RESULT_FAST_FORWARD_NOT_FOUND = 3; - // The request's `fast_forward_id` matched the execution's current `fast_forward_id`, but while - // waiting for it to complete the fast-forward was overridden by another call that changed the - // TimeSkippingConfig. - RESULT_FAST_FORWARD_OVERRIDDEN = 4; + POLL_FAST_FORWARD_RESULT_ID_MISMATCH = 3; // The request's `fast_forward_id` matched the execution's current `fast_forward_id`, - // but the workflow execution (the whole chain of runs) has completed before fast forward has a chance to complete. - RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION = 5; + // but the workflow execution (the entire chain of runs) completed before the fast-forward + // had a chance to complete. + POLL_FAST_FORWARD_RESULT_WORKFLOW_ENDED_BEFORE_COMPLETION = 5; } // The result of this poll. From aacade53cc079aa4a555071105c04155ea672e30 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Wed, 22 Jul 2026 18:37:29 -0700 Subject: [PATCH 10/15] similify poll result --- openapi/openapiv2.json | 7 +++---- openapi/openapiv3.yaml | 5 ++--- temporal/api/workflowservice/v1/request_response.proto | 10 +++++----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index dd14eb361..11ffadd85 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -11165,12 +11165,11 @@ "RESULT_UNSPECIFIED", "RESULT_POLL_TIMEOUT", "RESULT_FAST_FORWARD_COMPLETED", - "RESULT_FAST_FORWARD_NOT_FOUND", - "RESULT_FAST_FORWARD_OVERRIDDEN", - "RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION" + "RESULT_FAST_FORWARD_ID_MISMATCH", + "RESULT_WORKFLOW_ENDED_BEFORE_FAST_FORWARD_COMPLETION" ], "default": "RESULT_UNSPECIFIED", - "description": " - RESULT_UNSPECIFIED: Never returned; guards against an unset result.\n - RESULT_POLL_TIMEOUT: The poll timed out server-side before the fast-forward completed. The caller may poll again.\n - RESULT_FAST_FORWARD_COMPLETED: The fast-forward identified by the request's `fast_forward_id` reached its target time and completed.\n - RESULT_FAST_FORWARD_NOT_FOUND: The request's `fast_forward_id` does not match the execution's current `fast_forward_id`.\nThe API returns immediately with this result, and `fast_forward_info` carries the execution's\ncurrent fast-forward info.\n - RESULT_FAST_FORWARD_OVERRIDDEN: The request's `fast_forward_id` matched the execution's current `fast_forward_id`, but while\nwaiting for it to complete the fast-forward was overridden by another call that changed the\nTimeSkippingConfig.\n - RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION: The request's `fast_forward_id` matched the execution's current `fast_forward_id`,\nbut the workflow execution (the whole chain of runs) has completed before fast forward has a chance to complete." + "description": " - RESULT_UNSPECIFIED: Never returned; guards against an unset result.\n - RESULT_POLL_TIMEOUT: The poll timed out server-side before the fast-forward completed. The caller may poll again.\n - RESULT_FAST_FORWARD_COMPLETED: The fast-forward identified by the request's `fast_forward_id` reached its target time and completed.\n - RESULT_FAST_FORWARD_ID_MISMATCH: The request's `fast_forward_id` does not match the execution's current `fast_forward_id`.\n - RESULT_WORKFLOW_ENDED_BEFORE_FAST_FORWARD_COMPLETION: The request's `fast_forward_id` matched the execution's current `fast_forward_id`,\nbut the workflow execution (the entire chain of runs) completed before the fast-forward\nhad a chance to complete." }, "PostResetOperationSignalWorkflow": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index d23317105..cb5cde978 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -14502,9 +14502,8 @@ components: - RESULT_UNSPECIFIED - RESULT_POLL_TIMEOUT - RESULT_FAST_FORWARD_COMPLETED - - RESULT_FAST_FORWARD_NOT_FOUND - - RESULT_FAST_FORWARD_OVERRIDDEN - - RESULT_WORKFLOW_END_BEFORE_FAST_FORWARD_COMPLETION + - RESULT_FAST_FORWARD_ID_MISMATCH + - RESULT_WORKFLOW_ENDED_BEFORE_FAST_FORWARD_COMPLETION type: string description: The result of this poll. format: enum diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 1a23e4102..2bce43e73 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -3652,17 +3652,17 @@ message PollWorkflowExecutionTimeSkippingRequest { message PollWorkflowExecutionTimeSkippingResponse { enum Result { // Never returned; guards against an unset result. - POLL_FAST_FORWARD_RESULT_UNSPECIFIED = 0; + RESULT_UNSPECIFIED = 0; // The poll timed out server-side before the fast-forward completed. The caller may poll again. - POLL_FAST_FORWARD_RESULT_TIMEOUT = 1; + RESULT_POLL_TIMEOUT = 1; // The fast-forward identified by the request's `fast_forward_id` reached its target time and completed. - POLL_FAST_FORWARD_RESULT_COMPLETED = 2; + RESULT_FAST_FORWARD_COMPLETED = 2; // The request's `fast_forward_id` does not match the execution's current `fast_forward_id`. - POLL_FAST_FORWARD_RESULT_ID_MISMATCH = 3; + RESULT_FAST_FORWARD_ID_MISMATCH = 3; // The request's `fast_forward_id` matched the execution's current `fast_forward_id`, // but the workflow execution (the entire chain of runs) completed before the fast-forward // had a chance to complete. - POLL_FAST_FORWARD_RESULT_WORKFLOW_ENDED_BEFORE_COMPLETION = 5; + RESULT_WORKFLOW_ENDED_BEFORE_FAST_FORWARD_COMPLETION = 5; } // The result of this poll. From 588c15b2d2c837aec9f4138328d5b31fd3e0ff44 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Thu, 23 Jul 2026 15:33:16 -0700 Subject: [PATCH 11/15] add TimeSkippingStopReason --- openapi/openapiv2.json | 15 +++++++++++++++ openapi/openapiv3.yaml | 9 +++++++++ temporal/api/common/v1/message.proto | 3 +++ temporal/api/enums/v1/common.proto | 14 +++++++++++++- 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 11ffadd85..e708161ed 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20075,6 +20075,10 @@ "fastForwardInfo": { "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", "description": "The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward." + }, + "stopReason": { + "$ref": "#/definitions/v1TimeSkippingStopReason", + "description": "Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running." } }, "description": "Describes the current time-skipping state of a workflow execution." @@ -20099,6 +20103,17 @@ }, "description": "The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs\nwithin the same execution." }, + "v1TimeSkippingStopReason": { + "type": "string", + "enum": [ + "TIME_SKIPPING_STOP_REASON_UNSPECIFIED", + "TIME_SKIPPING_STOP_REASON_USER_DISABLED", + "TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED", + "TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED" + ], + "default": "TIME_SKIPPING_STOP_REASON_UNSPECIFIED", + "description": "Why time skipping has stopped on an execution.\n\n - TIME_SKIPPING_STOP_REASON_UNSPECIFIED: Unspecified. Set when time skipping is actively running (`is_running` is true), so no reason applies.\n - TIME_SKIPPING_STOP_REASON_USER_DISABLED: Time skipping is disabled by user configuration.\n - TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED: The current fast-forward reached its target time and completed.\n - TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED: The maximum amount of time skipping allowed per session was reached." + }, "v1TimeoutFailureInfo": { "type": "object", "properties": { diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index cb5cde978..5f85373fa 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17864,6 +17864,15 @@ components: allOf: - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' description: The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. + stopReason: + enum: + - TIME_SKIPPING_STOP_REASON_UNSPECIFIED + - TIME_SKIPPING_STOP_REASON_USER_DISABLED + - TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED + - TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED + type: string + description: Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running. + format: enum description: Describes the current time-skipping state of a workflow execution. TimeSkippingStatePropagation: type: object diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 455647b12..7f45e543c 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -486,6 +486,9 @@ message TimeSkippingInfo { // The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. TimeSkippingFastForwardInfo fast_forward_info = 3; + + // Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running. + temporal.api.enums.v1.TimeSkippingStopReason stop_reason = 4; } diff --git a/temporal/api/enums/v1/common.proto b/temporal/api/enums/v1/common.proto index cdc387173..c4b04b814 100644 --- a/temporal/api/enums/v1/common.proto +++ b/temporal/api/enums/v1/common.proto @@ -109,8 +109,20 @@ enum WorkerStatus { enum ExecutionType { EXECUTION_TYPE_UNSPECIFIED = 0; - // A workflow execution archetype. + // A workflow execution archetype. EXECUTION_TYPE_WORKFLOW = 1; // An activity execution archetype. This is reserved for standalone activities. EXECUTION_TYPE_ACTIVITY = 2; +} + +// Why time skipping has stopped on an execution. +enum TimeSkippingStopReason { + // Unspecified. Set when time skipping is actively running (`is_running` is true), so no reason applies. + TIME_SKIPPING_STOP_REASON_UNSPECIFIED = 0; + // Time skipping is disabled by user configuration. + TIME_SKIPPING_STOP_REASON_USER_DISABLED = 1; + // The current fast-forward reached its target time and completed. + TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED = 2; + // The maximum amount of time skipping allowed per session was reached. + TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED = 3; } \ No newline at end of file From 12228b67aedee3f7fd6a9ef5d4ef02f54e239b5e Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Thu, 23 Jul 2026 15:41:41 -0700 Subject: [PATCH 12/15] change field numbers --- openapi/openapiv2.json | 8 ++++---- openapi/openapiv3.yaml | 8 ++++---- temporal/api/common/v1/message.proto | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index e708161ed..74ac90f6b 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20072,13 +20072,13 @@ "type": "boolean", "description": "If the execution is actively trying to skip time automatically when there is a chance,\nthis field will be set to true. If time has stopped skipping either by fast-forward completion,\nmax skip allowed checking, or user configuration, it will be false." }, - "fastForwardInfo": { - "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", - "description": "The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward." - }, "stopReason": { "$ref": "#/definitions/v1TimeSkippingStopReason", "description": "Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running." + }, + "fastForwardInfo": { + "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", + "description": "The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward." } }, "description": "Describes the current time-skipping state of a workflow execution." diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 5f85373fa..ca92093ff 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17860,10 +17860,6 @@ components: If the execution is actively trying to skip time automatically when there is a chance, this field will be set to true. If time has stopped skipping either by fast-forward completion, max skip allowed checking, or user configuration, it will be false. - fastForwardInfo: - allOf: - - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' - description: The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. stopReason: enum: - TIME_SKIPPING_STOP_REASON_UNSPECIFIED @@ -17873,6 +17869,10 @@ components: type: string description: Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running. format: enum + fastForwardInfo: + allOf: + - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' + description: The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. description: Describes the current time-skipping state of a workflow execution. TimeSkippingStatePropagation: type: object diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 7f45e543c..166cc834c 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -484,11 +484,11 @@ message TimeSkippingInfo { // max skip allowed checking, or user configuration, it will be false. bool is_running = 2; - // The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. - TimeSkippingFastForwardInfo fast_forward_info = 3; - // Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running. - temporal.api.enums.v1.TimeSkippingStopReason stop_reason = 4; + temporal.api.enums.v1.TimeSkippingStopReason stop_reason = 3; + + // The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. + TimeSkippingFastForwardInfo fast_forward_info = 4; } From 02f400149f1880726a51808f269781c5310605cb Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Thu, 23 Jul 2026 16:31:43 -0700 Subject: [PATCH 13/15] delete reason --- openapi/openapiv2.json | 25 +++++++++---------------- openapi/openapiv3.yaml | 17 +++++++---------- temporal/api/common/v1/message.proto | 8 ++++---- temporal/api/enums/v1/common.proto | 12 ------------ 4 files changed, 20 insertions(+), 42 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 74ac90f6b..08760bd4b 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20029,7 +20029,7 @@ "type": "boolean", "description": "By default, executions started by another execution (e.g. a child workflow of a parent workflow or\na schedule with the timeskipping policy enabled), inherit the \"enabled\" flag and skip time when possible.\nThis flag disables that inheritance." }, - "maxSkipPerSession": { + "maxSessionSkipCount": { "type": "integer", "format": "int32", "description": "The maximum number of skips allowed within a single time-skipping session, where a session\nruns from when time skipping is enabled until it is disabled. It protects the execution from\nunlimited retries when backoff is skipped.\nEvery time the execution skips time, the skip count is incremented by one, and when it reaches\nmax_skip_per_session, time skipping is disabled.\nFor an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated\nacross all runs within the same session. The count resets to 0 at the start of each new session,\ni.e. each time this config is updated to re-enable time skipping.\n\nIf this field is not set, the server applies a large default value (e.g. 100). The default can\nbe changed through dynamic config, and is overridden by this field when set." @@ -20072,13 +20072,17 @@ "type": "boolean", "description": "If the execution is actively trying to skip time automatically when there is a chance,\nthis field will be set to true. If time has stopped skipping either by fast-forward completion,\nmax skip allowed checking, or user configuration, it will be false." }, - "stopReason": { - "$ref": "#/definitions/v1TimeSkippingStopReason", - "description": "Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running." - }, "fastForwardInfo": { "$ref": "#/definitions/v1TimeSkippingFastForwardInfo", "description": "The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward." + }, + "maxSessionSkipCount": { + "type": "integer", + "format": "int32" + }, + "currentSessionSkipCount": { + "type": "integer", + "format": "int32" } }, "description": "Describes the current time-skipping state of a workflow execution." @@ -20103,17 +20107,6 @@ }, "description": "The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs\nwithin the same execution." }, - "v1TimeSkippingStopReason": { - "type": "string", - "enum": [ - "TIME_SKIPPING_STOP_REASON_UNSPECIFIED", - "TIME_SKIPPING_STOP_REASON_USER_DISABLED", - "TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED", - "TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED" - ], - "default": "TIME_SKIPPING_STOP_REASON_UNSPECIFIED", - "description": "Why time skipping has stopped on an execution.\n\n - TIME_SKIPPING_STOP_REASON_UNSPECIFIED: Unspecified. Set when time skipping is actively running (`is_running` is true), so no reason applies.\n - TIME_SKIPPING_STOP_REASON_USER_DISABLED: Time skipping is disabled by user configuration.\n - TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED: The current fast-forward reached its target time and completed.\n - TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED: The maximum amount of time skipping allowed per session was reached." - }, "v1TimeoutFailureInfo": { "type": "object", "properties": { diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index ca92093ff..1ed2084e6 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17796,7 +17796,7 @@ components: By default, executions started by another execution (e.g. a child workflow of a parent workflow or a schedule with the timeskipping policy enabled), inherit the "enabled" flag and skip time when possible. This flag disables that inheritance. - maxSkipPerSession: + maxSessionSkipCount: type: integer description: |- The maximum number of skips allowed within a single time-skipping session, where a session @@ -17860,19 +17860,16 @@ components: If the execution is actively trying to skip time automatically when there is a chance, this field will be set to true. If time has stopped skipping either by fast-forward completion, max skip allowed checking, or user configuration, it will be false. - stopReason: - enum: - - TIME_SKIPPING_STOP_REASON_UNSPECIFIED - - TIME_SKIPPING_STOP_REASON_USER_DISABLED - - TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED - - TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED - type: string - description: Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running. - format: enum fastForwardInfo: allOf: - $ref: '#/components/schemas/TimeSkippingFastForwardInfo' description: The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. + maxSessionSkipCount: + type: integer + format: int32 + currentSessionSkipCount: + type: integer + format: int32 description: Describes the current time-skipping state of a workflow execution. TimeSkippingStatePropagation: type: object diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 166cc834c..1ef54f0d0 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -454,7 +454,7 @@ message TimeSkippingConfig { // // If this field is not set, the server applies a large default value (e.g. 100). The default can // be changed through dynamic config, and is overridden by this field when set. - int32 max_skip_per_session = 4; + int32 max_session_skip_count = 4; } // The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs @@ -484,11 +484,11 @@ message TimeSkippingInfo { // max skip allowed checking, or user configuration, it will be false. bool is_running = 2; - // Why time skipping has stopped. Set only when `is_running` is false; `UNSPECIFIED` while it is running. - temporal.api.enums.v1.TimeSkippingStopReason stop_reason = 3; - // The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward. TimeSkippingFastForwardInfo fast_forward_info = 4; + + int32 max_session_skip_count = 5; + int32 current_session_skip_count = 6; } diff --git a/temporal/api/enums/v1/common.proto b/temporal/api/enums/v1/common.proto index c4b04b814..5f0a0d3ae 100644 --- a/temporal/api/enums/v1/common.proto +++ b/temporal/api/enums/v1/common.proto @@ -114,15 +114,3 @@ enum ExecutionType { // An activity execution archetype. This is reserved for standalone activities. EXECUTION_TYPE_ACTIVITY = 2; } - -// Why time skipping has stopped on an execution. -enum TimeSkippingStopReason { - // Unspecified. Set when time skipping is actively running (`is_running` is true), so no reason applies. - TIME_SKIPPING_STOP_REASON_UNSPECIFIED = 0; - // Time skipping is disabled by user configuration. - TIME_SKIPPING_STOP_REASON_USER_DISABLED = 1; - // The current fast-forward reached its target time and completed. - TIME_SKIPPING_STOP_REASON_FAST_FORWARD_COMPLETED = 2; - // The maximum amount of time skipping allowed per session was reached. - TIME_SKIPPING_STOP_REASON_MAX_SKIP_PER_SESSION_REACHED = 3; -} \ No newline at end of file From 111f563e6e7424174c20f58929a76fe91c691170 Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Fri, 24 Jul 2026 11:24:44 -0700 Subject: [PATCH 14/15] remodel FastForwardConfig --- Makefile | 6 +--- openapi/openapiv2.json | 23 ++++++++++----- openapi/openapiv3.yaml | 43 ++++++++++++++++------------ temporal/api/common/v1/message.proto | 34 ++++++++++++---------- 4 files changed, 60 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index dc8ab5025..c3bca62cd 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..." @@ -121,10 +121,6 @@ buf-lint: $(STAMPDIR)/buf-mod-prune printf $(COLOR) "Run buf linter..." (cd $(PROTO_ROOT) && buf lint) -buf-breaking: - @printf $(COLOR) "Run buf breaking changes check against main branch..." - @(cd $(PROTO_ROOT) && buf breaking --against 'https://github.com/temporalio/api.git#branch=main') - nexus-rpc-yaml: nexus-rpc-yaml-install printf $(COLOR) "Generate nexus/temporal-proto-models-nexusrpc.yaml..." mkdir -p nexus diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 08760bd4b..2338f0ff6 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -15944,6 +15944,19 @@ } } }, + "v1FastForwardConfig": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "A client-supplied ID, required field, set alongside `duration`. It is used to poll for\nfast-forward completion via PollWorkflowExecutionTimeSkipping.\nThe server performs no idempotency check on this ID; the client is responsible for managing it." + }, + "duration": { + "type": "string", + "description": "Fast-forward the current execution by this duration ahead of the current workflow time; required field.\nOnce the fast-forward completes, no further time is skipped. Time skipping can be resumed either\nby updating the TimeSkippingConfig with a new FastForwardConfig, or by clearing the FastForwardConfig\nto skip through to the end of the execution.\n\nIf this duration exceeds the remaining execution timeout, time will not pass beyond the end\nof the execution, and the fast-forward won't have a chance to complete." + } + } + }, "v1FetchWorkerConfigResponse": { "type": "object", "properties": { @@ -20017,13 +20030,9 @@ "type": "boolean", "description": "Enables or disables time skipping for this workflow execution." }, - "fastForward": { - "type": "string", - "description": "Optionally fast-forward the current execution by this duration ahead of current workflow time.\nAfter the fast-forward completes, time skipping is disabled. And it can be re-enabled by calling\nan update API with the time-skipping opt-in.\n\nFor a given execution, only one active fast-forward is allowed at a time.\nIf a new fast-forward is set via an update call 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 not\npass beyond the end of the execution, and fast_forward won't have a chance to complete." - }, - "fastForwardId": { - "type": "string", - "description": "A client-supplied ID that must be set together with `fast_forward`. It is used to poll for\nfast-forward completion via PollWorkflowExecutionTimeSkipping.\nThe server performs no idempotency check on this ID; the client is responsible for managing it." + "fastForwardConfig": { + "$ref": "#/definitions/v1FastForwardConfig", + "description": "An optional opt-in to control time-skipping behavior through fast-forward; see its definition for details." }, "disablePropagation": { "type": "boolean", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 1ed2084e6..cc2b87bd4 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -12352,6 +12352,26 @@ components: $ref: '#/components/schemas/NexusOperationFailureInfo' nexusHandlerFailureInfo: $ref: '#/components/schemas/NexusHandlerFailureInfo' + FastForwardConfig: + type: object + properties: + id: + type: string + description: |- + A client-supplied ID, required field, set alongside `duration`. It is used to poll for + fast-forward completion via PollWorkflowExecutionTimeSkipping. + The server performs no idempotency check on this ID; the client is responsible for managing it. + duration: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: |- + Fast-forward the current execution by this duration ahead of the current workflow time; required field. + Once the fast-forward completes, no further time is skipped. Time skipping can be resumed either + by updating the TimeSkippingConfig with a new FastForwardConfig, or by clearing the FastForwardConfig + to skip through to the end of the execution. + + If this duration exceeds the remaining execution timeout, time will not pass beyond the end + of the execution, and the fast-forward won't have a chance to complete. FetchWorkerConfigRequest: type: object properties: @@ -17771,25 +17791,10 @@ components: enabled: type: boolean description: Enables or disables time skipping for this workflow execution. - fastForward: - pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ - type: string - description: |- - Optionally fast-forward the current execution by this duration ahead of current workflow time. - After the fast-forward completes, time skipping is disabled. And it can be re-enabled by calling - an update API with the time-skipping opt-in. - - For a given execution, only one active fast-forward is allowed at a time. - If a new fast-forward is set via an update call 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 not - pass beyond the end of the execution, and fast_forward won't have a chance to complete. - fastForwardId: - type: string - description: |- - A client-supplied ID that must be set together with `fast_forward`. It is used to poll for - fast-forward completion via PollWorkflowExecutionTimeSkipping. - The server performs no idempotency check on this ID; the client is responsible for managing it. + fastForwardConfig: + allOf: + - $ref: '#/components/schemas/FastForwardConfig' + description: An optional opt-in to control time-skipping behavior through fast-forward; see its definition for details. disablePropagation: type: boolean description: |- diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 1ef54f0d0..1cdc4386d 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -419,24 +419,12 @@ message OnConflictOptions { // "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 execution by this duration ahead of current workflow time. - // After the fast-forward completes, time skipping is disabled. And it can be re-enabled by calling - // an update API with the time-skipping opt-in. - // - // For a given execution, only one active fast-forward is allowed at a time. - // If a new fast-forward is set via an update call 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 not - // pass beyond the end of the execution, and fast_forward won't have a chance to complete. - google.protobuf.Duration fast_forward = 2; - - // A client-supplied ID that must be set together with `fast_forward`. It is used to poll for - // fast-forward completion via PollWorkflowExecutionTimeSkipping. - // The server performs no idempotency check on this ID; the client is responsible for managing it. - string fast_forward_id = 5; + // An optional opt-in to control time-skipping behavior through fast-forward; see its definition for details. + FastForwardConfig fast_forward_config = 2; // By default, executions started by another execution (e.g. a child workflow of a parent workflow or // a schedule with the timeskipping policy enabled), inherit the "enabled" flag and skip time when possible. @@ -457,6 +445,22 @@ message TimeSkippingConfig { int32 max_session_skip_count = 4; } +message FastForwardConfig { + // A client-supplied ID, required field, set alongside `duration`. It is used to poll for + // fast-forward completion via PollWorkflowExecutionTimeSkipping. + // The server performs no idempotency check on this ID; the client is responsible for managing it. + string id = 1; + + // Fast-forward the current execution by this duration ahead of the current workflow time; required field. + // Once the fast-forward completes, no further time is skipped. Time skipping can be resumed either + // by updating the TimeSkippingConfig with a new FastForwardConfig, or by clearing the FastForwardConfig + // to skip through to the end of the execution. + // + // If this duration exceeds the remaining execution timeout, time will not pass beyond the end + // of the execution, and the fast-forward won't have a chance to complete. + google.protobuf.Duration duration = 2; +} + // The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs // within the same execution. message TimeSkippingStatePropagation { From 803ddfe4ab0e24f394e54beacca2e0aee3ac8ffb Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Sat, 25 Jul 2026 18:47:37 -0700 Subject: [PATCH 15/15] refine comments of max_session_skip_count --- openapi/openapiv2.json | 2 +- openapi/openapiv3.yaml | 8 ++++---- temporal/api/common/v1/message.proto | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 2338f0ff6..ba085183d 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20041,7 +20041,7 @@ "maxSessionSkipCount": { "type": "integer", "format": "int32", - "description": "The maximum number of skips allowed within a single time-skipping session, where a session\nruns from when time skipping is enabled until it is disabled. It protects the execution from\nunlimited retries when backoff is skipped.\nEvery time the execution skips time, the skip count is incremented by one, and when it reaches\nmax_skip_per_session, time skipping is disabled.\nFor an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated\nacross all runs within the same session. The count resets to 0 at the start of each new session,\ni.e. each time this config is updated to re-enable time skipping.\n\nIf this field is not set, the server applies a large default value (e.g. 100). The default can\nbe changed through dynamic config, and is overridden by this field when set." + "description": "The maximum number of skips allowed every time this field is updated. It protects the execution from\nsituations like unlimited retries when backoff is skipped.\n\nEvery time the execution skips time, the skip count is incremented by one, and when it reaches\nmax_skip_per_session, time skipping stops.\nFor an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated\nacross all runs within the same session. The count resets to 0 at the start of each new session,\ni.e. each time this config is updated to re-enable time skipping.\n\nIf this field is not set, the server applies a large default value (e.g. 100). The default can\nbe changed through dynamic config, and is overridden by this field when set." } }, "description": "The configuration for time skipping of an execution.\nWhen time skipping is enabled, virtual time advances automatically whenever there is no in-flight work.\nOptions like fast_forward, disable_propagation, and max_skip_per_session are provided for granular\ncontrol of the execution's time skipping behavior. See each field's comment for a detailed explanation.\n\nAn example of workflows with time skipping:\nFor workflows, an execution is a chain of runs including retries, cron, and continue-as-new.\nIn-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc.\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.\nEvery time time is skipped, the skip count is incremented by one; max_skip_per_session bounds the number of skips allowed within a single time-skipping session.\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." diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index cc2b87bd4..6cda231ce 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17804,11 +17804,11 @@ components: maxSessionSkipCount: type: integer description: |- - The maximum number of skips allowed within a single time-skipping session, where a session - runs from when time skipping is enabled until it is disabled. It protects the execution from - unlimited retries when backoff is skipped. + The maximum number of skips allowed every time this field is updated. It protects the execution from + situations like unlimited retries when backoff is skipped. + Every time the execution skips time, the skip count is incremented by one, and when it reaches - max_skip_per_session, time skipping is disabled. + max_skip_per_session, time skipping stops. For an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated across all runs within the same session. The count resets to 0 at the start of each new session, i.e. each time this config is updated to re-enable time skipping. diff --git a/temporal/api/common/v1/message.proto b/temporal/api/common/v1/message.proto index 1cdc4386d..a6fd97d3e 100644 --- a/temporal/api/common/v1/message.proto +++ b/temporal/api/common/v1/message.proto @@ -431,11 +431,11 @@ message TimeSkippingConfig { // This flag disables that inheritance. bool disable_propagation = 3; - // The maximum number of skips allowed within a single time-skipping session, where a session - // runs from when time skipping is enabled until it is disabled. It protects the execution from - // unlimited retries when backoff is skipped. + // The maximum number of skips allowed every time this field is updated. It protects the execution from + // situations like unlimited retries when backoff is skipped. + // // Every time the execution skips time, the skip count is incremented by one, and when it reaches - // max_skip_per_session, time skipping is disabled. + // max_skip_per_session, time skipping stops. // For an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated // across all runs within the same session. The count resets to 0 at the start of each new session, // i.e. each time this config is updated to re-enable time skipping.