diff --git a/.gitignore b/.gitignore index f6e1955edc..54dbf13b40 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ # Ignoring AI agent files .agents/ +**/spec.md +**/CLAUDE.md diff --git a/api/enums/v1/task.go-helpers.pb.go b/api/enums/v1/task.go-helpers.pb.go index fdf9ecdb97..fe649d867a 100644 --- a/api/enums/v1/task.go-helpers.pb.go +++ b/api/enums/v1/task.go-helpers.pb.go @@ -57,6 +57,7 @@ var ( "ReplicationSyncVersionedTransition": 31, "ChasmPure": 32, "Chasm": 33, + "TimeSkipping": 34, } ) diff --git a/api/enums/v1/task.pb.go b/api/enums/v1/task.pb.go index 2b902a95b2..4a310896c8 100644 --- a/api/enums/v1/task.pb.go +++ b/api/enums/v1/task.pb.go @@ -126,6 +126,8 @@ const ( TASK_TYPE_CHASM_PURE TaskType = 32 // A task with side effects generated by a CHASM component. TASK_TYPE_CHASM TaskType = 33 + // A task that triggers time skipping logic for a workflow execution. + TASK_TYPE_TIME_SKIPPING TaskType = 34 ) // Enum value maps for TaskType. @@ -162,6 +164,7 @@ var ( 31: "TASK_TYPE_REPLICATION_SYNC_VERSIONED_TRANSITION", 32: "TASK_TYPE_CHASM_PURE", 33: "TASK_TYPE_CHASM", + 34: "TASK_TYPE_TIME_SKIPPING", } TaskType_value = map[string]int32{ "TASK_TYPE_UNSPECIFIED": 0, @@ -195,6 +198,7 @@ var ( "TASK_TYPE_REPLICATION_SYNC_VERSIONED_TRANSITION": 31, "TASK_TYPE_CHASM_PURE": 32, "TASK_TYPE_CHASM": 33, + "TASK_TYPE_TIME_SKIPPING": 34, } ) @@ -278,6 +282,8 @@ func (x TaskType) String() string { return "ChasmPure" case TASK_TYPE_CHASM: return "Chasm" + case TASK_TYPE_TIME_SKIPPING: + return "TimeSkipping" default: return strconv.Itoa(int(x)) } @@ -367,7 +373,7 @@ const file_temporal_server_api_enums_v1_task_proto_rawDesc = "" + "TaskSource\x12\x1b\n" + "\x17TASK_SOURCE_UNSPECIFIED\x10\x00\x12\x17\n" + "\x13TASK_SOURCE_HISTORY\x10\x01\x12\x1a\n" + - "\x16TASK_SOURCE_DB_BACKLOG\x10\x02*\xb6\t\n" + + "\x16TASK_SOURCE_DB_BACKLOG\x10\x02*\xd3\t\n" + "\bTaskType\x12\x19\n" + "\x15TASK_TYPE_UNSPECIFIED\x10\x00\x12!\n" + "\x1dTASK_TYPE_REPLICATION_HISTORY\x10\x01\x12'\n" + @@ -400,7 +406,8 @@ const file_temporal_server_api_enums_v1_task_proto_rawDesc = "" + "\x1eTASK_TYPE_REPLICATION_SYNC_HSM\x10\x1e\x123\n" + "/TASK_TYPE_REPLICATION_SYNC_VERSIONED_TRANSITION\x10\x1f\x12\x18\n" + "\x14TASK_TYPE_CHASM_PURE\x10 \x12\x13\n" + - "\x0fTASK_TYPE_CHASM\x10!\"\x04\b\t\x10\t\"\x04\b\v\x10\v\"\x04\b\x17\x10\x17*\\\n" + + "\x0fTASK_TYPE_CHASM\x10!\x12\x1b\n" + + "\x17TASK_TYPE_TIME_SKIPPING\x10\"\"\x04\b\t\x10\t\"\x04\b\v\x10\v\"\x04\b\x17\x10\x17*\\\n" + "\fTaskPriority\x12\x1d\n" + "\x19TASK_PRIORITY_UNSPECIFIED\x10\x00\x12\x16\n" + "\x12TASK_PRIORITY_HIGH\x10\x01\x12\x15\n" + diff --git a/api/persistence/v1/executions.go-helpers.pb.go b/api/persistence/v1/executions.go-helpers.pb.go index 0db1c1b452..9a25605460 100644 --- a/api/persistence/v1/executions.go-helpers.pb.go +++ b/api/persistence/v1/executions.go-helpers.pb.go @@ -116,6 +116,80 @@ func (this *LastNotifiedTargetVersion) Equal(that interface{}) bool { return proto.Equal(this, that1) } +// Marshal an object of type TimeSkippingInfo to the protobuf v3 wire format +func (val *TimeSkippingInfo) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type TimeSkippingInfo from the protobuf v3 wire format +func (val *TimeSkippingInfo) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *TimeSkippingInfo) Size() int { + return proto.Size(val) +} + +// Equal returns whether two TimeSkippingInfo 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 *TimeSkippingInfo) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *TimeSkippingInfo + switch t := that.(type) { + case *TimeSkippingInfo: + that1 = t + case TimeSkippingInfo: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type TimeSkippedDetails to the protobuf v3 wire format +func (val *TimeSkippedDetails) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type TimeSkippedDetails from the protobuf v3 wire format +func (val *TimeSkippedDetails) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *TimeSkippedDetails) Size() int { + return proto.Size(val) +} + +// Equal returns whether two TimeSkippedDetails 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 *TimeSkippedDetails) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *TimeSkippedDetails + switch t := that.(type) { + case *TimeSkippedDetails: + that1 = t + case TimeSkippedDetails: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + // Marshal an object of type ExecutionStats to the protobuf v3 wire format func (val *ExecutionStats) Marshal() ([]byte, error) { return proto.Marshal(val) diff --git a/api/persistence/v1/executions.pb.go b/api/persistence/v1/executions.pb.go index 36c3afdba8..62ab82a364 100644 --- a/api/persistence/v1/executions.pb.go +++ b/api/persistence/v1/executions.pb.go @@ -372,8 +372,10 @@ type WorkflowExecutionInfo struct { // // Wrapper distinguishes "never declined" (nil) from "declined unversioned" (non-nil, nil version). DeclinedTargetVersionUpgrade *v17.DeclinedTargetVersionUpgrade `protobuf:"bytes,114,opt,name=declined_target_version_upgrade,json=declinedTargetVersionUpgrade,proto3" json:"declined_target_version_upgrade,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Time skipping info that contains the config and duration offsets of the time skipping for the workflow. + TimeSkippingInfo *TimeSkippingInfo `protobuf:"bytes,120,opt,name=time_skipping_info,json=timeSkippingInfo,proto3" json:"time_skipping_info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *WorkflowExecutionInfo) Reset() { @@ -1145,6 +1147,13 @@ func (x *WorkflowExecutionInfo) GetDeclinedTargetVersionUpgrade() *v17.DeclinedT return nil } +func (x *WorkflowExecutionInfo) GetTimeSkippingInfo() *TimeSkippingInfo { + if x != nil { + return x.TimeSkippingInfo + } + return nil +} + type isWorkflowExecutionInfo_LastWorkflowTaskFailure interface { isWorkflowExecutionInfo_LastWorkflowTaskFailure() } @@ -1210,6 +1219,130 @@ func (x *LastNotifiedTargetVersion) GetDeploymentVersion() *v18.WorkerDeployment return nil } +type TimeSkippingInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + // metadata + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // runtime history + TimeSkippedDetails []*TimeSkippedDetails `protobuf:"bytes,2,rep,name=time_skipped_details,json=timeSkippedDetails,proto3" json:"time_skipped_details,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TimeSkippingInfo) Reset() { + *x = TimeSkippingInfo{} + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TimeSkippingInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeSkippingInfo) ProtoMessage() {} + +func (x *TimeSkippingInfo) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeSkippingInfo.ProtoReflect.Descriptor instead. +func (*TimeSkippingInfo) Descriptor() ([]byte, []int) { + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{3} +} + +func (x *TimeSkippingInfo) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *TimeSkippingInfo) GetTimeSkippedDetails() []*TimeSkippedDetails { + if x != nil { + return x.TimeSkippedDetails + } + return nil +} + +type TimeSkippedDetails struct { + state protoimpl.MessageState `protogen:"open.v1"` + // (-- api-linter: core::0142::time-field-names=disabled + // + // aip.dev/not-precedent: Ignoring lint rules. --) + SkippingTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=skipping_time,json=skippingTime,proto3" json:"skipping_time,omitempty"` + // (-- api-linter: core::0142::time-field-names=disabled + // + // api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: Ignoring lint rules. --) + DurationToSkip *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=duration_to_skip,json=durationToSkip,proto3" json:"duration_to_skip,omitempty"` + // (-- api-linter: core::0140::prepositions=disabled + // + // aip.dev/not-precedent: Ignoring lint rules. --) + ToTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=to_time,json=toTime,proto3" json:"to_time,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TimeSkippedDetails) Reset() { + *x = TimeSkippedDetails{} + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TimeSkippedDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeSkippedDetails) ProtoMessage() {} + +func (x *TimeSkippedDetails) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeSkippedDetails.ProtoReflect.Descriptor instead. +func (*TimeSkippedDetails) Descriptor() ([]byte, []int) { + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{4} +} + +func (x *TimeSkippedDetails) GetSkippingTime() *timestamppb.Timestamp { + if x != nil { + return x.SkippingTime + } + return nil +} + +func (x *TimeSkippedDetails) GetDurationToSkip() *timestamppb.Timestamp { + if x != nil { + return x.DurationToSkip + } + return nil +} + +func (x *TimeSkippedDetails) GetToTime() *timestamppb.Timestamp { + if x != nil { + return x.ToTime + } + return nil +} + type ExecutionStats struct { state protoimpl.MessageState `protogen:"open.v1"` HistorySize int64 `protobuf:"varint,1,opt,name=history_size,json=historySize,proto3" json:"history_size,omitempty"` @@ -1225,7 +1358,7 @@ type ExecutionStats struct { func (x *ExecutionStats) Reset() { *x = ExecutionStats{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[3] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1237,7 +1370,7 @@ func (x *ExecutionStats) String() string { func (*ExecutionStats) ProtoMessage() {} func (x *ExecutionStats) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[3] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1250,7 +1383,7 @@ func (x *ExecutionStats) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecutionStats.ProtoReflect.Descriptor instead. func (*ExecutionStats) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{3} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{5} } func (x *ExecutionStats) GetHistorySize() int64 { @@ -1293,7 +1426,7 @@ type WorkflowExecutionState struct { func (x *WorkflowExecutionState) Reset() { *x = WorkflowExecutionState{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[4] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1305,7 +1438,7 @@ func (x *WorkflowExecutionState) String() string { func (*WorkflowExecutionState) ProtoMessage() {} func (x *WorkflowExecutionState) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[4] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1318,7 +1451,7 @@ func (x *WorkflowExecutionState) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowExecutionState.ProtoReflect.Descriptor instead. func (*WorkflowExecutionState) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{4} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{6} } func (x *WorkflowExecutionState) GetCreateRequestId() string { @@ -1380,7 +1513,7 @@ type RequestIDInfo struct { func (x *RequestIDInfo) Reset() { *x = RequestIDInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[5] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1392,7 +1525,7 @@ func (x *RequestIDInfo) String() string { func (*RequestIDInfo) ProtoMessage() {} func (x *RequestIDInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[5] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1405,7 +1538,7 @@ func (x *RequestIDInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestIDInfo.ProtoReflect.Descriptor instead. func (*RequestIDInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{5} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{7} } func (x *RequestIDInfo) GetEventType() v11.EventType { @@ -1457,7 +1590,7 @@ type TransferTaskInfo struct { func (x *TransferTaskInfo) Reset() { *x = TransferTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[6] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1469,7 +1602,7 @@ func (x *TransferTaskInfo) String() string { func (*TransferTaskInfo) ProtoMessage() {} func (x *TransferTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[6] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1482,7 +1615,7 @@ func (x *TransferTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use TransferTaskInfo.ProtoReflect.Descriptor instead. func (*TransferTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{6} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{8} } func (x *TransferTaskInfo) GetNamespaceId() string { @@ -1667,7 +1800,7 @@ type ReplicationTaskInfo struct { func (x *ReplicationTaskInfo) Reset() { *x = ReplicationTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[7] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1679,7 +1812,7 @@ func (x *ReplicationTaskInfo) String() string { func (*ReplicationTaskInfo) ProtoMessage() {} func (x *ReplicationTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[7] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1692,7 +1825,7 @@ func (x *ReplicationTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ReplicationTaskInfo.ProtoReflect.Descriptor instead. func (*ReplicationTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{7} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{9} } func (x *ReplicationTaskInfo) GetNamespaceId() string { @@ -1864,7 +1997,7 @@ type VisibilityTaskInfo struct { func (x *VisibilityTaskInfo) Reset() { *x = VisibilityTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[8] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1876,7 +2009,7 @@ func (x *VisibilityTaskInfo) String() string { func (*VisibilityTaskInfo) ProtoMessage() {} func (x *VisibilityTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[8] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1889,7 +2022,7 @@ func (x *VisibilityTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use VisibilityTaskInfo.ProtoReflect.Descriptor instead. func (*VisibilityTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{8} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{10} } func (x *VisibilityTaskInfo) GetNamespaceId() string { @@ -2020,7 +2153,7 @@ type TimerTaskInfo struct { func (x *TimerTaskInfo) Reset() { *x = TimerTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[9] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2032,7 +2165,7 @@ func (x *TimerTaskInfo) String() string { func (*TimerTaskInfo) ProtoMessage() {} func (x *TimerTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[9] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2045,7 +2178,7 @@ func (x *TimerTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use TimerTaskInfo.ProtoReflect.Descriptor instead. func (*TimerTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{9} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{11} } func (x *TimerTaskInfo) GetNamespaceId() string { @@ -2202,7 +2335,7 @@ type ArchivalTaskInfo struct { func (x *ArchivalTaskInfo) Reset() { *x = ArchivalTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[10] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2214,7 +2347,7 @@ func (x *ArchivalTaskInfo) String() string { func (*ArchivalTaskInfo) ProtoMessage() {} func (x *ArchivalTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[10] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2227,7 +2360,7 @@ func (x *ArchivalTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ArchivalTaskInfo.ProtoReflect.Descriptor instead. func (*ArchivalTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{10} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{12} } func (x *ArchivalTaskInfo) GetTaskId() int64 { @@ -2301,7 +2434,7 @@ type OutboundTaskInfo struct { func (x *OutboundTaskInfo) Reset() { *x = OutboundTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[11] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2313,7 +2446,7 @@ func (x *OutboundTaskInfo) String() string { func (*OutboundTaskInfo) ProtoMessage() {} func (x *OutboundTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[11] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2326,7 +2459,7 @@ func (x *OutboundTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use OutboundTaskInfo.ProtoReflect.Descriptor instead. func (*OutboundTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{11} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{13} } func (x *OutboundTaskInfo) GetNamespaceId() string { @@ -2430,7 +2563,7 @@ type NexusInvocationTaskInfo struct { func (x *NexusInvocationTaskInfo) Reset() { *x = NexusInvocationTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[12] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2442,7 +2575,7 @@ func (x *NexusInvocationTaskInfo) String() string { func (*NexusInvocationTaskInfo) ProtoMessage() {} func (x *NexusInvocationTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[12] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2455,7 +2588,7 @@ func (x *NexusInvocationTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use NexusInvocationTaskInfo.ProtoReflect.Descriptor instead. func (*NexusInvocationTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{12} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{14} } func (x *NexusInvocationTaskInfo) GetAttempt() int32 { @@ -2474,7 +2607,7 @@ type NexusCancelationTaskInfo struct { func (x *NexusCancelationTaskInfo) Reset() { *x = NexusCancelationTaskInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[13] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2486,7 +2619,7 @@ func (x *NexusCancelationTaskInfo) String() string { func (*NexusCancelationTaskInfo) ProtoMessage() {} func (x *NexusCancelationTaskInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[13] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2499,7 +2632,7 @@ func (x *NexusCancelationTaskInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use NexusCancelationTaskInfo.ProtoReflect.Descriptor instead. func (*NexusCancelationTaskInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{13} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{15} } func (x *NexusCancelationTaskInfo) GetAttempt() int32 { @@ -2607,7 +2740,7 @@ type ActivityInfo struct { func (x *ActivityInfo) Reset() { *x = ActivityInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[14] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2619,7 +2752,7 @@ func (x *ActivityInfo) String() string { func (*ActivityInfo) ProtoMessage() {} func (x *ActivityInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[14] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2632,7 +2765,7 @@ func (x *ActivityInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ActivityInfo.ProtoReflect.Descriptor instead. func (*ActivityInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{14} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{16} } func (x *ActivityInfo) GetVersion() int64 { @@ -3014,7 +3147,7 @@ type TimerInfo struct { func (x *TimerInfo) Reset() { *x = TimerInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[15] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3026,7 +3159,7 @@ func (x *TimerInfo) String() string { func (*TimerInfo) ProtoMessage() {} func (x *TimerInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[15] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3039,7 +3172,7 @@ func (x *TimerInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use TimerInfo.ProtoReflect.Descriptor instead. func (*TimerInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{15} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{17} } func (x *TimerInfo) GetVersion() int64 { @@ -3107,7 +3240,7 @@ type ChildExecutionInfo struct { func (x *ChildExecutionInfo) Reset() { *x = ChildExecutionInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[16] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3119,7 +3252,7 @@ func (x *ChildExecutionInfo) String() string { func (*ChildExecutionInfo) ProtoMessage() {} func (x *ChildExecutionInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[16] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3132,7 +3265,7 @@ func (x *ChildExecutionInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ChildExecutionInfo.ProtoReflect.Descriptor instead. func (*ChildExecutionInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{16} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{18} } func (x *ChildExecutionInfo) GetVersion() int64 { @@ -3247,7 +3380,7 @@ type RequestCancelInfo struct { func (x *RequestCancelInfo) Reset() { *x = RequestCancelInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[17] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3259,7 +3392,7 @@ func (x *RequestCancelInfo) String() string { func (*RequestCancelInfo) ProtoMessage() {} func (x *RequestCancelInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[17] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3272,7 +3405,7 @@ func (x *RequestCancelInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestCancelInfo.ProtoReflect.Descriptor instead. func (*RequestCancelInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{17} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{19} } func (x *RequestCancelInfo) GetVersion() int64 { @@ -3324,7 +3457,7 @@ type SignalInfo struct { func (x *SignalInfo) Reset() { *x = SignalInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[18] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3336,7 +3469,7 @@ func (x *SignalInfo) String() string { func (*SignalInfo) ProtoMessage() {} func (x *SignalInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[18] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3349,7 +3482,7 @@ func (x *SignalInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SignalInfo.ProtoReflect.Descriptor instead. func (*SignalInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{18} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{20} } func (x *SignalInfo) GetVersion() int64 { @@ -3399,7 +3532,7 @@ type Checksum struct { func (x *Checksum) Reset() { *x = Checksum{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[19] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3411,7 +3544,7 @@ func (x *Checksum) String() string { func (*Checksum) ProtoMessage() {} func (x *Checksum) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[19] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3424,7 +3557,7 @@ func (x *Checksum) ProtoReflect() protoreflect.Message { // Deprecated: Use Checksum.ProtoReflect.Descriptor instead. func (*Checksum) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{19} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{21} } func (x *Checksum) GetVersion() int32 { @@ -3462,7 +3595,7 @@ type Callback struct { func (x *Callback) Reset() { *x = Callback{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[20] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3474,7 +3607,7 @@ func (x *Callback) String() string { func (*Callback) ProtoMessage() {} func (x *Callback) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[20] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3487,7 +3620,7 @@ func (x *Callback) ProtoReflect() protoreflect.Message { // Deprecated: Use Callback.ProtoReflect.Descriptor instead. func (*Callback) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{20} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{22} } func (x *Callback) GetVariant() isCallback_Variant { @@ -3554,7 +3687,7 @@ type HSMCompletionCallbackArg struct { func (x *HSMCompletionCallbackArg) Reset() { *x = HSMCompletionCallbackArg{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[21] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3566,7 +3699,7 @@ func (x *HSMCompletionCallbackArg) String() string { func (*HSMCompletionCallbackArg) ProtoMessage() {} func (x *HSMCompletionCallbackArg) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[21] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3579,7 +3712,7 @@ func (x *HSMCompletionCallbackArg) ProtoReflect() protoreflect.Message { // Deprecated: Use HSMCompletionCallbackArg.ProtoReflect.Descriptor instead. func (*HSMCompletionCallbackArg) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{21} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{23} } func (x *HSMCompletionCallbackArg) GetNamespaceId() string { @@ -3636,7 +3769,7 @@ type CallbackInfo struct { func (x *CallbackInfo) Reset() { *x = CallbackInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[22] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3648,7 +3781,7 @@ func (x *CallbackInfo) String() string { func (*CallbackInfo) ProtoMessage() {} func (x *CallbackInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[22] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3661,7 +3794,7 @@ func (x *CallbackInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use CallbackInfo.ProtoReflect.Descriptor instead. func (*CallbackInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{22} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{24} } func (x *CallbackInfo) GetCallback() *Callback { @@ -3782,7 +3915,7 @@ type NexusOperationInfo struct { func (x *NexusOperationInfo) Reset() { *x = NexusOperationInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[23] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3794,7 +3927,7 @@ func (x *NexusOperationInfo) String() string { func (*NexusOperationInfo) ProtoMessage() {} func (x *NexusOperationInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[23] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3807,7 +3940,7 @@ func (x *NexusOperationInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use NexusOperationInfo.ProtoReflect.Descriptor instead. func (*NexusOperationInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{23} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{25} } func (x *NexusOperationInfo) GetEndpoint() string { @@ -3952,7 +4085,7 @@ type NexusOperationCancellationInfo struct { func (x *NexusOperationCancellationInfo) Reset() { *x = NexusOperationCancellationInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[24] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3964,7 +4097,7 @@ func (x *NexusOperationCancellationInfo) String() string { func (*NexusOperationCancellationInfo) ProtoMessage() {} func (x *NexusOperationCancellationInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[24] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3977,7 +4110,7 @@ func (x *NexusOperationCancellationInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use NexusOperationCancellationInfo.ProtoReflect.Descriptor instead. func (*NexusOperationCancellationInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{24} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{26} } func (x *NexusOperationCancellationInfo) GetRequestedTime() *timestamppb.Timestamp { @@ -4040,7 +4173,7 @@ type ResetChildInfo struct { func (x *ResetChildInfo) Reset() { *x = ResetChildInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[25] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4052,7 +4185,7 @@ func (x *ResetChildInfo) String() string { func (*ResetChildInfo) ProtoMessage() {} func (x *ResetChildInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[25] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4065,7 +4198,7 @@ func (x *ResetChildInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetChildInfo.ProtoReflect.Descriptor instead. func (*ResetChildInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{25} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{27} } func (x *ResetChildInfo) GetShouldTerminateAndStart() bool { @@ -4091,7 +4224,7 @@ type WorkflowPauseInfo struct { func (x *WorkflowPauseInfo) Reset() { *x = WorkflowPauseInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[26] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4103,7 +4236,7 @@ func (x *WorkflowPauseInfo) String() string { func (*WorkflowPauseInfo) ProtoMessage() {} func (x *WorkflowPauseInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[26] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4116,7 +4249,7 @@ func (x *WorkflowPauseInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowPauseInfo.ProtoReflect.Descriptor instead. func (*WorkflowPauseInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{26} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{28} } func (x *WorkflowPauseInfo) GetPauseTime() *timestamppb.Timestamp { @@ -4158,7 +4291,7 @@ type TransferTaskInfo_CloseExecutionTaskDetails struct { func (x *TransferTaskInfo_CloseExecutionTaskDetails) Reset() { *x = TransferTaskInfo_CloseExecutionTaskDetails{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[35] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4170,7 +4303,7 @@ func (x *TransferTaskInfo_CloseExecutionTaskDetails) String() string { func (*TransferTaskInfo_CloseExecutionTaskDetails) ProtoMessage() {} func (x *TransferTaskInfo_CloseExecutionTaskDetails) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[35] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4183,7 +4316,7 @@ func (x *TransferTaskInfo_CloseExecutionTaskDetails) ProtoReflect() protoreflect // Deprecated: Use TransferTaskInfo_CloseExecutionTaskDetails.ProtoReflect.Descriptor instead. func (*TransferTaskInfo_CloseExecutionTaskDetails) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{6, 0} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{8, 0} } func (x *TransferTaskInfo_CloseExecutionTaskDetails) GetCanSkipVisibilityArchival() bool { @@ -4207,7 +4340,7 @@ type ActivityInfo_UseWorkflowBuildIdInfo struct { func (x *ActivityInfo_UseWorkflowBuildIdInfo) Reset() { *x = ActivityInfo_UseWorkflowBuildIdInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[36] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4219,7 +4352,7 @@ func (x *ActivityInfo_UseWorkflowBuildIdInfo) String() string { func (*ActivityInfo_UseWorkflowBuildIdInfo) ProtoMessage() {} func (x *ActivityInfo_UseWorkflowBuildIdInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[36] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4232,7 +4365,7 @@ func (x *ActivityInfo_UseWorkflowBuildIdInfo) ProtoReflect() protoreflect.Messag // Deprecated: Use ActivityInfo_UseWorkflowBuildIdInfo.ProtoReflect.Descriptor instead. func (*ActivityInfo_UseWorkflowBuildIdInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{14, 0} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{16, 0} } func (x *ActivityInfo_UseWorkflowBuildIdInfo) GetLastUsedBuildId() string { @@ -4264,7 +4397,7 @@ type ActivityInfo_PauseInfo struct { func (x *ActivityInfo_PauseInfo) Reset() { *x = ActivityInfo_PauseInfo{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[37] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4276,7 +4409,7 @@ func (x *ActivityInfo_PauseInfo) String() string { func (*ActivityInfo_PauseInfo) ProtoMessage() {} func (x *ActivityInfo_PauseInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[37] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4289,7 +4422,7 @@ func (x *ActivityInfo_PauseInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ActivityInfo_PauseInfo.ProtoReflect.Descriptor instead. func (*ActivityInfo_PauseInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{14, 1} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{16, 1} } func (x *ActivityInfo_PauseInfo) GetPauseTime() *timestamppb.Timestamp { @@ -4354,7 +4487,7 @@ type ActivityInfo_PauseInfo_Manual struct { func (x *ActivityInfo_PauseInfo_Manual) Reset() { *x = ActivityInfo_PauseInfo_Manual{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[38] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4366,7 +4499,7 @@ func (x *ActivityInfo_PauseInfo_Manual) String() string { func (*ActivityInfo_PauseInfo_Manual) ProtoMessage() {} func (x *ActivityInfo_PauseInfo_Manual) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[38] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4379,7 +4512,7 @@ func (x *ActivityInfo_PauseInfo_Manual) ProtoReflect() protoreflect.Message { // Deprecated: Use ActivityInfo_PauseInfo_Manual.ProtoReflect.Descriptor instead. func (*ActivityInfo_PauseInfo_Manual) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{14, 1, 0} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{16, 1, 0} } func (x *ActivityInfo_PauseInfo_Manual) GetIdentity() string { @@ -4411,7 +4544,7 @@ type Callback_Nexus struct { func (x *Callback_Nexus) Reset() { *x = Callback_Nexus{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[39] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4423,7 +4556,7 @@ func (x *Callback_Nexus) String() string { func (*Callback_Nexus) ProtoMessage() {} func (x *Callback_Nexus) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[39] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4436,7 +4569,7 @@ func (x *Callback_Nexus) ProtoReflect() protoreflect.Message { // Deprecated: Use Callback_Nexus.ProtoReflect.Descriptor instead. func (*Callback_Nexus) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{20, 0} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{22, 0} } func (x *Callback_Nexus) GetUrl() string { @@ -4473,7 +4606,7 @@ type Callback_HSM struct { func (x *Callback_HSM) Reset() { *x = Callback_HSM{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[40] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4485,7 +4618,7 @@ func (x *Callback_HSM) String() string { func (*Callback_HSM) ProtoMessage() {} func (x *Callback_HSM) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[40] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4498,7 +4631,7 @@ func (x *Callback_HSM) ProtoReflect() protoreflect.Message { // Deprecated: Use Callback_HSM.ProtoReflect.Descriptor instead. func (*Callback_HSM) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{20, 1} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{22, 1} } func (x *Callback_HSM) GetNamespaceId() string { @@ -4545,7 +4678,7 @@ type CallbackInfo_WorkflowClosed struct { func (x *CallbackInfo_WorkflowClosed) Reset() { *x = CallbackInfo_WorkflowClosed{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[42] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4557,7 +4690,7 @@ func (x *CallbackInfo_WorkflowClosed) String() string { func (*CallbackInfo_WorkflowClosed) ProtoMessage() {} func (x *CallbackInfo_WorkflowClosed) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[42] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4570,7 +4703,7 @@ func (x *CallbackInfo_WorkflowClosed) ProtoReflect() protoreflect.Message { // Deprecated: Use CallbackInfo_WorkflowClosed.ProtoReflect.Descriptor instead. func (*CallbackInfo_WorkflowClosed) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{22, 0} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{24, 0} } type CallbackInfo_Trigger struct { @@ -4585,7 +4718,7 @@ type CallbackInfo_Trigger struct { func (x *CallbackInfo_Trigger) Reset() { *x = CallbackInfo_Trigger{} - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[43] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4597,7 +4730,7 @@ func (x *CallbackInfo_Trigger) String() string { func (*CallbackInfo_Trigger) ProtoMessage() {} func (x *CallbackInfo_Trigger) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[43] + mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4610,7 +4743,7 @@ func (x *CallbackInfo_Trigger) ProtoReflect() protoreflect.Message { // Deprecated: Use CallbackInfo_Trigger.ProtoReflect.Descriptor instead. func (*CallbackInfo_Trigger) Descriptor() ([]byte, []int) { - return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{22, 1} + return file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP(), []int{24, 1} } func (x *CallbackInfo_Trigger) GetVariant() isCallbackInfo_Trigger_Variant { @@ -4660,7 +4793,7 @@ const file_temporal_server_api_persistence_v1_executions_proto_rawDesc = "" + "\x03key\x18\x01 \x01(\x05R\x03key\x12D\n" + "\x05value\x18\x02 \x01(\v2..temporal.server.api.persistence.v1.QueueStateR\x05value:\x028\x01J\x04\b\x04\x10\x05J\x04\b\x05\x10\x06J\x04\b\b\x10\tJ\x04\b\t\x10\n" + "J\x04\b\n" + - "\x10\vJ\x04\b\v\x10\fJ\x04\b\f\x10\rJ\x04\b\x0e\x10\x0fJ\x04\b\x0f\x10\x10J\x04\b\x10\x10\x11\"\xdbB\n" + + "\x10\vJ\x04\b\v\x10\fJ\x04\b\f\x10\rJ\x04\b\x0e\x10\x0fJ\x04\b\x0f\x10\x10J\x04\b\x10\x10\x11\"\xbfC\n" + "\x15WorkflowExecutionInfo\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12\x1f\n" + "\vworkflow_id\x18\x02 \x01(\tR\n" + @@ -4772,7 +4905,8 @@ const file_temporal_server_api_persistence_v1_executions_proto_rawDesc = "" + " last_workflow_task_failure_cause\x18k \x01(\x0e2..temporal.api.enums.v1.WorkflowTaskFailedCauseH\x00R\x1clastWorkflowTaskFailureCause\x12m\n" + "!last_workflow_task_timed_out_type\x18l \x01(\x0e2\".temporal.api.enums.v1.TimeoutTypeH\x00R\x1clastWorkflowTaskTimedOutType\x12~\n" + "\x1clast_notified_target_version\x18q \x01(\v2=.temporal.server.api.persistence.v1.LastNotifiedTargetVersionR\x19lastNotifiedTargetVersion\x12|\n" + - "\x1fdeclined_target_version_upgrade\x18r \x01(\v25.temporal.api.history.v1.DeclinedTargetVersionUpgradeR\x1cdeclinedTargetVersionUpgrade\x1ad\n" + + "\x1fdeclined_target_version_upgrade\x18r \x01(\v25.temporal.api.history.v1.DeclinedTargetVersionUpgradeR\x1cdeclinedTargetVersionUpgrade\x12b\n" + + "\x12time_skipping_info\x18x \x01(\v24.temporal.server.api.persistence.v1.TimeSkippingInfoR\x10timeSkippingInfo\x1ad\n" + "\x15SearchAttributesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x125\n" + "\x05value\x18\x02 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\x05value:\x028\x01\x1aX\n" + @@ -4790,7 +4924,14 @@ const file_temporal_server_api_persistence_v1_executions_proto_rawDesc = "" + "\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\b,\x10-J\x04\b-\x10.J\x04\b/\x100J\x04\b0\x101J\x04\b1\x102J\x04\b2\x103\"\x7f\n" + "\x19LastNotifiedTargetVersion\x12b\n" + - "\x12deployment_version\x18\x01 \x01(\v23.temporal.api.deployment.v1.WorkerDeploymentVersionR\x11deploymentVersion\"\x9d\x01\n" + + "\x12deployment_version\x18\x01 \x01(\v23.temporal.api.deployment.v1.WorkerDeploymentVersionR\x11deploymentVersion\"\x96\x01\n" + + "\x10TimeSkippingInfo\x12\x18\n" + + "\aenabled\x18\x01 \x01(\bR\aenabled\x12h\n" + + "\x14time_skipped_details\x18\x02 \x03(\v26.temporal.server.api.persistence.v1.TimeSkippedDetailsR\x12timeSkippedDetails\"\xd0\x01\n" + + "\x12TimeSkippedDetails\x12?\n" + + "\rskipping_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\fskippingTime\x12D\n" + + "\x10duration_to_skip\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x0edurationToSkip\x123\n" + + "\ato_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\x06toTime\"\x9d\x01\n" + "\x0eExecutionStats\x12!\n" + "\fhistory_size\x18\x01 \x01(\x03R\vhistorySize\x122\n" + "\x15external_payload_size\x18\x02 \x01(\x03R\x13externalPayloadSize\x124\n" + @@ -5125,250 +5266,257 @@ func file_temporal_server_api_persistence_v1_executions_proto_rawDescGZIP() []by return file_temporal_server_api_persistence_v1_executions_proto_rawDescData } -var file_temporal_server_api_persistence_v1_executions_proto_msgTypes = make([]protoimpl.MessageInfo, 44) +var file_temporal_server_api_persistence_v1_executions_proto_msgTypes = make([]protoimpl.MessageInfo, 46) 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 (*LastNotifiedTargetVersion)(nil), // 2: temporal.server.api.persistence.v1.LastNotifiedTargetVersion - (*ExecutionStats)(nil), // 3: temporal.server.api.persistence.v1.ExecutionStats - (*WorkflowExecutionState)(nil), // 4: temporal.server.api.persistence.v1.WorkflowExecutionState - (*RequestIDInfo)(nil), // 5: temporal.server.api.persistence.v1.RequestIDInfo - (*TransferTaskInfo)(nil), // 6: temporal.server.api.persistence.v1.TransferTaskInfo - (*ReplicationTaskInfo)(nil), // 7: temporal.server.api.persistence.v1.ReplicationTaskInfo - (*VisibilityTaskInfo)(nil), // 8: temporal.server.api.persistence.v1.VisibilityTaskInfo - (*TimerTaskInfo)(nil), // 9: temporal.server.api.persistence.v1.TimerTaskInfo - (*ArchivalTaskInfo)(nil), // 10: temporal.server.api.persistence.v1.ArchivalTaskInfo - (*OutboundTaskInfo)(nil), // 11: temporal.server.api.persistence.v1.OutboundTaskInfo - (*NexusInvocationTaskInfo)(nil), // 12: temporal.server.api.persistence.v1.NexusInvocationTaskInfo - (*NexusCancelationTaskInfo)(nil), // 13: temporal.server.api.persistence.v1.NexusCancelationTaskInfo - (*ActivityInfo)(nil), // 14: temporal.server.api.persistence.v1.ActivityInfo - (*TimerInfo)(nil), // 15: temporal.server.api.persistence.v1.TimerInfo - (*ChildExecutionInfo)(nil), // 16: temporal.server.api.persistence.v1.ChildExecutionInfo - (*RequestCancelInfo)(nil), // 17: temporal.server.api.persistence.v1.RequestCancelInfo - (*SignalInfo)(nil), // 18: temporal.server.api.persistence.v1.SignalInfo - (*Checksum)(nil), // 19: temporal.server.api.persistence.v1.Checksum - (*Callback)(nil), // 20: temporal.server.api.persistence.v1.Callback - (*HSMCompletionCallbackArg)(nil), // 21: temporal.server.api.persistence.v1.HSMCompletionCallbackArg - (*CallbackInfo)(nil), // 22: temporal.server.api.persistence.v1.CallbackInfo - (*NexusOperationInfo)(nil), // 23: temporal.server.api.persistence.v1.NexusOperationInfo - (*NexusOperationCancellationInfo)(nil), // 24: temporal.server.api.persistence.v1.NexusOperationCancellationInfo - (*ResetChildInfo)(nil), // 25: temporal.server.api.persistence.v1.ResetChildInfo - (*WorkflowPauseInfo)(nil), // 26: temporal.server.api.persistence.v1.WorkflowPauseInfo - nil, // 27: temporal.server.api.persistence.v1.ShardInfo.ReplicationDlqAckLevelEntry - nil, // 28: temporal.server.api.persistence.v1.ShardInfo.QueueStatesEntry - nil, // 29: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SearchAttributesEntry - nil, // 30: temporal.server.api.persistence.v1.WorkflowExecutionInfo.MemoEntry - nil, // 31: temporal.server.api.persistence.v1.WorkflowExecutionInfo.UpdateInfosEntry - nil, // 32: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SubStateMachinesByTypeEntry - nil, // 33: temporal.server.api.persistence.v1.WorkflowExecutionInfo.ChildrenInitializedPostResetPointEntry - nil, // 34: temporal.server.api.persistence.v1.WorkflowExecutionState.RequestIdsEntry - (*TransferTaskInfo_CloseExecutionTaskDetails)(nil), // 35: temporal.server.api.persistence.v1.TransferTaskInfo.CloseExecutionTaskDetails - (*ActivityInfo_UseWorkflowBuildIdInfo)(nil), // 36: temporal.server.api.persistence.v1.ActivityInfo.UseWorkflowBuildIdInfo - (*ActivityInfo_PauseInfo)(nil), // 37: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo - (*ActivityInfo_PauseInfo_Manual)(nil), // 38: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.Manual - (*Callback_Nexus)(nil), // 39: temporal.server.api.persistence.v1.Callback.Nexus - (*Callback_HSM)(nil), // 40: temporal.server.api.persistence.v1.Callback.HSM - nil, // 41: temporal.server.api.persistence.v1.Callback.Nexus.HeaderEntry - (*CallbackInfo_WorkflowClosed)(nil), // 42: temporal.server.api.persistence.v1.CallbackInfo.WorkflowClosed - (*CallbackInfo_Trigger)(nil), // 43: temporal.server.api.persistence.v1.CallbackInfo.Trigger - (*timestamppb.Timestamp)(nil), // 44: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 45: google.protobuf.Duration - (v1.WorkflowTaskType)(0), // 46: temporal.server.api.enums.v1.WorkflowTaskType - (v11.SuggestContinueAsNewReason)(0), // 47: temporal.api.enums.v1.SuggestContinueAsNewReason - (*v12.ResetPoints)(nil), // 48: temporal.api.workflow.v1.ResetPoints - (*v14.VersionHistories)(nil), // 49: temporal.server.api.history.v1.VersionHistories - (*v15.VectorClock)(nil), // 50: temporal.server.api.clock.v1.VectorClock - (*v16.BaseExecutionInfo)(nil), // 51: temporal.server.api.workflow.v1.BaseExecutionInfo - (*v13.WorkerVersionStamp)(nil), // 52: temporal.api.common.v1.WorkerVersionStamp - (*VersionedTransition)(nil), // 53: temporal.server.api.persistence.v1.VersionedTransition - (*StateMachineTimerGroup)(nil), // 54: temporal.server.api.persistence.v1.StateMachineTimerGroup - (*StateMachineTombstoneBatch)(nil), // 55: temporal.server.api.persistence.v1.StateMachineTombstoneBatch - (*v12.WorkflowExecutionVersioningInfo)(nil), // 56: temporal.api.workflow.v1.WorkflowExecutionVersioningInfo - (*v13.Priority)(nil), // 57: temporal.api.common.v1.Priority - (v11.WorkflowTaskFailedCause)(0), // 58: temporal.api.enums.v1.WorkflowTaskFailedCause - (v11.TimeoutType)(0), // 59: temporal.api.enums.v1.TimeoutType - (*v17.DeclinedTargetVersionUpgrade)(nil), // 60: temporal.api.history.v1.DeclinedTargetVersionUpgrade - (*v18.WorkerDeploymentVersion)(nil), // 61: temporal.api.deployment.v1.WorkerDeploymentVersion - (v1.WorkflowExecutionState)(0), // 62: temporal.server.api.enums.v1.WorkflowExecutionState - (v11.WorkflowExecutionStatus)(0), // 63: temporal.api.enums.v1.WorkflowExecutionStatus - (v11.EventType)(0), // 64: temporal.api.enums.v1.EventType - (v1.TaskType)(0), // 65: temporal.server.api.enums.v1.TaskType - (*ChasmTaskInfo)(nil), // 66: temporal.server.api.persistence.v1.ChasmTaskInfo - (v1.TaskPriority)(0), // 67: temporal.server.api.enums.v1.TaskPriority - (*v14.VersionHistoryItem)(nil), // 68: temporal.server.api.history.v1.VersionHistoryItem - (v1.WorkflowBackoffType)(0), // 69: temporal.server.api.enums.v1.WorkflowBackoffType - (*StateMachineTaskInfo)(nil), // 70: temporal.server.api.persistence.v1.StateMachineTaskInfo - (*v19.Failure)(nil), // 71: temporal.api.failure.v1.Failure - (*v13.Payloads)(nil), // 72: temporal.api.common.v1.Payloads - (*v13.ActivityType)(nil), // 73: temporal.api.common.v1.ActivityType - (*v18.Deployment)(nil), // 74: temporal.api.deployment.v1.Deployment - (v11.ParentClosePolicy)(0), // 75: temporal.api.enums.v1.ParentClosePolicy - (v1.ChecksumFlavor)(0), // 76: temporal.server.api.enums.v1.ChecksumFlavor - (*v13.Link)(nil), // 77: temporal.api.common.v1.Link - (*v17.HistoryEvent)(nil), // 78: temporal.api.history.v1.HistoryEvent - (v1.CallbackState)(0), // 79: temporal.server.api.enums.v1.CallbackState - (v1.NexusOperationState)(0), // 80: temporal.server.api.enums.v1.NexusOperationState - (v11.NexusOperationCancellationState)(0), // 81: temporal.api.enums.v1.NexusOperationCancellationState - (*QueueState)(nil), // 82: temporal.server.api.persistence.v1.QueueState - (*v13.Payload)(nil), // 83: temporal.api.common.v1.Payload - (*UpdateInfo)(nil), // 84: temporal.server.api.persistence.v1.UpdateInfo - (*StateMachineMap)(nil), // 85: temporal.server.api.persistence.v1.StateMachineMap - (*StateMachineRef)(nil), // 86: temporal.server.api.persistence.v1.StateMachineRef + (*TimeSkippingInfo)(nil), // 3: temporal.server.api.persistence.v1.TimeSkippingInfo + (*TimeSkippedDetails)(nil), // 4: temporal.server.api.persistence.v1.TimeSkippedDetails + (*ExecutionStats)(nil), // 5: temporal.server.api.persistence.v1.ExecutionStats + (*WorkflowExecutionState)(nil), // 6: temporal.server.api.persistence.v1.WorkflowExecutionState + (*RequestIDInfo)(nil), // 7: temporal.server.api.persistence.v1.RequestIDInfo + (*TransferTaskInfo)(nil), // 8: temporal.server.api.persistence.v1.TransferTaskInfo + (*ReplicationTaskInfo)(nil), // 9: temporal.server.api.persistence.v1.ReplicationTaskInfo + (*VisibilityTaskInfo)(nil), // 10: temporal.server.api.persistence.v1.VisibilityTaskInfo + (*TimerTaskInfo)(nil), // 11: temporal.server.api.persistence.v1.TimerTaskInfo + (*ArchivalTaskInfo)(nil), // 12: temporal.server.api.persistence.v1.ArchivalTaskInfo + (*OutboundTaskInfo)(nil), // 13: temporal.server.api.persistence.v1.OutboundTaskInfo + (*NexusInvocationTaskInfo)(nil), // 14: temporal.server.api.persistence.v1.NexusInvocationTaskInfo + (*NexusCancelationTaskInfo)(nil), // 15: temporal.server.api.persistence.v1.NexusCancelationTaskInfo + (*ActivityInfo)(nil), // 16: temporal.server.api.persistence.v1.ActivityInfo + (*TimerInfo)(nil), // 17: temporal.server.api.persistence.v1.TimerInfo + (*ChildExecutionInfo)(nil), // 18: temporal.server.api.persistence.v1.ChildExecutionInfo + (*RequestCancelInfo)(nil), // 19: temporal.server.api.persistence.v1.RequestCancelInfo + (*SignalInfo)(nil), // 20: temporal.server.api.persistence.v1.SignalInfo + (*Checksum)(nil), // 21: temporal.server.api.persistence.v1.Checksum + (*Callback)(nil), // 22: temporal.server.api.persistence.v1.Callback + (*HSMCompletionCallbackArg)(nil), // 23: temporal.server.api.persistence.v1.HSMCompletionCallbackArg + (*CallbackInfo)(nil), // 24: temporal.server.api.persistence.v1.CallbackInfo + (*NexusOperationInfo)(nil), // 25: temporal.server.api.persistence.v1.NexusOperationInfo + (*NexusOperationCancellationInfo)(nil), // 26: temporal.server.api.persistence.v1.NexusOperationCancellationInfo + (*ResetChildInfo)(nil), // 27: temporal.server.api.persistence.v1.ResetChildInfo + (*WorkflowPauseInfo)(nil), // 28: temporal.server.api.persistence.v1.WorkflowPauseInfo + nil, // 29: temporal.server.api.persistence.v1.ShardInfo.ReplicationDlqAckLevelEntry + nil, // 30: temporal.server.api.persistence.v1.ShardInfo.QueueStatesEntry + nil, // 31: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SearchAttributesEntry + nil, // 32: temporal.server.api.persistence.v1.WorkflowExecutionInfo.MemoEntry + nil, // 33: temporal.server.api.persistence.v1.WorkflowExecutionInfo.UpdateInfosEntry + nil, // 34: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SubStateMachinesByTypeEntry + nil, // 35: temporal.server.api.persistence.v1.WorkflowExecutionInfo.ChildrenInitializedPostResetPointEntry + nil, // 36: temporal.server.api.persistence.v1.WorkflowExecutionState.RequestIdsEntry + (*TransferTaskInfo_CloseExecutionTaskDetails)(nil), // 37: temporal.server.api.persistence.v1.TransferTaskInfo.CloseExecutionTaskDetails + (*ActivityInfo_UseWorkflowBuildIdInfo)(nil), // 38: temporal.server.api.persistence.v1.ActivityInfo.UseWorkflowBuildIdInfo + (*ActivityInfo_PauseInfo)(nil), // 39: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo + (*ActivityInfo_PauseInfo_Manual)(nil), // 40: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.Manual + (*Callback_Nexus)(nil), // 41: temporal.server.api.persistence.v1.Callback.Nexus + (*Callback_HSM)(nil), // 42: temporal.server.api.persistence.v1.Callback.HSM + nil, // 43: temporal.server.api.persistence.v1.Callback.Nexus.HeaderEntry + (*CallbackInfo_WorkflowClosed)(nil), // 44: temporal.server.api.persistence.v1.CallbackInfo.WorkflowClosed + (*CallbackInfo_Trigger)(nil), // 45: temporal.server.api.persistence.v1.CallbackInfo.Trigger + (*timestamppb.Timestamp)(nil), // 46: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 47: google.protobuf.Duration + (v1.WorkflowTaskType)(0), // 48: temporal.server.api.enums.v1.WorkflowTaskType + (v11.SuggestContinueAsNewReason)(0), // 49: temporal.api.enums.v1.SuggestContinueAsNewReason + (*v12.ResetPoints)(nil), // 50: temporal.api.workflow.v1.ResetPoints + (*v14.VersionHistories)(nil), // 51: temporal.server.api.history.v1.VersionHistories + (*v15.VectorClock)(nil), // 52: temporal.server.api.clock.v1.VectorClock + (*v16.BaseExecutionInfo)(nil), // 53: temporal.server.api.workflow.v1.BaseExecutionInfo + (*v13.WorkerVersionStamp)(nil), // 54: temporal.api.common.v1.WorkerVersionStamp + (*VersionedTransition)(nil), // 55: temporal.server.api.persistence.v1.VersionedTransition + (*StateMachineTimerGroup)(nil), // 56: temporal.server.api.persistence.v1.StateMachineTimerGroup + (*StateMachineTombstoneBatch)(nil), // 57: temporal.server.api.persistence.v1.StateMachineTombstoneBatch + (*v12.WorkflowExecutionVersioningInfo)(nil), // 58: temporal.api.workflow.v1.WorkflowExecutionVersioningInfo + (*v13.Priority)(nil), // 59: temporal.api.common.v1.Priority + (v11.WorkflowTaskFailedCause)(0), // 60: temporal.api.enums.v1.WorkflowTaskFailedCause + (v11.TimeoutType)(0), // 61: temporal.api.enums.v1.TimeoutType + (*v17.DeclinedTargetVersionUpgrade)(nil), // 62: temporal.api.history.v1.DeclinedTargetVersionUpgrade + (*v18.WorkerDeploymentVersion)(nil), // 63: temporal.api.deployment.v1.WorkerDeploymentVersion + (v1.WorkflowExecutionState)(0), // 64: temporal.server.api.enums.v1.WorkflowExecutionState + (v11.WorkflowExecutionStatus)(0), // 65: temporal.api.enums.v1.WorkflowExecutionStatus + (v11.EventType)(0), // 66: temporal.api.enums.v1.EventType + (v1.TaskType)(0), // 67: temporal.server.api.enums.v1.TaskType + (*ChasmTaskInfo)(nil), // 68: temporal.server.api.persistence.v1.ChasmTaskInfo + (v1.TaskPriority)(0), // 69: temporal.server.api.enums.v1.TaskPriority + (*v14.VersionHistoryItem)(nil), // 70: temporal.server.api.history.v1.VersionHistoryItem + (v1.WorkflowBackoffType)(0), // 71: temporal.server.api.enums.v1.WorkflowBackoffType + (*StateMachineTaskInfo)(nil), // 72: temporal.server.api.persistence.v1.StateMachineTaskInfo + (*v19.Failure)(nil), // 73: temporal.api.failure.v1.Failure + (*v13.Payloads)(nil), // 74: temporal.api.common.v1.Payloads + (*v13.ActivityType)(nil), // 75: temporal.api.common.v1.ActivityType + (*v18.Deployment)(nil), // 76: temporal.api.deployment.v1.Deployment + (v11.ParentClosePolicy)(0), // 77: temporal.api.enums.v1.ParentClosePolicy + (v1.ChecksumFlavor)(0), // 78: temporal.server.api.enums.v1.ChecksumFlavor + (*v13.Link)(nil), // 79: temporal.api.common.v1.Link + (*v17.HistoryEvent)(nil), // 80: temporal.api.history.v1.HistoryEvent + (v1.CallbackState)(0), // 81: temporal.server.api.enums.v1.CallbackState + (v1.NexusOperationState)(0), // 82: temporal.server.api.enums.v1.NexusOperationState + (v11.NexusOperationCancellationState)(0), // 83: temporal.api.enums.v1.NexusOperationCancellationState + (*QueueState)(nil), // 84: temporal.server.api.persistence.v1.QueueState + (*v13.Payload)(nil), // 85: temporal.api.common.v1.Payload + (*UpdateInfo)(nil), // 86: temporal.server.api.persistence.v1.UpdateInfo + (*StateMachineMap)(nil), // 87: temporal.server.api.persistence.v1.StateMachineMap + (*StateMachineRef)(nil), // 88: temporal.server.api.persistence.v1.StateMachineRef } var file_temporal_server_api_persistence_v1_executions_proto_depIdxs = []int32{ - 44, // 0: temporal.server.api.persistence.v1.ShardInfo.update_time:type_name -> google.protobuf.Timestamp - 27, // 1: temporal.server.api.persistence.v1.ShardInfo.replication_dlq_ack_level:type_name -> temporal.server.api.persistence.v1.ShardInfo.ReplicationDlqAckLevelEntry - 28, // 2: temporal.server.api.persistence.v1.ShardInfo.queue_states:type_name -> temporal.server.api.persistence.v1.ShardInfo.QueueStatesEntry - 45, // 3: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_execution_timeout:type_name -> google.protobuf.Duration - 45, // 4: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_run_timeout:type_name -> google.protobuf.Duration - 45, // 5: temporal.server.api.persistence.v1.WorkflowExecutionInfo.default_workflow_task_timeout:type_name -> google.protobuf.Duration - 44, // 6: temporal.server.api.persistence.v1.WorkflowExecutionInfo.start_time:type_name -> google.protobuf.Timestamp - 44, // 7: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_update_time:type_name -> google.protobuf.Timestamp - 45, // 8: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_timeout:type_name -> google.protobuf.Duration - 44, // 9: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_started_time:type_name -> google.protobuf.Timestamp - 44, // 10: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_scheduled_time:type_name -> google.protobuf.Timestamp - 44, // 11: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_original_scheduled_time:type_name -> google.protobuf.Timestamp - 46, // 12: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_type:type_name -> temporal.server.api.enums.v1.WorkflowTaskType - 47, // 13: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_suggest_continue_as_new_reasons:type_name -> temporal.api.enums.v1.SuggestContinueAsNewReason - 45, // 14: temporal.server.api.persistence.v1.WorkflowExecutionInfo.sticky_schedule_to_start_timeout:type_name -> google.protobuf.Duration - 45, // 15: temporal.server.api.persistence.v1.WorkflowExecutionInfo.retry_initial_interval:type_name -> google.protobuf.Duration - 45, // 16: temporal.server.api.persistence.v1.WorkflowExecutionInfo.retry_maximum_interval:type_name -> google.protobuf.Duration - 44, // 17: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_execution_expiration_time:type_name -> google.protobuf.Timestamp - 48, // 18: temporal.server.api.persistence.v1.WorkflowExecutionInfo.auto_reset_points:type_name -> temporal.api.workflow.v1.ResetPoints - 29, // 19: temporal.server.api.persistence.v1.WorkflowExecutionInfo.search_attributes:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.SearchAttributesEntry - 30, // 20: temporal.server.api.persistence.v1.WorkflowExecutionInfo.memo:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.MemoEntry - 49, // 21: temporal.server.api.persistence.v1.WorkflowExecutionInfo.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 3, // 22: temporal.server.api.persistence.v1.WorkflowExecutionInfo.execution_stats:type_name -> temporal.server.api.persistence.v1.ExecutionStats - 44, // 23: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_run_expiration_time:type_name -> google.protobuf.Timestamp - 44, // 24: temporal.server.api.persistence.v1.WorkflowExecutionInfo.execution_time:type_name -> google.protobuf.Timestamp - 50, // 25: temporal.server.api.persistence.v1.WorkflowExecutionInfo.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 44, // 26: temporal.server.api.persistence.v1.WorkflowExecutionInfo.close_time:type_name -> google.protobuf.Timestamp - 51, // 27: temporal.server.api.persistence.v1.WorkflowExecutionInfo.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 52, // 28: temporal.server.api.persistence.v1.WorkflowExecutionInfo.most_recent_worker_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp - 31, // 29: temporal.server.api.persistence.v1.WorkflowExecutionInfo.update_infos:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.UpdateInfosEntry - 53, // 30: temporal.server.api.persistence.v1.WorkflowExecutionInfo.transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 32, // 31: temporal.server.api.persistence.v1.WorkflowExecutionInfo.sub_state_machines_by_type:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.SubStateMachinesByTypeEntry - 54, // 32: temporal.server.api.persistence.v1.WorkflowExecutionInfo.state_machine_timers:type_name -> temporal.server.api.persistence.v1.StateMachineTimerGroup - 53, // 33: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 53, // 34: temporal.server.api.persistence.v1.WorkflowExecutionInfo.visibility_last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 53, // 35: temporal.server.api.persistence.v1.WorkflowExecutionInfo.signal_request_ids_last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 55, // 36: temporal.server.api.persistence.v1.WorkflowExecutionInfo.sub_state_machine_tombstone_batches:type_name -> temporal.server.api.persistence.v1.StateMachineTombstoneBatch - 56, // 37: temporal.server.api.persistence.v1.WorkflowExecutionInfo.versioning_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionVersioningInfo - 53, // 38: temporal.server.api.persistence.v1.WorkflowExecutionInfo.previous_transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 53, // 39: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_transition_history_break_point:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 33, // 40: temporal.server.api.persistence.v1.WorkflowExecutionInfo.children_initialized_post_reset_point:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.ChildrenInitializedPostResetPointEntry - 57, // 41: temporal.server.api.persistence.v1.WorkflowExecutionInfo.priority:type_name -> temporal.api.common.v1.Priority - 26, // 42: temporal.server.api.persistence.v1.WorkflowExecutionInfo.pause_info:type_name -> temporal.server.api.persistence.v1.WorkflowPauseInfo - 58, // 43: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_workflow_task_failure_cause:type_name -> temporal.api.enums.v1.WorkflowTaskFailedCause - 59, // 44: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_workflow_task_timed_out_type:type_name -> temporal.api.enums.v1.TimeoutType + 46, // 0: temporal.server.api.persistence.v1.ShardInfo.update_time:type_name -> google.protobuf.Timestamp + 29, // 1: temporal.server.api.persistence.v1.ShardInfo.replication_dlq_ack_level:type_name -> temporal.server.api.persistence.v1.ShardInfo.ReplicationDlqAckLevelEntry + 30, // 2: temporal.server.api.persistence.v1.ShardInfo.queue_states:type_name -> temporal.server.api.persistence.v1.ShardInfo.QueueStatesEntry + 47, // 3: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_execution_timeout:type_name -> google.protobuf.Duration + 47, // 4: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_run_timeout:type_name -> google.protobuf.Duration + 47, // 5: temporal.server.api.persistence.v1.WorkflowExecutionInfo.default_workflow_task_timeout:type_name -> google.protobuf.Duration + 46, // 6: temporal.server.api.persistence.v1.WorkflowExecutionInfo.start_time:type_name -> google.protobuf.Timestamp + 46, // 7: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_update_time:type_name -> google.protobuf.Timestamp + 47, // 8: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_timeout:type_name -> google.protobuf.Duration + 46, // 9: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_started_time:type_name -> google.protobuf.Timestamp + 46, // 10: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_scheduled_time:type_name -> google.protobuf.Timestamp + 46, // 11: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_original_scheduled_time:type_name -> google.protobuf.Timestamp + 48, // 12: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_type:type_name -> temporal.server.api.enums.v1.WorkflowTaskType + 49, // 13: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_suggest_continue_as_new_reasons:type_name -> temporal.api.enums.v1.SuggestContinueAsNewReason + 47, // 14: temporal.server.api.persistence.v1.WorkflowExecutionInfo.sticky_schedule_to_start_timeout:type_name -> google.protobuf.Duration + 47, // 15: temporal.server.api.persistence.v1.WorkflowExecutionInfo.retry_initial_interval:type_name -> google.protobuf.Duration + 47, // 16: temporal.server.api.persistence.v1.WorkflowExecutionInfo.retry_maximum_interval:type_name -> google.protobuf.Duration + 46, // 17: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_execution_expiration_time:type_name -> google.protobuf.Timestamp + 50, // 18: temporal.server.api.persistence.v1.WorkflowExecutionInfo.auto_reset_points:type_name -> temporal.api.workflow.v1.ResetPoints + 31, // 19: temporal.server.api.persistence.v1.WorkflowExecutionInfo.search_attributes:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.SearchAttributesEntry + 32, // 20: temporal.server.api.persistence.v1.WorkflowExecutionInfo.memo:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.MemoEntry + 51, // 21: temporal.server.api.persistence.v1.WorkflowExecutionInfo.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 5, // 22: temporal.server.api.persistence.v1.WorkflowExecutionInfo.execution_stats:type_name -> temporal.server.api.persistence.v1.ExecutionStats + 46, // 23: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_run_expiration_time:type_name -> google.protobuf.Timestamp + 46, // 24: temporal.server.api.persistence.v1.WorkflowExecutionInfo.execution_time:type_name -> google.protobuf.Timestamp + 52, // 25: temporal.server.api.persistence.v1.WorkflowExecutionInfo.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 46, // 26: temporal.server.api.persistence.v1.WorkflowExecutionInfo.close_time:type_name -> google.protobuf.Timestamp + 53, // 27: temporal.server.api.persistence.v1.WorkflowExecutionInfo.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 54, // 28: temporal.server.api.persistence.v1.WorkflowExecutionInfo.most_recent_worker_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp + 33, // 29: temporal.server.api.persistence.v1.WorkflowExecutionInfo.update_infos:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.UpdateInfosEntry + 55, // 30: temporal.server.api.persistence.v1.WorkflowExecutionInfo.transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 34, // 31: temporal.server.api.persistence.v1.WorkflowExecutionInfo.sub_state_machines_by_type:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.SubStateMachinesByTypeEntry + 56, // 32: temporal.server.api.persistence.v1.WorkflowExecutionInfo.state_machine_timers:type_name -> temporal.server.api.persistence.v1.StateMachineTimerGroup + 55, // 33: temporal.server.api.persistence.v1.WorkflowExecutionInfo.workflow_task_last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 55, // 34: temporal.server.api.persistence.v1.WorkflowExecutionInfo.visibility_last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 55, // 35: temporal.server.api.persistence.v1.WorkflowExecutionInfo.signal_request_ids_last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 57, // 36: temporal.server.api.persistence.v1.WorkflowExecutionInfo.sub_state_machine_tombstone_batches:type_name -> temporal.server.api.persistence.v1.StateMachineTombstoneBatch + 58, // 37: temporal.server.api.persistence.v1.WorkflowExecutionInfo.versioning_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionVersioningInfo + 55, // 38: temporal.server.api.persistence.v1.WorkflowExecutionInfo.previous_transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 55, // 39: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_transition_history_break_point:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 35, // 40: temporal.server.api.persistence.v1.WorkflowExecutionInfo.children_initialized_post_reset_point:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionInfo.ChildrenInitializedPostResetPointEntry + 59, // 41: temporal.server.api.persistence.v1.WorkflowExecutionInfo.priority:type_name -> temporal.api.common.v1.Priority + 28, // 42: temporal.server.api.persistence.v1.WorkflowExecutionInfo.pause_info:type_name -> temporal.server.api.persistence.v1.WorkflowPauseInfo + 60, // 43: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_workflow_task_failure_cause:type_name -> temporal.api.enums.v1.WorkflowTaskFailedCause + 61, // 44: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_workflow_task_timed_out_type:type_name -> temporal.api.enums.v1.TimeoutType 2, // 45: temporal.server.api.persistence.v1.WorkflowExecutionInfo.last_notified_target_version:type_name -> temporal.server.api.persistence.v1.LastNotifiedTargetVersion - 60, // 46: temporal.server.api.persistence.v1.WorkflowExecutionInfo.declined_target_version_upgrade:type_name -> temporal.api.history.v1.DeclinedTargetVersionUpgrade - 61, // 47: temporal.server.api.persistence.v1.LastNotifiedTargetVersion.deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion - 62, // 48: temporal.server.api.persistence.v1.WorkflowExecutionState.state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState - 63, // 49: temporal.server.api.persistence.v1.WorkflowExecutionState.status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus - 53, // 50: temporal.server.api.persistence.v1.WorkflowExecutionState.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 44, // 51: temporal.server.api.persistence.v1.WorkflowExecutionState.start_time:type_name -> google.protobuf.Timestamp - 34, // 52: temporal.server.api.persistence.v1.WorkflowExecutionState.request_ids:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionState.RequestIdsEntry - 64, // 53: temporal.server.api.persistence.v1.RequestIDInfo.event_type:type_name -> temporal.api.enums.v1.EventType - 65, // 54: temporal.server.api.persistence.v1.TransferTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType - 44, // 55: temporal.server.api.persistence.v1.TransferTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp - 35, // 56: temporal.server.api.persistence.v1.TransferTaskInfo.close_execution_task_details:type_name -> temporal.server.api.persistence.v1.TransferTaskInfo.CloseExecutionTaskDetails - 66, // 57: temporal.server.api.persistence.v1.TransferTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo - 65, // 58: temporal.server.api.persistence.v1.ReplicationTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType - 44, // 59: temporal.server.api.persistence.v1.ReplicationTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp - 67, // 60: temporal.server.api.persistence.v1.ReplicationTaskInfo.priority:type_name -> temporal.server.api.enums.v1.TaskPriority - 53, // 61: temporal.server.api.persistence.v1.ReplicationTaskInfo.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 7, // 62: temporal.server.api.persistence.v1.ReplicationTaskInfo.task_equivalents:type_name -> temporal.server.api.persistence.v1.ReplicationTaskInfo - 68, // 63: temporal.server.api.persistence.v1.ReplicationTaskInfo.last_version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 65, // 64: temporal.server.api.persistence.v1.VisibilityTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType - 44, // 65: temporal.server.api.persistence.v1.VisibilityTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp - 44, // 66: temporal.server.api.persistence.v1.VisibilityTaskInfo.close_time:type_name -> google.protobuf.Timestamp - 66, // 67: temporal.server.api.persistence.v1.VisibilityTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo - 65, // 68: temporal.server.api.persistence.v1.TimerTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType - 59, // 69: temporal.server.api.persistence.v1.TimerTaskInfo.timeout_type:type_name -> temporal.api.enums.v1.TimeoutType - 69, // 70: temporal.server.api.persistence.v1.TimerTaskInfo.workflow_backoff_type:type_name -> temporal.server.api.enums.v1.WorkflowBackoffType - 44, // 71: temporal.server.api.persistence.v1.TimerTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp - 66, // 72: temporal.server.api.persistence.v1.TimerTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo - 65, // 73: temporal.server.api.persistence.v1.ArchivalTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType - 44, // 74: temporal.server.api.persistence.v1.ArchivalTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp - 65, // 75: temporal.server.api.persistence.v1.OutboundTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType - 44, // 76: temporal.server.api.persistence.v1.OutboundTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp - 70, // 77: temporal.server.api.persistence.v1.OutboundTaskInfo.state_machine_info:type_name -> temporal.server.api.persistence.v1.StateMachineTaskInfo - 66, // 78: temporal.server.api.persistence.v1.OutboundTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo - 44, // 79: temporal.server.api.persistence.v1.ActivityInfo.scheduled_time:type_name -> google.protobuf.Timestamp - 44, // 80: temporal.server.api.persistence.v1.ActivityInfo.started_time:type_name -> google.protobuf.Timestamp - 45, // 81: temporal.server.api.persistence.v1.ActivityInfo.schedule_to_start_timeout:type_name -> google.protobuf.Duration - 45, // 82: temporal.server.api.persistence.v1.ActivityInfo.schedule_to_close_timeout:type_name -> google.protobuf.Duration - 45, // 83: temporal.server.api.persistence.v1.ActivityInfo.start_to_close_timeout:type_name -> google.protobuf.Duration - 45, // 84: temporal.server.api.persistence.v1.ActivityInfo.heartbeat_timeout:type_name -> google.protobuf.Duration - 45, // 85: temporal.server.api.persistence.v1.ActivityInfo.retry_initial_interval:type_name -> google.protobuf.Duration - 45, // 86: temporal.server.api.persistence.v1.ActivityInfo.retry_maximum_interval:type_name -> google.protobuf.Duration - 44, // 87: temporal.server.api.persistence.v1.ActivityInfo.retry_expiration_time:type_name -> google.protobuf.Timestamp - 71, // 88: temporal.server.api.persistence.v1.ActivityInfo.retry_last_failure:type_name -> temporal.api.failure.v1.Failure - 72, // 89: temporal.server.api.persistence.v1.ActivityInfo.last_heartbeat_details:type_name -> temporal.api.common.v1.Payloads - 44, // 90: temporal.server.api.persistence.v1.ActivityInfo.last_heartbeat_update_time:type_name -> google.protobuf.Timestamp - 73, // 91: temporal.server.api.persistence.v1.ActivityInfo.activity_type:type_name -> temporal.api.common.v1.ActivityType - 36, // 92: temporal.server.api.persistence.v1.ActivityInfo.use_workflow_build_id_info:type_name -> temporal.server.api.persistence.v1.ActivityInfo.UseWorkflowBuildIdInfo - 52, // 93: temporal.server.api.persistence.v1.ActivityInfo.last_worker_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp - 53, // 94: temporal.server.api.persistence.v1.ActivityInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 44, // 95: temporal.server.api.persistence.v1.ActivityInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp - 44, // 96: temporal.server.api.persistence.v1.ActivityInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 74, // 97: temporal.server.api.persistence.v1.ActivityInfo.last_started_deployment:type_name -> temporal.api.deployment.v1.Deployment - 61, // 98: temporal.server.api.persistence.v1.ActivityInfo.last_deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion - 57, // 99: temporal.server.api.persistence.v1.ActivityInfo.priority:type_name -> temporal.api.common.v1.Priority - 37, // 100: temporal.server.api.persistence.v1.ActivityInfo.pause_info:type_name -> temporal.server.api.persistence.v1.ActivityInfo.PauseInfo - 44, // 101: temporal.server.api.persistence.v1.TimerInfo.expiry_time:type_name -> google.protobuf.Timestamp - 53, // 102: temporal.server.api.persistence.v1.TimerInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 75, // 103: temporal.server.api.persistence.v1.ChildExecutionInfo.parent_close_policy:type_name -> temporal.api.enums.v1.ParentClosePolicy - 50, // 104: temporal.server.api.persistence.v1.ChildExecutionInfo.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 53, // 105: temporal.server.api.persistence.v1.ChildExecutionInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 57, // 106: temporal.server.api.persistence.v1.ChildExecutionInfo.priority:type_name -> temporal.api.common.v1.Priority - 53, // 107: temporal.server.api.persistence.v1.RequestCancelInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 53, // 108: temporal.server.api.persistence.v1.SignalInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 76, // 109: temporal.server.api.persistence.v1.Checksum.flavor:type_name -> temporal.server.api.enums.v1.ChecksumFlavor - 39, // 110: temporal.server.api.persistence.v1.Callback.nexus:type_name -> temporal.server.api.persistence.v1.Callback.Nexus - 40, // 111: temporal.server.api.persistence.v1.Callback.hsm:type_name -> temporal.server.api.persistence.v1.Callback.HSM - 77, // 112: temporal.server.api.persistence.v1.Callback.links:type_name -> temporal.api.common.v1.Link - 78, // 113: temporal.server.api.persistence.v1.HSMCompletionCallbackArg.last_event:type_name -> temporal.api.history.v1.HistoryEvent - 20, // 114: temporal.server.api.persistence.v1.CallbackInfo.callback:type_name -> temporal.server.api.persistence.v1.Callback - 43, // 115: temporal.server.api.persistence.v1.CallbackInfo.trigger:type_name -> temporal.server.api.persistence.v1.CallbackInfo.Trigger - 44, // 116: temporal.server.api.persistence.v1.CallbackInfo.registration_time:type_name -> google.protobuf.Timestamp - 79, // 117: temporal.server.api.persistence.v1.CallbackInfo.state:type_name -> temporal.server.api.enums.v1.CallbackState - 44, // 118: temporal.server.api.persistence.v1.CallbackInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 71, // 119: temporal.server.api.persistence.v1.CallbackInfo.last_attempt_failure:type_name -> temporal.api.failure.v1.Failure - 44, // 120: temporal.server.api.persistence.v1.CallbackInfo.next_attempt_schedule_time:type_name -> google.protobuf.Timestamp - 45, // 121: temporal.server.api.persistence.v1.NexusOperationInfo.schedule_to_close_timeout:type_name -> google.protobuf.Duration - 44, // 122: temporal.server.api.persistence.v1.NexusOperationInfo.scheduled_time:type_name -> google.protobuf.Timestamp - 80, // 123: temporal.server.api.persistence.v1.NexusOperationInfo.state:type_name -> temporal.server.api.enums.v1.NexusOperationState - 44, // 124: temporal.server.api.persistence.v1.NexusOperationInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 71, // 125: temporal.server.api.persistence.v1.NexusOperationInfo.last_attempt_failure:type_name -> temporal.api.failure.v1.Failure - 44, // 126: temporal.server.api.persistence.v1.NexusOperationInfo.next_attempt_schedule_time:type_name -> google.protobuf.Timestamp - 45, // 127: temporal.server.api.persistence.v1.NexusOperationInfo.schedule_to_start_timeout:type_name -> google.protobuf.Duration - 45, // 128: temporal.server.api.persistence.v1.NexusOperationInfo.start_to_close_timeout:type_name -> google.protobuf.Duration - 44, // 129: temporal.server.api.persistence.v1.NexusOperationInfo.started_time:type_name -> google.protobuf.Timestamp - 44, // 130: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.requested_time:type_name -> google.protobuf.Timestamp - 81, // 131: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.state:type_name -> temporal.api.enums.v1.NexusOperationCancellationState - 44, // 132: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 71, // 133: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.last_attempt_failure:type_name -> temporal.api.failure.v1.Failure - 44, // 134: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.next_attempt_schedule_time:type_name -> google.protobuf.Timestamp - 44, // 135: temporal.server.api.persistence.v1.WorkflowPauseInfo.pause_time:type_name -> google.protobuf.Timestamp - 82, // 136: temporal.server.api.persistence.v1.ShardInfo.QueueStatesEntry.value:type_name -> temporal.server.api.persistence.v1.QueueState - 83, // 137: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SearchAttributesEntry.value:type_name -> temporal.api.common.v1.Payload - 83, // 138: temporal.server.api.persistence.v1.WorkflowExecutionInfo.MemoEntry.value:type_name -> temporal.api.common.v1.Payload - 84, // 139: temporal.server.api.persistence.v1.WorkflowExecutionInfo.UpdateInfosEntry.value:type_name -> temporal.server.api.persistence.v1.UpdateInfo - 85, // 140: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SubStateMachinesByTypeEntry.value:type_name -> temporal.server.api.persistence.v1.StateMachineMap - 25, // 141: temporal.server.api.persistence.v1.WorkflowExecutionInfo.ChildrenInitializedPostResetPointEntry.value:type_name -> temporal.server.api.persistence.v1.ResetChildInfo - 5, // 142: temporal.server.api.persistence.v1.WorkflowExecutionState.RequestIdsEntry.value:type_name -> temporal.server.api.persistence.v1.RequestIDInfo - 44, // 143: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.pause_time:type_name -> google.protobuf.Timestamp - 38, // 144: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.manual:type_name -> temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.Manual - 41, // 145: temporal.server.api.persistence.v1.Callback.Nexus.header:type_name -> temporal.server.api.persistence.v1.Callback.Nexus.HeaderEntry - 86, // 146: temporal.server.api.persistence.v1.Callback.HSM.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef - 42, // 147: temporal.server.api.persistence.v1.CallbackInfo.Trigger.workflow_closed:type_name -> temporal.server.api.persistence.v1.CallbackInfo.WorkflowClosed - 148, // [148:148] is the sub-list for method output_type - 148, // [148:148] is the sub-list for method input_type - 148, // [148:148] is the sub-list for extension type_name - 148, // [148:148] is the sub-list for extension extendee - 0, // [0:148] is the sub-list for field type_name + 62, // 46: temporal.server.api.persistence.v1.WorkflowExecutionInfo.declined_target_version_upgrade:type_name -> temporal.api.history.v1.DeclinedTargetVersionUpgrade + 3, // 47: temporal.server.api.persistence.v1.WorkflowExecutionInfo.time_skipping_info:type_name -> temporal.server.api.persistence.v1.TimeSkippingInfo + 63, // 48: temporal.server.api.persistence.v1.LastNotifiedTargetVersion.deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion + 4, // 49: temporal.server.api.persistence.v1.TimeSkippingInfo.time_skipped_details:type_name -> temporal.server.api.persistence.v1.TimeSkippedDetails + 46, // 50: temporal.server.api.persistence.v1.TimeSkippedDetails.skipping_time:type_name -> google.protobuf.Timestamp + 46, // 51: temporal.server.api.persistence.v1.TimeSkippedDetails.duration_to_skip:type_name -> google.protobuf.Timestamp + 46, // 52: temporal.server.api.persistence.v1.TimeSkippedDetails.to_time:type_name -> google.protobuf.Timestamp + 64, // 53: temporal.server.api.persistence.v1.WorkflowExecutionState.state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState + 65, // 54: temporal.server.api.persistence.v1.WorkflowExecutionState.status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus + 55, // 55: temporal.server.api.persistence.v1.WorkflowExecutionState.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 46, // 56: temporal.server.api.persistence.v1.WorkflowExecutionState.start_time:type_name -> google.protobuf.Timestamp + 36, // 57: temporal.server.api.persistence.v1.WorkflowExecutionState.request_ids:type_name -> temporal.server.api.persistence.v1.WorkflowExecutionState.RequestIdsEntry + 66, // 58: temporal.server.api.persistence.v1.RequestIDInfo.event_type:type_name -> temporal.api.enums.v1.EventType + 67, // 59: temporal.server.api.persistence.v1.TransferTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType + 46, // 60: temporal.server.api.persistence.v1.TransferTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp + 37, // 61: temporal.server.api.persistence.v1.TransferTaskInfo.close_execution_task_details:type_name -> temporal.server.api.persistence.v1.TransferTaskInfo.CloseExecutionTaskDetails + 68, // 62: temporal.server.api.persistence.v1.TransferTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo + 67, // 63: temporal.server.api.persistence.v1.ReplicationTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType + 46, // 64: temporal.server.api.persistence.v1.ReplicationTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp + 69, // 65: temporal.server.api.persistence.v1.ReplicationTaskInfo.priority:type_name -> temporal.server.api.enums.v1.TaskPriority + 55, // 66: temporal.server.api.persistence.v1.ReplicationTaskInfo.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 9, // 67: temporal.server.api.persistence.v1.ReplicationTaskInfo.task_equivalents:type_name -> temporal.server.api.persistence.v1.ReplicationTaskInfo + 70, // 68: temporal.server.api.persistence.v1.ReplicationTaskInfo.last_version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 67, // 69: temporal.server.api.persistence.v1.VisibilityTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType + 46, // 70: temporal.server.api.persistence.v1.VisibilityTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp + 46, // 71: temporal.server.api.persistence.v1.VisibilityTaskInfo.close_time:type_name -> google.protobuf.Timestamp + 68, // 72: temporal.server.api.persistence.v1.VisibilityTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo + 67, // 73: temporal.server.api.persistence.v1.TimerTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType + 61, // 74: temporal.server.api.persistence.v1.TimerTaskInfo.timeout_type:type_name -> temporal.api.enums.v1.TimeoutType + 71, // 75: temporal.server.api.persistence.v1.TimerTaskInfo.workflow_backoff_type:type_name -> temporal.server.api.enums.v1.WorkflowBackoffType + 46, // 76: temporal.server.api.persistence.v1.TimerTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp + 68, // 77: temporal.server.api.persistence.v1.TimerTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo + 67, // 78: temporal.server.api.persistence.v1.ArchivalTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType + 46, // 79: temporal.server.api.persistence.v1.ArchivalTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp + 67, // 80: temporal.server.api.persistence.v1.OutboundTaskInfo.task_type:type_name -> temporal.server.api.enums.v1.TaskType + 46, // 81: temporal.server.api.persistence.v1.OutboundTaskInfo.visibility_time:type_name -> google.protobuf.Timestamp + 72, // 82: temporal.server.api.persistence.v1.OutboundTaskInfo.state_machine_info:type_name -> temporal.server.api.persistence.v1.StateMachineTaskInfo + 68, // 83: temporal.server.api.persistence.v1.OutboundTaskInfo.chasm_task_info:type_name -> temporal.server.api.persistence.v1.ChasmTaskInfo + 46, // 84: temporal.server.api.persistence.v1.ActivityInfo.scheduled_time:type_name -> google.protobuf.Timestamp + 46, // 85: temporal.server.api.persistence.v1.ActivityInfo.started_time:type_name -> google.protobuf.Timestamp + 47, // 86: temporal.server.api.persistence.v1.ActivityInfo.schedule_to_start_timeout:type_name -> google.protobuf.Duration + 47, // 87: temporal.server.api.persistence.v1.ActivityInfo.schedule_to_close_timeout:type_name -> google.protobuf.Duration + 47, // 88: temporal.server.api.persistence.v1.ActivityInfo.start_to_close_timeout:type_name -> google.protobuf.Duration + 47, // 89: temporal.server.api.persistence.v1.ActivityInfo.heartbeat_timeout:type_name -> google.protobuf.Duration + 47, // 90: temporal.server.api.persistence.v1.ActivityInfo.retry_initial_interval:type_name -> google.protobuf.Duration + 47, // 91: temporal.server.api.persistence.v1.ActivityInfo.retry_maximum_interval:type_name -> google.protobuf.Duration + 46, // 92: temporal.server.api.persistence.v1.ActivityInfo.retry_expiration_time:type_name -> google.protobuf.Timestamp + 73, // 93: temporal.server.api.persistence.v1.ActivityInfo.retry_last_failure:type_name -> temporal.api.failure.v1.Failure + 74, // 94: temporal.server.api.persistence.v1.ActivityInfo.last_heartbeat_details:type_name -> temporal.api.common.v1.Payloads + 46, // 95: temporal.server.api.persistence.v1.ActivityInfo.last_heartbeat_update_time:type_name -> google.protobuf.Timestamp + 75, // 96: temporal.server.api.persistence.v1.ActivityInfo.activity_type:type_name -> temporal.api.common.v1.ActivityType + 38, // 97: temporal.server.api.persistence.v1.ActivityInfo.use_workflow_build_id_info:type_name -> temporal.server.api.persistence.v1.ActivityInfo.UseWorkflowBuildIdInfo + 54, // 98: temporal.server.api.persistence.v1.ActivityInfo.last_worker_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp + 55, // 99: temporal.server.api.persistence.v1.ActivityInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 46, // 100: temporal.server.api.persistence.v1.ActivityInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp + 46, // 101: temporal.server.api.persistence.v1.ActivityInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 76, // 102: temporal.server.api.persistence.v1.ActivityInfo.last_started_deployment:type_name -> temporal.api.deployment.v1.Deployment + 63, // 103: temporal.server.api.persistence.v1.ActivityInfo.last_deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion + 59, // 104: temporal.server.api.persistence.v1.ActivityInfo.priority:type_name -> temporal.api.common.v1.Priority + 39, // 105: temporal.server.api.persistence.v1.ActivityInfo.pause_info:type_name -> temporal.server.api.persistence.v1.ActivityInfo.PauseInfo + 46, // 106: temporal.server.api.persistence.v1.TimerInfo.expiry_time:type_name -> google.protobuf.Timestamp + 55, // 107: temporal.server.api.persistence.v1.TimerInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 77, // 108: temporal.server.api.persistence.v1.ChildExecutionInfo.parent_close_policy:type_name -> temporal.api.enums.v1.ParentClosePolicy + 52, // 109: temporal.server.api.persistence.v1.ChildExecutionInfo.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 55, // 110: temporal.server.api.persistence.v1.ChildExecutionInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 59, // 111: temporal.server.api.persistence.v1.ChildExecutionInfo.priority:type_name -> temporal.api.common.v1.Priority + 55, // 112: temporal.server.api.persistence.v1.RequestCancelInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 55, // 113: temporal.server.api.persistence.v1.SignalInfo.last_update_versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 78, // 114: temporal.server.api.persistence.v1.Checksum.flavor:type_name -> temporal.server.api.enums.v1.ChecksumFlavor + 41, // 115: temporal.server.api.persistence.v1.Callback.nexus:type_name -> temporal.server.api.persistence.v1.Callback.Nexus + 42, // 116: temporal.server.api.persistence.v1.Callback.hsm:type_name -> temporal.server.api.persistence.v1.Callback.HSM + 79, // 117: temporal.server.api.persistence.v1.Callback.links:type_name -> temporal.api.common.v1.Link + 80, // 118: temporal.server.api.persistence.v1.HSMCompletionCallbackArg.last_event:type_name -> temporal.api.history.v1.HistoryEvent + 22, // 119: temporal.server.api.persistence.v1.CallbackInfo.callback:type_name -> temporal.server.api.persistence.v1.Callback + 45, // 120: temporal.server.api.persistence.v1.CallbackInfo.trigger:type_name -> temporal.server.api.persistence.v1.CallbackInfo.Trigger + 46, // 121: temporal.server.api.persistence.v1.CallbackInfo.registration_time:type_name -> google.protobuf.Timestamp + 81, // 122: temporal.server.api.persistence.v1.CallbackInfo.state:type_name -> temporal.server.api.enums.v1.CallbackState + 46, // 123: temporal.server.api.persistence.v1.CallbackInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 73, // 124: temporal.server.api.persistence.v1.CallbackInfo.last_attempt_failure:type_name -> temporal.api.failure.v1.Failure + 46, // 125: temporal.server.api.persistence.v1.CallbackInfo.next_attempt_schedule_time:type_name -> google.protobuf.Timestamp + 47, // 126: temporal.server.api.persistence.v1.NexusOperationInfo.schedule_to_close_timeout:type_name -> google.protobuf.Duration + 46, // 127: temporal.server.api.persistence.v1.NexusOperationInfo.scheduled_time:type_name -> google.protobuf.Timestamp + 82, // 128: temporal.server.api.persistence.v1.NexusOperationInfo.state:type_name -> temporal.server.api.enums.v1.NexusOperationState + 46, // 129: temporal.server.api.persistence.v1.NexusOperationInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 73, // 130: temporal.server.api.persistence.v1.NexusOperationInfo.last_attempt_failure:type_name -> temporal.api.failure.v1.Failure + 46, // 131: temporal.server.api.persistence.v1.NexusOperationInfo.next_attempt_schedule_time:type_name -> google.protobuf.Timestamp + 47, // 132: temporal.server.api.persistence.v1.NexusOperationInfo.schedule_to_start_timeout:type_name -> google.protobuf.Duration + 47, // 133: temporal.server.api.persistence.v1.NexusOperationInfo.start_to_close_timeout:type_name -> google.protobuf.Duration + 46, // 134: temporal.server.api.persistence.v1.NexusOperationInfo.started_time:type_name -> google.protobuf.Timestamp + 46, // 135: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.requested_time:type_name -> google.protobuf.Timestamp + 83, // 136: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.state:type_name -> temporal.api.enums.v1.NexusOperationCancellationState + 46, // 137: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 73, // 138: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.last_attempt_failure:type_name -> temporal.api.failure.v1.Failure + 46, // 139: temporal.server.api.persistence.v1.NexusOperationCancellationInfo.next_attempt_schedule_time:type_name -> google.protobuf.Timestamp + 46, // 140: temporal.server.api.persistence.v1.WorkflowPauseInfo.pause_time:type_name -> google.protobuf.Timestamp + 84, // 141: temporal.server.api.persistence.v1.ShardInfo.QueueStatesEntry.value:type_name -> temporal.server.api.persistence.v1.QueueState + 85, // 142: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SearchAttributesEntry.value:type_name -> temporal.api.common.v1.Payload + 85, // 143: temporal.server.api.persistence.v1.WorkflowExecutionInfo.MemoEntry.value:type_name -> temporal.api.common.v1.Payload + 86, // 144: temporal.server.api.persistence.v1.WorkflowExecutionInfo.UpdateInfosEntry.value:type_name -> temporal.server.api.persistence.v1.UpdateInfo + 87, // 145: temporal.server.api.persistence.v1.WorkflowExecutionInfo.SubStateMachinesByTypeEntry.value:type_name -> temporal.server.api.persistence.v1.StateMachineMap + 27, // 146: temporal.server.api.persistence.v1.WorkflowExecutionInfo.ChildrenInitializedPostResetPointEntry.value:type_name -> temporal.server.api.persistence.v1.ResetChildInfo + 7, // 147: temporal.server.api.persistence.v1.WorkflowExecutionState.RequestIdsEntry.value:type_name -> temporal.server.api.persistence.v1.RequestIDInfo + 46, // 148: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.pause_time:type_name -> google.protobuf.Timestamp + 40, // 149: temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.manual:type_name -> temporal.server.api.persistence.v1.ActivityInfo.PauseInfo.Manual + 43, // 150: temporal.server.api.persistence.v1.Callback.Nexus.header:type_name -> temporal.server.api.persistence.v1.Callback.Nexus.HeaderEntry + 88, // 151: temporal.server.api.persistence.v1.Callback.HSM.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef + 44, // 152: temporal.server.api.persistence.v1.CallbackInfo.Trigger.workflow_closed:type_name -> temporal.server.api.persistence.v1.CallbackInfo.WorkflowClosed + 153, // [153:153] is the sub-list for method output_type + 153, // [153:153] is the sub-list for method input_type + 153, // [153:153] is the sub-list for extension type_name + 153, // [153:153] is the sub-list for extension extendee + 0, // [0:153] is the sub-list for field type_name } func init() { file_temporal_server_api_persistence_v1_executions_proto_init() } @@ -5384,33 +5532,33 @@ func file_temporal_server_api_persistence_v1_executions_proto_init() { (*WorkflowExecutionInfo_LastWorkflowTaskFailureCause)(nil), (*WorkflowExecutionInfo_LastWorkflowTaskTimedOutType)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[6].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[8].OneofWrappers = []any{ (*TransferTaskInfo_CloseExecutionTaskDetails_)(nil), (*TransferTaskInfo_ChasmTaskInfo)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[8].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[10].OneofWrappers = []any{ (*VisibilityTaskInfo_ChasmTaskInfo)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[9].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[11].OneofWrappers = []any{ (*TimerTaskInfo_ChasmTaskInfo)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[11].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[13].OneofWrappers = []any{ (*OutboundTaskInfo_StateMachineInfo)(nil), (*OutboundTaskInfo_ChasmTaskInfo)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[14].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[16].OneofWrappers = []any{ (*ActivityInfo_UseWorkflowBuildIdInfo_)(nil), (*ActivityInfo_LastIndependentlyAssignedBuildId)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[20].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[22].OneofWrappers = []any{ (*Callback_Nexus_)(nil), (*Callback_Hsm)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[37].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[39].OneofWrappers = []any{ (*ActivityInfo_PauseInfo_Manual_)(nil), (*ActivityInfo_PauseInfo_RuleId)(nil), } - file_temporal_server_api_persistence_v1_executions_proto_msgTypes[43].OneofWrappers = []any{ + file_temporal_server_api_persistence_v1_executions_proto_msgTypes[45].OneofWrappers = []any{ (*CallbackInfo_Trigger_WorkflowClosed)(nil), } type x struct{} @@ -5419,7 +5567,7 @@ func file_temporal_server_api_persistence_v1_executions_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_executions_proto_rawDesc), len(file_temporal_server_api_persistence_v1_executions_proto_rawDesc)), NumEnums: 0, - NumMessages: 44, + NumMessages: 46, NumExtensions: 0, NumServices: 0, }, diff --git a/common/clock/time_skipping_time_source.go b/common/clock/time_skipping_time_source.go new file mode 100644 index 0000000000..b0996429de --- /dev/null +++ b/common/clock/time_skipping_time_source.go @@ -0,0 +1,93 @@ +package clock + +import ( + "time" + + persistencespb "go.temporal.io/server/api/persistence/v1" + "google.golang.org/protobuf/types/known/timestamppb" +) + +// TimeSkippingTimeSource is a TimeSource decorator for workflows with time skipping enabled. +// +// Virtual time = real time + offset, where offset is the total duration skipped so far. +// The offset starts at zero and grows each time a WorkflowExecutionTimeSkipped event is applied +// (via Advance). It is never stored as its own field — on workflow reload it is re-derived from +// the persisted TimeSkippedDetails by summing all DurationToSkip values. +// +// Timer methods (AfterFunc, NewTimer) are intentionally not virtualized: OS-level timers always +// fire on real wall clock time. Only Now and Since reflect virtual time. +type TimeSkippingTimeSource struct { + base TimeSource + offset time.Duration +} + +var _ TimeSource = (*TimeSkippingTimeSource)(nil) + +// NewTimeSkippingTimeSource creates a TimeSkippingTimeSource whose initial offset is the sum of +// all previously skipped durations. Pass nil or empty details for a brand-new workflow (offset 0). +func NewTimeSkippingTimeSource(base TimeSource, details []*persistencespb.TimeSkippedDetails) *TimeSkippingTimeSource { + return &TimeSkippingTimeSource{ + base: base, + offset: ComputeTotalSkippedOffset(details), + } +} + +// Now returns the current virtual time (real time + accumulated skipped offset). +func (ts *TimeSkippingTimeSource) Now() time.Time { + return ts.base.Now().Add(ts.offset) +} + +// Since returns the time elapsed since t in virtual time. +func (ts *TimeSkippingTimeSource) Since(t time.Time) time.Duration { + return ts.Now().Sub(t) +} + +// AfterFunc schedules f to run after duration d on the real wall clock. +// TODO(@feiyang): explore if this method needs to respect virtual time — currently it delegates +// to the base clock so f fires after d of wall time, not virtual time. +func (ts *TimeSkippingTimeSource) AfterFunc(d time.Duration, f func()) Timer { + return ts.base.AfterFunc(d, f) +} + +// NewTimer creates a timer that fires after duration d on the real wall clock. +// TODO(@feiyang): explore if this method needs to respect virtual time — currently it delegates +// to the base clock so the timer fires after d of wall time, not virtual time. +func (ts *TimeSkippingTimeSource) NewTimer(d time.Duration) (<-chan time.Time, Timer) { + return ts.base.NewTimer(d) +} + +// Advance increases the virtual time offset by d. +// Called each time a WorkflowExecutionTimeSkipped event is applied to keep the +// in-memory clock in sync without rebuilding it from the full persisted history. +func (ts *TimeSkippingTimeSource) Advance(d time.Duration) { + ts.offset += d +} + +// ComputeTotalSkippedOffset sums the DurationToSkip of each persisted TimeSkippedDetails entry. +// This reconstructs the total virtual time offset after a workflow is loaded from the database. +func ComputeTotalSkippedOffset(details []*persistencespb.TimeSkippedDetails) time.Duration { + var total time.Duration + for _, d := range details { + total += TimeSkippedDurationFromTimestamp(d.GetDurationToSkip()) + } + return total +} + +// TimeSkippedDurationToTimestamp encodes a time.Duration into a *timestamppb.Timestamp by storing +// seconds and nanoseconds in the timestamp's integer fields. This is not a real point in time — +// it is a convention for the DurationToSkip field in TimeSkippedDetails that lets a duration be +// stored in a proto message that has no native duration type. +func TimeSkippedDurationToTimestamp(d time.Duration) *timestamppb.Timestamp { + return ×tamppb.Timestamp{ + Seconds: int64(d / time.Second), + Nanos: int32(d % time.Second), + } +} + +// TimeSkippedDurationFromTimestamp reverses TimeSkippedDurationToTimestamp. +func TimeSkippedDurationFromTimestamp(ts *timestamppb.Timestamp) time.Duration { + if ts == nil { + return 0 + } + return time.Duration(ts.GetSeconds())*time.Second + time.Duration(ts.GetNanos()) +} diff --git a/common/clock/time_skipping_time_source_test.go b/common/clock/time_skipping_time_source_test.go new file mode 100644 index 0000000000..caf88d92f8 --- /dev/null +++ b/common/clock/time_skipping_time_source_test.go @@ -0,0 +1,120 @@ +package clock + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + persistencespb "go.temporal.io/server/api/persistence/v1" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestTimeSkippingTimeSource_NowWithNoSkip(t *testing.T) { + base := NewEventTimeSource() + base.Update(time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)) + + ts := NewTimeSkippingTimeSource(base, nil) + + assert.Equal(t, base.Now(), ts.Now()) +} + +func TestTimeSkippingTimeSource_NowAfterAdvance(t *testing.T) { + base := NewEventTimeSource() + realNow := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) + base.Update(realNow) + + ts := NewTimeSkippingTimeSource(base, nil) + ts.Advance(24 * time.Hour) + + assert.Equal(t, realNow.Add(24*time.Hour), ts.Now()) + // base is unchanged + assert.Equal(t, realNow, base.Now()) +} + +func TestTimeSkippingTimeSource_NowWithMultipleAdvances(t *testing.T) { + base := NewEventTimeSource() + realNow := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) + base.Update(realNow) + + ts := NewTimeSkippingTimeSource(base, nil) + ts.Advance(24 * time.Hour) + ts.Advance(48 * time.Hour) + + assert.Equal(t, realNow.Add(72*time.Hour), ts.Now()) +} + +func TestTimeSkippingTimeSource_ReconstructedFromPersistedDetails(t *testing.T) { + base := NewEventTimeSource() + realNow := time.Date(2025, 6, 1, 12, 0, 0, 0, time.UTC) + base.Update(realNow) + + // Simulate two previously persisted skip events: 10h + 5h = 15h total offset + details := []*persistencespb.TimeSkippedDetails{ + {DurationToSkip: TimeSkippedDurationToTimestamp(10 * time.Hour)}, + {DurationToSkip: TimeSkippedDurationToTimestamp(5 * time.Hour)}, + } + + ts := NewTimeSkippingTimeSource(base, details) + + assert.Equal(t, realNow.Add(15*time.Hour), ts.Now()) +} + +func TestTimeSkippingTimeSource_Since(t *testing.T) { + base := NewEventTimeSource() + realNow := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) + base.Update(realNow) + + ts := NewTimeSkippingTimeSource(base, nil) + ts.Advance(10 * time.Hour) + + past := realNow.Add(-5 * time.Hour) // 5 hours before real now + // virtual now = realNow + 10h, so since(past) = 15h + assert.Equal(t, 15*time.Hour, ts.Since(past)) +} + +func TestTimeSkippingTimeSource_DelegatesTimersToBase(t *testing.T) { + base := NewEventTimeSource() + base.Update(time.Now()) + + ts := NewTimeSkippingTimeSource(base, nil) + + fired := false + ts.AfterFunc(time.Millisecond, func() { fired = true }) + base.Advance(time.Millisecond) + + assert.True(t, fired, "AfterFunc should fire via base time source") +} + +func TestComputeTotalSkippedOffset(t *testing.T) { + details := []*persistencespb.TimeSkippedDetails{ + {DurationToSkip: TimeSkippedDurationToTimestamp(3 * time.Hour)}, + {DurationToSkip: TimeSkippedDurationToTimestamp(7 * time.Hour)}, + {DurationToSkip: TimeSkippedDurationToTimestamp(2 * time.Hour)}, + } + assert.Equal(t, 12*time.Hour, ComputeTotalSkippedOffset(details)) +} + +func TestComputeTotalSkippedOffset_Empty(t *testing.T) { + assert.Equal(t, time.Duration(0), ComputeTotalSkippedOffset(nil)) + assert.Equal(t, time.Duration(0), ComputeTotalSkippedOffset([]*persistencespb.TimeSkippedDetails{})) +} + +// roundtrip helper — verifies encoding/decoding is consistent +func TestTimeSkippedDurationRoundtrip(t *testing.T) { + durations := []time.Duration{ + 0, + time.Second, + time.Hour, + 24 * time.Hour, + 365 * 24 * time.Hour, + time.Hour + 30*time.Minute + 15*time.Second, + } + for _, d := range durations { + ts := TimeSkippedDurationToTimestamp(d) + assert.Equal(t, d, TimeSkippedDurationFromTimestamp(ts)) + } +} + +func TestTimeSkippedDurationFromTimestamp_Nil(t *testing.T) { + assert.Equal(t, time.Duration(0), TimeSkippedDurationFromTimestamp((*timestamppb.Timestamp)(nil))) +} diff --git a/common/dynamicconfig/constants.go b/common/dynamicconfig/constants.go index 1bf78babd8..297911ee48 100644 --- a/common/dynamicconfig/constants.go +++ b/common/dynamicconfig/constants.go @@ -3189,4 +3189,9 @@ WorkerActivitiesPerSecond, MaxConcurrentActivityTaskPollers. false, `WorkflowPauseEnabled is a "feature enable" flag. When enabled it allows clients to pause workflows.`, ) + TimeSkippingEnabled = NewNamespaceBoolSetting( + "frontend.TimeSkippingEnabled", + false, + `TimeSkippingEnabled is a "feature enable" flag. When enabled it allows clients to skip time in workflows.`, + ) ) diff --git a/common/log/tag/values.go b/common/log/tag/values.go index cbb44a4e03..173185609e 100644 --- a/common/log/tag/values.go +++ b/common/log/tag/values.go @@ -12,6 +12,7 @@ var ( WorkflowActionWorkflowContinueAsNew = workflowAction("add-workflow-continue-as-new-event") WorkflowActionWorkflowPaused = workflowAction("add-workflow-paused-event") WorkflowActionWorkflowUnpaused = workflowAction("add-workflow-unpaused-event") + WorkflowActionWorkflowTimeSkipped = workflowAction("add-workflow-time-skipped-event") // workflow cancellation / sign / update-options WorkflowActionWorkflowCancelRequested = workflowAction("add-workflow-cancel-requested-event") diff --git a/common/metrics/metric_defs.go b/common/metrics/metric_defs.go index ce6a4c3641..7c2763f164 100644 --- a/common/metrics/metric_defs.go +++ b/common/metrics/metric_defs.go @@ -1128,6 +1128,10 @@ var ( NamespaceRegistryRefreshFailures = NewCounterDef("namespace_registry_refresh_failures") NamespaceRegistryRefreshLatency = NewTimerDef("namespace_registry_refresh_latency") + ExecutionTimeSkippingDurationSkippedEventCount = NewCounterDef("execution_time_skipping_duration_skipped_event_count") + ExecutionTimeSkippingEnabledCount = NewCounterDef("execution_time_skipping_enabled_count") + ExecutionTimeSkippingDisabledCount = NewCounterDef("execution_time_skipping_disabled_count") + // Matching MatchingClientForwardedCounter = NewCounterDef("forwarded") MatchingClientInvalidTaskQueueName = NewCounterDef("invalid_task_queue_name") diff --git a/common/persistence/serialization/task_serializers.go b/common/persistence/serialization/task_serializers.go index 598cd194dd..22e10d1032 100644 --- a/common/persistence/serialization/task_serializers.go +++ b/common/persistence/serialization/task_serializers.go @@ -134,6 +134,8 @@ func serializeTimerTask( timerTask = timerChasmTaskToProto(task) case *tasks.ChasmTaskPure: timerTask = timerChasmPureTaskToProto(task) + case *tasks.TimeSkippingTimerTask: + timerTask = timerTimeSkippingTaskToProto(task) default: return nil, serviceerror.NewInternalf("Unknown timer task type: %v", task) } @@ -202,6 +204,8 @@ func deserializeTimerTask( timer = timerChasmTaskFromProto(timerTask) case enumsspb.TASK_TYPE_CHASM_PURE: timer = timerChasmPureTaskFromProto(timerTask) + case enumsspb.TASK_TYPE_TIME_SKIPPING: + timer = timerTimeSkippingTaskFromProto(timerTask) default: return nil, serviceerror.NewInternalf("Unknown timer task type: %v", timerTask.TaskType) } @@ -1492,3 +1496,26 @@ func deserializeOutboundTask( return nil, serviceerror.NewInternalf("unknown outbound task type while deserializing: %v", info) } } + +func timerTimeSkippingTaskToProto(task *tasks.TimeSkippingTimerTask) *persistencespb.TimerTaskInfo { + return &persistencespb.TimerTaskInfo{ + NamespaceId: task.NamespaceID, + WorkflowId: task.WorkflowID, + RunId: task.RunID, + TaskType: enumsspb.TASK_TYPE_TIME_SKIPPING, + TaskId: task.TaskID, + VisibilityTime: timestamppb.New(task.VisibilityTimestamp), + } +} + +func timerTimeSkippingTaskFromProto(info *persistencespb.TimerTaskInfo) *tasks.TimeSkippingTimerTask { + return &tasks.TimeSkippingTimerTask{ + WorkflowKey: definition.NewWorkflowKey( + info.NamespaceId, + info.WorkflowId, + info.RunId, + ), + VisibilityTimestamp: info.VisibilityTime.AsTime(), + TaskID: info.TaskId, + } +} diff --git a/go.mod b/go.mod index aa2d419616..daf2e0ea52 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( go.opentelemetry.io/otel/sdk v1.40.0 go.opentelemetry.io/otel/sdk/metric v1.40.0 go.opentelemetry.io/otel/trace v1.40.0 - go.temporal.io/api v1.62.5 + go.temporal.io/api v1.62.5-0.20260323233506-7011abe4c28d go.temporal.io/sdk v1.38.0 go.uber.org/fx v1.24.0 go.uber.org/mock v0.6.0 diff --git a/go.sum b/go.sum index ab7a7f9f22..c946d3414e 100644 --- a/go.sum +++ b/go.sum @@ -376,8 +376,8 @@ go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZY go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= -go.temporal.io/api v1.62.5 h1:9R/9CeyM7xqHSlsNt+QIvapQLcRxCZ38bnXQx4mCN6I= -go.temporal.io/api v1.62.5/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= +go.temporal.io/api v1.62.5-0.20260323233506-7011abe4c28d h1:EdQsq0dRCYVrvE6mPDyUdM7j/+HE7FRHE7WqGhlIs/8= +go.temporal.io/api v1.62.5-0.20260323233506-7011abe4c28d/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= go.temporal.io/sdk v1.38.0 h1:4Bok5LEdED7YKpsSjIa3dDqram5VOq+ydBf4pyx0Wo4= go.temporal.io/sdk v1.38.0/go.mod h1:a+R2Ej28ObvHoILbHaxMyind7M6D+W0L7edt5UJF4SE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= diff --git a/proto/internal/temporal/server/api/enums/v1/task.proto b/proto/internal/temporal/server/api/enums/v1/task.proto index 34e026271a..653c49c65c 100644 --- a/proto/internal/temporal/server/api/enums/v1/task.proto +++ b/proto/internal/temporal/server/api/enums/v1/task.proto @@ -59,6 +59,9 @@ enum TaskType { // A task with side effects generated by a CHASM component. TASK_TYPE_CHASM = 33; + + // A task that triggers time skipping logic for a workflow execution. + TASK_TYPE_TIME_SKIPPING = 34; } // TaskPriority is only used for replication task as of May 2024 diff --git a/proto/internal/temporal/server/api/persistence/v1/executions.proto b/proto/internal/temporal/server/api/persistence/v1/executions.proto index 0c1f412cfb..a6967aa1cb 100644 --- a/proto/internal/temporal/server/api/persistence/v1/executions.proto +++ b/proto/internal/temporal/server/api/persistence/v1/executions.proto @@ -315,6 +315,9 @@ message WorkflowExecutionInfo { // // Wrapper distinguishes "never declined" (nil) from "declined unversioned" (non-nil, nil version). temporal.api.history.v1.DeclinedTargetVersionUpgrade declined_target_version_upgrade = 114; + + // Time skipping info that contains the config and duration offsets of the time skipping for the workflow. + TimeSkippingInfo time_skipping_info = 120; } // Internal wrapper message to distinguish "never notified" (nil wrapper) from @@ -324,6 +327,26 @@ message LastNotifiedTargetVersion { temporal.api.deployment.v1.WorkerDeploymentVersion deployment_version = 1; } +message TimeSkippingInfo { + // metadata + bool enabled = 1; + // runtime history + repeated TimeSkippedDetails time_skipped_details = 2; +} + +message TimeSkippedDetails { + // (-- api-linter: core::0142::time-field-names=disabled + // aip.dev/not-precedent: Ignoring lint rules. --) + google.protobuf.Timestamp skipping_time = 2; + // (-- api-linter: core::0142::time-field-names=disabled + // api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: Ignoring lint rules. --) + google.protobuf.Timestamp duration_to_skip = 3; + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: Ignoring lint rules. --) + google.protobuf.Timestamp to_time = 4; +} + message ExecutionStats { int64 history_size = 1; // Total size in bytes of all external payloads referenced in the entire history tree of the execution, not just the current branch. diff --git a/service/frontend/service.go b/service/frontend/service.go index 486988b200..7f661beeda 100644 --- a/service/frontend/service.go +++ b/service/frontend/service.go @@ -226,6 +226,7 @@ type Config struct { NumTaskQueueReadPartitions dynamicconfig.IntPropertyFnWithTaskQueueFilter WorkerCommandsEnabled dynamicconfig.BoolPropertyFnWithNamespaceFilter WorkflowPauseEnabled dynamicconfig.BoolPropertyFnWithNamespaceFilter + TimeSkippingEnabled dynamicconfig.BoolPropertyFnWithNamespaceFilter HTTPAllowedHosts dynamicconfig.TypedPropertyFn[*regexp.Regexp] AllowedExperiments dynamicconfig.TypedPropertyFnWithNamespaceFilter[[]string] @@ -392,6 +393,7 @@ func NewConfig( NumTaskQueueReadPartitions: dynamicconfig.MatchingNumTaskqueueReadPartitions.Get(dc), WorkerCommandsEnabled: dynamicconfig.WorkerCommandsEnabled.Get(dc), WorkflowPauseEnabled: dynamicconfig.WorkflowPauseEnabled.Get(dc), + TimeSkippingEnabled: dynamicconfig.TimeSkippingEnabled.Get(dc), HTTPAllowedHosts: dynamicconfig.FrontendHTTPAllowedHosts.Get(dc), AllowedExperiments: dynamicconfig.FrontendAllowedExperiments.Get(dc), diff --git a/service/frontend/workflow_handler.go b/service/frontend/workflow_handler.go index b8207ba89e..08cbbbadf0 100644 --- a/service/frontend/workflow_handler.go +++ b/service/frontend/workflow_handler.go @@ -525,7 +525,6 @@ func (wh *WorkflowHandler) prepareStartWorkflowRequest( if err := wh.validateWorkflowCompletionCallbacks(namespaceName, request.GetCompletionCallbacks()); err != nil { return nil, err } - request.Links = dedupLinksFromCallbacks(request.GetLinks(), request.GetCompletionCallbacks()) allLinks := make([]*commonpb.Link, 0, len(request.GetLinks())+len(request.GetCompletionCallbacks())) @@ -537,9 +536,32 @@ func (wh *WorkflowHandler) prepareStartWorkflowRequest( return nil, err } + if err := wh.validateTimeSkippingConfig(request.GetTimeSkippingConfig(), namespaceName); err != nil { + return nil, err + } return request, nil } +func (wh *WorkflowHandler) validateTimeSkippingConfig( + timeSkippingConfig *workflowpb.TimeSkippingConfig, + namespaceName namespace.Name, +) error { + if timeSkippingConfig == nil { + return nil + } + if !timeSkippingConfig.GetEnabled() { + return nil + } + enabled := wh.config.TimeSkippingEnabled(namespaceName.String()) + if !enabled { + return serviceerror.NewInvalidArgumentf( + "Time skipping is not enabled for namespace %s", + namespaceName, + ) + } + return nil +} + func (wh *WorkflowHandler) unaliasedSearchAttributesFrom( attributes *commonpb.SearchAttributes, namespaceName namespace.Name, @@ -2175,6 +2197,9 @@ func (wh *WorkflowHandler) SignalWithStartWorkflowExecution(ctx context.Context, ); err != nil { return nil, err } + if err := wh.validateTimeSkippingConfig(request.GetTimeSkippingConfig(), namespaceName); err != nil { + return nil, err + } if request.WorkflowIdConflictPolicy == enumspb.WORKFLOW_ID_CONFLICT_POLICY_FAIL { // Signal-with-*Required*-Start is not supported @@ -2251,6 +2276,17 @@ func (wh *WorkflowHandler) ResetWorkflowExecution(ctx context.Context, request * return nil, serviceerror.NewInternalf("unknown reset reapply type: %v", request.GetResetReapplyType()) } + for _, postOp := range request.GetPostResetOperations() { + if updateOpts := postOp.GetUpdateWorkflowOptions(); updateOpts != nil { + if err := wh.validateTimeSkippingConfig( + updateOpts.GetWorkflowExecutionOptions().GetTimeSkippingConfig(), + namespace.Name(request.GetNamespace()), + ); err != nil { + return nil, err + } + } + } + namespaceID, err := wh.namespaceRegistry.GetNamespaceID(namespace.Name(request.GetNamespace())) if err != nil { return nil, err @@ -5379,9 +5415,25 @@ func (wh *WorkflowHandler) StartBatchOperation( case *workflowservice.StartBatchOperationRequest_ResetOperation: input.BatchType = enumspb.BATCH_OPERATION_TYPE_RESET identity = op.ResetOperation.GetIdentity() + for _, postOp := range op.ResetOperation.GetPostResetOperations() { + if updateOpts := postOp.GetUpdateWorkflowOptions(); updateOpts != nil { + if err := wh.validateTimeSkippingConfig( + updateOpts.GetWorkflowExecutionOptions().GetTimeSkippingConfig(), + namespace.Name(request.GetNamespace()), + ); err != nil { + return nil, err + } + } + } case *workflowservice.StartBatchOperationRequest_UpdateWorkflowOptionsOperation: input.BatchType = enumspb.BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS identity = op.UpdateWorkflowOptionsOperation.GetIdentity() + if err := wh.validateTimeSkippingConfig( + op.UpdateWorkflowOptionsOperation.GetWorkflowExecutionOptions().GetTimeSkippingConfig(), + namespace.Name(request.GetNamespace()), + ); err != nil { + return nil, err + } case *workflowservice.StartBatchOperationRequest_UnpauseActivitiesOperation: input.BatchType = enumspb.BATCH_OPERATION_TYPE_UNPAUSE_ACTIVITY identity = op.UnpauseActivitiesOperation.GetIdentity() @@ -6586,6 +6638,9 @@ func (wh *WorkflowHandler) UpdateWorkflowExecutionOptions( if err := priorities.Validate(opts.GetPriority()); err != nil { return nil, err } + if err := wh.validateTimeSkippingConfig(opts.GetTimeSkippingConfig(), namespace.Name(request.GetNamespace())); err != nil { + return nil, err + } namespaceID, err := wh.namespaceRegistry.GetNamespaceID(namespace.Name(request.GetNamespace())) if err != nil { diff --git a/service/frontend/workflow_handler_test.go b/service/frontend/workflow_handler_test.go index c202ddbddc..f1007d77c9 100644 --- a/service/frontend/workflow_handler_test.go +++ b/service/frontend/workflow_handler_test.go @@ -3220,6 +3220,481 @@ func (s *WorkflowHandlerSuite) TestGetWorkflowExecutionHistory_InternalRawHistor s.Equal("this workflow failed", attrs2.Failure.Message) } +func (s *WorkflowHandlerSuite) TestValidateTimeSkippingConfig() { + config := s.newConfig() + wh := s.getWorkflowHandler(config) + + // nil config is valid + s.NoError(wh.validateTimeSkippingConfig(nil, s.testNamespace)) + + // config with enabled=false is valid + s.NoError(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{Enabled: false}, s.testNamespace)) + + // config with enabled=true but dynamic config disabled returns error + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + s.Error(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{Enabled: true}, s.testNamespace)) + + // config with enabled=true and dynamic config enabled is valid + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + s.NoError(wh.validateTimeSkippingConfig(&workflowpb.TimeSkippingConfig{Enabled: true}, s.testNamespace)) +} + +// TestStartWorkflowExecution_TimeSkipping_DCDisabled verifies that requesting time skipping when the +// dynamic-config gate is off returns an InvalidArgument error before the history client is called. +// Note: validateTimeSkippingConfig fires after unaliasedSearchAttributesFrom inside +// prepareStartWorkflowRequest, so the SA-mapper mock must be set up even for the error path. +func (s *WorkflowHandlerSuite) TestStartWorkflowExecution_TimeSkipping_DCDisabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + wh := s.getWorkflowHandler(config) + s.mockSearchAttributesMapperProvider.EXPECT().GetMapper(gomock.Any()).Return(nil, nil) + + _, err := wh.StartWorkflowExecution(context.Background(), &workflowservice.StartWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowId: "workflow-id", + WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }) + var invalidArg *serviceerror.InvalidArgument + s.ErrorAs(err, &invalidArg) + s.ErrorContains(err, "Time skipping is not enabled") +} + +// TestStartWorkflowExecution_TimeSkipping_DCEnabled verifies that when the gate is on, the +// time-skipping config is forwarded to the history client. +func (s *WorkflowHandlerSuite) TestStartWorkflowExecution_TimeSkipping_DCEnabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + wh := s.getWorkflowHandler(config) + s.mockSearchAttributesMapperProvider.EXPECT().GetMapper(gomock.Any()).Return(nil, nil) + s.mockNamespaceCache.EXPECT().GetNamespaceID(s.testNamespace).Return(s.testNamespaceID, nil) + s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), mock.MatchedBy(func(req *historyservice.StartWorkflowExecutionRequest) bool { + return req.GetStartRequest().GetTimeSkippingConfig().GetEnabled() + })).Return(&historyservice.StartWorkflowExecutionResponse{}, nil) + + _, err := wh.StartWorkflowExecution(context.Background(), &workflowservice.StartWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowId: "workflow-id", + WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }) + s.NoError(err) +} + +// TestSignalWithStartWorkflowExecution_TimeSkipping_DCDisabled verifies the DC gate for +// SignalWithStart. validateTimeSkippingConfig fires before unaliasedSearchAttributesFrom in this +// handler, so no mapper mock is needed on the error path. +func (s *WorkflowHandlerSuite) TestSignalWithStartWorkflowExecution_TimeSkipping_DCDisabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + wh := s.getWorkflowHandler(config) + + _, err := wh.SignalWithStartWorkflowExecution(context.Background(), &workflowservice.SignalWithStartWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowId: "workflow-id", + WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, + SignalName: "my-signal", + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }) + var invalidArg *serviceerror.InvalidArgument + s.ErrorAs(err, &invalidArg) + s.ErrorContains(err, "Time skipping is not enabled") +} + +// TestSignalWithStartWorkflowExecution_TimeSkipping_DCEnabled verifies that when the gate is on, +// the time-skipping config reaches the history client. +func (s *WorkflowHandlerSuite) TestSignalWithStartWorkflowExecution_TimeSkipping_DCEnabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + wh := s.getWorkflowHandler(config) + s.mockSearchAttributesMapperProvider.EXPECT().GetMapper(gomock.Any()).Return(nil, nil) + s.mockNamespaceCache.EXPECT().GetNamespaceID(s.testNamespace).Return(s.testNamespaceID, nil) + s.mockHistoryClient.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), mock.MatchedBy(func(req *historyservice.SignalWithStartWorkflowExecutionRequest) bool { + return req.GetSignalWithStartRequest().GetTimeSkippingConfig().GetEnabled() + })).Return(&historyservice.SignalWithStartWorkflowExecutionResponse{Started: true}, nil) + + _, err := wh.SignalWithStartWorkflowExecution(context.Background(), &workflowservice.SignalWithStartWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowId: "workflow-id", + WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, + SignalName: "my-signal", + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }) + s.NoError(err) +} + +// TestExecuteMultiOperation_TimeSkipping_DCDisabled verifies that when the DC gate is off, +// a Start-with-time-skipping inside ExecuteMultiOperation is rejected. The error is wrapped +// as a MultiOperationExecution error with the per-operation InvalidArgument at index 0. +func (s *WorkflowHandlerSuite) TestExecuteMultiOperation_TimeSkipping_DCDisabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + wh := s.getWorkflowHandler(config) + // Namespace lookup happens before operation validation. + s.mockNamespaceCache.EXPECT().GetNamespaceID(namespace.Name(s.testNamespace.String())).Return(s.testNamespaceID, nil) + // The SA mapper is called inside prepareStartWorkflowRequest before validateTimeSkippingConfig. + s.mockSearchAttributesMapperProvider.EXPECT().GetMapper(gomock.Any()).Return(nil, nil) + + _, err := wh.ExecuteMultiOperation(context.Background(), &workflowservice.ExecuteMultiOperationRequest{ + Namespace: s.testNamespace.String(), + Operations: []*workflowservice.ExecuteMultiOperationRequest_Operation{ + { + Operation: &workflowservice.ExecuteMultiOperationRequest_Operation_StartWorkflow{ + StartWorkflow: &workflowservice.StartWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowId: "WORKFLOW_ID", + WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + }, + }, + { + Operation: &workflowservice.ExecuteMultiOperationRequest_Operation_UpdateWorkflow{ + UpdateWorkflow: &workflowservice.UpdateWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: "WORKFLOW_ID"}, + Request: &updatepb.Request{ + Meta: &updatepb.Meta{UpdateId: "UPDATE_ID"}, + Input: &updatepb.Input{Name: "NAME"}, + }, + }, + }, + }, + }, + }) + s.Equal("Update-with-Start could not be executed.", err.Error()) + var multiOpErr *serviceerror.MultiOperationExecution + s.ErrorAs(err, &multiOpErr) + var invalidArg *serviceerror.InvalidArgument + s.ErrorAs(multiOpErr.OperationErrors()[0], &invalidArg) + s.ErrorContains(multiOpErr.OperationErrors()[0], "Time skipping is not enabled") +} + +// TestUpdateWorkflowExecutionOptions_TimeSkipping_DCDisabled verifies the DC gate for +// UpdateWorkflowExecutionOptions. The validation fires before the namespace lookup, so +// no namespace or history-client mock is needed. +func (s *WorkflowHandlerSuite) TestUpdateWorkflowExecutionOptions_TimeSkipping_DCDisabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + wh := s.getWorkflowHandler(config) + + _, err := wh.UpdateWorkflowExecutionOptions(context.Background(), &workflowservice.UpdateWorkflowExecutionOptionsRequest{ + Namespace: s.testNamespace.String(), + WorkflowExecution: &commonpb.WorkflowExecution{ + WorkflowId: "workflow-id", + }, + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }) + var invalidArg *serviceerror.InvalidArgument + s.ErrorAs(err, &invalidArg) + s.ErrorContains(err, "Time skipping is not enabled") +} + +// TestUpdateWorkflowExecutionOptions_TimeSkipping_DCEnabled verifies that when the gate is on, +// the time-skipping config is forwarded to the history client. +func (s *WorkflowHandlerSuite) TestUpdateWorkflowExecutionOptions_TimeSkipping_DCEnabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + wh := s.getWorkflowHandler(config) + s.mockNamespaceCache.EXPECT().GetNamespaceID(s.testNamespace).Return(s.testNamespaceID, nil) + s.mockHistoryClient.EXPECT().UpdateWorkflowExecutionOptions(gomock.Any(), mock.MatchedBy(func(req *historyservice.UpdateWorkflowExecutionOptionsRequest) bool { + return req.GetUpdateRequest().GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled() + })).Return(&historyservice.UpdateWorkflowExecutionOptionsResponse{}, nil) + + _, err := wh.UpdateWorkflowExecutionOptions(context.Background(), &workflowservice.UpdateWorkflowExecutionOptionsRequest{ + Namespace: s.testNamespace.String(), + WorkflowExecution: &commonpb.WorkflowExecution{ + WorkflowId: "workflow-id", + }, + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }) + s.NoError(err) +} + +// TestExecuteMultiOperation_TimeSkipping_DCEnabled verifies that when the DC gate is on, +// a Start-with-time-skipping inside ExecuteMultiOperation is accepted and the config is +// forwarded to the history client inside the StartWorkflow request. +func (s *WorkflowHandlerSuite) TestExecuteMultiOperation_TimeSkipping_DCEnabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + wh := s.getWorkflowHandler(config) + s.mockNamespaceCache.EXPECT().GetNamespaceID(namespace.Name(s.testNamespace.String())).Return(s.testNamespaceID, nil) + s.mockSearchAttributesMapperProvider.EXPECT().GetMapper(gomock.Any()).Return(nil, nil) + s.mockHistoryClient.EXPECT().ExecuteMultiOperation(gomock.Any(), mock.MatchedBy(func(req *historyservice.ExecuteMultiOperationRequest) bool { + startOp := req.GetOperations()[0].GetStartWorkflow() + return startOp.GetStartRequest().GetTimeSkippingConfig().GetEnabled() + })).Return(&historyservice.ExecuteMultiOperationResponse{ + Responses: []*historyservice.ExecuteMultiOperationResponse_Response{ + { + Response: &historyservice.ExecuteMultiOperationResponse_Response_StartWorkflow{ + StartWorkflow: &historyservice.StartWorkflowExecutionResponse{}, + }, + }, + { + Response: &historyservice.ExecuteMultiOperationResponse_Response_UpdateWorkflow{ + UpdateWorkflow: &historyservice.UpdateWorkflowExecutionResponse{}, + }, + }, + }, + }, nil) + + _, err := wh.ExecuteMultiOperation(context.Background(), &workflowservice.ExecuteMultiOperationRequest{ + Namespace: s.testNamespace.String(), + Operations: []*workflowservice.ExecuteMultiOperationRequest_Operation{ + { + Operation: &workflowservice.ExecuteMultiOperationRequest_Operation_StartWorkflow{ + StartWorkflow: &workflowservice.StartWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowId: "WORKFLOW_ID", + WorkflowType: &commonpb.WorkflowType{Name: "workflow-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "task-queue"}, + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + }, + }, + { + Operation: &workflowservice.ExecuteMultiOperationRequest_Operation_UpdateWorkflow{ + UpdateWorkflow: &workflowservice.UpdateWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: "WORKFLOW_ID"}, + Request: &updatepb.Request{ + Meta: &updatepb.Meta{UpdateId: "UPDATE_ID"}, + Input: &updatepb.Input{Name: "NAME"}, + }, + }, + }, + }, + }, + }) + s.NoError(err) +} + +// TestStartBatchOperation_UpdateWorkflowOptionsOperation_TimeSkipping_DCDisabled verifies that +// a batch UpdateWorkflowOptions operation with time-skipping enabled is rejected when the DC gate is off. +func (s *WorkflowHandlerSuite) TestStartBatchOperation_UpdateWorkflowOptionsOperation_TimeSkipping_DCDisabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + wh := s.getWorkflowHandler(config) + // CountWorkflowExecutions and the direct namespace lookup both call GetNamespaceID. + s.mockNamespaceCache.EXPECT().GetNamespaceID(gomock.Any()).Return(s.testNamespaceID, nil).AnyTimes() + s.mockVisibilityMgr.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&manager.CountWorkflowExecutionsResponse{Count: 0}, nil) + + _, err := wh.StartBatchOperation(context.Background(), &workflowservice.StartBatchOperationRequest{ + Namespace: s.testNamespace.String(), + JobId: uuid.NewString(), + Reason: "test", + VisibilityQuery: "WorkflowType='test'", + Operation: &workflowservice.StartBatchOperationRequest_UpdateWorkflowOptionsOperation{ + UpdateWorkflowOptionsOperation: &batchpb.BatchOperationUpdateWorkflowExecutionOptions{ + Identity: "test-identity", + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }, + }, + }) + var invalidArg *serviceerror.InvalidArgument + s.ErrorAs(err, &invalidArg) + s.ErrorContains(err, "Time skipping is not enabled") +} + +// TestStartBatchOperation_UpdateWorkflowOptionsOperation_TimeSkipping_DCEnabled verifies that +// when the gate is on, the batch operation proceeds and the config is forwarded to history. +func (s *WorkflowHandlerSuite) TestStartBatchOperation_UpdateWorkflowOptionsOperation_TimeSkipping_DCEnabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + wh := s.getWorkflowHandler(config) + s.mockNamespaceCache.EXPECT().GetNamespaceID(gomock.Any()).Return(s.testNamespaceID, nil).AnyTimes() + s.mockVisibilityMgr.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&manager.CountWorkflowExecutionsResponse{Count: 0}, nil) + s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), mock.MatchedBy(func(req *historyservice.StartWorkflowExecutionRequest) bool { + var input batchspb.BatchOperationInput + _ = payloads.Decode(req.GetStartRequest().GetInput(), &input) + op := input.GetRequest().GetOperation().(*workflowservice.StartBatchOperationRequest_UpdateWorkflowOptionsOperation) + return op.UpdateWorkflowOptionsOperation.GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled() + })).Return(&historyservice.StartWorkflowExecutionResponse{}, nil) + + _, err := wh.StartBatchOperation(context.Background(), &workflowservice.StartBatchOperationRequest{ + Namespace: s.testNamespace.String(), + JobId: uuid.NewString(), + Reason: "test", + VisibilityQuery: "WorkflowType='test'", + Operation: &workflowservice.StartBatchOperationRequest_UpdateWorkflowOptionsOperation{ + UpdateWorkflowOptionsOperation: &batchpb.BatchOperationUpdateWorkflowExecutionOptions{ + Identity: "test-identity", + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }, + }, + }) + s.NoError(err) +} + +// TestStartBatchOperation_ResetOperation_PostReset_TimeSkipping_DCDisabled verifies that a batch +// reset with a post-reset UpdateWorkflowOptions that enables time-skipping is rejected when the DC +// gate is off. +func (s *WorkflowHandlerSuite) TestStartBatchOperation_ResetOperation_PostReset_TimeSkipping_DCDisabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + wh := s.getWorkflowHandler(config) + s.mockNamespaceCache.EXPECT().GetNamespaceID(gomock.Any()).Return(s.testNamespaceID, nil).AnyTimes() + s.mockVisibilityMgr.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&manager.CountWorkflowExecutionsResponse{Count: 0}, nil) + + _, err := wh.StartBatchOperation(context.Background(), &workflowservice.StartBatchOperationRequest{ + Namespace: s.testNamespace.String(), + JobId: uuid.NewString(), + Reason: "test", + VisibilityQuery: "WorkflowType='test'", + Operation: &workflowservice.StartBatchOperationRequest_ResetOperation{ + ResetOperation: &batchpb.BatchOperationReset{ + Identity: "test-identity", + Options: &commonpb.ResetOptions{ + Target: &commonpb.ResetOptions_WorkflowTaskId{WorkflowTaskId: 10}, + }, + PostResetOperations: []*workflowpb.PostResetOperation{ + { + Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ + UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }, + }, + }, + }, + }, + }, + }) + var invalidArg *serviceerror.InvalidArgument + s.ErrorAs(err, &invalidArg) + s.ErrorContains(err, "Time skipping is not enabled") +} + +// TestStartBatchOperation_ResetOperation_PostReset_TimeSkipping_DCEnabled verifies that when the +// gate is on, a batch reset with a time-skipping post-reset operation proceeds and the config is +// forwarded to history inside the encoded batch input. +func (s *WorkflowHandlerSuite) TestStartBatchOperation_ResetOperation_PostReset_TimeSkipping_DCEnabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + wh := s.getWorkflowHandler(config) + s.mockNamespaceCache.EXPECT().GetNamespaceID(gomock.Any()).Return(s.testNamespaceID, nil).AnyTimes() + s.mockVisibilityMgr.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&manager.CountWorkflowExecutionsResponse{Count: 0}, nil) + s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), mock.MatchedBy(func(req *historyservice.StartWorkflowExecutionRequest) bool { + var input batchspb.BatchOperationInput + _ = payloads.Decode(req.GetStartRequest().GetInput(), &input) + postOps := input.GetRequest().GetOperation().(*workflowservice.StartBatchOperationRequest_ResetOperation).ResetOperation.GetPostResetOperations() + return len(postOps) == 1 && + postOps[0].GetUpdateWorkflowOptions().GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled() + })).Return(&historyservice.StartWorkflowExecutionResponse{}, nil) + + _, err := wh.StartBatchOperation(context.Background(), &workflowservice.StartBatchOperationRequest{ + Namespace: s.testNamespace.String(), + JobId: uuid.NewString(), + Reason: "test", + VisibilityQuery: "WorkflowType='test'", + Operation: &workflowservice.StartBatchOperationRequest_ResetOperation{ + ResetOperation: &batchpb.BatchOperationReset{ + Identity: "test-identity", + Options: &commonpb.ResetOptions{ + Target: &commonpb.ResetOptions_WorkflowTaskId{WorkflowTaskId: 10}, + }, + PostResetOperations: []*workflowpb.PostResetOperation{ + { + Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ + UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }, + }, + }, + }, + }, + }, + }) + s.NoError(err) +} + +// TestResetWorkflowExecution_PostReset_TimeSkipping_DCDisabled verifies that a reset request with a +// post-reset UpdateWorkflowOptions that enables time-skipping is rejected when the DC gate is off. +// The validation fires before the namespace lookup. +func (s *WorkflowHandlerSuite) TestResetWorkflowExecution_PostReset_TimeSkipping_DCDisabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(false) + wh := s.getWorkflowHandler(config) + + _, err := wh.ResetWorkflowExecution(context.Background(), &workflowservice.ResetWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + RequestId: uuid.NewString(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: "workflow-id", RunId: uuid.NewString()}, + WorkflowTaskFinishEventId: 5, + PostResetOperations: []*workflowpb.PostResetOperation{ + { + Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ + UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }, + }, + }, + }, + }) + var invalidArg *serviceerror.InvalidArgument + s.ErrorAs(err, &invalidArg) + s.ErrorContains(err, "Time skipping is not enabled") +} + +// TestResetWorkflowExecution_PostReset_TimeSkipping_DCEnabled verifies that when the gate is on, +// a reset with a time-skipping post-reset operation proceeds and the config reaches history. +func (s *WorkflowHandlerSuite) TestResetWorkflowExecution_PostReset_TimeSkipping_DCEnabled() { + config := s.newConfig() + config.TimeSkippingEnabled = dc.GetBoolPropertyFnFilteredByNamespace(true) + wh := s.getWorkflowHandler(config) + s.mockNamespaceCache.EXPECT().GetNamespaceID(s.testNamespace).Return(s.testNamespaceID, nil) + s.mockHistoryClient.EXPECT().ResetWorkflowExecution(gomock.Any(), mock.MatchedBy(func(req *historyservice.ResetWorkflowExecutionRequest) bool { + postOps := req.GetResetRequest().GetPostResetOperations() + return len(postOps) == 1 && + postOps[0].GetUpdateWorkflowOptions().GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled() + })).Return(&historyservice.ResetWorkflowExecutionResponse{RunId: uuid.NewString()}, nil) + + _, err := wh.ResetWorkflowExecution(context.Background(), &workflowservice.ResetWorkflowExecutionRequest{ + Namespace: s.testNamespace.String(), + RequestId: uuid.NewString(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: "workflow-id", RunId: uuid.NewString()}, + WorkflowTaskFinishEventId: 5, + PostResetOperations: []*workflowpb.PostResetOperation{ + { + Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ + UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + }, + }, + }, + }, + }) + s.NoError(err) +} + func (s *WorkflowHandlerSuite) newConfig() *Config { return NewConfig(dc.NewNoopCollection(), numHistoryShards) } diff --git a/service/history/api/signalwithstartworkflow/convert.go b/service/history/api/signalwithstartworkflow/convert.go index 0e7a0a131c..39a68c32e5 100644 --- a/service/history/api/signalwithstartworkflow/convert.go +++ b/service/history/api/signalwithstartworkflow/convert.go @@ -37,6 +37,7 @@ func ConvertToStartRequest( Links: request.GetLinks(), VersioningOverride: request.GetVersioningOverride(), Priority: request.GetPriority(), + TimeSkippingConfig: request.GetTimeSkippingConfig(), } return common.CreateHistoryStartWorkflowRequest(namespaceID.String(), req, nil, nil, now) diff --git a/service/history/api/startworkflow/api.go b/service/history/api/startworkflow/api.go index 030c9e36ab..fde932928a 100644 --- a/service/history/api/startworkflow/api.go +++ b/service/history/api/startworkflow/api.go @@ -162,6 +162,11 @@ func (s *Starter) prepare(ctx context.Context) error { request.RequestEagerExecution = false } } + + if tsc := request.GetTimeSkippingConfig(); tsc != nil && tsc.GetEnabled() { + metrics.ExecutionTimeSkippingEnabledCount.With(s.getMetricsHandler()).Record(1) + } + return nil } @@ -699,6 +704,7 @@ func (s *Starter) handleUseExistingWorkflowOnConflictOptions( links, "", // identity nil, // priority + nil, // timeSkippingConfig ) return api.UpdateWorkflowWithoutWorkflowTask, err }, diff --git a/service/history/api/updateworkflowoptions/api.go b/service/history/api/updateworkflowoptions/api.go index 12a2f12dbd..c9a2a6e88e 100644 --- a/service/history/api/updateworkflowoptions/api.go +++ b/service/history/api/updateworkflowoptions/api.go @@ -10,6 +10,7 @@ import ( "go.temporal.io/server/api/historyservice/v1" "go.temporal.io/server/api/matchingservice/v1" "go.temporal.io/server/common/definition" + "go.temporal.io/server/common/metrics" "go.temporal.io/server/common/namespace" "go.temporal.io/server/common/util" "go.temporal.io/server/common/worker_versioning" @@ -84,6 +85,7 @@ func Invoke( return nil, err } + oldTimeSkippingConfig := getOptionsFromMutableState(mutableState).GetTimeSkippingConfig() mergedOpts, hasChanges, err := MergeAndApply(mutableState, requestedOptions, req.GetUpdateMask(), req.GetIdentity()) if err != nil { return nil, err @@ -91,6 +93,14 @@ func Invoke( // Set options for gRPC response ret.WorkflowExecutionOptions = mergedOpts + if hasChanges && !proto.Equal(mergedOpts.GetTimeSkippingConfig(), oldTimeSkippingConfig) { + if mergedOpts.GetTimeSkippingConfig().GetEnabled() { + metrics.ExecutionTimeSkippingEnabledCount.With(shardCtx.GetMetricsHandler()).Record(1) + } else { + metrics.ExecutionTimeSkippingDisabledCount.With(shardCtx.GetMetricsHandler()).Record(1) + } + } + // If there is no mutable state change at all, return with no new history event and Noop=true if !hasChanges { return &api.UpdateWorkflowAction{ @@ -151,7 +161,7 @@ func MergeAndApply( if mergedOpts.GetVersioningOverride() == nil { unsetOverride = true } - _, err = ms.AddWorkflowExecutionOptionsUpdatedEvent(mergedOpts.GetVersioningOverride(), unsetOverride, "", nil, nil, identity, mergedOpts.GetPriority()) + _, err = ms.AddWorkflowExecutionOptionsUpdatedEvent(mergedOpts.GetVersioningOverride(), unsetOverride, "", nil, nil, identity, mergedOpts.GetPriority(), mergedOpts.GetTimeSkippingConfig()) if err != nil { return nil, hasChanges, err } @@ -172,6 +182,11 @@ func getOptionsFromMutableState(ms historyi.MutableState) *workflowpb.WorkflowEx opts.Priority = cloned } } + if timeSkippingInfo := ms.GetExecutionInfo().GetTimeSkippingInfo(); timeSkippingInfo != nil { + opts.TimeSkippingConfig = &workflowpb.TimeSkippingConfig{ + Enabled: timeSkippingInfo.GetEnabled(), + } + } return opts } @@ -230,5 +245,14 @@ func mergeWorkflowExecutionOptions( mergeInto.Priority.FairnessWeight = mergeFrom.Priority.GetFairnessWeight() } + // ==== Time Skipping Config + // todo: only assuming enabled is the only field we use now + // nil means "no change" — only update if the caller provided an explicit value. + if _, ok := updateFields["timeSkippingConfig"]; ok { + if mergeFrom.GetTimeSkippingConfig() != nil { + mergeInto.TimeSkippingConfig = mergeFrom.GetTimeSkippingConfig() + } + } + return mergeInto, nil } diff --git a/service/history/api/updateworkflowoptions/api_test.go b/service/history/api/updateworkflowoptions/api_test.go index 8f41922fe1..7bb62189c6 100644 --- a/service/history/api/updateworkflowoptions/api_test.go +++ b/service/history/api/updateworkflowoptions/api_test.go @@ -21,6 +21,7 @@ import ( "go.temporal.io/server/common/cluster" "go.temporal.io/server/common/cluster/clustertest" "go.temporal.io/server/common/locks" + "go.temporal.io/server/common/metrics" "go.temporal.io/server/common/namespace" "go.temporal.io/server/service/history/api" historyi "go.temporal.io/server/service/history/interfaces" @@ -161,6 +162,34 @@ func TestMergeOptions_FooMask(t *testing.T) { assert.Error(t, err) } +func TestMergeOptions_TimeSkippingConfigMask(t *testing.T) { + mask := &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}} + + t.Run("enable time skipping", func(t *testing.T) { + current := &workflowpb.WorkflowExecutionOptions{} + update := &workflowpb.WorkflowExecutionOptions{TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}} + merged, err := mergeWorkflowExecutionOptions(current, update, mask) + require.NoError(t, err) + assert.True(t, merged.GetTimeSkippingConfig().GetEnabled()) + }) + + t.Run("disable time skipping", func(t *testing.T) { + current := &workflowpb.WorkflowExecutionOptions{TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}} + update := &workflowpb.WorkflowExecutionOptions{TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: false}} + merged, err := mergeWorkflowExecutionOptions(current, update, mask) + require.NoError(t, err) + assert.False(t, merged.GetTimeSkippingConfig().GetEnabled()) + }) + + t.Run("nil update leaves current unchanged", func(t *testing.T) { + current := &workflowpb.WorkflowExecutionOptions{} + update := &workflowpb.WorkflowExecutionOptions{TimeSkippingConfig: nil} + merged, err := mergeWorkflowExecutionOptions(current, update, mask) + require.NoError(t, err) + assert.Nil(t, merged.GetTimeSkippingConfig()) + }) +} + type ( // updateWorkflowOptionsSuite contains tests for the UpdateWorkflowOptions API. updateWorkflowOptionsSuite struct { @@ -231,6 +260,55 @@ func (s *updateWorkflowOptionsSuite) TearDownTest() { s.controller.Finish() } +func (s *updateWorkflowOptionsSuite) TestInvoke_TimeSkipping() { + expectedOptions := &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + } + s.currentMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true) + s.shardContext.EXPECT().GetMetricsHandler().Return(metrics.NoopMetricsHandler).AnyTimes() + // AddWorkflowExecutionOptionsUpdatedEvent is called with the time-skipping config; + // use gomock.Any() for unrelated args (versioning override is preserved from mutable state). + s.currentMutableState.EXPECT().AddWorkflowExecutionOptionsUpdatedEvent( + gomock.Any(), // versioning override — preserved from mutable state, not under test + gomock.Any(), // unsetOverride + gomock.Any(), // deployment + gomock.Any(), // deployment version + gomock.Any(), // deployment series + "test-identity", + gomock.Any(), // priority — not in mask + &workflowpb.TimeSkippingConfig{Enabled: true}, + ).Return(&historypb.HistoryEvent{}, nil) + s.currentContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), s.shardContext).Return(nil) + + updateReq := &historyservice.UpdateWorkflowExecutionOptionsRequest{ + NamespaceId: tests.NamespaceID.String(), + UpdateRequest: &workflowservice.UpdateWorkflowExecutionOptionsRequest{ + Namespace: tests.Namespace.String(), + WorkflowExecution: &commonpb.WorkflowExecution{ + WorkflowId: tests.WorkflowID, + RunId: tests.RunID, + }, + WorkflowExecutionOptions: expectedOptions, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + Identity: "test-identity", + }, + } + + resp, err := Invoke( + context.Background(), + updateReq, + s.shardContext, + s.workflowConsistencyChecker, + s.mockMatchingClient, + noopVersionMembershipCache{}, + noopReactivationSignalCache{}, + noopReactivationSignaler, + ) + s.NoError(err) + s.NotNil(resp) + s.True(resp.GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled()) +} + func (s *updateWorkflowOptionsSuite) TestInvoke_Success() { expectedOverrideOptions := &workflowpb.WorkflowExecutionOptions{ @@ -253,7 +331,7 @@ func (s *updateWorkflowOptionsSuite) TestInvoke_Success() { ).Return(&matchingservice.CheckTaskQueueVersionMembershipResponse{ IsMember: true, }, nil) - s.currentMutableState.EXPECT().AddWorkflowExecutionOptionsUpdatedEvent(expectedOverrideOptions.VersioningOverride, false, "", nil, nil, "", expectedOverrideOptions.Priority).Return(&historypb.HistoryEvent{}, nil) + s.currentMutableState.EXPECT().AddWorkflowExecutionOptionsUpdatedEvent(expectedOverrideOptions.VersioningOverride, false, "", nil, nil, "", expectedOverrideOptions.Priority, expectedOverrideOptions.TimeSkippingConfig).Return(&historypb.HistoryEvent{}, nil) s.currentContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), s.shardContext).Return(nil) updateReq := &historyservice.UpdateWorkflowExecutionOptionsRequest{ diff --git a/service/history/historybuilder/event_factory.go b/service/history/historybuilder/event_factory.go index f6831f2f55..07bff54f56 100644 --- a/service/history/historybuilder/event_factory.go +++ b/service/history/historybuilder/event_factory.go @@ -401,6 +401,7 @@ func (b *EventFactory) CreateWorkflowExecutionOptionsUpdatedEvent( links []*commonpb.Link, identity string, priority *commonpb.Priority, + timeSkippingConfig *workflowpb.TimeSkippingConfig, ) *historypb.HistoryEvent { event := b.createHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED, b.timeSource.Now()) event.Attributes = &historypb.HistoryEvent_WorkflowExecutionOptionsUpdatedEventAttributes{ @@ -411,6 +412,7 @@ func (b *EventFactory) CreateWorkflowExecutionOptionsUpdatedEvent( AttachedCompletionCallbacks: attachCompletionCallbacks, Identity: identity, Priority: priority, + TimeSkippingConfig: timeSkippingConfig, }, } event.Links = links @@ -1058,6 +1060,19 @@ func (b *EventFactory) CreateWorkflowExecutionUnpausedEvent( return event } +func (b *EventFactory) CreateWorkflowExecutionTimePointAdvancedEvent( + advanceToTimePoint time.Time, +) *historypb.HistoryEvent { + event := b.createHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPED, b.timeSource.Now()) + event.Attributes = &historypb.HistoryEvent_WorkflowExecutionTimeSkippedEventAttributes{ + WorkflowExecutionTimeSkippedEventAttributes: &historypb.WorkflowExecutionTimeSkippedEventAttributes{ + ToTime: timestamppb.New(advanceToTimePoint), + }, + } + event.WorkerMayIgnore = true + return event +} + func (b *EventFactory) createHistoryEvent( eventType enumspb.EventType, time time.Time, diff --git a/service/history/historybuilder/event_store.go b/service/history/historybuilder/event_store.go index c4918c4521..da99134fc5 100644 --- a/service/history/historybuilder/event_store.go +++ b/service/history/historybuilder/event_store.go @@ -271,7 +271,8 @@ func (b *EventStore) bufferEvent( enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT, enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED, enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW, - enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: + enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED, + enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPED: return false case // workflow task event should not be buffered diff --git a/service/history/historybuilder/history_builder.go b/service/history/historybuilder/history_builder.go index 993659e4c2..78cb757a9c 100644 --- a/service/history/historybuilder/history_builder.go +++ b/service/history/historybuilder/history_builder.go @@ -304,6 +304,17 @@ func (b *HistoryBuilder) AddWorkflowExecutionUnpausedEvent( return event } +func (b *HistoryBuilder) AddWorkflowExecutionTimeSkippedEvent( + advanceToTimePoint time.Time, +) *historypb.HistoryEvent { + event := b.CreateWorkflowExecutionTimePointAdvancedEvent(advanceToTimePoint) + // Mark the event as 'worker may ignore' so that older SDKs can safely ignore it. + event.WorkerMayIgnore = true + event, _ = b.add(event) + b.metricsHandler.Counter(metrics.ExecutionTimeSkippingDurationSkippedEventCount.Name()).Record(1) + return event +} + func (b *HistoryBuilder) AddActivityTaskScheduledEvent( workflowTaskCompletedEventID int64, command *commandpb.ScheduleActivityTaskCommandAttributes, @@ -458,6 +469,7 @@ func (b *HistoryBuilder) AddWorkflowExecutionOptionsUpdatedEvent( links []*commonpb.Link, identity string, priority *commonpb.Priority, + timeSkippingConfig *workflowpb.TimeSkippingConfig, ) *historypb.HistoryEvent { event := b.EventFactory.CreateWorkflowExecutionOptionsUpdatedEvent( worker_versioning.ConvertOverrideToV32(versioningOverride), @@ -467,6 +479,7 @@ func (b *HistoryBuilder) AddWorkflowExecutionOptionsUpdatedEvent( links, identity, priority, + timeSkippingConfig, ) event, _ = b.EventStore.add(event) return event diff --git a/service/history/historybuilder/history_builder_categorization_test.go b/service/history/historybuilder/history_builder_categorization_test.go index ee2386a2cc..66534ff653 100644 --- a/service/history/historybuilder/history_builder_categorization_test.go +++ b/service/history/historybuilder/history_builder_categorization_test.go @@ -208,7 +208,7 @@ func TestHistoryBuilder_FlushBufferToCurrentBatch(t *testing.T) { t.Errorf("expected 1 event in memBufferBatch got %d", len(hb.memBufferBatch)) } // add another event to memBufferBatch - hb.AddWorkflowExecutionOptionsUpdatedEvent(nil, false, "request-id-1", nil, nil, "", nil) + hb.AddWorkflowExecutionOptionsUpdatedEvent(nil, false, "request-id-1", nil, nil, "", nil, nil) if len(hb.memBufferBatch) != 2 { t.Errorf("expected 2 event in memBufferBatch got %d", len(hb.memBufferBatch)) } diff --git a/service/history/historybuilder/history_builder_test.go b/service/history/historybuilder/history_builder_test.go index cd73332543..ff1757578b 100644 --- a/service/history/historybuilder/history_builder_test.go +++ b/service/history/historybuilder/history_builder_test.go @@ -2227,6 +2227,7 @@ func (s *historyBuilderSuite) TestBufferEvent() { enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: true, enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: true, enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: true, + enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPED: true, } // workflow task events will be assign event ID immediately diff --git a/service/history/interfaces/mutable_state.go b/service/history/interfaces/mutable_state.go index 68aedfd2ac..3ccb7e8e6e 100644 --- a/service/history/interfaces/mutable_state.go +++ b/service/history/interfaces/mutable_state.go @@ -89,6 +89,10 @@ type ( AddTimeoutWorkflowEvent(int64, enumspb.RetryState, string) (*historypb.HistoryEvent, error) AddTimerCanceledEvent(int64, *commandpb.CancelTimerCommandAttributes, string) (*historypb.HistoryEvent, error) AddTimerFiredEvent(string) (*historypb.HistoryEvent, error) + AddWorkflowExecutionTimeSkippedEvent(ctx context.Context, advanceToTimePoint time.Time) (*historypb.HistoryEvent, error) + ApplyWorkflowExecutionTimeSkippedEvent(ctx context.Context, event *historypb.HistoryEvent) error + IsAutoTimeSkippable() bool + VirtualTimeNow() time.Time AddTimerStartedEvent(int64, *commandpb.StartTimerCommandAttributes) (*historypb.HistoryEvent, *persistencespb.TimerInfo, error) AddUpsertWorkflowSearchAttributesEvent(int64, *commandpb.UpsertWorkflowSearchAttributesCommandAttributes) (*historypb.HistoryEvent, error) AddWorkflowPropertiesModifiedEvent(int64, *commandpb.ModifyWorkflowPropertiesCommandAttributes) (*historypb.HistoryEvent, error) @@ -120,6 +124,7 @@ type ( links []*commonpb.Link, identity string, priority *commonpb.Priority, + timeSkippingConfig *workflowpb.TimeSkippingConfig, ) (*historypb.HistoryEvent, error) AddWorkflowExecutionUpdateAcceptedEvent(protocolInstanceID string, acceptedRequestMessageId string, acceptedRequestSequencingEventId int64, acceptedRequest *updatepb.Request) (*historypb.HistoryEvent, error) AddWorkflowExecutionUpdateCompletedEvent(acceptedEventID int64, updResp *updatepb.Response) (*historypb.HistoryEvent, error) diff --git a/service/history/interfaces/mutable_state_mock.go b/service/history/interfaces/mutable_state_mock.go index 103a53840e..489d7c44c6 100644 --- a/service/history/interfaces/mutable_state_mock.go +++ b/service/history/interfaces/mutable_state_mock.go @@ -663,18 +663,18 @@ 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) (*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 *workflow.TimeSkippingConfig) (*history.HistoryEvent, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddWorkflowExecutionOptionsUpdatedEvent", versioningOverride, unsetVersioningOverride, attachRequestID, attachCompletionCallbacks, links, identity, priority) + ret := m.ctrl.Call(m, "AddWorkflowExecutionOptionsUpdatedEvent", versioningOverride, unsetVersioningOverride, attachRequestID, attachCompletionCallbacks, links, identity, priority, timeSkippingConfig) ret0, _ := ret[0].(*history.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionOptionsUpdatedEvent indicates an expected call of AddWorkflowExecutionOptionsUpdatedEvent. -func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionOptionsUpdatedEvent(versioningOverride, unsetVersioningOverride, attachRequestID, attachCompletionCallbacks, links, identity, priority any) *gomock.Call { +func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionOptionsUpdatedEvent(versioningOverride, unsetVersioningOverride, attachRequestID, attachCompletionCallbacks, links, identity, priority, timeSkippingConfig any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionOptionsUpdatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionOptionsUpdatedEvent), versioningOverride, unsetVersioningOverride, attachRequestID, attachCompletionCallbacks, links, identity, priority) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionOptionsUpdatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionOptionsUpdatedEvent), versioningOverride, unsetVersioningOverride, attachRequestID, attachCompletionCallbacks, links, identity, priority, timeSkippingConfig) } // AddWorkflowExecutionPausedEvent mocks base method. @@ -767,6 +767,21 @@ func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionTerminatedEvent(firs return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionTerminatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionTerminatedEvent), firstEventID, reason, details, identity, deleteAfterTerminate, links) } +// AddWorkflowExecutionTimeSkippedEvent mocks base method. +func (m *MockMutableState) AddWorkflowExecutionTimeSkippedEvent(ctx context.Context, advanceToTimePoint time.Time) (*history.HistoryEvent, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddWorkflowExecutionTimeSkippedEvent", ctx, advanceToTimePoint) + ret0, _ := ret[0].(*history.HistoryEvent) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddWorkflowExecutionTimeSkippedEvent indicates an expected call of AddWorkflowExecutionTimeSkippedEvent. +func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionTimeSkippedEvent(ctx, advanceToTimePoint any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionTimeSkippedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionTimeSkippedEvent), ctx, advanceToTimePoint) +} + // AddWorkflowExecutionUnpausedEvent mocks base method. func (m *MockMutableState) AddWorkflowExecutionUnpausedEvent(identity, reason, requestID string) (*history.HistoryEvent, error) { m.ctrl.T.Helper() @@ -1498,6 +1513,20 @@ func (mr *MockMutableStateMockRecorder) ApplyWorkflowExecutionTerminatedEvent(ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyWorkflowExecutionTerminatedEvent", reflect.TypeOf((*MockMutableState)(nil).ApplyWorkflowExecutionTerminatedEvent), arg0, arg1) } +// ApplyWorkflowExecutionTimeSkippedEvent mocks base method. +func (m *MockMutableState) ApplyWorkflowExecutionTimeSkippedEvent(ctx context.Context, event *history.HistoryEvent) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyWorkflowExecutionTimeSkippedEvent", ctx, event) + ret0, _ := ret[0].(error) + return ret0 +} + +// ApplyWorkflowExecutionTimeSkippedEvent indicates an expected call of ApplyWorkflowExecutionTimeSkippedEvent. +func (mr *MockMutableStateMockRecorder) ApplyWorkflowExecutionTimeSkippedEvent(ctx, event any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyWorkflowExecutionTimeSkippedEvent", reflect.TypeOf((*MockMutableState)(nil).ApplyWorkflowExecutionTimeSkippedEvent), ctx, event) +} + // ApplyWorkflowExecutionTimedoutEvent mocks base method. func (m *MockMutableState) ApplyWorkflowExecutionTimedoutEvent(arg0 int64, arg1 *history.HistoryEvent) error { m.ctrl.T.Helper() @@ -3009,6 +3038,20 @@ func (mr *MockMutableStateMockRecorder) InitTransitionHistory() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitTransitionHistory", reflect.TypeOf((*MockMutableState)(nil).InitTransitionHistory)) } +// IsAutoTimeSkippable mocks base method. +func (m *MockMutableState) IsAutoTimeSkippable() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsAutoTimeSkippable") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsAutoTimeSkippable indicates an expected call of IsAutoTimeSkippable. +func (mr *MockMutableStateMockRecorder) IsAutoTimeSkippable() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsAutoTimeSkippable", reflect.TypeOf((*MockMutableState)(nil).IsAutoTimeSkippable)) +} + // IsCancelRequested mocks base method. func (m *MockMutableState) IsCancelRequested() bool { m.ctrl.T.Helper() @@ -3727,6 +3770,20 @@ func (mr *MockMutableStateMockRecorder) UpdateWorkflowStateStatus(state, status return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowStateStatus", reflect.TypeOf((*MockMutableState)(nil).UpdateWorkflowStateStatus), state, status) } +// VirtualTimeNow mocks base method. +func (m *MockMutableState) VirtualTimeNow() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VirtualTimeNow") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// VirtualTimeNow indicates an expected call of VirtualTimeNow. +func (mr *MockMutableStateMockRecorder) VirtualTimeNow() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VirtualTimeNow", reflect.TypeOf((*MockMutableState)(nil).VirtualTimeNow)) +} + // VisitUpdates mocks base method. func (m *MockMutableState) VisitUpdates(visitor func(string, *persistence.UpdateInfo)) { m.ctrl.T.Helper() diff --git a/service/history/ndc/events_reapplier_test.go b/service/history/ndc/events_reapplier_test.go index b32cf35ca5..146f908f40 100644 --- a/service/history/ndc/events_reapplier_test.go +++ b/service/history/ndc/events_reapplier_test.go @@ -113,6 +113,7 @@ func (s *nDCEventReapplicationSuite) TestReapplyEvents_AppliedEvent_WorkflowExec event.Links, attr.GetIdentity(), attr.GetPriority(), + attr.GetTimeSkippingConfig(), ).Return(event, nil) msCurrent.EXPECT().HSM().Return(s.hsmNode).AnyTimes() msCurrent.EXPECT().IsWorkflowPendingOnWorkflowTaskBackoff().Return(true) diff --git a/service/history/ndc/workflow_resetter.go b/service/history/ndc/workflow_resetter.go index 3005eb1d2c..9ba31f17cf 100644 --- a/service/history/ndc/workflow_resetter.go +++ b/service/history/ndc/workflow_resetter.go @@ -969,6 +969,7 @@ func reapplyEvents( event.Links, attr.GetIdentity(), attr.GetPriority(), + attr.GetTimeSkippingConfig(), ); err != nil { return reappliedEvents, err } diff --git a/service/history/ndc/workflow_resetter_test.go b/service/history/ndc/workflow_resetter_test.go index 486dbc7702..1d567e95ac 100644 --- a/service/history/ndc/workflow_resetter_test.go +++ b/service/history/ndc/workflow_resetter_test.go @@ -1203,6 +1203,7 @@ func (s *workflowResetterSuite) TestReapplyEvents() { event.Links, attr.GetIdentity(), attr.GetPriority(), + attr.GetTimeSkippingConfig(), ).Return(&historypb.HistoryEvent{}, nil) case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: attr := event.GetWorkflowExecutionSignaledEventAttributes() diff --git a/service/history/queues/metrics.go b/service/history/queues/metrics.go index c836dc5451..9eb2368532 100644 --- a/service/history/queues/metrics.go +++ b/service/history/queues/metrics.go @@ -113,6 +113,8 @@ func GetActiveTimerTaskTypeTagValue( return prefix + "." + getCHASMTaskTypeTagValue(t, chasmRegistry) case *tasks.ChasmTaskPure: return metrics.TaskTypeTimerActiveTaskChasmPureTask + case *tasks.TimeSkippingTimerTask: + return prefix + ".TimeSkipping" default: return prefix + task.GetType().String() } @@ -142,6 +144,8 @@ func GetStandbyTimerTaskTypeTagValue( return metrics.TaskTypeTimerStandbyTaskWorkflowBackoffTimer case *tasks.ChasmTask: return prefix + "." + getCHASMTaskTypeTagValue(t, chasmRegistry) + case *tasks.TimeSkippingTimerTask: + return prefix + ".TimeSkipping" case *tasks.ChasmTaskPure: return metrics.TaskTypeTimerStandbyTaskChasmPureTask default: diff --git a/service/history/tasks/time_skipping_timer.go b/service/history/tasks/time_skipping_timer.go new file mode 100644 index 0000000000..b4b205c5f7 --- /dev/null +++ b/service/history/tasks/time_skipping_timer.go @@ -0,0 +1,55 @@ +package tasks + +import ( + "fmt" + "time" + + enumsspb "go.temporal.io/server/api/enums/v1" + "go.temporal.io/server/common/definition" +) + +var _ Task = (*TimeSkippingTimerTask)(nil) + +type ( + TimeSkippingTimerTask struct { + definition.WorkflowKey + VisibilityTimestamp time.Time + TaskID int64 + } +) + +func (t *TimeSkippingTimerTask) GetKey() Key { + return NewKey(t.VisibilityTimestamp, t.TaskID) +} + +func (t *TimeSkippingTimerTask) GetTaskID() int64 { + return t.TaskID +} + +func (t *TimeSkippingTimerTask) SetTaskID(id int64) { + t.TaskID = id +} + +func (t *TimeSkippingTimerTask) GetVisibilityTime() time.Time { + return t.VisibilityTimestamp +} + +func (t *TimeSkippingTimerTask) SetVisibilityTime(ts time.Time) { + t.VisibilityTimestamp = ts +} + +func (t *TimeSkippingTimerTask) GetCategory() Category { + return CategoryTimer +} + +func (t *TimeSkippingTimerTask) GetType() enumsspb.TaskType { + return enumsspb.TASK_TYPE_TIME_SKIPPING +} + +func (t *TimeSkippingTimerTask) String() string { + return fmt.Sprintf("TimeSkippingTimerTask{WorkflowKey: %s, VisibilityTimestamp: %v, TaskID: %v}", + t.WorkflowKey.String(), + t.VisibilityTimestamp, + t.TaskID, + ) +} diff --git a/service/history/tasks/utils.go b/service/history/tasks/utils.go index 1d6a385c08..3010e0f14d 100644 --- a/service/history/tasks/utils.go +++ b/service/history/tasks/utils.go @@ -112,6 +112,8 @@ func GetTimerTaskEventID( return getChasmTaskEventID() case *StateMachineTimerTask: eventID = common.FirstEventID + case *TimeSkippingTimerTask: + eventID = common.FirstEventID case *ChasmTaskPure: return getChasmTaskEventID() case *ChasmTask: diff --git a/service/history/timer_queue_active_task_executor.go b/service/history/timer_queue_active_task_executor.go index 9737bf6cfc..7ace583bda 100644 --- a/service/history/timer_queue_active_task_executor.go +++ b/service/history/timer_queue_active_task_executor.go @@ -3,6 +3,7 @@ package history import ( "context" "fmt" + "time" "github.com/google/uuid" commonpb "go.temporal.io/api/common/v1" @@ -112,6 +113,8 @@ func (t *timerQueueActiveTaskExecutor) Execute( err = t.executeWorkflowExecutionTimeoutTask(ctx, task) case *tasks.ActivityRetryTimerTask: err = t.executeActivityRetryTimerTask(ctx, task) + case *tasks.TimeSkippingTimerTask: + err = t.executeTimeSkippingTimerTask(ctx, task) case *tasks.WorkflowBackoffTimerTask: err = t.executeWorkflowBackoffTimerTask(ctx, task) case *tasks.DeleteHistoryEventTask: @@ -133,6 +136,75 @@ func (t *timerQueueActiveTaskExecutor) Execute( } } +func (t *timerQueueActiveTaskExecutor) executeTimeSkippingTimerTask( + ctx context.Context, + task *tasks.TimeSkippingTimerTask, +) (retError error) { + ctx, cancel := context.WithTimeout(ctx, taskTimeout) + defer cancel() + noSkippingReason := "" + defer func() { + if noSkippingReason != "" { + reason := fmt.Sprintf("no skipping reason: %s", noSkippingReason) + t.logger.Debug(reason, + tag.WorkflowID(task.WorkflowID), + tag.WorkflowRunID(task.RunID), + tag.WorkflowScheduledEventID(task.TaskID), + ) + } + }() + + weContext, release, err := getWorkflowExecutionContextForTask(ctx, t.shardContext, t.cache, task) + if err != nil { + return err + } + defer func() { release(retError) }() + + mutableState, err := loadMutableStateForTimerTask(ctx, t.shardContext, weContext, task, t.metricsHandler, t.logger) + if err != nil { + return err + } + if mutableState == nil { + release(nil) + return nil + } + timeSkippingInfo := mutableState.GetExecutionInfo().GetTimeSkippingInfo() + if timeSkippingInfo == nil || !timeSkippingInfo.GetEnabled() { + t.logger.Warn("time skipping is not enabled when running this task, this should be a rare case") + release(nil) + return nil + } + + // GUARD: check if time skipping is allowed by current state + if !mutableState.IsAutoTimeSkippable() { + release(nil) + return nil + } + + // time-skipping logic: + // Find the next pending user timer. + timerSequence := t.getTimerSequence(mutableState) + userTimers := timerSequence.LoadAndSortUserTimers() + if len(userTimers) == 0 { + release(nil) + noSkippingReason = "no pending user timers" + return nil + } + + advanceToTimePoint := userTimers[0].Timestamp // todo: @feiyang need to reorder? + if !advanceToTimePoint.After(mutableState.VirtualTimeNow().Add(1 * time.Second)) { + noSkippingReason = "next timer is close to now, skipping is not necessary" + release(nil) + return nil + } + + // Persist the time skipped event, updated mutable state, and refreshed tasks atomically. + if _, err := mutableState.AddWorkflowExecutionTimeSkippedEvent(ctx, advanceToTimePoint); err != nil { + return err + } + return t.updateWorkflowExecution(ctx, weContext, mutableState, false) +} + func (t *timerQueueActiveTaskExecutor) executeUserTimerTimeoutTask( ctx context.Context, task *tasks.UserTimerTask, @@ -156,7 +228,8 @@ func (t *timerQueueActiveTaskExecutor) executeUserTimerTimeoutTask( } timerSequence := t.getTimerSequence(mutableState) - referenceTime := t.Now() + // Use virtual time as reference so time-skipped timers are recognized as expired. + referenceTime := mutableState.VirtualTimeNow() timerFired := false Loop: for _, timerSequenceID := range timerSequence.LoadAndSortUserTimers() { diff --git a/service/history/workflow/mutable_state_impl.go b/service/history/workflow/mutable_state_impl.go index 7d271229bf..f96556de4b 100644 --- a/service/history/workflow/mutable_state_impl.go +++ b/service/history/workflow/mutable_state_impl.go @@ -61,6 +61,7 @@ import ( "go.temporal.io/server/common/util" "go.temporal.io/server/common/worker_versioning" "go.temporal.io/server/components/callbacks" + "go.temporal.io/server/components/nexusoperations" "go.temporal.io/server/service/history/configs" "go.temporal.io/server/service/history/consts" "go.temporal.io/server/service/history/events" @@ -501,6 +502,13 @@ func NewMutableStateFromDB( mutableState.approximateSize += dbRecord.ExecutionInfo.Size() - mutableState.executionInfo.Size() mutableState.executionInfo = dbRecord.ExecutionInfo + if mutableState.executionInfo.GetTimeSkippingInfo() != nil { + mutableState.timeSource = clock.NewTimeSkippingTimeSource( + mutableState.timeSource, + mutableState.executionInfo.TimeSkippingInfo.TimeSkippedDetails, + ) + } + // StartTime was moved from ExecutionInfo to executionState if mutableState.executionState.StartTime == nil && dbRecord.ExecutionInfo.StartTime != nil { mutableState.executionState.StartTime = dbRecord.ExecutionInfo.StartTime @@ -2751,6 +2759,13 @@ func (ms *MutableStateImpl) AddWorkflowExecutionStartedEventWithOptions( return nil, err } + if tsc := startRequest.GetStartRequest().GetTimeSkippingConfig(); tsc.GetEnabled() { + ms.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ + Enabled: true, + } + ms.timeSource = clock.NewTimeSkippingTimeSource(ms.timeSource, nil) + } + // Versioning Override set on StartWorkflowExecutionRequest if startRequest.GetStartRequest().GetVersioningOverride() != nil { metrics.WorkerDeploymentVersioningOverrideCounter.With( @@ -3919,6 +3934,89 @@ func (ms *MutableStateImpl) ApplyWorkflowTaskFailedEvent() error { return ms.workflowTaskManager.ApplyWorkflowTaskFailedEvent() } +// AddWorkflowExecutionTimeSkippedEvent adds a WorkflowExecutionTimeSkippedEvent to the history +// and refreshes tasks to reflect the advanced time point. +func (ms *MutableStateImpl) AddWorkflowExecutionTimeSkippedEvent( + ctx context.Context, + advanceToTimePoint time.Time, +) (*historypb.HistoryEvent, error) { + opTag := tag.WorkflowActionWorkflowTimeSkipped + if err := ms.checkMutability(opTag); err != nil { + return nil, err + } + + event := ms.hBuilder.AddWorkflowExecutionTimeSkippedEvent(advanceToTimePoint) + if err := ms.ApplyWorkflowExecutionTimeSkippedEvent(ctx, event); err != nil { + return nil, err + } + return event, nil +} + +func (ms *MutableStateImpl) ApplyWorkflowExecutionTimeSkippedEvent(ctx context.Context, event *historypb.HistoryEvent) error { + executionInfo := ms.GetExecutionInfo() + newDetails := buildTimeSkippedDetails(event) + executionInfo.TimeSkippingInfo.TimeSkippedDetails = append( + executionInfo.TimeSkippingInfo.TimeSkippedDetails, + newDetails, + ) + if ts, ok := ms.timeSource.(*clock.TimeSkippingTimeSource); ok { + ts.Advance(clock.TimeSkippedDurationFromTimestamp(newDetails.GetDurationToSkip())) + } + return NewTaskRefresher(ms.shard).Refresh(ctx, ms, false) +} + +func buildTimeSkippedDetails( + event *historypb.HistoryEvent, +) *persistencespb.TimeSkippedDetails { + attrs := event.GetWorkflowExecutionTimeSkippedEventAttributes() + skipDuration := attrs.GetToTime().AsTime().Sub(event.GetEventTime().AsTime()) + return &persistencespb.TimeSkippedDetails{ + SkippingTime: event.GetEventTime(), + DurationToSkip: clock.TimeSkippedDurationToTimestamp(skipDuration), + ToTime: attrs.GetToTime(), + } +} + +// VirtualTimeNow returns the current effective time for this workflow. +// For workflows with time skipping enabled, this returns the virtual (advanced) time. +// For regular workflows, this returns the real shard time. +func (ms *MutableStateImpl) VirtualTimeNow() time.Time { + return ms.timeSource.Now() +} + +func (ms *MutableStateImpl) IsAutoTimeSkippable() bool { + noSkippingReason := "" + defer func() { + if noSkippingReason != "" { + ms.logInfo(fmt.Sprintf("no auto time skipping allowed: %s", noSkippingReason), + tag.WorkflowID(ms.GetExecutionInfo().WorkflowId), + tag.WorkflowRunID(ms.GetExecutionState().RunId), + ) + } + }() + if !ms.IsWorkflowExecutionRunning() { + noSkippingReason = "workflow is not running" + return false + } + if ms.HasPendingWorkflowTask() { + noSkippingReason = "pending workflow task" + return false + } + if len(ms.GetPendingActivityInfos()) > 0 { + noSkippingReason = "pending activity" + return false + } + if len(ms.GetPendingChildExecutionInfos()) > 0 { + noSkippingReason = "pending child execution" + return false + } + if nexusoperations.MachineCollection(ms.HSM()).Size() > 0 { + noSkippingReason = "pending Nexus operations" + return false + } + return true +} + func (ms *MutableStateImpl) AddActivityTaskScheduledEvent( workflowTaskCompletedEventID int64, command *commandpb.ScheduleActivityTaskCommandAttributes, @@ -3947,7 +4045,6 @@ func (ms *MutableStateImpl) AddActivityTaskScheduledEvent( return nil, nil, err } } - return event, ai, err } @@ -5389,6 +5486,7 @@ func (ms *MutableStateImpl) AddWorkflowExecutionOptionsUpdatedEvent( links []*commonpb.Link, identity string, priority *commonpb.Priority, + timeSkippingConfig *workflowpb.TimeSkippingConfig, ) (*historypb.HistoryEvent, error) { if err := ms.checkMutability(tag.WorkflowActionWorkflowOptionsUpdated); err != nil { return nil, err @@ -5401,6 +5499,7 @@ func (ms *MutableStateImpl) AddWorkflowExecutionOptionsUpdatedEvent( links, identity, priority, + timeSkippingConfig, ) prevEffectiveVersioningBehavior := ms.GetEffectiveVersioningBehavior() prevEffectiveDeployment := ms.GetEffectiveDeployment() @@ -5461,6 +5560,23 @@ func (ms *MutableStateImpl) ApplyWorkflowExecutionOptionsUpdatedEvent(event *his ms.executionInfo.Priority = attributes.GetPriority() } + // Update time skipping config. + if tsc := attributes.GetTimeSkippingConfig(); tsc != nil { + if ms.executionInfo.TimeSkippingInfo == nil { + ms.executionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{} + } + ms.executionInfo.TimeSkippingInfo.Enabled = tsc.GetEnabled() + + if tsc.GetEnabled() { + if _, alreadySkipping := ms.timeSource.(*clock.TimeSkippingTimeSource); !alreadySkipping { + ms.timeSource = clock.NewTimeSkippingTimeSource( + ms.timeSource, + ms.executionInfo.TimeSkippingInfo.GetTimeSkippedDetails(), + ) + } + } + } + // Finally, reschedule the pending workflow task if so requested. if requestReschedulePendingWorkflowTask { return ms.reschedulePendingWorkflowTask() @@ -6948,6 +7064,8 @@ func (ms *MutableStateImpl) CloseTransactionAsMutation( return workflowMutation, result.workflowEventsSeq, nil } +// todo @feiyang this is a place for triggering time skipping changes so that it is inside the transaction + func (ms *MutableStateImpl) CloseTransactionAsSnapshot( ctx context.Context, transactionPolicy historyi.TransactionPolicy, @@ -7136,6 +7254,16 @@ func (ms *MutableStateImpl) closeTransaction( ms.executionInfo.StateTransitionCount += 1 ms.executionInfo.LastUpdateTime = timestamppb.New(ms.shard.GetTimeSource().Now()) + // whenever time skipping is enabled && mutable state changes, + // we need to schedule a time skipping timer task to check possible time skipping opportunities + timeSkippingInfo := ms.executionInfo.GetTimeSkippingInfo() + if timeSkippingInfo != nil && timeSkippingInfo.Enabled { + ms.InsertTasks[tasks.CategoryTimer] = append(ms.InsertTasks[tasks.CategoryTimer], &tasks.TimeSkippingTimerTask{ + WorkflowKey: ms.GetWorkflowKey(), + VisibilityTimestamp: ms.shard.GetTimeSource().Now(), + }) + } + // We generate checksum here based on the assumption that the returned // snapshot object is considered immutable. As of this writing, the only // code that modifies the returned object lives inside Context.resetWorkflowExecution. diff --git a/service/history/workflow/mutable_state_impl_test.go b/service/history/workflow/mutable_state_impl_test.go index 2fdaace570..4bd02c4f9e 100644 --- a/service/history/workflow/mutable_state_impl_test.go +++ b/service/history/workflow/mutable_state_impl_test.go @@ -33,6 +33,7 @@ import ( taskqueuespb "go.temporal.io/server/api/taskqueue/v1" "go.temporal.io/server/chasm" "go.temporal.io/server/common" + "go.temporal.io/server/common/clock" "go.temporal.io/server/common/cluster" "go.temporal.io/server/common/contextutil" "go.temporal.io/server/common/definition" @@ -1140,7 +1141,7 @@ func (s *mutableStateSuite) TestOverride_UnpinnedBase_SetPinnedAndUnsetWithEmpty s.createMutableStateWithVersioningBehavior(baseBehavior, deployment1, tq) // set pinned override - event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(pinnedOptions2.GetVersioningOverride(), false, "", nil, nil, id, nil) + event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(pinnedOptions2.GetVersioningOverride(), false, "", nil, nil, id, nil, nil) s.NoError(err) s.verifyEffectiveDeployment(deployment2, overrideBehavior) s.verifyWorkflowOptionsUpdatedEventAttr( @@ -1155,7 +1156,7 @@ func (s *mutableStateSuite) TestOverride_UnpinnedBase_SetPinnedAndUnsetWithEmpty // unset pinned override with boolean id = uuid.NewString() - event, err = s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(nil, true, "", nil, nil, id, nil) + event, err = s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(nil, true, "", nil, nil, id, nil, nil) s.NoError(err) s.verifyEffectiveDeployment(deployment1, baseBehavior) s.verifyWorkflowOptionsUpdatedEventAttr( @@ -1177,7 +1178,7 @@ func (s *mutableStateSuite) TestOverride_PinnedBase_SetUnpinnedAndUnsetWithEmpty s.createMutableStateWithVersioningBehavior(baseBehavior, deployment1, tq) // set unpinned override - event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(unpinnedOptions.GetVersioningOverride(), false, "", nil, nil, id, nil) + event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(unpinnedOptions.GetVersioningOverride(), false, "", nil, nil, id, nil, nil) s.NoError(err) s.verifyEffectiveDeployment(deployment1, overrideBehavior) s.verifyWorkflowOptionsUpdatedEventAttr( @@ -1192,7 +1193,7 @@ func (s *mutableStateSuite) TestOverride_PinnedBase_SetUnpinnedAndUnsetWithEmpty // unset pinned override with empty id = uuid.NewString() - event, err = s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(nil, true, "", nil, nil, id, nil) + event, err = s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(nil, true, "", nil, nil, id, nil, nil) s.NoError(err) s.verifyEffectiveDeployment(deployment1, baseBehavior) s.verifyWorkflowOptionsUpdatedEventAttr( @@ -1213,7 +1214,7 @@ func (s *mutableStateSuite) TestOverride_RedirectFails() { id := uuid.NewString() s.createMutableStateWithVersioningBehavior(baseBehavior, deployment1, tq) - event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(pinnedOptions3.GetVersioningOverride(), false, "", nil, nil, id, nil) + event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(pinnedOptions3.GetVersioningOverride(), false, "", nil, nil, id, nil, nil) s.NoError(err) s.verifyEffectiveDeployment(deployment3, overrideBehavior) s.verifyWorkflowOptionsUpdatedEventAttr( @@ -1240,7 +1241,7 @@ func (s *mutableStateSuite) TestOverride_BaseDeploymentUpdatedOnCompletion() { id := uuid.NewString() s.createMutableStateWithVersioningBehavior(baseBehavior, deployment1, tq) - event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(pinnedOptions3.GetVersioningOverride(), false, "", nil, nil, id, nil) + event, err := s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(pinnedOptions3.GetVersioningOverride(), false, "", nil, nil, id, nil, nil) s.NoError(err) s.verifyEffectiveDeployment(deployment3, overrideBehavior) s.verifyWorkflowOptionsUpdatedEventAttr( @@ -1293,7 +1294,7 @@ func (s *mutableStateSuite) TestOverride_BaseDeploymentUpdatedOnCompletion() { // now we unset the override and check that the base deployment/behavior is in effect id = uuid.NewString() - event, err = s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(nil, true, "", nil, nil, id, nil) + event, err = s.mutableState.AddWorkflowExecutionOptionsUpdatedEvent(nil, true, "", nil, nil, id, nil, nil) s.NoError(err) s.verifyEffectiveDeployment(deployment2, baseBehavior) s.verifyWorkflowOptionsUpdatedEventAttr( @@ -6149,3 +6150,118 @@ func (s *mutableStateSuite) TestSetContextMetadata() { s.True(ok) s.Equal(taskQueue, tq) } + +// --------------------------------------------------------------------------- +// Time-skipping unit tests +// --------------------------------------------------------------------------- + +func (s *mutableStateSuite) TestBuildTimeSkippedDetails_FirstSkip() { + realTime := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC) + skipDuration := 2 * time.Hour + targetVirtualTime := realTime.Add(skipDuration) + + event := &historypb.HistoryEvent{ + EventTime: timestamppb.New(realTime), + Attributes: &historypb.HistoryEvent_WorkflowExecutionTimeSkippedEventAttributes{ + WorkflowExecutionTimeSkippedEventAttributes: &historypb.WorkflowExecutionTimeSkippedEventAttributes{ + ToTime: timestamppb.New(targetVirtualTime), + }, + }, + } + + details := buildTimeSkippedDetails(event) + + // Skipping time is the event time. + s.Equal(realTime, details.GetSkippingTime().AsTime()) + // Duration encodes correctly. + s.Equal(skipDuration, clock.TimeSkippedDurationFromTimestamp(details.GetDurationToSkip())) + // Target = realTime + 2h. + s.Equal(targetVirtualTime, details.GetToTime().AsTime()) +} + +func (s *mutableStateSuite) TestBuildTimeSkippedDetails_SecondSkip() { + // Virtual time at start of second skip = 14:00 (the previous skip's target). + eventTime := time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC) + skipDuration := 3 * time.Hour + targetTime := eventTime.Add(skipDuration) // 17:00 + + event := &historypb.HistoryEvent{ + EventTime: timestamppb.New(eventTime), + Attributes: &historypb.HistoryEvent_WorkflowExecutionTimeSkippedEventAttributes{ + WorkflowExecutionTimeSkippedEventAttributes: &historypb.WorkflowExecutionTimeSkippedEventAttributes{ + ToTime: timestamppb.New(targetTime), + }, + }, + } + details := buildTimeSkippedDetails(event) + + s.Equal(eventTime, details.GetSkippingTime().AsTime()) + s.Equal(skipDuration, clock.TimeSkippedDurationFromTimestamp(details.GetDurationToSkip())) + s.Equal(targetTime, details.GetToTime().AsTime()) +} + +func (s *mutableStateSuite) TestBuildTimeSkippedDetails_ThreeSkips_VirtualTimeAccumulates() { + base := time.Date(2024, 1, 1, 10, 0, 0, 0, time.UTC) + + // event.EventTime is the virtual clock reading at the start of each skip (= previous target). + // skipDuration = toTime - eventTime, so duration is preserved exactly. + skipDurations := []time.Duration{1 * time.Hour, 2 * time.Hour, 3 * time.Hour} + + prevTarget := base + for i, dur := range skipDurations { + target := prevTarget.Add(dur) + eventTime := prevTarget // virtual time at start of skip = previous skip's target + event := &historypb.HistoryEvent{ + EventTime: timestamppb.New(eventTime), + Attributes: &historypb.HistoryEvent_WorkflowExecutionTimeSkippedEventAttributes{ + WorkflowExecutionTimeSkippedEventAttributes: &historypb.WorkflowExecutionTimeSkippedEventAttributes{ + ToTime: timestamppb.New(target), + }, + }, + } + d := buildTimeSkippedDetails(event) + + s.Equal(eventTime, d.GetSkippingTime().AsTime(), "skip %d: skipping time", i) + s.Equal(dur, clock.TimeSkippedDurationFromTimestamp(d.GetDurationToSkip()), "skip %d: duration", i) + s.Equal(target, d.GetToTime().AsTime(), "skip %d: target time", i) + + prevTarget = target + } +} + +func (s *mutableStateSuite) TestTimeSkippedDurationRoundtrip() { + durations := []time.Duration{ + 0, + time.Second, + time.Minute, + time.Hour, + 2*time.Hour + 30*time.Minute + 15*time.Second, + 24 * time.Hour, + } + for _, d := range durations { + s.Equal(d, clock.TimeSkippedDurationFromTimestamp(clock.TimeSkippedDurationToTimestamp(d)), "duration: %v", d) + } +} + +func (s *mutableStateSuite) TestNewMutableStateFromDB_TimeSkippingDisabled_VirtualTimeUsed() { + // Verify that when TimeSkippingInfo exists but Enabled=false, the mutable state still uses + // a TimeSkippingTimeSource so that virtual time (real time + skipped offset) is returned. + skipDuration := 2 * time.Hour + dbState := s.buildWorkflowMutableState() + dbState.ExecutionInfo.TimeSkippingInfo = &persistencespb.TimeSkippingInfo{ + Enabled: false, + TimeSkippedDetails: []*persistencespb.TimeSkippedDetails{ + { + DurationToSkip: clock.TimeSkippedDurationToTimestamp(skipDuration), + }, + }, + } + + ms, err := NewMutableStateFromDB(s.mockShard, s.mockEventsCache, s.logger, s.namespaceEntry, dbState, 123) + s.NoError(err) + + // VirtualTimeNow should be ahead of real time by the accumulated skipped duration. + realNow := s.mockShard.GetTimeSource().Now() + virtualNow := ms.VirtualTimeNow() + s.WithinDuration(realNow.Add(skipDuration), virtualNow, time.Second) +} diff --git a/service/history/workflow/mutable_state_rebuilder.go b/service/history/workflow/mutable_state_rebuilder.go index 2724af3099..a1ba8d67fc 100644 --- a/service/history/workflow/mutable_state_rebuilder.go +++ b/service/history/workflow/mutable_state_rebuilder.go @@ -672,6 +672,10 @@ func (b *MutableStateRebuilderImpl) applyEvents( if err := b.mutableState.ApplyWorkflowExecutionUnpausedEvent(event); err != nil { return nil, err } + case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPED: + if err := b.mutableState.ApplyWorkflowExecutionTimeSkippedEvent(ctx, event); err != nil { + return nil, err + } default: def, ok := b.shard.StateMachineRegistry().EventDefinition(event.GetEventType()) diff --git a/service/history/workflow/mutable_state_rebuilder_test.go b/service/history/workflow/mutable_state_rebuilder_test.go index 966b65a5eb..f64b793044 100644 --- a/service/history/workflow/mutable_state_rebuilder_test.go +++ b/service/history/workflow/mutable_state_rebuilder_test.go @@ -2159,6 +2159,37 @@ func (s *stateBuilderSuite) TestApplyEvents_HSMRegistry() { s.Equal(enumsspb.NEXUS_OPERATION_STATE_SCHEDULED, sm.State()) } +func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionTimeSkipped() { + version := int64(1) + requestID := uuid.NewString() + execution := &commonpb.WorkflowExecution{ + WorkflowId: "some random workflow ID", + RunId: tests.RunID, + } + + now := time.Now().UTC() + event := &historypb.HistoryEvent{ + TaskId: rand.Int63(), + Version: version, + EventId: 130, + EventTime: timestamppb.New(now), + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPED, + Attributes: &historypb.HistoryEvent_WorkflowExecutionTimeSkippedEventAttributes{ + WorkflowExecutionTimeSkippedEventAttributes: &historypb.WorkflowExecutionTimeSkippedEventAttributes{ + ToTime: timestamppb.New(now.Add(time.Hour)), + }, + }, + } + + s.mockMutableState.EXPECT().ApplyWorkflowExecutionTimeSkippedEvent(gomock.Any(), protomock.Eq(event)).Return(nil) + s.mockUpdateVersion(event) + s.mockMutableState.EXPECT().ClearStickyTaskQueue() + + _, err := s.stateRebuilder.ApplyEvents(context.Background(), tests.NamespaceID, requestID, execution, s.toHistory(event), nil, "") + s.NoError(err) + s.Equal(event.TaskId, s.executionInfo.LastRunningClock) +} + func (p *testTaskGeneratorProvider) NewTaskGenerator( shardContext historyi.ShardContext, mutableState historyi.MutableState, diff --git a/service/history/workflow/task_refresher.go b/service/history/workflow/task_refresher.go index b9315cfc00..a3aa18c934 100644 --- a/service/history/workflow/task_refresher.go +++ b/service/history/workflow/task_refresher.go @@ -10,10 +10,12 @@ import ( enumsspb "go.temporal.io/server/api/enums/v1" persistencespb "go.temporal.io/server/api/persistence/v1" "go.temporal.io/server/common" + "go.temporal.io/server/common/clock" "go.temporal.io/server/common/persistence/transitionhistory" "go.temporal.io/server/common/primitives/timestamp" "go.temporal.io/server/service/history/hsm" historyi "go.temporal.io/server/service/history/interfaces" + "go.temporal.io/server/service/history/tasks" ) type ( @@ -75,6 +77,8 @@ func (r *TaskRefresherImpl) Refresh( return err } + r.applyTimeSkippingOffsetToTimerTasks(mutableState) + if err := mutableState.ChasmTree().RefreshTasks(); err != nil { return err } @@ -688,3 +692,47 @@ func (r *TaskRefresherImpl) refreshTasksForSubStateMachines( return nil } + +// applyTimeSkippingOffsetToTimerTasks shifts the VisibilityTimestamp of all pending timer tasks +// that were just generated by subtracting the total skipped duration. This ensures the timer queue +// fires them at the correct real-clock time relative to the advanced virtual clock. +// +// skippedDuration = sum of all DurationToSkip in TimeSkippedDetails +// adjustedFireTime = max(now, virtualFireTime - skippedDuration) +// +// DeleteHistoryEventTask and TimeSkippingTimerTask are excluded: the former is a retention-based +// cleanup task unrelated to virtual time; the latter is the time-skipping mechanism itself. +func (r *TaskRefresherImpl) applyTimeSkippingOffsetToTimerTasks(mutableState historyi.MutableState) { + timeSkippingInfo := mutableState.GetExecutionInfo().GetTimeSkippingInfo() + if timeSkippingInfo == nil { + return + } + details := timeSkippingInfo.GetTimeSkippedDetails() + if len(details) == 0 { + return + } + + skippedDuration := clock.ComputeTotalSkippedOffset(details) + if skippedDuration <= 0 { + return + } + + ms, ok := mutableState.(*MutableStateImpl) + if !ok { + return + } + now := r.shard.GetTimeSource().Now() + for _, task := range ms.InsertTasks[tasks.CategoryTimer] { + switch task.GetType() { + case enumsspb.TASK_TYPE_DELETE_HISTORY_EVENT, enumsspb.TASK_TYPE_TIME_SKIPPING: + // not virtual-time based, skip + continue + default: + } + adjusted := task.GetVisibilityTime().Add(-skippedDuration) + if adjusted.Before(now) { + adjusted = now + } + task.SetVisibilityTime(adjusted) + } +} diff --git a/service/history/workflow/task_refresher_test.go b/service/history/workflow/task_refresher_test.go index 11bdc928c7..b74f8910cc 100644 --- a/service/history/workflow/task_refresher_test.go +++ b/service/history/workflow/task_refresher_test.go @@ -14,6 +14,7 @@ import ( historyspb "go.temporal.io/server/api/history/v1" persistencespb "go.temporal.io/server/api/persistence/v1" "go.temporal.io/server/common" + "go.temporal.io/server/common/clock" "go.temporal.io/server/common/cluster" "go.temporal.io/server/common/log" "go.temporal.io/server/common/namespace" @@ -1434,6 +1435,151 @@ func (s *taskRefresherSuite) TestRefreshSubStateMachineTasks() { s.False(hsmRoot.Dirty()) } +// buildMutableStateWithTimeSkipping creates a MutableStateImpl with TimeSkippingInfo having +// the given total skipped duration in a single entry. +func (s *taskRefresherSuite) buildMutableStateWithTimeSkipping(totalSkip time.Duration, enabled bool) *MutableStateImpl { + dbRecord := &persistencespb.WorkflowMutableState{ + ExecutionInfo: &persistencespb.WorkflowExecutionInfo{ + NamespaceId: tests.NamespaceID.String(), + WorkflowId: tests.WorkflowID, + TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ + Enabled: enabled, + TimeSkippedDetails: []*persistencespb.TimeSkippedDetails{ + {DurationToSkip: clock.TimeSkippedDurationToTimestamp(totalSkip)}, + }, + }, + }, + ExecutionState: &persistencespb.WorkflowExecutionState{ + RunId: tests.RunID, + State: enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING, + Status: enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING, + }, + NextEventId: 1, + } + ms, err := NewMutableStateFromDB( + s.mockShard, + s.mockShard.GetEventsCache(), + log.NewTestLogger(), + tests.LocalNamespaceEntry, + dbRecord, + 1, + ) + s.NoError(err) + return ms +} + +func (s *taskRefresherSuite) TestApplyTimeSkippingOffset_AdjustsAllTimerTaskTypes() { + skipDuration := 2 * time.Hour + ms := s.buildMutableStateWithTimeSkipping(skipDuration, true) + + now := s.mockShard.GetTimeSource().Now() + fireTime := now.Add(3 * time.Hour) + wfKey := ms.GetWorkflowKey() + + ms.InsertTasks[tasks.CategoryTimer] = []tasks.Task{ + &tasks.UserTimerTask{WorkflowKey: wfKey, VisibilityTimestamp: fireTime}, + &tasks.ActivityTimeoutTask{WorkflowKey: wfKey, VisibilityTimestamp: fireTime}, + &tasks.WorkflowRunTimeoutTask{WorkflowKey: wfKey, VisibilityTimestamp: fireTime}, + &tasks.WorkflowExecutionTimeoutTask{NamespaceID: wfKey.NamespaceID, WorkflowID: wfKey.WorkflowID, VisibilityTimestamp: fireTime}, + } + + s.taskRefresher.applyTimeSkippingOffsetToTimerTasks(ms) + + expected := fireTime.Add(-skipDuration) // now + 1h + for _, task := range ms.InsertTasks[tasks.CategoryTimer] { + s.Equal(expected, task.GetVisibilityTime(), "task type %v should be adjusted", task.GetType()) + } +} + +func (s *taskRefresherSuite) TestApplyTimeSkippingOffset_ExcludesDeleteAndTimeSkippingTasks() { + skipDuration := 2 * time.Hour + ms := s.buildMutableStateWithTimeSkipping(skipDuration, true) + + now := s.mockShard.GetTimeSource().Now() + fireTime := now.Add(3 * time.Hour) + wfKey := ms.GetWorkflowKey() + + ms.InsertTasks[tasks.CategoryTimer] = []tasks.Task{ + &tasks.DeleteHistoryEventTask{WorkflowKey: wfKey, VisibilityTimestamp: fireTime}, + &tasks.TimeSkippingTimerTask{WorkflowKey: wfKey, VisibilityTimestamp: fireTime}, + } + + s.taskRefresher.applyTimeSkippingOffsetToTimerTasks(ms) + + for _, task := range ms.InsertTasks[tasks.CategoryTimer] { + s.Equal(fireTime, task.GetVisibilityTime(), "task type %v should not be adjusted", task.GetType()) + } +} + +func (s *taskRefresherSuite) TestApplyTimeSkippingOffset_MultipleSkipEntries_UsesTotalOffset() { + // Two separate skip entries: 1h + 1h = 2h total. The old bug used + // latestTargetVirtualTime - realNow instead of summing DurationToSkip. + dbRecord := &persistencespb.WorkflowMutableState{ + ExecutionInfo: &persistencespb.WorkflowExecutionInfo{ + NamespaceId: tests.NamespaceID.String(), + WorkflowId: tests.WorkflowID, + TimeSkippingInfo: &persistencespb.TimeSkippingInfo{ + Enabled: true, + TimeSkippedDetails: []*persistencespb.TimeSkippedDetails{ + {DurationToSkip: clock.TimeSkippedDurationToTimestamp(time.Hour)}, + {DurationToSkip: clock.TimeSkippedDurationToTimestamp(time.Hour)}, + }, + }, + }, + ExecutionState: &persistencespb.WorkflowExecutionState{ + RunId: tests.RunID, + State: enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING, + Status: enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING, + }, + NextEventId: 1, + } + ms, err := NewMutableStateFromDB(s.mockShard, s.mockShard.GetEventsCache(), log.NewTestLogger(), tests.LocalNamespaceEntry, dbRecord, 1) + s.NoError(err) + + now := s.mockShard.GetTimeSource().Now() + fireTime := now.Add(3 * time.Hour) + ms.InsertTasks[tasks.CategoryTimer] = []tasks.Task{ + &tasks.UserTimerTask{WorkflowKey: ms.GetWorkflowKey(), VisibilityTimestamp: fireTime}, + } + + s.taskRefresher.applyTimeSkippingOffsetToTimerTasks(ms) + + // Total offset = 2h, so adjusted = now + 3h - 2h = now + 1h + s.Equal(now.Add(time.Hour), ms.InsertTasks[tasks.CategoryTimer][0].GetVisibilityTime()) +} + +func (s *taskRefresherSuite) TestApplyTimeSkippingOffset_ClampsToNow() { + skipDuration := 5 * time.Hour + ms := s.buildMutableStateWithTimeSkipping(skipDuration, true) + + now := s.mockShard.GetTimeSource().Now() + fireTime := now.Add(3 * time.Hour) // adjusted = now + 3h - 5h = 2h in the past + ms.InsertTasks[tasks.CategoryTimer] = []tasks.Task{ + &tasks.UserTimerTask{WorkflowKey: ms.GetWorkflowKey(), VisibilityTimestamp: fireTime}, + } + + s.taskRefresher.applyTimeSkippingOffsetToTimerTasks(ms) + + s.WithinDuration(now, ms.InsertTasks[tasks.CategoryTimer][0].GetVisibilityTime(), time.Second) +} + +func (s *taskRefresherSuite) TestApplyTimeSkippingOffset_DisabledButHasSkips_StillAdjusts() { + // Even when Enabled=false, accumulated skips must be applied because virtual time + // has deviated from wall clock time and tasks must fire at the correct real time. + skipDuration := 2 * time.Hour + ms := s.buildMutableStateWithTimeSkipping(skipDuration, false) + + now := s.mockShard.GetTimeSource().Now() + fireTime := now.Add(3 * time.Hour) + ms.InsertTasks[tasks.CategoryTimer] = []tasks.Task{ + &tasks.UserTimerTask{WorkflowKey: ms.GetWorkflowKey(), VisibilityTimestamp: fireTime}, + } + + s.taskRefresher.applyTimeSkippingOffsetToTimerTasks(ms) + + s.Equal(fireTime.Add(-skipDuration), ms.InsertTasks[tasks.CategoryTimer][0].GetVisibilityTime()) +} + type mockTaskGeneratorProvider struct { mockTaskGenerator *MockTaskGenerator } diff --git a/tests/timeskipping_test.go b/tests/timeskipping_test.go new file mode 100644 index 0000000000..74cda47a56 --- /dev/null +++ b/tests/timeskipping_test.go @@ -0,0 +1,575 @@ +package tests + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + 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" + "go.temporal.io/sdk/workflow" + "go.temporal.io/server/common/dynamicconfig" + "go.temporal.io/server/common/testing/taskpoller" + "go.temporal.io/server/common/testing/testvars" + "go.temporal.io/server/tests/testcore" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/fieldmaskpb" +) + +func updateTimeSkipping( + env testcore.Env, + workflowExecution *commonpb.WorkflowExecution, + identity string, + enabled bool, +) *workflowservice.UpdateWorkflowExecutionOptionsResponse { + resp, err := env.FrontendClient().UpdateWorkflowExecutionOptions(env.Context(), &workflowservice.UpdateWorkflowExecutionOptionsRequest{ + Namespace: env.Namespace().String(), + WorkflowExecution: workflowExecution, + WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: enabled}, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"time_skipping_config"}}, + Identity: identity, + }) + require.NoError(env.T(), err) + return resp +} + +// TestTimeSkipping_EnabledToDisabled starts a workflow with time skipping enabled, +// then disables it via UpdateWorkflowExecutionOptions. Verifies a +// WorkflowExecutionOptionsUpdated event is written with Enabled=false. +func TestTimeSkipping_EnabledToDisabled(t *testing.T) { + s := testcore.NewEnv(t, testcore.WithDynamicConfig(dynamicconfig.TimeSkippingEnabled, true)) + + id := "functional-timeskipping-enabled-to-disabled" + tl := "functional-timeskipping-enabled-to-disabled-tq" + tv := testvars.New(t).WithTaskQueue(tl) + + startResp, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: id + "-type"}, + 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}, + }) + s.NoError(err) + workflowExecution := &commonpb.WorkflowExecution{WorkflowId: id, RunId: startResp.GetRunId()} + + // Disable time skipping — a real change that must produce an options-updated event. + updateResp := updateTimeSkipping(s, workflowExecution, tv.WorkerIdentity(), false) + s.False(updateResp.GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled()) + + poller := taskpoller.New(t, s.FrontendClient(), s.Namespace().String()) + _, err = poller.PollWorkflowTask( + &workflowservice.PollWorkflowTaskQueueRequest{ + TaskQueue: &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + }, + ).HandleTask(tv, taskpoller.CompleteWorkflowHandler) + s.NoError(err) + + // todo: @feiyang, will change with a new event type after data plane is added + historyEvents := s.GetHistory(s.Namespace().String(), workflowExecution) + s.EqualHistoryEvents(` + 1 WorkflowExecutionStarted + 2 WorkflowTaskScheduled + 3 WorkflowExecutionOptionsUpdated + 4 WorkflowTaskStarted + 5 WorkflowTaskCompleted + 6 WorkflowExecutionCompleted +`, historyEvents) + + for _, event := range historyEvents { + if event.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED { + s.False(event.GetWorkflowExecutionOptionsUpdatedEventAttributes().GetTimeSkippingConfig().GetEnabled()) + } + } +} + +// TestTimeSkipping_DisabledToEnabled starts a workflow without time skipping, +// then enables it via UpdateWorkflowExecutionOptions. Verifies a +// WorkflowExecutionOptionsUpdated event is written with Enabled=true. +func TestTimeSkipping_DisabledToEnabled(t *testing.T) { + s := testcore.NewEnv(t, testcore.WithDynamicConfig(dynamicconfig.TimeSkippingEnabled, true)) + + id := "functional-timeskipping-disabled-to-enabled" + tl := "functional-timeskipping-disabled-to-enabled-tq" + tv := testvars.New(t).WithTaskQueue(tl) + + startResp, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: id + "-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + }) + s.NoError(err) + workflowExecution := &commonpb.WorkflowExecution{WorkflowId: id, RunId: startResp.GetRunId()} + + // Enable time skipping — a real change that must produce an options-updated event. + updateResp := updateTimeSkipping(s, workflowExecution, tv.WorkerIdentity(), true) + s.True(updateResp.GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled()) + + poller := taskpoller.New(t, s.FrontendClient(), s.Namespace().String()) + _, err = poller.PollWorkflowTask( + &workflowservice.PollWorkflowTaskQueueRequest{ + TaskQueue: &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + }, + ).HandleTask(tv, taskpoller.CompleteWorkflowHandler) + s.NoError(err) + + // todo: @feiyang, will change with a new event type after data plane is added + historyEvents := s.GetHistory(s.Namespace().String(), workflowExecution) + s.EqualHistoryEvents(` + 1 WorkflowExecutionStarted + 2 WorkflowTaskScheduled + 3 WorkflowExecutionOptionsUpdated + 4 WorkflowTaskStarted + 5 WorkflowTaskCompleted + 6 WorkflowExecutionCompleted +`, historyEvents) + + for _, event := range historyEvents { + if event.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED { + s.True(event.GetWorkflowExecutionOptionsUpdatedEventAttributes().GetTimeSkippingConfig().GetEnabled()) + } + } +} + +// TestTimeSkipping_DisabledToDisabled starts a workflow with time skipping enabled, +// disables it, then attempts to disable it again. The second update is a no-op +// and must not produce a second WorkflowExecutionOptionsUpdated event. +func TestTimeSkipping_DisabledToDisabled(t *testing.T) { + s := testcore.NewEnv(t, testcore.WithDynamicConfig(dynamicconfig.TimeSkippingEnabled, true)) + + id := "functional-timeskipping-disabled-to-disabled" + tl := "functional-timeskipping-disabled-to-disabled-tq" + tv := testvars.New(t).WithTaskQueue(tl) + + startResp, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: id + "-type"}, + 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}, + }) + s.NoError(err) + workflowExecution := &commonpb.WorkflowExecution{WorkflowId: id, RunId: startResp.GetRunId()} + + // First update: enabled → disabled. This is a real change that produces an event. + updateTimeSkipping(s, workflowExecution, tv.WorkerIdentity(), false) + + // Second update: disabled → disabled again. Must be a no-op with no additional event. + updateResp := updateTimeSkipping(s, workflowExecution, tv.WorkerIdentity(), false) + s.False(updateResp.GetWorkflowExecutionOptions().GetTimeSkippingConfig().GetEnabled()) + + poller := taskpoller.New(t, s.FrontendClient(), s.Namespace().String()) + _, err = poller.PollWorkflowTask( + &workflowservice.PollWorkflowTaskQueueRequest{ + TaskQueue: &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + }, + ).HandleTask(tv, taskpoller.CompleteWorkflowHandler) + s.NoError(err) + + // Only one WorkflowExecutionOptionsUpdated event (from the first update). + historyEvents := s.GetHistory(s.Namespace().String(), workflowExecution) + s.EqualHistoryEvents(` + 1 WorkflowExecutionStarted + 2 WorkflowTaskScheduled + 3 WorkflowExecutionOptionsUpdated + 4 WorkflowTaskStarted + 5 WorkflowTaskCompleted + 6 WorkflowExecutionCompleted +`, historyEvents) +} + +// TestTimeSkipping_FeatureDisabled verifies that starting a workflow with time skipping +// returns an error when the feature flag is off for the namespace. +func TestTimeSkipping_FeatureDisabled(t *testing.T) { + // TimeSkippingEnabled defaults to false; no override needed. + s := testcore.NewEnv(t) + + id := "functional-timeskipping-feature-disabled" + tl := "functional-timeskipping-feature-disabled-tq" + + _, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: id + "-type"}, + 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}, + }) + s.Error(err, "expected error when time skipping is disabled for namespace") +} + +// TestTimeSkipping_Automatic_Server starts a workflow with time skipping enabled +// that mimics: sleep(1h) → check time → sleep(1h) → check time → complete. +// Each sleep is a separate timer scheduled sequentially. The time-skipping mechanism +// advances virtual time once per timer, completing the workflow in wall-clock seconds. +// Virtual time is verified at each checkpoint via WorkflowTaskStarted event timestamps. +func TestTimeSkipping_Automatic_Server(t *testing.T) { + s := testcore.NewEnv(t, testcore.WithDynamicConfig(dynamicconfig.TimeSkippingEnabled, true)) + + id := "functional-timeskipping-timer-fires-after-skip" + tl := "functional-timeskipping-timer-fires-after-skip-tq" + tv := testvars.New(t).WithTaskQueue(tl) + + startResp, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: id + "-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + WorkflowRunTimeout: durationpb.New(3 * time.Hour), // must exceed total timer duration (2h) + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }) + s.NoError(err) + workflowExecution := &commonpb.WorkflowExecution{WorkflowId: id, RunId: startResp.RunId} + + poller := taskpoller.New(t, s.FrontendClient(), s.Namespace().String()) + taskQueue := &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + startTimer := func(id string, d time.Duration) *commandpb.Command { + return &commandpb.Command{ + CommandType: enumspb.COMMAND_TYPE_START_TIMER, + Attributes: &commandpb.Command_StartTimerCommandAttributes{ + StartTimerCommandAttributes: &commandpb.StartTimerCommandAttributes{ + TimerId: id, + StartToFireTimeout: durationpb.New(d), + }, + }, + } + } + + virtualNow := func(task *workflowservice.PollWorkflowTaskQueueResponse) time.Time { + events := task.GetHistory().GetEvents() + return events[len(events)-1].GetEventTime().AsTime() + } + + // WT1: sleep(1h) — schedule the first 1-hour timer. Capture workflow start time. + var startTime time.Time + _, err = poller.PollWorkflowTask( + &workflowservice.PollWorkflowTaskQueueRequest{TaskQueue: taskQueue}, + ).HandleTask(tv, func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) { + startTime = task.GetHistory().GetEvents()[0].GetEventTime().AsTime() + return &workflowservice.RespondWorkflowTaskCompletedRequest{ + Commands: []*commandpb.Command{startTimer("sleep-1", time.Hour)}, + }, nil + }) + s.NoError(err) + + // WT2: fires after the first 1h skip. Check virtual time ≈ start+1h, then sleep(1h) again. + _, err = poller.PollWorkflowTask( + &workflowservice.PollWorkflowTaskQueueRequest{TaskQueue: taskQueue}, + ).HandleTask(tv, func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) { + s.GreaterOrEqual( + virtualNow(task).Sub(startTime), time.Hour, + "expected virtual time ≥ start+1h after first skip, got %v", virtualNow(task).Sub(startTime), + ) + s.LessOrEqual( + virtualNow(task).Sub(startTime), time.Hour+5*time.Second, + "expected virtual time ≤ start+1h+5s after first skip, got %v", virtualNow(task).Sub(startTime), + ) + return &workflowservice.RespondWorkflowTaskCompletedRequest{ + Commands: []*commandpb.Command{startTimer("sleep-2", time.Hour)}, + }, nil + }) + s.NoError(err) + + // WT3: fires after the second 1h skip. Check virtual time ≈ start+2h, then complete. + _, err = poller.PollWorkflowTask( + &workflowservice.PollWorkflowTaskQueueRequest{TaskQueue: taskQueue}, + ).HandleTask(tv, func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) { + s.GreaterOrEqual( + virtualNow(task).Sub(startTime), 2*time.Hour, + "expected virtual time ≥ start+2h after second skip, got %v", virtualNow(task).Sub(startTime), + ) + s.LessOrEqual( + virtualNow(task).Sub(startTime), 2*time.Hour+5*time.Second, + "expected virtual time ≤ start+2h+5s after second skip, got %v", virtualNow(task).Sub(startTime), + ) + return taskpoller.CompleteWorkflowHandler(task) + }) + s.NoError(err) + + s.EqualHistoryEvents(` + 1 WorkflowExecutionStarted + 2 WorkflowTaskScheduled + 3 WorkflowTaskStarted + 4 WorkflowTaskCompleted + 5 TimerStarted + 6 WorkflowExecutionTimeSkipped + 7 TimerFired + 8 WorkflowTaskScheduled + 9 WorkflowTaskStarted + 10 WorkflowTaskCompleted + 11 TimerStarted + 12 WorkflowExecutionTimeSkipped + 13 TimerFired + 14 WorkflowTaskScheduled + 15 WorkflowTaskStarted + 16 WorkflowTaskCompleted + 17 WorkflowExecutionCompleted +`, s.GetHistory(s.Namespace().String(), workflowExecution)) +} + +// TestTimeSkipping_EnableMidFlight starts a workflow without time skipping, waits for +// it to reach a 1h timer, then enables time skipping via UpdateWorkflowExecutionOptions. +// Verifies the workflow completes quickly and workflow.Now() reflects virtual time ≈ start+1h. +func TestTimeSkipping_EnableMidFlight(t *testing.T) { + s := testcore.NewEnv(t, testcore.WithSdkWorker(), testcore.WithDynamicConfig(dynamicconfig.TimeSkippingEnabled, true)) + + type result struct { + StartTime time.Time + TimeAfterSleep time.Time + } + + workflowFn := func(ctx workflow.Context) (result, error) { + startTime := workflow.Now(ctx) + if err := workflow.Sleep(ctx, time.Hour); err != nil { + return result{}, err + } + return result{startTime, workflow.Now(ctx)}, nil + } + + const wfType = "timeskipping-enable-mid-flight" + s.SdkWorker().RegisterWorkflowWithOptions(workflowFn, workflow.RegisterOptions{Name: wfType}) + + const wfID = "functional-timeskipping-enable-mid-flight" + startResp, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: wfID, + WorkflowType: &commonpb.WorkflowType{Name: wfType}, + TaskQueue: &taskqueuepb.TaskQueue{Name: s.WorkerTaskQueue(), Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + WorkflowRunTimeout: durationpb.New(3 * time.Hour), + // No TimeSkippingConfig — time skipping disabled at start. + }) + s.NoError(err) + + // Wait for the SDK worker to execute the first WorkflowTask and schedule the timer. + wfExec := &commonpb.WorkflowExecution{WorkflowId: wfID, RunId: startResp.RunId} + s.Eventually(func() bool { + history := s.GetHistory(s.Namespace().String(), wfExec) + for _, e := range history { + if e.GetEventType() == enumspb.EVENT_TYPE_TIMER_STARTED { + return true + } + } + return false + }, 10*time.Second, 100*time.Millisecond, "expected timer to be scheduled before enabling time skipping") + + // Enable time skipping mid-flight — the already-scheduled 1h timer should fire virtually. + updateTimeSkipping(s, wfExec, "test", true) + + var res result + s.NoError(s.SdkClient().GetWorkflow(s.Context(), wfID, startResp.RunId).Get(s.Context(), &res)) + + elapsed := res.TimeAfterSleep.Sub(res.StartTime) + s.GreaterOrEqual(elapsed, time.Hour, + "expected virtual time ≥ start+1h after skip, got %v", elapsed) + fmt.Println("time elapsed: ", elapsed.Minutes()) + s.LessOrEqual(elapsed, time.Hour+10*time.Second, + "expected virtual time ≤ start+1h+5s after skip, got %v", elapsed) +} + +// TestTimeSkipping_ParentChild tests that a parent workflow correctly receives results +// from a child workflow that contains a time-skipped sleep. +// The parent starts a child with a fixed WorkflowID. Once the child's timer is +// scheduled, time skipping is enabled on the child mid-flight. The parent waits +// for the child to complete and verifies virtual time advanced by ~1h. +func TestTimeSkipping_ParentChild(t *testing.T) { + s := testcore.NewEnv(t, testcore.WithSdkWorker(), testcore.WithDynamicConfig(dynamicconfig.TimeSkippingEnabled, true)) + + type childResult struct { + StartTime time.Time + TimeAfterSleep time.Time + } + + const childWfType = "timeskipping-parent-child-child" + const childWfID = "functional-timeskipping-parent-child-child" + wallClockStartTime := time.Now() + + childWorkflowFn := func(ctx workflow.Context) (childResult, error) { + start := workflow.Now(ctx) + if err := workflow.Sleep(ctx, time.Hour); err != nil { + return childResult{}, err + } + return childResult{start, workflow.Now(ctx)}, nil + } + + parentWorkflowFn := func(ctx workflow.Context) (childResult, error) { + childCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{ + WorkflowID: childWfID, + WorkflowExecutionTimeout: 2 * time.Hour, + }) + var res childResult + if err := workflow.ExecuteChildWorkflow(childCtx, childWfType).Get(ctx, &res); err != nil { + return childResult{}, err + } + return res, nil + } + + const parentWfType = "timeskipping-parent-child-parent" + s.SdkWorker().RegisterWorkflowWithOptions(childWorkflowFn, workflow.RegisterOptions{Name: childWfType}) + s.SdkWorker().RegisterWorkflowWithOptions(parentWorkflowFn, workflow.RegisterOptions{Name: parentWfType}) + + const parentWfID = "functional-timeskipping-parent-child-parent" + startResp, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: parentWfID, + WorkflowType: &commonpb.WorkflowType{Name: parentWfType}, + TaskQueue: &taskqueuepb.TaskQueue{Name: s.WorkerTaskQueue(), Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + WorkflowRunTimeout: durationpb.New(3 * time.Hour), + }) + s.NoError(err) + + // Wait for the child workflow's timer to be scheduled. + childExec := &commonpb.WorkflowExecution{WorkflowId: childWfID} + s.Eventually(func() bool { + resp, err := s.FrontendClient().GetWorkflowExecutionHistory(s.Context(), &workflowservice.GetWorkflowExecutionHistoryRequest{ + Namespace: s.Namespace().String(), + Execution: childExec, + }) + if err != nil { + return false + } + for _, e := range resp.History.GetEvents() { + if e.GetEventType() == enumspb.EVENT_TYPE_TIMER_STARTED { + return true + } + } + return false + }, 10*time.Second, 100*time.Millisecond, "expected child timer to be scheduled before enabling time skipping") + + // Enable time skipping on the child mid-flight so its 1h timer fires virtually. + // todo: @feiyang - this can be discussed as to be a default behavior, + // right now child doesn't inherit the time skipping config from the parent, + updateTimeSkipping(s, childExec, "test", true) + + // Parent should complete once the child's timer fires and the child returns its result. + var res childResult + s.NoError(s.SdkClient().GetWorkflow(s.Context(), parentWfID, startResp.RunId).Get(s.Context(), &res)) + + elapsed := res.TimeAfterSleep.Sub(res.StartTime) + s.GreaterOrEqual(elapsed, time.Hour, + "expected child virtual time >= start+1h after skip, got %v", elapsed) + s.LessOrEqual(elapsed, time.Hour+5*time.Second, + "expected child virtual time <= start+1h+5s after skip, got %v", elapsed) + // check this testing finishes in 1 minute + s.LessOrEqual(time.Since(wallClockStartTime), 1*time.Minute, + "expected child virtual time <= 1 minute after skip, got %v", time.Since(wallClockStartTime)) + +} + +// TestTimeSkipping_Automatic_SDKIntegration uses a real SDK worker to run a workflow that: +// 1. sleep(1h) — skipped virtually +// 2. run a dummy activity — must complete without timeout +// 3. sleep(1h) — skipped virtually +// +// Verifies that workflow.Now() reflects virtual time at each checkpoint, and that +// the activity completes successfully (time skipping does not cause spurious timeouts). +func TestTimeSkipping_Automatic_SDKIntegration(t *testing.T) { + s := testcore.NewEnv(t, testcore.WithSdkWorker(), testcore.WithDynamicConfig(dynamicconfig.TimeSkippingEnabled, true)) + + // Dummy activity — returns immediately with the real wall-clock time it ran at, + // so the test can confirm it executed well within its timeout window. + activityFn := func(ctx context.Context) (time.Time, error) { + return time.Now(), nil + } + s.SdkWorker().RegisterActivity(activityFn) + + type result struct { + StartTime time.Time + TimeAfterFirstSleep time.Time + ActivityRealTime time.Time // real wall-clock time the activity body ran + TimeAfterSecondSleep time.Time + } + + workflowFn := func(ctx workflow.Context) (result, error) { + startTime := workflow.Now(ctx) + + if err := workflow.Sleep(ctx, time.Hour); err != nil { + return result{}, err + } + t1 := workflow.Now(ctx) + + // Run the dummy activity between the two sleeps. + // A 10-second start-to-close timeout is generous for a no-op activity. + var activityRealTime time.Time + actCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + StartToCloseTimeout: 10 * time.Second, + }) + if err := workflow.ExecuteActivity(actCtx, activityFn).Get(ctx, &activityRealTime); err != nil { + return result{}, err + } + + if err := workflow.Sleep(ctx, time.Hour); err != nil { + return result{}, err + } + t2 := workflow.Now(ctx) + + return result{startTime, t1, activityRealTime, t2}, nil + } + + // Register with an explicit name so we can pass it to the gRPC start request. + const wfType = "timeskipping-sdk-sleep-workflow" + s.SdkWorker().RegisterWorkflowWithOptions(workflowFn, workflow.RegisterOptions{Name: wfType}) + + // The SDK's StartWorkflowOptions does not expose TimeSkippingConfig yet, so start + // via the gRPC frontend directly, then obtain the run handle from SdkClient. + const wfID = "functional-timeskipping-sdk-user-timers" + startResp, err := s.FrontendClient().StartWorkflowExecution(s.Context(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.Namespace().String(), + WorkflowId: wfID, + WorkflowType: &commonpb.WorkflowType{Name: wfType}, + TaskQueue: &taskqueuepb.TaskQueue{Name: s.WorkerTaskQueue(), Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + WorkflowRunTimeout: durationpb.New(3 * time.Hour), + TimeSkippingConfig: &workflowpb.TimeSkippingConfig{Enabled: true}, + }) + s.NoError(err) + + run := s.SdkClient().GetWorkflow(s.Context(), wfID, startResp.RunId) + + var res result + s.NoError(run.Get(s.Context(), &res)) + + // workflow.Now() after first sleep should be ~1h after start + s.GreaterOrEqual(res.TimeAfterFirstSleep.Sub(res.StartTime), time.Hour, + "expected virtual time ≥ start+1h after first sleep, got %v", res.TimeAfterFirstSleep.Sub(res.StartTime)) + s.LessOrEqual(res.TimeAfterFirstSleep.Sub(res.StartTime), time.Hour+3*time.Second, + "expected virtual time ≤ start+1h+3s after first sleep, got %v", res.TimeAfterFirstSleep.Sub(res.StartTime)) + + // The activity ran at a real wall-clock time after the first skip, so its real + // execution time must be within the 10-second start-to-close timeout window. + s.True(res.ActivityRealTime.After(res.StartTime), + "activity should have run after workflow start") + s.Less(time.Since(res.ActivityRealTime), 30*time.Second, + "activity real execution time should be recent (within test wall-clock), got %v ago", time.Since(res.ActivityRealTime)) + + // workflow.Now() after second sleep should be ~2h after start + s.GreaterOrEqual(res.TimeAfterSecondSleep.Sub(res.StartTime), 2*time.Hour, + "expected virtual time ≥ start+2h after second sleep, got %v", res.TimeAfterSecondSleep.Sub(res.StartTime)) + s.LessOrEqual(res.TimeAfterSecondSleep.Sub(res.StartTime), 2*time.Hour+3*time.Second, + "expected virtual time ≤ start+2h+3s after second sleep, got %v", res.TimeAfterSecondSleep.Sub(res.StartTime)) +}