From 8451b876f3921169898d560ac4f94a86e9f1140f Mon Sep 17 00:00:00 2001 From: Feiyang Xie Date: Mon, 15 Jun 2026 18:18:32 -0700 Subject: [PATCH] TS: fast forward and propagation policy change (#10576) ## What changed and why 1) make server compatible to breaking api change https://github.com/temporalio/api/pull/786 2) change the propagation policy of time skipping to - for the same execution (a chain of runs) all time skipping state and config are shared using StatePropagation - for child executions virtual time is always propagated to have causal time but the fast forward action is never propagated configuration propagation is controlled by a separate flag 3) rename bound to fast-forward ## How did you test it? - [x] built - [ ] run locally and tested manually - [x] covered by existing tests - [x] added new unit test(s) - [x] added new functional test(s) ## Potential risks the sequence of this pr 1. review this pr first 2. merge api 3. update this pr with public api dependency and merge --- api/historyservice/v1/request_response.pb.go | 657 +++++++-------- .../v1/executions.go-helpers.pb.go | 20 +- api/persistence/v1/executions.pb.go | 66 +- go.mod | 2 +- go.sum | 4 +- proto/internal/buf.yaml | 7 +- .../historyservice/v1/request_response.proto | 10 +- .../api/persistence/v1/executions.proto | 17 +- service/frontend/workflow_handler.go | 12 +- service/frontend/workflow_handler_test.go | 36 +- .../history/api/updateworkflowoptions/api.go | 15 +- .../api/updateworkflowoptions/api_test.go | 73 +- .../history/historybuilder/event_factory.go | 26 +- .../history/historybuilder/history_builder.go | 8 +- .../historybuilder/history_builder_test.go | 6 +- service/history/interfaces/mutable_state.go | 4 +- .../history/interfaces/mutable_state_mock.go | 10 +- service/history/ndc/events_reapplier_test.go | 3 +- service/history/ndc/workflow_resetter_test.go | 2 +- .../history/tasks/timeskipping_timer_task.go | 10 +- .../timer_queue_active_task_executor.go | 24 +- .../timer_queue_active_task_executor_test.go | 49 +- .../timer_queue_standby_task_executor.go | 2 +- .../transfer_queue_active_task_executor.go | 2 +- .../history/workflow/mutable_state_impl.go | 191 ++--- .../workflow/mutable_state_impl_test.go | 749 ++++++++---------- .../workflow/mutable_state_rebuilder_test.go | 3 +- service/history/workflow/retry.go | 10 +- service/history/workflow/task_generator.go | 10 +- .../history/workflow/task_generator_test.go | 75 +- service/history/workflow/timeskipping.go | 62 ++ service/history/workflow/timeskipping_test.go | 154 ++++ service/history/workflow/update/store.go | 2 +- .../workflow/update/store_mock_test.go | 4 +- .../history/workflow/update/update_test.go | 12 +- ...t.go => timeskipping_fast_forward_test.go} | 173 ++-- tests/timeskipping_propagation_test.go | 392 ++++++--- tests/timeskipping_test.go | 74 +- 38 files changed, 1603 insertions(+), 1373 deletions(-) create mode 100644 service/history/workflow/timeskipping.go create mode 100644 service/history/workflow/timeskipping_test.go rename tests/{timeskipping_bound_test.go => timeskipping_fast_forward_test.go} (78%) diff --git a/api/historyservice/v1/request_response.pb.go b/api/historyservice/v1/request_response.pb.go index f332c01a9e..bff4f2c305 100644 --- a/api/historyservice/v1/request_response.pb.go +++ b/api/historyservice/v1/request_response.pb.go @@ -201,12 +201,9 @@ type StartWorkflowExecutionRequest struct { // directly from the started event. Written onto the new run's // WorkflowExecutionStartedEvent. DeclinedTargetVersionUpgrade *v17.DeclinedTargetVersionUpgrade `protobuf:"bytes,17,opt,name=declined_target_version_upgrade,json=declinedTargetVersionUpgrade,proto3" json:"declined_target_version_upgrade,omitempty"` - // If a workflow execution is started by a previous execution (parent-child workflow or continue-as-new) - // that has already skipped some time, the accumulated skipped duration from that execution - // can be passed to the new workflow execution through this field. - InitialSkippedDuration *durationpb.Duration `protobuf:"bytes,18,opt,name=initial_skipped_duration,json=initialSkippedDuration,proto3" json:"initial_skipped_duration,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + TimeSkippingStatePropagation *v14.TimeSkippingStatePropagation `protobuf:"bytes,19,opt,name=time_skipping_state_propagation,json=timeSkippingStatePropagation,proto3" json:"time_skipping_state_propagation,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *StartWorkflowExecutionRequest) Reset() { @@ -358,9 +355,9 @@ func (x *StartWorkflowExecutionRequest) GetDeclinedTargetVersionUpgrade() *v17.D return nil } -func (x *StartWorkflowExecutionRequest) GetInitialSkippedDuration() *durationpb.Duration { +func (x *StartWorkflowExecutionRequest) GetTimeSkippingStatePropagation() *v14.TimeSkippingStatePropagation { if x != nil { - return x.InitialSkippedDuration + return x.TimeSkippingStatePropagation } return nil } @@ -9844,8 +9841,11 @@ type UpdateWorkflowExecutionOptionsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Workflow Execution options after update. WorkflowExecutionOptions *v15.WorkflowExecutionOptions `protobuf:"bytes,1,opt,name=workflow_execution_options,json=workflowExecutionOptions,proto3" json:"workflow_execution_options,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Time of the workflow execution at the moment the options were applied. + // If time skipping happened in this execution, it will return virtual time. + UpdateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *UpdateWorkflowExecutionOptionsResponse) Reset() { @@ -9885,6 +9885,13 @@ func (x *UpdateWorkflowExecutionOptionsResponse) GetWorkflowExecutionOptions() * return nil } +func (x *UpdateWorkflowExecutionOptionsResponse) GetUpdateTime() *timestamppb.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + type PauseWorkflowExecutionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Namespace ID of the workflow which is being paused @@ -10588,7 +10595,7 @@ const file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc "task_token\x18\x06 \x01(\tR\ttaskToken\x12\x1d\n" + "\n" + "task_infos\x18\a \x01(\tR\ttaskInfos\x12.\n" + - "\x13chasm_component_ref\x18\b \x01(\tR\x11chasmComponentRef\"\xd6\f\n" + + "\x13chasm_component_ref\x18\b \x01(\tR\x11chasmComponentRef\"\x84\r\n" + "\x1dStartWorkflowExecutionRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12c\n" + "\rstart_request\x18\x02 \x01(\v2>.temporal.api.workflowservice.v1.StartWorkflowExecutionRequestR\fstartRequest\x12h\n" + @@ -10607,8 +10614,8 @@ const file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc "\x13child_workflow_only\x18\x0e \x01(\bR\x11childWorkflowOnly\x12m\n" + "\x18inherited_pinned_version\x18\x0f \x01(\v23.temporal.api.deployment.v1.WorkerDeploymentVersionR\x16inheritedPinnedVersion\x12s\n" + "\x1binherited_auto_upgrade_info\x18\x10 \x01(\v24.temporal.api.deployment.v1.InheritedAutoUpgradeInfoR\x18inheritedAutoUpgradeInfo\x12|\n" + - "\x1fdeclined_target_version_upgrade\x18\x11 \x01(\v25.temporal.api.history.v1.DeclinedTargetVersionUpgradeR\x1cdeclinedTargetVersionUpgrade\x12S\n" + - "\x18initial_skipped_duration\x18\x12 \x01(\v2\x19.google.protobuf.DurationR\x16initialSkippedDuration:\x1f\x92\xc4\x03\x1b*\x19start_request.workflow_id\"\xfc\x02\n" + + "\x1fdeclined_target_version_upgrade\x18\x11 \x01(\v25.temporal.api.history.v1.DeclinedTargetVersionUpgradeR\x1cdeclinedTargetVersionUpgrade\x12{\n" + + "\x1ftime_skipping_state_propagation\x18\x13 \x01(\v24.temporal.api.common.v1.TimeSkippingStatePropagationR\x1ctimeSkippingStatePropagation:\x1f\x92\xc4\x03\x1b*\x19start_request.workflow_idJ\x04\b\x12\x10\x13\"\xfc\x02\n" + "\x1eStartWorkflowExecutionResponse\x12\x15\n" + "\x06run_id\x18\x01 \x01(\tR\x05runId\x12?\n" + "\x05clock\x18\x02 \x01(\v2).temporal.server.api.clock.v1.VectorClockR\x05clock\x12n\n" + @@ -11318,9 +11325,11 @@ const file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc "\x15ResetActivityResponse\"\xee\x01\n" + "%UpdateWorkflowExecutionOptionsRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12m\n" + - "\x0eupdate_request\x18\x02 \x01(\v2F.temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequestR\rupdateRequest:3\x92\xc4\x03/*-update_request.workflow_execution.workflow_id\"\x9a\x01\n" + + "\x0eupdate_request\x18\x02 \x01(\v2F.temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequestR\rupdateRequest:3\x92\xc4\x03/*-update_request.workflow_execution.workflow_id\"\xd7\x01\n" + "&UpdateWorkflowExecutionOptionsResponse\x12p\n" + - "\x1aworkflow_execution_options\x18\x01 \x01(\v22.temporal.api.workflow.v1.WorkflowExecutionOptionsR\x18workflowExecutionOptions\"\xc8\x01\n" + + "\x1aworkflow_execution_options\x18\x01 \x01(\v22.temporal.api.workflow.v1.WorkflowExecutionOptionsR\x18workflowExecutionOptions\x12;\n" + + "\vupdate_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "updateTime\"\xc8\x01\n" + "\x1dPauseWorkflowExecutionRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12c\n" + "\rpause_request\x18\x02 \x01(\v2>.temporal.api.workflowservice.v1.PauseWorkflowExecutionRequestR\fpauseRequest:\x1f\x92\xc4\x03\x1b*\x19pause_request.workflow_id\" \n" + @@ -11543,108 +11552,109 @@ var file_temporal_server_api_historyservice_v1_request_response_proto_goTypes = (*v16.WorkerDeploymentVersion)(nil), // 181: temporal.api.deployment.v1.WorkerDeploymentVersion (*v16.InheritedAutoUpgradeInfo)(nil), // 182: temporal.api.deployment.v1.InheritedAutoUpgradeInfo (*v17.DeclinedTargetVersionUpgrade)(nil), // 183: temporal.api.history.v1.DeclinedTargetVersionUpgrade - (*v18.VectorClock)(nil), // 184: temporal.server.api.clock.v1.VectorClock - (*v1.PollWorkflowTaskQueueResponse)(nil), // 185: temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse - (v12.WorkflowExecutionStatus)(0), // 186: temporal.api.enums.v1.WorkflowExecutionStatus - (*v14.Link)(nil), // 187: temporal.api.common.v1.Link - (*v14.WorkflowExecution)(nil), // 188: temporal.api.common.v1.WorkflowExecution - (*v19.VersionHistoryItem)(nil), // 189: temporal.server.api.history.v1.VersionHistoryItem - (*v110.VersionedTransition)(nil), // 190: temporal.server.api.persistence.v1.VersionedTransition - (*v14.WorkflowType)(nil), // 191: temporal.api.common.v1.WorkflowType - (*v111.TaskQueue)(nil), // 192: temporal.api.taskqueue.v1.TaskQueue - (v112.WorkflowExecutionState)(0), // 193: temporal.server.api.enums.v1.WorkflowExecutionState - (*v19.VersionHistories)(nil), // 194: temporal.server.api.history.v1.VersionHistories - (*v15.WorkflowExecutionVersioningInfo)(nil), // 195: temporal.api.workflow.v1.WorkflowExecutionVersioningInfo - (*v19.TransientWorkflowTaskInfo)(nil), // 196: temporal.server.api.history.v1.TransientWorkflowTaskInfo - (*v1.PollWorkflowTaskQueueRequest)(nil), // 197: temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest - (*v113.BuildIdRedirectInfo)(nil), // 198: temporal.server.api.taskqueue.v1.BuildIdRedirectInfo - (*v16.Deployment)(nil), // 199: temporal.api.deployment.v1.Deployment - (*v113.TaskVersionDirective)(nil), // 200: temporal.server.api.taskqueue.v1.TaskVersionDirective - (*v115.Message)(nil), // 201: temporal.api.protocol.v1.Message - (*v17.History)(nil), // 202: temporal.api.history.v1.History - (*v1.PollActivityTaskQueueRequest)(nil), // 203: temporal.api.workflowservice.v1.PollActivityTaskQueueRequest - (*v17.HistoryEvent)(nil), // 204: temporal.api.history.v1.HistoryEvent - (*v14.Priority)(nil), // 205: temporal.api.common.v1.Priority - (*v14.RetryPolicy)(nil), // 206: temporal.api.common.v1.RetryPolicy - (*v1.RespondWorkflowTaskCompletedRequest)(nil), // 207: temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest - (*v1.PollActivityTaskQueueResponse)(nil), // 208: temporal.api.workflowservice.v1.PollActivityTaskQueueResponse - (*v1.RespondWorkflowTaskFailedRequest)(nil), // 209: temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest - (*v1.RecordActivityTaskHeartbeatRequest)(nil), // 210: temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest - (*v1.RespondActivityTaskCompletedRequest)(nil), // 211: temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest - (*v1.RespondActivityTaskFailedRequest)(nil), // 212: temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest - (*v1.RespondActivityTaskCanceledRequest)(nil), // 213: temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest - (*v1.SignalWorkflowExecutionRequest)(nil), // 214: temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest - (*v1.SignalWithStartWorkflowExecutionRequest)(nil), // 215: temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest - (*v1.TerminateWorkflowExecutionRequest)(nil), // 216: temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest - (*v1.ResetWorkflowExecutionRequest)(nil), // 217: temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest - (*v1.RequestCancelWorkflowExecutionRequest)(nil), // 218: temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest - (*v1.DescribeWorkflowExecutionRequest)(nil), // 219: temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest - (*v15.WorkflowExecutionConfig)(nil), // 220: temporal.api.workflow.v1.WorkflowExecutionConfig - (*v15.WorkflowExecutionInfo)(nil), // 221: temporal.api.workflow.v1.WorkflowExecutionInfo - (*v15.PendingActivityInfo)(nil), // 222: temporal.api.workflow.v1.PendingActivityInfo - (*v15.PendingChildExecutionInfo)(nil), // 223: temporal.api.workflow.v1.PendingChildExecutionInfo - (*v15.PendingWorkflowTaskInfo)(nil), // 224: temporal.api.workflow.v1.PendingWorkflowTaskInfo - (*v15.CallbackInfo)(nil), // 225: temporal.api.workflow.v1.CallbackInfo - (*v15.PendingNexusOperationInfo)(nil), // 226: temporal.api.workflow.v1.PendingNexusOperationInfo - (*v15.WorkflowExecutionExtendedInfo)(nil), // 227: temporal.api.workflow.v1.WorkflowExecutionExtendedInfo - (*v14.DataBlob)(nil), // 228: temporal.api.common.v1.DataBlob - (*v11.BaseExecutionInfo)(nil), // 229: temporal.server.api.workflow.v1.BaseExecutionInfo - (*v110.WorkflowMutableState)(nil), // 230: temporal.server.api.persistence.v1.WorkflowMutableState - (*v19.VersionHistory)(nil), // 231: temporal.server.api.history.v1.VersionHistory - (*v116.NamespaceCacheInfo)(nil), // 232: temporal.server.api.namespace.v1.NamespaceCacheInfo - (*v110.ShardInfo)(nil), // 233: temporal.server.api.persistence.v1.ShardInfo - (*v117.ReplicationToken)(nil), // 234: temporal.server.api.replication.v1.ReplicationToken - (*v117.ReplicationTaskInfo)(nil), // 235: temporal.server.api.replication.v1.ReplicationTaskInfo - (*v117.ReplicationTask)(nil), // 236: temporal.server.api.replication.v1.ReplicationTask - (*v1.QueryWorkflowRequest)(nil), // 237: temporal.api.workflowservice.v1.QueryWorkflowRequest - (*v1.QueryWorkflowResponse)(nil), // 238: temporal.api.workflowservice.v1.QueryWorkflowResponse - (*v118.ReapplyEventsRequest)(nil), // 239: temporal.server.api.adminservice.v1.ReapplyEventsRequest - (v112.DeadLetterQueueType)(0), // 240: temporal.server.api.enums.v1.DeadLetterQueueType - (*v118.RefreshWorkflowTasksRequest)(nil), // 241: temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest - (*v1.UpdateWorkflowExecutionRequest)(nil), // 242: temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest - (*v1.UpdateWorkflowExecutionResponse)(nil), // 243: temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse - (*v117.SyncReplicationState)(nil), // 244: temporal.server.api.replication.v1.SyncReplicationState - (*v117.WorkflowReplicationMessages)(nil), // 245: temporal.server.api.replication.v1.WorkflowReplicationMessages - (*v1.PollWorkflowExecutionUpdateRequest)(nil), // 246: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest - (*v1.PollWorkflowExecutionUpdateResponse)(nil), // 247: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse - (*v1.GetWorkflowExecutionHistoryRequest)(nil), // 248: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest - (*v1.GetWorkflowExecutionHistoryResponse)(nil), // 249: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - (*v1.GetWorkflowExecutionHistoryReverseRequest)(nil), // 250: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest - (*v1.GetWorkflowExecutionHistoryReverseResponse)(nil), // 251: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse - (*v118.GetWorkflowExecutionRawHistoryV2Request)(nil), // 252: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request - (*v118.GetWorkflowExecutionRawHistoryV2Response)(nil), // 253: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response - (*v118.GetWorkflowExecutionRawHistoryRequest)(nil), // 254: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest - (*v118.GetWorkflowExecutionRawHistoryResponse)(nil), // 255: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse - (*v118.DeleteWorkflowExecutionRequest)(nil), // 256: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest - (*v118.DeleteWorkflowExecutionResponse)(nil), // 257: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse - (*v119.HistoryDLQKey)(nil), // 258: temporal.server.api.common.v1.HistoryDLQKey - (*v119.HistoryDLQTask)(nil), // 259: temporal.server.api.common.v1.HistoryDLQTask - (*v119.HistoryDLQTaskMetadata)(nil), // 260: temporal.server.api.common.v1.HistoryDLQTaskMetadata - (*v118.ListHistoryTasksRequest)(nil), // 261: temporal.server.api.adminservice.v1.ListHistoryTasksRequest - (*v118.ListHistoryTasksResponse)(nil), // 262: temporal.server.api.adminservice.v1.ListHistoryTasksResponse - (*v120.NexusOperationCompletion)(nil), // 263: temporal.server.api.token.v1.NexusOperationCompletion - (*v14.Payload)(nil), // 264: temporal.api.common.v1.Payload - (*v121.Failure)(nil), // 265: temporal.api.nexus.v1.Failure - (*v110.StateMachineRef)(nil), // 266: temporal.server.api.persistence.v1.StateMachineRef - (v112.HealthState)(0), // 267: temporal.server.api.enums.v1.HealthState - (*v122.HealthCheck)(nil), // 268: temporal.server.api.health.v1.HealthCheck - (*v117.VersionedTransitionArtifact)(nil), // 269: temporal.server.api.replication.v1.VersionedTransitionArtifact - (*v1.UpdateActivityOptionsRequest)(nil), // 270: temporal.api.workflowservice.v1.UpdateActivityOptionsRequest - (*v123.ActivityOptions)(nil), // 271: temporal.api.activity.v1.ActivityOptions - (*v1.PauseActivityRequest)(nil), // 272: temporal.api.workflowservice.v1.PauseActivityRequest - (*v1.UnpauseActivityRequest)(nil), // 273: temporal.api.workflowservice.v1.UnpauseActivityRequest - (*v1.ResetActivityRequest)(nil), // 274: temporal.api.workflowservice.v1.ResetActivityRequest - (*v1.UpdateWorkflowExecutionOptionsRequest)(nil), // 275: temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest - (*v15.WorkflowExecutionOptions)(nil), // 276: temporal.api.workflow.v1.WorkflowExecutionOptions - (*v1.PauseWorkflowExecutionRequest)(nil), // 277: temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest - (*v1.UnpauseWorkflowExecutionRequest)(nil), // 278: temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest - (*v121.StartOperationRequest)(nil), // 279: temporal.api.nexus.v1.StartOperationRequest - (*v121.StartOperationResponse)(nil), // 280: temporal.api.nexus.v1.StartOperationResponse - (*v121.CancelOperationRequest)(nil), // 281: temporal.api.nexus.v1.CancelOperationRequest - (*v121.CancelOperationResponse)(nil), // 282: temporal.api.nexus.v1.CancelOperationResponse - (*v114.WorkflowQuery)(nil), // 283: temporal.api.query.v1.WorkflowQuery - (*v117.ReplicationMessages)(nil), // 284: temporal.server.api.replication.v1.ReplicationMessages - (*descriptorpb.MessageOptions)(nil), // 285: google.protobuf.MessageOptions + (*v14.TimeSkippingStatePropagation)(nil), // 184: temporal.api.common.v1.TimeSkippingStatePropagation + (*v18.VectorClock)(nil), // 185: temporal.server.api.clock.v1.VectorClock + (*v1.PollWorkflowTaskQueueResponse)(nil), // 186: temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse + (v12.WorkflowExecutionStatus)(0), // 187: temporal.api.enums.v1.WorkflowExecutionStatus + (*v14.Link)(nil), // 188: temporal.api.common.v1.Link + (*v14.WorkflowExecution)(nil), // 189: temporal.api.common.v1.WorkflowExecution + (*v19.VersionHistoryItem)(nil), // 190: temporal.server.api.history.v1.VersionHistoryItem + (*v110.VersionedTransition)(nil), // 191: temporal.server.api.persistence.v1.VersionedTransition + (*v14.WorkflowType)(nil), // 192: temporal.api.common.v1.WorkflowType + (*v111.TaskQueue)(nil), // 193: temporal.api.taskqueue.v1.TaskQueue + (v112.WorkflowExecutionState)(0), // 194: temporal.server.api.enums.v1.WorkflowExecutionState + (*v19.VersionHistories)(nil), // 195: temporal.server.api.history.v1.VersionHistories + (*v15.WorkflowExecutionVersioningInfo)(nil), // 196: temporal.api.workflow.v1.WorkflowExecutionVersioningInfo + (*v19.TransientWorkflowTaskInfo)(nil), // 197: temporal.server.api.history.v1.TransientWorkflowTaskInfo + (*v1.PollWorkflowTaskQueueRequest)(nil), // 198: temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest + (*v113.BuildIdRedirectInfo)(nil), // 199: temporal.server.api.taskqueue.v1.BuildIdRedirectInfo + (*v16.Deployment)(nil), // 200: temporal.api.deployment.v1.Deployment + (*v113.TaskVersionDirective)(nil), // 201: temporal.server.api.taskqueue.v1.TaskVersionDirective + (*v115.Message)(nil), // 202: temporal.api.protocol.v1.Message + (*v17.History)(nil), // 203: temporal.api.history.v1.History + (*v1.PollActivityTaskQueueRequest)(nil), // 204: temporal.api.workflowservice.v1.PollActivityTaskQueueRequest + (*v17.HistoryEvent)(nil), // 205: temporal.api.history.v1.HistoryEvent + (*v14.Priority)(nil), // 206: temporal.api.common.v1.Priority + (*v14.RetryPolicy)(nil), // 207: temporal.api.common.v1.RetryPolicy + (*v1.RespondWorkflowTaskCompletedRequest)(nil), // 208: temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest + (*v1.PollActivityTaskQueueResponse)(nil), // 209: temporal.api.workflowservice.v1.PollActivityTaskQueueResponse + (*v1.RespondWorkflowTaskFailedRequest)(nil), // 210: temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest + (*v1.RecordActivityTaskHeartbeatRequest)(nil), // 211: temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest + (*v1.RespondActivityTaskCompletedRequest)(nil), // 212: temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest + (*v1.RespondActivityTaskFailedRequest)(nil), // 213: temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest + (*v1.RespondActivityTaskCanceledRequest)(nil), // 214: temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest + (*v1.SignalWorkflowExecutionRequest)(nil), // 215: temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest + (*v1.SignalWithStartWorkflowExecutionRequest)(nil), // 216: temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest + (*v1.TerminateWorkflowExecutionRequest)(nil), // 217: temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest + (*v1.ResetWorkflowExecutionRequest)(nil), // 218: temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest + (*v1.RequestCancelWorkflowExecutionRequest)(nil), // 219: temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest + (*v1.DescribeWorkflowExecutionRequest)(nil), // 220: temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest + (*v15.WorkflowExecutionConfig)(nil), // 221: temporal.api.workflow.v1.WorkflowExecutionConfig + (*v15.WorkflowExecutionInfo)(nil), // 222: temporal.api.workflow.v1.WorkflowExecutionInfo + (*v15.PendingActivityInfo)(nil), // 223: temporal.api.workflow.v1.PendingActivityInfo + (*v15.PendingChildExecutionInfo)(nil), // 224: temporal.api.workflow.v1.PendingChildExecutionInfo + (*v15.PendingWorkflowTaskInfo)(nil), // 225: temporal.api.workflow.v1.PendingWorkflowTaskInfo + (*v15.CallbackInfo)(nil), // 226: temporal.api.workflow.v1.CallbackInfo + (*v15.PendingNexusOperationInfo)(nil), // 227: temporal.api.workflow.v1.PendingNexusOperationInfo + (*v15.WorkflowExecutionExtendedInfo)(nil), // 228: temporal.api.workflow.v1.WorkflowExecutionExtendedInfo + (*v14.DataBlob)(nil), // 229: temporal.api.common.v1.DataBlob + (*v11.BaseExecutionInfo)(nil), // 230: temporal.server.api.workflow.v1.BaseExecutionInfo + (*v110.WorkflowMutableState)(nil), // 231: temporal.server.api.persistence.v1.WorkflowMutableState + (*v19.VersionHistory)(nil), // 232: temporal.server.api.history.v1.VersionHistory + (*v116.NamespaceCacheInfo)(nil), // 233: temporal.server.api.namespace.v1.NamespaceCacheInfo + (*v110.ShardInfo)(nil), // 234: temporal.server.api.persistence.v1.ShardInfo + (*v117.ReplicationToken)(nil), // 235: temporal.server.api.replication.v1.ReplicationToken + (*v117.ReplicationTaskInfo)(nil), // 236: temporal.server.api.replication.v1.ReplicationTaskInfo + (*v117.ReplicationTask)(nil), // 237: temporal.server.api.replication.v1.ReplicationTask + (*v1.QueryWorkflowRequest)(nil), // 238: temporal.api.workflowservice.v1.QueryWorkflowRequest + (*v1.QueryWorkflowResponse)(nil), // 239: temporal.api.workflowservice.v1.QueryWorkflowResponse + (*v118.ReapplyEventsRequest)(nil), // 240: temporal.server.api.adminservice.v1.ReapplyEventsRequest + (v112.DeadLetterQueueType)(0), // 241: temporal.server.api.enums.v1.DeadLetterQueueType + (*v118.RefreshWorkflowTasksRequest)(nil), // 242: temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest + (*v1.UpdateWorkflowExecutionRequest)(nil), // 243: temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest + (*v1.UpdateWorkflowExecutionResponse)(nil), // 244: temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse + (*v117.SyncReplicationState)(nil), // 245: temporal.server.api.replication.v1.SyncReplicationState + (*v117.WorkflowReplicationMessages)(nil), // 246: temporal.server.api.replication.v1.WorkflowReplicationMessages + (*v1.PollWorkflowExecutionUpdateRequest)(nil), // 247: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest + (*v1.PollWorkflowExecutionUpdateResponse)(nil), // 248: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse + (*v1.GetWorkflowExecutionHistoryRequest)(nil), // 249: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest + (*v1.GetWorkflowExecutionHistoryResponse)(nil), // 250: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + (*v1.GetWorkflowExecutionHistoryReverseRequest)(nil), // 251: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest + (*v1.GetWorkflowExecutionHistoryReverseResponse)(nil), // 252: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse + (*v118.GetWorkflowExecutionRawHistoryV2Request)(nil), // 253: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request + (*v118.GetWorkflowExecutionRawHistoryV2Response)(nil), // 254: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response + (*v118.GetWorkflowExecutionRawHistoryRequest)(nil), // 255: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest + (*v118.GetWorkflowExecutionRawHistoryResponse)(nil), // 256: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse + (*v118.DeleteWorkflowExecutionRequest)(nil), // 257: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest + (*v118.DeleteWorkflowExecutionResponse)(nil), // 258: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse + (*v119.HistoryDLQKey)(nil), // 259: temporal.server.api.common.v1.HistoryDLQKey + (*v119.HistoryDLQTask)(nil), // 260: temporal.server.api.common.v1.HistoryDLQTask + (*v119.HistoryDLQTaskMetadata)(nil), // 261: temporal.server.api.common.v1.HistoryDLQTaskMetadata + (*v118.ListHistoryTasksRequest)(nil), // 262: temporal.server.api.adminservice.v1.ListHistoryTasksRequest + (*v118.ListHistoryTasksResponse)(nil), // 263: temporal.server.api.adminservice.v1.ListHistoryTasksResponse + (*v120.NexusOperationCompletion)(nil), // 264: temporal.server.api.token.v1.NexusOperationCompletion + (*v14.Payload)(nil), // 265: temporal.api.common.v1.Payload + (*v121.Failure)(nil), // 266: temporal.api.nexus.v1.Failure + (*v110.StateMachineRef)(nil), // 267: temporal.server.api.persistence.v1.StateMachineRef + (v112.HealthState)(0), // 268: temporal.server.api.enums.v1.HealthState + (*v122.HealthCheck)(nil), // 269: temporal.server.api.health.v1.HealthCheck + (*v117.VersionedTransitionArtifact)(nil), // 270: temporal.server.api.replication.v1.VersionedTransitionArtifact + (*v1.UpdateActivityOptionsRequest)(nil), // 271: temporal.api.workflowservice.v1.UpdateActivityOptionsRequest + (*v123.ActivityOptions)(nil), // 272: temporal.api.activity.v1.ActivityOptions + (*v1.PauseActivityRequest)(nil), // 273: temporal.api.workflowservice.v1.PauseActivityRequest + (*v1.UnpauseActivityRequest)(nil), // 274: temporal.api.workflowservice.v1.UnpauseActivityRequest + (*v1.ResetActivityRequest)(nil), // 275: temporal.api.workflowservice.v1.ResetActivityRequest + (*v1.UpdateWorkflowExecutionOptionsRequest)(nil), // 276: temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest + (*v15.WorkflowExecutionOptions)(nil), // 277: temporal.api.workflow.v1.WorkflowExecutionOptions + (*v1.PauseWorkflowExecutionRequest)(nil), // 278: temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest + (*v1.UnpauseWorkflowExecutionRequest)(nil), // 279: temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest + (*v121.StartOperationRequest)(nil), // 280: temporal.api.nexus.v1.StartOperationRequest + (*v121.StartOperationResponse)(nil), // 281: temporal.api.nexus.v1.StartOperationResponse + (*v121.CancelOperationRequest)(nil), // 282: temporal.api.nexus.v1.CancelOperationRequest + (*v121.CancelOperationResponse)(nil), // 283: temporal.api.nexus.v1.CancelOperationResponse + (*v114.WorkflowQuery)(nil), // 284: temporal.api.query.v1.WorkflowQuery + (*v117.ReplicationMessages)(nil), // 285: temporal.server.api.replication.v1.ReplicationMessages + (*descriptorpb.MessageOptions)(nil), // 286: google.protobuf.MessageOptions } var file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs = []int32{ 171, // 0: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.start_request:type_name -> temporal.api.workflowservice.v1.StartWorkflowExecutionRequest @@ -11660,139 +11670,139 @@ var file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs = 181, // 10: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.inherited_pinned_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion 182, // 11: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.inherited_auto_upgrade_info:type_name -> temporal.api.deployment.v1.InheritedAutoUpgradeInfo 183, // 12: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.declined_target_version_upgrade:type_name -> temporal.api.history.v1.DeclinedTargetVersionUpgrade - 177, // 13: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.initial_skipped_duration:type_name -> google.protobuf.Duration - 184, // 14: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 185, // 15: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.eager_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse - 186, // 16: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus - 187, // 17: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link - 188, // 18: temporal.server.api.historyservice.v1.GetMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 189, // 19: temporal.server.api.historyservice.v1.GetMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 190, // 20: temporal.server.api.historyservice.v1.GetMutableStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 188, // 21: temporal.server.api.historyservice.v1.GetMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 22: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 192, // 23: temporal.server.api.historyservice.v1.GetMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 192, // 24: temporal.server.api.historyservice.v1.GetMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 184, // 13: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.time_skipping_state_propagation:type_name -> temporal.api.common.v1.TimeSkippingStatePropagation + 185, // 14: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 186, // 15: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.eager_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse + 187, // 16: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus + 188, // 17: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link + 189, // 18: temporal.server.api.historyservice.v1.GetMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 190, // 19: temporal.server.api.historyservice.v1.GetMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 191, // 20: temporal.server.api.historyservice.v1.GetMutableStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 189, // 21: temporal.server.api.historyservice.v1.GetMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 192, // 22: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 193, // 23: temporal.server.api.historyservice.v1.GetMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 193, // 24: temporal.server.api.historyservice.v1.GetMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue 177, // 25: temporal.server.api.historyservice.v1.GetMutableStateResponse.sticky_task_queue_schedule_to_start_timeout:type_name -> google.protobuf.Duration - 193, // 26: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState - 186, // 27: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus - 194, // 28: temporal.server.api.historyservice.v1.GetMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 194, // 26: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState + 187, // 27: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus + 195, // 28: temporal.server.api.historyservice.v1.GetMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories 178, // 29: temporal.server.api.historyservice.v1.GetMutableStateResponse.most_recent_worker_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp - 190, // 30: temporal.server.api.historyservice.v1.GetMutableStateResponse.transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 195, // 31: temporal.server.api.historyservice.v1.GetMutableStateResponse.versioning_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionVersioningInfo - 196, // 32: temporal.server.api.historyservice.v1.GetMutableStateResponse.transient_or_speculative_tasks:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 188, // 33: temporal.server.api.historyservice.v1.PollMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 189, // 34: temporal.server.api.historyservice.v1.PollMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 188, // 35: temporal.server.api.historyservice.v1.PollMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 36: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 192, // 37: temporal.server.api.historyservice.v1.PollMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 192, // 38: temporal.server.api.historyservice.v1.PollMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 191, // 30: temporal.server.api.historyservice.v1.GetMutableStateResponse.transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 196, // 31: temporal.server.api.historyservice.v1.GetMutableStateResponse.versioning_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionVersioningInfo + 197, // 32: temporal.server.api.historyservice.v1.GetMutableStateResponse.transient_or_speculative_tasks:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 189, // 33: temporal.server.api.historyservice.v1.PollMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 190, // 34: temporal.server.api.historyservice.v1.PollMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 189, // 35: temporal.server.api.historyservice.v1.PollMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 192, // 36: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 193, // 37: temporal.server.api.historyservice.v1.PollMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 193, // 38: temporal.server.api.historyservice.v1.PollMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue 177, // 39: temporal.server.api.historyservice.v1.PollMutableStateResponse.sticky_task_queue_schedule_to_start_timeout:type_name -> google.protobuf.Duration - 194, // 40: temporal.server.api.historyservice.v1.PollMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 193, // 41: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState - 186, // 42: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus - 188, // 43: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 195, // 40: temporal.server.api.historyservice.v1.PollMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 194, // 41: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState + 187, // 42: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus + 189, // 43: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution 162, // 44: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.operations:type_name -> temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation 163, // 45: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.responses:type_name -> temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response - 188, // 46: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 197, // 47: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest - 184, // 48: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 198, // 49: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo - 199, // 50: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment - 200, // 51: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 189, // 46: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 198, // 47: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest + 185, // 48: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 199, // 49: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo + 200, // 50: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment + 201, // 51: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective 181, // 52: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.target_deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion - 191, // 53: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 196, // 54: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 192, // 55: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 192, // 53: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 197, // 54: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 193, // 55: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue 173, // 56: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.scheduled_time:type_name -> google.protobuf.Timestamp 173, // 57: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp 164, // 58: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry - 184, // 59: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 201, // 60: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.messages:type_name -> temporal.api.protocol.v1.Message - 202, // 61: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.history:type_name -> temporal.api.history.v1.History - 202, // 62: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.raw_history:type_name -> temporal.api.history.v1.History - 191, // 63: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 196, // 64: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 192, // 65: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 185, // 59: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 202, // 60: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.messages:type_name -> temporal.api.protocol.v1.Message + 203, // 61: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.history:type_name -> temporal.api.history.v1.History + 203, // 62: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.raw_history:type_name -> temporal.api.history.v1.History + 192, // 63: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 197, // 64: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 193, // 65: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue 173, // 66: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.scheduled_time:type_name -> google.protobuf.Timestamp 173, // 67: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.started_time:type_name -> google.protobuf.Timestamp 165, // 68: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry - 184, // 69: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 201, // 70: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message - 202, // 71: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History - 188, // 72: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 203, // 73: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest - 184, // 74: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 198, // 75: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo - 199, // 76: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment - 200, // 77: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective - 204, // 78: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.scheduled_event:type_name -> temporal.api.history.v1.HistoryEvent + 185, // 69: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 202, // 70: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message + 203, // 71: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History + 189, // 72: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 204, // 73: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest + 185, // 74: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 199, // 75: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo + 200, // 76: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment + 201, // 77: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 205, // 78: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.scheduled_event:type_name -> temporal.api.history.v1.HistoryEvent 173, // 79: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp 173, // 80: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.current_attempt_scheduled_time:type_name -> google.protobuf.Timestamp 176, // 81: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.heartbeat_details:type_name -> temporal.api.common.v1.Payloads - 191, // 82: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 184, // 83: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 205, // 84: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.priority:type_name -> temporal.api.common.v1.Priority - 206, // 85: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy - 207, // 86: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest + 192, // 82: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 185, // 83: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 206, // 84: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.priority:type_name -> temporal.api.common.v1.Priority + 207, // 85: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy + 208, // 86: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest 12, // 87: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.started_response:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse - 208, // 88: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.activity_tasks:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueResponse - 185, // 89: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.new_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse - 209, // 90: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest - 188, // 91: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 184, // 92: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 210, // 93: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest.heartbeat_request:type_name -> temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest - 211, // 94: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest - 212, // 95: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest - 213, // 96: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest - 188, // 97: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 184, // 98: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 214, // 99: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.signal_request:type_name -> temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest - 188, // 100: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 101: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link - 215, // 102: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest - 187, // 103: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse.signal_link:type_name -> temporal.api.common.v1.Link - 188, // 104: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 216, // 105: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest - 188, // 106: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 188, // 107: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 217, // 108: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest - 218, // 109: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest - 188, // 110: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 188, // 111: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 184, // 112: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 184, // 113: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 188, // 114: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 184, // 115: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 188, // 116: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 188, // 117: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 204, // 118: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent - 184, // 119: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 188, // 120: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 188, // 121: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 184, // 122: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 219, // 123: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest - 220, // 124: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig - 221, // 125: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo - 222, // 126: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo - 223, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo - 224, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo - 225, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo - 226, // 130: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo - 227, // 131: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo - 188, // 132: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 189, // 133: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 228, // 134: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob - 228, // 135: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob - 229, // 136: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 230, // 137: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 209, // 88: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.activity_tasks:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueResponse + 186, // 89: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.new_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse + 210, // 90: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest + 189, // 91: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 185, // 92: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 211, // 93: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest.heartbeat_request:type_name -> temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest + 212, // 94: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest + 213, // 95: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest + 214, // 96: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest + 189, // 97: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 185, // 98: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 215, // 99: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.signal_request:type_name -> temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest + 189, // 100: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 188, // 101: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link + 216, // 102: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest + 188, // 103: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse.signal_link:type_name -> temporal.api.common.v1.Link + 189, // 104: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 217, // 105: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest + 189, // 106: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 107: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 218, // 108: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest + 219, // 109: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest + 189, // 110: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 111: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 185, // 112: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 185, // 113: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 189, // 114: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 185, // 115: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 189, // 116: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 117: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 205, // 118: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent + 185, // 119: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 189, // 120: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 121: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 185, // 122: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 220, // 123: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest + 221, // 124: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig + 222, // 125: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo + 223, // 126: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo + 224, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo + 225, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo + 226, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo + 227, // 130: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo + 228, // 131: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo + 189, // 132: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 190, // 133: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 229, // 134: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob + 229, // 135: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob + 230, // 136: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 231, // 137: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState 173, // 138: temporal.server.api.historyservice.v1.SyncShardStatusRequest.status_time:type_name -> google.protobuf.Timestamp 173, // 139: temporal.server.api.historyservice.v1.SyncActivityRequest.scheduled_time:type_name -> google.protobuf.Timestamp 173, // 140: temporal.server.api.historyservice.v1.SyncActivityRequest.started_time:type_name -> google.protobuf.Timestamp 173, // 141: temporal.server.api.historyservice.v1.SyncActivityRequest.last_heartbeat_time:type_name -> google.protobuf.Timestamp 176, // 142: temporal.server.api.historyservice.v1.SyncActivityRequest.details:type_name -> temporal.api.common.v1.Payloads 175, // 143: temporal.server.api.historyservice.v1.SyncActivityRequest.last_failure:type_name -> temporal.api.failure.v1.Failure - 231, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 229, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 232, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 230, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo 173, // 146: temporal.server.api.historyservice.v1.SyncActivityRequest.first_scheduled_time:type_name -> google.protobuf.Timestamp 173, // 147: temporal.server.api.historyservice.v1.SyncActivityRequest.last_attempt_complete_time:type_name -> google.protobuf.Timestamp 177, // 148: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_initial_interval:type_name -> google.protobuf.Duration @@ -11803,121 +11813,122 @@ var file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs = 173, // 153: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_heartbeat_time:type_name -> google.protobuf.Timestamp 176, // 154: temporal.server.api.historyservice.v1.ActivitySyncInfo.details:type_name -> temporal.api.common.v1.Payloads 175, // 155: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_failure:type_name -> temporal.api.failure.v1.Failure - 231, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 232, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory 173, // 157: temporal.server.api.historyservice.v1.ActivitySyncInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp 173, // 158: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp 177, // 159: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_initial_interval:type_name -> google.protobuf.Duration 177, // 160: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_maximum_interval:type_name -> google.protobuf.Duration - 188, // 161: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 230, // 162: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 230, // 163: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 188, // 164: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 232, // 165: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo - 233, // 166: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo + 189, // 161: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 231, // 162: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 231, // 163: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 189, // 164: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 233, // 165: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo + 234, // 166: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo 173, // 167: temporal.server.api.historyservice.v1.RemoveTaskRequest.visibility_time:type_name -> google.protobuf.Timestamp - 234, // 168: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken + 235, // 168: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken 166, // 169: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.shard_messages:type_name -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry - 235, // 170: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 236, // 171: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 237, // 172: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest - 238, // 173: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse - 239, // 174: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest - 240, // 175: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 240, // 176: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 236, // 177: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 235, // 178: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 240, // 179: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 240, // 180: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 241, // 181: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest - 188, // 182: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 236, // 170: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 237, // 171: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 238, // 172: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest + 239, // 173: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse + 240, // 174: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest + 241, // 175: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 241, // 176: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 237, // 177: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 236, // 178: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 241, // 179: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 241, // 180: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 242, // 181: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest + 189, // 182: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution 96, // 183: temporal.server.api.historyservice.v1.GetReplicationStatusResponse.shards:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus 173, // 184: temporal.server.api.historyservice.v1.ShardReplicationStatus.shard_local_time:type_name -> google.protobuf.Timestamp 167, // 185: temporal.server.api.historyservice.v1.ShardReplicationStatus.remote_clusters:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry 168, // 186: temporal.server.api.historyservice.v1.ShardReplicationStatus.handover_namespaces:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry 173, // 187: temporal.server.api.historyservice.v1.ShardReplicationStatus.max_replication_task_visibility_time:type_name -> google.protobuf.Timestamp 173, // 188: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster.acked_task_visibility_time:type_name -> google.protobuf.Timestamp - 188, // 189: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 188, // 190: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 228, // 191: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob - 231, // 192: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 188, // 193: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 189: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 190: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 229, // 191: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob + 232, // 192: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 189, // 193: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution 173, // 194: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_start_time:type_name -> google.protobuf.Timestamp 173, // 195: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_close_time:type_name -> google.protobuf.Timestamp - 242, // 196: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest - 243, // 197: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse - 244, // 198: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState - 245, // 199: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages - 246, // 200: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest - 247, // 201: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse - 248, // 202: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest - 249, // 203: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 202, // 204: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History - 249, // 205: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 250, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest - 251, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse - 252, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request - 253, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response - 254, // 210: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest - 255, // 211: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse - 256, // 212: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest - 257, // 213: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse - 188, // 214: temporal.server.api.historyservice.v1.DeleteExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 258, // 215: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 259, // 216: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask - 258, // 217: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 260, // 218: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata + 243, // 196: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest + 244, // 197: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse + 245, // 198: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState + 246, // 199: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages + 247, // 200: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest + 248, // 201: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse + 249, // 202: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest + 250, // 203: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 203, // 204: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History + 250, // 205: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 251, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest + 252, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse + 253, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request + 254, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response + 255, // 210: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest + 256, // 211: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse + 257, // 212: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest + 258, // 213: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse + 189, // 214: temporal.server.api.historyservice.v1.DeleteExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 259, // 215: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 260, // 216: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask + 259, // 217: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 261, // 218: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata 169, // 219: temporal.server.api.historyservice.v1.ListQueuesResponse.queues:type_name -> temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo 170, // 220: temporal.server.api.historyservice.v1.AddTasksRequest.tasks:type_name -> temporal.server.api.historyservice.v1.AddTasksRequest.Task - 261, // 221: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest - 262, // 222: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse - 263, // 223: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 264, // 224: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload + 262, // 221: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest + 263, // 222: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse + 264, // 223: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 265, // 224: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload 175, // 225: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.failure:type_name -> temporal.api.failure.v1.Failure 173, // 226: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.close_time:type_name -> google.protobuf.Timestamp - 187, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link + 188, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link 173, // 228: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.start_time:type_name -> google.protobuf.Timestamp - 263, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 264, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload - 265, // 231: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure + 264, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 265, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload + 266, // 231: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure 173, // 232: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.start_time:type_name -> google.protobuf.Timestamp - 187, // 233: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link - 266, // 234: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef - 267, // 235: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState - 268, // 236: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck - 188, // 237: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 190, // 238: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 194, // 239: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 269, // 240: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact - 270, // 241: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest - 271, // 242: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions - 272, // 243: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest - 273, // 244: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest - 274, // 245: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest - 275, // 246: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest - 276, // 247: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions - 277, // 248: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest - 278, // 249: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest - 279, // 250: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest - 280, // 251: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse - 281, // 252: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest - 282, // 253: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse - 1, // 254: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest - 105, // 255: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest - 2, // 256: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse - 106, // 257: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse - 283, // 258: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 283, // 259: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 284, // 260: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages - 98, // 261: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster - 97, // 262: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo - 228, // 263: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob - 285, // 264: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions - 0, // 265: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions - 266, // [266:266] is the sub-list for method output_type - 266, // [266:266] is the sub-list for method input_type - 265, // [265:266] is the sub-list for extension type_name - 264, // [264:265] is the sub-list for extension extendee - 0, // [0:264] is the sub-list for field type_name + 188, // 233: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link + 267, // 234: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef + 268, // 235: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState + 269, // 236: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck + 189, // 237: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 191, // 238: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 195, // 239: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 270, // 240: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact + 271, // 241: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest + 272, // 242: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions + 273, // 243: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest + 274, // 244: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest + 275, // 245: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest + 276, // 246: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest + 277, // 247: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions + 173, // 248: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.update_time:type_name -> google.protobuf.Timestamp + 278, // 249: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest + 279, // 250: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest + 280, // 251: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest + 281, // 252: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse + 282, // 253: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest + 283, // 254: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse + 1, // 255: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest + 105, // 256: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest + 2, // 257: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse + 106, // 258: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse + 284, // 259: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 284, // 260: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 285, // 261: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages + 98, // 262: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster + 97, // 263: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo + 229, // 264: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob + 286, // 265: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions + 0, // 266: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions + 267, // [267:267] is the sub-list for method output_type + 267, // [267:267] is the sub-list for method input_type + 266, // [266:267] is the sub-list for extension type_name + 265, // [265:266] is the sub-list for extension extendee + 0, // [0:265] is the sub-list for field type_name } func init() { file_temporal_server_api_historyservice_v1_request_response_proto_init() } diff --git a/api/persistence/v1/executions.go-helpers.pb.go b/api/persistence/v1/executions.go-helpers.pb.go index ccd9ed4898..260fe434e0 100644 --- a/api/persistence/v1/executions.go-helpers.pb.go +++ b/api/persistence/v1/executions.go-helpers.pb.go @@ -116,35 +116,35 @@ func (this *TimeSkippingInfo) Equal(that interface{}) bool { return proto.Equal(this, that1) } -// Marshal an object of type TimeSkippingBoundInfo to the protobuf v3 wire format -func (val *TimeSkippingBoundInfo) Marshal() ([]byte, error) { +// Marshal an object of type FastForwardInfo to the protobuf v3 wire format +func (val *FastForwardInfo) Marshal() ([]byte, error) { return proto.Marshal(val) } -// Unmarshal an object of type TimeSkippingBoundInfo from the protobuf v3 wire format -func (val *TimeSkippingBoundInfo) Unmarshal(buf []byte) error { +// Unmarshal an object of type FastForwardInfo from the protobuf v3 wire format +func (val *FastForwardInfo) Unmarshal(buf []byte) error { return proto.Unmarshal(buf, val) } // Size returns the size of the object, in bytes, once serialized -func (val *TimeSkippingBoundInfo) Size() int { +func (val *FastForwardInfo) Size() int { return proto.Size(val) } -// Equal returns whether two TimeSkippingBoundInfo values are equivalent by recursively +// Equal returns whether two FastForwardInfo values are equivalent by recursively // comparing the message's fields. // For more information see the documentation for // https://pkg.go.dev/google.golang.org/protobuf/proto#Equal -func (this *TimeSkippingBoundInfo) Equal(that interface{}) bool { +func (this *FastForwardInfo) Equal(that interface{}) bool { if that == nil { return this == nil } - var that1 *TimeSkippingBoundInfo + var that1 *FastForwardInfo switch t := that.(type) { - case *TimeSkippingBoundInfo: + case *FastForwardInfo: that1 = t - case TimeSkippingBoundInfo: + case FastForwardInfo: that1 = &t default: return false diff --git a/api/persistence/v1/executions.pb.go b/api/persistence/v1/executions.pb.go index 30760c7ce5..d42bdb3ce8 100644 --- a/api/persistence/v1/executions.pb.go +++ b/api/persistence/v1/executions.pb.go @@ -1168,13 +1168,13 @@ func (*WorkflowExecutionInfo_LastWorkflowTaskTimedOutType) isWorkflowExecutionIn type TimeSkippingInfo struct { state protoimpl.MessageState `protogen:"open.v1"` // Current time-skipping configuration applied to the workflow. - Config *v12.TimeSkippingConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + Config *v13.TimeSkippingConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` // Total skipped duration for the current workflow execution run, including any AccumulatedSkippedDuration *durationpb.Duration `protobuf:"bytes,2,opt,name=accumulated_skipped_duration,json=accumulatedSkippedDuration,proto3" json:"accumulated_skipped_duration,omitempty"` - // The current bound based on elapsed duration for time skipping. - CurrentElapsedDurationBound *TimeSkippingBoundInfo `protobuf:"bytes,3,opt,name=current_elapsed_duration_bound,json=currentElapsedDurationBound,proto3" json:"current_elapsed_duration_bound,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // The current fast-forward info for time skipping. + FastForwardInfo *FastForwardInfo `protobuf:"bytes,4,opt,name=fast_forward_info,json=fastForwardInfo,proto3" json:"fast_forward_info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *TimeSkippingInfo) Reset() { @@ -1207,7 +1207,7 @@ func (*TimeSkippingInfo) Descriptor() ([]byte, []int) { return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{2} } -func (x *TimeSkippingInfo) GetConfig() *v12.TimeSkippingConfig { +func (x *TimeSkippingInfo) GetConfig() *v13.TimeSkippingConfig { if x != nil { return x.Config } @@ -1221,43 +1221,43 @@ func (x *TimeSkippingInfo) GetAccumulatedSkippedDuration() *durationpb.Duration return nil } -func (x *TimeSkippingInfo) GetCurrentElapsedDurationBound() *TimeSkippingBoundInfo { +func (x *TimeSkippingInfo) GetFastForwardInfo() *FastForwardInfo { if x != nil { - return x.CurrentElapsedDurationBound + return x.FastForwardInfo } return nil } -type TimeSkippingBoundInfo struct { +type FastForwardInfo struct { state protoimpl.MessageState `protogen:"open.v1"` - // Target time for a bound, expressed in virtual time. + // Target time for the fast-forward, expressed in virtual time. TargetTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=target_time,json=targetTime,proto3" json:"target_time,omitempty"` - // Indicates whether this bound has already been reached, used for idempotency checks. + // Indicates whether this fast-forward has already been reached, used for idempotency checks. HasReached bool `protobuf:"varint,2,opt,name=has_reached,json=hasReached,proto3" json:"has_reached,omitempty"` // Event ID of the WorkflowExecutionStartedEvent (always 1) or the most recent // WorkflowExecutionOptionsUpdatedEvent that introduced the current time-skipping // configuration. This is used as the task event ID for the time-skipping timer task, - // enabling reset and replication-conflict resolution to identify obsolete bound tasks + // enabling reset and replication-conflict resolution to identify obsolete fast-forward tasks // via the standard staleness check. SourceEventId int64 `protobuf:"varint,3,opt,name=source_event_id,json=sourceEventId,proto3" json:"source_event_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *TimeSkippingBoundInfo) Reset() { - *x = TimeSkippingBoundInfo{} +func (x *FastForwardInfo) Reset() { + *x = FastForwardInfo{} mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *TimeSkippingBoundInfo) String() string { +func (x *FastForwardInfo) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TimeSkippingBoundInfo) ProtoMessage() {} +func (*FastForwardInfo) ProtoMessage() {} -func (x *TimeSkippingBoundInfo) ProtoReflect() protoreflect.Message { +func (x *FastForwardInfo) ProtoReflect() protoreflect.Message { mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1269,26 +1269,26 @@ func (x *TimeSkippingBoundInfo) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TimeSkippingBoundInfo.ProtoReflect.Descriptor instead. -func (*TimeSkippingBoundInfo) Descriptor() ([]byte, []int) { +// Deprecated: Use FastForwardInfo.ProtoReflect.Descriptor instead. +func (*FastForwardInfo) Descriptor() ([]byte, []int) { return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{3} } -func (x *TimeSkippingBoundInfo) GetTargetTime() *timestamppb.Timestamp { +func (x *FastForwardInfo) GetTargetTime() *timestamppb.Timestamp { if x != nil { return x.TargetTime } return nil } -func (x *TimeSkippingBoundInfo) GetHasReached() bool { +func (x *FastForwardInfo) GetHasReached() bool { if x != nil { return x.HasReached } return false } -func (x *TimeSkippingBoundInfo) GetSourceEventId() int64 { +func (x *FastForwardInfo) GetSourceEventId() int64 { if x != nil { return x.SourceEventId } @@ -5040,12 +5040,12 @@ const file_temporal_server_api_persistence_v1_executions_proto_rawDesc = "" + "&ChildrenInitializedPostResetPointEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12H\n" + "\x05value\x18\x02 \x01(\v22.temporal.server.api.persistence.v1.ResetChildInfoR\x05value:\x028\x01B\x1c\n" + - "\x1alast_workflow_task_failureJ\x04\b\b\x10\tJ\x04\b\x0e\x10\x0fJ\x04\b\x0f\x10\x10J\x04\b\x10\x10\x11J\x04\bp\x10qJ\x04\b,\x10-J\x04\b-\x10.J\x04\b/\x100J\x04\b0\x101J\x04\b1\x102J\x04\b2\x103\"\xb5\x02\n" + - "\x10TimeSkippingInfo\x12D\n" + - "\x06config\x18\x01 \x01(\v2,.temporal.api.workflow.v1.TimeSkippingConfigR\x06config\x12[\n" + - "\x1caccumulated_skipped_duration\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\x1aaccumulatedSkippedDuration\x12~\n" + - "\x1ecurrent_elapsed_duration_bound\x18\x03 \x01(\v29.temporal.server.api.persistence.v1.TimeSkippingBoundInfoR\x1bcurrentElapsedDurationBound\"\x9d\x01\n" + - "\x15TimeSkippingBoundInfo\x12;\n" + + "\x1alast_workflow_task_failureJ\x04\b\b\x10\tJ\x04\b\x0e\x10\x0fJ\x04\b\x0f\x10\x10J\x04\b\x10\x10\x11J\x04\bp\x10qJ\x04\b,\x10-J\x04\b-\x10.J\x04\b/\x100J\x04\b0\x101J\x04\b1\x102J\x04\b2\x103\"\xba\x02\n" + + "\x10TimeSkippingInfo\x12B\n" + + "\x06config\x18\x01 \x01(\v2*.temporal.api.common.v1.TimeSkippingConfigR\x06config\x12[\n" + + "\x1caccumulated_skipped_duration\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\x1aaccumulatedSkippedDuration\x12_\n" + + "\x11fast_forward_info\x18\x04 \x01(\v23.temporal.server.api.persistence.v1.FastForwardInfoR\x0ffastForwardInfoJ\x04\b\x03\x10\x04R\x1ecurrent_elapsed_duration_bound\"\x97\x01\n" + + "\x0fFastForwardInfo\x12;\n" + "\vtarget_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "targetTime\x12\x1f\n" + "\vhas_reached\x18\x02 \x01(\bR\n" + @@ -5402,7 +5402,7 @@ var file_temporal_server_api_persistence_v1_executions_proto_goTypes = []any{ (*ShardInfo)(nil), // 0: temporal.server.api.persistence.v1.ShardInfo (*WorkflowExecutionInfo)(nil), // 1: temporal.server.api.persistence.v1.WorkflowExecutionInfo (*TimeSkippingInfo)(nil), // 2: temporal.server.api.persistence.v1.TimeSkippingInfo - (*TimeSkippingBoundInfo)(nil), // 3: temporal.server.api.persistence.v1.TimeSkippingBoundInfo + (*FastForwardInfo)(nil), // 3: temporal.server.api.persistence.v1.FastForwardInfo (*LastNotifiedTargetVersion)(nil), // 4: temporal.server.api.persistence.v1.LastNotifiedTargetVersion (*ExecutionStats)(nil), // 5: temporal.server.api.persistence.v1.ExecutionStats (*WorkflowExecutionState)(nil), // 6: temporal.server.api.persistence.v1.WorkflowExecutionState @@ -5463,7 +5463,7 @@ var file_temporal_server_api_persistence_v1_executions_proto_goTypes = []any{ (v11.WorkflowTaskFailedCause)(0), // 61: temporal.api.enums.v1.WorkflowTaskFailedCause (v11.TimeoutType)(0), // 62: temporal.api.enums.v1.TimeoutType (*v17.DeclinedTargetVersionUpgrade)(nil), // 63: temporal.api.history.v1.DeclinedTargetVersionUpgrade - (*v12.TimeSkippingConfig)(nil), // 64: temporal.api.workflow.v1.TimeSkippingConfig + (*v13.TimeSkippingConfig)(nil), // 64: temporal.api.common.v1.TimeSkippingConfig (*v18.WorkerDeploymentVersion)(nil), // 65: temporal.api.deployment.v1.WorkerDeploymentVersion (v1.WorkflowExecutionState)(0), // 66: temporal.server.api.enums.v1.WorkflowExecutionState (v11.WorkflowExecutionStatus)(0), // 67: temporal.api.enums.v1.WorkflowExecutionStatus @@ -5541,10 +5541,10 @@ var file_temporal_server_api_persistence_v1_executions_proto_depIdxs = []int32{ 4, // 45: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_notified_target_version:type_name -> temporal.server.api.persistence.v1.LastNotifiedTargetVersion 63, // 46: temporal.server.api.persistence.v1.WorkflowExecutionInfo.declined_target_version_upgrade:type_name -> temporal.api.history.v1.DeclinedTargetVersionUpgrade 2, // 47: temporal.server.api.persistence.v1.WorkflowExecutionInfo.time_skipping_info:type_name -> temporal.server.api.persistence.v1.TimeSkippingInfo - 64, // 48: temporal.server.api.persistence.v1.TimeSkippingInfo.config:type_name -> temporal.api.workflow.v1.TimeSkippingConfig + 64, // 48: temporal.server.api.persistence.v1.TimeSkippingInfo.config:type_name -> temporal.api.common.v1.TimeSkippingConfig 48, // 49: temporal.server.api.persistence.v1.TimeSkippingInfo.accumulated_skipped_duration:type_name -> google.protobuf.Duration - 3, // 50: temporal.server.api.persistence.v1.TimeSkippingInfo.current_elapsed_duration_bound:type_name -> temporal.server.api.persistence.v1.TimeSkippingBoundInfo - 47, // 51: temporal.server.api.persistence.v1.TimeSkippingBoundInfo.target_time:type_name -> google.protobuf.Timestamp + 3, // 50: temporal.server.api.persistence.v1.TimeSkippingInfo.fast_forward_info:type_name -> temporal.server.api.persistence.v1.FastForwardInfo + 47, // 51: temporal.server.api.persistence.v1.FastForwardInfo.target_time:type_name -> google.protobuf.Timestamp 65, // 52: temporal.server.api.persistence.v1.LastNotifiedTargetVersion.deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion 66, // 53: temporal.server.api.persistence.v1.WorkflowExecutionState.state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState 67, // 54: temporal.server.api.persistence.v1.WorkflowExecutionState.status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus diff --git a/go.mod b/go.mod index 21f6d69fda..8c3ae7e2fa 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( go.opentelemetry.io/otel/sdk v1.43.0 go.opentelemetry.io/otel/sdk/metric v1.43.0 go.opentelemetry.io/otel/trace v1.43.0 - go.temporal.io/api v1.62.14-0.20260610204950-15ceffd37b1b + go.temporal.io/api v1.62.15-0.20260615235047-378792ab2240 go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 go.temporal.io/sdk v1.41.1 go.uber.org/fx v1.24.0 diff --git a/go.sum b/go.sum index 318c180054..fdb33780b7 100644 --- a/go.sum +++ b/go.sum @@ -471,8 +471,8 @@ go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0 h1:R go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0/go.mod h1:I89cynRj8y+383o7tEQVg2SVA6SRgDVIouWPUVXjx0U= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0 h1:CQvJSldHRUN6Z8jsUeYv8J0lXRvygALXIzsmAeCcZE0= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0/go.mod h1:xSQ+mEfJe/GjK1LXEyVOoSI1N9JV9ZI923X5kup43W4= -go.temporal.io/api v1.62.14-0.20260610204950-15ceffd37b1b h1:cm1d0DOh5VXiEZC0QEjxuy7ZtK//4H+K+bYpV7B2gV4= -go.temporal.io/api v1.62.14-0.20260610204950-15ceffd37b1b/go.mod h1:0k75tRljEuELWGeXjEZZO7zYqBln4+1FrG6+IMOMy7Q= +go.temporal.io/api v1.62.15-0.20260615235047-378792ab2240 h1:Up/CNfkScGxN1TdrGZ3ez+0k6MIIhuhlbBgdZnrPhm0= +go.temporal.io/api v1.62.15-0.20260615235047-378792ab2240/go.mod h1:0k75tRljEuELWGeXjEZZO7zYqBln4+1FrG6+IMOMy7Q= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 h1:1hKeH3GyR6YD6LKMHGCZ76t6h1Sgha0hXVQBxWi3dlQ= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2/go.mod h1:T8dnzVPeO+gaUTj9eDgm/lT2lZH4+JXNvrGaQGyVi50= go.temporal.io/sdk v1.41.1 h1:yOpvsHyDD1lNuwlGBv/SUodCPhjv9nDeC9lLHW/fJUA= diff --git a/proto/internal/buf.yaml b/proto/internal/buf.yaml index 0625f0cffa..86c527801e 100644 --- a/proto/internal/buf.yaml +++ b/proto/internal/buf.yaml @@ -9,9 +9,10 @@ deps: breaking: use: - WIRE - # Uncomment this to temporarily ignore specific files or directories: - # ignore: - # example: - temporal/server/api/.../message.proto + ignore: + # temporarily added to accomodate time skipping api change + # will add back instantly + - temporal/server/api/persistence/v1/executions.proto lint: use: - DEFAULT diff --git a/proto/internal/temporal/server/api/historyservice/v1/request_response.proto b/proto/internal/temporal/server/api/historyservice/v1/request_response.proto index ba91e5a008..b1fa8ce100 100644 --- a/proto/internal/temporal/server/api/historyservice/v1/request_response.proto +++ b/proto/internal/temporal/server/api/historyservice/v1/request_response.proto @@ -104,11 +104,8 @@ message StartWorkflowExecutionRequest { // directly from the started event. Written onto the new run's // WorkflowExecutionStartedEvent. temporal.api.history.v1.DeclinedTargetVersionUpgrade declined_target_version_upgrade = 17; - - // If a workflow execution is started by a previous execution (parent-child workflow or continue-as-new) - // that has already skipped some time, the accumulated skipped duration from that execution - // can be passed to the new workflow execution through this field. - google.protobuf.Duration initial_skipped_duration = 18; + reserved 18; + temporal.api.common.v1.TimeSkippingStatePropagation time_skipping_state_propagation = 19; } message StartWorkflowExecutionResponse { @@ -1384,6 +1381,9 @@ message UpdateWorkflowExecutionOptionsRequest { message UpdateWorkflowExecutionOptionsResponse { // Workflow Execution options after update. temporal.api.workflow.v1.WorkflowExecutionOptions workflow_execution_options = 1; + // Time of the workflow execution at the moment the options were applied. + // If time skipping happened in this execution, it will return virtual time. + google.protobuf.Timestamp update_time = 2; } message PauseWorkflowExecutionRequest { diff --git a/proto/internal/temporal/server/api/persistence/v1/executions.proto b/proto/internal/temporal/server/api/persistence/v1/executions.proto index 3942d3261a..a729bafa62 100644 --- a/proto/internal/temporal/server/api/persistence/v1/executions.proto +++ b/proto/internal/temporal/server/api/persistence/v1/executions.proto @@ -321,27 +321,30 @@ message WorkflowExecutionInfo { } message TimeSkippingInfo { + reserved 3; + reserved "current_elapsed_duration_bound"; + // Current time-skipping configuration applied to the workflow. - temporal.api.workflow.v1.TimeSkippingConfig config = 1; + temporal.api.common.v1.TimeSkippingConfig config = 1; // Total skipped duration for the current workflow execution run, including any ./* inherited skipped duration carried over from a preceding execution that started this run. */google.protobuf.Duration accumulated_skipped_duration = 2; - // The current bound based on elapsed duration for time skipping. - TimeSkippingBoundInfo current_elapsed_duration_bound = 3; + // The current fast-forward info for time skipping. + FastForwardInfo fast_forward_info = 4; } -message TimeSkippingBoundInfo { - // Target time for a bound, expressed in virtual time. +message FastForwardInfo { + // Target time for the fast-forward, expressed in virtual time. google.protobuf.Timestamp target_time = 1; - // Indicates whether this bound has already been reached, used for idempotency checks. + // Indicates whether this fast-forward has already been reached, used for idempotency checks. bool has_reached = 2; // Event ID of the WorkflowExecutionStartedEvent (always 1) or the most recent // WorkflowExecutionOptionsUpdatedEvent that introduced the current time-skipping // configuration. This is used as the task event ID for the time-skipping timer task, - // enabling reset and replication-conflict resolution to identify obsolete bound tasks + // enabling reset and replication-conflict resolution to identify obsolete fast-forward tasks // via the standard staleness check. int64 source_event_id = 3; } diff --git a/service/frontend/workflow_handler.go b/service/frontend/workflow_handler.go index 728814c0b4..a693919431 100644 --- a/service/frontend/workflow_handler.go +++ b/service/frontend/workflow_handler.go @@ -700,7 +700,7 @@ func (wh *WorkflowHandler) prepareStartWorkflowRequest( } func (wh *WorkflowHandler) validateTimeSkippingConfig( - tsc *workflowpb.TimeSkippingConfig, + tsc *commonpb.TimeSkippingConfig, ns namespace.Name, ) error { if tsc == nil { @@ -715,15 +715,13 @@ func (wh *WorkflowHandler) validateTimeSkippingConfig( } if !tsc.GetEnabled() { - if tsc.GetBound() != nil { - return serviceerror.NewInvalidArgument("time_skipping_config: cannot set bound when enabled is false") + if tsc.GetFastForward() != nil { + return serviceerror.NewInvalidArgument("time_skipping_config: cannot set fast_forward when enabled is false") } return nil } - if b, ok := tsc.GetBound().(*workflowpb.TimeSkippingConfig_MaxElapsedDuration); ok { - if b.MaxElapsedDuration.AsDuration() < 0 { - return serviceerror.NewInvalidArgument("time_skipping_config: max_elapsed_duration must be positive") - } + if ff := tsc.GetFastForward(); ff != nil && ff.AsDuration() < 0 { + return serviceerror.NewInvalidArgument("time_skipping_config: fast_forward must be positive") } return nil diff --git a/service/frontend/workflow_handler_test.go b/service/frontend/workflow_handler_test.go index ae7f705cb7..01d7be33b3 100644 --- a/service/frontend/workflow_handler_test.go +++ b/service/frontend/workflow_handler_test.go @@ -3426,23 +3426,23 @@ func (s *WorkflowHandlerSuite) TestValidateTimeSkippingConfig() { // config with enabled=false but dynamic config disabled returns error config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) - s.Require().ErrorAs(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{Enabled: false}, s.testNamespace), &unimplementedErr) + s.Require().ErrorAs(wh.validateTimeSkippingConfig(&commonpb.TimeSkippingConfig{Enabled: false}, s.testNamespace), &unimplementedErr) // config with enabled=true but dynamic config disabled returns error - s.Require().ErrorAs(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{Enabled: true}, s.testNamespace), &unimplementedErr) + s.Require().ErrorAs(wh.validateTimeSkippingConfig(&commonpb.TimeSkippingConfig{Enabled: true}, s.testNamespace), &unimplementedErr) // config with enabled=false and dynamic config enabled is valid config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) - s.Require().NoError(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{Enabled: false}, s.testNamespace)) + s.Require().NoError(wh.validateTimeSkippingConfig(&commonpb.TimeSkippingConfig{Enabled: false}, s.testNamespace)) // config with enabled=true and dynamic config enabled is valid - s.Require().NoError(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{Enabled: true}, s.testNamespace)) + s.Require().NoError(wh.validateTimeSkippingConfig(&commonpb.TimeSkippingConfig{Enabled: true}, s.testNamespace)) - s.Require().ErrorAs(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{ - Enabled: false, Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Second * 10)}}, s.testNamespace), &invalidArgumentErr) + s.Require().ErrorAs(wh.validateTimeSkippingConfig(&commonpb.TimeSkippingConfig{ + Enabled: false, FastForward: durationpb.New(time.Second * 10)}, s.testNamespace), &invalidArgumentErr) - s.Require().ErrorAs(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{ - Enabled: true, Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Second * -10)}}, s.testNamespace), &invalidArgumentErr) + s.Require().ErrorAs(wh.validateTimeSkippingConfig(&commonpb.TimeSkippingConfig{ + Enabled: true, FastForward: durationpb.New(time.Second * -10)}, s.testNamespace), &invalidArgumentErr) } // TestExecuteMultiOperation_TimeSkipping_DCDisabled verifies that when the DC gate is off, @@ -3467,7 +3467,7 @@ func (s *WorkflowHandlerSuite) TestExecuteMultiOperation_TimeSkipping_DCDisabled WorkflowId: "WORKFLOW_ID", WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, }, }, @@ -3530,7 +3530,7 @@ func (s *WorkflowHandlerSuite) TestExecuteMultiOperation_TimeSkipping_DCEnabled( WorkflowId: "WORKFLOW_ID", WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, }, }, @@ -3564,7 +3564,7 @@ func (s *WorkflowHandlerSuite) TestStartWorkflowExecution_TimeSkipping_DCDisable WorkflowId: "WORKFLOW_ID", WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, } _, err := wh.StartWorkflowExecution(context.Background(), req) @@ -3591,7 +3591,7 @@ func (s *WorkflowHandlerSuite) TestStartWorkflowExecution_TimeSkipping_DCEnabled WorkflowId: "WORKFLOW_ID", WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, } resp, err := wh.StartWorkflowExecution(context.Background(), req) @@ -3613,7 +3613,7 @@ func (s *WorkflowHandlerSuite) TestSignalWithStartWorkflowExecution_TimeSkipping WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, SignalName: "signal-name", - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, } _, err := wh.SignalWithStartWorkflowExecution(context.Background(), req) @@ -3641,7 +3641,7 @@ func (s *WorkflowHandlerSuite) TestSignalWithStartWorkflowExecution_TimeSkipping WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, SignalName: "signal-name", - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, } resp, err := wh.SignalWithStartWorkflowExecution(context.Background(), req) @@ -3668,7 +3668,7 @@ func (s *WorkflowHandlerSuite) TestResetWorkflowExecution_TimeSkipping_DCDisable Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, }, @@ -3706,7 +3706,7 @@ func (s *WorkflowHandlerSuite) TestStartBatchOperation_ResetOperation_TimeSkippi Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, }, @@ -3739,7 +3739,7 @@ func (s *WorkflowHandlerSuite) TestStartBatchOperation_UpdateWorkflowOptionsOper Operation: &workflowservice.StartBatchOperationRequest_UpdateWorkflowOptionsOperation{ UpdateWorkflowOptionsOperation: &batchpb.BatchOperationUpdateWorkflowExecutionOptions{ WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, }, @@ -4806,7 +4806,7 @@ func (s *WorkflowHandlerSuite) TestUpdateWorkflowExecutionOptions_TimeSkipping_D RunId: "run-id", }, WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, }) diff --git a/service/history/api/updateworkflowoptions/api.go b/service/history/api/updateworkflowoptions/api.go index 2c6a6f21dc..84f0f9d29d 100644 --- a/service/history/api/updateworkflowoptions/api.go +++ b/service/history/api/updateworkflowoptions/api.go @@ -19,6 +19,7 @@ import ( "go.temporal.io/server/service/history/workflow" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/fieldmaskpb" + "google.golang.org/protobuf/types/known/timestamppb" ) func Invoke( @@ -99,6 +100,8 @@ func Invoke( }, nil } + ret.UpdateTime = timestamppb.New(mutableState.Now()) + // Store versioning override to send reactivation signal after successful persistence versioningOverrideForReactivation = mergedOpts.GetVersioningOverride() @@ -173,7 +176,7 @@ func getOptionsFromMutableState(ms historyi.MutableState) *workflowpb.WorkflowEx } } if tsInfo := ms.GetExecutionInfo().GetTimeSkippingInfo(); tsInfo != nil { - if cloned, ok := proto.Clone(tsInfo.GetConfig()).(*workflowpb.TimeSkippingConfig); ok { + if cloned, ok := proto.Clone(tsInfo.GetConfig()).(*commonpb.TimeSkippingConfig); ok { opts.TimeSkippingConfig = cloned } } @@ -245,18 +248,16 @@ func mergeWorkflowExecutionOptions( if _, ok := updateFields["timeSkippingConfig.enabled"]; ok { if mergeInto.TimeSkippingConfig == nil { - mergeInto.TimeSkippingConfig = &workflowpb.TimeSkippingConfig{} + mergeInto.TimeSkippingConfig = &commonpb.TimeSkippingConfig{} } mergeInto.TimeSkippingConfig.Enabled = mergeFrom.GetTimeSkippingConfig().GetEnabled() } - if _, ok := updateFields["timeSkippingConfig.maxElapsedDuration"]; ok { + if _, ok := updateFields["timeSkippingConfig.fastForward"]; ok { if mergeInto.TimeSkippingConfig == nil { - mergeInto.TimeSkippingConfig = &workflowpb.TimeSkippingConfig{} - } - mergeInto.TimeSkippingConfig.Bound = &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: mergeFrom.GetTimeSkippingConfig().GetMaxElapsedDuration(), + mergeInto.TimeSkippingConfig = &commonpb.TimeSkippingConfig{} } + mergeInto.TimeSkippingConfig.FastForward = mergeFrom.GetTimeSkippingConfig().GetFastForward() } return mergeInto, nil diff --git a/service/history/api/updateworkflowoptions/api_test.go b/service/history/api/updateworkflowoptions/api_test.go index 5b106a9776..85291dfec8 100644 --- a/service/history/api/updateworkflowoptions/api_test.go +++ b/service/history/api/updateworkflowoptions/api_test.go @@ -160,10 +160,10 @@ func TestMergeOptions_FooMask(t *testing.T) { func TestMergeOptions_TimeSkippingConfig(t *testing.T) { tscMask := &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}} - cfgA := &workflowpb.TimeSkippingConfig{Enabled: true} - cfgB := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, + cfgA := &commonpb.TimeSkippingConfig{Enabled: true} + cfgB := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), } tcs := []struct { @@ -171,7 +171,7 @@ func TestMergeOptions_TimeSkippingConfig(t *testing.T) { current *workflowpb.WorkflowExecutionOptions update *workflowpb.WorkflowExecutionOptions wantChanged bool - wantConfig *workflowpb.TimeSkippingConfig + wantConfig *commonpb.TimeSkippingConfig }{ // nil update means "don't touch" even when mask is present { @@ -304,6 +304,7 @@ func (s *updateWorkflowOptionsSuite) TestInvoke_Success() { IsMember: true, }, nil) s.currentMutableState.EXPECT().AddWorkflowExecutionOptionsUpdatedEvent(expectedOverrideOptions.VersioningOverride, false, "", nil, nil, "", expectedOverrideOptions.Priority, expectedOverrideOptions.TimeSkippingConfig, nil).Return(&historypb.HistoryEvent{}, nil) + s.currentMutableState.EXPECT().Now().Return(time.Time{}) s.currentContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), s.shardContext).Return(nil) updateReq := &historyservice.UpdateWorkflowExecutionOptionsRequest{ @@ -340,73 +341,63 @@ func TestMergeAndApply_TimeSkippingConfig(t *testing.T) { testCases := []struct { name string - initialConfig *workflowpb.TimeSkippingConfig + initialConfig *commonpb.TimeSkippingConfig updateOptions *workflowpb.WorkflowExecutionOptions updateMask *fieldmaskpb.FieldMask - expectedConfig *workflowpb.TimeSkippingConfig + expectedConfig *commonpb.TimeSkippingConfig }{ { name: "update max_elapsed_duration while enabled", - initialConfig: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: oneHour, - }, + initialConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: oneHour, }, updateOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: twoHours, - }, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: twoHours, }, }, - updateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config.max_elapsed_duration"}}, - expectedConfig: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: twoHours, - }, + updateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config.fast_forward"}}, + expectedConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: twoHours, }, }, { - name: "change bound type to max_elapsed_duration while enabled", - initialConfig: &workflowpb.TimeSkippingConfig{ + name: "set max_elapsed_duration while enabled", + initialConfig: &commonpb.TimeSkippingConfig{ Enabled: true, }, updateOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: thirtyMin, - }, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: thirtyMin, }, }, - updateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config.max_elapsed_duration"}}, - expectedConfig: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: thirtyMin, - }, + updateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config.fast_forward"}}, + expectedConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: thirtyMin, }, }, { name: "enable from nil config", initialConfig: nil, updateOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, updateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config.enabled"}}, - expectedConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + expectedConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, { name: "disable from enabled config", - initialConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + initialConfig: &commonpb.TimeSkippingConfig{Enabled: true}, updateOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: false}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: false}, }, updateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config.enabled"}}, - expectedConfig: &workflowpb.TimeSkippingConfig{Enabled: false}, + expectedConfig: &commonpb.TimeSkippingConfig{Enabled: false}, }, } diff --git a/service/history/historybuilder/event_factory.go b/service/history/historybuilder/event_factory.go index 04c51ecf37..34a28cc75b 100644 --- a/service/history/historybuilder/event_factory.go +++ b/service/history/historybuilder/event_factory.go @@ -85,7 +85,7 @@ func (b *EventFactory) CreateWorkflowExecutionStartedEvent( InheritedAutoUpgradeInfo: request.InheritedAutoUpgradeInfo, DeclinedTargetVersionUpgrade: request.DeclinedTargetVersionUpgrade, TimeSkippingConfig: req.GetTimeSkippingConfig(), - InitialSkippedDuration: request.GetInitialSkippedDuration(), + TimeSkippingStatePropagation: request.GetTimeSkippingStatePropagation(), } parentInfo := request.ParentExecutionInfo @@ -406,7 +406,7 @@ func (b *EventFactory) CreateWorkflowExecutionOptionsUpdatedEvent( links []*commonpb.Link, identity string, priority *commonpb.Priority, - timeSkippingConfig *workflowpb.TimeSkippingConfig, + timeSkippingConfig *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) *historypb.HistoryEvent { event := b.createHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED, b.timeSource.Now()) @@ -843,8 +843,8 @@ func (b *EventFactory) CreateStartChildWorkflowExecutionInitiatedEvent( workflowTaskCompletedEventID int64, command *commandpb.StartChildWorkflowExecutionCommandAttributes, targetNamespaceID namespace.ID, - timeSkippingConfig *workflowpb.TimeSkippingConfig, - initialSkippedDuration *durationpb.Duration, + timeSkippingConfig *commonpb.TimeSkippingConfig, + timeSkippingStatePropagation *commonpb.TimeSkippingStatePropagation, ) *historypb.HistoryEvent { event := b.createHistoryEvent(enumspb.EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED, b.timeSource.Now()) event.Attributes = &historypb.HistoryEvent_StartChildWorkflowExecutionInitiatedEventAttributes{ @@ -867,13 +867,13 @@ func (b *EventFactory) CreateStartChildWorkflowExecutionInitiatedEvent( // Filter nil values here rather than in the API layer because not all // creation paths go through the frontend (continue-as-new, child workflows, replication). // This CaN event is created on the parent workflow, so we need to filter nil values here. - Memo: payload.FilterNilMemo(command.Memo), - SearchAttributes: payload.FilterNilSearchAttributes(command.SearchAttributes), - ParentClosePolicy: command.GetParentClosePolicy(), - InheritBuildId: command.InheritBuildId, //nolint:staticcheck // SA1019: worker versioning v0.2 - Priority: command.Priority, - TimeSkippingConfig: timeSkippingConfig, - InitialSkippedDuration: initialSkippedDuration, + Memo: payload.FilterNilMemo(command.Memo), + SearchAttributes: payload.FilterNilSearchAttributes(command.SearchAttributes), + ParentClosePolicy: command.GetParentClosePolicy(), + InheritBuildId: command.InheritBuildId, //nolint:staticcheck // SA1019: worker versioning v0.2 + Priority: command.Priority, + TimeSkippingConfig: timeSkippingConfig, + TimeSkippingStatePropagation: timeSkippingStatePropagation, }, } return event @@ -1102,8 +1102,8 @@ func (b *EventFactory) CreateWorkflowExecutionTimeSkippingTransitionedEvent( ) *historypb.HistoryEvent { event := b.createHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED, b.timeSource.Now()) transitionedAttr := &historypb.WorkflowExecutionTimeSkippingTransitionedEventAttributes{ - WallClockTime: timestamppb.New(b.timeSource.Now()), - DisabledAfterBound: triggeredDisable, + WallClockTime: timestamppb.New(b.timeSource.Now()), + DisabledAfterFastForward: triggeredDisable, } if !targetTime.IsZero() { transitionedAttr.TargetTime = timestamppb.New(targetTime.UTC()) diff --git a/service/history/historybuilder/history_builder.go b/service/history/historybuilder/history_builder.go index dfd7a1d9a7..860a0c5563 100644 --- a/service/history/historybuilder/history_builder.go +++ b/service/history/historybuilder/history_builder.go @@ -473,7 +473,7 @@ func (b *HistoryBuilder) AddWorkflowExecutionOptionsUpdatedEvent( links []*commonpb.Link, identity string, priority *commonpb.Priority, - timeSkippingConfig *workflowpb.TimeSkippingConfig, + timeSkippingConfig *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) *historypb.HistoryEvent { event := b.EventFactory.CreateWorkflowExecutionOptionsUpdatedEvent( @@ -769,15 +769,15 @@ func (b *HistoryBuilder) AddStartChildWorkflowExecutionInitiatedEvent( workflowTaskCompletedEventID int64, command *commandpb.StartChildWorkflowExecutionCommandAttributes, targetNamespaceID namespace.ID, - timeSkippingConfig *workflowpb.TimeSkippingConfig, - initialSkippedDuration *durationpb.Duration, + timeSkippingConfig *commonpb.TimeSkippingConfig, + timeSkippingStatePropagation *commonpb.TimeSkippingStatePropagation, ) (*historypb.HistoryEvent, int64) { event := b.EventFactory.CreateStartChildWorkflowExecutionInitiatedEvent( workflowTaskCompletedEventID, command, targetNamespaceID, timeSkippingConfig, - initialSkippedDuration, + timeSkippingStatePropagation, ) return b.add(event) } diff --git a/service/history/historybuilder/history_builder_test.go b/service/history/historybuilder/history_builder_test.go index aa36f08752..8248c17651 100644 --- a/service/history/historybuilder/history_builder_test.go +++ b/service/history/historybuilder/history_builder_test.go @@ -2602,7 +2602,7 @@ func (s *historyBuilderSuite) TestAddWorkflowExecutionTimeSkippingTransitionedEv s.NotNil(attrs.TargetTime) s.Equal(targetTime, attrs.TargetTime.AsTime()) s.NotNil(attrs.WallClockTime) - s.False(attrs.DisabledAfterBound) + s.False(attrs.DisabledAfterFastForward) }) s.Run("DisabledAfterBoundPropagated", func() { @@ -2610,7 +2610,7 @@ func (s *historyBuilderSuite) TestAddWorkflowExecutionTimeSkippingTransitionedEv attrs := event.GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() s.NotNil(attrs) - s.True(attrs.DisabledAfterBound) + s.True(attrs.DisabledAfterFastForward) s.True(event.WorkerMayIgnore) }) @@ -2620,7 +2620,7 @@ func (s *historyBuilderSuite) TestAddWorkflowExecutionTimeSkippingTransitionedEv attrs := event.GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() s.NotNil(attrs) - s.True(attrs.DisabledAfterBound) + s.True(attrs.DisabledAfterFastForward) s.True(event.WorkerMayIgnore) }) } diff --git a/service/history/interfaces/mutable_state.go b/service/history/interfaces/mutable_state.go index 8f6a6a0002..9f65c04a11 100644 --- a/service/history/interfaces/mutable_state.go +++ b/service/history/interfaces/mutable_state.go @@ -128,7 +128,7 @@ type ( links []*commonpb.Link, identity string, priority *commonpb.Priority, - timeSkippingConfig *workflowpb.TimeSkippingConfig, + timeSkippingConfig *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) AddWorkflowExecutionUpdateAcceptedEvent(updateID string, acceptedRequestMessageID string, acceptedRequestSequencingEventID int64, acceptedRequest *updatepb.Request) (*historypb.HistoryEvent, error) @@ -421,7 +421,7 @@ type ( // adjusting for accumulated skipped duration which may have happened. ToRealTime(virtualTime time.Time) time.Time AddWorkflowExecutionTimeSkippingTransitionedEvent( - ctx context.Context, targetTime time.Time, disabledAfterBound bool) (*historypb.HistoryEvent, error) + ctx context.Context, targetTime time.Time, disabledAfterFastForward bool) (*historypb.HistoryEvent, error) ApplyWorkflowExecutionTimeSkippingTransitionedEvent(ctx context.Context, event *historypb.HistoryEvent) error } ) diff --git a/service/history/interfaces/mutable_state_mock.go b/service/history/interfaces/mutable_state_mock.go index 4f19431f25..0d6b44a33c 100644 --- a/service/history/interfaces/mutable_state_mock.go +++ b/service/history/interfaces/mutable_state_mock.go @@ -678,7 +678,7 @@ func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionCanceledEvent(arg0, } // AddWorkflowExecutionOptionsUpdatedEvent mocks base method. -func (m *MockMutableState) AddWorkflowExecutionOptionsUpdatedEvent(versioningOverride *workflow.VersioningOverride, unsetVersioningOverride bool, attachRequestID string, attachCompletionCallbacks []*common.Callback, links []*common.Link, identity string, priority *common.Priority, timeSkippingConfig *workflow.TimeSkippingConfig, workflowUpdateOptions []*history.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate) (*history.HistoryEvent, error) { +func (m *MockMutableState) AddWorkflowExecutionOptionsUpdatedEvent(versioningOverride *workflow.VersioningOverride, unsetVersioningOverride bool, attachRequestID string, attachCompletionCallbacks []*common.Callback, links []*common.Link, identity string, priority *common.Priority, timeSkippingConfig *common.TimeSkippingConfig, workflowUpdateOptions []*history.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate) (*history.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddWorkflowExecutionOptionsUpdatedEvent", versioningOverride, unsetVersioningOverride, attachRequestID, attachCompletionCallbacks, links, identity, priority, timeSkippingConfig, workflowUpdateOptions) ret0, _ := ret[0].(*history.HistoryEvent) @@ -783,18 +783,18 @@ func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionTerminatedEvent(reas } // AddWorkflowExecutionTimeSkippingTransitionedEvent mocks base method. -func (m *MockMutableState) AddWorkflowExecutionTimeSkippingTransitionedEvent(ctx context.Context, targetTime time.Time, disabledAfterBound bool) (*history.HistoryEvent, error) { +func (m *MockMutableState) AddWorkflowExecutionTimeSkippingTransitionedEvent(ctx context.Context, targetTime time.Time, disabledAfterFastForward bool) (*history.HistoryEvent, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddWorkflowExecutionTimeSkippingTransitionedEvent", ctx, targetTime, disabledAfterBound) + ret := m.ctrl.Call(m, "AddWorkflowExecutionTimeSkippingTransitionedEvent", ctx, targetTime, disabledAfterFastForward) ret0, _ := ret[0].(*history.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionTimeSkippingTransitionedEvent indicates an expected call of AddWorkflowExecutionTimeSkippingTransitionedEvent. -func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionTimeSkippingTransitionedEvent(ctx, targetTime, disabledAfterBound any) *gomock.Call { +func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionTimeSkippingTransitionedEvent(ctx, targetTime, disabledAfterFastForward any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionTimeSkippingTransitionedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionTimeSkippingTransitionedEvent), ctx, targetTime, disabledAfterBound) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionTimeSkippingTransitionedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionTimeSkippingTransitionedEvent), ctx, targetTime, disabledAfterFastForward) } // AddWorkflowExecutionUnpausedEvent mocks base method. diff --git a/service/history/ndc/events_reapplier_test.go b/service/history/ndc/events_reapplier_test.go index a932a0199d..1c90a910a5 100644 --- a/service/history/ndc/events_reapplier_test.go +++ b/service/history/ndc/events_reapplier_test.go @@ -13,7 +13,6 @@ import ( historypb "go.temporal.io/api/history/v1" "go.temporal.io/api/serviceerror" updatepb "go.temporal.io/api/update/v1" - workflowpb "go.temporal.io/api/workflow/v1" enumsspb "go.temporal.io/server/api/enums/v1" persistencespb "go.temporal.io/server/api/persistence/v1" "go.temporal.io/server/common/definition" @@ -136,7 +135,7 @@ func (s *nDCEventReapplicationSuite) TestReapplyEvents_AppliedEvent_WorkflowExec execution := &persistencespb.WorkflowExecutionInfo{ NamespaceId: uuid.NewString(), } - timeSkippingConfig := &workflowpb.TimeSkippingConfig{Enabled: true} + timeSkippingConfig := &commonpb.TimeSkippingConfig{Enabled: true} event := &historypb.HistoryEvent{ EventId: 1, EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED, diff --git a/service/history/ndc/workflow_resetter_test.go b/service/history/ndc/workflow_resetter_test.go index f4a8c2cc5f..406ce5d555 100644 --- a/service/history/ndc/workflow_resetter_test.go +++ b/service/history/ndc/workflow_resetter_test.go @@ -1707,7 +1707,7 @@ func (s *workflowResetterSuite) TestReapplyEvents_WorkflowOptionsUpdated_Complet } func (s *workflowResetterSuite) TestReapplyEvents_WorkflowOptionsUpdated_WithTimeSkippingConfig() { - timeSkippingConfig := &workflowpb.TimeSkippingConfig{Enabled: true} + timeSkippingConfig := &commonpb.TimeSkippingConfig{Enabled: true} event := &historypb.HistoryEvent{ EventId: 101, EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED, diff --git a/service/history/tasks/timeskipping_timer_task.go b/service/history/tasks/timeskipping_timer_task.go index cf4ed5cf35..839b6aa5aa 100644 --- a/service/history/tasks/timeskipping_timer_task.go +++ b/service/history/tasks/timeskipping_timer_task.go @@ -11,13 +11,13 @@ import ( var _ Task = (*TimeSkippingTimerTask)(nil) type ( - // TimeSkippingTimerTask wakes a workflow when the elapsed-duration bound configured - // on its TimeSkippingConfig is reached, so the executor can emit a disable transition. + // TimeSkippingTimerTask wakes a workflow when the fast-forward configured + // on its TimeSkippingConfig should take effect. // // EventID identifies the event (WorkflowExecutionStartedEvent or - // WorkflowExecutionOptionsUpdatedEvent) that installed the bound this task targets. - // It is matched against TimeSkippingInfo.CurrentElapsedDurationBound.SourceEventId at - // firing time to detect superseded tasks: re-configuring the bound emits a new task + // WorkflowExecutionOptionsUpdatedEvent) that installed the fast-forward this task targets. + // It is matched against TimeSkippingInfo.FastForward.SourceEventId at + // firing time to detect superseded tasks: re-configuring the fast-forward emits a new task // with a new EventID, and the old task is silently dropped on mismatch. // todo@time-skipping: replication related feature (ndc) TimeSkippingTimerTask struct { diff --git a/service/history/timer_queue_active_task_executor.go b/service/history/timer_queue_active_task_executor.go index efb7d44630..dc07530e4f 100644 --- a/service/history/timer_queue_active_task_executor.go +++ b/service/history/timer_queue_active_task_executor.go @@ -730,7 +730,7 @@ func (t *timerQueueActiveTaskExecutor) executeWorkflowRunTimeoutTask( startAttr := startEvent.GetWorkflowExecutionStartedEventAttributes() // TODO@time-skipping: if time skipping happened, the virtual time is - // propagated to the new mutable state, need to check the bound works correctly in the retry. + // propagated to the new mutable state, need to check the fast-forward works correctly in the retry. newMutableState, err := workflow.NewMutableStateInChain( t.shardContext, t.shardContext.GetEventsCache(), @@ -901,9 +901,9 @@ func (t *timerQueueActiveTaskExecutor) getTimerSequence( return workflow.NewTimerSequence(mutableState) } -// executeTimeSkippingTimerTask fires when the elapsed-duration bound is hit. It emits the -// disable transition directly so the bound is honored even if the workflow has accumulated -// in-flight work since the bound was configured. The bound's wake-up cue is wall-clock-anchored, +// executeTimeSkippingTimerTask fires when the fast-forward is hit. It emits the +// disable transition directly so the fast-forward is honored even if the workflow has accumulated +// in-flight work since the fast-forward was configured. The fast-forward's wake-up cue is wall-clock-anchored, // so by the time we get here the user-visible elapsed budget is genuinely exhausted. func (t *timerQueueActiveTaskExecutor) executeTimeSkippingTimerTask( ctx context.Context, @@ -932,7 +932,7 @@ func (t *timerQueueActiveTaskExecutor) executeTimeSkippingTimerTask( return consts.ErrWorkflowCompleted } - if !timeSkippingBoundTaskIsLive(mutableState, task) { + if !fastForwardTaskIsLive(mutableState, task) { release(nil) return errNoTimerFired } @@ -945,20 +945,20 @@ func (t *timerQueueActiveTaskExecutor) executeTimeSkippingTimerTask( return t.updateWorkflowExecution(ctx, weContext, mutableState, false) } -// timeSkippingBoundTaskIsLive returns false when the task should be dropped silently — -// either time skipping has been disabled since the task was emitted, or the bound this +// fastForwardTaskIsLive returns false when the task should be dropped silently — +// either time skipping has been disabled since the task was emitted, or the fast-forward this // task was associated with has been superseded (different SourceEventId) or already fired -// (HasReached=true). Dropping is harmless: the new bound, if any, has its own wake-up task. -func timeSkippingBoundTaskIsLive(mutableState historyi.MutableState, task *tasks.TimeSkippingTimerTask) bool { +// (HasReached=true). Dropping is harmless: the new fast-forward, if any, has its own wake-up task. +func fastForwardTaskIsLive(mutableState historyi.MutableState, task *tasks.TimeSkippingTimerTask) bool { tsi := mutableState.GetExecutionInfo().GetTimeSkippingInfo() if tsi == nil || !tsi.GetConfig().GetEnabled() { return false } - boundInfo := tsi.GetCurrentElapsedDurationBound() - if boundInfo == nil || boundInfo.GetTargetTime() == nil || boundInfo.GetSourceEventId() == 0 || boundInfo.GetHasReached() { + fastForward := tsi.GetFastForwardInfo() + if fastForward == nil || fastForward.GetTargetTime() == nil || fastForward.GetSourceEventId() == 0 || fastForward.GetHasReached() { return false } - return boundInfo.GetSourceEventId() == task.EventID + return fastForward.GetSourceEventId() == task.EventID } func (t *timerQueueActiveTaskExecutor) updateWorkflowExecution( diff --git a/service/history/timer_queue_active_task_executor_test.go b/service/history/timer_queue_active_task_executor_test.go index 796db3cc16..9fe23ca39d 100644 --- a/service/history/timer_queue_active_task_executor_test.go +++ b/service/history/timer_queue_active_task_executor_test.go @@ -11,7 +11,6 @@ import ( commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" - workflowpb "go.temporal.io/api/workflow/v1" "go.temporal.io/api/workflowservice/v1" enumsspb "go.temporal.io/server/api/enums/v1" "go.temporal.io/server/api/historyservice/v1" @@ -2382,7 +2381,7 @@ func (s *timerQueueActiveTaskExecutorSuite) mustGenerateTaskID() int64 { // programming GetWorkflowExecution. func (s *timerQueueActiveTaskExecutorSuite) makeTimeSkippingMS() (*persistencespb.WorkflowMutableState, definition.WorkflowKey) { execution := &commonpb.WorkflowExecution{ - WorkflowId: "ts-bound-wf-" + uuid.NewString(), + WorkflowId: "ts-fast-forward-wf-" + uuid.NewString(), RunId: uuid.NewString(), } workflowKey := definition.NewWorkflowKey(s.namespaceID.String(), execution.GetWorkflowId(), execution.GetRunId()) @@ -2414,11 +2413,10 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_Wor pms, workflowKey := s.makeTimeSkippingMS() pms.ExecutionState.State = enumsspb.WORKFLOW_EXECUTION_STATE_COMPLETED pms.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, - }, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour)}, + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.now.Add(time.Hour)), SourceEventId: 1, }, @@ -2457,8 +2455,8 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_TSI func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_ConfigDisabled() { pms, workflowKey := s.makeTimeSkippingMS() pms.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: false}, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + Config: &commonpb.TimeSkippingConfig{Enabled: false}, + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.now.Add(time.Hour)), SourceEventId: 1, }, @@ -2477,11 +2475,11 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_Con s.ErrorIs(resp.ExecutionErr, errNoTimerFired) } -func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_NoBoundInfo() { +func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_NoFastForwardInfo() { pms, workflowKey := s.makeTimeSkippingMS() pms.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, - // CurrentElapsedDurationBound deliberately not set. + Config: &commonpb.TimeSkippingConfig{Enabled: true}, + // FastForward deliberately not set. } timerTask := &tasks.TimeSkippingTimerTask{ @@ -2500,8 +2498,8 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_NoB func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_HasReached() { pms, workflowKey := s.makeTimeSkippingMS() pms.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + Config: &commonpb.TimeSkippingConfig{Enabled: true}, + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.now.Add(time.Hour)), SourceEventId: 1, HasReached: true, @@ -2524,8 +2522,8 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_Has func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_SourceEventIDZero() { pms, workflowKey := s.makeTimeSkippingMS() pms.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + Config: &commonpb.TimeSkippingConfig{Enabled: true}, + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.now.Add(time.Hour)), SourceEventId: 0, }, @@ -2547,10 +2545,10 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_Sou func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_SourceEventIDMismatch() { pms, workflowKey := s.makeTimeSkippingMS() pms.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + Config: &commonpb.TimeSkippingConfig{Enabled: true}, + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.now.Add(time.Hour)), - SourceEventId: 2, // bound was reconfigured; old task carries EventID=1. + SourceEventId: 2, // fast-forward was reconfigured; old task carries EventID=1. }, } @@ -2570,11 +2568,10 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_Sou func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_HappyPath() { pms, workflowKey := s.makeTimeSkippingMS() pms.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, - }, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour)}, + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.now.Add(time.Hour)), SourceEventId: 1, }, @@ -2600,7 +2597,7 @@ func (s *timerQueueActiveTaskExecutorSuite) TestExecuteTimeSkippingTimerTask_Hap tsi := loaded.GetExecutionInfo().GetTimeSkippingInfo() s.Require().NotNil(tsi) s.False(tsi.GetConfig().GetEnabled(), "happy path must flip Enabled=false") - s.True(tsi.GetCurrentElapsedDurationBound().GetHasReached(), "happy path must set HasReached=true") + s.True(tsi.GetFastForwardInfo().GetHasReached(), "happy path must set HasReached=true") } func (s *timerQueueActiveTaskExecutorSuite) TestProcessSingleActivityTimeoutTask() { @@ -2771,7 +2768,7 @@ func (s *timerQueueActiveTaskExecutorSuite) TestProcessActivityTimeout_Heartbeat // on reload. After load, pendingActivityTimerHeartbeats[id] is reset to the // year-2000 sentinel by NewMutableStateFromDB, and ToRealTime(year 2000) = 1999-12-31 23:00. persistenceMutableState.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, AccumulatedSkippedDuration: durationpb.New(time.Hour), } diff --git a/service/history/timer_queue_standby_task_executor.go b/service/history/timer_queue_standby_task_executor.go index 573648e414..560e0c2d6f 100644 --- a/service/history/timer_queue_standby_task_executor.go +++ b/service/history/timer_queue_standby_task_executor.go @@ -110,7 +110,7 @@ func (t *timerQueueStandbyTaskExecutor) Execute( case *tasks.ChasmTask: err = t.executeChasmSideEffectTimerTask(ctx, task) case *tasks.TimeSkippingTimerTask: - // todo@time-skipping: replication. The disable-after-bound transition is emitted + // todo@time-skipping: replication. The disable-after-fast-forward transition is emitted // on the active side and will replicate; standby drops the local task. err = nil default: diff --git a/service/history/transfer_queue_active_task_executor.go b/service/history/transfer_queue_active_task_executor.go index 730ecf0821..9b70923721 100644 --- a/service/history/transfer_queue_active_task_executor.go +++ b/service/history/transfer_queue_active_task_executor.go @@ -1716,7 +1716,7 @@ func (t *transferQueueActiveTaskExecutor) startWorkflow( request.SourceVersionStamp = sourceVersionStamp request.InheritedBuildId = inheritedBuildId request.InheritedPinnedVersion = inheritedPinnedVersion - request.InitialSkippedDuration = attributes.GetInitialSkippedDuration() + request.TimeSkippingStatePropagation = attributes.GetTimeSkippingStatePropagation() // Only set the AutoUpgrade info if the Pinned version is not set. if request.InheritedPinnedVersion == nil { diff --git a/service/history/workflow/mutable_state_impl.go b/service/history/workflow/mutable_state_impl.go index 7edd2658cd..234c452fa4 100644 --- a/service/history/workflow/mutable_state_impl.go +++ b/service/history/workflow/mutable_state_impl.go @@ -1913,12 +1913,8 @@ func (ms *MutableStateImpl) Now() time.Time { return ms.timeSource.Now() } -// accumulatedSkippedDuration returns the workflow's accumulated time-skipping offset, or 0 if -// time skipping is not configured. Timestamps in mutable state are stored in the virtual frame -// (i.e. they may include skipped time); this offset is the amount to subtract to get real -// wall-clock, used by AddTasks when persisting timer-category tasks. func (ms *MutableStateImpl) accumulatedSkippedDuration() time.Duration { - return ms.executionInfo.GetTimeSkippingInfo().GetAccumulatedSkippedDuration().AsDuration() + return accumulatedSkippedDuration(ms.executionInfo) } // GetWorkflowCloseTime returns workflow closed time, returns a zero time for open workflow @@ -2719,8 +2715,7 @@ func (ms *MutableStateImpl) addWorkflowExecutionStartedEventForContinueAsNew( } declinedTargetVersionUpgrade := computeDeclinedTargetVersionUpgrade(previousExecutionInfo, inheritedPinnedVersion != nil) - newRunTSConfig, newRunInitialSkipped := snapshotTimeSkippingInfo(previousExecutionInfo) - + tsc, stateProp := propagateTimeSkippingToNextRun(previousExecutionInfo) createRequest := &workflowservice.StartWorkflowExecutionRequest{ RequestId: uuid.NewString(), Namespace: ms.namespaceEntry.Name().String(), @@ -2741,7 +2736,7 @@ func (ms *MutableStateImpl) addWorkflowExecutionStartedEventForContinueAsNew( CompletionCallbacks: completionCallbacks, Links: links, Priority: previousExecutionInfo.Priority, - TimeSkippingConfig: newRunTSConfig, + TimeSkippingConfig: tsc, } enums.SetDefaultContinueAsNewInitiator(&command.Initiator) @@ -2776,7 +2771,7 @@ func (ms *MutableStateImpl) addWorkflowExecutionStartedEventForContinueAsNew( InheritedPinnedVersion: inheritedPinnedVersion, VersioningOverride: pinnedOverride, DeclinedTargetVersionUpgrade: declinedTargetVersionUpgrade, - InitialSkippedDuration: newRunInitialSkipped, + TimeSkippingStatePropagation: stateProp, } if command.GetInitiator() == enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY { req.Attempt = previousExecutionState.GetExecutionInfo().Attempt + 1 @@ -3183,8 +3178,8 @@ func (ms *MutableStateImpl) ApplyWorkflowExecutionStartedEvent( ms.executionInfo.MostRecentWorkerVersionStamp = event.SourceVersionStamp ms.executionInfo.Priority = event.Priority - if tsc := event.GetTimeSkippingConfig(); tsc != nil { - ms.initTimeSkippingInfo(tsc, event.GetInitialSkippedDuration(), startEvent.GetEventId()) + if tsc, stateProp := event.GetTimeSkippingConfig(), event.GetTimeSkippingStatePropagation(); tsc != nil || stateProp.GetInitialSkippedDuration().AsDuration() > 0 { + ms.initTimeSkippingInfo(tsc, stateProp, startEvent.GetEventId()) } ms.approximateSize += ms.executionInfo.Size() @@ -4153,13 +4148,13 @@ func (ms *MutableStateImpl) AddWorkflowTaskFailedEvent( // AddWorkflowExecutionTimeSkippingTransitionedEvent adds a workflow execution time skipping transitioned event to the mutable state. // This should only be called only when `ShouldExecuteTimeSkipping` returns true. func (ms *MutableStateImpl) AddWorkflowExecutionTimeSkippingTransitionedEvent( - ctx context.Context, targetTime time.Time, disabledAfterBound bool) (*historypb.HistoryEvent, error) { + ctx context.Context, targetTime time.Time, disabledAfterFastForward bool) (*historypb.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowExecutionTimeSkippingTransitioned if err := ms.checkMutability(opTag); err != nil { return nil, err } event := ms.hBuilder.AddWorkflowExecutionTimeSkippingTransitionedEvent( - targetTime, disabledAfterBound) + targetTime, disabledAfterFastForward) return event, ms.ApplyWorkflowExecutionTimeSkippingTransitionedEvent(ctx, event) } @@ -4174,7 +4169,7 @@ func (ms *MutableStateImpl) ApplyWorkflowExecutionTimeSkippingTransitionedEvent( "TimeSkippingInfo is not set when applying WorkflowExecutionTimeSkippingTransitionedEvent, mutable state is corrupted", ) } - if attr.TargetTime == nil && !attr.GetDisabledAfterBound() { + if attr.TargetTime == nil && !attr.GetDisabledAfterFastForward() { return serviceerror.NewInternal( "empty WorkflowExecutionTimeSkippingTransitionedEvent found, event is corrupted", ) @@ -4188,10 +4183,10 @@ func (ms *MutableStateImpl) ApplyWorkflowExecutionTimeSkippingTransitionedEvent( accumulatedSkippedDuration += attr.TargetTime.AsTime().Sub(event.GetEventTime().AsTime()) } tsi.AccumulatedSkippedDuration = durationpb.New(accumulatedSkippedDuration) - tsi.Config.Enabled = !attr.GetDisabledAfterBound() + tsi.Config.Enabled = !attr.GetDisabledAfterFastForward() - if attr.GetDisabledAfterBound() && tsi.GetCurrentElapsedDurationBound() != nil { - tsi.CurrentElapsedDurationBound.HasReached = true + if attr.GetDisabledAfterFastForward() && tsi.GetFastForwardInfo() != nil { + tsi.FastForwardInfo.HasReached = true } ms.timeSkippingInfoUpdated = true @@ -5845,7 +5840,7 @@ func (ms *MutableStateImpl) AddWorkflowExecutionOptionsUpdatedEvent( links []*commonpb.Link, identity string, priority *commonpb.Priority, - timeSkippingConfig *workflowpb.TimeSkippingConfig, + timeSkippingConfig *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { if err := ms.checkMutability(tag.WorkflowActionWorkflowOptionsUpdated); err != nil { @@ -6353,14 +6348,13 @@ func (ms *MutableStateImpl) AddStartChildWorkflowExecutionInitiatedEvent( if err := ms.checkMutability(opTag); err != nil { return nil, nil, err } - - childTSConfig, childInitialSkipped := snapshotTimeSkippingInfo(ms.executionInfo) + childTSC, childTSStateProp := propagateTimeSkippingToChild(ms.executionInfo) event, batchID := ms.hBuilder.AddStartChildWorkflowExecutionInitiatedEvent( workflowTaskCompletedEventID, command, targetNamespaceID, - childTSConfig, - childInitialSkipped, + childTSC, + childTSStateProp, ) ci, err := ms.ApplyStartChildWorkflowExecutionInitiatedEvent(batchID, event) if err != nil { @@ -8905,7 +8899,7 @@ func (ms *MutableStateImpl) closeTransactionHandleTimeSkipping( } if shouldExecute, transition := ms.shouldExecuteTimeSkipping(); shouldExecute { _, err := ms.AddWorkflowExecutionTimeSkippingTransitionedEvent( - ctx, transition.targetTime, transition.disabledAfterBound) + ctx, transition.targetTime, transition.disabledAfterFastForward) if err != nil { ms.metricsHandler.Counter(metrics.ExecutionTimeSkippingTransitionedErrorCounter.Name()).Record(1) ms.logger.Error( @@ -9991,72 +9985,73 @@ func (ms *MutableStateImpl) shiftWorkflowTimes(initialSkippedDuration *durationp } func (ms *MutableStateImpl) initTimeSkippingInfo( - config *workflowpb.TimeSkippingConfig, - initialSkippedDuration *durationpb.Duration, + config *commonpb.TimeSkippingConfig, + timeSkippingStatePropagation *commonpb.TimeSkippingStatePropagation, currentEventID int64, ) { + // we only need to init time skipping info if + // either config is not nil or it has initial skip + initialSkip := timeSkippingStatePropagation.GetInitialSkippedDuration() + if config == nil && initialSkip == nil { + return + } ms.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ Config: config, - AccumulatedSkippedDuration: initialSkippedDuration, + AccumulatedSkippedDuration: initialSkip, } ms.wrapTimeSourceWithTimeSkipping() - ms.shiftWorkflowTimes(initialSkippedDuration) - ms.applyTimeSkippingBound(currentEventID) + ms.shiftWorkflowTimes(initialSkip) + ms.applyFastForward(currentEventID, timeSkippingStatePropagation.GetFastForwardTargetTime()) ms.timeSkippingInfoUpdated = true } func (ms *MutableStateImpl) updateTimeSkippingInfo( - config *workflowpb.TimeSkippingConfig, + config *commonpb.TimeSkippingConfig, currentEventID int64, ) { ms.executionInfo.TimeSkippingInfo.Config = config - ms.applyTimeSkippingBound(currentEventID) + // Options update: the new ff duration is a fresh budget measured from now. + ms.applyFastForward(currentEventID, nil) ms.timeSkippingInfoUpdated = true } -// applyTimeSkippingBound should be called whenever time skipping config is updated. -func (ms *MutableStateImpl) applyTimeSkippingBound(currentEventID int64) { - config := ms.GetExecutionInfo().GetTimeSkippingInfo().GetConfig() - if config == nil { - return - } - if !config.GetEnabled() { - return - } +// applyFastForward (re)computes the FastForwardInfo using the new TimeSkippingConfig (TSC) and propagated time-skippingstates. +// This method should be called whenever the TimeSkippingConfig is initialized or updated. +// An invariant of the FastForwardInfo is that after this method is called, if the current TSC has a FastForward value, +// the FastForwardInfo should never be nil. +func (ms *MutableStateImpl) applyFastForward(currentEventID int64, propagatedTargetTime *timestamppb.Timestamp) { + + tsc := ms.GetExecutionInfo().GetTimeSkippingInfo().GetConfig() tsi := ms.executionInfo.TimeSkippingInfo - bound := config.GetBound() - if bound == nil { - tsi.CurrentElapsedDurationBound = nil + + // clear fast forward if disabled or zero max_elapsed_duration + if !tsc.GetEnabled() || tsc.GetFastForward().AsDuration() <= 0 { + if tsi.FastForwardInfo != nil { + tsi.FastForwardInfo = nil + } return } - switch b := bound.(type) { - case *workflowpb.TimeSkippingConfig_MaxElapsedDuration: - if b.MaxElapsedDuration == nil { - return - } - target := ms.Now().Add(b.MaxElapsedDuration.AsDuration()) - // Skip task emission when the existing bound already targets the same - // virtual time — avoids leaving a stale wake-up task with a superseded - // SourceEventId that the executor would then drop. - if existing := tsi.GetCurrentElapsedDurationBound(); existing != nil && - existing.GetTargetTime().AsTime().Equal(target) { - return - } - tsi.CurrentElapsedDurationBound = &persistencespb.TimeSkippingBoundInfo{ - TargetTime: timestamppb.New(target), - SourceEventId: currentEventID, - HasReached: false, - } - ms.AddTasks(&tasks.TimeSkippingTimerTask{ - WorkflowKey: ms.GetWorkflowKey(), - VisibilityTimestamp: target, - EventID: currentEventID, - }) - default: - // Non-elapsed bound types don't emit a wake-up task and must not leave a - // stale CurrentElapsedDurationBound from a previous elapsed-bound config. - tsi.CurrentElapsedDurationBound = nil + + var targetTime time.Time + if propagatedTargetTime != nil { + targetTime = propagatedTargetTime.AsTime() + } else { + // if there is no propagated target time, + // fast-forward refers to a new duration from now. + targetTime = ms.Now().Add(tsc.GetFastForward().AsDuration()) } + + // always install a fresh fast-forward bound + tsi.FastForwardInfo = &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(targetTime), + SourceEventId: currentEventID, + HasReached: false, + } + ms.AddTasks(&tasks.TimeSkippingTimerTask{ + WorkflowKey: ms.GetWorkflowKey(), + VisibilityTimestamp: targetTime, + EventID: currentEventID, + }) } // wrapTimeSourceWithTimeSkipping wraps ms.timeSource (and the hBuilder's copy) with a time-skipping @@ -10072,29 +10067,6 @@ func (ms *MutableStateImpl) wrapTimeSourceWithTimeSkipping() { ms.hBuilder.SetTimeSource(ms.timeSource) } -// snapshotTimeSkippingInfo returns a clone of the source execution's TimeSkippingConfig -// and a copy of its AccumulatedSkippedDuration, for propagation to a new run (child workflow -// or continue-as-new). The source is passed explicitly so callers can snapshot from the -// originating run's executionInfo — on CaN, the new MS is empty at snapshot time, so we -// must read from the previous run. -func snapshotTimeSkippingInfo(source *persistencespb.WorkflowExecutionInfo) (*workflowpb.TimeSkippingConfig, *durationpb.Duration) { - tsInfo := source.GetTimeSkippingInfo() - if tsInfo == nil { - return nil, nil - } - var tsc *workflowpb.TimeSkippingConfig - var initialSkipped *durationpb.Duration - if srcTSC := tsInfo.GetConfig(); srcTSC != nil { - if cloned, ok := proto.Clone(srcTSC).(*workflowpb.TimeSkippingConfig); ok { - tsc = cloned - } - } - if skipped := tsInfo.GetAccumulatedSkippedDuration(); skipped != nil { - initialSkipped = durationpb.New(skipped.AsDuration()) - } - return tsc, initialSkipped -} - func (ms *MutableStateImpl) hasInflightWorkToPreventTimeSkipping() (bool, string) { // HasPendingWorkflowTask covers both normal and speculative workflow tasks if ms.HasPendingWorkflowTask() { @@ -10164,6 +10136,7 @@ func (ms *MutableStateImpl) shouldExecuteTimeSkipping() (bool, *timeSkippingTran } // Compute the transition early so we can short-circuit before allocating an event. + // todo(@time-skipping): replace error with nil transition, err := ms.calculateTimeSkippingTransition() if err != nil { noSkippingReason = fmt.Sprintf("error calculating time skipping decision: %v", err) @@ -10176,34 +10149,34 @@ func (ms *MutableStateImpl) shouldExecuteTimeSkipping() (bool, *timeSkippingTran return false, nil } if !transition.isValid() { - noSkippingReason = "time skipping has no candidate target time nor disabled after bound flag" + noSkippingReason = "time skipping has no candidate target time nor disabled after fast-forward flag" return false, nil } return true, &transition } type timeSkippingTransition struct { - targetTime time.Time - disabledAfterBound bool + targetTime time.Time + disabledAfterFastForward bool } func (d timeSkippingTransition) isValid() bool { - return !d.targetTime.IsZero() || d.disabledAfterBound + return !d.targetTime.IsZero() || d.disabledAfterFastForward } // calculateTimeSkippingTransition determines the next skip target. // Candidates (in collection order): pending user timers, activity retry backoffs, -// workflow start-with-delay/CaN/retry backoff, and the MaxElapsed bound. +// workflow start-with-delay/CaN/retry backoff, and the fast-forward. // The run/execution timeout is NOT a standalone candidate — it only applies as // a cap: if any candidate wins, the skip target is clamped to min(target, // runExpiry, execExpiry). This ensures we never advance virtual time past the -// workflow timeout, even when a user timer or bound would otherwise overshoot. +// workflow timeout, even when a user timer or fast-forward would otherwise overshoot. func (ms *MutableStateImpl) calculateTimeSkippingTransition() (timeSkippingTransition, error) { var transition timeSkippingTransition - advance := func(candidate time.Time, dueToBound bool) { + advance := func(candidate time.Time, dueToFastForward bool) { if transition.targetTime.IsZero() || candidate.Before(transition.targetTime) { transition.targetTime = candidate - transition.disabledAfterBound = dueToBound + transition.disabledAfterFastForward = dueToFastForward } } @@ -10236,23 +10209,15 @@ func (ms *MutableStateImpl) calculateTimeSkippingTransition() (timeSkippingTrans } } - info := ms.GetExecutionInfo().GetTimeSkippingInfo() - if bound := info.GetConfig().GetBound(); bound != nil { - switch bound.(type) { - case *workflowpb.TimeSkippingConfig_MaxElapsedDuration: - if info.GetCurrentElapsedDurationBound() == nil { - return timeSkippingTransition{}, serviceerror.NewInternal("time skipping bound target time is not set for elapsed-duration bound") - } - advance(info.GetCurrentElapsedDurationBound().GetTargetTime().AsTime(), true) - default: - return timeSkippingTransition{}, serviceerror.NewInternal("unknown time skipping bound type") - } + tsi := ms.GetExecutionInfo().GetTimeSkippingInfo() + if !tsi.GetFastForwardInfo().GetHasReached() && tsi.GetFastForwardInfo().GetTargetTime() != nil { + advance(tsi.GetFastForwardInfo().GetTargetTime().AsTime(), true) } // Cap any skip target at the run/execution timeout: never advance virtual time past // them. Timeouts alone do not create a skip target — only existing candidates - // (timers, backoffs, bound) do. This also handles the case where a user timer fires - // past the workflow timeout: we cap the skip so the timeout fires on schedule. + // (timers, backoffs, fast-forward) do. This also handles the case where a user + // timer fires past the workflow timeout: we cap the skip so the timeout fires on schedule. if !transition.targetTime.IsZero() { if t := ms.executionInfo.GetWorkflowRunExpirationTime(); t != nil && !t.AsTime().IsZero() { advance(t.AsTime(), false) diff --git a/service/history/workflow/mutable_state_impl_test.go b/service/history/workflow/mutable_state_impl_test.go index 92ebe72a4f..82a4669cee 100644 --- a/service/history/workflow/mutable_state_impl_test.go +++ b/service/history/workflow/mutable_state_impl_test.go @@ -4145,57 +4145,56 @@ func (s *mutableStateSuite) TestStartChildWorkflowRequestID() { s.Equal(createRequestID, ci.CreateRequestId) } -// TestAddStartChildWorkflowExecutionInitiatedEvent_TimeSkippingSnapshot verifies that -// AddStartChildWorkflowExecutionInitiatedEvent snapshots the parent's TimeSkippingInfo -// into the event attributes at command time. The transfer task that later starts the -// child reads this snapshot off the event (see transfer_queue_active_task_executor.go), -// so the snapshot must faithfully represent what the parent workflow observed. func (s *mutableStateSuite) TestAddStartChildWorkflowExecutionInitiatedEvent_TimeSkippingSnapshot() { cases := []struct { name string parentTSI *persistencespb.TimeSkippingInfo expectNilCfg bool - expectCfg *workflowpb.TimeSkippingConfig + expectCfg *commonpb.TimeSkippingConfig expectInitialSkip *durationpb.Duration }{ { - name: "no TimeSkippingInfo on parent → no snapshot on event", + name: "no TimeSkippingInfo on parent", parentTSI: nil, expectNilCfg: true, }, { - name: "config only, no accumulated skip → config cloned verbatim, no initial skip", + name: "config only", parentTSI: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, }, - expectCfg: &workflowpb.TimeSkippingConfig{Enabled: true}, + expectCfg: &commonpb.TimeSkippingConfig{Enabled: true}, + expectInitialSkip: nil, }, { - name: "config + accumulated skip → config cloned, InitialSkippedDuration = parent's accumulated", + name: "config + accumulated skip", parentTSI: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, AccumulatedSkippedDuration: durationpb.New(time.Hour), }, - expectCfg: &workflowpb.TimeSkippingConfig{Enabled: true}, + expectCfg: &commonpb.TimeSkippingConfig{Enabled: true}, expectInitialSkip: durationpb.New(time.Hour), }, { - name: "multi-generation: parent's accumulated (which already includes grandparent's contribution) becomes InitialSkippedDuration", - parentTSI: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, - AccumulatedSkippedDuration: durationpb.New(2 * time.Hour), - }, - expectCfg: &workflowpb.TimeSkippingConfig{Enabled: true}, - expectInitialSkip: durationpb.New(2 * time.Hour), - }, - { - name: "accumulated skip only, no config (corrupt-ish but handled) → no config snapshot, just InitialSkippedDuration", + name: "accumulated skip only", parentTSI: &persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{Enabled: false}, AccumulatedSkippedDuration: durationpb.New(15 * time.Minute), }, expectNilCfg: true, expectInitialSkip: durationpb.New(15 * time.Minute), }, + { + name: "MaxElapsedDuration is never propagated to children → cleared from child snapshot", + parentTSI: &persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(3 * time.Hour)}, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + }, + expectCfg: &commonpb.TimeSkippingConfig{Enabled: true}, + expectInitialSkip: durationpb.New(time.Hour), + }, } for _, tc := range cases { @@ -4214,9 +4213,13 @@ func (s *mutableStateSuite) TestAddStartChildWorkflowExecutionInitiatedEvent_Tim s.NoError(err) gotAttrs := event.GetStartChildWorkflowExecutionInitiatedEventAttributes() - gotTSC := gotAttrs.GetTimeSkippingConfig() - gotInitialSkip := gotAttrs.GetInitialSkippedDuration() + // forever nil + gotFastForward := gotAttrs.GetTimeSkippingStatePropagation().GetFastForwardTargetTime() + s.Nil(gotFastForward) + + gotTSC := gotAttrs.GetTimeSkippingConfig() + gotInitialSkip := gotAttrs.GetTimeSkippingStatePropagation().GetInitialSkippedDuration() if tc.expectInitialSkip == nil { s.Nil(gotInitialSkip) } else { @@ -6732,7 +6735,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.Run("FalseWhenConfigDisabled", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: false}, + Config: &commonpb.TimeSkippingConfig{Enabled: false}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} s.False(s.mutableState.shouldExecuteTimeSkipping()) @@ -6742,7 +6745,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.mutableState.executionState.State = enumsspb.WORKFLOW_EXECUTION_STATE_COMPLETED s.mutableState.executionState.Status = enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} s.False(s.mutableState.shouldExecuteTimeSkipping()) @@ -6750,7 +6753,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.Run("FalseWhenPendingWorkflowTask", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} s.mutableState.executionInfo.WorkflowTaskScheduledEventId = 1 @@ -6760,7 +6763,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.Run("FalseWhenPendingActivity", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} s.mutableState.pendingActivityInfoIDs[1] = &persistencespb.ActivityInfo{} @@ -6769,7 +6772,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.Run("FalseWhenPendingChildExecution", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} s.mutableState.pendingChildExecutionInfoIDs[1] = &persistencespb.ChildExecutionInfo{} @@ -6778,7 +6781,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.Run("FalseWhenPendingNexusOperation", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} _, err := nexusoperations.AddChild(s.mutableState.HSM(), "op-1", &historypb.HistoryEvent{ @@ -6791,45 +6794,41 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.False(s.mutableState.shouldExecuteTimeSkipping()) }) - s.Run("FalseWhenNoPendingTimersAndNoBound", func() { + s.Run("FalseWhenNoPendingTimersAndNoFastForward", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.False(s.mutableState.shouldExecuteTimeSkipping()) }) - s.Run("TrueWhenPendingTimerAndNoBound", func() { + s.Run("TrueWhenPendingTimerAndNoFastForward", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} s.True(s.mutableState.shouldExecuteTimeSkipping()) }) - s.Run("TrueWhenBoundAndNoPendingTimer", func() { + s.Run("TrueWhenFastForwardAndNoPendingTimer", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: durationpb.New(time.Hour), - }, + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), }, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.mutableState.Now().Add(time.Hour)), }, } s.True(s.mutableState.shouldExecuteTimeSkipping()) }) - s.Run("TrueWhenBoundAndPendingTimer", func() { + s.Run("TrueWhenFastForwardAndPendingTimer", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: durationpb.New(time.Hour), - }, + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), }, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.mutableState.Now().Add(time.Hour)), }, } @@ -6840,7 +6839,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.Run("FalseWhenPaused", func() { s.mutableState.executionState.Status = enumspb.WORKFLOW_EXECUTION_STATUS_PAUSED s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.pendingTimerInfoIDs["t1"] = &persistencespb.TimerInfo{TimerId: "t1"} s.False(s.mutableState.shouldExecuteTimeSkipping()) @@ -6848,7 +6847,7 @@ func (s *mutableStateSuite) TestShouldExecuteTimeSkipping() { s.Run("TrueWhenOnlyActivityInRetryBackoff", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } now := s.mutableState.Now() s.mutableState.pendingActivityInfoIDs[1] = &persistencespb.ActivityInfo{ @@ -6867,7 +6866,7 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionTimeSkippingTransitionedEv makeEvent := func(eventTime time.Time, targetTime *time.Time, disabledAfterBound bool) *historypb.HistoryEvent { attr := &historypb.WorkflowExecutionTimeSkippingTransitionedEventAttributes{ - DisabledAfterBound: disabledAfterBound, + DisabledAfterFastForward: disabledAfterBound, } if targetTime != nil { attr.TargetTime = timestamppb.New(*targetTime) @@ -6891,9 +6890,9 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionTimeSkippingTransitionedEv s.Require().Error(err) }) - s.Run("AccumulatesDurationFirstTime", func() { + s.Run("AccumulatesDuration", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.mutableState.timeSkippingInfoUpdated = false targetTime := baseTime.Add(2 * time.Hour) @@ -6905,15 +6904,13 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionTimeSkippingTransitionedEv s.Require().NoError(err) accumulated := s.mutableState.GetExecutionInfo().TimeSkippingInfo.AccumulatedSkippedDuration - s.Require().NotNil(accumulated) s.Require().Equal(2*time.Hour, accumulated.AsDuration()) - s.Require().True(s.mutableState.GetExecutionInfo().TimeSkippingInfo.Config.Enabled) s.Require().True(s.mutableState.timeSkippingInfoUpdated) }) s.Run("AccumulatesDurationAdditively", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, AccumulatedSkippedDuration: durationpb.New(time.Hour), } targetTime := baseTime.Add(2 * time.Hour) @@ -6930,9 +6927,9 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionTimeSkippingTransitionedEv s.Run("ErrorWhenNilTargetTimeAndNotDisabledAfterBound", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } - + s.mutableState.timeSkippingInfoUpdated = false err := s.mutableState.ApplyWorkflowExecutionTimeSkippingTransitionedEvent( context.Background(), makeEvent(baseTime, nil, false), @@ -6942,10 +6939,10 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionTimeSkippingTransitionedEv s.Run("NilTargetTimeWithDisabledAfterBoundDoesNotAccumulateDuration", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, AccumulatedSkippedDuration: durationpb.New(time.Hour), } - + s.mutableState.timeSkippingInfoUpdated = false err := s.mutableState.ApplyWorkflowExecutionTimeSkippingTransitionedEvent( context.Background(), makeEvent(baseTime, nil, true), @@ -6955,11 +6952,12 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionTimeSkippingTransitionedEv accumulated := s.mutableState.GetExecutionInfo().TimeSkippingInfo.AccumulatedSkippedDuration s.Require().Equal(time.Hour, accumulated.AsDuration()) s.Require().False(s.mutableState.GetExecutionInfo().TimeSkippingInfo.Config.Enabled) + s.Require().True(s.mutableState.timeSkippingInfoUpdated) }) s.Run("DisabledAfterBoundDisablesConfigAndAccumulatesDuration", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } targetTime := baseTime.Add(2 * time.Hour) @@ -7143,9 +7141,11 @@ func (s *mutableStateSuite) TestAddCompletedWorkflowEvent_ArchivalConvertsVirtua }, &historyservice.StartWorkflowExecutionRequest{ StartRequest: &workflowservice.StartWorkflowExecutionRequest{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, + }, + TimeSkippingStatePropagation: &commonpb.TimeSkippingStatePropagation{ + InitialSkippedDuration: durationpb.New(skipped), }, - InitialSkippedDuration: durationpb.New(skipped), }, ) s.NoError(err) @@ -7291,7 +7291,7 @@ func (s *mutableStateSuite) TestAccumulatedSkippedDuration_NilSafety() { // with already-stamped events / tasks. skipped := 17 * time.Minute s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: false}, + Config: &commonpb.TimeSkippingConfig{Enabled: false}, AccumulatedSkippedDuration: durationpb.New(skipped), } s.Equal(skipped, s.mutableState.accumulatedSkippedDuration()) @@ -7335,7 +7335,7 @@ func (s *mutableStateSuite) TestCloseTransactionTimeSkipping() { WorkflowTypeName: "testWorkflowType", WorkflowExecutionTimerTaskStatus: TimerTaskStatusCreated, TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, }, VersionHistories: &historyspb.VersionHistories{ Histories: []*historyspb.VersionHistory{ @@ -7489,7 +7489,7 @@ func (s *mutableStateSuite) TestCloseTransactionTimeSkipping() { s.Run("Active_NoPendingTimers_NoEvent", func() { // ShouldExecuteTimeSkipping returns false when there are no pending timers and no - // time bound configured. No event should be emitted. + // time fast-forward configured. No event should be emitted. now := s.mockShard.GetTimeSource().Now() dbState := buildEligibleState(now.Add(time.Hour)) @@ -7769,7 +7769,7 @@ func (s *mutableStateSuite) TestCloseTransactionPrepareTasks() { // runtime dedup handles the duplicate. dbState := buildRunningState() dbState.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } dbState.TimerInfos = pendingTimer(TimerTaskStatusNone) @@ -7801,7 +7801,7 @@ func (s *mutableStateSuite) TestCloseTransactionPrepareTasks() { // generates the time-shifted task. Only the time-skipped task appears. dbState := buildRunningState() dbState.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } dbState.TimerInfos = pendingTimer(TimerTaskStatusCreated) @@ -7831,15 +7831,17 @@ func (s *mutableStateSuite) TestCloseTransactionPrepareTasks() { // executionInfo.TimeSkippingInfo. This is the code path exercised by all mutable-state // rebuild flows. func (s *mutableStateSuite) TestApplyWorkflowExecutionStartedEvent_TimeSkippingConfig() { - inputConfig := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, - } + inputConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour)} testCases := []struct { - name string - timeSkippingConfig *workflowpb.TimeSkippingConfig - wantConfig *workflowpb.TimeSkippingConfig + name string + timeSkippingConfig *commonpb.TimeSkippingConfig + timeSkippingStatePropagation *commonpb.TimeSkippingStatePropagation + wantTSInfoNil bool + wantConfig *commonpb.TimeSkippingConfig + wantAccum time.Duration }{ { name: "with config", @@ -7847,9 +7849,18 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionStartedEvent_TimeSkippingC wantConfig: inputConfig, }, { - name: "without config", - timeSkippingConfig: nil, - wantConfig: nil, + name: "without config and no inherited skip", + wantTSInfoNil: true, + }, + { + // A child that opted out of config propagation arrives with a nil config but a + // positive InitialSkippedDuration: virtual time must still be seeded. + name: "nil config with inherited virtual time", + timeSkippingStatePropagation: &commonpb.TimeSkippingStatePropagation{ + InitialSkippedDuration: durationpb.New(2 * time.Hour), + }, + wantConfig: nil, + wantAccum: 2 * time.Hour, }, } @@ -7867,12 +7878,13 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionStartedEvent_TimeSkippingC EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, Attributes: &historypb.HistoryEvent_WorkflowExecutionStartedEventAttributes{ WorkflowExecutionStartedEventAttributes: &historypb.WorkflowExecutionStartedEventAttributes{ - WorkflowType: &commonpb.WorkflowType{Name: "test-workflow-type"}, - TaskQueue: &taskqueuepb.TaskQueue{Name: "test-task-queue"}, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - FirstExecutionRunId: tests.RunID, - TimeSkippingConfig: tc.timeSkippingConfig, + WorkflowType: &commonpb.WorkflowType{Name: "test-workflow-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "test-task-queue"}, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + FirstExecutionRunId: tests.RunID, + TimeSkippingConfig: tc.timeSkippingConfig, + TimeSkippingStatePropagation: tc.timeSkippingStatePropagation, }, }, } @@ -7885,11 +7897,14 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionStartedEvent_TimeSkippingC ) s.NoError(err) - if tc.wantConfig == nil { - s.Nil(s.mutableState.executionInfo.GetTimeSkippingInfo()) - } else { - s.True(proto.Equal(tc.wantConfig, s.mutableState.executionInfo.GetTimeSkippingInfo().GetConfig())) + tsi := s.mutableState.executionInfo.GetTimeSkippingInfo() + if tc.wantTSInfoNil { + s.Nil(tsi) + return } + s.Require().NotNil(tsi) + s.True(proto.Equal(tc.wantConfig, tsi.GetConfig())) + s.Equal(tc.wantAccum, tsi.GetAccumulatedSkippedDuration().AsDuration()) }) } } @@ -7899,20 +7914,18 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionStartedEvent_TimeSkippingC // or overwrites executionInfo.TimeSkippingInfo. This is the rebuild path for // time-skipping config that is set or changed after workflow start. func (s *mutableStateSuite) TestApplyWorkflowExecutionOptionsUpdatedEvent_TimeSkippingConfig() { - initialConfig := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, - } - updatedConfig := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(2 * time.Hour)}, - } + initialConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour)} + updatedConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(2 * time.Hour)} testCases := []struct { name string - existing *workflowpb.TimeSkippingConfig - eventConfig *workflowpb.TimeSkippingConfig - wantConfig *workflowpb.TimeSkippingConfig + existing *commonpb.TimeSkippingConfig + eventConfig *commonpb.TimeSkippingConfig + wantConfig *commonpb.TimeSkippingConfig wantInfoIsNil bool }{ { @@ -8149,305 +8162,231 @@ func TestGenerateActivityCancelCommandsForClose(t *testing.T) { } } -// TestApplyTimeSkippingBound covers the full branch table of applyTimeSkippingBound: -// MaxElapsedDuration set / nil duration / nil bound / nil config / Enabled=false. +// TestApplyFastForward covers the full branch table of applyFastForward: +// MaxElapsedDuration set / nil duration / nil fast-forward / nil config / Enabled=false. // The first-init virtual-time path is covered separately in -// TestInitTimeSkippingInfo_VirtualTimeBound. -func (s *mutableStateSuite) TestApplyTimeSkippingBound() { - const ( - eventID = int64(42) - maxLapse = 3 * time.Hour - ) +// TestInitTimeSkippingInfo_VirtualTime. +func (s *mutableStateSuite) TestApplyFastForward() { + s.Run("FastForward_WithNoPropagatedTargetTime", func() { - // findBoundTask returns the most-recently-emitted TimeSkippingTimerTask in InsertTasks - // (CategoryTimer slot), or nil if none. - findBoundTask := func(ms *MutableStateImpl) *tasks.TimeSkippingTimerTask { - for _, t := range ms.InsertTasks[tasks.CategoryTimer] { - if bt, ok := t.(*tasks.TimeSkippingTimerTask); ok { - return bt - } - } - return nil - } + fastForwardDuration := 3 * time.Hour + eventID := int64(1) + s.mutableState.timeSource = clock.NewEventTimeSource() + baseTime := s.mutableState.timeSource.Now() - s.Run("MaxElapsedDuration_SetsBoundAndEmitsTask", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(maxLapse)}, - }, + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(fastForwardDuration)}, + AccumulatedSkippedDuration: durationpb.New(time.Hour), } - before := s.mutableState.Now() - s.mutableState.applyTimeSkippingBound(eventID) + s.mutableState.applyFastForward(eventID, nil) + fastForward := s.mutableState.executionInfo.TimeSkippingInfo.GetFastForwardInfo() + s.Require().NotNil(fastForward) + s.Equal(eventID, fastForward.GetSourceEventId()) + s.False(fastForward.GetHasReached()) + s.WithinDuration(fastForward.GetTargetTime().AsTime(), baseTime.Add(fastForwardDuration), 1*time.Second) + }) - boundInfo := s.mutableState.executionInfo.TimeSkippingInfo.GetCurrentElapsedDurationBound() - s.Require().NotNil(boundInfo) - s.Equal(eventID, boundInfo.GetSourceEventId()) - s.False(boundInfo.GetHasReached()) - s.GreaterOrEqual(boundInfo.GetTargetTime().AsTime(), before.Add(maxLapse)) - - task := findBoundTask(s.mutableState) - s.Require().NotNil(task) - s.Equal(eventID, task.EventID) - s.Equal(boundInfo.GetTargetTime().AsTime(), task.VisibilityTimestamp, - "task VisibilityTimestamp must equal stored bound TargetTime (single ms.Now() read)") + s.Run("FastForward_WithPropagatedTargetTime", func() { + fixed := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC) + eventID := int64(1) + propagatedTarget := fixed.Add(2 * time.Hour) + s.mutableState.timeSource = clock.NewEventTimeSource().Update(fixed) + s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(3 * time.Hour)}, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + } + s.mutableState.applyFastForward(eventID, timestamppb.New(propagatedTarget)) + fastForward := s.mutableState.executionInfo.TimeSkippingInfo.GetFastForwardInfo() + s.Require().NotNil(fastForward) + s.Equal(propagatedTarget, fastForward.GetTargetTime().AsTime(), + "propagated target used directly, not recomputed from ff duration") }) s.Run("MaxElapsedDuration_NilDuration_NoOp", func() { + eventID := int64(1) s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: nil}, - }, + Config: &commonpb.TimeSkippingConfig{ + Enabled: true}, } - s.mutableState.applyTimeSkippingBound(eventID) - - s.Nil(s.mutableState.executionInfo.TimeSkippingInfo.GetCurrentElapsedDurationBound()) - s.Nil(findBoundTask(s.mutableState)) + s.mutableState.applyFastForward(eventID, nil) + s.Nil(s.mutableState.executionInfo.TimeSkippingInfo.GetFastForwardInfo()) }) - s.Run("NilBound_ClearsStaleElapsedBound", func() { + s.Run("FastForward_ClearsExistingInfo", func() { + eventID := int64(1) s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ + Config: &commonpb.TimeSkippingConfig{Enabled: false}, + FastForwardInfo: &persistencespb.FastForwardInfo{ TargetTime: timestamppb.New(s.mutableState.Now().Add(time.Hour)), SourceEventId: 7, }, } - s.mutableState.applyTimeSkippingBound(eventID) - - s.Nil(s.mutableState.executionInfo.TimeSkippingInfo.GetCurrentElapsedDurationBound()) - s.Nil(findBoundTask(s.mutableState)) + s.mutableState.applyFastForward(eventID, nil) + s.Nil(s.mutableState.executionInfo.TimeSkippingInfo.GetFastForwardInfo()) }) - s.Run("NilConfig_ReturnsSilently", func() { + s.Run("FastForward_OverridesExistingInfo", func() { + eventID := int64(1) + fixed := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC) s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: nil, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ - TargetTime: timestamppb.New(s.mutableState.Now().Add(time.Hour)), + Config: &commonpb.TimeSkippingConfig{Enabled: true, FastForward: durationpb.New(time.Hour)}, + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(fixed), SourceEventId: 7, }, } - s.mutableState.applyTimeSkippingBound(eventID) - - // Nil config short-circuits without touching state — preserves whatever was there. - s.NotNil(s.mutableState.executionInfo.TimeSkippingInfo.GetCurrentElapsedDurationBound()) - s.Nil(findBoundTask(s.mutableState)) - }) - - s.Run("EnabledFalse_SuppressesTaskEmission", func() { - s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: false, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(maxLapse)}, - }, - } - s.mutableState.applyTimeSkippingBound(eventID) - - s.Nil(s.mutableState.executionInfo.TimeSkippingInfo.GetCurrentElapsedDurationBound()) - s.Nil(findBoundTask(s.mutableState)) - }) - - s.Run("MaxElapsedDuration_NoOpWhenTargetTimeUnchanged", func() { - // Pin the time source so the seeded TargetTime exactly matches the value - // applyTimeSkippingBound will compute on this transaction. - fixed := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC) - s.mutableState.timeSource = clock.NewEventTimeSource().Update(fixed) - - target := fixed.Add(maxLapse) - s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(maxLapse)}, - }, - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ - TargetTime: timestamppb.New(target), - SourceEventId: 7, - }, - } - s.mutableState.applyTimeSkippingBound(eventID) - - // SourceEventId must NOT change — the no-op short-circuit protects no-op - // UpdateWorkflowExecutionOptions from invalidating still-pending wake-up tasks. - s.Equal(int64(7), s.mutableState.executionInfo.TimeSkippingInfo.GetCurrentElapsedDurationBound().GetSourceEventId()) - s.Nil(findBoundTask(s.mutableState)) + newTarget := s.mutableState.Now().Add(time.Hour) + s.mutableState.applyFastForward(eventID, nil) + s.WithinDuration(s.mutableState.executionInfo.TimeSkippingInfo.GetFastForwardInfo().GetTargetTime().AsTime(), newTarget, 1*time.Second) }) } -// TestInitTimeSkippingInfo verifies the time-shift block and the virtual-time bound -// computation in initTimeSkippingInfo (B1 fix). +// TestInitTimeSkippingInfo covers 3 basic scenarios this function is called. func (s *mutableStateSuite) TestInitTimeSkippingInfo() { - const ( - eventID = int64(11) - initialDur = 50 * time.Minute - maxLapse = time.Hour - ) - baseTime := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC) - s.Run("ZeroInitialSkip_TimestampsUnchanged", func() { - s.mutableState.timeSource = clock.NewEventTimeSource().Update(baseTime) - s.mutableState.executionInfo.StartTime = timestamppb.New(baseTime) - s.mutableState.executionInfo.ExecutionTime = timestamppb.New(baseTime) - s.mutableState.executionState.StartTime = timestamppb.New(baseTime) - runExp := baseTime.Add(24 * time.Hour) - s.mutableState.executionInfo.WorkflowRunExpirationTime = timestamppb.New(runExp) - - cfg := &workflowpb.TimeSkippingConfig{Enabled: true} - s.mutableState.initTimeSkippingInfo(cfg, durationpb.New(0), eventID) - - s.Equal(baseTime, s.mutableState.executionInfo.StartTime.AsTime()) - s.Equal(baseTime, s.mutableState.executionInfo.ExecutionTime.AsTime()) - s.Equal(baseTime, s.mutableState.executionState.StartTime.AsTime()) - s.Equal(runExp, s.mutableState.executionInfo.WorkflowRunExpirationTime.AsTime()) - }) - - s.Run("PositiveInitialSkip_NilTimestamps_NoPanic", func() { - s.mutableState.timeSource = clock.NewEventTimeSource().Update(baseTime) - s.mutableState.executionInfo.StartTime = nil - s.mutableState.executionInfo.ExecutionTime = nil - s.mutableState.executionState.StartTime = nil - s.mutableState.executionInfo.WorkflowRunExpirationTime = nil - s.mutableState.executionInfo.WorkflowExecutionExpirationTime = nil - - cfg := &workflowpb.TimeSkippingConfig{Enabled: true} + s.Run("InitWithNil_ForExecutionsWithoutTS", func() { + s.mutableState.timeSource = clock.NewEventTimeSource() + baseTime := s.mutableState.timeSource.Now() s.NotPanics(func() { - s.mutableState.initTimeSkippingInfo(cfg, durationpb.New(initialDur), eventID) + s.mutableState.initTimeSkippingInfo(nil, nil, 0) }) - s.Nil(s.mutableState.executionInfo.StartTime) - s.Nil(s.mutableState.executionInfo.ExecutionTime) + s.Nil(s.mutableState.executionInfo.TimeSkippingInfo) + s.Equal(baseTime, s.mutableState.Now()) }) - s.Run("PositiveInitialSkip_TimestampsShifted", func() { - s.mutableState.timeSource = clock.NewEventTimeSource().Update(baseTime) - s.mutableState.executionInfo.StartTime = timestamppb.New(baseTime) - s.mutableState.executionInfo.ExecutionTime = timestamppb.New(baseTime) - s.mutableState.executionState.StartTime = timestamppb.New(baseTime) - runExp := baseTime.Add(24 * time.Hour) - execExp := baseTime.Add(48 * time.Hour) - s.mutableState.executionInfo.WorkflowRunExpirationTime = timestamppb.New(runExp) - s.mutableState.executionInfo.WorkflowExecutionExpirationTime = timestamppb.New(execExp) + s.Run("InitWithConfigOnly_ForExecutionsWithTSStartedByUser", func() { + eventID := int64(1) + s.mutableState.timeSource = clock.NewEventTimeSource() + baseTime := s.mutableState.timeSource.Now() + cfg := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(3 * time.Hour)} - cfg := &workflowpb.TimeSkippingConfig{Enabled: true} - s.mutableState.initTimeSkippingInfo(cfg, durationpb.New(initialDur), eventID) - - s.Equal(baseTime.Add(initialDur), s.mutableState.executionInfo.StartTime.AsTime()) - s.Equal(baseTime.Add(initialDur), s.mutableState.executionInfo.ExecutionTime.AsTime()) - s.Equal(baseTime.Add(initialDur), s.mutableState.executionState.StartTime.AsTime()) - s.Equal(runExp.Add(initialDur), s.mutableState.executionInfo.WorkflowRunExpirationTime.AsTime()) - s.Equal(execExp.Add(initialDur), s.mutableState.executionInfo.WorkflowExecutionExpirationTime.AsTime()) + s.mutableState.initTimeSkippingInfo(cfg, nil, eventID) + s.Equal(baseTime, s.mutableState.Now()) + tsi := s.mutableState.executionInfo.GetTimeSkippingInfo() + s.Require().NotNil(tsi) + s.True(proto.Equal(cfg, tsi.GetConfig())) + s.Require().NotNil(tsi.GetFastForwardInfo()) + s.Require().Nil(tsi.GetAccumulatedSkippedDuration()) + // timestamppb translates to UTC time + s.Require().Equal(baseTime.Add(3*time.Hour).UTC(), + tsi.GetFastForwardInfo().GetTargetTime().AsTime()) }) - s.Run("VirtualTimeBound_TargetUsesAccumulatedSkip", func() { - // Verifies B1 fix: applyTimeSkippingBound runs after the time source is wrapped - // and the time-shift block, so ms.Now() reflects virtual time. The bound's - // TargetTime must equal wall_now + accum + maxElapsed (not wall_now + maxElapsed). - s.mutableState.timeSource = clock.NewEventTimeSource().Update(baseTime) - s.mutableState.executionInfo.StartTime = timestamppb.New(baseTime) - s.mutableState.executionInfo.ExecutionTime = timestamppb.New(baseTime) - s.mutableState.executionState.StartTime = timestamppb.New(baseTime) + s.Run("InitWithPropagation_ForExecutionsWithTSStartedByPropagation", func() { + s.mutableState.timeSource = clock.NewEventTimeSource() - cfg := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(maxLapse)}, + // all local time + baseTime := s.mutableState.timeSource.Now() + targetTime := baseTime.Add(3 * time.Hour) + + hasSkipped := 2 * time.Hour + fastForward := 3 * time.Hour + eventID := int64(1) + + cfg := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(fastForward), + DisableChildPropagation: true, } - s.mutableState.initTimeSkippingInfo(cfg, durationpb.New(initialDur), eventID) + propagation := &commonpb.TimeSkippingStatePropagation{ + InitialSkippedDuration: durationpb.New(hasSkipped), + FastForwardTargetTime: timestamppb.New(targetTime), + } + s.mutableState.initTimeSkippingInfo(cfg, propagation, eventID) - boundInfo := s.mutableState.executionInfo.TimeSkippingInfo.GetCurrentElapsedDurationBound() - s.Require().NotNil(boundInfo) - // Virtual now = baseTime + initialDur, target = virtual_now + maxLapse. - s.Equal(baseTime.Add(initialDur).Add(maxLapse), boundInfo.GetTargetTime().AsTime()) - s.Equal(eventID, boundInfo.GetSourceEventId()) + tsi := s.mutableState.executionInfo.GetTimeSkippingInfo() + s.Require().NotNil(tsi) + s.Equal(baseTime.Add(hasSkipped), s.mutableState.Now()) + s.Equal(hasSkipped, tsi.GetAccumulatedSkippedDuration().AsDuration()) + s.Equal(targetTime.UTC(), + tsi.GetFastForwardInfo().GetTargetTime().AsTime()) }) } -// TestTimeSkippingPreservesUnboundedExpiration locks down the invariant that -// time skipping never turns an unbounded workflow (no execution / run timeout) -// into a bounded one. The only write path under skip that touches -// WorkflowExecutionExpirationTime / WorkflowRunExpirationTime is -// shiftWorkflowTimes (driven by initTimeSkippingInfo for propagated runs), -// which gates per-field on timeNotSet — so nil/zero inputs must round-trip -// unchanged. RefreshExpirationTimeoutTask has its own pre-skipping guards -// (weTimeout > 0 / wrTimeout != 0) that are independent of time-skipping -// state. The companion read path ToRealTime is covered by -// TestToRealTime/ZeroInputReturnedUnchangedEvenUnderSkip. -func (s *mutableStateSuite) TestTimeSkippingPreservesUnboundedExpiration() { - const initialDur = 50 * time.Minute - baseTime := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC) - - s.Run("NilExpiration_StaysNil_AfterPositiveInitialSkip", func() { - s.mutableState.timeSource = clock.NewEventTimeSource().Update(baseTime) - s.mutableState.executionInfo.StartTime = timestamppb.New(baseTime) - s.mutableState.executionInfo.ExecutionTime = timestamppb.New(baseTime) - s.mutableState.executionState.StartTime = timestamppb.New(baseTime) - s.mutableState.executionInfo.WorkflowRunExpirationTime = nil - s.mutableState.executionInfo.WorkflowExecutionExpirationTime = nil - - cfg := &workflowpb.TimeSkippingConfig{Enabled: true} - s.mutableState.initTimeSkippingInfo(cfg, durationpb.New(initialDur), 1) - - s.Nil(s.mutableState.executionInfo.WorkflowRunExpirationTime) - s.Nil(s.mutableState.executionInfo.WorkflowExecutionExpirationTime) - }) - - s.Run("ZeroExpiration_StaysZero_AfterPositiveInitialSkip", func() { - // A proto-wrapped zero time is the second flavor of "not set" — timeNotSet - // treats it the same as nil, so shiftWorkflowTimes must leave it at zero. - s.mutableState.timeSource = clock.NewEventTimeSource().Update(baseTime) - s.mutableState.executionInfo.StartTime = timestamppb.New(baseTime) - s.mutableState.executionInfo.ExecutionTime = timestamppb.New(baseTime) - s.mutableState.executionState.StartTime = timestamppb.New(baseTime) - s.mutableState.executionInfo.WorkflowRunExpirationTime = timestamppb.New(time.Time{}) - s.mutableState.executionInfo.WorkflowExecutionExpirationTime = timestamppb.New(time.Time{}) - - cfg := &workflowpb.TimeSkippingConfig{Enabled: true} - s.mutableState.initTimeSkippingInfo(cfg, durationpb.New(initialDur), 1) - - s.True(s.mutableState.executionInfo.WorkflowRunExpirationTime.AsTime().IsZero()) - s.True(s.mutableState.executionInfo.WorkflowExecutionExpirationTime.AsTime().IsZero()) - }) - - s.Run("ShiftWorkflowTimes_DirectCall_NilAndZero_NotShifted", func() { - // Directly exercise shiftWorkflowTimes to pin the contract independent of - // the initTimeSkippingInfo wrapper. accum > 0 must be a no-op on - // unset expiration fields regardless of how it was invoked. - s.mutableState.executionInfo.WorkflowRunExpirationTime = nil - s.mutableState.executionInfo.WorkflowExecutionExpirationTime = timestamppb.New(time.Time{}) - s.mutableState.shiftWorkflowTimes(durationpb.New(2 * time.Hour)) - s.Nil(s.mutableState.executionInfo.WorkflowRunExpirationTime) - s.True(s.mutableState.executionInfo.WorkflowExecutionExpirationTime.AsTime().IsZero()) - }) - -} - -// TestUpdateTimeSkippingInfo verifies updateTimeSkippingInfo replaces Config but -// preserves AccumulatedSkippedDuration, and re-runs applyTimeSkippingBound. func (s *mutableStateSuite) TestUpdateTimeSkippingInfo() { - const eventID = int64(33) - prevAccum := 15 * time.Minute + s.Run("UpdateTimeSkippingInfo_EnableTS", func() { + s.mutableState.timeSource = clock.NewEventTimeSource() + baseTime := s.mutableState.timeSource.Now() + currentTSI := persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: false, + }, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + } + s.mutableState.executionInfo.TimeSkippingInfo = ¤tTSI - s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, - }, - AccumulatedSkippedDuration: durationpb.New(prevAccum), - } + // new config + newConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(2 * time.Hour), + DisableChildPropagation: true, + } + newEventID := int64(8) + s.mutableState.updateTimeSkippingInfo(newConfig, newEventID) + newTSI := s.mutableState.executionInfo.GetTimeSkippingInfo() - newConfig := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(2 * time.Hour)}, - } - s.mutableState.updateTimeSkippingInfo(newConfig, eventID) + s.Require().NotNil(newTSI) + s.True(proto.Equal(newConfig, newTSI.GetConfig())) + s.Require().NotNil(newTSI.GetFastForwardInfo()) + s.Equal(newEventID, newTSI.GetFastForwardInfo().GetSourceEventId()) + s.Equal(baseTime.Add(2*time.Hour).UTC(), newTSI.GetFastForwardInfo().GetTargetTime().AsTime()) + s.False(newTSI.GetFastForwardInfo().GetHasReached()) + s.Equal(time.Hour, newTSI.GetAccumulatedSkippedDuration().AsDuration()) + }) + + s.Run("UpdateTimeSkippingInfo_OverrideFFThenTurnOff", func() { + s.mutableState.timeSource = clock.NewEventTimeSource() + baseTime := s.mutableState.timeSource.Now() + currentTSI := persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), + }, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(baseTime.Add(time.Hour)), + SourceEventId: 7, + HasReached: false, + }, + } + s.mutableState.executionInfo.TimeSkippingInfo = ¤tTSI + + // update with new config with a new FF + tsc2 := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(2 * time.Hour), + } + newEventID := int64(8) + s.mutableState.updateTimeSkippingInfo(tsc2, newEventID) + tsc2TSI := s.mutableState.executionInfo.GetTimeSkippingInfo() + + s.Require().NotNil(tsc2TSI) + s.True(proto.Equal(tsc2, tsc2TSI.GetConfig())) + s.Require().NotNil(tsc2TSI.GetFastForwardInfo()) + s.Equal(newEventID, tsc2TSI.GetFastForwardInfo().GetSourceEventId()) + s.Equal(baseTime.Add(2*time.Hour).UTC(), tsc2TSI.GetFastForwardInfo().GetTargetTime().AsTime()) + s.False(tsc2TSI.GetFastForwardInfo().GetHasReached()) + s.Equal(time.Hour, tsc2TSI.GetAccumulatedSkippedDuration().AsDuration()) + + // disable the time skipping + tsc3 := &commonpb.TimeSkippingConfig{ + Enabled: false, + } + s.mutableState.updateTimeSkippingInfo(tsc3, newEventID) + tsc3TSI := s.mutableState.executionInfo.GetTimeSkippingInfo() + s.Require().NotNil(tsc3TSI) + s.True(proto.Equal(tsc3, tsc3TSI.GetConfig())) + s.Nil(tsc3TSI.GetFastForwardInfo()) + s.Equal(time.Hour, tsc3TSI.GetAccumulatedSkippedDuration().AsDuration()) + + }) - tsi := s.mutableState.executionInfo.GetTimeSkippingInfo() - s.True(proto.Equal(newConfig, tsi.GetConfig())) - s.Require().NotNil(tsi.GetAccumulatedSkippedDuration()) - s.Equal(prevAccum, tsi.GetAccumulatedSkippedDuration().AsDuration(), - "AccumulatedSkippedDuration must be preserved across config update") - // applyTimeSkippingBound was invoked: a new MaxElapsedDuration bound is installed. - boundInfo := tsi.GetCurrentElapsedDurationBound() - s.Require().NotNil(boundInfo) - s.Equal(eventID, boundInfo.GetSourceEventId()) } // TestCalculateTimeSkippingTransition exercises the full candidate-selection @@ -8469,7 +8408,7 @@ func (s *mutableStateSuite) TestCalculateTimeSkippingTransition() { s.mutableState.pendingTimerInfoIDs = make(map[string]*persistencespb.TimerInfo) s.mutableState.pendingActivityInfoIDs = make(map[int64]*persistencespb.ActivityInfo) s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } // Default: workflow has had a workflow task, so the backoff branch is skipped. s.mutableState.executionInfo.StartTime = timestamppb.New(baseTime) @@ -8492,7 +8431,7 @@ func (s *mutableStateSuite) TestCalculateTimeSkippingTransition() { t, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) s.Equal(t1, t.targetTime) - s.False(t.disabledAfterBound) + s.False(t.disabledAfterFastForward) }) s.Run("TwoUserTimers_TargetIsEarliest", func() { @@ -8505,35 +8444,34 @@ func (s *mutableStateSuite) TestCalculateTimeSkippingTransition() { tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) s.Equal(t1, tr.targetTime) - s.False(tr.disabledAfterBound) + s.False(tr.disabledAfterFastForward) }) - s.Run("UserTimer_PlusEarlierElapsedBound_TargetIsBound", func() { + s.Run("UserTimer_PlusEarlierFastForward_TargetIsFastForward", func() { resetMS() t1 := baseTime.Add(3 * time.Hour) - boundTarget := baseTime.Add(time.Hour) + fastForwardTarget := baseTime.Add(time.Hour) addTimer("t1", t1) - s.mutableState.executionInfo.TimeSkippingInfo.Config.Bound = - &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)} - s.mutableState.executionInfo.TimeSkippingInfo.CurrentElapsedDurationBound = - &persistencespb.TimeSkippingBoundInfo{TargetTime: timestamppb.New(boundTarget)} + s.mutableState.executionInfo.TimeSkippingInfo.Config.FastForward = durationpb.New(time.Hour) + s.mutableState.executionInfo.TimeSkippingInfo.FastForwardInfo = + &persistencespb.FastForwardInfo{TargetTime: timestamppb.New(fastForwardTarget)} tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) - s.Equal(boundTarget, tr.targetTime) - s.True(tr.disabledAfterBound) + s.Equal(fastForwardTarget, tr.targetTime) + s.True(tr.disabledAfterFastForward) }) - s.Run("MaxElapsed_NilCurrentBound_InternalError", func() { + s.Run("ZeroFastForward_NilTarget_NoErrorNoCandidate", func() { resetMS() - s.mutableState.executionInfo.TimeSkippingInfo.Config.Bound = - &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)} - // CurrentElapsedDurationBound deliberately not set. + // A zero max_elapsed_duration is treated as no fast-forward: it contributes no candidate + // and must NOT trip the nil-fast-forward corruption check. + s.mutableState.executionInfo.TimeSkippingInfo.Config.FastForward = durationpb.New(0) + // FastForward persistence deliberately not set. - _, err := s.mutableState.calculateTimeSkippingTransition() - s.Require().Error(err) - var internalErr *serviceerror.Internal - s.Require().ErrorAs(err, &internalErr) + tr, err := s.mutableState.calculateTimeSkippingTransition() + s.Require().NoError(err) + s.False(tr.isValid()) }) s.Run("Backoff_NotChildAndExecutionTimeFuture_IsCandidate", func() { @@ -8548,7 +8486,7 @@ func (s *mutableStateSuite) TestCalculateTimeSkippingTransition() { tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) s.Equal(execTime, tr.targetTime) - s.False(tr.disabledAfterBound) + s.False(tr.disabledAfterFastForward) }) s.Run("Backoff_ChildWFCase_NotCandidate", func() { @@ -8598,7 +8536,7 @@ func (s *mutableStateSuite) TestCalculateTimeSkippingTransition() { tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) s.Equal(schedTime, tr.targetTime) - s.False(tr.disabledAfterBound) + s.False(tr.disabledAfterFastForward) }) s.Run("TwoActivitiesInBackoff_TargetIsEarliest", func() { @@ -8636,69 +8574,68 @@ func (s *mutableStateSuite) TestCalculateTimeSkippingTransition() { // Universal cap: skip target must not exceed the run/execution timeout. // MaxElapsedDuration's value is irrelevant to calculateTimeSkippingTransition; - // only CurrentElapsedDurationBound.TargetTime is read. We use a large dummy - // duration solely to configure the bound type. - const largeBound = 24 * time.Hour - setBoundAt := func(target time.Time) { - s.mutableState.executionInfo.TimeSkippingInfo.Config.Bound = - &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(largeBound)} - s.mutableState.executionInfo.TimeSkippingInfo.CurrentElapsedDurationBound = - &persistencespb.TimeSkippingBoundInfo{TargetTime: timestamppb.New(target)} + // only FastForward.TargetTime is read. We use a large dummy + // duration solely to configure the fast-forward. + const largeFastForward = 24 * time.Hour + setFastForwardAt := func(target time.Time) { + s.mutableState.executionInfo.TimeSkippingInfo.Config.FastForward = durationpb.New(largeFastForward) + s.mutableState.executionInfo.TimeSkippingInfo.FastForwardInfo = + &persistencespb.FastForwardInfo{TargetTime: timestamppb.New(target)} } - s.Run("Bound_LargerThanRunTimeout_CappedAtRunTimeout", func() { + s.Run("FastForward_LargerThanRunTimeout_CappedAtRunTimeout", func() { resetMS() runExpiry := baseTime.Add(30 * time.Minute) - boundTarget := baseTime.Add(2 * time.Hour) // bound > run timeout - setBoundAt(boundTarget) + fastForwardTarget := baseTime.Add(2 * time.Hour) // fast-forward > run timeout + setFastForwardAt(fastForwardTarget) s.mutableState.executionInfo.WorkflowRunExpirationTime = timestamppb.New(runExpiry) tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) s.Equal(runExpiry, tr.targetTime, "skip must be capped at run timeout") - s.False(tr.disabledAfterBound, "cap fires before bound; bound must not be marked reached") + s.False(tr.disabledAfterFastForward, "cap fires before fast-forward; fast-forward must not be marked reached") }) - s.Run("Bound_SmallerThanRunTimeout_NoCap_TargetIsBound", func() { + s.Run("FastForward_SmallerThanRunTimeout_NoCap_TargetIsFastForward", func() { resetMS() - boundTarget := baseTime.Add(30 * time.Minute) - runExpiry := baseTime.Add(2 * time.Hour) // run timeout > bound, no cap needed - setBoundAt(boundTarget) + fastForwardTarget := baseTime.Add(30 * time.Minute) + runExpiry := baseTime.Add(2 * time.Hour) // run timeout > fast-forward, no cap needed + setFastForwardAt(fastForwardTarget) s.mutableState.executionInfo.WorkflowRunExpirationTime = timestamppb.New(runExpiry) tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) - s.Equal(boundTarget, tr.targetTime, "bound is minimum; no cap applies") - s.True(tr.disabledAfterBound, "bound fires before run timeout; bound must be marked reached") + s.Equal(fastForwardTarget, tr.targetTime, "fast-forward is minimum; no cap applies") + s.True(tr.disabledAfterFastForward, "fast-forward fires before run timeout; fast-forward must be marked reached") }) - s.Run("Bound_LargerThanExecTimeout_NoRunTimeout_CappedAtExecTimeout", func() { + s.Run("FastForward_LargerThanExecTimeout_NoRunTimeout_CappedAtExecTimeout", func() { resetMS() execExpiry := baseTime.Add(30 * time.Minute) - boundTarget := baseTime.Add(2 * time.Hour) // bound > execution timeout - setBoundAt(boundTarget) + fastForwardTarget := baseTime.Add(2 * time.Hour) // fast-forward > execution timeout + setFastForwardAt(fastForwardTarget) // No WorkflowRunExpirationTime; only execution timeout. s.mutableState.executionInfo.WorkflowExecutionExpirationTime = timestamppb.New(execExpiry) tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) s.Equal(execExpiry, tr.targetTime, "skip must be capped at execution timeout") - s.False(tr.disabledAfterBound, "cap fires before bound; bound must not be marked reached") + s.False(tr.disabledAfterFastForward, "cap fires before fast-forward; fast-forward must not be marked reached") }) - s.Run("Bound_ZeroRunTimeout_TreatedAsNoTimeout_NoCap", func() { + s.Run("FastForward_ZeroRunTimeout_TreatedAsNoTimeout_NoCap", func() { // A zero-value timestamp means "no timeout configured". The cap must not - // fire: the bound target should be the skip destination unchanged. + // fire: the fast-forward target should be the skip destination unchanged. resetMS() - boundTarget := baseTime.Add(time.Hour) - setBoundAt(boundTarget) + fastForwardTarget := baseTime.Add(time.Hour) + setFastForwardAt(fastForwardTarget) s.mutableState.executionInfo.WorkflowRunExpirationTime = timestamppb.New(time.Time{}) s.mutableState.executionInfo.WorkflowExecutionExpirationTime = timestamppb.New(time.Time{}) tr, err := s.mutableState.calculateTimeSkippingTransition() s.Require().NoError(err) - s.Equal(boundTarget, tr.targetTime, "zero timeout must not cap the skip") - s.True(tr.disabledAfterBound) + s.Equal(fastForwardTarget, tr.targetTime, "zero timeout must not cap the skip") + s.True(tr.disabledAfterFastForward) }) } @@ -8712,21 +8649,21 @@ func (s *mutableStateSuite) TestToRealTime() { }) s.Run("IdentityWhenTimeSkippingInfoWithNilAccumulatedSkippedDuration", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, } s.Equal(virtualTime, s.mutableState.ToRealTime(virtualTime)) }) s.Run("SubtractsAccumulatedSkip", func() { accum := time.Hour s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: false}, + Config: &commonpb.TimeSkippingConfig{Enabled: false}, AccumulatedSkippedDuration: durationpb.New(accum), } s.Equal(virtualTime.Add(-accum), s.mutableState.ToRealTime(virtualTime)) }) s.Run("ZeroInputReturnedUnchangedEvenUnderSkip", func() { s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, AccumulatedSkippedDuration: durationpb.New(time.Hour), } s.True(s.mutableState.ToRealTime(time.Time{}).IsZero()) @@ -8835,7 +8772,7 @@ func (s *mutableStateSuite) TestMutableStateImpl_Now() { const skip = time.Hour s.mutableState.timeSource = fixedTimeSource() s.mutableState.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: false}, + Config: &commonpb.TimeSkippingConfig{Enabled: false}, AccumulatedSkippedDuration: durationpb.New(skip), } s.mutableState.wrapTimeSourceWithTimeSkipping() diff --git a/service/history/workflow/mutable_state_rebuilder_test.go b/service/history/workflow/mutable_state_rebuilder_test.go index 78e9bed76e..e134c908ef 100644 --- a/service/history/workflow/mutable_state_rebuilder_test.go +++ b/service/history/workflow/mutable_state_rebuilder_test.go @@ -13,7 +13,6 @@ import ( enumspb "go.temporal.io/api/enums/v1" historypb "go.temporal.io/api/history/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" - workflowpb "go.temporal.io/api/workflow/v1" enumsspb "go.temporal.io/server/api/enums/v1" historyspb "go.temporal.io/server/api/history/v1" persistencespb "go.temporal.io/server/api/persistence/v1" @@ -2141,7 +2140,7 @@ func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionOptionsUpd EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED, Attributes: &historypb.HistoryEvent_WorkflowExecutionOptionsUpdatedEventAttributes{ WorkflowExecutionOptionsUpdatedEventAttributes: &historypb.WorkflowExecutionOptionsUpdatedEventAttributes{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{ + TimeSkippingConfig: &commonpb.TimeSkippingConfig{ Enabled: true, }, }, diff --git a/service/history/workflow/retry.go b/service/history/workflow/retry.go index 54dc4b0174..b5ae88d657 100644 --- a/service/history/workflow/retry.go +++ b/service/history/workflow/retry.go @@ -303,9 +303,8 @@ func SetupNewWorkflowForRetryOrCron( VersioningOverride: pinnedOverride, } - // Retry and Cron inherit the TimeSkippingConfig snapshot from the previous - // run's WorkflowExecutionStarted event verbatim. - createRequest.TimeSkippingConfig = startAttr.GetTimeSkippingConfig() + tsc, stateProp := propagateTimeSkippingToNextRun(previousExecutionInfo) + createRequest.TimeSkippingConfig = tsc attempt := int32(1) if initiator == enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY { @@ -340,9 +339,8 @@ func SetupNewWorkflowForRetryOrCron( InheritedAutoUpgradeInfo: inheritedAutoUpgradeInfo, // For retries, pass through the declined value from the started event directly. DeclinedTargetVersionUpgrade: startAttr.GetDeclinedTargetVersionUpgrade(), - // Retry and Cron inherit InitialSkippedDuration from the previous run's - // WorkflowExecutionStarted event verbatim — same semantics as TimeSkippingConfig above. - InitialSkippedDuration: startAttr.GetInitialSkippedDuration(), + // Carry the previous run's accumulated skip and fast-forward target forward. + TimeSkippingStatePropagation: stateProp, } workflowTimeoutTime := timestamp.TimeValue(previousExecutionInfo.WorkflowExecutionExpirationTime) if !workflowTimeoutTime.IsZero() { diff --git a/service/history/workflow/task_generator.go b/service/history/workflow/task_generator.go index 8e4725670d..f30c96a7c0 100644 --- a/service/history/workflow/task_generator.go +++ b/service/history/workflow/task_generator.go @@ -1091,17 +1091,17 @@ func (r *TaskGeneratorImpl) RegenerateTimerTasksForTimeSkipping() error { }) } - // (3) elapsed-duration bound timer — regenerate when configured so its real-time + // (3) fast-forward timer — regenerate when configured so its real-time // VisibilityTimestamp tracks the new accumulated skip. tsi := r.mutableState.GetExecutionInfo().GetTimeSkippingInfo() if tsi.GetConfig().GetEnabled() { - boundInfo := tsi.GetCurrentElapsedDurationBound() - if boundInfo != nil && !boundInfo.GetHasReached() { + fastForward := tsi.GetFastForwardInfo() + if fastForward != nil && !fastForward.GetHasReached() { r.mutableState.AddTasks(&tasks.TimeSkippingTimerTask{ // TaskID is set by shard WorkflowKey: r.mutableState.GetWorkflowKey(), - VisibilityTimestamp: boundInfo.GetTargetTime().AsTime(), - EventID: boundInfo.GetSourceEventId(), + VisibilityTimestamp: fastForward.GetTargetTime().AsTime(), + EventID: fastForward.GetSourceEventId(), }) } } diff --git a/service/history/workflow/task_generator_test.go b/service/history/workflow/task_generator_test.go index 3991c666d0..ea1152eec3 100644 --- a/service/history/workflow/task_generator_test.go +++ b/service/history/workflow/task_generator_test.go @@ -9,10 +9,10 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" historypb "go.temporal.io/api/history/v1" workerpb "go.temporal.io/api/worker/v1" - workflowpb "go.temporal.io/api/workflow/v1" enumsspb "go.temporal.io/server/api/enums/v1" historyspb "go.temporal.io/server/api/history/v1" persistencespb "go.temporal.io/server/api/persistence/v1" @@ -1425,13 +1425,13 @@ func TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_ExecutionTimers(t } } -// TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_BoundTimer covers step (3) -// of RegenerateTimerTasksForTimeSkipping: the elapsed-bound wake-up task regeneration. -func TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_BoundTimer(t *testing.T) { +// TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_FastForwardTimer covers step (3) +// of RegenerateTimerTasksForTimeSkipping: the fast-forward wake-up task regeneration. +func TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_FastForwardTimer(t *testing.T) { t.Parallel() now := time.Now().UTC() - boundTarget := now.Add(2 * time.Hour) + fastForwardTarget := now.Add(2 * time.Hour) type wantTask struct { visibilityTimestamp time.Time @@ -1439,37 +1439,35 @@ func TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_BoundTimer(t *tes } for _, tc := range []struct { - name string - tsi *persistencespb.TimeSkippingInfo - wantBoundTask *wantTask - wantUserTimerLen int + name string + tsi *persistencespb.TimeSkippingInfo + wantFastForwardTask *wantTask + wantUserTimerLen int }{ { - name: "bound configured and unreached emits task", + name: "fast-forward configured and unreached emits task", tsi: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(2 * time.Hour)}, - }, + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(2 * time.Hour)}, AccumulatedSkippedDuration: durationpb.New(time.Hour), - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ - TargetTime: timestamppb.New(boundTarget), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(fastForwardTarget), SourceEventId: 7, HasReached: false, }, }, - wantBoundTask: &wantTask{visibilityTimestamp: boundTarget, eventID: 7}, + wantFastForwardTask: &wantTask{visibilityTimestamp: fastForwardTarget, eventID: 7}, }, { name: "HasReached=true skips task emission", tsi: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(2 * time.Hour)}, - }, + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(2 * time.Hour)}, AccumulatedSkippedDuration: durationpb.New(time.Hour), - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ - TargetTime: timestamppb.New(boundTarget), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(fastForwardTarget), SourceEventId: 7, HasReached: true, }, @@ -1478,22 +1476,21 @@ func TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_BoundTimer(t *tes { name: "Enabled=false skips task emission", tsi: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{ - Enabled: false, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(2 * time.Hour)}, - }, + Config: &commonpb.TimeSkippingConfig{ + Enabled: false, + FastForward: durationpb.New(2 * time.Hour)}, AccumulatedSkippedDuration: durationpb.New(time.Hour), - CurrentElapsedDurationBound: &persistencespb.TimeSkippingBoundInfo{ - TargetTime: timestamppb.New(boundTarget), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(fastForwardTarget), SourceEventId: 7, HasReached: false, }, }, }, { - name: "no bound info does not emit bound task", + name: "no fast-forward info does not emit fast-forward task", tsi: &persistencespb.TimeSkippingInfo{ - Config: &workflowpb.TimeSkippingConfig{Enabled: true}, + Config: &commonpb.TimeSkippingConfig{Enabled: true}, AccumulatedSkippedDuration: durationpb.New(time.Hour), }, }, @@ -1522,23 +1519,23 @@ func TestTaskGeneratorImpl_RegenerateTimerTasksForTimeSkipping_BoundTimer(t *tes taskGenerator := NewTaskGenerator(nil, mutableState, &configs.Config{}, nil, log.NewTestLogger()) require.NoError(t, taskGenerator.RegenerateTimerTasksForTimeSkipping()) - var boundTasks []*tasks.TimeSkippingTimerTask + var fastForwardTasks []*tasks.TimeSkippingTimerTask for _, task := range captured { if bt, ok := task.(*tasks.TimeSkippingTimerTask); ok { - boundTasks = append(boundTasks, bt) + fastForwardTasks = append(fastForwardTasks, bt) } } - if tc.wantBoundTask == nil { - require.Empty(t, boundTasks) + if tc.wantFastForwardTask == nil { + require.Empty(t, fastForwardTasks) return } - require.Len(t, boundTasks, 1) - bt := boundTasks[0] + require.Len(t, fastForwardTasks, 1) + bt := fastForwardTasks[0] require.Equal(t, tests.WorkflowKey, bt.WorkflowKey) - require.Equal(t, tc.wantBoundTask.visibilityTimestamp, bt.VisibilityTimestamp) - require.Equal(t, tc.wantBoundTask.eventID, bt.EventID) + require.Equal(t, tc.wantFastForwardTask.visibilityTimestamp, bt.VisibilityTimestamp) + require.Equal(t, tc.wantFastForwardTask.eventID, bt.EventID) require.Equal(t, int64(0), bt.TaskID, "TaskID must be zero (set by shard)") }) } diff --git a/service/history/workflow/timeskipping.go b/service/history/workflow/timeskipping.go new file mode 100644 index 0000000000..a99248fd01 --- /dev/null +++ b/service/history/workflow/timeskipping.go @@ -0,0 +1,62 @@ +package workflow + +import ( + "time" + + commonpb "go.temporal.io/api/common/v1" + persistencespb "go.temporal.io/server/api/persistence/v1" + "go.temporal.io/server/common" + "google.golang.org/protobuf/types/known/durationpb" +) + +// propagateTimeSkippingToNextRun propagates both time skipping config and state to the next run in +// the chain (CaN, retry, cron). The config is deep-cloned so the next run can mutate it without +// affecting the source. +func propagateTimeSkippingToNextRun( + source *persistencespb.WorkflowExecutionInfo, +) (*commonpb.TimeSkippingConfig, *commonpb.TimeSkippingStatePropagation) { + previousTSC := source.GetTimeSkippingInfo().GetConfig() + + // if disabled, we just return nil for the new TSC + var newTSC *commonpb.TimeSkippingConfig + if previousTSC.GetEnabled() { + newTSC = common.CloneProto(previousTSC) + } + + stateProp := &commonpb.TimeSkippingStatePropagation{ + InitialSkippedDuration: durationpb.New(accumulatedSkippedDuration(source)), + } + if ff := source.GetTimeSkippingInfo().GetFastForwardInfo(); ff != nil && !ff.GetHasReached() { + stateProp.FastForwardTargetTime = ff.GetTargetTime() + } + return newTSC, stateProp +} + +// propagateTimeSkippingToChild makes sure the start time of the child workflow execution +// is shifted forward by the accumulated skipped duration. +// FastForward is never propagated to children. +func propagateTimeSkippingToChild( + source *persistencespb.WorkflowExecutionInfo, +) (*commonpb.TimeSkippingConfig, *commonpb.TimeSkippingStatePropagation) { + accum := accumulatedSkippedDuration(source) + var stateProp *commonpb.TimeSkippingStatePropagation + if accum > 0 { + stateProp = &commonpb.TimeSkippingStatePropagation{ + InitialSkippedDuration: durationpb.New(accum), + } + } + + enabled := source.GetTimeSkippingInfo().GetConfig().GetEnabled() + disableChildPropagation := source.GetTimeSkippingInfo().GetConfig().GetDisableChildPropagation() + if !enabled || disableChildPropagation { + return nil, stateProp + } + + return &commonpb.TimeSkippingConfig{ + Enabled: enabled, + }, stateProp +} + +func accumulatedSkippedDuration(source *persistencespb.WorkflowExecutionInfo) time.Duration { + return source.GetTimeSkippingInfo().GetAccumulatedSkippedDuration().AsDuration() +} diff --git a/service/history/workflow/timeskipping_test.go b/service/history/workflow/timeskipping_test.go new file mode 100644 index 0000000000..9e1c8efc4c --- /dev/null +++ b/service/history/workflow/timeskipping_test.go @@ -0,0 +1,154 @@ +package workflow + +import ( + "time" + + commonpb "go.temporal.io/api/common/v1" + persistencespb "go.temporal.io/server/api/persistence/v1" + "go.temporal.io/server/common/clock" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func (s *mutableStateSuite) TestPropagateTimeSkippingToNextRun_FastForwardInfo() { + fixed := time.Date(2027, 1, 1, 12, 0, 0, 0, time.UTC) + fixedTS := timestamppb.New(fixed) + + s.Run("FastForwardNotYetReached_TargetTimePropagated", func() { + src := &persistencespb.WorkflowExecutionInfo{ + TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(3 * time.Hour), + }, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: fixedTS, + HasReached: false, + }, + }, + } + tsc, stateProp := propagateTimeSkippingToNextRun(src) + s.Require().NotNil(stateProp.GetFastForwardTargetTime()) + s.Equal(fixed, stateProp.GetFastForwardTargetTime().AsTime()) + s.Require().NotNil(tsc) + s.True(proto.Equal(tsc, src.GetTimeSkippingInfo().GetConfig())) + }) + + s.Run("FastForwardAlreadyReached_TargetTimeOmitted", func() { + src := &persistencespb.WorkflowExecutionInfo{ + TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: false, + FastForward: durationpb.New(3 * time.Hour), + }, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: fixedTS, + HasReached: true, + }, + }, + } + tsc, stateProp := propagateTimeSkippingToNextRun(src) + s.Nil(tsc) + s.False(tsc.GetEnabled(), "disabled config must be propagated to the next run") + s.NotNil(stateProp) + s.Nil(stateProp.GetFastForwardTargetTime(), "consumed fast-forward must not be re-propagated") + s.Equal(time.Hour, stateProp.GetInitialSkippedDuration().AsDuration()) + }) + + s.Run("disabled config still propagates virtual time", func() { + src := &persistencespb.WorkflowExecutionInfo{ + TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: false, + FastForward: durationpb.New(3 * time.Hour), + }, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(s.mutableState.timeSource.Now().Add(3 * time.Hour)), + HasReached: true, + }, + }, + } + tsc, stateProp := propagateTimeSkippingToNextRun(src) + s.Nil(tsc) + s.Nil(stateProp.GetFastForwardTargetTime()) + s.Equal(time.Hour, stateProp.GetInitialSkippedDuration().AsDuration()) + }) + + s.Run("nil config still propagates virtual time", func() { + src := &persistencespb.WorkflowExecutionInfo{ + TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ + Config: nil, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + }, + } + tsc, stateProp := propagateTimeSkippingToNextRun(src) + s.Nil(tsc) + s.Nil(stateProp.GetFastForwardTargetTime()) + s.Equal(time.Hour, stateProp.GetInitialSkippedDuration().AsDuration()) + }) +} + +func (s *mutableStateSuite) TestSnapshotTimeSkippingInfo_ForChildWorkflows() { + newSource := func() *persistencespb.WorkflowExecutionInfo { + s.mutableState.timeSource = clock.NewEventTimeSource() + return &persistencespb.WorkflowExecutionInfo{ + TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ + Config: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(3 * time.Hour), + }, + AccumulatedSkippedDuration: durationpb.New(time.Hour), + FastForwardInfo: &persistencespb.FastForwardInfo{ + TargetTime: timestamppb.New(s.mutableState.timeSource.Now().Add(3 * time.Hour)), + HasReached: false, + }, + }, + } + } + + s.Run("child workflows have no fast-forward", func() { + tsc, propagatedState := propagateTimeSkippingToChild(newSource()) + s.Require().NotNil(tsc) + s.True(tsc.GetEnabled()) + s.Nil(tsc.GetFastForward()) + s.Equal(time.Hour, propagatedState.GetInitialSkippedDuration().AsDuration()) + }) + + s.Run("child workflow propagation can be turned off", func() { + src := newSource() + src.TimeSkippingInfo.Config.DisableChildPropagation = true + tsc, propagatedState := propagateTimeSkippingToChild(src) + s.Nil(tsc) + s.Require().NotNil(propagatedState) + s.Equal(time.Hour, propagatedState.GetInitialSkippedDuration().AsDuration(), + "virtual time is always propagated, even when config propagation is disabled") + }) + + s.Run("nil config still propagates virtual time", func() { + src := newSource() + src.TimeSkippingInfo.Config = nil + tsc, propagatedState := propagateTimeSkippingToChild(src) + s.Nil(tsc) + s.Require().NotNil(propagatedState) + s.Equal(time.Hour, propagatedState.GetInitialSkippedDuration().AsDuration(), + "virtual time is always propagated, even when config propagation is disabled") + s.Nil(propagatedState.GetFastForwardTargetTime()) + }) + + s.Run("disableChildPropagation still propagates virtual time", func() { + src := newSource() + src.TimeSkippingInfo.Config.Enabled = false + tsc, propagatedState := propagateTimeSkippingToChild(src) + s.Nil(tsc) + s.Require().NotNil(propagatedState) + s.Equal(time.Hour, propagatedState.GetInitialSkippedDuration().AsDuration(), + "virtual time is always propagated, even when config propagation is disabled") + s.Nil(propagatedState.GetFastForwardTargetTime()) + + }) + +} diff --git a/service/history/workflow/update/store.go b/service/history/workflow/update/store.go index b0fc93fa7c..8c8eba91b9 100644 --- a/service/history/workflow/update/store.go +++ b/service/history/workflow/update/store.go @@ -58,7 +58,7 @@ type ( links []*commonpb.Link, identity string, priority *commonpb.Priority, - timeSkippingConfig *workflowpb.TimeSkippingConfig, + timeSkippingConfig *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) diff --git a/service/history/workflow/update/store_mock_test.go b/service/history/workflow/update/store_mock_test.go index 8fb08d5c69..43bbb55a3c 100644 --- a/service/history/workflow/update/store_mock_test.go +++ b/service/history/workflow/update/store_mock_test.go @@ -83,7 +83,7 @@ type mockEventStore struct { links []*commonpb.Link, identity string, priority *commonpb.Priority, - timeSkippingConfig *workflowpb.TimeSkippingConfig, + timeSkippingConfig *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) @@ -99,7 +99,7 @@ func (m mockEventStore) AddWorkflowExecutionOptionsUpdatedEvent( links []*commonpb.Link, identity string, priority *commonpb.Priority, - timeSkippingConfig *workflowpb.TimeSkippingConfig, + timeSkippingConfig *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { if m.AddWorkflowExecutionOptionsUpdatedEventFunc != nil { diff --git a/service/history/workflow/update/update_test.go b/service/history/workflow/update/update_test.go index 1a05b98aec..51d43fd8ef 100644 --- a/service/history/workflow/update/update_test.go +++ b/service/history/workflow/update/update_test.go @@ -1215,7 +1215,7 @@ func TestAttachCallbacks(t *testing.T) { Controller: effects, AddWorkflowExecutionOptionsUpdatedEventFunc: func( _ *workflowpb.VersioningOverride, _ bool, _ string, _ []*commonpb.Callback, _ []*commonpb.Link, _ string, _ *commonpb.Priority, - _ *workflowpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, + _ *commonpb.TimeSkippingConfig, workflowUpdateOptions []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { captured = workflowUpdateOptions return &historypb.HistoryEvent{}, nil @@ -1230,7 +1230,7 @@ func TestAttachCallbacks(t *testing.T) { Controller: effects, AddWorkflowExecutionOptionsUpdatedEventFunc: func( _ *workflowpb.VersioningOverride, _ bool, _ string, _ []*commonpb.Callback, _ []*commonpb.Link, _ string, _ *commonpb.Priority, - _ *workflowpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, + _ *commonpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { eventCreated = true return &historypb.HistoryEvent{}, nil @@ -1245,7 +1245,7 @@ func TestAttachCallbacks(t *testing.T) { Controller: effects, AddWorkflowExecutionOptionsUpdatedEventFunc: func( _ *workflowpb.VersioningOverride, _ bool, _ string, _ []*commonpb.Callback, _ []*commonpb.Link, _ string, _ *commonpb.Priority, - _ *workflowpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, + _ *commonpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { count++ return &historypb.HistoryEvent{}, nil @@ -1403,7 +1403,7 @@ func TestAttachCallbacks(t *testing.T) { Controller: effects, AddWorkflowExecutionOptionsUpdatedEventFunc: func( _ *workflowpb.VersioningOverride, _ bool, _ string, _ []*commonpb.Callback, _ []*commonpb.Link, _ string, _ *commonpb.Priority, - _ *workflowpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, + _ *commonpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { return nil, serviceerror.NewInternal("flush error") }, @@ -1529,7 +1529,7 @@ func TestAttachCallbacks(t *testing.T) { Controller: effects, AddWorkflowExecutionOptionsUpdatedEventFunc: func( _ *workflowpb.VersioningOverride, _ bool, _ string, _ []*commonpb.Callback, _ []*commonpb.Link, _ string, _ *commonpb.Priority, - _ *workflowpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, + _ *commonpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { return nil, serviceerror.NewInternal("store error") }, @@ -1567,7 +1567,7 @@ func TestAttachCallbacks(t *testing.T) { }, AddWorkflowExecutionOptionsUpdatedEventFunc: func( _ *workflowpb.VersioningOverride, _ bool, _ string, _ []*commonpb.Callback, _ []*commonpb.Link, _ string, _ *commonpb.Priority, - _ *workflowpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, + _ *commonpb.TimeSkippingConfig, _ []*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate, ) (*historypb.HistoryEvent, error) { eventCreated = true return &historypb.HistoryEvent{}, nil diff --git a/tests/timeskipping_bound_test.go b/tests/timeskipping_fast_forward_test.go similarity index 78% rename from tests/timeskipping_bound_test.go rename to tests/timeskipping_fast_forward_test.go index 43266f4db8..29a898807d 100644 --- a/tests/timeskipping_bound_test.go +++ b/tests/timeskipping_fast_forward_test.go @@ -26,15 +26,15 @@ import ( "google.golang.org/protobuf/types/known/fieldmaskpb" ) -type TimeSkippingBoundFunctionalSuite struct { - parallelsuite.Suite[*TimeSkippingBoundFunctionalSuite] +type TimeSkippingFastForwardFunctionalSuite struct { + parallelsuite.Suite[*TimeSkippingFastForwardFunctionalSuite] } -func TestTimeSkippingBoundFunctionalSuite(t *testing.T) { - parallelsuite.Run(t, &TimeSkippingBoundFunctionalSuite{}) +func TestTimeSkippingFastForwardFunctionalSuite(t *testing.T) { + parallelsuite.Run(t, &TimeSkippingFastForwardFunctionalSuite{}) } -func (s *TimeSkippingBoundFunctionalSuite) getMutableState(env *testcore.TestEnv, workflowID, runID string) *persistence.GetWorkflowExecutionResponse { +func (s *TimeSkippingFastForwardFunctionalSuite) getMutableState(env *testcore.TestEnv, workflowID, runID string) *persistence.GetWorkflowExecutionResponse { shardID := common.WorkflowIDToHistoryShard( env.NamespaceID().String(), workflowID, @@ -51,7 +51,7 @@ func (s *TimeSkippingBoundFunctionalSuite) getMutableState(env *testcore.TestEnv return ms } -func (s *TimeSkippingBoundFunctionalSuite) findTransitionedEvents(history []*historypb.HistoryEvent) []*historypb.HistoryEvent { +func (s *TimeSkippingFastForwardFunctionalSuite) findTransitionedEvents(history []*historypb.HistoryEvent) []*historypb.HistoryEvent { var out []*historypb.HistoryEvent for _, e := range history { if e.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED { @@ -61,7 +61,7 @@ func (s *TimeSkippingBoundFunctionalSuite) findTransitionedEvents(history []*his return out } -func boundStartReq(env *testcore.TestEnv, tv *testvars.TestVars, runTimeout time.Duration, cfg *workflowpb.TimeSkippingConfig) *workflowservice.StartWorkflowExecutionRequest { +func fastForwardStartReq(env *testcore.TestEnv, tv *testvars.TestVars, runTimeout time.Duration, cfg *commonpb.TimeSkippingConfig) *workflowservice.StartWorkflowExecutionRequest { return &workflowservice.StartWorkflowExecutionRequest{ RequestId: uuid.NewString(), Namespace: env.Namespace().String(), @@ -74,8 +74,8 @@ func boundStartReq(env *testcore.TestEnv, tv *testvars.TestVars, runTimeout time } } -func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { - // B3 not fixed: bound disable fires regardless of in-flight activity. Update this +func (s *TimeSkippingFastForwardFunctionalSuite) TestFastForward_WithActivity() { + // B3 not fixed: fast-forward disable fires regardless of in-flight activity. Update this // test when B3 lands so it asserts the disable is deferred to the next idle moment. env := testcore.NewEnv(s.T()) env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) @@ -83,17 +83,16 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { ctx := testcore.NewContext() const ( - bound = 30 * time.Minute + fastForward = 30 * time.Minute timer1Dur = 29*time.Minute + 58*time.Second minuteToler = time.Minute accumTol = 30 * time.Second ) - cfg := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(bound)}, - } - startResp, err := env.FrontendClient().StartWorkflowExecution(ctx, boundStartReq(env, tv, 24*time.Hour, cfg)) + cfg := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(fastForward)} + startResp, err := env.FrontendClient().StartWorkflowExecution(ctx, fastForwardStartReq(env, tv, 24*time.Hour, cfg)) s.NoError(err) runID := startResp.RunId @@ -113,17 +112,17 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { }) s.NoError(err) - // Wait for the bound's TimeSkippingTimerTask to fire while the activity is still in - // flight. With timer1=29:58 and bound=30m, the regenerated bound task's wall + // Wait for the fast-forward's TimeSkippingTimerTask to fire while the activity is still in + // flight. With timer1=29:58 and fast-forward=30m, the regenerated fast-forward task's wall // VisibilityTime is at startTime+2s; the executor hits this within seconds of WT2 // closing. B3 not fixed: the executor emits the disable transition regardless of - // the in-flight activity, flipping Enabled=false / HasReached=true on the bound. + // the in-flight activity, flipping Enabled=false / HasReached=true on the fast-forward. s.Eventually(func() bool { ms := s.getMutableState(env, tv.WorkflowID(), runID) tsi := ms.State.ExecutionInfo.GetTimeSkippingInfo() - bi := tsi.GetCurrentElapsedDurationBound() - return bi != nil && bi.GetHasReached() - }, 30*time.Second, 200*time.Millisecond, "expected bound timer task to fire while activity is in-flight (B3 path)") + ff := tsi.GetFastForwardInfo() + return ff != nil && ff.GetHasReached() + }, 30*time.Second, 200*time.Millisecond, "expected fastForward timer task to fire while activity is in-flight (B3 path)") // AT1: complete the activity. _, err = env.TaskPoller().PollAndHandleActivityTask(tv, taskpoller.CompleteActivityTask(tv)) @@ -142,13 +141,13 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { s.Len(transitions, 2) first := transitions[0].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() - s.False(first.GetDisabledAfterBound()) + s.False(first.GetDisabledAfterFastForward()) s.NotNil(first.GetTargetTime()) firstSkip := first.GetTargetTime().AsTime().Sub(transitions[0].GetEventTime().AsTime()) s.InDelta(float64(timer1Dur), float64(firstSkip), float64(accumTol)) second := transitions[1].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() - s.True(second.GetDisabledAfterBound()) + s.True(second.GetDisabledAfterFastForward()) desc, err := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ Namespace: env.Namespace().String(), @@ -158,7 +157,7 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { startTime := desc.WorkflowExecutionInfo.GetStartTime().AsTime() secondVirtual := transitions[1].GetEventTime().AsTime().Sub(startTime) - s.InDelta(float64(bound), float64(secondVirtual), float64(minuteToler)) + s.InDelta(float64(fastForward), float64(secondVirtual), float64(minuteToler)) var activityCompleted int for _, e := range hist { @@ -172,19 +171,19 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { tsi := ms.State.ExecutionInfo.GetTimeSkippingInfo() s.NotNil(tsi) s.False(tsi.GetConfig().GetEnabled()) - bi := tsi.GetCurrentElapsedDurationBound() - s.NotNil(bi) - s.True(bi.GetHasReached()) + ff := tsi.GetFastForwardInfo() + s.NotNil(ff) + s.True(ff.GetHasReached()) } -// TestBound_MaxElapsed_PauseLifecycle exercises the full paused-workflow +// TestFastForward_PauseLifecycle exercises the full paused-workflow // time-skipping lifecycle and verifies three invariants together: // // (1) Pause blocks close-transaction skipping. While paused, no new // TimeSkippingTransitioned event is added on close-tx — both // closeTransactionHandleTimeSkipping and shouldExecuteTimeSkipping // short-circuit on IsWorkflowExecutionStatusPaused. -// (2) The MaxElapsed bound's TimeSkippingTimerTask still fires while paused. +// (2) The fast-forward's TimeSkippingTimerTask still fires while paused. // executeTimeSkippingTimerTask only checks IsWorkflowExecutionRunning // (paused workflows are State=RUNNING), so the disable transition is // written through pause — analogous to user-timer-fired events firing @@ -197,12 +196,12 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { // Sequence: // // WT1 → start timer1 (29:50). Close-tx fires transition 1 (skip-to-timer1) -// and timer1 fires. Bound wake-up timer task is wall-anchored at +// and timer1 fires. Fast-forward wake-up timer task is wall-anchored at // ~10s real-time from now. // WT2 → schedule activity1. Close-tx: pending activity → no skip. // Pause → activity1 stamp bumped (dispatched task now invalid). Pause // close-tx is blocked by IsWorkflowExecutionStatusPaused. -// (wait) Bound timer task fires while paused → transition 2 +// (wait) Fast-forward timer task fires while paused → transition 2 // (DisabledAfterBound=true); Config.Enabled becomes false. // Unpause → activity1 re-dispatched, WT3 scheduled in the same tx; // close-tx pending WFT → no extra transition. @@ -210,8 +209,8 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_WithActivity() { // WT3 → completeWorkflowCmd. // // Final history must contain exactly two transitions, in order: -// (a) skip-to-timer1 (DisabledAfterBound=false), (b) bound-disable. -func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_PauseLifecycle() { +// (a) skip-to-timer1 (DisabledAfterBound=false), (b) fast-forward-disable. +func (s *TimeSkippingFastForwardFunctionalSuite) TestFastForward_PauseLifecycle() { env := testcore.NewEnv(s.T()) env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) env.OverrideDynamicConfig(dynamicconfig.WorkflowPauseEnabled, true) @@ -219,16 +218,15 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_PauseLifecycle() ctx := testcore.NewContext() const ( - bound = 30 * time.Minute + fastForward = 30 * time.Minute timer1Dur = 29*time.Minute + 50*time.Second minuteToler = time.Minute ) - cfg := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(bound)}, - } - startResp, err := env.FrontendClient().StartWorkflowExecution(ctx, boundStartReq(env, tv, 24*time.Hour, cfg)) + cfg := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(fastForward)} + startResp, err := env.FrontendClient().StartWorkflowExecution(ctx, fastForwardStartReq(env, tv, 24*time.Hour, cfg)) s.NoError(err) runID := startResp.RunId @@ -240,8 +238,8 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_PauseLifecycle() }) s.NoError(err) - // WT2: timer1 fired → schedule activity1. The bound wake-up timer is wall-anchored - // at start+bound (~10s real time from this point). + // WT2: timer1 fired → schedule activity1. The fast-forward wake-up timer is wall-anchored + // at start+fast-forward (~10s real time from this point). _, err = env.TaskPoller().PollAndHandleWorkflowTask(tv, func(_ *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) { return &workflowservice.RespondWorkflowTaskCompletedRequest{ Commands: []*commandpb.Command{scheduleActivityCmd(tv)}, @@ -262,18 +260,18 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_PauseLifecycle() }) s.NoError(err) - // Wait for the bound TimeSkippingTimerTask to fire while paused. The executor + // Wait for the fast-forward TimeSkippingTimerTask to fire while paused. The executor // writes the disable event regardless of pause status: HasReached becomes true // and Config.Enabled becomes false. s.AwaitTruef(func() bool { ms := s.getMutableState(env, tv.WorkflowID(), runID) tsi := ms.State.ExecutionInfo.GetTimeSkippingInfo() - bi := tsi.GetCurrentElapsedDurationBound() - return bi != nil && bi.GetHasReached() - }, 30*time.Second, 200*time.Millisecond, "expected bound timer task to fire while paused") + ff := tsi.GetFastForwardInfo() + return ff != nil && ff.GetHasReached() + }, 30*time.Second, 200*time.Millisecond, "expected fastForward timer task to fire while paused") // Snapshot history while still paused: exactly two transitions — skip-to-timer1 - // (from WT1 close-tx) and bound-disable (from the timer task that just fired). + // (from WT1 close-tx) and fast-forward-disable (from the timer task that just fired). // No spurious third transition from any close-tx evaluated under pause. histDuringPause := env.GetHistory(env.Namespace().String(), &commonpb.WorkflowExecution{ WorkflowId: tv.WorkflowID(), RunId: runID, @@ -312,11 +310,11 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_PauseLifecycle() s.Len(transitions, 2, "expected exactly two transitions across the entire lifecycle") first := transitions[0].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() - s.False(first.GetDisabledAfterBound()) + s.False(first.GetDisabledAfterFastForward()) s.NotNil(first.GetTargetTime()) second := transitions[1].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() - s.True(second.GetDisabledAfterBound(), "second transition must be the bound-disable event") + s.True(second.GetDisabledAfterFastForward(), "second transition must be the fast-forward-disable event") s.True(hasEventType(hist, enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED), "pause event must be in history") s.True(hasEventType(hist, enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED), "unpause event must be in history") @@ -329,34 +327,33 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_PauseLifecycle() s.NoError(err) startTime := desc.WorkflowExecutionInfo.GetStartTime().AsTime() secondVirtual := transitions[1].GetEventTime().AsTime().Sub(startTime) - s.InDelta(float64(bound), float64(secondVirtual), float64(minuteToler)) + s.InDelta(float64(fastForward), float64(secondVirtual), float64(minuteToler)) ms := s.getMutableState(env, tv.WorkflowID(), runID) tsi := ms.State.ExecutionInfo.GetTimeSkippingInfo() s.NotNil(tsi) - s.False(tsi.GetConfig().GetEnabled(), "Config.Enabled must be false after bound reached") - bi := tsi.GetCurrentElapsedDurationBound() - s.NotNil(bi) - s.True(bi.GetHasReached(), "HasReached must be true after bound timer fired") + s.False(tsi.GetConfig().GetEnabled(), "Config.Enabled must be false after fastForward reached") + ff := tsi.GetFastForwardInfo() + s.NotNil(ff) + s.True(ff.GetHasReached(), "HasReached must be true after fastForward timer fired") } -func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_NoUserTimer() { +func (s *TimeSkippingFastForwardFunctionalSuite) TestFastForward_NoUserTimer() { env := testcore.NewEnv(s.T()) env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) tv := testvars.New(s.T()) ctx := testcore.NewContext() const ( - bound = 30 * time.Minute + fastForward = 30 * time.Minute minuteToler = time.Minute accumTol = 30 * time.Second ) - cfg := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(bound)}, - } - startResp, err := env.FrontendClient().StartWorkflowExecution(ctx, boundStartReq(env, tv, 24*time.Hour, cfg)) + cfg := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(fastForward)} + startResp, err := env.FrontendClient().StartWorkflowExecution(ctx, fastForwardStartReq(env, tv, 24*time.Hour, cfg)) s.NoError(err) runID := startResp.RunId @@ -376,17 +373,17 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_NoUserTimer() { transitions := s.findTransitionedEvents(hist) s.Len(transitions, 1) attrs := transitions[0].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() - s.True(attrs.GetDisabledAfterBound()) + s.True(attrs.GetDisabledAfterFastForward()) s.WithinDuration(startTime, transitions[0].GetEventTime().AsTime(), minuteToler) ms := s.getMutableState(env, tv.WorkflowID(), runID) tsi := ms.State.ExecutionInfo.GetTimeSkippingInfo() s.NotNil(tsi) s.False(tsi.GetConfig().GetEnabled()) - s.InDelta(float64(bound), float64(tsi.GetAccumulatedSkippedDuration().AsDuration()), float64(accumTol)) - bi := tsi.GetCurrentElapsedDurationBound() - s.NotNil(bi) - s.True(bi.GetHasReached()) + s.InDelta(float64(fastForward), float64(tsi.GetAccumulatedSkippedDuration().AsDuration()), float64(accumTol)) + ff := tsi.GetFastForwardInfo() + s.NotNil(ff) + s.True(ff.GetHasReached()) _, _ = env.FrontendClient().TerminateWorkflowExecution(ctx, &workflowservice.TerminateWorkflowExecutionRequest{ Namespace: env.Namespace().String(), @@ -395,23 +392,23 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_NoUserTimer() { }) } -func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_BoundEqualsRunTimeout() { +func (s *TimeSkippingFastForwardFunctionalSuite) TestFastForward_EqualsRunTimeout() { env := testcore.NewEnv(s.T()) env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) ctx := testcore.NewContext() const ( - runTimeout = 5 * time.Minute - bound = runTimeout // bound == runTimeout + runTimeout = 5 * time.Minute + fastForward = runTimeout // fastForward == runTimeout ) env.SdkWorker().RegisterWorkflowWithOptions(func(ctx workflow.Context) error { return workflow.Sleep(ctx, runTimeout) }, workflow.RegisterOptions{Name: "sleepEqualsTimeoutWorkflow"}) - cfg := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(bound)}, + cfg := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(fastForward), } workflowID := uuid.NewString() startResp, err := env.FrontendClient().StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ @@ -448,27 +445,27 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_BoundEqualsRunTi s.Less(transitionIdx, timedOutIdx, "the time-skipping transition must be recorded before the timeout") } -// TestBound_MaxElapsed_RunTimeoutBeforeBound is a two-phase test: +// TestFastForward_RunTimeoutBeforeFastForward is a two-phase test: // -// 1. Workflow starts with time-skipping enabled but NO bound. The workflow is +// 1. Workflow starts with time-skipping enabled but NO fast-forward. The workflow is // idle (blocking on a condition) so there are no skip candidates. The // run/execution timeout alone must NOT advance virtual time — it is not a // skip target. The workflow stays RUNNING indefinitely in real time. // -// 2. A MaxElapsed bound larger than the run timeout is added via -// UpdateWorkflowExecutionOptions. The update's close-tx now finds the bound +// 2. A fast-forward larger than the run timeout is added via +// UpdateWorkflowExecutionOptions. The update's close-tx now finds the fast-forward // as the only skip candidate, which is then capped at the run timeout -// (5 min < 10 min bound). Virtual time advances to the run timeout and the +// (5 min < 10 min fast-forward). Virtual time advances to the run timeout and the // workflow is TIMED_OUT. The single transition carries DisabledAfterBound=false -// (the cap fired before the bound was reached). -func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_RunTimeoutBeforeBound() { +// (the cap fired before the fast-forward was reached). +func (s *TimeSkippingFastForwardFunctionalSuite) TestFastForward_RunTimeoutBeforeFastForward() { env := testcore.NewEnv(s.T()) env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) ctx := testcore.NewContext() const ( - runTimeout = 5 * time.Minute - bound = 10 * time.Minute // larger than runTimeout + runTimeout = 5 * time.Minute + fastForward = 10 * time.Minute // larger than runTimeout ) env.SdkWorker().RegisterWorkflowWithOptions(func(ctx workflow.Context) error { @@ -484,7 +481,7 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_RunTimeoutBefore TaskQueue: &taskqueuepb.TaskQueue{Name: env.WorkerTaskQueue()}, WorkflowRunTimeout: durationpb.New(runTimeout), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, // no bound yet + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, // no fastForward yet }) s.NoError(err) runID := startResp.RunId @@ -501,16 +498,16 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_RunTimeoutBefore s.Empty(s.findTransitionedEvents(hist), "run/execution timeout alone is not a skip target — no transition must have fired") - // Phase 2: add MaxElapsed bound > run timeout. The update's close-tx finds - // the bound as the skip candidate, caps it at the run timeout, and the + // Phase 2: add fast-forward > run timeout. The update's close-tx finds + // the fast-forward as the skip candidate, caps it at the run timeout, and the // workflow is timed out. _, err = env.FrontendClient().UpdateWorkflowExecutionOptions(ctx, &workflowservice.UpdateWorkflowExecutionOptionsRequest{ Namespace: env.Namespace().String(), WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(bound)}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(fastForward), }, }, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, @@ -525,7 +522,7 @@ func (s *TimeSkippingBoundFunctionalSuite) TestBound_MaxElapsed_RunTimeoutBefore hist = env.GetHistory(env.Namespace().String(), &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}) transitions := s.findTransitionedEvents(hist) s.Len(transitions, 1, "exactly one transition: skip to run timeout") - s.False(transitions[0].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes().GetDisabledAfterBound(), - "run timeout caps the skip before bound is reached: DisabledAfterBound must be false") + s.False(transitions[0].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes().GetDisabledAfterFastForward(), + "run timeout caps the skip before fast-forward is reached: DisabledAfterFastForward must be false") s.True(hasEventType(hist, enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT)) } diff --git a/tests/timeskipping_propagation_test.go b/tests/timeskipping_propagation_test.go index 863e657a78..cd1f4ed9c7 100644 --- a/tests/timeskipping_propagation_test.go +++ b/tests/timeskipping_propagation_test.go @@ -1,52 +1,25 @@ -// Tests in this file exercise how TimeSkippingConfig and AccumulatedSkippedDuration -// cross workflow boundaries. Each test pins one of three inheritance semantics. +// Tests in this file exercise how TimeSkippingConfig and StatePropagation +// propagate across workflow boundaries. Each test pins one of two inheritance semantics. // -// Group 1 — Inherit both from the current execution. -// -// Continue-as-new: a new run is a technical continuation of the same logical run, -// so the current config AND accumulated skipped duration are both inherited. The -// configured bound is shared: it caps the sum of inherited skip + new-run skip, -// not each separately. -// -// Child workflows: same as continue-as-new. A child can be viewed as either an -// extension of the parent or a separate workflow; either way, virtual time must -// not rewind, so the child inherits the parent's accumulated skip. The default -// behavior treats the child as an extension and shares the config; a child-level -// override is only added when explicitly needed. +// Group 1 — Chain of runs (continue-as-new, retry, cron). +// The policy is to propagate all TimeSkippingConfig and StatePropagation across the chain. // // Covered by: -// - TestTSPInChildWf_Basic -// - TestTSPInChildWf_TwoChildren -// - TestTSPInChildWf_ThreeGenerations // - TestTSPInCaN -// -// Group 2 — Inherit from a specific point in history. -// -// Retry: a retried run is defined as restarting execution from the previous run's -// WorkflowExecutionStarted event. The new run inherits the config and PSD recorded -// on that event verbatim; in-flight skip accumulated during the failed attempt is -// NOT carried forward. -// -// Cron: same as retry. Each cron run restarts from the previous run's event #1 -// snapshot; the previous run's in-flight skip is not carried forward. -// -// Covered by: // - TestTSPInRetry // - TestTSPInCron // -// Group 3 — Inherit from a specific point, then catch up on config changes. +// Group 2 — Child workflows. // -// Reset: retains the current TimeSkippingConfig at reset time. Reset replays all -// events up to the reset point (giving the new run the config as-of that point), -// then reapplies any WorkflowExecutionOptionsUpdated events that occurred between -// the reset point and the original run's terminal state. Reapply is unconditional -// — there is no ResetReapplyExcludeType value that can suppress OPTIONS_UPDATED. -// AccumulatedSkippedDuration on the reset run starts from the event #1 -// InitialSkippedDuration (same as Group 2); original-run in-flight skip is not -// carried forward. -// -// Covered by: -// - TestTSPInReset +// 1. fast-forward is not propagated to children. +// 2. DisableChildPropagation disables propagation of the enabled flag. +// 3. the child inherits the parent's accumulated skipped duration in all cases. +// Covered by: +// - TestTSPInChildWf_Basic +// - TestTSPInChildWf_TwoChildren +// - TestTSPInChildWf_ThreeGenerations +// - TestTSPInChildWf_AdmissionTimestampsShifted +// - TestTSPInChildWf_PropagationDisabled package tests import ( @@ -97,7 +70,9 @@ func TestTimeSkippingPropagationTestSuite(t *testing.T) { // from where the parent left off. // // Scenario: -// - Parent starts with TimeSkippingConfig{Enabled: true}. +// - Parent starts with TimeSkippingConfig{Enabled: true, FastForward: 3h}. The +// fast-forward never cascades into children: the child's config carries Enabled +// only, and the child's own skipping is uncapped. // - Parent issues StartTimer(t1, 1h). Parent is idle on t1 → server skips 1h. // After skip: parent.AccumulatedSkippedDuration = 1h. // - t1 fires. Parent issues StartChildWorkflow(child) and StartTimer(t2, 1h). @@ -114,12 +89,14 @@ func TestTimeSkippingPropagationTestSuite(t *testing.T) { // - t2 fires. Parent completes. // // End-state assertions: -// - parent.AccumulatedSkippedDuration == 2h. -// - child.TimeSkippingInfo.Config.Enabled == true (propagated). -// - child.AccumulatedSkippedDuration == 4h (1h inherited + 3h own from tc). +// - parent.AccumulatedSkippedDuration == 2h, within its own 3h FastForward budget. +// - child.TimeSkippingInfo.Config.Enabled == true (propagated), FastForward unset. +// - child.AccumulatedSkippedDuration == 4h (1h inherited + 3h own from tc) — exceeding +// the parent's 3h FastForward, which is possible only because the budget did not +// cascade into the child. // - The parent's StartChildWorkflowExecutionInitiated event carries -// TimeSkippingConfig{Enabled=true} and InitialSkippedDuration=1h (the pre-apply -// snapshots; the wire form the transfer task consumes). +// TimeSkippingConfig{Enabled=true} with no FastForward, and InitialSkippedDuration=1h +// (the pre-apply snapshots; the wire form the transfer task consumes). // - Wall-clock elapsed is nowhere near 5h of virtual time. func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_Basic() { env := testcore.NewEnv(s.T()) @@ -142,7 +119,12 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_Basic() { TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + // FastForward never cascades into children; the parent's 2h of own skipping + // stays under this budget so the parent's flow is unaffected by it. + TimeSkippingConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(3 * time.Hour), + }, }) s.NoError(err) parentRunID := parentStart.RunId @@ -200,6 +182,8 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_Basic() { s.Equal(enumsspb.WORKFLOW_EXECUTION_STATE_COMPLETED, parentMS.State.ExecutionState.State) parentTSI := parentMS.State.ExecutionInfo.GetTimeSkippingInfo() s.NotNil(parentTSI) + s.Equal(3*time.Hour, parentTSI.GetConfig().GetFastForward().AsDuration(), + "parent keeps its own MaxElapsedDuration; child propagation must not mutate it") s.approxDuration(2*time.Hour, parentTSI.GetAccumulatedSkippedDuration().AsDuration(), "parent should accumulate 1h (t1) + 1h (t2 after child) = 2h") @@ -211,8 +195,9 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_Basic() { childCfg := childTSI.GetConfig() s.NotNil(childCfg) s.True(childCfg.GetEnabled(), "child TSC.Enabled propagated from parent") + s.Nil(childCfg.GetFastForward(), "MaxElapsedDuration never cascades into children") s.approxDuration(4*time.Hour, childTSI.GetAccumulatedSkippedDuration().AsDuration(), - "child AccumulatedSkippedDuration == parent's accumulated at child-start (1h) + child's own skip for tc (3h) = 4h") + "child AccumulatedSkippedDuration == parent's accumulated at child-start (1h) + child's own skip for tc (3h) = 4h — exceeding the parent's 3h FastForward, possible only because the budget did not cascade") // The parent's StartChildWorkflowExecutionInitiated event must carry the propagated // TimeSkippingConfig and InitialSkippedDuration — these are what the transfer task @@ -223,7 +208,8 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_Basic() { initTSC := initAttrs.GetTimeSkippingConfig() s.NotNil(initTSC, "initiated event should carry TimeSkippingConfig snapshot") s.True(initTSC.GetEnabled(), "snapshot mirrors parent's Enabled flag") - s.approxDuration(time.Hour, initAttrs.GetInitialSkippedDuration().AsDuration(), + s.Nil(initTSC.GetFastForward(), "initiated-event snapshot excludes the parent's MaxElapsedDuration") + s.approxDuration(time.Hour, initAttrs.GetTimeSkippingStatePropagation().GetInitialSkippedDuration().AsDuration(), "initiated event InitialSkippedDuration == parent's AccumulatedSkippedDuration at command time (1h)") } @@ -266,7 +252,7 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_TwoChildren() { TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }) s.NoError(err) parentRunID := parentStart.RunId @@ -350,7 +336,7 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_TwoChildren() { tsc := attrs.GetTimeSkippingConfig() s.NotNil(tsc) s.True(tsc.GetEnabled()) - s.approxDuration(time.Hour, attrs.GetInitialSkippedDuration().AsDuration()) + s.approxDuration(time.Hour, attrs.GetTimeSkippingStatePropagation().GetInitialSkippedDuration().AsDuration()) } } @@ -401,7 +387,7 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_ThreeGenerations() { TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }) s.NoError(err) gRunID := gStart.RunId @@ -508,14 +494,14 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_ThreeGenerations() { s.Len(gInit, 1) gInitAttrs := gInit[0].GetStartChildWorkflowExecutionInitiatedEventAttributes() s.NotNil(gInitAttrs.GetTimeSkippingConfig()) - s.approxDuration(time.Hour, gInitAttrs.GetInitialSkippedDuration().AsDuration()) + s.approxDuration(time.Hour, gInitAttrs.GetTimeSkippingStatePropagation().GetInitialSkippedDuration().AsDuration()) pRunID := pMS.State.ExecutionState.RunId pInit := s.initiatedChildEvents(ctx, env, pWFID, pRunID) s.Len(pInit, 1) pInitAttrs := pInit[0].GetStartChildWorkflowExecutionInitiatedEventAttributes() s.NotNil(pInitAttrs.GetTimeSkippingConfig()) - s.approxDuration(3*time.Hour, pInitAttrs.GetInitialSkippedDuration().AsDuration(), + s.approxDuration(3*time.Hour, pInitAttrs.GetTimeSkippingStatePropagation().GetInitialSkippedDuration().AsDuration(), "P's initiated event for C carries P's full accumulated at command time (3h = G's 1h + P's pt1 2h)") } @@ -564,7 +550,7 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInChildWf_AdmissionTimestampsS TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }) s.NoError(err) parentRunID := parentStart.RunId @@ -986,7 +972,7 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInReset() { TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: false}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: false}, }) s.NoError(err) originalRunID := startResp.RunId @@ -1008,7 +994,7 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInReset() { Namespace: env.Namespace().String(), WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: wfID, RunId: originalRunID}, WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, }) @@ -1091,14 +1077,17 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInReset() { // applyTimeSkippingConfig uses InitialSkippedDuration to seed AccumulatedSkippedDuration // on the new MS, so the virtual clock continues seamlessly. // +// DisableChildPropagation gates child propagation only — CaN ignores it — so run 1 sets +// it to verify the continuation still inherits Enabled=true. +// // Scenario: -// - Run 1 starts with TimeSkippingConfig{Enabled: true}. +// - Run 1 starts with TimeSkippingConfig{Enabled: true, DisableChildPropagation: true}. // - Run 1 issues StartTimer(t1, 1h). Idle → server skips 1h. // After skip: run1.AccumulatedSkippedDuration = 1h. // - t1 fires. Run 1 issues ContinueAsNew (same type, same task queue). -// - Run 2 starts. Its WorkflowExecutionStarted event carries -// TimeSkippingConfig{Enabled=true} and InitialSkippedDuration=1h. The applied MS -// has Config.Enabled=true and AccumulatedSkippedDuration=1h. +// - Run 2 starts. Its WorkflowExecutionStarted event carries the current config +// (Enabled=true, DisableChildPropagation=true) and InitialSkippedDuration=1h. The +// applied MS has Config.Enabled=true and AccumulatedSkippedDuration=1h. // - Run 2 issues StartTimer(t2, 2h). Idle → skips 2h. // After skip: run2.AccumulatedSkippedDuration = 1h + 2h = 3h. // - t2 fires. Run 2 completes. @@ -1131,7 +1120,7 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInCaN() { TaskQueue: tq, WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }) s.NoError(err) run1ID := start1.RunId @@ -1179,7 +1168,8 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInCaN() { s.NotNil(run2TSI, "run 2 should have TimeSkippingInfo propagated from run 1") run2Cfg := run2TSI.GetConfig() s.NotNil(run2Cfg) - s.True(run2Cfg.GetEnabled(), "run 2 TSC.Enabled propagated from run 1") + s.True(run2Cfg.GetEnabled(), + "run 2 TSC.Enabled propagated from run 1") s.approxDuration(3*time.Hour, run2TSI.GetAccumulatedSkippedDuration().AsDuration(), "run 2 AccumulatedSkippedDuration == run 1's accumulated (1h) + run 2's own skip for t2 (2h) = 3h") @@ -1197,44 +1187,37 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInCaN() { startedTSC := startedAttr.GetTimeSkippingConfig() s.NotNil(startedTSC, "run 2's WorkflowExecutionStarted must carry the TSC snapshot") s.True(startedTSC.GetEnabled(), "started event TSC.Enabled mirrors run 1's config") - s.approxDuration(time.Hour, startedAttr.GetInitialSkippedDuration().AsDuration(), + s.approxDuration(time.Hour, startedAttr.GetTimeSkippingStatePropagation().GetInitialSkippedDuration().AsDuration(), "started event retains InitialSkippedDuration=1h (run 1's accumulated at CaN time); applyTimeSkippingConfig uses it to seed AccumulatedSkippedDuration on the MS but the event snapshot is unchanged") } -// TestTSPInRetry verifies that TimeSkippingConfig is propagated from the -// original run's WorkflowExecutionStarted event to a retried workflow run. +// TestTSPInRetry verifies that a retried run is a same-lineage continuation: it inherits +// the previous attempt's current TimeSkippingConfig AND its in-flight accumulated skip. // -// Retry's createRequest.TimeSkippingConfig is a verbatim copy of startAttr.TimeSkippingConfig -// (event #1 of the previous run), and req.InitialSkippedDuration is a verbatim copy of -// startAttr.InitialSkippedDuration. applyTimeSkippingConfig then produces the same MS state -// the original run was booted with: Config.Enabled preserved; if the original event #1 had -// a non-zero InitialSkippedDuration snapshot (e.g., the original run was itself started as -// a child or via CaN), that value seeds AccumulatedSkippedDuration on the retry — otherwise -// the retry starts with Accum=0. -// -// Key semantic: unlike continue-as-new, the previous attempt's *in-flight* accumulated -// skip (skip that happened during the attempt) is NOT carried forward to the retry. -// Each attempt gets a fresh virtual-time clock at whatever offset the original run was -// booted with — the retry is semantically a re-attempt, not a continuation. +// Retry snapshots from the previous run's live executionInfo (via +// snapshotTimeSkippingInfoForExecutionChain), not event #1: createRequest.TimeSkippingConfig +// carries the current config (including FastForward, ignoring DisableChildPropagation) and +// req.InitialSkippedDuration carries the previous attempt's AccumulatedSkippedDuration at +// retry time. initTimeSkippingInfo then seeds the retry's AccumulatedSkippedDuration from +// it — so the previous attempt's in-flight skip IS carried forward, exactly like +// continue-as-new. // // Scenario: -// - Start a top-level workflow with TimeSkippingConfig{Enabled: true} and -// RetryPolicy{MaximumAttempts: 2}. Event #1's InitialSkippedDuration is unset -// (no parent, no prior CaN). +// - Start a top-level workflow with TimeSkippingConfig{Enabled: true, FastForward: 4h} +// and RetryPolicy{MaximumAttempts: 2}. Event #1's InitialSkippedDuration is unset. // - Attempt 1 issues StartTimer(t1, 1h). Idle → server skips 1h → attempt1.Accum = 1h. // - Attempt 1 issues FailWorkflowExecution with a bare (retryable) Failure. -// - Server schedules retry. Attempt 2 (retry run) starts. -// Its MS applies startAttr.TimeSkippingConfig: Config.Enabled=true, no initial skip -// → Accum=0. Attempt 1's 1h in-flight skip is NOT carried forward. -// - Attempt 2 issues StartTimer(t2, 2h). Idle → server skips 2h → attempt2.Accum = 2h. +// - Server schedules retry. Attempt 2 (retry run) starts with InitialSkippedDuration=1h +// (attempt 1's accumulated at retry time) → Accum seeded to 1h. +// - Attempt 2 issues StartTimer(t2, 2h). Idle → server skips 2h → attempt2.Accum = 3h. // - Attempt 2 completes. // // End-state assertions: // - Attempt 1: status=FAILED, Config.Enabled=true, Accum=1h. -// - Attempt 2: status=COMPLETED, Config.Enabled=true, Accum=2h (own skip only; -// 1h from attempt 1 is NOT inherited). -// - Attempt 2's WorkflowExecutionStarted carries the same TSC snapshot as attempt 1's -// event #1 (Enabled=true, no InitialSkippedDuration) and references attempt 1 as +// - Attempt 2: status=COMPLETED, Config.Enabled=true, FastForward=4h inherited +// (the chain shares one budget), Accum=3h (1h inherited + own 2h) within that budget. +// - Attempt 2's WorkflowExecutionStarted carries the current TSC snapshot (Enabled=true, +// FastForward=4h) with InitialSkippedDuration≈1h and references attempt 1 as // ContinuedExecutionRunId. func (s *TimeSkippingPropagationTestSuite) TestTSPInRetry() { env := testcore.NewEnv(s.T()) @@ -1256,7 +1239,11 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInRetry() { TaskQueue: tq, WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + // FastForward is part of the inherited config: it caps the whole chain of attempts. + TimeSkippingConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(4 * time.Hour), + }, RetryPolicy: &commonpb.RetryPolicy{ InitialInterval: durationpb.New(time.Second), BackoffCoefficient: 1.0, @@ -1310,9 +1297,13 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInRetry() { s.NotNil(attempt2TSI, "attempt 2 should have TimeSkippingInfo from the propagated event #1 snapshot") attempt2Cfg := attempt2TSI.GetConfig() s.NotNil(attempt2Cfg) - s.True(attempt2Cfg.GetEnabled(), "attempt 2 TSC.Enabled propagated from attempt 1's event #1") - s.approxDuration(2*time.Hour, attempt2TSI.GetAccumulatedSkippedDuration().AsDuration(), - "attempt 2 accumulated only its own 2h from t2; attempt 1's 1h in-flight skip is NOT carried forward on retry") + s.True(attempt2Cfg.GetEnabled(), "attempt 2 TSC.Enabled propagated from attempt 1's current config") + s.Equal(4*time.Hour, attempt2Cfg.GetFastForward().AsDuration(), + "attempt 2 inherits MaxElapsedDuration verbatim — the budget caps the whole chain, not each attempt") + s.approxDuration(3*time.Hour, attempt2TSI.GetAccumulatedSkippedDuration().AsDuration(), + "attempt 2 carries attempt 1's 1h in-flight skip forward plus its own 2h from t2 = 3h") + s.LessOrEqual(attempt2TSI.GetAccumulatedSkippedDuration().AsDuration(), 4*time.Hour, + "cap-total: the chain's accumulated skip stays within the shared MaxElapsedDuration budget") // Attempt 2's WorkflowExecutionStarted must carry the same TSC snapshot as attempt 1's event #1. hist2, err := env.FrontendClient().GetWorkflowExecutionHistory(ctx, &workflowservice.GetWorkflowExecutionHistoryRequest{ @@ -1329,40 +1320,39 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInRetry() { "attempt 2 is initiated by retry") startedTSC := startedAttr.GetTimeSkippingConfig() s.NotNil(startedTSC, "attempt 2's WorkflowExecutionStarted must carry the TSC snapshot") - s.True(startedTSC.GetEnabled(), "started event TSC.Enabled mirrors attempt 1's event #1") - s.Zero(startedAttr.GetInitialSkippedDuration().AsDuration(), - "started event InitialSkippedDuration matches attempt 1's event #1 (unset for a top-level workflow)") + s.True(startedTSC.GetEnabled(), "started event TSC.Enabled mirrors attempt 1's current config") + s.Equal(4*time.Hour, startedTSC.GetFastForward().AsDuration(), + "started event carries the inherited MaxElapsedDuration — the snapshot of attempt 1's current config") + s.approxDuration(time.Hour, startedAttr.GetTimeSkippingStatePropagation().GetInitialSkippedDuration().AsDuration(), + "started event InitialSkippedDuration carries attempt 1's in-flight accumulated skip (1h) at retry time") } -// TestTSPInCron verifies that TimeSkippingConfig (Enabled + Bound) is -// propagated from the previous run's WorkflowExecutionStarted event to a cron-scheduled -// next run verbatim, while AccumulatedSkippedDuration is NOT inherited. +// TestTSPInCron verifies that a cron-scheduled next run is a same-lineage continuation: +// it inherits the previous run's current TimeSkippingConfig (Enabled + FastForward) AND +// its in-flight accumulated skip. // -// Cron and retry share SetupNewWorkflowForRetryOrCron, which copies -// createRequest.TimeSkippingConfig = startAttr.GetTimeSkippingConfig() and -// req.InitialSkippedDuration = startAttr.GetInitialSkippedDuration() from event #1 of -// the previous run onto the new run. applyTimeSkippingConfig then seeds -// AccumulatedSkippedDuration from InitialSkippedDuration. For a top-level cron workflow, -// event #1 has no InitialSkippedDuration, so each cron run starts with Accum=0 — the -// previous run's in-flight skip is NOT carried forward. +// Cron and retry share SetupNewWorkflowForRetryOrCron, which snapshots from the previous +// run's live executionInfo (via snapshotTimeSkippingInfoForExecutionChain): the current +// config (incl. FastForward) and its AccumulatedSkippedDuration. initTimeSkippingInfo seeds +// the new run's AccumulatedSkippedDuration from that InitialSkippedDuration — so the +// previous run's in-flight skip IS carried forward, like continue-as-new. The FastForward +// is shared across the lineage (cap-total): run 2 inherits ~50min of the 1h budget, +// leaving only ~10min of skip for itself. // // Scenario: // - Start a cron workflow (`* * * * *`) with TimeSkippingConfig{Enabled: true, -// Bound: MaxElapsedDuration(1h)}. Event #1's InitialSkippedDuration is unset -// (no parent, no prior CaN). +// MaxElapsedDuration: 1h}. Event #1's InitialSkippedDuration is unset. // - Run 1 issues StartTimer(t1, 50min). Idle → server skips 50min → run1.Accum ≈ 50min. // - Run 1 completes. Cron rolls forward; server creates run 2 with -// createRequest.TimeSkippingConfig = run 1's event #1 TSC (Enabled=true, -// Bound=MaxElapsedDuration(1h)) and no InitialSkippedDuration → run 2 MS has -// Config{Enabled: true, Bound: MaxElapsedDuration(1h)}, Accum=0. -// - Run 2 completes immediately (no own skipping). Run 1's 50min accum is NOT inherited. +// createRequest.TimeSkippingConfig = run 1's current config (Enabled=true, +// MaxElapsedDuration=1h) and InitialSkippedDuration ≈ 50min → run 2 MS Accum seeded ≈ 50min. // // End-state assertions: -// - Run 1: Config{Enabled, Bound=1h}, Accum ≈ 50min. -// - Run 2: Config{Enabled, Bound=1h} (inherited verbatim), -// Accum ≪ 50min (the previous run's in-flight skip is not carried forward). -// - Run 2's WorkflowExecutionStarted event carries the verbatim TSC snapshot from -// run 1's event #1, with initiator=CRON_SCHEDULE. +// - Run 1: Config{Enabled, FastForward=1h}, Accum ≈ 50min. +// - Run 2: Config{Enabled, FastForward=1h} (inherited), Accum ≈ 50min carried forward +// and never exceeding the 1h cap-total budget. +// - Run 2's WorkflowExecutionStarted carries the current TSC snapshot with +// InitialSkippedDuration ≈ 50min, initiator=CRON_SCHEDULE. func (s *TimeSkippingPropagationTestSuite) TestTSPInCron() { env := testcore.NewEnv(s.T()) env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) @@ -1373,10 +1363,10 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInCron() { wfID := tv.WorkflowID() tq := tv.TaskQueue() - bound := &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: durationpb.New(time.Hour), + inputCfg := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), } - inputCfg := &workflowpb.TimeSkippingConfig{Enabled: true, Bound: bound} startWall := time.Now() @@ -1432,25 +1422,27 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInCron() { Reason: "test cleanup: stop cron chain", }) - // ---- Run 1: Config{Enabled, Bound=1h}, Accum ≈ 50min ---- + // ---- Run 1: Config{Enabled, FastForward=1h}, Accum ≈ 50min ---- run1MS := s.getMutableState(env, wfID, run1ID) run1TSI := run1MS.State.ExecutionInfo.GetTimeSkippingInfo() s.NotNil(run1TSI) s.True(proto.Equal(inputCfg, run1TSI.GetConfig()), - "run 1 Config mirrors the start request: Enabled=true, Bound=MaxElapsedDuration(1h)") + "run 1 Config mirrors the start request: Enabled=true, MaxElapsedDuration=1h") // Tolerance absorbs per-skip clock drift plus up to ~60s of cron backoff that may // be skipped while waiting for the first WFT. s.InDelta(float64(50*time.Minute), float64(run1TSI.GetAccumulatedSkippedDuration().AsDuration()), float64(90*time.Second), "run 1 skipped ~50min for t1 before cron rolled it forward") - // ---- Run 2: Config inherited (Enabled + Bound); Accum NOT inherited ---- + // ---- Run 2: Config inherited (Enabled + FastForward); in-flight skip carried forward ---- run2MS := s.getMutableState(env, wfID, run2ID) run2TSI := run2MS.State.ExecutionInfo.GetTimeSkippingInfo() - s.NotNil(run2TSI, "run 2 should have TimeSkippingInfo propagated from run 1's event #1") + s.NotNil(run2TSI, "run 2 should have TimeSkippingInfo propagated from run 1's current config") s.True(proto.Equal(inputCfg, run2TSI.GetConfig()), - "run 2 Config matches run 1's event #1 verbatim: Enabled=true and Bound=MaxElapsedDuration(1h) are inherited") - s.Less(run2TSI.GetAccumulatedSkippedDuration().AsDuration(), 10*time.Minute, - "run 2 Accum is fresh — nowhere near run 1's 50min; the previous run's in-flight skip is NOT carried forward on cron") + "run 2 Config matches run 1's current config: Enabled=true and MaxElapsedDuration=1h are inherited") + s.GreaterOrEqual(run2TSI.GetAccumulatedSkippedDuration().AsDuration(), 45*time.Minute, + "run 2 carries run 1's ~50min in-flight skip forward (cron is a continuation)") + s.LessOrEqual(run2TSI.GetAccumulatedSkippedDuration().AsDuration(), time.Hour+time.Minute, + "cap-total: run 2's accumulated skip never exceeds the inherited MaxElapsedDuration=1h budget") // Run 2's WorkflowExecutionStarted carries the verbatim TSC snapshot from run 1's event #1. hist2, err := env.FrontendClient().GetWorkflowExecutionHistory(ctx, &workflowservice.GetWorkflowExecutionHistoryRequest{ @@ -1466,7 +1458,145 @@ func (s *TimeSkippingPropagationTestSuite) TestTSPInCron() { s.Equal(enumspb.CONTINUE_AS_NEW_INITIATOR_CRON_SCHEDULE, startedAttr.GetInitiator(), "run 2 is initiated by cron schedule") s.True(proto.Equal(inputCfg, startedAttr.GetTimeSkippingConfig()), - "started event carries Enabled=true, Bound=MaxElapsedDuration(1h) — the verbatim snapshot from run 1's event #1") - s.Zero(startedAttr.GetInitialSkippedDuration().AsDuration(), - "started event InitialSkippedDuration is unset for a top-level cron workflow") + "started event carries Enabled=true, MaxElapsedDuration=1h — the snapshot from run 1's current config") + s.InDelta(float64(50*time.Minute), float64(startedAttr.GetTimeSkippingStatePropagation().GetInitialSkippedDuration().AsDuration()), float64(90*time.Second), + "started event InitialSkippedDuration carries run 1's in-flight accumulated skip at cron-rollover time") } + +// TestTSPInCaN verifies end-to-end that one FastForward budget caps +// the whole chain of runs: a continuation inherits the budget already consumed by its +// predecessor, may only skip the remainder, and consuming it disables time skipping. +// +// initTimeSkippingInfo applies the full-cap anchor on a new run: the fast-forward target +// is now + (FastForward - inherited AccumulatedSkippedDuration). Run 2 therefore boots +// with only 1h of the 3h budget left, even though it never skipped anything itself. +// +// Scenario: +// - Run 1 starts with TimeSkippingConfig{Enabled: true, FastForward: 3h}. +// - Run 1 issues StartTimer(t1, 2h). Idle → skips 2h (budget not reached) → +// run1.Accum = 2h. t1 fires; run 1 continues-as-new. +// - Run 2 boots with Config{Enabled, FastForward=3h} and Accum seeded to 2h → +// remaining budget 1h. +// - Run 2's first WFT completes with no commands. Run 2 is idle with the fast-forward +// target as its only skip candidate → skips the remaining 1h, reaches the target, +// and time skipping is disabled (DisabledAfterFastForward=true). run2.Accum = 3h — +// the full chain budget, of which run 2 itself contributed only 1h. +// +// End-state assertions: +// - run1: CONTINUED_AS_NEW, Config.Enabled=true at CaN time (budget not reached), +// FastForward not reached, Accum ≈ 2h. +// - run2: Config.Enabled=false, FastForward.HasReached=true, Accum ≈ 3h. +// - run2 history has exactly one transition, carrying DisabledAfterFastForward=true. +func (s *TimeSkippingPropagationTestSuite) TestTSPInCaN_BudgetCapOverChain() { + env := testcore.NewEnv(s.T()) + env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) + tv := testvars.New(s.T()) + ctx := testcore.NewContext() + + wfType := tv.WorkflowType() + wfID := tv.WorkflowID() + tq := tv.TaskQueue() + + startWall := time.Now() + + start1, err := env.FrontendClient().StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: env.Namespace().String(), + WorkflowId: wfID, + WorkflowType: wfType, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(24 * time.Hour), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + TimeSkippingConfig: &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(3 * time.Hour), + }, + }) + s.NoError(err) + run1ID := start1.RunId + + state := 0 + handler := func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) { + fired := firedTimers(task) + switch { + case state == 0: + state = 1 + return cmdsResponse(timerCmd("t1", 2*time.Hour)), nil + case state == 1 && fired["t1"]: + state = 2 + return cmdsResponse(continueAsNewCmd(wfType, tq)), nil + } + // Run 2's WFTs: no commands — run 2 idles so the fast-forward target is its + // only skip candidate. + return &workflowservice.RespondWorkflowTaskCompletedRequest{}, nil + } + + // Drive run 1 to CONTINUED_AS_NEW. + s.drivePollsUntilRunNotRunning(ctx, env, tv, handler, wfID, run1ID, 20) + + run2MS := s.getMutableStateByID(ctx, env, wfID) + run2ID := run2MS.State.ExecutionState.RunId + s.NotEqual(run1ID, run2ID, "CaN should have created a new run") + + // Serve run 2's first WFT with an empty response; its close-tx skips the remaining + // budget and disables time skipping synchronously. + _, err = env.TaskPoller().PollAndHandleWorkflowTask(tv, handler) + s.NoError(err) + + elapsed := time.Since(startWall) + s.Less(elapsed, 2*time.Minute, "wall-clock elapsed (%s) should be far less than 3h of virtual time", elapsed) + + // ---- Run 1: consumed 2h of the budget but never reached it ---- + run1MS := s.getMutableState(env, wfID, run1ID) + s.Equal(enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW, run1MS.State.ExecutionState.Status) + run1TSI := run1MS.State.ExecutionInfo.GetTimeSkippingInfo() + s.NotNil(run1TSI) + s.True(run1TSI.GetConfig().GetEnabled(), + "run 1 hands the config over still enabled — it consumed only 2h of the 3h budget") + s.False(run1TSI.GetFastForwardInfo().GetHasReached(), + "run 1 must not have reached the fast-forward target") + s.approxDuration(2*time.Hour, run1TSI.GetAccumulatedSkippedDuration().AsDuration(), + "run 1 skipped 2h for t1 before continue-as-new") + + // ---- Run 2: inherits 2h consumed, skips only the remaining 1h, then disables ---- + run2MS = s.getMutableState(env, wfID, run2ID) + run2TSI := run2MS.State.ExecutionInfo.GetTimeSkippingInfo() + s.NotNil(run2TSI, "run 2 should have TimeSkippingInfo propagated from run 1") + s.False(run2TSI.GetConfig().GetEnabled(), + "the chain's budget is exhausted in run 2 → time skipping disabled") + s.True(run2TSI.GetFastForwardInfo().GetHasReached(), + "run 2 reached the fast-forward target inherited from the chain") + s.approxDuration(3*time.Hour, run2TSI.GetAccumulatedSkippedDuration().AsDuration(), + "chain total: run 1 contributed 2h, run 2 only the remaining 1h — the budget caps the chain, not each run") + + // Run 2's history carries exactly one transition: the cap-hit skip+disable. + hist2 := env.GetHistory(env.Namespace().String(), &commonpb.WorkflowExecution{WorkflowId: wfID, RunId: run2ID}) + var transitions []*historypb.HistoryEvent + for _, e := range hist2 { + if e.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED { + transitions = append(transitions, e) + } + } + s.Len(transitions, 1, "run 2 skips exactly once: the remaining budget") + if len(transitions) == 1 { + attrs := transitions[0].GetWorkflowExecutionTimeSkippingTransitionedEventAttributes() + s.True(attrs.GetDisabledAfterFastForward(), + "run 2's only transition is the fast-forward disable") + } + + // Run 2 is idle and RUNNING with skipping disabled; stop it. + _, _ = env.FrontendClient().TerminateWorkflowExecution(ctx, &workflowservice.TerminateWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: wfID, RunId: run2ID}, + Reason: "test cleanup", + }) +} + +// (TestTSPInChildWf_PropagationDisabled removed: DisableChildPropagation is not yet in the API) +// Scenario: +// - Parent starts with TimeSkippingConfig{Enabled: true, DisableChildPropagation: true}. +// - Parent issues StartTimer(t1, 1h) → idle → skips 1h → parent.Accum = 1h. +// - t1 fires. Parent issues StartChild(child) + StartTimer(t2, 1h). The child gets no +// TSC but its TSI has accumulated skipped duration of 1hour. +// +// End-state assertions: diff --git a/tests/timeskipping_test.go b/tests/timeskipping_test.go index 0ffc71ed52..0ec4a9fd8c 100644 --- a/tests/timeskipping_test.go +++ b/tests/timeskipping_test.go @@ -55,7 +55,7 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_FeatureDisabled() { TaskQueue: &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, WorkflowRunTimeout: durationpb.New(100 * time.Second), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }) s.Error(err, "expected error when time skipping is disabled for namespace") } @@ -67,8 +67,9 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_StartWorkflow_DCEnabled() { env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) tv := testvars.New(s.T()) - inputBound := &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: durationpb.New(time.Hour), + inputConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), } resp, err := env.FrontendClient().StartWorkflowExecution(testcore.NewContext(), &workflowservice.StartWorkflowExecutionRequest{ @@ -79,16 +80,13 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_StartWorkflow_DCEnabled() { TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(100 * time.Second), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true, Bound: inputBound}, + TimeSkippingConfig: inputConfig, }) s.NoError(err) ms := s.getMutableState(env, tv.WorkflowID(), resp.RunId) s.True(ms.State.ExecutionInfo.GetTimeSkippingInfo().GetConfig().GetEnabled()) - s.True(proto.Equal(&workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: inputBound, - }, ms.State.ExecutionInfo.GetTimeSkippingInfo().GetConfig())) + s.True(proto.Equal(inputConfig, ms.State.ExecutionInfo.GetTimeSkippingInfo().GetConfig())) } // TestTimeSkipping_SignalWithStart_DCEnabled verifies that SignalWithStartWorkflowExecution @@ -98,8 +96,9 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_SignalWithStart_DCEnabled() { env.OverrideDynamicConfig(dynamicconfig.TimeSkippingEnabled, true) tv := testvars.New(s.T()) - inputBound := &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: durationpb.New(time.Hour), + inputConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), } resp, err := env.FrontendClient().SignalWithStartWorkflowExecution(testcore.NewContext(), &workflowservice.SignalWithStartWorkflowExecutionRequest{ @@ -111,18 +110,12 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_SignalWithStart_DCEnabled() { WorkflowRunTimeout: durationpb.New(100 * time.Second), WorkflowTaskTimeout: durationpb.New(10 * time.Second), SignalName: tv.SignalName(), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: inputBound, - }, + TimeSkippingConfig: inputConfig, }) s.NoError(err) ms := s.getMutableState(env, tv.WorkflowID(), resp.RunId) - s.True(proto.Equal(&workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: inputBound, - }, ms.State.ExecutionInfo.GetTimeSkippingInfo().GetConfig())) + s.True(proto.Equal(inputConfig, ms.State.ExecutionInfo.GetTimeSkippingInfo().GetConfig())) } // TestTimeSkipping_ExecuteMultiOperation_DCEnabled verifies that a StartWorkflow inside @@ -134,11 +127,9 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_ExecuteMultiOperation_DCEnabled tv := testvars.New(s.T()) maxElapsedDuration := time.Hour - inputConfig := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{ - MaxElapsedDuration: durationpb.New(maxElapsedDuration), - }, + inputConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(maxElapsedDuration), } resp, err := env.FrontendClient().ExecuteMultiOperation(testcore.NewContext(), &workflowservice.ExecuteMultiOperationRequest{ @@ -182,8 +173,8 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_ExecuteMultiOperation_DCEnabled // TestTimeSkipping_UpdateWorkflowOptions_DCEnabled exercises the full UpdateWorkflowExecutionOptions // lifecycle for TimeSkippingConfig: // 1. Start workflow with no time-skipping — assert mutable state has no config. -// 2. First update: enable with MaxElapsedDuration bound — check MS and event 1 attributes. -// 3. Second update: change the MaxElapsedDuration value — check MS and event 2 attributes. +// 2. First update: enable with max_elapsed_duration — check MS and event 1 attributes. +// 3. Second update: change the max_elapsed_duration value — check MS and event 2 attributes. // 4. Third update: disable (Enabled=false) — check MS and event 3 attributes. // 5. Assert exactly 3 WorkflowExecutionOptionsUpdated events appear in history. func (s *TimeSkippingTestSuite) TestTimeSkipping_UpdateWorkflowOptions_DCEnabled() { @@ -219,7 +210,7 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_UpdateWorkflowOptions_DCEnabled } return events } - updateOptions := func(cfg *workflowpb.TimeSkippingConfig) { + updateOptions := func(cfg *commonpb.TimeSkippingConfig) { _, err := env.FrontendClient().UpdateWorkflowExecutionOptions(testcore.NewContext(), &workflowservice.UpdateWorkflowExecutionOptionsRequest{ Namespace: env.Namespace().String(), WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: tv.WorkflowID(), RunId: runID}, @@ -233,10 +224,10 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_UpdateWorkflowOptions_DCEnabled ms := s.getMutableState(env, tv.WorkflowID(), runID) s.Nil(ms.State.ExecutionInfo.GetTimeSkippingInfo().GetConfig()) - // First update: enable with a bound. - config1 := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, + // First update: enable with a max_elapsed_duration. + config1 := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour), } updateOptions(config1) @@ -246,10 +237,10 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_UpdateWorkflowOptions_DCEnabled s.Len(events, 1) s.True(proto.Equal(config1, events[0].GetWorkflowExecutionOptionsUpdatedEventAttributes().GetTimeSkippingConfig())) - // Second update: change the bound duration. - config2 := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(2 * time.Hour)}, + // Second update: change the max_elapsed_duration duration. + config2 := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(2 * time.Hour), } updateOptions(config2) @@ -260,7 +251,7 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_UpdateWorkflowOptions_DCEnabled s.True(proto.Equal(config2, events[1].GetWorkflowExecutionOptionsUpdatedEventAttributes().GetTimeSkippingConfig())) // Third update: disable time-skipping. - config3 := &workflowpb.TimeSkippingConfig{Enabled: false} + config3 := &commonpb.TimeSkippingConfig{Enabled: false} updateOptions(config3) ms = s.getMutableState(env, tv.WorkflowID(), runID) @@ -312,10 +303,9 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_ResetWithUpdateOptions() { s.NotZero(wftCompletedEventID) // Reset with PostResetOperations that sets TimeSkippingConfig. - inputConfig := &workflowpb.TimeSkippingConfig{ - Enabled: true, - Bound: &workflowpb.TimeSkippingConfig_MaxElapsedDuration{MaxElapsedDuration: durationpb.New(time.Hour)}, - } + inputConfig := &commonpb.TimeSkippingConfig{ + Enabled: true, + FastForward: durationpb.New(time.Hour)} resetResp, err := env.FrontendClient().ResetWorkflowExecution(ctx, &workflowservice.ResetWorkflowExecutionRequest{ Namespace: env.Namespace().String(), WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: tv.WorkflowID(), RunId: runID}, @@ -386,7 +376,7 @@ func (s *TimeSkippingTestSuite) startWorkflowWithTimeSkipping(env *testcore.Test TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(runTimeout), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }) s.NoError(err) return resp.GetRunId() @@ -639,7 +629,7 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_PendingSignalExternalBlocksSkip Input: input, WorkflowRunTimeout: durationpb.New(2 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, }) s.NoError(err) @@ -716,7 +706,7 @@ func (s *TimeSkippingTestSuite) TestTimeSkipping_StartWithDelay() { TaskQueue: tv.TaskQueue(), WorkflowRunTimeout: durationpb.New(24 * time.Hour), WorkflowTaskTimeout: durationpb.New(10 * time.Second), - TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + TimeSkippingConfig: &commonpb.TimeSkippingConfig{Enabled: true}, WorkflowStartDelay: durationpb.New(startDelay), }) s.NoError(err)