diff --git a/api/historyservice/v1/request_response.pb.go b/api/historyservice/v1/request_response.pb.go index d2b58ab890..46d476bcba 100644 --- a/api/historyservice/v1/request_response.pb.go +++ b/api/historyservice/v1/request_response.pb.go @@ -2921,6 +2921,7 @@ func (x *SignalWorkflowExecutionRequest) GetChildWorkflowOnly() bool { type SignalWorkflowExecutionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` + Link *v14.Link `protobuf:"bytes,1,opt,name=link,proto3" json:"link,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2955,6 +2956,13 @@ func (*SignalWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{33} } +func (x *SignalWorkflowExecutionResponse) GetLink() *v14.Link { + if x != nil { + return x.Link + } + return nil +} + type SignalWithStartWorkflowExecutionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` @@ -3014,6 +3022,7 @@ type SignalWithStartWorkflowExecutionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` RunId string `protobuf:"bytes,1,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` Started bool `protobuf:"varint,2,opt,name=started,proto3" json:"started,omitempty"` + SignalLink *v14.Link `protobuf:"bytes,3,opt,name=signal_link,json=signalLink,proto3" json:"signal_link,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3062,6 +3071,13 @@ func (x *SignalWithStartWorkflowExecutionResponse) GetStarted() bool { return false } +func (x *SignalWithStartWorkflowExecutionResponse) GetSignalLink() *v14.Link { + if x != nil { + return x.SignalLink + } + return nil +} + type RemoveSignalMutableStateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` @@ -10715,14 +10731,17 @@ const file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12f\n" + "\x0esignal_request\x18\x02 \x01(\v2?.temporal.api.workflowservice.v1.SignalWorkflowExecutionRequestR\rsignalRequest\x12i\n" + "\x1bexternal_workflow_execution\x18\x03 \x01(\v2).temporal.api.common.v1.WorkflowExecutionR\x19externalWorkflowExecution\x12.\n" + - "\x13child_workflow_only\x18\x04 \x01(\bR\x11childWorkflowOnly:3\x92\xc4\x03/*-signal_request.workflow_execution.workflow_id\"!\n" + - "\x1fSignalWorkflowExecutionResponse\"\xff\x01\n" + + "\x13child_workflow_only\x18\x04 \x01(\bR\x11childWorkflowOnly:3\x92\xc4\x03/*-signal_request.workflow_execution.workflow_id\"S\n" + + "\x1fSignalWorkflowExecutionResponse\x120\n" + + "\x04link\x18\x01 \x01(\v2\x1c.temporal.api.common.v1.LinkR\x04link\"\xff\x01\n" + "'SignalWithStartWorkflowExecutionRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12\x83\x01\n" + - "\x19signal_with_start_request\x18\x02 \x01(\v2H.temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequestR\x16signalWithStartRequest:+\x92\xc4\x03'*%signal_with_start_request.workflow_id\"[\n" + + "\x19signal_with_start_request\x18\x02 \x01(\v2H.temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequestR\x16signalWithStartRequest:+\x92\xc4\x03'*%signal_with_start_request.workflow_id\"\x9a\x01\n" + "(SignalWithStartWorkflowExecutionResponse\x12\x15\n" + "\x06run_id\x18\x01 \x01(\tR\x05runId\x12\x18\n" + - "\astarted\x18\x02 \x01(\bR\astarted\"\xe3\x01\n" + + "\astarted\x18\x02 \x01(\bR\astarted\x12=\n" + + "\vsignal_link\x18\x03 \x01(\v2\x1c.temporal.api.common.v1.LinkR\n" + + "signalLink\"\xe3\x01\n" + "\x1fRemoveSignalMutableStateRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12X\n" + "\x12workflow_execution\x18\x02 \x01(\v2).temporal.api.common.v1.WorkflowExecutionR\x11workflowExecution\x12\x1d\n" + @@ -11607,173 +11626,175 @@ var file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs = 182, // 98: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock 212, // 99: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.signal_request:type_name -> temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest 186, // 100: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 213, // 101: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest - 186, // 102: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 214, // 103: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest - 186, // 104: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 186, // 105: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 215, // 106: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest - 216, // 107: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest - 186, // 108: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 186, // 109: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 182, // 110: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 182, // 111: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 186, // 112: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 182, // 113: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 186, // 114: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 186, // 115: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 202, // 116: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent - 182, // 117: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 186, // 118: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 186, // 119: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 182, // 120: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 217, // 121: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest - 218, // 122: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig - 219, // 123: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo - 220, // 124: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo - 221, // 125: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo - 222, // 126: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo - 223, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo - 224, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo - 225, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo - 186, // 130: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 131: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 226, // 132: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob - 226, // 133: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob - 227, // 134: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 228, // 135: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 171, // 136: temporal.server.api.historyservice.v1.SyncShardStatusRequest.status_time:type_name -> google.protobuf.Timestamp - 171, // 137: temporal.server.api.historyservice.v1.SyncActivityRequest.scheduled_time:type_name -> google.protobuf.Timestamp - 171, // 138: temporal.server.api.historyservice.v1.SyncActivityRequest.started_time:type_name -> google.protobuf.Timestamp - 171, // 139: temporal.server.api.historyservice.v1.SyncActivityRequest.last_heartbeat_time:type_name -> google.protobuf.Timestamp - 174, // 140: temporal.server.api.historyservice.v1.SyncActivityRequest.details:type_name -> temporal.api.common.v1.Payloads - 173, // 141: temporal.server.api.historyservice.v1.SyncActivityRequest.last_failure:type_name -> temporal.api.failure.v1.Failure - 229, // 142: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 227, // 143: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 171, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.first_scheduled_time:type_name -> google.protobuf.Timestamp - 171, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 175, // 146: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_initial_interval:type_name -> google.protobuf.Duration - 175, // 147: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_maximum_interval:type_name -> google.protobuf.Duration - 64, // 148: temporal.server.api.historyservice.v1.SyncActivitiesRequest.activities_info:type_name -> temporal.server.api.historyservice.v1.ActivitySyncInfo - 171, // 149: temporal.server.api.historyservice.v1.ActivitySyncInfo.scheduled_time:type_name -> google.protobuf.Timestamp - 171, // 150: temporal.server.api.historyservice.v1.ActivitySyncInfo.started_time:type_name -> google.protobuf.Timestamp - 171, // 151: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_heartbeat_time:type_name -> google.protobuf.Timestamp - 174, // 152: temporal.server.api.historyservice.v1.ActivitySyncInfo.details:type_name -> temporal.api.common.v1.Payloads - 173, // 153: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_failure:type_name -> temporal.api.failure.v1.Failure - 229, // 154: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 171, // 155: temporal.server.api.historyservice.v1.ActivitySyncInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp - 171, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 175, // 157: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_initial_interval:type_name -> google.protobuf.Duration - 175, // 158: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_maximum_interval:type_name -> google.protobuf.Duration - 186, // 159: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 228, // 160: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 228, // 161: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 186, // 162: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 230, // 163: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo - 231, // 164: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo - 171, // 165: temporal.server.api.historyservice.v1.RemoveTaskRequest.visibility_time:type_name -> google.protobuf.Timestamp - 232, // 166: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken - 164, // 167: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.shard_messages:type_name -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry - 233, // 168: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 234, // 169: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 235, // 170: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest - 236, // 171: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse - 237, // 172: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest - 238, // 173: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 238, // 174: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 234, // 175: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 233, // 176: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 238, // 177: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 238, // 178: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 239, // 179: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest - 186, // 180: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 96, // 181: temporal.server.api.historyservice.v1.GetReplicationStatusResponse.shards:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus - 171, // 182: temporal.server.api.historyservice.v1.ShardReplicationStatus.shard_local_time:type_name -> google.protobuf.Timestamp - 165, // 183: temporal.server.api.historyservice.v1.ShardReplicationStatus.remote_clusters:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry - 166, // 184: temporal.server.api.historyservice.v1.ShardReplicationStatus.handover_namespaces:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry - 171, // 185: temporal.server.api.historyservice.v1.ShardReplicationStatus.max_replication_task_visibility_time:type_name -> google.protobuf.Timestamp - 171, // 186: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster.acked_task_visibility_time:type_name -> google.protobuf.Timestamp - 186, // 187: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 186, // 188: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 226, // 189: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob - 229, // 190: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 186, // 191: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 171, // 192: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_start_time:type_name -> google.protobuf.Timestamp - 171, // 193: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_close_time:type_name -> google.protobuf.Timestamp - 240, // 194: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest - 241, // 195: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse - 242, // 196: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState - 243, // 197: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages - 244, // 198: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest - 245, // 199: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse - 246, // 200: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest - 247, // 201: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 200, // 202: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History - 247, // 203: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 248, // 204: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest - 249, // 205: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse - 250, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request - 251, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response - 252, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest - 253, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse - 254, // 210: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest - 255, // 211: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse - 256, // 212: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 257, // 213: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask - 256, // 214: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 258, // 215: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata - 167, // 216: temporal.server.api.historyservice.v1.ListQueuesResponse.queues:type_name -> temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo - 168, // 217: temporal.server.api.historyservice.v1.AddTasksRequest.tasks:type_name -> temporal.server.api.historyservice.v1.AddTasksRequest.Task - 259, // 218: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest - 260, // 219: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse - 261, // 220: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 262, // 221: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload - 173, // 222: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.failure:type_name -> temporal.api.failure.v1.Failure - 171, // 223: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.close_time:type_name -> google.protobuf.Timestamp - 185, // 224: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link - 171, // 225: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.start_time:type_name -> google.protobuf.Timestamp - 261, // 226: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 262, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload - 263, // 228: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure - 171, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.start_time:type_name -> google.protobuf.Timestamp - 185, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link - 264, // 231: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef - 265, // 232: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState - 266, // 233: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck - 186, // 234: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 188, // 235: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 192, // 236: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 267, // 237: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact - 268, // 238: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest - 269, // 239: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions - 270, // 240: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest - 271, // 241: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest - 272, // 242: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest - 273, // 243: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest - 274, // 244: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions - 275, // 245: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest - 276, // 246: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest - 277, // 247: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest - 278, // 248: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse - 279, // 249: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest - 280, // 250: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse - 1, // 251: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest - 105, // 252: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest - 2, // 253: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse - 106, // 254: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse - 281, // 255: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 281, // 256: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 282, // 257: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages - 98, // 258: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster - 97, // 259: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo - 226, // 260: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob - 283, // 261: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions - 0, // 262: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions - 263, // [263:263] is the sub-list for method output_type - 263, // [263:263] is the sub-list for method input_type - 262, // [262:263] is the sub-list for extension type_name - 261, // [261:262] is the sub-list for extension extendee - 0, // [0:261] is the sub-list for field type_name + 185, // 101: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link + 213, // 102: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest + 185, // 103: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse.signal_link:type_name -> temporal.api.common.v1.Link + 186, // 104: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 214, // 105: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest + 186, // 106: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 186, // 107: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 215, // 108: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest + 216, // 109: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest + 186, // 110: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 186, // 111: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 182, // 112: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 182, // 113: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 186, // 114: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 182, // 115: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 186, // 116: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 186, // 117: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 202, // 118: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent + 182, // 119: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 186, // 120: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 186, // 121: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 182, // 122: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 217, // 123: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest + 218, // 124: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig + 219, // 125: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo + 220, // 126: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo + 221, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo + 222, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo + 223, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo + 224, // 130: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo + 225, // 131: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo + 186, // 132: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 187, // 133: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 226, // 134: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob + 226, // 135: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob + 227, // 136: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 228, // 137: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 171, // 138: temporal.server.api.historyservice.v1.SyncShardStatusRequest.status_time:type_name -> google.protobuf.Timestamp + 171, // 139: temporal.server.api.historyservice.v1.SyncActivityRequest.scheduled_time:type_name -> google.protobuf.Timestamp + 171, // 140: temporal.server.api.historyservice.v1.SyncActivityRequest.started_time:type_name -> google.protobuf.Timestamp + 171, // 141: temporal.server.api.historyservice.v1.SyncActivityRequest.last_heartbeat_time:type_name -> google.protobuf.Timestamp + 174, // 142: temporal.server.api.historyservice.v1.SyncActivityRequest.details:type_name -> temporal.api.common.v1.Payloads + 173, // 143: temporal.server.api.historyservice.v1.SyncActivityRequest.last_failure:type_name -> temporal.api.failure.v1.Failure + 229, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 227, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 171, // 146: temporal.server.api.historyservice.v1.SyncActivityRequest.first_scheduled_time:type_name -> google.protobuf.Timestamp + 171, // 147: temporal.server.api.historyservice.v1.SyncActivityRequest.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 175, // 148: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_initial_interval:type_name -> google.protobuf.Duration + 175, // 149: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_maximum_interval:type_name -> google.protobuf.Duration + 64, // 150: temporal.server.api.historyservice.v1.SyncActivitiesRequest.activities_info:type_name -> temporal.server.api.historyservice.v1.ActivitySyncInfo + 171, // 151: temporal.server.api.historyservice.v1.ActivitySyncInfo.scheduled_time:type_name -> google.protobuf.Timestamp + 171, // 152: temporal.server.api.historyservice.v1.ActivitySyncInfo.started_time:type_name -> google.protobuf.Timestamp + 171, // 153: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_heartbeat_time:type_name -> google.protobuf.Timestamp + 174, // 154: temporal.server.api.historyservice.v1.ActivitySyncInfo.details:type_name -> temporal.api.common.v1.Payloads + 173, // 155: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_failure:type_name -> temporal.api.failure.v1.Failure + 229, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 171, // 157: temporal.server.api.historyservice.v1.ActivitySyncInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp + 171, // 158: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 175, // 159: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_initial_interval:type_name -> google.protobuf.Duration + 175, // 160: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_maximum_interval:type_name -> google.protobuf.Duration + 186, // 161: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 228, // 162: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 228, // 163: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 186, // 164: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 230, // 165: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo + 231, // 166: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo + 171, // 167: temporal.server.api.historyservice.v1.RemoveTaskRequest.visibility_time:type_name -> google.protobuf.Timestamp + 232, // 168: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken + 164, // 169: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.shard_messages:type_name -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry + 233, // 170: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 234, // 171: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 235, // 172: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest + 236, // 173: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse + 237, // 174: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest + 238, // 175: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 238, // 176: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 234, // 177: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 233, // 178: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 238, // 179: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 238, // 180: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 239, // 181: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest + 186, // 182: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 96, // 183: temporal.server.api.historyservice.v1.GetReplicationStatusResponse.shards:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus + 171, // 184: temporal.server.api.historyservice.v1.ShardReplicationStatus.shard_local_time:type_name -> google.protobuf.Timestamp + 165, // 185: temporal.server.api.historyservice.v1.ShardReplicationStatus.remote_clusters:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry + 166, // 186: temporal.server.api.historyservice.v1.ShardReplicationStatus.handover_namespaces:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry + 171, // 187: temporal.server.api.historyservice.v1.ShardReplicationStatus.max_replication_task_visibility_time:type_name -> google.protobuf.Timestamp + 171, // 188: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster.acked_task_visibility_time:type_name -> google.protobuf.Timestamp + 186, // 189: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 186, // 190: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 226, // 191: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob + 229, // 192: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 186, // 193: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 171, // 194: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_start_time:type_name -> google.protobuf.Timestamp + 171, // 195: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_close_time:type_name -> google.protobuf.Timestamp + 240, // 196: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest + 241, // 197: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse + 242, // 198: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState + 243, // 199: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages + 244, // 200: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest + 245, // 201: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse + 246, // 202: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest + 247, // 203: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 200, // 204: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History + 247, // 205: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 248, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest + 249, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse + 250, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request + 251, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response + 252, // 210: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest + 253, // 211: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse + 254, // 212: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest + 255, // 213: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse + 256, // 214: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 257, // 215: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask + 256, // 216: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 258, // 217: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata + 167, // 218: temporal.server.api.historyservice.v1.ListQueuesResponse.queues:type_name -> temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo + 168, // 219: temporal.server.api.historyservice.v1.AddTasksRequest.tasks:type_name -> temporal.server.api.historyservice.v1.AddTasksRequest.Task + 259, // 220: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest + 260, // 221: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse + 261, // 222: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 262, // 223: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload + 173, // 224: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.failure:type_name -> temporal.api.failure.v1.Failure + 171, // 225: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.close_time:type_name -> google.protobuf.Timestamp + 185, // 226: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link + 171, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.start_time:type_name -> google.protobuf.Timestamp + 261, // 228: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 262, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload + 263, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure + 171, // 231: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.start_time:type_name -> google.protobuf.Timestamp + 185, // 232: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link + 264, // 233: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef + 265, // 234: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState + 266, // 235: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck + 186, // 236: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 188, // 237: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 192, // 238: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 267, // 239: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact + 268, // 240: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest + 269, // 241: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions + 270, // 242: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest + 271, // 243: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest + 272, // 244: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest + 273, // 245: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest + 274, // 246: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions + 275, // 247: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest + 276, // 248: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest + 277, // 249: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest + 278, // 250: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse + 279, // 251: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest + 280, // 252: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse + 1, // 253: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest + 105, // 254: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest + 2, // 255: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse + 106, // 256: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse + 281, // 257: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 281, // 258: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 282, // 259: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages + 98, // 260: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster + 97, // 261: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo + 226, // 262: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob + 283, // 263: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions + 0, // 264: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions + 265, // [265:265] is the sub-list for method output_type + 265, // [265:265] is the sub-list for method input_type + 264, // [264:265] is the sub-list for extension type_name + 263, // [263:264] is the sub-list for extension extendee + 0, // [0:263] is the sub-list for field type_name } func init() { file_temporal_server_api_historyservice_v1_request_response_proto_init() } diff --git a/chasm/lib/workflow/gen/workflowpb/v1/state.go-helpers.pb.go b/chasm/lib/workflow/gen/workflowpb/v1/state.go-helpers.pb.go index 99fa7d3f46..50980db658 100644 --- a/chasm/lib/workflow/gen/workflowpb/v1/state.go-helpers.pb.go +++ b/chasm/lib/workflow/gen/workflowpb/v1/state.go-helpers.pb.go @@ -78,3 +78,40 @@ func (this *NexusCancellationParentData) Equal(that interface{}) bool { return proto.Equal(this, that1) } + +// Marshal an object of type IncomingSignalData to the protobuf v3 wire format +func (val *IncomingSignalData) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type IncomingSignalData from the protobuf v3 wire format +func (val *IncomingSignalData) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *IncomingSignalData) Size() int { + return proto.Size(val) +} + +// Equal returns whether two IncomingSignalData 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 *IncomingSignalData) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *IncomingSignalData + switch t := that.(type) { + case *IncomingSignalData: + that1 = t + case IncomingSignalData: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} diff --git a/chasm/lib/workflow/gen/workflowpb/v1/state.pb.go b/chasm/lib/workflow/gen/workflowpb/v1/state.pb.go index 4669064b27..304853e6af 100644 --- a/chasm/lib/workflow/gen/workflowpb/v1/state.pb.go +++ b/chasm/lib/workflow/gen/workflowpb/v1/state.pb.go @@ -125,6 +125,52 @@ func (x *NexusCancellationParentData) GetRequestedEventId() int64 { return 0 } +// IncomingSignalData records the event associated with a signal's request ID, which allows +// DescribeWorkflow to resolve RequestIDRef signal backlinks. +type IncomingSignalData struct { + state protoimpl.MessageState `protogen:"open.v1"` + EventId int64 `protobuf:"varint,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IncomingSignalData) Reset() { + *x = IncomingSignalData{} + mi := &file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IncomingSignalData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IncomingSignalData) ProtoMessage() {} + +func (x *IncomingSignalData) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_msgTypes[2] + 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 IncomingSignalData.ProtoReflect.Descriptor instead. +func (*IncomingSignalData) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDescGZIP(), []int{2} +} + +func (x *IncomingSignalData) GetEventId() int64 { + if x != nil { + return x.EventId + } + return 0 +} + var File_temporal_server_chasm_lib_workflow_proto_v1_state_proto protoreflect.FileDescriptor const file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDesc = "" + @@ -134,7 +180,9 @@ const file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDesc = "" "\x12scheduled_event_id\x18\x01 \x01(\x03R\x10scheduledEventId\x122\n" + "\x15scheduled_event_token\x18\x02 \x01(\fR\x13scheduledEventToken\"K\n" + "\x1bNexusCancellationParentData\x12,\n" + - "\x12requested_event_id\x18\x01 \x01(\x03R\x10requestedEventIdBDZBgo.temporal.io/server/chasm/lib/workflow/gen/workflowpb;workflowpbb\x06proto3" + "\x12requested_event_id\x18\x01 \x01(\x03R\x10requestedEventId\"/\n" + + "\x12IncomingSignalData\x12\x19\n" + + "\bevent_id\x18\x01 \x01(\x03R\aeventIdBDZBgo.temporal.io/server/chasm/lib/workflow/gen/workflowpb;workflowpbb\x06proto3" var ( file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDescOnce sync.Once @@ -148,10 +196,11 @@ func file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDescGZIP() return file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDescData } -var file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_goTypes = []any{ (*NexusOperationParentData)(nil), // 0: temporal.server.chasm.lib.workflow.proto.v1.NexusOperationParentData (*NexusCancellationParentData)(nil), // 1: temporal.server.chasm.lib.workflow.proto.v1.NexusCancellationParentData + (*IncomingSignalData)(nil), // 2: temporal.server.chasm.lib.workflow.proto.v1.IncomingSignalData } var file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -172,7 +221,7 @@ func file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDesc), len(file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDesc)), NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/chasm/lib/workflow/proto/v1/state.proto b/chasm/lib/workflow/proto/v1/state.proto index 8f82cf1af4..0ff01d116b 100644 --- a/chasm/lib/workflow/proto/v1/state.proto +++ b/chasm/lib/workflow/proto/v1/state.proto @@ -19,3 +19,9 @@ message NexusCancellationParentData { // Event ID of the NEXUS_OPERATION_CANCEL_REQUESTED event. int64 requested_event_id = 1; } + +// IncomingSignalData records the event associated with a signal's request ID, which allows +// DescribeWorkflow to resolve RequestIDRef signal backlinks. +message IncomingSignalData { + int64 event_id = 1; +} diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index df0355886b..8e3148cc03 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -10,6 +10,7 @@ import ( "go.temporal.io/server/chasm/lib/callback" callbackspb "go.temporal.io/server/chasm/lib/callback/gen/callbackpb/v1" "go.temporal.io/server/chasm/lib/nexusoperation" + chasmworkflowpb "go.temporal.io/server/chasm/lib/workflow/gen/workflowpb/v1" "go.temporal.io/server/service/history/historybuilder" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" @@ -30,6 +31,10 @@ type Workflow struct { // Operations map is used to store the Nexus operations for the workflow, keyed by scheduled event ID. Operations chasm.Map[int64, *nexusoperation.Operation] + + // IncomingSignals map is used to track incoming signals, keyed by request ID, + // to allow DescribeWorkflow to resolve RequestIDRef signal backlinks. + IncomingSignals chasm.Map[string, *chasmworkflowpb.IncomingSignalData] } func NewWorkflow( @@ -131,6 +136,53 @@ func addAndApplyHistoryEvent[D EventDefinition]( return event, def.Apply(ctx, w, event) } +// AddIncomingSignalEvent adds an entry for the signal requestID -> eventID mapping to +// track all signals that have been received by the workflow. +// Note that since signals are buffered, the eventID may the common.BufferedEventID, which +// will be updated to a concrete eventID once this signal is flushed to the DB. +// If caller tries to add an already-existing eventID, this function will ignore and silently return +// instead of overwriting -- use UpdateIncomingSignalEvent to update existing entries. +func (w *Workflow) AddIncomingSignalEvent( + ctx chasm.MutableContext, + requestID string, + eventID int64, +) error { + if w.IncomingSignals == nil { + w.IncomingSignals = make(chasm.Map[string, *chasmworkflowpb.IncomingSignalData]) + } + if w.HasIncomingSignalEvent(ctx, requestID) { + return nil + } + w.IncomingSignals[requestID] = chasm.NewDataField(ctx, &chasmworkflowpb.IncomingSignalData{ + // This might be common.BufferedEventID, which will be updated via UpdateIncomingSignalEvent + // once this signal is flushed to DB. + EventId: eventID, + }) + return nil +} + +// UpdateIncomingSignalEvent updates the eventID for an existing signal requestID in the map. +// If the requestID is not in the map, this is a no-op (e.g. when called for non-signal request IDs +// during buffer flush). +func (w *Workflow) UpdateIncomingSignalEvent( + ctx chasm.MutableContext, + requestID string, + eventID int64, +) error { + if w.HasIncomingSignalEvent(ctx, requestID) { + w.IncomingSignals[requestID].Get(ctx).EventId = eventID + } + + return nil +} + +// HasIncomingSignalEvent returns true if a signal with this requestID is already persisted +// in this CHASM tree. +func (w *Workflow) HasIncomingSignalEvent(_ chasm.Context, requestID string) bool { + _, exists := w.IncomingSignals[requestID] + return exists +} + // HasAnyBufferedEvent returns true if the workflow has any buffered event matching the given filter. func (w *Workflow) HasAnyBufferedEvent(filter historybuilder.BufferedEventFilter) bool { return w.MSPointer.HasAnyBufferedEvent(filter) diff --git a/common/dynamicconfig/constants.go b/common/dynamicconfig/constants.go index 1f078fff81..e56097eecf 100644 --- a/common/dynamicconfig/constants.go +++ b/common/dynamicconfig/constants.go @@ -2943,6 +2943,15 @@ to the CHASM (V2) implementation on active scheduler workflows.`, instead of the previous HSM backed implementation.`, ) + EnableCHASMSignalBacklinks = NewNamespaceBoolSetting( + "history.enableCHASMSignalBacklinks", + false, + `Controls whether incoming signal request IDs are tracked in the CHASM IncomingSignals +map to enable DescribeWorkflow to resolve RequestIDRef signal backlinks. Requires EnableChasm. +Only enable once all servers in the fleet have been upgraded to a version that understands +the IncomingSignals CHASM field.`, + ) + VersionMembershipCacheTTL = NewGlobalDurationSetting( "history.versionMembershipCacheTTL", 1*time.Second, diff --git a/common/metrics/metric_defs.go b/common/metrics/metric_defs.go index c4d806c277..85e58aaacb 100644 --- a/common/metrics/metric_defs.go +++ b/common/metrics/metric_defs.go @@ -904,6 +904,14 @@ var ( "chasm_pure_task_errors", WithDescription("The number of errors during CHASM pure task execution."), ) + ChasmIncomingSignalWritten = NewCounterDef( + "chasm_incoming_signal_written", + WithDescription("The number of signal backlinks written to the CHASM IncomingSignals map."), + ) + ChasmIncomingSignalDuplicate = NewCounterDef( + "chasm_incoming_signal_duplicate", + WithDescription("The number of duplicate signal request IDs detected when writing to the CHASM IncomingSignals map. Non-zero values indicate unexpected signal redelivery."), + ) TaskScheduleToStartLatency = NewTimerDef("task_schedule_to_start_latency") TaskBatchCompleteCounter = NewCounterDef("task_batch_complete_counter") TaskReschedulerPendingTasks = NewDimensionlessHistogramDef("task_rescheduler_pending_tasks") @@ -1006,30 +1014,34 @@ var ( "persisted_mutable_state_size", WithDescription("Size of the persisted Workflow Execution's state in DB, emitted each time a workflow execution is updated."), ) - ExecutionInfoSize = NewBytesHistogramDef("execution_info_size") - ExecutionStateSize = NewBytesHistogramDef("execution_state_size") - ActivityInfoSize = NewBytesHistogramDef("activity_info_size") - TimerInfoSize = NewBytesHistogramDef("timer_info_size") - ChildInfoSize = NewBytesHistogramDef("child_info_size") - RequestCancelInfoSize = NewBytesHistogramDef("request_cancel_info_size") - SignalInfoSize = NewBytesHistogramDef("signal_info_size") - SignalRequestIDSize = NewBytesHistogramDef("signal_request_id_size") - BufferedEventsSize = NewBytesHistogramDef("buffered_events_size") - ChasmTotalSize = NewBytesHistogramDef("chasm_total_size") - ActivityInfoCount = NewDimensionlessHistogramDef("activity_info_count") - TimerInfoCount = NewDimensionlessHistogramDef("timer_info_count") - ChildInfoCount = NewDimensionlessHistogramDef("child_info_count") - SignalInfoCount = NewDimensionlessHistogramDef("signal_info_count") - RequestCancelInfoCount = NewDimensionlessHistogramDef("request_cancel_info_count") - SignalRequestIDCount = NewDimensionlessHistogramDef("signal_request_id_count") - BufferedEventsCount = NewDimensionlessHistogramDef("buffered_events_count") - TaskCount = NewDimensionlessHistogramDef("task_count") - TotalActivityCount = NewDimensionlessHistogramDef("total_activity_count") - TotalUserTimerCount = NewDimensionlessHistogramDef("total_user_timer_count") - TotalChildExecutionCount = NewDimensionlessHistogramDef("total_child_execution_count") - TotalRequestCancelExternalCount = NewDimensionlessHistogramDef("total_request_cancel_external_count") - TotalSignalExternalCount = NewDimensionlessHistogramDef("total_signal_external_count") - TotalSignalCount = NewDimensionlessHistogramDef("total_signal_count") + ExecutionInfoSize = NewBytesHistogramDef("execution_info_size") + ExecutionStateSize = NewBytesHistogramDef("execution_state_size") + ActivityInfoSize = NewBytesHistogramDef("activity_info_size") + TimerInfoSize = NewBytesHistogramDef("timer_info_size") + ChildInfoSize = NewBytesHistogramDef("child_info_size") + RequestCancelInfoSize = NewBytesHistogramDef("request_cancel_info_size") + SignalInfoSize = NewBytesHistogramDef("signal_info_size") + SignalRequestIDSize = NewBytesHistogramDef("signal_request_id_size") + BufferedEventsSize = NewBytesHistogramDef("buffered_events_size") + ChasmTotalSize = NewBytesHistogramDef("chasm_total_size") + ActivityInfoCount = NewDimensionlessHistogramDef("activity_info_count") + TimerInfoCount = NewDimensionlessHistogramDef("timer_info_count") + ChildInfoCount = NewDimensionlessHistogramDef("child_info_count") + SignalInfoCount = NewDimensionlessHistogramDef("signal_info_count") + RequestCancelInfoCount = NewDimensionlessHistogramDef("request_cancel_info_count") + SignalRequestIDCount = NewDimensionlessHistogramDef("signal_request_id_count") + BufferedEventsCount = NewDimensionlessHistogramDef("buffered_events_count") + TaskCount = NewDimensionlessHistogramDef("task_count") + TotalActivityCount = NewDimensionlessHistogramDef("total_activity_count") + TotalUserTimerCount = NewDimensionlessHistogramDef("total_user_timer_count") + TotalChildExecutionCount = NewDimensionlessHistogramDef("total_child_execution_count") + TotalRequestCancelExternalCount = NewDimensionlessHistogramDef("total_request_cancel_external_count") + TotalSignalExternalCount = NewDimensionlessHistogramDef("total_signal_external_count") + TotalSignalCount = NewDimensionlessHistogramDef("total_signal_count") + DescribeWorkflowSignalBacklinksCount = NewCounterDef( + "describe_workflow_signal_backlinks", + WithDescription("The number of signal backlinks resolved from the CHASM IncomingSignals map in DescribeWorkflow responses."), + ) WorkflowBackoffCount = NewCounterDef("workflow_backoff_timer") WorkflowRetryBackoffTimerCount = NewCounterDef("workflow_retry_backoff_timer") WorkflowCronBackoffTimerCount = NewCounterDef("workflow_cron_backoff_timer") diff --git a/proto/internal/temporal/server/api/historyservice/v1/request_response.proto b/proto/internal/temporal/server/api/historyservice/v1/request_response.proto index 6ec02b539b..7269a7ca33 100644 --- a/proto/internal/temporal/server/api/historyservice/v1/request_response.proto +++ b/proto/internal/temporal/server/api/historyservice/v1/request_response.proto @@ -496,7 +496,9 @@ message SignalWorkflowExecutionRequest { bool child_workflow_only = 4; } -message SignalWorkflowExecutionResponse {} +message SignalWorkflowExecutionResponse { + temporal.api.common.v1.Link link = 1; +} message SignalWithStartWorkflowExecutionRequest { option (routing).workflow_id = "signal_with_start_request.workflow_id"; @@ -510,6 +512,7 @@ message SignalWithStartWorkflowExecutionRequest { message SignalWithStartWorkflowExecutionResponse { string run_id = 1; bool started = 2; + temporal.api.common.v1.Link signal_link = 3; } message RemoveSignalMutableStateRequest { diff --git a/service/frontend/workflow_handler.go b/service/frontend/workflow_handler.go index 47af481aa2..d6a3d5f7f3 100644 --- a/service/frontend/workflow_handler.go +++ b/service/frontend/workflow_handler.go @@ -2320,7 +2320,7 @@ func (wh *WorkflowHandler) SignalWorkflowExecution(ctx context.Context, request return nil, err } - _, err = wh.historyClient.SignalWorkflowExecution(ctx, &historyservice.SignalWorkflowExecutionRequest{ + resp, err := wh.historyClient.SignalWorkflowExecution(ctx, &historyservice.SignalWorkflowExecutionRequest{ NamespaceId: namespaceID.String(), SignalRequest: request, }) @@ -2328,7 +2328,9 @@ func (wh *WorkflowHandler) SignalWorkflowExecution(ctx context.Context, request return nil, err } - return &workflowservice.SignalWorkflowExecutionResponse{}, nil + return &workflowservice.SignalWorkflowExecutionResponse{ + Link: resp.GetLink(), + }, nil } // SignalWithStartWorkflowExecution is used to ensure sending signal to a workflow. @@ -2446,8 +2448,9 @@ func (wh *WorkflowHandler) SignalWithStartWorkflowExecution(ctx context.Context, } return &workflowservice.SignalWithStartWorkflowExecutionResponse{ - RunId: resp.GetRunId(), - Started: resp.Started, + RunId: resp.GetRunId(), + Started: resp.Started, + SignalLink: resp.GetSignalLink(), }, nil } diff --git a/service/history/api/create_workflow_util.go b/service/history/api/create_workflow_util.go index 8f783cab75..15f0f55258 100644 --- a/service/history/api/create_workflow_util.go +++ b/service/history/api/create_workflow_util.go @@ -81,6 +81,7 @@ func NewWorkflowWithSignal( signalWithStartRequest.GetSignalInput(), signalWithStartRequest.GetIdentity(), signalWithStartRequest.GetHeader(), + signalWithStartRequest.GetRequestId(), signalWithStartRequest.GetLinks(), ); err != nil { return nil, err diff --git a/service/history/api/describeworkflow/api.go b/service/history/api/describeworkflow/api.go index e3255c2dc0..ca52e2ab63 100644 --- a/service/history/api/describeworkflow/api.go +++ b/service/history/api/describeworkflow/api.go @@ -6,6 +6,7 @@ import ( "fmt" "strconv" + "github.com/google/uuid" "github.com/sony/gobreaker" commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" @@ -15,14 +16,17 @@ import ( enumsspb "go.temporal.io/server/api/enums/v1" "go.temporal.io/server/api/historyservice/v1" persistencespb "go.temporal.io/server/api/persistence/v1" + "go.temporal.io/server/chasm" chasmcallback "go.temporal.io/server/chasm/lib/callback" callbackspb "go.temporal.io/server/chasm/lib/callback/gen/callbackpb/v1" "go.temporal.io/server/chasm/lib/nexusoperation" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/common" "go.temporal.io/server/common/definition" "go.temporal.io/server/common/locks" "go.temporal.io/server/common/log" "go.temporal.io/server/common/log/tag" + "go.temporal.io/server/common/metrics" "go.temporal.io/server/common/namespace" "go.temporal.io/server/common/persistence/visibility/manager" "go.temporal.io/server/components/callbacks" @@ -258,10 +262,27 @@ func Invoke( // Check for CHASM callbacks (regardless of feature flag setting) // Only process CHASM callbacks if we have an actual chasm.Node (not a noopChasmTree) if mutableState.ChasmEnabled() { + wf, chasmCtx, err := mutableState.ChasmWorkflowComponentReadOnly(ctx) + if err != nil { + // Generate a requestID to tag onto errors for ease of debugging. + requestID := uuid.NewString() + shard.GetLogger().Error( + "failed to get workflow component from CHASM tree", + tag.WorkflowNamespaceID(namespaceID.String()), + tag.WorkflowID(executionInfo.WorkflowId), + tag.WorkflowRunID(executionState.RunId), + tag.Error(err), + tag.RequestID(requestID), + ) + return nil, serviceerror.NewInternal( + fmt.Sprintf("failed to construct describe response for requestID: %s", requestID), + ) + } chasmCallbackInfos, err := buildCallbackInfosFromChasm( ctx, namespaceID, - mutableState, + wf, + chasmCtx, executionInfo, executionState, outboundQueueCBPool, @@ -271,6 +292,26 @@ func Invoke( return nil, err } result.Callbacks = append(result.Callbacks, chasmCallbackInfos...) + + if wf.IncomingSignals != nil { + for requestID, incomingSignalDataField := range wf.IncomingSignals { + incomingSignalData := incomingSignalDataField.Get(chasmCtx) + buffered := incomingSignalData.EventId == common.BufferedEventID + info := &workflowpb.RequestIdInfo{ + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + Buffered: buffered, + } + if !buffered { + info.EventId = incomingSignalData.EventId + } + result.WorkflowExtendedInfo.RequestIdInfos[requestID] = info + } + if n := len(wf.IncomingSignals); n > 0 { + metrics.DescribeWorkflowSignalBacklinksCount.With( + shard.GetMetricsHandler().WithTags(metrics.NamespaceTag(namespaceName)), + ).Record(int64(n)) + } + } } // Check for HSM callbacks @@ -453,28 +494,18 @@ func buildCallbackInfosFromHSM( return result, nil } -// buildCallbackInfosFromChasm reads callbacks from the CHASM tree and converts them to API format. +// buildCallbackInfosFromChasm reads callbacks from the CHASM workflow component and converts them to API format. +// TODO(long-nt-tran): move this to chasm/lib/workflow/workflow.go to be within the CHASM workflow context. func buildCallbackInfosFromChasm( ctx context.Context, namespaceID namespace.ID, - mutableState historyi.MutableState, + wf *chasmworkflow.Workflow, + chasmCtx chasm.Context, executionInfo *persistencespb.WorkflowExecutionInfo, executionState *persistencespb.WorkflowExecutionState, outboundQueueCBPool *circuitbreakerpool.OutboundQueueCircuitBreakerPool, logger log.Logger, ) ([]*workflowpb.CallbackInfo, error) { - wf, chasmCtx, err := mutableState.ChasmWorkflowComponentReadOnly(ctx) - if err != nil { - logger.Error( - "failed to get workflow component from CHASM tree", - tag.WorkflowNamespaceID(namespaceID.String()), - tag.WorkflowID(executionInfo.WorkflowId), - tag.WorkflowRunID(executionState.RunId), - tag.Error(err), - ) - return nil, serviceerror.NewInternal("failed to construct describe response") - } - result := make([]*workflowpb.CallbackInfo, 0, len(wf.Callbacks)) for _, field := range wf.Callbacks { callback := field.Get(chasmCtx) diff --git a/service/history/api/link_util.go b/service/history/api/link_util.go new file mode 100644 index 0000000000..1c28ae0039 --- /dev/null +++ b/service/history/api/link_util.go @@ -0,0 +1,50 @@ +package api + +import ( + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + "go.temporal.io/server/common" +) + +// GenerateStartedEventRefLink builds a Link pointing to the WORKFLOW_EXECUTION_STARTED event. +// Use this for backlinks to workflow start: the started event is always EventId=1 (FirstEventID) +// and is never buffered, so a concrete EventReference is appropriate. +func GenerateStartedEventRefLink(namespace, workflowID, runID string) *commonpb.Link { + return &commonpb.Link{ + Variant: &commonpb.Link_WorkflowEvent_{ + WorkflowEvent: &commonpb.Link_WorkflowEvent{ + Namespace: namespace, + WorkflowId: workflowID, + RunId: runID, + Reference: &commonpb.Link_WorkflowEvent_EventRef{ + EventRef: &commonpb.Link_WorkflowEvent_EventReference{ + EventId: common.FirstEventID, + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, + }, + }, + }, + }, + } +} + +// GenerateRequestIDRefLink builds a Link with a RequestIdReference. +// Use this for events that are buffered at signal time (e.g. SIGNALED), where the +// concrete EventId is not yet known. The server resolves the RequestId to a real +// EventId once the buffer flushes. +func GenerateRequestIDRefLink(namespace, workflowID, runID, requestID string, eventType enumspb.EventType) *commonpb.Link { + return &commonpb.Link{ + Variant: &commonpb.Link_WorkflowEvent_{ + WorkflowEvent: &commonpb.Link_WorkflowEvent{ + Namespace: namespace, + WorkflowId: workflowID, + RunId: runID, + Reference: &commonpb.Link_WorkflowEvent_RequestIdRef{ + RequestIdRef: &commonpb.Link_WorkflowEvent_RequestIdReference{ + RequestId: requestID, + EventType: eventType, + }, + }, + }, + }, + } +} diff --git a/service/history/api/multioperation/api.go b/service/history/api/multioperation/api.go index 332945845a..91150502b6 100644 --- a/service/history/api/multioperation/api.go +++ b/service/history/api/multioperation/api.go @@ -10,7 +10,6 @@ import ( "go.temporal.io/api/serviceerror" "go.temporal.io/server/api/historyservice/v1" "go.temporal.io/server/api/matchingservice/v1" - "go.temporal.io/server/common" "go.temporal.io/server/common/definition" "go.temporal.io/server/common/locks" "go.temporal.io/server/common/namespace" @@ -336,20 +335,7 @@ func (uws *updateWithStart) updateWorkflow( RunId: currentWorkflowLease.GetContext().GetWorkflowKey().RunID, Started: false, // set explicitly for emphasis Status: enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING, - Link: &commonpb.Link{ - Variant: &commonpb.Link_WorkflowEvent_{ - WorkflowEvent: &commonpb.Link_WorkflowEvent{ - WorkflowId: wfKey.WorkflowID, - RunId: wfKey.RunID, - Reference: &commonpb.Link_WorkflowEvent_EventRef{ - EventRef: &commonpb.Link_WorkflowEvent_EventReference{ - EventId: common.FirstEventID, - EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, - }, - }, - }, - }, - }, + Link: api.GenerateStartedEventRefLink(uws.startReq.StartRequest.GetNamespace(), wfKey.WorkflowID, wfKey.RunID), } return makeResponse(startResp, updateResp), nil diff --git a/service/history/api/removesignalmutablestate/api.go b/service/history/api/removesignalmutablestate/api.go index 9ff4691549..d53c43204c 100644 --- a/service/history/api/removesignalmutablestate/api.go +++ b/service/history/api/removesignalmutablestate/api.go @@ -37,8 +37,11 @@ func Invoke( } mutableState.DeleteSignalRequested(req.GetRequestId()) + // This is a no-op if signals are stored in CHASM, since we'll ignore the deletion request, + // see DeleteSignalRequested implementation. + // TODO(long-nt-tran): Clean up code once ChasmSignalBacklinksEnabled is fully rolled out. return &api.UpdateWorkflowAction{ - Noop: false, + Noop: mutableState.ChasmSignalBacklinksEnabled(), CreateWorkflowTask: false, }, nil }, diff --git a/service/history/api/signalwithstartworkflow/api.go b/service/history/api/signalwithstartworkflow/api.go index 8fc6880f79..6ccc4e50ae 100644 --- a/service/history/api/signalwithstartworkflow/api.go +++ b/service/history/api/signalwithstartworkflow/api.go @@ -92,8 +92,16 @@ func Invoke( api.ReactivateVersionWorkflowIfPinned(ctx, namespaceEntry, request.GetVersioningOverride(), reactivationSignaler, shard.GetConfig().EnableVersionReactivationSignals(), shouldSkipReactivation, revisionNumber) } + swr := signalWithStartRequest.SignalWithStartRequest return &historyservice.SignalWithStartWorkflowExecutionResponse{ RunId: runID, Started: started, + SignalLink: api.GenerateRequestIDRefLink( + swr.GetNamespace(), + swr.GetWorkflowId(), + runID, + swr.GetRequestId(), + enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + ), }, nil } diff --git a/service/history/api/signalwithstartworkflow/signal_with_start_workflow.go b/service/history/api/signalwithstartworkflow/signal_with_start_workflow.go index 0510635ae5..c6a27ea92a 100644 --- a/service/history/api/signalwithstartworkflow/signal_with_start_workflow.go +++ b/service/history/api/signalwithstartworkflow/signal_with_start_workflow.go @@ -307,6 +307,7 @@ func signalWorkflow( request.GetSignalInput(), request.GetIdentity(), request.GetHeader(), + request.GetRequestId(), request.GetLinks(), ); err != nil { return err diff --git a/service/history/api/signalwithstartworkflow/signal_with_start_workflow_test.go b/service/history/api/signalwithstartworkflow/signal_with_start_workflow_test.go index 3beeb2e7d3..91b89a497f 100644 --- a/service/history/api/signalwithstartworkflow/signal_with_start_workflow_test.go +++ b/service/history/api/signalwithstartworkflow/signal_with_start_workflow_test.go @@ -140,6 +140,7 @@ func (s *signalWithStartWorkflowSuite) TestSignalWorkflow_NewWorkflowTask() { request.GetSignalInput(), request.GetIdentity(), request.GetHeader(), + request.GetRequestId(), request.GetLinks(), ).Return(&historypb.HistoryEvent{}, nil) s.currentMutableState.EXPECT().HasPendingWorkflowTask().Return(false) @@ -174,6 +175,7 @@ func (s *signalWithStartWorkflowSuite) TestSignalWorkflow_NoNewWorkflowTask() { request.GetSignalInput(), request.GetIdentity(), request.GetHeader(), + request.GetRequestId(), request.GetLinks(), ).Return(&historypb.HistoryEvent{}, nil) s.currentMutableState.EXPECT().HasPendingWorkflowTask().Return(true) @@ -207,6 +209,7 @@ func (s *signalWithStartWorkflowSuite) TestSignalWorkflow_WhenPaused() { request.GetSignalInput(), request.GetIdentity(), request.GetHeader(), + request.GetRequestId(), request.GetLinks(), ).Return(&historypb.HistoryEvent{}, nil) s.currentMutableState.EXPECT().HasPendingWorkflowTask().Return(false) diff --git a/service/history/api/signalworkflow/api.go b/service/history/api/signalworkflow/api.go index f9e112c076..a7cd4487b8 100644 --- a/service/history/api/signalworkflow/api.go +++ b/service/history/api/signalworkflow/api.go @@ -3,6 +3,7 @@ package signalworkflow import ( "context" + enumspb "go.temporal.io/api/enums/v1" "go.temporal.io/server/api/historyservice/v1" "go.temporal.io/server/common/definition" "go.temporal.io/server/common/namespace" @@ -27,6 +28,9 @@ func Invoke( externalWorkflowExecution := req.ExternalWorkflowExecution childWorkflowOnly := req.GetChildWorkflowOnly() + // Capture the currently-running workflow's runID from mutableState via our closure, + // in case the caller didn't pin the signal to a specific runID. + var runID string err = api.GetAndUpdateWorkflowWithNew( ctx, nil, @@ -37,6 +41,7 @@ func Invoke( ), func(workflowLease api.WorkflowLease) (*api.UpdateWorkflowAction, error) { mutableState := workflowLease.GetMutableState() + runID = mutableState.GetExecutionState().GetRunId() if request.GetRequestId() != "" && mutableState.IsSignalRequested(request.GetRequestId()) { return &api.UpdateWorkflowAction{ Noop: true, @@ -88,6 +93,7 @@ func Invoke( request.GetIdentity(), request.GetHeader(), externalWorkflowExecution, + request.GetRequestId(), request.GetLinks(), ) if err != nil { @@ -106,5 +112,13 @@ func Invoke( if err != nil { return nil, err } - return &historyservice.SignalWorkflowExecutionResponse{}, nil + return &historyservice.SignalWorkflowExecutionResponse{ + Link: api.GenerateRequestIDRefLink( + request.GetNamespace(), + request.GetWorkflowExecution().GetWorkflowId(), + runID, + request.GetRequestId(), + enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + ), + }, nil } diff --git a/service/history/api/startworkflow/api.go b/service/history/api/startworkflow/api.go index f5f2d25d00..5127311a50 100644 --- a/service/history/api/startworkflow/api.go +++ b/service/history/api/startworkflow/api.go @@ -13,7 +13,6 @@ import ( "go.temporal.io/server/api/historyservice/v1" "go.temporal.io/server/api/matchingservice/v1" "go.temporal.io/server/chasm" - "go.temporal.io/server/common" "go.temporal.io/server/common/definition" "go.temporal.io/server/common/locks" "go.temporal.io/server/common/metrics" @@ -816,39 +815,21 @@ func (s *Starter) generateResponse( } func (s *Starter) generateStartedEventRefLink(runID string) *commonpb.Link { - return &commonpb.Link{ - Variant: &commonpb.Link_WorkflowEvent_{ - WorkflowEvent: &commonpb.Link_WorkflowEvent{ - Namespace: s.namespace.Name().String(), - WorkflowId: s.request.StartRequest.WorkflowId, - RunId: runID, - Reference: &commonpb.Link_WorkflowEvent_EventRef{ - EventRef: &commonpb.Link_WorkflowEvent_EventReference{ - EventId: common.FirstEventID, - EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, - }, - }, - }, - }, - } + return api.GenerateStartedEventRefLink( + s.namespace.Name().String(), + s.request.StartRequest.WorkflowId, + runID, + ) } func (s *Starter) generateRequestIdRefLink(runID string) *commonpb.Link { - return &commonpb.Link{ - Variant: &commonpb.Link_WorkflowEvent_{ - WorkflowEvent: &commonpb.Link_WorkflowEvent{ - Namespace: s.namespace.Name().String(), - WorkflowId: s.request.StartRequest.WorkflowId, - RunId: runID, - Reference: &commonpb.Link_WorkflowEvent_RequestIdRef{ - RequestIdRef: &commonpb.Link_WorkflowEvent_RequestIdReference{ - RequestId: s.request.StartRequest.RequestId, - EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED, - }, - }, - }, - }, - } + return api.GenerateRequestIDRefLink( + s.namespace.Name().String(), + s.request.StartRequest.WorkflowId, + runID, + s.request.StartRequest.RequestId, + enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED, + ) } func (s StartOutcome) String() string { diff --git a/service/history/configs/config.go b/service/history/configs/config.go index d932f4feee..57cc6ae1e1 100644 --- a/service/history/configs/config.go +++ b/service/history/configs/config.go @@ -72,6 +72,7 @@ type Config struct { MaxCallbacksPerExecution dynamicconfig.IntPropertyFnWithNamespaceFilter EnableChasm dynamicconfig.BoolPropertyFnWithNamespaceFilter EnableCHASMCallbacks dynamicconfig.BoolPropertyFnWithNamespaceFilter + EnableCHASMSignalBacklinks dynamicconfig.BoolPropertyFnWithNamespaceFilter ChasmMaxInMemoryPureTasks dynamicconfig.IntPropertyFn EnableCHASMSchedulerCreation dynamicconfig.BoolPropertyFnWithNamespaceFilter EnableCHASMSchedulerMigration dynamicconfig.BoolPropertyFnWithNamespaceFilter @@ -500,8 +501,9 @@ func NewConfig( EnableCHASMSchedulerCreation: dynamicconfig.EnableCHASMSchedulerCreation.Get(dc), EnableCHASMSchedulerMigration: dynamicconfig.EnableCHASMSchedulerMigration.Get(dc), - EnableCHASMCallbacks: dynamicconfig.EnableCHASMCallbacks.Get(dc), - ExternalPayloadsEnabled: dynamicconfig.ExternalPayloadsEnabled.Get(dc), + EnableCHASMCallbacks: dynamicconfig.EnableCHASMCallbacks.Get(dc), + EnableCHASMSignalBacklinks: dynamicconfig.EnableCHASMSignalBacklinks.Get(dc), + ExternalPayloadsEnabled: dynamicconfig.ExternalPayloadsEnabled.Get(dc), EventsShardLevelCacheMaxSizeBytes: dynamicconfig.EventsCacheMaxSizeBytes.Get(dc), // 512KB EventsHostLevelCacheMaxSizeBytes: dynamicconfig.EventsHostLevelCacheMaxSizeBytes.Get(dc), // 256MB diff --git a/service/history/history_engine_test.go b/service/history/history_engine_test.go index 7cf162b298..a201a6628a 100644 --- a/service/history/history_engine_test.go +++ b/service/history/history_engine_test.go @@ -34,6 +34,7 @@ import ( tokenspb "go.temporal.io/server/api/token/v1" workflowspb "go.temporal.io/server/api/workflow/v1" "go.temporal.io/server/chasm" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/common" "go.temporal.io/server/common/clock" "go.temporal.io/server/common/cluster" @@ -5005,97 +5006,173 @@ func (s *engineSuite) TestSignalWorkflowExecution() { // Test signal workflow task by adding request ID func (s *engineSuite) TestSignalWorkflowExecution_DuplicateRequest() { - we := commonpb.WorkflowExecution{ - WorkflowId: "wId2", - RunId: tests.RunID, - } - signalRequest := &historyservice.SignalWorkflowExecutionRequest{ - SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{ - WorkflowExecution: &we, - }, - } - _, err := s.historyEngine.SignalWorkflowExecution(context.Background(), signalRequest) + // Verify error when namespace is missing (independent of CHASM flag). + _, err := s.historyEngine.SignalWorkflowExecution(context.Background(), &historyservice.SignalWorkflowExecutionRequest{ + SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{WorkflowExecution: &commonpb.WorkflowExecution{ + WorkflowId: "wId2", + RunId: tests.RunID, + }}, + }) s.EqualError(err, "Missing namespace UUID.") - taskqueue := "testTaskQueue" - identity := "testIdentity" - signalName := "my signal name 2" - input := payloads.EncodeString("test input 2") - requestID := uuid.NewString() - signalRequest = &historyservice.SignalWorkflowExecutionRequest{ - NamespaceId: tests.NamespaceID.String(), - SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{ - Namespace: tests.NamespaceID.String(), - WorkflowExecution: &we, - Identity: identity, - SignalName: signalName, - Input: input, - RequestId: requestID, - }, + for _, tc := range []struct { + name string + chasmEnabled bool + }{ + {name: "Legacy", chasmEnabled: false}, + {name: "Chasm", chasmEnabled: true}, + } { + tc := tc + s.Run(tc.name, func() { + // Use a unique RunId per sub-test to avoid workflow cache collisions + // between the Legacy and Chasm sub-tests. + we := commonpb.WorkflowExecution{ + WorkflowId: "wId2", + RunId: uuid.NewString(), + } + + if tc.chasmEnabled { + s.config.EnableChasm = dynamicconfig.GetBoolPropertyFnFilteredByNamespace(true) + s.config.EnableCHASMSignalBacklinks = dynamicconfig.GetBoolPropertyFnFilteredByNamespace(true) + reg := s.mockShard.ChasmRegistry() + s.NoError(reg.Register(&chasm.CoreLibrary{})) + s.NoError(reg.Register(chasmworkflow.NewLibrary(chasmworkflow.NewRegistry()))) + } + + requestID := uuid.NewString() + signalRequest := &historyservice.SignalWorkflowExecutionRequest{ + NamespaceId: tests.NamespaceID.String(), + SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: tests.NamespaceID.String(), + WorkflowExecution: &we, + Identity: "testIdentity", + SignalName: "my signal name 2", + Input: payloads.EncodeString("test input 2"), + RequestId: requestID, + }, + } + + ms := workflow.TestLocalMutableState(s.historyEngine.shardContext, s.eventsCache, + tests.LocalNamespaceEntry, we.GetWorkflowId(), we.GetRunId(), log.NewTestLogger()) + addWorkflowExecutionStartedEvent(ms, &we, "wType", "testTaskQueue", payloads.EncodeString("input"), 100*time.Second, 50*time.Second, 200*time.Second, "testIdentity") + addWorkflowTaskScheduledEvent(ms) + + if tc.chasmEnabled { + // CHASM path: populate the CHASM IncomingSignals map with the requestID so that + // IsSignalRequested returns true for this ID when the DB record is loaded. + s.NoError(ms.ApplyWorkflowExecutionSignaled(&historypb.HistoryEvent{ + EventId: common.BufferedEventID, + EventTime: timestamppb.New(time.Now()), + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + Attributes: &historypb.HistoryEvent_WorkflowExecutionSignaledEventAttributes{ + WorkflowExecutionSignaledEventAttributes: &historypb.WorkflowExecutionSignaledEventAttributes{ + SignalName: "my signal name 2", + RequestId: requestID, + }, + }, + })) + } + + wfMs := workflow.TestCloneToProto(context.Background(), ms) + if !tc.chasmEnabled { + // Legacy path: dedup via the SignalRequestedIds set field. + wfMs.SignalRequestedIds = []string{requestID} + } + wfMs.ExecutionInfo.NamespaceId = tests.NamespaceID.String() + gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: wfMs} + + s.mockExecutionMgr.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(gwmsResponse, nil) + + _, err := s.historyEngine.SignalWorkflowExecution(context.Background(), signalRequest) + s.NoError(err) + }) } - - ms := workflow.TestLocalMutableState(s.historyEngine.shardContext, s.eventsCache, - tests.LocalNamespaceEntry, we.GetWorkflowId(), we.GetRunId(), log.NewTestLogger()) - addWorkflowExecutionStartedEvent(ms, &we, "wType", taskqueue, payloads.EncodeString("input"), 100*time.Second, 50*time.Second, 200*time.Second, identity) - addWorkflowTaskScheduledEvent(ms) - wfMs := workflow.TestCloneToProto(context.Background(), ms) - // assume duplicate request id - wfMs.SignalRequestedIds = []string{requestID} - wfMs.ExecutionInfo.NamespaceId = tests.NamespaceID.String() - gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: wfMs} - - s.mockExecutionMgr.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(gwmsResponse, nil) - - _, err = s.historyEngine.SignalWorkflowExecution(context.Background(), signalRequest) - s.Nil(err) } // Test signal workflow task by dedup request ID & workflow finished func (s *engineSuite) TestSignalWorkflowExecution_DuplicateRequest_Completed() { - we := commonpb.WorkflowExecution{ - WorkflowId: "wId2", - RunId: tests.RunID, - } - signalRequest := &historyservice.SignalWorkflowExecutionRequest{ - SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{ - WorkflowExecution: &we, - }, - } - _, err := s.historyEngine.SignalWorkflowExecution(context.Background(), signalRequest) + // Verify error when namespace is missing (independent of CHASM flag). + _, err := s.historyEngine.SignalWorkflowExecution(context.Background(), &historyservice.SignalWorkflowExecutionRequest{ + SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{WorkflowExecution: &commonpb.WorkflowExecution{ + WorkflowId: "wId2", + RunId: tests.RunID, + }}, + }) s.EqualError(err, "Missing namespace UUID.") - taskqueue := "testTaskQueue" - identity := "testIdentity" - signalName := "my signal name 2" - input := payloads.EncodeString("test input 2") - requestID := uuid.NewString() - signalRequest = &historyservice.SignalWorkflowExecutionRequest{ - NamespaceId: tests.NamespaceID.String(), - SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{ - Namespace: tests.NamespaceID.String(), - WorkflowExecution: &we, - Identity: identity, - SignalName: signalName, - Input: input, - RequestId: requestID, - }, + for _, tc := range []struct { + name string + chasmEnabled bool + }{ + {name: "Legacy", chasmEnabled: false}, + {name: "Chasm", chasmEnabled: true}, + } { + tc := tc + s.Run(tc.name, func() { + // Use a unique RunId per sub-test to avoid workflow cache collisions + // between the Legacy and Chasm sub-tests. + we := commonpb.WorkflowExecution{ + WorkflowId: "wId2", + RunId: uuid.NewString(), + } + + if tc.chasmEnabled { + s.config.EnableChasm = dynamicconfig.GetBoolPropertyFnFilteredByNamespace(true) + s.config.EnableCHASMSignalBacklinks = dynamicconfig.GetBoolPropertyFnFilteredByNamespace(true) + reg := s.mockShard.ChasmRegistry() + s.NoError(reg.Register(&chasm.CoreLibrary{})) + s.NoError(reg.Register(chasmworkflow.NewLibrary(chasmworkflow.NewRegistry()))) + } + + requestID := uuid.NewString() + signalRequest := &historyservice.SignalWorkflowExecutionRequest{ + NamespaceId: tests.NamespaceID.String(), + SignalRequest: &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: tests.NamespaceID.String(), + WorkflowExecution: &we, + Identity: "testIdentity", + SignalName: "my signal name 2", + Input: payloads.EncodeString("test input 2"), + RequestId: requestID, + }, + } + + ms := workflow.TestLocalMutableState(s.historyEngine.shardContext, s.eventsCache, + tests.LocalNamespaceEntry, we.GetWorkflowId(), we.GetRunId(), log.NewTestLogger()) + addWorkflowExecutionStartedEvent(ms, &we, "wType", "testTaskQueue", payloads.EncodeString("input"), 100*time.Second, 50*time.Second, 200*time.Second, "testIdentity") + addWorkflowTaskScheduledEvent(ms) + + if tc.chasmEnabled { + // CHASM path: populate the CHASM IncomingSignals map with the requestID so that + // IsSignalRequested returns true for this ID when the DB record is loaded. + s.NoError(ms.ApplyWorkflowExecutionSignaled(&historypb.HistoryEvent{ + EventId: common.BufferedEventID, + EventTime: timestamppb.New(time.Now()), + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + Attributes: &historypb.HistoryEvent_WorkflowExecutionSignaledEventAttributes{ + WorkflowExecutionSignaledEventAttributes: &historypb.WorkflowExecutionSignaledEventAttributes{ + SignalName: "my signal name 2", + RequestId: requestID, + }, + }, + })) + } + + wfMs := workflow.TestCloneToProto(context.Background(), ms) + if !tc.chasmEnabled { + // Legacy path: dedup via the SignalRequestedIds set field. + wfMs.SignalRequestedIds = []string{requestID} + } + wfMs.ExecutionInfo.NamespaceId = tests.NamespaceID.String() + wfMs.ExecutionState.State = enumsspb.WORKFLOW_EXECUTION_STATE_COMPLETED + gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: wfMs} + + s.mockExecutionMgr.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(gwmsResponse, nil) + + _, err := s.historyEngine.SignalWorkflowExecution(context.Background(), signalRequest) + s.NoError(err) + }) } - - ms := workflow.TestLocalMutableState(s.historyEngine.shardContext, s.eventsCache, - tests.LocalNamespaceEntry, we.GetWorkflowId(), we.GetRunId(), log.NewTestLogger()) - addWorkflowExecutionStartedEvent(ms, &we, "wType", taskqueue, payloads.EncodeString("input"), 100*time.Second, 50*time.Second, 200*time.Second, identity) - addWorkflowTaskScheduledEvent(ms) - wfMs := workflow.TestCloneToProto(context.Background(), ms) - // assume duplicate request id - wfMs.SignalRequestedIds = []string{requestID} - wfMs.ExecutionInfo.NamespaceId = tests.NamespaceID.String() - wfMs.ExecutionState.State = enumsspb.WORKFLOW_EXECUTION_STATE_COMPLETED - gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: wfMs} - - s.mockExecutionMgr.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(gwmsResponse, nil) - - _, err = s.historyEngine.SignalWorkflowExecution(context.Background(), signalRequest) - s.Nil(err) } func (s *engineSuite) TestSignalWorkflowExecution_Failed() { diff --git a/service/history/historybuilder/event_factory.go b/service/history/historybuilder/event_factory.go index c6368ad954..0d2cdb0469 100644 --- a/service/history/historybuilder/event_factory.go +++ b/service/history/historybuilder/event_factory.go @@ -819,6 +819,7 @@ func (b *EventFactory) CreateWorkflowExecutionSignaledEvent( identity string, header *commonpb.Header, externalWorkflowExecution *commonpb.WorkflowExecution, + requestID string, links []*commonpb.Link, ) *historypb.HistoryEvent { event := b.createHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, b.timeSource.Now()) @@ -829,6 +830,7 @@ func (b *EventFactory) CreateWorkflowExecutionSignaledEvent( Identity: identity, Header: header, ExternalWorkflowExecution: externalWorkflowExecution, + RequestId: requestID, }, } event.Links = links diff --git a/service/history/historybuilder/event_store.go b/service/history/historybuilder/event_store.go index 3bdec9807a..8e00db4643 100644 --- a/service/history/historybuilder/event_store.go +++ b/service/history/historybuilder/event_store.go @@ -405,6 +405,12 @@ func (b *EventStore) wireEventIDs( if attributes.GetAttachedRequestId() != "" { b.requestIDToEventID[attributes.AttachedRequestId] = event.GetEventId() } + + case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: + attributes := event.GetWorkflowExecutionSignaledEventAttributes() + if attributes.GetRequestId() != "" { + b.requestIDToEventID[attributes.RequestId] = event.GetEventId() + } } } } diff --git a/service/history/historybuilder/history_builder.go b/service/history/historybuilder/history_builder.go index 6da2fc185e..35416a4839 100644 --- a/service/history/historybuilder/history_builder.go +++ b/service/history/historybuilder/history_builder.go @@ -752,6 +752,7 @@ func (b *HistoryBuilder) AddWorkflowExecutionSignaledEvent( identity string, header *commonpb.Header, externalWorkflowExecution *commonpb.WorkflowExecution, + requestID string, links []*commonpb.Link, ) *historypb.HistoryEvent { event := b.EventFactory.CreateWorkflowExecutionSignaledEvent( @@ -760,6 +761,7 @@ func (b *HistoryBuilder) AddWorkflowExecutionSignaledEvent( identity, header, externalWorkflowExecution, + requestID, links, ) event, _ = b.EventStore.add(event) diff --git a/service/history/historybuilder/history_builder_categorization_test.go b/service/history/historybuilder/history_builder_categorization_test.go index 97e825ed7f..14a9029a1c 100644 --- a/service/history/historybuilder/history_builder_categorization_test.go +++ b/service/history/historybuilder/history_builder_categorization_test.go @@ -223,6 +223,29 @@ func TestHistoryBuilder_FlushBufferToCurrentBatch(t *testing.T) { } }) + t.Run("signal requestID should be wired into requestIDToEventID map after flush", func(t *testing.T) { + nextEventID := int64(12) + hb := newHistoryBuilderFromConfig(builderConfig{nextEventId: nextEventID}) + // Signal events are buffered (go to memBufferBatch) + signalEvent := hb.AddWorkflowExecutionSignaledEvent("signal-name", nil, "identity-1", nil, nil, "signal-request-id", nil) + if signalEvent.EventId != common.BufferedEventID { + t.Fatalf("expected signal to be buffered, got event id %d", signalEvent.EventId) + } + + _, requestIDToEventID := hb.FlushBufferToCurrentBatch() + + if signalEvent.EventId != nextEventID { + t.Errorf("expected signal event id %d after flush, got %d", nextEventID, signalEvent.EventId) + } + eventID, ok := requestIDToEventID["signal-request-id"] + if !ok { + t.Fatal("signal requestID not found in requestIDToEventID map after flush") + } + if eventID != nextEventID { + t.Errorf("expected requestIDToEventID[signal-request-id] == %d, got %d", nextEventID, eventID) + } + }) + t.Run("when there is ACTIVITY_TASK_COMPLETED event will move it to the end", func(t *testing.T) { hb := newHistoryBuilderFromConfig(builderConfig{nextEventId: 12}) hb.AddActivityTaskCompletedEvent(14, 13, "activity-completed", nil, defaultNamespace) @@ -1473,6 +1496,7 @@ func (s *sutTestingAdapter) AddWorkflowExecutionSignaledEvent(_ ...eventConfig) "identity-1", nil, nil, + "", nil, ) } diff --git a/service/history/historybuilder/history_builder_test.go b/service/history/historybuilder/history_builder_test.go index f87cfdf055..1e66efb649 100644 --- a/service/history/historybuilder/history_builder_test.go +++ b/service/history/historybuilder/history_builder_test.go @@ -332,7 +332,7 @@ func (s *historyBuilderSuite) TestWorkflowExecutionCancelRequested() { func (s *historyBuilderSuite) TestWorkflowExecutionSignaled() { signalName := "random signal name" event := s.historyBuilder.AddWorkflowExecutionSignaledEvent( - signalName, testPayloads, testIdentity, testHeader, nil, nil, + signalName, testPayloads, testIdentity, testHeader, nil, "", nil, ) s.Equal(event, s.flush()) s.Equal(&historypb.HistoryEvent{ @@ -2372,6 +2372,7 @@ func (s *historyBuilderSuite) TestBufferSize_Memory() { "identity", &commonpb.Header{}, nil, + "", nil, ) s.Assert().Equal(1, s.historyBuilder.NumBufferedEvents()) diff --git a/service/history/interfaces/mutable_state.go b/service/history/interfaces/mutable_state.go index 277368a26e..a0b85253cf 100644 --- a/service/history/interfaces/mutable_state.go +++ b/service/history/interfaces/mutable_state.go @@ -103,6 +103,7 @@ type ( input *commonpb.Payloads, identity string, header *commonpb.Header, + requestID string, links []*commonpb.Link, ) (*historypb.HistoryEvent, error) AddWorkflowExecutionSignaledEvent( @@ -111,6 +112,7 @@ type ( identity string, header *commonpb.Header, externalWorkflowExecution *commonpb.WorkflowExecution, + requestID string, links []*commonpb.Link, ) (*historypb.HistoryEvent, error) AddWorkflowExecutionStartedEvent(*commonpb.WorkflowExecution, *historyservice.StartWorkflowExecutionRequest) (*historypb.HistoryEvent, error) @@ -345,6 +347,7 @@ type ( IsWorkflow() bool ChasmTree() ChasmTree ChasmEnabled() bool + ChasmSignalBacklinksEnabled() bool ChasmWorkflowComponent(ctx context.Context) (*chasmworkflow.Workflow, chasm.MutableContext, error) ChasmWorkflowComponentReadOnly(ctx context.Context) (*chasmworkflow.Workflow, chasm.Context, error) // Ensures that the chasm workflow component is installed in the mutable state CHASM tree. diff --git a/service/history/interfaces/mutable_state_mock.go b/service/history/interfaces/mutable_state_mock.go index 5fcba16e81..20378e67ac 100644 --- a/service/history/interfaces/mutable_state_mock.go +++ b/service/history/interfaces/mutable_state_mock.go @@ -708,33 +708,33 @@ func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionPausedEvent(identity } // AddWorkflowExecutionSignaled mocks base method. -func (m *MockMutableState) AddWorkflowExecutionSignaled(signalName string, input *common.Payloads, identity string, header *common.Header, links []*common.Link) (*history.HistoryEvent, error) { +func (m *MockMutableState) AddWorkflowExecutionSignaled(signalName string, input *common.Payloads, identity string, header *common.Header, requestID string, links []*common.Link) (*history.HistoryEvent, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddWorkflowExecutionSignaled", signalName, input, identity, header, links) + ret := m.ctrl.Call(m, "AddWorkflowExecutionSignaled", signalName, input, identity, header, requestID, links) ret0, _ := ret[0].(*history.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionSignaled indicates an expected call of AddWorkflowExecutionSignaled. -func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionSignaled(signalName, input, identity, header, links any) *gomock.Call { +func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionSignaled(signalName, input, identity, header, requestID, links any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionSignaled", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionSignaled), signalName, input, identity, header, links) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionSignaled", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionSignaled), signalName, input, identity, header, requestID, links) } // AddWorkflowExecutionSignaledEvent mocks base method. -func (m *MockMutableState) AddWorkflowExecutionSignaledEvent(signalName string, input *common.Payloads, identity string, header *common.Header, externalWorkflowExecution *common.WorkflowExecution, links []*common.Link) (*history.HistoryEvent, error) { +func (m *MockMutableState) AddWorkflowExecutionSignaledEvent(signalName string, input *common.Payloads, identity string, header *common.Header, externalWorkflowExecution *common.WorkflowExecution, requestID string, links []*common.Link) (*history.HistoryEvent, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddWorkflowExecutionSignaledEvent", signalName, input, identity, header, externalWorkflowExecution, links) + ret := m.ctrl.Call(m, "AddWorkflowExecutionSignaledEvent", signalName, input, identity, header, externalWorkflowExecution, requestID, links) ret0, _ := ret[0].(*history.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionSignaledEvent indicates an expected call of AddWorkflowExecutionSignaledEvent. -func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionSignaledEvent(signalName, input, identity, header, externalWorkflowExecution, links any) *gomock.Call { +func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionSignaledEvent(signalName, input, identity, header, externalWorkflowExecution, requestID, links any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionSignaledEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionSignaledEvent), signalName, input, identity, header, externalWorkflowExecution, links) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionSignaledEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionSignaledEvent), signalName, input, identity, header, externalWorkflowExecution, requestID, links) } // AddWorkflowExecutionStartedEvent mocks base method. @@ -1722,6 +1722,20 @@ func (mr *MockMutableStateMockRecorder) ChasmEnabled() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChasmEnabled", reflect.TypeOf((*MockMutableState)(nil).ChasmEnabled)) } +// ChasmSignalBacklinksEnabled mocks base method. +func (m *MockMutableState) ChasmSignalBacklinksEnabled() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChasmSignalBacklinksEnabled") + ret0, _ := ret[0].(bool) + return ret0 +} + +// ChasmSignalBacklinksEnabled indicates an expected call of ChasmSignalBacklinksEnabled. +func (mr *MockMutableStateMockRecorder) ChasmSignalBacklinksEnabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChasmSignalBacklinksEnabled", reflect.TypeOf((*MockMutableState)(nil).ChasmSignalBacklinksEnabled)) +} + // ChasmTree mocks base method. func (m *MockMutableState) ChasmTree() ChasmTree { m.ctrl.T.Helper() diff --git a/service/history/ndc/events_reapplier_test.go b/service/history/ndc/events_reapplier_test.go index ce3c4b88a4..2f5995dc16 100644 --- a/service/history/ndc/events_reapplier_test.go +++ b/service/history/ndc/events_reapplier_test.go @@ -217,6 +217,7 @@ func (s *nDCEventReapplicationSuite) TestReapplyEvents_AppliedEvent_Signal() { attr.GetInput(), attr.GetIdentity(), attr.GetHeader(), + "", event.Links, ).Return(event, nil) msCurrent.EXPECT().HSM().Return(s.hsmNode).AnyTimes() @@ -361,6 +362,7 @@ func (s *nDCEventReapplicationSuite) TestReapplyEvents_PartialAppliedEvent() { attr1.GetInput(), attr1.GetIdentity(), attr1.GetHeader(), + "", event1.Links, ).Return(event1, nil) msCurrent.EXPECT().IsWorkflowPendingOnWorkflowTaskBackoff().Return(true) @@ -408,6 +410,7 @@ func (s *nDCEventReapplicationSuite) TestReapplyEvents_Error() { attr.GetInput(), attr.GetIdentity(), attr.GetHeader(), + "", event.Links, ).Return(nil, fmt.Errorf("test")) dedupResource := definition.NewEventReappliedID(runID, event.GetEventId(), event.GetVersion()) @@ -507,6 +510,7 @@ func (s *nDCEventReapplicationSuite) TestReapplyEvents_AppliedEvent_NoPendingWor attr.GetInput(), attr.GetIdentity(), attr.GetHeader(), + "", event.Links, ).Return(event, nil) msCurrent.EXPECT().HSM().Return(s.hsmNode).AnyTimes() @@ -560,6 +564,7 @@ func (s *nDCEventReapplicationSuite) TestReapplyEvents_PausedWorkflow_NoWorkflow attr.GetInput(), attr.GetIdentity(), attr.GetHeader(), + "", event.Links, ).Return(event, nil) msCurrent.EXPECT().HSM().Return(s.hsmNode).AnyTimes() diff --git a/service/history/ndc/workflow_resetter.go b/service/history/ndc/workflow_resetter.go index d0e6bbf610..26d7fb1c53 100644 --- a/service/history/ndc/workflow_resetter.go +++ b/service/history/ndc/workflow_resetter.go @@ -873,6 +873,7 @@ func reapplyEvents( attr.GetInput(), attr.GetIdentity(), attr.GetHeader(), + attr.GetRequestId(), event.Links, ); err != nil { return reappliedEvents, err diff --git a/service/history/ndc/workflow_resetter_test.go b/service/history/ndc/workflow_resetter_test.go index 21118ce1f4..4da5bcfc06 100644 --- a/service/history/ndc/workflow_resetter_test.go +++ b/service/history/ndc/workflow_resetter_test.go @@ -1045,6 +1045,7 @@ func (s *workflowResetterSuite) TestReapplyEvents() { Input: payloads.EncodeString("signal-input-1"), Identity: "signal-identity-1", Header: &commonpb.Header{Fields: map[string]*commonpb.Payload{"myheader": {Data: []byte("myheader")}}}, + RequestId: "signal-request-id-1", }}, } // This event is not reapplied @@ -1063,6 +1064,7 @@ func (s *workflowResetterSuite) TestReapplyEvents() { SignalName: "signal-name-2", Input: payloads.EncodeString("signal-input-2"), Identity: "signal-identity-2", + RequestId: "signal-request-id-2", }, }, } @@ -1213,6 +1215,7 @@ func (s *workflowResetterSuite) TestReapplyEvents() { attr.GetInput(), attr.GetIdentity(), attr.GetHeader(), + attr.GetRequestId(), event.Links, ).Return(&historypb.HistoryEvent{}, nil) case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: @@ -1315,7 +1318,7 @@ func (s *workflowResetterSuite) TestReapplyEvents_Excludes() { ms := historyi.NewMockMutableState(s.controller) // Assert that none of these following methods are invoked. arg := gomock.Any() - ms.EXPECT().AddWorkflowExecutionSignaled(arg, arg, arg, arg, arg).Times(0) + ms.EXPECT().AddWorkflowExecutionSignaled(arg, arg, arg, arg, arg, arg).Times(0) ms.EXPECT().AddWorkflowExecutionUpdateAdmittedEvent(arg, arg).Times(0) ms.EXPECT().AddHistoryEvent(arg, arg).Times(0) diff --git a/service/history/workflow/mutable_state_impl.go b/service/history/workflow/mutable_state_impl.go index 4339f4d39f..1e4d679feb 100644 --- a/service/history/workflow/mutable_state_impl.go +++ b/service/history/workflow/mutable_state_impl.go @@ -684,6 +684,11 @@ func (ms *MutableStateImpl) chasmCallbacksEnabled() bool { return ms.shard.GetConfig().EnableCHASMCallbacks(ms.GetNamespaceEntry().Name().String()) } +// ChasmSignalBacklinksEnabled returns true if CHASM-based signal requestID backlink tracking is enabled. +func (ms *MutableStateImpl) ChasmSignalBacklinksEnabled() bool { + return ms.ChasmEnabled() && ms.shard.GetConfig().EnableCHASMSignalBacklinks(ms.GetNamespaceEntry().Name().String()) +} + // ChasmWorkflowComponent gets the root workflow component from the CHASM tree. // Returns the workflow component (which is *chasmworkflow.Workflow) and the CHASM mutable context. // This method is for write operations. Callers can type assert to *chasmworkflow.Workflow if needed. @@ -2430,8 +2435,22 @@ func (ms *MutableStateImpl) IsWorkflowCloseAttempted() bool { func (ms *MutableStateImpl) IsSignalRequested( requestID string, ) bool { - _, ok := ms.pendingSignalRequestedIDs[requestID] - return ok + // First check CHASM map, then fallback to existing set fields -- will be cleaned up once we + // fully ramp the writes to CHASM only. + signalExists := false + if ms.ChasmSignalBacklinksEnabled() { + wf, chasmCtx, err := ms.ChasmWorkflowComponentReadOnly(context.Background()) + if err != nil { + softassert.Fail(ms.logger, fmt.Sprintf("Unexpected error reading CHASM component: %v", err)) + } + signalExists = wf.HasIncomingSignalEvent(chasmCtx, requestID) + } + + // TODO(long-nt-tran): Remove fallback to existing map once we fully roll out writes to CHASM signals map + if !signalExists { + _, signalExists = ms.pendingSignalRequestedIDs[requestID] + } + return signalExists } func (ms *MutableStateImpl) IsWorkflowPendingOnWorkflowTaskBackoff() bool { @@ -2455,6 +2474,12 @@ func (ms *MutableStateImpl) GetApproximatePersistedSize() int { func (ms *MutableStateImpl) AddSignalRequested( requestID string, ) { + if ms.ChasmSignalBacklinksEnabled() { + // Signal deduplication is managed by CHASM IncomingSignals; the CHASM write + // happens in ApplyWorkflowExecutionSignaled. + // TODO(long-nt-tran): Cleanup this path after ChasmSignalBacklinksEnabled is rolled out. + return + } if ms.pendingSignalRequestedIDs == nil { ms.pendingSignalRequestedIDs = make(map[string]struct{}) } @@ -2469,6 +2494,11 @@ func (ms *MutableStateImpl) AddSignalRequested( func (ms *MutableStateImpl) DeleteSignalRequested( requestID string, ) { + if ms.ChasmSignalBacklinksEnabled() { + // Signal IDs are kept in CHASM IncomingSignals for backlink resolution. + // TODO(long-nt-tran): Clean up this path after config is rolled out. + return + } delete(ms.pendingSignalRequestedIDs, requestID) delete(ms.updateSignalRequestedIDs, requestID) ms.deleteSignalRequestedIDs[requestID] = struct{}{} @@ -5770,6 +5800,7 @@ func (ms *MutableStateImpl) AddWorkflowExecutionSignaled( input *commonpb.Payloads, identity string, header *commonpb.Header, + requestID string, links []*commonpb.Link, ) (*historypb.HistoryEvent, error) { return ms.AddWorkflowExecutionSignaledEvent( @@ -5778,6 +5809,7 @@ func (ms *MutableStateImpl) AddWorkflowExecutionSignaled( identity, header, nil, + requestID, links, ) } @@ -5788,6 +5820,7 @@ func (ms *MutableStateImpl) AddWorkflowExecutionSignaledEvent( identity string, header *commonpb.Header, externalWorkflowExecution *commonpb.WorkflowExecution, + requestID string, links []*commonpb.Link, ) (*historypb.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowSignaled @@ -5801,6 +5834,7 @@ func (ms *MutableStateImpl) AddWorkflowExecutionSignaledEvent( identity, header, externalWorkflowExecution, + requestID, links, ) if err := ms.ApplyWorkflowExecutionSignaled(event); err != nil { @@ -5810,10 +5844,40 @@ func (ms *MutableStateImpl) AddWorkflowExecutionSignaledEvent( } func (ms *MutableStateImpl) ApplyWorkflowExecutionSignaled( - _ *historypb.HistoryEvent, + event *historypb.HistoryEvent, ) error { // Increment signal count in mutable state for this workflow execution ms.executionInfo.SignalCount++ + + // Add signal requestID to workflow CHASM tree (if feature is enabled) + signalEventAttrs, ok := event.GetAttributes().(*historypb.HistoryEvent_WorkflowExecutionSignaledEventAttributes) + if !ok { + return softassert.UnexpectedInternalErr( + ms.logger, + fmt.Sprintf( + "Expect ApplyWorkflowExecutionSignaled to be called only on signal events, but called from: %v", + event, + ), + nil, + ) + } + requestID := signalEventAttrs.WorkflowExecutionSignaledEventAttributes.GetRequestId() + if requestID != "" && ms.ChasmSignalBacklinksEnabled() { + ctx := context.Background() + ms.EnsureChasmWorkflowComponent(ctx) + wf, chasmCtx, err := ms.ChasmWorkflowComponent(ctx) + if err != nil { + return err + } + // Persist the signal requestID to the current eventID. + // - For buffered events (normal processing path), event.GetEventId() returns the common.BufferedEventID and will be resolved later. + // - For already-persisted events (rebuild/replay path), event.GetEventId() returns the real history event ID. + nsTag := metrics.NamespaceTag(ms.GetNamespaceEntry().Name().String()) + if err := wf.AddIncomingSignalEvent(chasmCtx, requestID, event.GetEventId()); err != nil { + return err + } + metrics.ChasmIncomingSignalWritten.With(ms.metricsHandler.WithTags(nsTag)).Record(1) + } return nil } @@ -8143,7 +8207,7 @@ func (ms *MutableStateImpl) dirtyHSMToReplicationTask( func (ms *MutableStateImpl) updatePendingEventIDs( scheduledIDToStartedID map[int64]int64, requestIDToEventID map[string]int64, -) { +) error { for scheduledEventID, startedEventID := range scheduledIDToStartedID { if activityInfo, ok := ms.GetActivityInfo(scheduledEventID); ok { activityInfo.StartedEventId = startedEventID @@ -8158,12 +8222,29 @@ func (ms *MutableStateImpl) updatePendingEventIDs( } } if len(requestIDToEventID) > 0 { + var wf *chasmworkflow.Workflow + var chasmCtx chasm.MutableContext + var err error + if ms.ChasmSignalBacklinksEnabled() { + wf, chasmCtx, err = ms.ChasmWorkflowComponent(context.Background()) + if err != nil { + return err + } + } + for requestID, eventID := range requestIDToEventID { if requestIDInfo, ok := ms.executionState.RequestIds[requestID]; ok { requestIDInfo.EventId = eventID } + if wf != nil { + // UpdateIncomingSignalEvent is a no-op for non-signal request IDs as they won't exist in the map. + if err := wf.UpdateIncomingSignalEvent(chasmCtx, requestID, eventID); err != nil { + return err + } + } } } + return nil } func (ms *MutableStateImpl) updateWithLastWriteEvent( @@ -8893,6 +8974,7 @@ func (ms *MutableStateImpl) applyUpdatesToStateMachineNodes( } func (ms *MutableStateImpl) applySignalRequestedIds(signalRequestedIds []string, incomingExecutionInfo *persistencespb.WorkflowExecutionInfo) { + // TODO(long-nt-tran): Deprecate this function once we fully ramp up writing signals to workflow CHASM component if transitionhistory.Compare( incomingExecutionInfo.SignalRequestIdsLastUpdateVersionedTransition, ms.executionInfo.SignalRequestIdsLastUpdateVersionedTransition, diff --git a/service/history/workflow/mutable_state_impl_test.go b/service/history/workflow/mutable_state_impl_test.go index 24de0d9ac4..464c90fc4e 100644 --- a/service/history/workflow/mutable_state_impl_test.go +++ b/service/history/workflow/mutable_state_impl_test.go @@ -2763,6 +2763,7 @@ func (s *mutableStateSuite) TestTotalEntitiesCount() { &commonpb.Payloads{}, "identity", &commonpb.Header{}, + "", nil, ) s.NoError(err) @@ -3321,6 +3322,7 @@ func (s *mutableStateSuite) TestCloseTransactionUpdateTransition() { "identity", &commonpb.Header{}, nil, + "", nil, ) if err != nil { @@ -6507,6 +6509,7 @@ func (s *mutableStateSuite) TestCloseTransaction_PrincipalPreserved() { "alice-identity", &commonpb.Header{}, nil, + "", nil, ) s.NoError(err) @@ -6526,6 +6529,7 @@ func (s *mutableStateSuite) TestCloseTransaction_PrincipalPreserved() { "bob-identity", &commonpb.Header{}, nil, + "", nil, ) s.NoError(err) diff --git a/service/history/workflow/workflow_test/mutable_state_impl_test.go b/service/history/workflow/workflow_test/mutable_state_impl_test.go index 2f8ea11428..0d47e564b5 100644 --- a/service/history/workflow/workflow_test/mutable_state_impl_test.go +++ b/service/history/workflow/workflow_test/mutable_state_impl_test.go @@ -186,6 +186,7 @@ func addWorkflowExecutionSignaled(t *testing.T, i int, ms *workflow.MutableState payload, identity, header, + "", nil, ) if err != nil { diff --git a/tests/links_test.go b/tests/links_test.go index 210808219b..987053bfdd 100644 --- a/tests/links_test.go +++ b/tests/links_test.go @@ -11,6 +11,7 @@ import ( taskqueuepb "go.temporal.io/api/taskqueue/v1" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/sdk/client" + "go.temporal.io/server/common/dynamicconfig" "go.temporal.io/server/common/testing/parallelsuite" "go.temporal.io/server/common/testing/protorequire" "go.temporal.io/server/tests/testcore" @@ -36,6 +37,13 @@ var links = []*commonpb.Link{ }, } +func enableSignalBacklinkOpts() []testcore.TestOption { + return []testcore.TestOption{ + testcore.WithDynamicConfig(dynamicconfig.EnableChasm, true), + testcore.WithDynamicConfig(dynamicconfig.EnableCHASMSignalBacklinks, true), + } +} + func (s *LinksSuite) TestTerminateWorkflow_LinksAttachedToEvent() { env := testcore.NewEnv(s.T()) ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) @@ -49,7 +57,6 @@ func (s *LinksSuite) TestTerminateWorkflow_LinksAttachedToEvent() { ) s.NoError(err) - // TODO(bergundy): Use SdkClient if and when it exposes links on TerminateWorkflow. _, err = env.FrontendClient().TerminateWorkflowExecution(ctx, &workflowservice.TerminateWorkflowExecutionRequest{ Namespace: env.Namespace().String(), WorkflowExecution: &commonpb.WorkflowExecution{ @@ -60,6 +67,7 @@ func (s *LinksSuite) TestTerminateWorkflow_LinksAttachedToEvent() { }) s.NoError(err) + // TODO(bergundy): Use SdkClient if and when it exposes links on TerminateWorkflow. history := env.SdkClient().GetWorkflowHistory(ctx, run.GetID(), "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT) event, err := history.Next() s.NoError(err) @@ -79,7 +87,6 @@ func (s *LinksSuite) TestRequestCancelWorkflow_LinksAttachedToEvent() { ) s.NoError(err) - // TODO(bergundy): Use SdkClient if and when it exposes links on CancelWorkflow. _, err = env.FrontendClient().RequestCancelWorkflowExecution(ctx, &workflowservice.RequestCancelWorkflowExecutionRequest{ Namespace: env.Namespace().String(), WorkflowExecution: &commonpb.WorkflowExecution{ @@ -90,6 +97,7 @@ func (s *LinksSuite) TestRequestCancelWorkflow_LinksAttachedToEvent() { }) s.NoError(err) + // TODO(bergundy): Use SdkClient if and when it exposes links on CancelWorkflow. history := env.SdkClient().GetWorkflowHistory(ctx, run.GetID(), "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) foundEvent := false for history.HasNext() { @@ -105,7 +113,7 @@ func (s *LinksSuite) TestRequestCancelWorkflow_LinksAttachedToEvent() { } func (s *LinksSuite) TestSignalWorkflowExecution_LinksAttachedToEvent() { - env := testcore.NewEnv(s.T()) + env := testcore.NewEnv(s.T(), enableSignalBacklinkOpts()...) ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() run, err := env.SdkClient().ExecuteWorkflow( @@ -117,8 +125,7 @@ func (s *LinksSuite) TestSignalWorkflowExecution_LinksAttachedToEvent() { ) s.NoError(err) - // TODO(bergundy): Use SdkClient if and when it exposes links on SignalWorkflow. - _, err = env.FrontendClient().SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{ + req := &workflowservice.SignalWorkflowExecutionRequest{ Namespace: env.Namespace().String(), WorkflowExecution: &commonpb.WorkflowExecution{ WorkflowId: run.GetID(), @@ -127,31 +134,256 @@ func (s *LinksSuite) TestSignalWorkflowExecution_LinksAttachedToEvent() { Identity: "test", RequestId: uuid.NewString(), Links: links, - }) + } + expectedLink := &commonpb.Link{ + Variant: &commonpb.Link_WorkflowEvent_{ + WorkflowEvent: &commonpb.Link_WorkflowEvent{ + Namespace: env.Namespace().String(), + WorkflowId: run.GetID(), + RunId: run.GetRunID(), + Reference: &commonpb.Link_WorkflowEvent_RequestIdRef{ + RequestIdRef: &commonpb.Link_WorkflowEvent_RequestIdReference{ + RequestId: req.RequestId, + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + }, + }, + }, + }, + } + + // TODO(bergundy): Use SdkClient if and when it exposes links on SignalWorkflow. + resp, err := env.FrontendClient().SignalWorkflowExecution(ctx, req) s.NoError(err) + protorequire.ProtoEqual(s.T(), expectedLink, resp.GetLink()) + + // Second call with same RequestId hits the dedup path but must still return the same link. + resp, err = env.FrontendClient().SignalWorkflowExecution(ctx, req) + s.NoError(err) + protorequire.ProtoEqual(s.T(), expectedLink, resp.GetLink()) history := env.SdkClient().GetWorkflowHistory(ctx, run.GetID(), "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) foundEvent := false + foundDuplicatedEvent := false + var signaledEventID int64 for history.HasNext() { event, err := history.Next() s.NoError(err) if event.EventType != enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED { continue } + if foundEvent { + foundDuplicatedEvent = true + } else { + signaledEventID = event.GetEventId() + } foundEvent = true protorequire.ProtoSliceEqual(s.T(), links, event.Links) } s.True(foundEvent) + s.False(foundDuplicatedEvent, "second signal with same RequestId should be deduped and not produce a second event") + + // Verify the requestID is tracked and resolves to the correct event ID. + descResp, err := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{ + WorkflowId: run.GetID(), + }, + }) + s.NoError(err) + requestIDInfos := descResp.GetWorkflowExtendedInfo().GetRequestIdInfos() + s.Contains(requestIDInfos, req.RequestId) + info := requestIDInfos[req.RequestId] + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, info.GetEventType()) + s.Equal(signaledEventID, info.GetEventId(), "requestID map entry must point to the SIGNALED event in history") +} + +// TestSignalWorkflowExecution_BacklinkSurvivesReset verifies that after a workflow is reset, +// the new run's CHASM IncomingSignals map is rebuilt from history so that DescribeWorkflow +// continues to return a valid requestID -> event-ID backlink for signals that occurred before +// the reset point. +// +// This exercises the rebuild/replay path through ApplyWorkflowExecutionSignaled, which uses +// the event's real event ID (not common.BufferedEventID) when writing to the CHASM tree. +func (s *LinksSuite) TestSignalWorkflowExecution_BacklinkSurvivesReset() { + env := testcore.NewEnv(s.T(), enableSignalBacklinkOpts()...) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + taskQueue := testcore.RandomizeStr(s.T().Name()) + workflowID := testcore.RandomizeStr(s.T().Name()) + + // Start the workflow. + run, err := env.SdkClient().ExecuteWorkflow(ctx, client.StartWorkflowOptions{ + ID: workflowID, + TaskQueue: taskQueue, + }, "dont-care") + s.NoError(err) + runID := run.GetRunID() + + signalRequestID := uuid.NewString() + + // Signal the workflow. The signal will be included in the first WFT batch, so it will + // appear in history before the WFT completion event. + _, err = env.FrontendClient().SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, + SignalName: "dont-care", + Identity: "test", + RequestId: signalRequestID, + Links: links, + }) + s.NoError(err) + + // Poll and complete the WFT so the signal is flushed to history with a real event ID. + pollResp, pollErr := env.FrontendClient().PollWorkflowTaskQueue(ctx, &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: env.Namespace().String(), + TaskQueue: &taskqueuepb.TaskQueue{Name: taskQueue, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + Identity: "test", + }) + s.NoError(pollErr) + s.NotNil(pollResp.GetTaskToken()) + _, completeErr := env.FrontendClient().RespondWorkflowTaskCompleted(ctx, &workflowservice.RespondWorkflowTaskCompletedRequest{ + Namespace: env.Namespace().String(), + Identity: "test", + TaskToken: pollResp.TaskToken, + }) + s.NoError(completeErr) + + // Find the WFT completed event ID in the original run's history. + var wftCompletedEventID int64 + history := env.SdkClient().GetWorkflowHistory(ctx, workflowID, runID, false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) + for history.HasNext() { + event, histErr := history.Next() + s.NoError(histErr) + if event.EventType == enumspb.EVENT_TYPE_WORKFLOW_TASK_COMPLETED { + wftCompletedEventID = event.EventId + break + } + } + s.Positive(wftCompletedEventID, "WFT completed event not found in history") + + // Reset the workflow to the first WFT completion. The signal event is before this point, + // so it will be included in the new run's replayed history. + resetResp, err := env.FrontendClient().ResetWorkflowExecution(ctx, &workflowservice.ResetWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + WorkflowExecution: &commonpb.WorkflowExecution{ + WorkflowId: workflowID, + RunId: runID, + }, + Reason: "testing-backlink-survival", + RequestId: uuid.NewString(), + WorkflowTaskFinishEventId: wftCompletedEventID, + }) + s.NoError(err) + newRunID := resetResp.RunId + s.NotEmpty(newRunID) + + // During reset, ApplyWorkflowExecutionSignaled rebuilds the CHASM IncomingSignals map + // from history, so the backlink should be present once the new run is created. + descResp, descErr := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: newRunID}, + }) + s.NoError(descErr) + _, signalExists := descResp.GetWorkflowExtendedInfo().GetRequestIdInfos()[signalRequestID] + s.True(signalExists) + + // Verify the backlink on the new run points to a real (non-buffered) SIGNALED event. + descResp, err = env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: newRunID}, + }) + s.NoError(err) + requestIDInfos := descResp.GetWorkflowExtendedInfo().GetRequestIdInfos() + s.Contains(requestIDInfos, signalRequestID) + info := requestIDInfos[signalRequestID] + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, info.GetEventType()) + s.Positive(info.GetEventId(), "backlink event ID must be a real, non-buffered event ID in the new run's history") + s.False(info.GetBuffered()) +} + +// TestSignalWorkflowExecution_BufferedDuringWorkflowTask verifies that when a signal arrives +// while a workflow task is being processed, DescribeWorkflow reports the backlink as buffered. +// Once the workflow task completes and the signal is flushed to history, the backlink must +// reflect a real (non-buffered) event ID. +func (s *LinksSuite) TestSignalWorkflowExecution_BufferedDuringWorkflowTask() { + env := testcore.NewEnv(s.T(), enableSignalBacklinkOpts()...) + + taskQueue := testcore.RandomizeStr(s.T().Name()) + workflowID := testcore.RandomizeStr(s.T().Name()) + + run, err := env.SdkClient().ExecuteWorkflow(env.Context(), client.StartWorkflowOptions{ + ID: workflowID, + TaskQueue: taskQueue, + // Use a really long WFT timeout to avoid flakiness when we're checking that the signal is buffered. + WorkflowTaskTimeout: 60 * time.Second, + }, "dont-care") + s.NoError(err) + runID := run.GetRunID() + + // Poll to move the WFT into "started" state to have the server wait for us to complete it. + // This will force the signal to stay in the buffer until the task is finished. + pollResp, err := env.FrontendClient().PollWorkflowTaskQueue(env.Context(), &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: env.Namespace().String(), + TaskQueue: &taskqueuepb.TaskQueue{Name: taskQueue, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}, + Identity: "test", + }) + s.NoError(err) + s.NotNil(pollResp.GetTaskToken()) + + // This signal will be buffered since there is a WFT in-flight. + signalRequestID := uuid.NewString() + _, err = env.FrontendClient().SignalWorkflowExecution(env.Context(), &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, + SignalName: "dont-care", + Identity: "test", + RequestId: signalRequestID, + Links: links, + }) + s.NoError(err) + + // WFT is still running: backlink must be present and marked buffered. + descResp, err := env.FrontendClient().DescribeWorkflowExecution(env.Context(), &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, + }) + s.NoError(err) + requestIDInfos := descResp.GetWorkflowExtendedInfo().GetRequestIdInfos() + s.Contains(requestIDInfos, signalRequestID) + info := requestIDInfos[signalRequestID] + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, info.GetEventType()) + s.True(info.GetBuffered(), "backlink must be buffered while WFT is in progress") + + // Complete the WFT, which flushes the signal to DB with a concrete EventID. + _, err = env.FrontendClient().RespondWorkflowTaskCompleted(env.Context(), &workflowservice.RespondWorkflowTaskCompletedRequest{ + Namespace: env.Namespace().String(), + Identity: "test", + TaskToken: pollResp.TaskToken, + }) + s.NoError(err) + + // After WFT completion the backlink must resolve to a real, non-buffered event. + descResp, err = env.FrontendClient().DescribeWorkflowExecution(env.Context(), &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, + }) + s.NoError(err) + requestIDInfos = descResp.GetWorkflowExtendedInfo().GetRequestIdInfos() + s.Contains(requestIDInfos, signalRequestID) + info = requestIDInfos[signalRequestID] + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, info.GetEventType()) + s.False(info.GetBuffered(), "backlink must not be buffered after WFT completion") + s.Positive(info.GetEventId(), "backlink must reference a real event ID after WFT completion") } func (s *LinksSuite) TestSignalWithStartWorkflowExecution_LinksAttachedToRelevantEvents() { - env := testcore.NewEnv(s.T()) + env := testcore.NewEnv(s.T(), enableSignalBacklinkOpts()...) ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() workflowID := testcore.RandomizeStr(s.T().Name()) - // TODO(bergundy): Use SdkClient if and when it exposes links on SignalWithStartWorkflow. request := &workflowservice.SignalWithStartWorkflowExecutionRequest{ Namespace: env.Namespace().String(), WorkflowId: workflowID, @@ -166,26 +398,74 @@ func (s *LinksSuite) TestSignalWithStartWorkflowExecution_LinksAttachedToRelevan RequestId: uuid.NewString(), Links: links, } - _, err := env.FrontendClient().SignalWithStartWorkflowExecution(ctx, request) + + // TODO(bergundy): Use SdkClient if and when it exposes links on SignalWithStartWorkflow. + resp, err := env.FrontendClient().SignalWithStartWorkflowExecution(ctx, request) s.NoError(err) + firstRunID := resp.GetRunId() + protorequire.ProtoEqual( + s.T(), + &commonpb.Link{ + Variant: &commonpb.Link_WorkflowEvent_{ + WorkflowEvent: &commonpb.Link_WorkflowEvent{ + Namespace: env.Namespace().String(), + WorkflowId: workflowID, + RunId: firstRunID, + Reference: &commonpb.Link_WorkflowEvent_RequestIdRef{ + RequestIdRef: &commonpb.Link_WorkflowEvent_RequestIdReference{ + RequestId: request.RequestId, + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + }, + }, + }, + }, + }, + resp.GetSignalLink(), + ) + + firstRequestID := request.RequestId // Send a second request and verify that the new signal has links attached to it too. request.RequestId = uuid.NewString() - _, err = env.FrontendClient().SignalWithStartWorkflowExecution(ctx, request) + resp, err = env.FrontendClient().SignalWithStartWorkflowExecution(ctx, request) s.NoError(err) + // Expect backlinks with the same RunID as before since the workflow execution didn't change, + // but the signal requestID should differ since this is a different request. + protorequire.ProtoEqual( + s.T(), + &commonpb.Link{ + Variant: &commonpb.Link_WorkflowEvent_{ + WorkflowEvent: &commonpb.Link_WorkflowEvent{ + Namespace: env.Namespace().String(), + WorkflowId: workflowID, + RunId: resp.GetRunId(), + Reference: &commonpb.Link_WorkflowEvent_RequestIdRef{ + RequestIdRef: &commonpb.Link_WorkflowEvent_RequestIdReference{ + RequestId: request.RequestId, // This requestID should differ from the first backlink. + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, + }, + }, + }, + }, + }, + resp.GetSignalLink(), + ) history := env.SdkClient().GetWorkflowHistory(ctx, workflowID, "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) foundStartEvent := false foundFirstSignal := false foundSecondSignal := false + var firstSignalEventID, secondSignalEventID int64 for history.HasNext() { event, err := history.Next() s.NoError(err) if event.EventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED { if foundFirstSignal { foundSecondSignal = true + secondSignalEventID = event.GetEventId() } else { foundFirstSignal = true + firstSignalEventID = event.GetEventId() } protorequire.ProtoSliceEqual(s.T(), links, event.Links) } @@ -197,4 +477,24 @@ func (s *LinksSuite) TestSignalWithStartWorkflowExecution_LinksAttachedToRelevan s.True(foundStartEvent) s.True(foundFirstSignal) s.True(foundSecondSignal) + + // Verify both requestIDs are tracked and resolve to the correct signal event IDs. + descResp, err := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{ + WorkflowId: workflowID, + }, + }) + s.NoError(err) + requestIDInfos := descResp.GetWorkflowExtendedInfo().GetRequestIdInfos() + + s.Contains(requestIDInfos, firstRequestID) + firstInfo := requestIDInfos[firstRequestID] + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, firstInfo.GetEventType()) + s.Equal(firstSignalEventID, firstInfo.GetEventId(), "first requestID map entry must point to the first SIGNALED event in history") + + s.Contains(requestIDInfos, request.RequestId) + secondInfo := requestIDInfos[request.RequestId] + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED, secondInfo.GetEventType()) + s.Equal(secondSignalEventID, secondInfo.GetEventId(), "second requestID map entry must point to the second SIGNALED event in history") } diff --git a/tests/nexus_workflow_test.go b/tests/nexus_workflow_test.go index a17dacbfb9..35e3b3a59b 100644 --- a/tests/nexus_workflow_test.go +++ b/tests/nexus_workflow_test.go @@ -2208,11 +2208,30 @@ func (s *NexusWorkflowTestSuite) TestNexusAsyncOperationWithMultipleCallers(chas s.NoError(err) requestIDInfos := descResp.GetWorkflowExtendedInfo().GetRequestIdInfos() s.NotNil(requestIDInfos) - s.Len(requestIDInfos, 1) + cntStarted := 0 + cntSignaled := 0 for _, info := range requestIDInfos { s.False(info.Buffered) s.GreaterOrEqual(info.EventId, common.FirstEventID) - s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, info.EventType) + switch info.EventType { + case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: + cntStarted++ + case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: + // The Signal event's request ID is attached to the requestIDInfos map for backlinking, + // so it should be present here. + cntSignaled++ + default: + s.Fail("Unexpected event type in request ID info") + } + } + s.Equal(1, cntStarted) + + // DescribeWorkflowExecution only incorporates signal events if CHASM is enabled since these signal requests + // are stored under the Workflow's CHASM component. + if chasmEnabled { + s.Equal(1, cntSignaled) + } else { + s.Equal(0, cntSignaled) } }, }, @@ -2230,6 +2249,7 @@ func (s *NexusWorkflowTestSuite) TestNexusAsyncOperationWithMultipleCallers(chas s.NotNil(requestIDInfos) cntStarted := 0 cntAttached := 0 + cntSignaled := 0 for _, info := range requestIDInfos { s.False(info.Buffered) s.GreaterOrEqual(info.EventId, common.FirstEventID) @@ -2238,11 +2258,23 @@ func (s *NexusWorkflowTestSuite) TestNexusAsyncOperationWithMultipleCallers(chas cntStarted++ case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: cntAttached++ + case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: + // The Signal event's request ID is attached to the requestIDInfos map for backlinking, + // so it should be present here. + cntSignaled++ default: s.Fail("Unexpected event type in request ID info") } } s.Equal(1, cntStarted) + + // DescribeWorkflowExecution only incorporates signal events if CHASM is enabled since these signal requests + // are stored under the Workflow's CHASM component. + if chasmEnabled { + s.Equal(1, cntSignaled) + } else { + s.Equal(0, cntSignaled) + } s.Equal(numCalls-1, cntAttached) }, }, diff --git a/tests/signal_workflow_test.go b/tests/signal_workflow_test.go index 7e764d0dac..0c782e91a5 100644 --- a/tests/signal_workflow_test.go +++ b/tests/signal_workflow_test.go @@ -34,12 +34,23 @@ type SignalWorkflowTestSuite struct { parallelsuite.Suite[*SignalWorkflowTestSuite] } -func TestSignalWorkflowTestSuite(t *testing.T) { - parallelsuite.Run(t, &SignalWorkflowTestSuite{}) +func TestSignalWorkflowTestSuiteLegacy(t *testing.T) { + parallelsuite.Run(t, &SignalWorkflowTestSuite{}, []testcore.TestOption{}) } -func (s *SignalWorkflowTestSuite) TestSignalWorkflow() { - env := testcore.NewEnv(s.T()) +func TestSignalWorkflowTestSuiteChasm(t *testing.T) { + parallelsuite.Run( + t, + &SignalWorkflowTestSuite{}, + []testcore.TestOption{ + testcore.WithDynamicConfig(dynamicconfig.EnableChasm, true), + testcore.WithDynamicConfig(dynamicconfig.EnableCHASMSignalBacklinks, true), + }, + ) +} + +func (s *SignalWorkflowTestSuite) TestSignalWorkflow(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-workflow-test" wt := "functional-signal-workflow-test-type" tl := "functional-signal-workflow-test-taskqueue" @@ -231,8 +242,8 @@ func (s *SignalWorkflowTestSuite) TestSignalWorkflow() { s.IsType(&serviceerror.NotFound{}, err) } -func (s *SignalWorkflowTestSuite) TestSignalWorkflow_DuplicateRequest() { - env := testcore.NewEnv(s.T()) +func (s *SignalWorkflowTestSuite) TestSignalWorkflow_DuplicateRequest(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-workflow-test-duplicate" wt := "functional-signal-workflow-test-duplicate-type" tl := "functional-signal-workflow-test-duplicate-taskqueue" @@ -372,9 +383,15 @@ func (s *SignalWorkflowTestSuite) TestSignalWorkflow_DuplicateRequest() { s.Equal(0, numOfSignaledEvent) } -func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand() { - env := testcore.NewEnv(s.T(), testcore.WithDedicatedCluster()) - env.OverrideDynamicConfig(dynamicconfig.EnableCrossNamespaceCommands, true) // explicitly enable cross namespace commands for this test +func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand(opts []testcore.TestOption) { + // Explicitly enable cross namespace commands for this test, + // need a dedicated cluster to enable cross namespace commands + opts = append( + opts, + testcore.WithDedicatedCluster(), + testcore.WithDynamicConfig(dynamicconfig.EnableCrossNamespaceCommands, true), + ) + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-external-workflow-test" wt := "functional-signal-external-workflow-test-type" tl := "functional-signal-external-workflow-test-taskqueue" @@ -590,8 +607,8 @@ CheckHistoryLoopForSignalSent: s.Equal("history-service", signalEvent.GetWorkflowExecutionSignaledEventAttributes().Identity) } -func (s *SignalWorkflowTestSuite) TestSignalWorkflow_Cron_NoWorkflowTaskCreated() { - env := testcore.NewEnv(s.T()) +func (s *SignalWorkflowTestSuite) TestSignalWorkflow_Cron_NoWorkflowTaskCreated(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-workflow-test-cron" wt := "functional-signal-workflow-test-cron-type" tl := "functional-signal-workflow-test-cron-taskqueue" @@ -667,8 +684,8 @@ func (s *SignalWorkflowTestSuite) TestSignalWorkflow_Cron_NoWorkflowTaskCreated( s.Greater(workflowTaskDelay, time.Second*2) } -func (s *SignalWorkflowTestSuite) TestSignalWorkflow_WorkflowCloseAttempted() { - env := testcore.NewEnv(s.T()) +func (s *SignalWorkflowTestSuite) TestSignalWorkflow_WorkflowCloseAttempted(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-workflow-workflow-close-attempted-test" wt := "functional-signal-workflow-workflow-close-attempted-test-type" tl := "functional-signal-workflow-workflow-close-attempted-test-taskqueue" @@ -751,9 +768,15 @@ func (s *SignalWorkflowTestSuite) TestSignalWorkflow_WorkflowCloseAttempted() { s.NoError(err) } -func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand_WithoutRunID() { - env := testcore.NewEnv(s.T(), testcore.WithDedicatedCluster()) - env.OverrideDynamicConfig(dynamicconfig.EnableCrossNamespaceCommands, true) // explicitly enable cross namespace commands for this test +func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand_WithoutRunID(opts []testcore.TestOption) { + // Explicitly enable cross namespace commands for this test, + // need a dedicated cluster to enable cross namespace commands + opts = append( + opts, + testcore.WithDedicatedCluster(), + testcore.WithDynamicConfig(dynamicconfig.EnableCrossNamespaceCommands, true), + ) + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-external-workflow-test-without-run-id" wt := "functional-signal-external-workflow-test-without-run-id-type" tl := "functional-signal-external-workflow-test-without-run-id-taskqueue" @@ -965,9 +988,15 @@ CheckHistoryLoopForSignalSent: s.Equal("history-service", signalEvent.GetWorkflowExecutionSignaledEventAttributes().Identity) } -func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand_UnKnownTarget() { - env := testcore.NewEnv(s.T(), testcore.WithDedicatedCluster()) - env.OverrideDynamicConfig(dynamicconfig.EnableCrossNamespaceCommands, true) // explicitly enable cross namespace commands for this test +func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand_UnKnownTarget(opts []testcore.TestOption) { + // Explicitly enable cross namespace commands for this test, + // need a dedicated cluster to enable cross namespace commands + opts = append( + opts, + testcore.WithDedicatedCluster(), + testcore.WithDynamicConfig(dynamicconfig.EnableCrossNamespaceCommands, true), + ) + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-unknown-workflow-command-test" wt := "functional-signal-unknown-workflow-command-test-type" tl := "functional-signal-unknown-workflow-command-test-taskqueue" @@ -1089,8 +1118,8 @@ CheckHistoryLoopForCancelSent: 12 WorkflowTaskScheduled`, we.RunId), historyEvents) } -func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand_SignalSelf() { - env := testcore.NewEnv(s.T()) +func (s *SignalWorkflowTestSuite) TestSignalExternalWorkflowCommand_SignalSelf(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-self-workflow-command-test" wt := "functional-signal-self-workflow-command-test-type" tl := "functional-signal-self-workflow-command-test-taskqueue" @@ -1212,8 +1241,8 @@ CheckHistoryLoopForCancelSent: 12 WorkflowTaskScheduled`, we.RunId, id), historyEvents) } -func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow() { - env := testcore.NewEnv(s.T()) +func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-with-start-workflow-test" wt := "functional-signal-with-start-workflow-test-type" tl := "functional-signal-with-start-workflow-test-taskqueue" @@ -1488,8 +1517,8 @@ func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow() { s.Len(listClosedResp.Executions, 1) } -func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow_ResolveIDDeduplication() { - env := testcore.NewEnv(s.T()) +func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow_ResolveIDDeduplication(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) // setting this to 0 to be sure we are terminating the current workflow env.OverrideDynamicConfig(dynamicconfig.WorkflowIdReuseMinimalInterval, 0) @@ -1683,8 +1712,8 @@ func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow_ResolveIDDeduplica s.Equal(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING, descResp.WorkflowExecutionInfo.Status) } -func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow_StartDelay() { - env := testcore.NewEnv(s.T()) +func (s *SignalWorkflowTestSuite) TestSignalWithStartWorkflow_StartDelay(opts []testcore.TestOption) { + env := testcore.NewEnv(s.T(), opts...) id := "functional-signal-with-start-workflow-start-delay-test" wt := "functional-signal-with-start-workflow-start-delay-test-type" tl := "functional-signal-with-start-workflow-start-delay-test-taskqueue" diff --git a/tests/update_workflow_test.go b/tests/update_workflow_test.go index 3bcc88742f..e16367866b 100644 --- a/tests/update_workflow_test.go +++ b/tests/update_workflow_test.go @@ -5168,6 +5168,13 @@ func (s *UpdateWithStartSuite) TestWorkflowIsRunning() { startResp := uwsRes.response.Responses[0].GetStartWorkflow() updateRep := uwsRes.response.Responses[1].GetUpdateWorkflow() requireNotStartedButRunning(s.T(), startResp) + s.NotNil(startResp.Link) + wfEvent := startResp.Link.GetWorkflowEvent() + s.Equal(env.Namespace().String(), wfEvent.GetNamespace()) + s.Equal(env.Tv().WorkflowID(), wfEvent.GetWorkflowId()) + s.Equal(startResp.RunId, wfEvent.GetRunId()) + s.Equal(int64(common.FirstEventID), wfEvent.GetEventRef().GetEventId()) + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, wfEvent.GetEventRef().GetEventType()) s.Equal("success-result-of-"+env.Tv().UpdateID(), testcore.DecodeString(s.T(), updateRep.GetOutcome().GetSuccess())) // poll update to ensure same outcome is returned @@ -5218,6 +5225,13 @@ func (s *UpdateWithStartSuite) TestWorkflowIsRunning() { startResp := uwsRes.response.Responses[0].GetStartWorkflow() updateRep := uwsRes.response.Responses[1].GetUpdateWorkflow() requireNotStartedButRunning(s.T(), startResp) + s.NotNil(startResp.Link) + wfEvent := startResp.Link.GetWorkflowEvent() + s.Equal(env.Namespace().String(), wfEvent.GetNamespace()) + s.Equal(env.Tv().WorkflowID(), wfEvent.GetWorkflowId()) + s.Equal(startResp.RunId, wfEvent.GetRunId()) + s.Equal(int64(common.FirstEventID), wfEvent.GetEventRef().GetEventId()) + s.Equal(enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, wfEvent.GetEventRef().GetEventType()) s.Equal("rejection-of-"+env.Tv().UpdateID(), updateRep.GetOutcome().GetFailure().GetMessage()) // poll update to ensure same outcome is returned