From 938bf8b9ebb1f7dc9d65b0a7ead7ac0dd29512bd Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 14 Sep 2026 15:59:55 -0700 Subject: [PATCH 01/47] Added the stream protos, commands, and API surface. --- api/historyservice/v1/request_response.pb.go | 788 ++-- api/matchingservice/v1/request_response.pb.go | 684 ++-- .../gen/streampb/v1/message.go-helpers.pb.go | 101 + .../lib/stream/gen/streampb/v1/message.pb.go | 305 ++ chasm/lib/stream/gen/streampb/v1/namespace.go | 75 + .../stream/gen/streampb/v1/namespace_test.go | 38 + .../v1/request_response.go-helpers.pb.go | 2152 ++++++++++ .../gen/streampb/v1/request_response.pb.go | 3620 +++++++++++++++++ .../lib/stream/gen/streampb/v1/service.pb.go | 140 + .../gen/streampb/v1/service_client.pb.go | 713 ++++ .../stream/gen/streampb/v1/service_grpc.pb.go | 640 +++ .../streampb/v1/stream_state.go-helpers.pb.go | 191 + .../stream/gen/streampb/v1/stream_state.pb.go | 665 +++ .../gen/streampb/v1/tasks.go-helpers.pb.go | 80 + chasm/lib/stream/gen/streampb/v1/tasks.pb.go | 160 + chasm/lib/stream/proto/v1/message.proto | 39 + .../stream/proto/v1/request_response.proto | 364 ++ chasm/lib/stream/proto/v1/service.proto | 92 + chasm/lib/stream/proto/v1/stream_state.proto | 123 + chasm/lib/stream/proto/v1/tasks.proto | 17 + cmd/tools/getproto/files.go | 2 + cmd/tools/protogen/main.go | 53 +- go.mod | 2 + go.sum | 4 +- .../historyservice/v1/request_response.proto | 9 + .../matchingservice/v1/request_response.proto | 9 + 26 files changed, 10353 insertions(+), 713 deletions(-) create mode 100644 chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/message.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/namespace.go create mode 100644 chasm/lib/stream/gen/streampb/v1/namespace_test.go create mode 100644 chasm/lib/stream/gen/streampb/v1/request_response.go-helpers.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/request_response.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/service.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/service_client.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/service_grpc.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/stream_state.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/tasks.go-helpers.pb.go create mode 100644 chasm/lib/stream/gen/streampb/v1/tasks.pb.go create mode 100644 chasm/lib/stream/proto/v1/message.proto create mode 100644 chasm/lib/stream/proto/v1/request_response.proto create mode 100644 chasm/lib/stream/proto/v1/service.proto create mode 100644 chasm/lib/stream/proto/v1/stream_state.proto create mode 100644 chasm/lib/stream/proto/v1/tasks.proto diff --git a/api/historyservice/v1/request_response.pb.go b/api/historyservice/v1/request_response.pb.go index a7f13f19b10..3b92b759aa9 100644 --- a/api/historyservice/v1/request_response.pb.go +++ b/api/historyservice/v1/request_response.pb.go @@ -11,29 +11,30 @@ import ( sync "sync" unsafe "unsafe" - v123 "go.temporal.io/api/activity/v1" + v124 "go.temporal.io/api/activity/v1" v14 "go.temporal.io/api/common/v1" v16 "go.temporal.io/api/deployment/v1" v12 "go.temporal.io/api/enums/v1" v13 "go.temporal.io/api/failure/v1" v17 "go.temporal.io/api/history/v1" - v121 "go.temporal.io/api/nexus/v1" + v122 "go.temporal.io/api/nexus/v1" v115 "go.temporal.io/api/protocol/v1" v114 "go.temporal.io/api/query/v1" + v116 "go.temporal.io/api/stream/v1" v111 "go.temporal.io/api/taskqueue/v1" v15 "go.temporal.io/api/workflow/v1" v1 "go.temporal.io/api/workflowservice/v1" - v118 "go.temporal.io/server/api/adminservice/v1" + v119 "go.temporal.io/server/api/adminservice/v1" v18 "go.temporal.io/server/api/clock/v1" - v119 "go.temporal.io/server/api/common/v1" + v120 "go.temporal.io/server/api/common/v1" v112 "go.temporal.io/server/api/enums/v1" - v122 "go.temporal.io/server/api/health/v1" + v123 "go.temporal.io/server/api/health/v1" v19 "go.temporal.io/server/api/history/v1" - v116 "go.temporal.io/server/api/namespace/v1" + v117 "go.temporal.io/server/api/namespace/v1" v110 "go.temporal.io/server/api/persistence/v1" - v117 "go.temporal.io/server/api/replication/v1" + v118 "go.temporal.io/server/api/replication/v1" v113 "go.temporal.io/server/api/taskqueue/v1" - v120 "go.temporal.io/server/api/token/v1" + v121 "go.temporal.io/server/api/token/v1" v11 "go.temporal.io/server/api/workflow/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -1353,8 +1354,12 @@ type RecordWorkflowTaskStartedResponse struct { // Deprecated: Marked as deprecated in temporal/server/api/historyservice/v1/request_response.proto. RawHistory *v17.History `protobuf:"bytes,20,opt,name=raw_history,json=rawHistory,proto3" json:"raw_history,omitempty"` RawHistoryBytes [][]byte `protobuf:"bytes,21,rep,name=raw_history_bytes,json=rawHistoryBytes,proto3" json:"raw_history_bytes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + StreamSlices []*v116.StreamSlice `protobuf:"bytes,22,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RecordWorkflowTaskStartedResponse) Reset() { @@ -1528,6 +1533,13 @@ func (x *RecordWorkflowTaskStartedResponse) GetRawHistoryBytes() [][]byte { return nil } +func (x *RecordWorkflowTaskStartedResponse) GetStreamSlices() []*v116.StreamSlice { + if x != nil { + return x.StreamSlices + } + return nil +} + // RecordWorkflowTaskStartedResponseWithRawHistory is wire-compatible with RecordWorkflowTaskStartedResponse. // // WIRE COMPATIBILITY PATTERN: @@ -1572,8 +1584,12 @@ type RecordWorkflowTaskStartedResponseWithRawHistory struct { // Deprecated: Marked as deprecated in temporal/server/api/historyservice/v1/request_response.proto. RawHistory [][]byte `protobuf:"bytes,20,rep,name=raw_history,json=rawHistory,proto3" json:"raw_history,omitempty"` RawHistoryBytes [][]byte `protobuf:"bytes,21,rep,name=raw_history_bytes,json=rawHistoryBytes,proto3" json:"raw_history_bytes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + StreamSlices []*v116.StreamSlice `protobuf:"bytes,22,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RecordWorkflowTaskStartedResponseWithRawHistory) Reset() { @@ -1747,6 +1763,13 @@ func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetRawHistoryBytes() [ return nil } +func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetStreamSlices() []*v116.StreamSlice { + if x != nil { + return x.StreamSlices + } + return nil +} + type RecordActivityTaskStartedRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` @@ -5348,7 +5371,7 @@ type DescribeHistoryHostResponse struct { state protoimpl.MessageState `protogen:"open.v1"` ShardsNumber int32 `protobuf:"varint,1,opt,name=shards_number,json=shardsNumber,proto3" json:"shards_number,omitempty"` ShardIds []int32 `protobuf:"varint,2,rep,packed,name=shard_ids,json=shardIds,proto3" json:"shard_ids,omitempty"` - NamespaceCache *v116.NamespaceCacheInfo `protobuf:"bytes,3,opt,name=namespace_cache,json=namespaceCache,proto3" json:"namespace_cache,omitempty"` + NamespaceCache *v117.NamespaceCacheInfo `protobuf:"bytes,3,opt,name=namespace_cache,json=namespaceCache,proto3" json:"namespace_cache,omitempty"` Address string `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -5398,7 +5421,7 @@ func (x *DescribeHistoryHostResponse) GetShardIds() []int32 { return nil } -func (x *DescribeHistoryHostResponse) GetNamespaceCache() *v116.NamespaceCacheInfo { +func (x *DescribeHistoryHostResponse) GetNamespaceCache() *v117.NamespaceCacheInfo { if x != nil { return x.NamespaceCache } @@ -5687,7 +5710,7 @@ func (*RemoveTaskResponse) Descriptor() ([]byte, []int) { type GetReplicationMessagesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Tokens []*v117.ReplicationToken `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"` + Tokens []*v118.ReplicationToken `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"` ClusterName string `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -5723,7 +5746,7 @@ func (*GetReplicationMessagesRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{76} } -func (x *GetReplicationMessagesRequest) GetTokens() []*v117.ReplicationToken { +func (x *GetReplicationMessagesRequest) GetTokens() []*v118.ReplicationToken { if x != nil { return x.Tokens } @@ -5739,7 +5762,7 @@ func (x *GetReplicationMessagesRequest) GetClusterName() string { type GetReplicationMessagesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - ShardMessages map[int32]*v117.ReplicationMessages `protobuf:"bytes,1,rep,name=shard_messages,json=shardMessages,proto3" json:"shard_messages,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + ShardMessages map[int32]*v118.ReplicationMessages `protobuf:"bytes,1,rep,name=shard_messages,json=shardMessages,proto3" json:"shard_messages,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5774,7 +5797,7 @@ func (*GetReplicationMessagesResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{77} } -func (x *GetReplicationMessagesResponse) GetShardMessages() map[int32]*v117.ReplicationMessages { +func (x *GetReplicationMessagesResponse) GetShardMessages() map[int32]*v118.ReplicationMessages { if x != nil { return x.ShardMessages } @@ -5783,7 +5806,7 @@ func (x *GetReplicationMessagesResponse) GetShardMessages() map[int32]*v117.Repl type GetDLQReplicationMessagesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - TaskInfos []*v117.ReplicationTaskInfo `protobuf:"bytes,1,rep,name=task_infos,json=taskInfos,proto3" json:"task_infos,omitempty"` + TaskInfos []*v118.ReplicationTaskInfo `protobuf:"bytes,1,rep,name=task_infos,json=taskInfos,proto3" json:"task_infos,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5818,7 +5841,7 @@ func (*GetDLQReplicationMessagesRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{78} } -func (x *GetDLQReplicationMessagesRequest) GetTaskInfos() []*v117.ReplicationTaskInfo { +func (x *GetDLQReplicationMessagesRequest) GetTaskInfos() []*v118.ReplicationTaskInfo { if x != nil { return x.TaskInfos } @@ -5827,7 +5850,7 @@ func (x *GetDLQReplicationMessagesRequest) GetTaskInfos() []*v117.ReplicationTas type GetDLQReplicationMessagesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - ReplicationTasks []*v117.ReplicationTask `protobuf:"bytes,1,rep,name=replication_tasks,json=replicationTasks,proto3" json:"replication_tasks,omitempty"` + ReplicationTasks []*v118.ReplicationTask `protobuf:"bytes,1,rep,name=replication_tasks,json=replicationTasks,proto3" json:"replication_tasks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5862,7 +5885,7 @@ func (*GetDLQReplicationMessagesResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{79} } -func (x *GetDLQReplicationMessagesResponse) GetReplicationTasks() []*v117.ReplicationTask { +func (x *GetDLQReplicationMessagesResponse) GetReplicationTasks() []*v118.ReplicationTask { if x != nil { return x.ReplicationTasks } @@ -5968,7 +5991,7 @@ func (x *QueryWorkflowResponse) GetResponse() *v1.QueryWorkflowResponse { type ReapplyEventsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - Request *v118.ReapplyEventsRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Request *v119.ReapplyEventsRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -6010,7 +6033,7 @@ func (x *ReapplyEventsRequest) GetNamespaceId() string { return "" } -func (x *ReapplyEventsRequest) GetRequest() *v118.ReapplyEventsRequest { +func (x *ReapplyEventsRequest) GetRequest() *v119.ReapplyEventsRequest { if x != nil { return x.Request } @@ -6140,9 +6163,9 @@ func (x *GetDLQMessagesRequest) GetNextPageToken() []byte { type GetDLQMessagesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Type v112.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` - ReplicationTasks []*v117.ReplicationTask `protobuf:"bytes,2,rep,name=replication_tasks,json=replicationTasks,proto3" json:"replication_tasks,omitempty"` + ReplicationTasks []*v118.ReplicationTask `protobuf:"bytes,2,rep,name=replication_tasks,json=replicationTasks,proto3" json:"replication_tasks,omitempty"` NextPageToken []byte `protobuf:"bytes,3,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - ReplicationTasksInfo []*v117.ReplicationTaskInfo `protobuf:"bytes,4,rep,name=replication_tasks_info,json=replicationTasksInfo,proto3" json:"replication_tasks_info,omitempty"` + ReplicationTasksInfo []*v118.ReplicationTaskInfo `protobuf:"bytes,4,rep,name=replication_tasks_info,json=replicationTasksInfo,proto3" json:"replication_tasks_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -6184,7 +6207,7 @@ func (x *GetDLQMessagesResponse) GetType() v112.DeadLetterQueueType { return v112.DeadLetterQueueType(0) } -func (x *GetDLQMessagesResponse) GetReplicationTasks() []*v117.ReplicationTask { +func (x *GetDLQMessagesResponse) GetReplicationTasks() []*v118.ReplicationTask { if x != nil { return x.ReplicationTasks } @@ -6198,7 +6221,7 @@ func (x *GetDLQMessagesResponse) GetNextPageToken() []byte { return nil } -func (x *GetDLQMessagesResponse) GetReplicationTasksInfo() []*v117.ReplicationTaskInfo { +func (x *GetDLQMessagesResponse) GetReplicationTasksInfo() []*v118.ReplicationTaskInfo { if x != nil { return x.ReplicationTasksInfo } @@ -6442,7 +6465,7 @@ type RefreshWorkflowTasksRequest struct { NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` // (-- api-linter: core::0141::forbidden-types=disabled --) ArchetypeId uint32 `protobuf:"varint,3,opt,name=archetype_id,json=archetypeId,proto3" json:"archetype_id,omitempty"` - Request *v118.RefreshWorkflowTasksRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Request *v119.RefreshWorkflowTasksRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -6491,7 +6514,7 @@ func (x *RefreshWorkflowTasksRequest) GetArchetypeId() uint32 { return 0 } -func (x *RefreshWorkflowTasksRequest) GetRequest() *v118.RefreshWorkflowTasksRequest { +func (x *RefreshWorkflowTasksRequest) GetRequest() *v119.RefreshWorkflowTasksRequest { if x != nil { return x.Request } @@ -7395,7 +7418,7 @@ func (x *StreamWorkflowReplicationMessagesRequest) GetAttributes() isStreamWorkf return nil } -func (x *StreamWorkflowReplicationMessagesRequest) GetSyncReplicationState() *v117.SyncReplicationState { +func (x *StreamWorkflowReplicationMessagesRequest) GetSyncReplicationState() *v118.SyncReplicationState { if x != nil { if x, ok := x.Attributes.(*StreamWorkflowReplicationMessagesRequest_SyncReplicationState); ok { return x.SyncReplicationState @@ -7409,7 +7432,7 @@ type isStreamWorkflowReplicationMessagesRequest_Attributes interface { } type StreamWorkflowReplicationMessagesRequest_SyncReplicationState struct { - SyncReplicationState *v117.SyncReplicationState `protobuf:"bytes,1,opt,name=sync_replication_state,json=syncReplicationState,proto3,oneof"` + SyncReplicationState *v118.SyncReplicationState `protobuf:"bytes,1,opt,name=sync_replication_state,json=syncReplicationState,proto3,oneof"` } func (*StreamWorkflowReplicationMessagesRequest_SyncReplicationState) isStreamWorkflowReplicationMessagesRequest_Attributes() { @@ -7462,7 +7485,7 @@ func (x *StreamWorkflowReplicationMessagesResponse) GetAttributes() isStreamWork return nil } -func (x *StreamWorkflowReplicationMessagesResponse) GetMessages() *v117.WorkflowReplicationMessages { +func (x *StreamWorkflowReplicationMessagesResponse) GetMessages() *v118.WorkflowReplicationMessages { if x != nil { if x, ok := x.Attributes.(*StreamWorkflowReplicationMessagesResponse_Messages); ok { return x.Messages @@ -7476,7 +7499,7 @@ type isStreamWorkflowReplicationMessagesResponse_Attributes interface { } type StreamWorkflowReplicationMessagesResponse_Messages struct { - Messages *v117.WorkflowReplicationMessages `protobuf:"bytes,1,opt,name=messages,proto3,oneof"` + Messages *v118.WorkflowReplicationMessages `protobuf:"bytes,1,opt,name=messages,proto3,oneof"` } func (*StreamWorkflowReplicationMessagesResponse_Messages) isStreamWorkflowReplicationMessagesResponse_Attributes() { @@ -7837,7 +7860,7 @@ func (x *GetWorkflowExecutionHistoryReverseResponse) GetResponse() *v1.GetWorkfl type GetWorkflowExecutionRawHistoryV2Request struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - Request *v118.GetWorkflowExecutionRawHistoryV2Request `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Request *v119.GetWorkflowExecutionRawHistoryV2Request `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -7879,7 +7902,7 @@ func (x *GetWorkflowExecutionRawHistoryV2Request) GetNamespaceId() string { return "" } -func (x *GetWorkflowExecutionRawHistoryV2Request) GetRequest() *v118.GetWorkflowExecutionRawHistoryV2Request { +func (x *GetWorkflowExecutionRawHistoryV2Request) GetRequest() *v119.GetWorkflowExecutionRawHistoryV2Request { if x != nil { return x.Request } @@ -7888,7 +7911,7 @@ func (x *GetWorkflowExecutionRawHistoryV2Request) GetRequest() *v118.GetWorkflow type GetWorkflowExecutionRawHistoryV2Response struct { state protoimpl.MessageState `protogen:"open.v1"` - Response *v118.GetWorkflowExecutionRawHistoryV2Response `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + Response *v119.GetWorkflowExecutionRawHistoryV2Response `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -7923,7 +7946,7 @@ func (*GetWorkflowExecutionRawHistoryV2Response) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{117} } -func (x *GetWorkflowExecutionRawHistoryV2Response) GetResponse() *v118.GetWorkflowExecutionRawHistoryV2Response { +func (x *GetWorkflowExecutionRawHistoryV2Response) GetResponse() *v119.GetWorkflowExecutionRawHistoryV2Response { if x != nil { return x.Response } @@ -7933,7 +7956,7 @@ func (x *GetWorkflowExecutionRawHistoryV2Response) GetResponse() *v118.GetWorkfl type GetWorkflowExecutionRawHistoryRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - Request *v118.GetWorkflowExecutionRawHistoryRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Request *v119.GetWorkflowExecutionRawHistoryRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -7975,7 +7998,7 @@ func (x *GetWorkflowExecutionRawHistoryRequest) GetNamespaceId() string { return "" } -func (x *GetWorkflowExecutionRawHistoryRequest) GetRequest() *v118.GetWorkflowExecutionRawHistoryRequest { +func (x *GetWorkflowExecutionRawHistoryRequest) GetRequest() *v119.GetWorkflowExecutionRawHistoryRequest { if x != nil { return x.Request } @@ -7984,7 +8007,7 @@ func (x *GetWorkflowExecutionRawHistoryRequest) GetRequest() *v118.GetWorkflowEx type GetWorkflowExecutionRawHistoryResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Response *v118.GetWorkflowExecutionRawHistoryResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + Response *v119.GetWorkflowExecutionRawHistoryResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -8019,7 +8042,7 @@ func (*GetWorkflowExecutionRawHistoryResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{119} } -func (x *GetWorkflowExecutionRawHistoryResponse) GetResponse() *v118.GetWorkflowExecutionRawHistoryResponse { +func (x *GetWorkflowExecutionRawHistoryResponse) GetResponse() *v119.GetWorkflowExecutionRawHistoryResponse { if x != nil { return x.Response } @@ -8031,7 +8054,7 @@ type ForceDeleteWorkflowExecutionRequest struct { NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` // (-- api-linter: core::0141::forbidden-types=disabled --) ArchetypeId uint32 `protobuf:"varint,3,opt,name=archetype_id,json=archetypeId,proto3" json:"archetype_id,omitempty"` - Request *v118.DeleteWorkflowExecutionRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Request *v119.DeleteWorkflowExecutionRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -8080,7 +8103,7 @@ func (x *ForceDeleteWorkflowExecutionRequest) GetArchetypeId() uint32 { return 0 } -func (x *ForceDeleteWorkflowExecutionRequest) GetRequest() *v118.DeleteWorkflowExecutionRequest { +func (x *ForceDeleteWorkflowExecutionRequest) GetRequest() *v119.DeleteWorkflowExecutionRequest { if x != nil { return x.Request } @@ -8089,7 +8112,7 @@ func (x *ForceDeleteWorkflowExecutionRequest) GetRequest() *v118.DeleteWorkflowE type ForceDeleteWorkflowExecutionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Response *v118.DeleteWorkflowExecutionResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + Response *v119.DeleteWorkflowExecutionResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -8124,7 +8147,7 @@ func (*ForceDeleteWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{121} } -func (x *ForceDeleteWorkflowExecutionResponse) GetResponse() *v118.DeleteWorkflowExecutionResponse { +func (x *ForceDeleteWorkflowExecutionResponse) GetResponse() *v119.DeleteWorkflowExecutionResponse { if x != nil { return x.Response } @@ -8246,7 +8269,7 @@ func (*DeleteExecutionResponse) Descriptor() ([]byte, []int) { type GetDLQTasksRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - DlqKey *v119.HistoryDLQKey `protobuf:"bytes,1,opt,name=dlq_key,json=dlqKey,proto3" json:"dlq_key,omitempty"` + DlqKey *v120.HistoryDLQKey `protobuf:"bytes,1,opt,name=dlq_key,json=dlqKey,proto3" json:"dlq_key,omitempty"` // page_size must be positive. Up to this many tasks will be returned. PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` NextPageToken []byte `protobuf:"bytes,3,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` @@ -8284,7 +8307,7 @@ func (*GetDLQTasksRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{124} } -func (x *GetDLQTasksRequest) GetDlqKey() *v119.HistoryDLQKey { +func (x *GetDLQTasksRequest) GetDlqKey() *v120.HistoryDLQKey { if x != nil { return x.DlqKey } @@ -8307,7 +8330,7 @@ func (x *GetDLQTasksRequest) GetNextPageToken() []byte { type GetDLQTasksResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - DlqTasks []*v119.HistoryDLQTask `protobuf:"bytes,1,rep,name=dlq_tasks,json=dlqTasks,proto3" json:"dlq_tasks,omitempty"` + DlqTasks []*v120.HistoryDLQTask `protobuf:"bytes,1,rep,name=dlq_tasks,json=dlqTasks,proto3" json:"dlq_tasks,omitempty"` // next_page_token is empty if there are no more results. However, the converse is not true. If there are no more // results, this field may still be non-empty. This is to avoid having to do a count query to determine whether // there are more results. @@ -8346,7 +8369,7 @@ func (*GetDLQTasksResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{125} } -func (x *GetDLQTasksResponse) GetDlqTasks() []*v119.HistoryDLQTask { +func (x *GetDLQTasksResponse) GetDlqTasks() []*v120.HistoryDLQTask { if x != nil { return x.DlqTasks } @@ -8362,8 +8385,8 @@ func (x *GetDLQTasksResponse) GetNextPageToken() []byte { type DeleteDLQTasksRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - DlqKey *v119.HistoryDLQKey `protobuf:"bytes,1,opt,name=dlq_key,json=dlqKey,proto3" json:"dlq_key,omitempty"` - InclusiveMaxTaskMetadata *v119.HistoryDLQTaskMetadata `protobuf:"bytes,2,opt,name=inclusive_max_task_metadata,json=inclusiveMaxTaskMetadata,proto3" json:"inclusive_max_task_metadata,omitempty"` + DlqKey *v120.HistoryDLQKey `protobuf:"bytes,1,opt,name=dlq_key,json=dlqKey,proto3" json:"dlq_key,omitempty"` + InclusiveMaxTaskMetadata *v120.HistoryDLQTaskMetadata `protobuf:"bytes,2,opt,name=inclusive_max_task_metadata,json=inclusiveMaxTaskMetadata,proto3" json:"inclusive_max_task_metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -8398,14 +8421,14 @@ func (*DeleteDLQTasksRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{126} } -func (x *DeleteDLQTasksRequest) GetDlqKey() *v119.HistoryDLQKey { +func (x *DeleteDLQTasksRequest) GetDlqKey() *v120.HistoryDLQKey { if x != nil { return x.DlqKey } return nil } -func (x *DeleteDLQTasksRequest) GetInclusiveMaxTaskMetadata() *v119.HistoryDLQTaskMetadata { +func (x *DeleteDLQTasksRequest) GetInclusiveMaxTaskMetadata() *v120.HistoryDLQTaskMetadata { if x != nil { return x.InclusiveMaxTaskMetadata } @@ -8663,7 +8686,7 @@ func (*AddTasksResponse) Descriptor() ([]byte, []int) { type ListTasksRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Request *v118.ListHistoryTasksRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` + Request *v119.ListHistoryTasksRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -8698,7 +8721,7 @@ func (*ListTasksRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{132} } -func (x *ListTasksRequest) GetRequest() *v118.ListHistoryTasksRequest { +func (x *ListTasksRequest) GetRequest() *v119.ListHistoryTasksRequest { if x != nil { return x.Request } @@ -8707,7 +8730,7 @@ func (x *ListTasksRequest) GetRequest() *v118.ListHistoryTasksRequest { type ListTasksResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Response *v118.ListHistoryTasksResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + Response *v119.ListHistoryTasksResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -8742,7 +8765,7 @@ func (*ListTasksResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{133} } -func (x *ListTasksResponse) GetResponse() *v118.ListHistoryTasksResponse { +func (x *ListTasksResponse) GetResponse() *v119.ListHistoryTasksResponse { if x != nil { return x.Response } @@ -8752,7 +8775,7 @@ func (x *ListTasksResponse) GetResponse() *v118.ListHistoryTasksResponse { type CompleteNexusOperationChasmRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Completion token - holds information for locating an entity and the corresponding component. - Completion *v120.NexusOperationCompletion `protobuf:"bytes,1,opt,name=completion,proto3" json:"completion,omitempty"` + Completion *v121.NexusOperationCompletion `protobuf:"bytes,1,opt,name=completion,proto3" json:"completion,omitempty"` // Types that are valid to be assigned to Outcome: // // *CompleteNexusOperationChasmRequest_Success @@ -8801,7 +8824,7 @@ func (*CompleteNexusOperationChasmRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{134} } -func (x *CompleteNexusOperationChasmRequest) GetCompletion() *v120.NexusOperationCompletion { +func (x *CompleteNexusOperationChasmRequest) GetCompletion() *v121.NexusOperationCompletion { if x != nil { return x.Completion } @@ -8918,7 +8941,7 @@ func (*CompleteNexusOperationChasmResponse) Descriptor() ([]byte, []int) { type CompleteNexusOperationRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Completion token - holds information for locating a run and the corresponding operation state machine. - Completion *v120.NexusOperationCompletion `protobuf:"bytes,1,opt,name=completion,proto3" json:"completion,omitempty"` + Completion *v121.NexusOperationCompletion `protobuf:"bytes,1,opt,name=completion,proto3" json:"completion,omitempty"` // Operation state - may only be successful / failed / canceled. State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` // Types that are valid to be assigned to Outcome: @@ -8966,7 +8989,7 @@ func (*CompleteNexusOperationRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{136} } -func (x *CompleteNexusOperationRequest) GetCompletion() *v120.NexusOperationCompletion { +func (x *CompleteNexusOperationRequest) GetCompletion() *v121.NexusOperationCompletion { if x != nil { return x.Completion } @@ -8996,7 +9019,7 @@ func (x *CompleteNexusOperationRequest) GetSuccess() *v14.Payload { return nil } -func (x *CompleteNexusOperationRequest) GetFailure() *v121.Failure { +func (x *CompleteNexusOperationRequest) GetFailure() *v122.Failure { if x != nil { if x, ok := x.Outcome.(*CompleteNexusOperationRequest_Failure); ok { return x.Failure @@ -9037,7 +9060,7 @@ type CompleteNexusOperationRequest_Success struct { type CompleteNexusOperationRequest_Failure struct { // Operation failure, only set if state != successful. - Failure *v121.Failure `protobuf:"bytes,4,opt,name=failure,proto3,oneof"` + Failure *v122.Failure `protobuf:"bytes,4,opt,name=failure,proto3,oneof"` } func (*CompleteNexusOperationRequest_Success) isCompleteNexusOperationRequest_Outcome() {} @@ -9268,7 +9291,7 @@ type DeepHealthCheckResponse struct { state protoimpl.MessageState `protogen:"open.v1"` State v112.HealthState `protobuf:"varint,1,opt,name=state,proto3,enum=temporal.server.api.enums.v1.HealthState" json:"state,omitempty"` // Per-check diagnostic results. Populated for all checks regardless of state. - Checks []*v122.HealthCheck `protobuf:"bytes,2,rep,name=checks,proto3" json:"checks,omitempty"` + Checks []*v123.HealthCheck `protobuf:"bytes,2,rep,name=checks,proto3" json:"checks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -9310,7 +9333,7 @@ func (x *DeepHealthCheckResponse) GetState() v112.HealthState { return v112.HealthState(0) } -func (x *DeepHealthCheckResponse) GetChecks() []*v122.HealthCheck { +func (x *DeepHealthCheckResponse) GetChecks() []*v123.HealthCheck { if x != nil { return x.Checks } @@ -9404,7 +9427,7 @@ func (x *SyncWorkflowStateRequest) GetArchetypeId() uint32 { type SyncWorkflowStateResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - VersionedTransitionArtifact *v117.VersionedTransitionArtifact `protobuf:"bytes,5,opt,name=versioned_transition_artifact,json=versionedTransitionArtifact,proto3" json:"versioned_transition_artifact,omitempty"` + VersionedTransitionArtifact *v118.VersionedTransitionArtifact `protobuf:"bytes,5,opt,name=versioned_transition_artifact,json=versionedTransitionArtifact,proto3" json:"versioned_transition_artifact,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -9439,7 +9462,7 @@ func (*SyncWorkflowStateResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{143} } -func (x *SyncWorkflowStateResponse) GetVersionedTransitionArtifact() *v117.VersionedTransitionArtifact { +func (x *SyncWorkflowStateResponse) GetVersionedTransitionArtifact() *v118.VersionedTransitionArtifact { if x != nil { return x.VersionedTransitionArtifact } @@ -9504,7 +9527,7 @@ func (x *UpdateActivityOptionsRequest) GetUpdateRequest() *v1.UpdateActivityOpti type UpdateActivityOptionsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Activity options after an update - ActivityOptions *v123.ActivityOptions `protobuf:"bytes,1,opt,name=activity_options,json=activityOptions,proto3" json:"activity_options,omitempty"` + ActivityOptions *v124.ActivityOptions `protobuf:"bytes,1,opt,name=activity_options,json=activityOptions,proto3" json:"activity_options,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -9539,7 +9562,7 @@ func (*UpdateActivityOptionsResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{145} } -func (x *UpdateActivityOptionsResponse) GetActivityOptions() *v123.ActivityOptions { +func (x *UpdateActivityOptionsResponse) GetActivityOptions() *v124.ActivityOptions { if x != nil { return x.ActivityOptions } @@ -10104,7 +10127,7 @@ type StartNexusOperationRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` - Request *v121.StartOperationRequest `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` + Request *v122.StartOperationRequest `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -10153,7 +10176,7 @@ func (x *StartNexusOperationRequest) GetShardId() int32 { return 0 } -func (x *StartNexusOperationRequest) GetRequest() *v121.StartOperationRequest { +func (x *StartNexusOperationRequest) GetRequest() *v122.StartOperationRequest { if x != nil { return x.Request } @@ -10162,7 +10185,7 @@ func (x *StartNexusOperationRequest) GetRequest() *v121.StartOperationRequest { type StartNexusOperationResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Response *v121.StartOperationResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + Response *v122.StartOperationResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -10197,7 +10220,7 @@ func (*StartNexusOperationResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{159} } -func (x *StartNexusOperationResponse) GetResponse() *v121.StartOperationResponse { +func (x *StartNexusOperationResponse) GetResponse() *v122.StartOperationResponse { if x != nil { return x.Response } @@ -10208,7 +10231,7 @@ type CancelNexusOperationRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` - Request *v121.CancelOperationRequest `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` + Request *v122.CancelOperationRequest `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -10257,7 +10280,7 @@ func (x *CancelNexusOperationRequest) GetShardId() int32 { return 0 } -func (x *CancelNexusOperationRequest) GetRequest() *v121.CancelOperationRequest { +func (x *CancelNexusOperationRequest) GetRequest() *v122.CancelOperationRequest { if x != nil { return x.Request } @@ -10266,7 +10289,7 @@ func (x *CancelNexusOperationRequest) GetRequest() *v121.CancelOperationRequest type CancelNexusOperationResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Response *v121.CancelOperationResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + Response *v122.CancelOperationResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -10301,7 +10324,7 @@ func (*CancelNexusOperationResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{161} } -func (x *CancelNexusOperationResponse) GetResponse() *v121.CancelOperationResponse { +func (x *CancelNexusOperationResponse) GetResponse() *v122.CancelOperationResponse { if x != nil { return x.Response } @@ -10709,7 +10732,7 @@ var File_temporal_server_api_historyservice_v1_request_response_proto protorefle const file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc = "" + "\n" + - " temporal.api.workflowservice.v1.StartWorkflowExecutionRequest @@ -11858,217 +11882,219 @@ var file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs = 204, // 60: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.messages:type_name -> temporal.api.protocol.v1.Message 205, // 61: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.history:type_name -> temporal.api.history.v1.History 205, // 62: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.raw_history:type_name -> temporal.api.history.v1.History - 194, // 63: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 199, // 64: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 195, // 65: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 175, // 66: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 67: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.started_time:type_name -> google.protobuf.Timestamp - 167, // 68: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry - 187, // 69: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 204, // 70: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message - 205, // 71: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History - 191, // 72: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 206, // 73: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest - 187, // 74: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 201, // 75: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo - 202, // 76: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment - 203, // 77: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective - 207, // 78: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.scheduled_event:type_name -> temporal.api.history.v1.HistoryEvent - 175, // 79: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp - 175, // 80: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.current_attempt_scheduled_time:type_name -> google.protobuf.Timestamp - 178, // 81: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.heartbeat_details:type_name -> temporal.api.common.v1.Payloads - 194, // 82: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 187, // 83: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 208, // 84: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.priority:type_name -> temporal.api.common.v1.Priority - 209, // 85: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy - 210, // 86: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest - 12, // 87: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.started_response:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse - 211, // 88: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.activity_tasks:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueResponse - 188, // 89: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.new_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse - 212, // 90: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest - 191, // 91: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 92: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 213, // 93: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest.heartbeat_request:type_name -> temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest - 214, // 94: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest - 215, // 95: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest - 216, // 96: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest - 191, // 97: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 98: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 217, // 99: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.signal_request:type_name -> temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest - 191, // 100: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 190, // 101: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link - 218, // 102: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest - 190, // 103: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse.signal_link:type_name -> temporal.api.common.v1.Link - 191, // 104: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 219, // 105: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest - 191, // 106: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 107: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 220, // 108: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest - 221, // 109: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest - 191, // 110: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 111: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 112: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 187, // 113: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 191, // 114: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 115: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 191, // 116: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 117: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 207, // 118: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent - 187, // 119: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 191, // 120: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 121: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 122: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 222, // 123: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest - 223, // 124: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig - 224, // 125: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo - 225, // 126: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo - 226, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo - 227, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo - 228, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo - 229, // 130: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo - 230, // 131: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo - 191, // 132: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 192, // 133: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 231, // 134: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob - 231, // 135: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob - 232, // 136: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 233, // 137: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 175, // 138: temporal.server.api.historyservice.v1.SyncShardStatusRequest.status_time:type_name -> google.protobuf.Timestamp - 175, // 139: temporal.server.api.historyservice.v1.SyncActivityRequest.scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 140: temporal.server.api.historyservice.v1.SyncActivityRequest.started_time:type_name -> google.protobuf.Timestamp - 175, // 141: temporal.server.api.historyservice.v1.SyncActivityRequest.last_heartbeat_time:type_name -> google.protobuf.Timestamp - 178, // 142: temporal.server.api.historyservice.v1.SyncActivityRequest.details:type_name -> temporal.api.common.v1.Payloads - 177, // 143: temporal.server.api.historyservice.v1.SyncActivityRequest.last_failure:type_name -> temporal.api.failure.v1.Failure - 234, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 232, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 175, // 146: temporal.server.api.historyservice.v1.SyncActivityRequest.first_scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 147: temporal.server.api.historyservice.v1.SyncActivityRequest.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 179, // 148: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_initial_interval:type_name -> google.protobuf.Duration - 179, // 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 - 175, // 151: temporal.server.api.historyservice.v1.ActivitySyncInfo.scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 152: temporal.server.api.historyservice.v1.ActivitySyncInfo.started_time:type_name -> google.protobuf.Timestamp - 175, // 153: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_heartbeat_time:type_name -> google.protobuf.Timestamp - 178, // 154: temporal.server.api.historyservice.v1.ActivitySyncInfo.details:type_name -> temporal.api.common.v1.Payloads - 177, // 155: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_failure:type_name -> temporal.api.failure.v1.Failure - 234, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 175, // 157: temporal.server.api.historyservice.v1.ActivitySyncInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 158: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 179, // 159: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_initial_interval:type_name -> google.protobuf.Duration - 179, // 160: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_maximum_interval:type_name -> google.protobuf.Duration - 191, // 161: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 233, // 162: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 233, // 163: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 191, // 164: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 235, // 165: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo - 236, // 166: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo - 175, // 167: temporal.server.api.historyservice.v1.RemoveTaskRequest.visibility_time:type_name -> google.protobuf.Timestamp - 237, // 168: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken - 168, // 169: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.shard_messages:type_name -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry - 238, // 170: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 239, // 171: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 240, // 172: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest - 241, // 173: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse - 242, // 174: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest - 243, // 175: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 243, // 176: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 239, // 177: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 238, // 178: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 243, // 179: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 243, // 180: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 244, // 181: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest - 191, // 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 - 175, // 184: temporal.server.api.historyservice.v1.ShardReplicationStatus.shard_local_time:type_name -> google.protobuf.Timestamp - 169, // 185: temporal.server.api.historyservice.v1.ShardReplicationStatus.remote_clusters:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry - 170, // 186: temporal.server.api.historyservice.v1.ShardReplicationStatus.handover_namespaces:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry - 175, // 187: temporal.server.api.historyservice.v1.ShardReplicationStatus.max_replication_task_visibility_time:type_name -> google.protobuf.Timestamp - 175, // 188: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster.acked_task_visibility_time:type_name -> google.protobuf.Timestamp - 191, // 189: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 190: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 231, // 191: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob - 234, // 192: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 191, // 193: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 175, // 194: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_start_time:type_name -> google.protobuf.Timestamp - 175, // 195: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_close_time:type_name -> google.protobuf.Timestamp - 245, // 196: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest - 246, // 197: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse - 247, // 198: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState - 248, // 199: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages - 249, // 200: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest - 250, // 201: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse - 251, // 202: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest - 252, // 203: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 205, // 204: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History - 252, // 205: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 253, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest - 254, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse - 255, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request - 256, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response - 257, // 210: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest - 258, // 211: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse - 259, // 212: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest - 260, // 213: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse - 191, // 214: temporal.server.api.historyservice.v1.DeleteExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 261, // 215: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 262, // 216: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask - 261, // 217: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 263, // 218: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata - 171, // 219: temporal.server.api.historyservice.v1.ListQueuesResponse.queues:type_name -> temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo - 172, // 220: temporal.server.api.historyservice.v1.AddTasksRequest.tasks:type_name -> temporal.server.api.historyservice.v1.AddTasksRequest.Task - 264, // 221: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest - 265, // 222: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse - 266, // 223: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 267, // 224: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload - 177, // 225: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.failure:type_name -> temporal.api.failure.v1.Failure - 175, // 226: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.close_time:type_name -> google.protobuf.Timestamp - 190, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link - 175, // 228: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.start_time:type_name -> google.protobuf.Timestamp - 266, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 267, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload - 268, // 231: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure - 175, // 232: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.start_time:type_name -> google.protobuf.Timestamp - 190, // 233: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link - 269, // 234: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef - 270, // 235: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState - 271, // 236: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck - 191, // 237: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 193, // 238: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 197, // 239: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 272, // 240: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact - 273, // 241: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest - 274, // 242: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions - 275, // 243: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest - 276, // 244: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest - 277, // 245: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest - 278, // 246: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest - 279, // 247: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions - 175, // 248: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.update_time:type_name -> google.protobuf.Timestamp - 280, // 249: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest - 281, // 250: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest - 282, // 251: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest - 283, // 252: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse - 284, // 253: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest - 285, // 254: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse - 286, // 255: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest - 287, // 256: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse - 1, // 257: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest - 105, // 258: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest - 2, // 259: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse - 106, // 260: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse - 288, // 261: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 288, // 262: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 289, // 263: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages - 98, // 264: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster - 97, // 265: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo - 231, // 266: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob - 290, // 267: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions - 0, // 268: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions - 269, // [269:269] is the sub-list for method output_type - 269, // [269:269] is the sub-list for method input_type - 268, // [268:269] is the sub-list for extension type_name - 267, // [267:268] is the sub-list for extension extendee - 0, // [0:267] is the sub-list for field type_name + 206, // 63: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice + 194, // 64: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 199, // 65: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 195, // 66: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 175, // 67: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.scheduled_time:type_name -> google.protobuf.Timestamp + 175, // 68: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.started_time:type_name -> google.protobuf.Timestamp + 167, // 69: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry + 187, // 70: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 204, // 71: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message + 205, // 72: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History + 206, // 73: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice + 191, // 74: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 207, // 75: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest + 187, // 76: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 201, // 77: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo + 202, // 78: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment + 203, // 79: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 208, // 80: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.scheduled_event:type_name -> temporal.api.history.v1.HistoryEvent + 175, // 81: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp + 175, // 82: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.current_attempt_scheduled_time:type_name -> google.protobuf.Timestamp + 178, // 83: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.heartbeat_details:type_name -> temporal.api.common.v1.Payloads + 194, // 84: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 187, // 85: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 209, // 86: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.priority:type_name -> temporal.api.common.v1.Priority + 210, // 87: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy + 211, // 88: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest + 12, // 89: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.started_response:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse + 212, // 90: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.activity_tasks:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueResponse + 188, // 91: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.new_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse + 213, // 92: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest + 191, // 93: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 187, // 94: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 214, // 95: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest.heartbeat_request:type_name -> temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest + 215, // 96: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest + 216, // 97: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest + 217, // 98: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest + 191, // 99: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 187, // 100: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 218, // 101: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.signal_request:type_name -> temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest + 191, // 102: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 190, // 103: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link + 219, // 104: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest + 190, // 105: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse.signal_link:type_name -> temporal.api.common.v1.Link + 191, // 106: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 220, // 107: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest + 191, // 108: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 191, // 109: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 221, // 110: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest + 222, // 111: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest + 191, // 112: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 191, // 113: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 187, // 114: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 187, // 115: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 191, // 116: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 187, // 117: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 191, // 118: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 191, // 119: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 208, // 120: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent + 187, // 121: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 191, // 122: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 191, // 123: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 187, // 124: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 223, // 125: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest + 224, // 126: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig + 225, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo + 226, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo + 227, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo + 228, // 130: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo + 229, // 131: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo + 230, // 132: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo + 231, // 133: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo + 191, // 134: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 192, // 135: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 232, // 136: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob + 232, // 137: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob + 233, // 138: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 234, // 139: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 175, // 140: temporal.server.api.historyservice.v1.SyncShardStatusRequest.status_time:type_name -> google.protobuf.Timestamp + 175, // 141: temporal.server.api.historyservice.v1.SyncActivityRequest.scheduled_time:type_name -> google.protobuf.Timestamp + 175, // 142: temporal.server.api.historyservice.v1.SyncActivityRequest.started_time:type_name -> google.protobuf.Timestamp + 175, // 143: temporal.server.api.historyservice.v1.SyncActivityRequest.last_heartbeat_time:type_name -> google.protobuf.Timestamp + 178, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.details:type_name -> temporal.api.common.v1.Payloads + 177, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.last_failure:type_name -> temporal.api.failure.v1.Failure + 235, // 146: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 233, // 147: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 175, // 148: temporal.server.api.historyservice.v1.SyncActivityRequest.first_scheduled_time:type_name -> google.protobuf.Timestamp + 175, // 149: temporal.server.api.historyservice.v1.SyncActivityRequest.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 179, // 150: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_initial_interval:type_name -> google.protobuf.Duration + 179, // 151: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_maximum_interval:type_name -> google.protobuf.Duration + 64, // 152: temporal.server.api.historyservice.v1.SyncActivitiesRequest.activities_info:type_name -> temporal.server.api.historyservice.v1.ActivitySyncInfo + 175, // 153: temporal.server.api.historyservice.v1.ActivitySyncInfo.scheduled_time:type_name -> google.protobuf.Timestamp + 175, // 154: temporal.server.api.historyservice.v1.ActivitySyncInfo.started_time:type_name -> google.protobuf.Timestamp + 175, // 155: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_heartbeat_time:type_name -> google.protobuf.Timestamp + 178, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.details:type_name -> temporal.api.common.v1.Payloads + 177, // 157: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_failure:type_name -> temporal.api.failure.v1.Failure + 235, // 158: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 175, // 159: temporal.server.api.historyservice.v1.ActivitySyncInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp + 175, // 160: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 179, // 161: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_initial_interval:type_name -> google.protobuf.Duration + 179, // 162: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_maximum_interval:type_name -> google.protobuf.Duration + 191, // 163: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 234, // 164: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 234, // 165: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 191, // 166: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 236, // 167: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo + 237, // 168: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo + 175, // 169: temporal.server.api.historyservice.v1.RemoveTaskRequest.visibility_time:type_name -> google.protobuf.Timestamp + 238, // 170: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken + 168, // 171: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.shard_messages:type_name -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry + 239, // 172: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 240, // 173: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 241, // 174: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest + 242, // 175: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse + 243, // 176: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest + 244, // 177: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 244, // 178: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 240, // 179: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 239, // 180: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 244, // 181: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 244, // 182: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 245, // 183: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest + 191, // 184: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 96, // 185: temporal.server.api.historyservice.v1.GetReplicationStatusResponse.shards:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus + 175, // 186: temporal.server.api.historyservice.v1.ShardReplicationStatus.shard_local_time:type_name -> google.protobuf.Timestamp + 169, // 187: temporal.server.api.historyservice.v1.ShardReplicationStatus.remote_clusters:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry + 170, // 188: temporal.server.api.historyservice.v1.ShardReplicationStatus.handover_namespaces:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry + 175, // 189: temporal.server.api.historyservice.v1.ShardReplicationStatus.max_replication_task_visibility_time:type_name -> google.protobuf.Timestamp + 175, // 190: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster.acked_task_visibility_time:type_name -> google.protobuf.Timestamp + 191, // 191: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 191, // 192: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 232, // 193: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob + 235, // 194: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 191, // 195: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 175, // 196: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_start_time:type_name -> google.protobuf.Timestamp + 175, // 197: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_close_time:type_name -> google.protobuf.Timestamp + 246, // 198: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest + 247, // 199: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse + 248, // 200: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState + 249, // 201: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages + 250, // 202: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest + 251, // 203: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse + 252, // 204: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest + 253, // 205: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 205, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History + 253, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 254, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest + 255, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse + 256, // 210: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request + 257, // 211: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response + 258, // 212: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest + 259, // 213: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse + 260, // 214: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest + 261, // 215: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse + 191, // 216: temporal.server.api.historyservice.v1.DeleteExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 262, // 217: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 263, // 218: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask + 262, // 219: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 264, // 220: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata + 171, // 221: temporal.server.api.historyservice.v1.ListQueuesResponse.queues:type_name -> temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo + 172, // 222: temporal.server.api.historyservice.v1.AddTasksRequest.tasks:type_name -> temporal.server.api.historyservice.v1.AddTasksRequest.Task + 265, // 223: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest + 266, // 224: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse + 267, // 225: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 268, // 226: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload + 177, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.failure:type_name -> temporal.api.failure.v1.Failure + 175, // 228: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.close_time:type_name -> google.protobuf.Timestamp + 190, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link + 175, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.start_time:type_name -> google.protobuf.Timestamp + 267, // 231: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 268, // 232: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload + 269, // 233: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure + 175, // 234: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.start_time:type_name -> google.protobuf.Timestamp + 190, // 235: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link + 270, // 236: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef + 271, // 237: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState + 272, // 238: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck + 191, // 239: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 193, // 240: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 197, // 241: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 273, // 242: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact + 274, // 243: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest + 275, // 244: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions + 276, // 245: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest + 277, // 246: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest + 278, // 247: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest + 279, // 248: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest + 280, // 249: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions + 175, // 250: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.update_time:type_name -> google.protobuf.Timestamp + 281, // 251: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest + 282, // 252: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest + 283, // 253: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest + 284, // 254: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse + 285, // 255: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest + 286, // 256: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse + 287, // 257: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest + 288, // 258: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse + 1, // 259: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest + 105, // 260: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest + 2, // 261: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse + 106, // 262: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse + 289, // 263: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 289, // 264: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 290, // 265: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages + 98, // 266: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster + 97, // 267: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo + 232, // 268: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob + 291, // 269: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions + 0, // 270: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions + 271, // [271:271] is the sub-list for method output_type + 271, // [271:271] is the sub-list for method input_type + 270, // [270:271] is the sub-list for extension type_name + 269, // [269:270] is the sub-list for extension extendee + 0, // [0:269] is the sub-list for field type_name } func init() { file_temporal_server_api_historyservice_v1_request_response_proto_init() } diff --git a/api/matchingservice/v1/request_response.pb.go b/api/matchingservice/v1/request_response.pb.go index 4a2f7325773..bbdbe0254c9 100644 --- a/api/matchingservice/v1/request_response.pb.go +++ b/api/matchingservice/v1/request_response.pb.go @@ -12,22 +12,23 @@ import ( unsafe "unsafe" v11 "go.temporal.io/api/common/v1" - v112 "go.temporal.io/api/deployment/v1" - v19 "go.temporal.io/api/enums/v1" - v114 "go.temporal.io/api/failure/v1" + v113 "go.temporal.io/api/deployment/v1" + v110 "go.temporal.io/api/enums/v1" + v115 "go.temporal.io/api/failure/v1" v16 "go.temporal.io/api/history/v1" - v113 "go.temporal.io/api/nexus/v1" + v114 "go.temporal.io/api/nexus/v1" v15 "go.temporal.io/api/protocol/v1" v12 "go.temporal.io/api/query/v1" + v17 "go.temporal.io/api/stream/v1" v14 "go.temporal.io/api/taskqueue/v1" - v115 "go.temporal.io/api/worker/v1" + v116 "go.temporal.io/api/worker/v1" v1 "go.temporal.io/api/workflowservice/v1" - v17 "go.temporal.io/server/api/clock/v1" - v110 "go.temporal.io/server/api/deployment/v1" - v116 "go.temporal.io/server/api/enums/v1" + v18 "go.temporal.io/server/api/clock/v1" + v111 "go.temporal.io/server/api/deployment/v1" + v117 "go.temporal.io/server/api/enums/v1" v13 "go.temporal.io/server/api/history/v1" - v111 "go.temporal.io/server/api/persistence/v1" - v18 "go.temporal.io/server/api/taskqueue/v1" + v112 "go.temporal.io/server/api/persistence/v1" + v19 "go.temporal.io/server/api/taskqueue/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" @@ -145,7 +146,11 @@ type PollWorkflowTaskQueueResponse struct { PollerScalingDecision *v14.PollerScalingDecision `protobuf:"bytes,21,opt,name=poller_scaling_decision,json=pollerScalingDecision,proto3" json:"poller_scaling_decision,omitempty"` // Raw history bytes sent from matching service when history.sendRawHistoryBetweenInternalServices is enabled. // Matching client will deserialize this to History when it receives the response. - RawHistory *v16.History `protobuf:"bytes,22,opt,name=raw_history,json=rawHistory,proto3" json:"raw_history,omitempty"` + RawHistory *v16.History `protobuf:"bytes,22,opt,name=raw_history,json=rawHistory,proto3" json:"raw_history,omitempty"` + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + StreamSlices []*v17.StreamSlice `protobuf:"bytes,23,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -327,6 +332,13 @@ func (x *PollWorkflowTaskQueueResponse) GetRawHistory() *v16.History { return nil } +func (x *PollWorkflowTaskQueueResponse) GetStreamSlices() []*v17.StreamSlice { + if x != nil { + return x.StreamSlices + } + return nil +} + // PollWorkflowTaskQueueResponseWithRawHistory is wire-compatible with PollWorkflowTaskQueueResponse. // // WIRE COMPATIBILITY PATTERN: @@ -374,7 +386,11 @@ type PollWorkflowTaskQueueResponseWithRawHistory struct { // Raw history bytes. Each element is a proto-encoded batch of history events. // When matching client deserializes this to PollWorkflowTaskQueueResponse, this field // will be automatically deserialized to the raw_history field as History. - RawHistory [][]byte `protobuf:"bytes,22,rep,name=raw_history,json=rawHistory,proto3" json:"raw_history,omitempty"` + RawHistory [][]byte `protobuf:"bytes,22,rep,name=raw_history,json=rawHistory,proto3" json:"raw_history,omitempty"` + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + StreamSlices []*v17.StreamSlice `protobuf:"bytes,23,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -556,6 +572,13 @@ func (x *PollWorkflowTaskQueueResponseWithRawHistory) GetRawHistory() [][]byte { return nil } +func (x *PollWorkflowTaskQueueResponseWithRawHistory) GetStreamSlices() []*v17.StreamSlice { + if x != nil { + return x.StreamSlices + } + return nil +} + type PollActivityTaskQueueRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` @@ -846,11 +869,11 @@ type AddWorkflowTaskRequest struct { // // aip.dev/not-precedent: "to" is used to indicate interval. --) ScheduleToStartTimeout *durationpb.Duration `protobuf:"bytes,5,opt,name=schedule_to_start_timeout,json=scheduleToStartTimeout,proto3" json:"schedule_to_start_timeout,omitempty"` - Clock *v17.VectorClock `protobuf:"bytes,9,opt,name=clock,proto3" json:"clock,omitempty"` + Clock *v18.VectorClock `protobuf:"bytes,9,opt,name=clock,proto3" json:"clock,omitempty"` // How this task should be directed by matching. (Missing means the default // for TaskVersionDirective, which is unversioned.) - VersionDirective *v18.TaskVersionDirective `protobuf:"bytes,10,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` - ForwardInfo *v18.TaskForwardInfo `protobuf:"bytes,11,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` + VersionDirective *v19.TaskVersionDirective `protobuf:"bytes,10,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` + ForwardInfo *v19.TaskForwardInfo `protobuf:"bytes,11,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` Priority *v11.Priority `protobuf:"bytes,12,opt,name=priority,proto3" json:"priority,omitempty"` // Stamp value from when the workflow task was scheduled. Used to validate the task is still relevant. Stamp int32 `protobuf:"varint,13,opt,name=stamp,proto3" json:"stamp,omitempty"` @@ -923,21 +946,21 @@ func (x *AddWorkflowTaskRequest) GetScheduleToStartTimeout() *durationpb.Duratio return nil } -func (x *AddWorkflowTaskRequest) GetClock() *v17.VectorClock { +func (x *AddWorkflowTaskRequest) GetClock() *v18.VectorClock { if x != nil { return x.Clock } return nil } -func (x *AddWorkflowTaskRequest) GetVersionDirective() *v18.TaskVersionDirective { +func (x *AddWorkflowTaskRequest) GetVersionDirective() *v19.TaskVersionDirective { if x != nil { return x.VersionDirective } return nil } -func (x *AddWorkflowTaskRequest) GetForwardInfo() *v18.TaskForwardInfo { +func (x *AddWorkflowTaskRequest) GetForwardInfo() *v19.TaskForwardInfo { if x != nil { return x.ForwardInfo } @@ -1014,11 +1037,11 @@ type AddActivityTaskRequest struct { // // aip.dev/not-precedent: "to" is used to indicate interval. --) ScheduleToStartTimeout *durationpb.Duration `protobuf:"bytes,6,opt,name=schedule_to_start_timeout,json=scheduleToStartTimeout,proto3" json:"schedule_to_start_timeout,omitempty"` - Clock *v17.VectorClock `protobuf:"bytes,9,opt,name=clock,proto3" json:"clock,omitempty"` + Clock *v18.VectorClock `protobuf:"bytes,9,opt,name=clock,proto3" json:"clock,omitempty"` // How this task should be directed by matching. (Missing means the default // for TaskVersionDirective, which is unversioned.) - VersionDirective *v18.TaskVersionDirective `protobuf:"bytes,10,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` - ForwardInfo *v18.TaskForwardInfo `protobuf:"bytes,11,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` + VersionDirective *v19.TaskVersionDirective `protobuf:"bytes,10,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` + ForwardInfo *v19.TaskForwardInfo `protobuf:"bytes,11,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` Stamp int32 `protobuf:"varint,12,opt,name=stamp,proto3" json:"stamp,omitempty"` Priority *v11.Priority `protobuf:"bytes,13,opt,name=priority,proto3" json:"priority,omitempty"` // Reference to the Chasm component for activity execution (if applicable). For standalone activities, all @@ -1094,21 +1117,21 @@ func (x *AddActivityTaskRequest) GetScheduleToStartTimeout() *durationpb.Duratio return nil } -func (x *AddActivityTaskRequest) GetClock() *v17.VectorClock { +func (x *AddActivityTaskRequest) GetClock() *v18.VectorClock { if x != nil { return x.Clock } return nil } -func (x *AddActivityTaskRequest) GetVersionDirective() *v18.TaskVersionDirective { +func (x *AddActivityTaskRequest) GetVersionDirective() *v19.TaskVersionDirective { if x != nil { return x.VersionDirective } return nil } -func (x *AddActivityTaskRequest) GetForwardInfo() *v18.TaskForwardInfo { +func (x *AddActivityTaskRequest) GetForwardInfo() *v19.TaskForwardInfo { if x != nil { return x.ForwardInfo } @@ -1189,8 +1212,8 @@ type QueryWorkflowRequest struct { QueryRequest *v1.QueryWorkflowRequest `protobuf:"bytes,3,opt,name=query_request,json=queryRequest,proto3" json:"query_request,omitempty"` // How this task should be directed by matching. (Missing means the default // for TaskVersionDirective, which is unversioned.) - VersionDirective *v18.TaskVersionDirective `protobuf:"bytes,5,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` - ForwardInfo *v18.TaskForwardInfo `protobuf:"bytes,6,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` + VersionDirective *v19.TaskVersionDirective `protobuf:"bytes,5,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` + ForwardInfo *v19.TaskForwardInfo `protobuf:"bytes,6,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` Priority *v11.Priority `protobuf:"bytes,7,opt,name=priority,proto3" json:"priority,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1247,14 +1270,14 @@ func (x *QueryWorkflowRequest) GetQueryRequest() *v1.QueryWorkflowRequest { return nil } -func (x *QueryWorkflowRequest) GetVersionDirective() *v18.TaskVersionDirective { +func (x *QueryWorkflowRequest) GetVersionDirective() *v19.TaskVersionDirective { if x != nil { return x.VersionDirective } return nil } -func (x *QueryWorkflowRequest) GetForwardInfo() *v18.TaskForwardInfo { +func (x *QueryWorkflowRequest) GetForwardInfo() *v19.TaskForwardInfo { if x != nil { return x.ForwardInfo } @@ -1427,7 +1450,7 @@ func (*RespondQueryTaskCompletedResponse) Descriptor() ([]byte, []int) { type CancelOutstandingPollRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - TaskQueueType v19.TaskQueueType `protobuf:"varint,2,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` + TaskQueueType v110.TaskQueueType `protobuf:"varint,2,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` TaskQueue *v14.TaskQueue `protobuf:"bytes,3,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` PollerId string `protobuf:"bytes,4,opt,name=poller_id,json=pollerId,proto3" json:"poller_id,omitempty"` unknownFields protoimpl.UnknownFields @@ -1471,11 +1494,11 @@ func (x *CancelOutstandingPollRequest) GetNamespaceId() string { return "" } -func (x *CancelOutstandingPollRequest) GetTaskQueueType() v19.TaskQueueType { +func (x *CancelOutstandingPollRequest) GetTaskQueueType() v110.TaskQueueType { if x != nil { return x.TaskQueueType } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } func (x *CancelOutstandingPollRequest) GetTaskQueue() *v14.TaskQueue { @@ -1533,7 +1556,7 @@ type CancelOutstandingWorkerPollsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` TaskQueue *v14.TaskQueue `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - TaskQueueType v19.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` + TaskQueueType v110.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` WorkerInstanceKey string `protobuf:"bytes,4,opt,name=worker_instance_key,json=workerInstanceKey,proto3" json:"worker_instance_key,omitempty"` // Worker identity string (e.g., "pid@hostname"). Used to eagerly remove the worker // from pollerHistory so DescribeTaskQueue doesn't show stale pollers. @@ -1588,11 +1611,11 @@ func (x *CancelOutstandingWorkerPollsRequest) GetTaskQueue() *v14.TaskQueue { return nil } -func (x *CancelOutstandingWorkerPollsRequest) GetTaskQueueType() v19.TaskQueueType { +func (x *CancelOutstandingWorkerPollsRequest) GetTaskQueueType() v110.TaskQueueType { if x != nil { return x.TaskQueueType } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } func (x *CancelOutstandingWorkerPollsRequest) GetWorkerInstanceKey() string { @@ -1661,9 +1684,9 @@ type CancelOutstandingWorkerPollsPartitionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` // Used for RPC routing only. Set to any partition on the target host. - TaskQueuePartition *v18.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` + TaskQueuePartition *v19.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` // Partitions to process on this host. - Partitions []*v18.TaskQueuePartition `protobuf:"bytes,3,rep,name=partitions,proto3" json:"partitions,omitempty"` + Partitions []*v19.TaskQueuePartition `protobuf:"bytes,3,rep,name=partitions,proto3" json:"partitions,omitempty"` // Workers to cancel polls for. Workers []*CancelOutstandingWorkerPollsPartitionRequest_WorkerEntry `protobuf:"bytes,4,rep,name=workers,proto3" json:"workers,omitempty"` unknownFields protoimpl.UnknownFields @@ -1707,14 +1730,14 @@ func (x *CancelOutstandingWorkerPollsPartitionRequest) GetNamespaceId() string { return "" } -func (x *CancelOutstandingWorkerPollsPartitionRequest) GetTaskQueuePartition() *v18.TaskQueuePartition { +func (x *CancelOutstandingWorkerPollsPartitionRequest) GetTaskQueuePartition() *v19.TaskQueuePartition { if x != nil { return x.TaskQueuePartition } return nil } -func (x *CancelOutstandingWorkerPollsPartitionRequest) GetPartitions() []*v18.TaskQueuePartition { +func (x *CancelOutstandingWorkerPollsPartitionRequest) GetPartitions() []*v19.TaskQueuePartition { if x != nil { return x.Partitions } @@ -1776,7 +1799,7 @@ type DescribeTaskQueueRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` DescRequest *v1.DescribeTaskQueueRequest `protobuf:"bytes,2,opt,name=desc_request,json=descRequest,proto3" json:"desc_request,omitempty"` - Version *v110.WorkerDeploymentVersion `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Version *v111.WorkerDeploymentVersion `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1825,7 +1848,7 @@ func (x *DescribeTaskQueueRequest) GetDescRequest() *v1.DescribeTaskQueueRequest return nil } -func (x *DescribeTaskQueueRequest) GetVersion() *v110.WorkerDeploymentVersion { +func (x *DescribeTaskQueueRequest) GetVersion() *v111.WorkerDeploymentVersion { if x != nil { return x.Version } @@ -1880,9 +1903,9 @@ type DescribeVersionedTaskQueuesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` // This task queue is for routing purposes. - TaskQueueType v19.TaskQueueType `protobuf:"varint,2,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` + TaskQueueType v110.TaskQueueType `protobuf:"varint,2,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` TaskQueue *v14.TaskQueue `protobuf:"bytes,3,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - Version *v110.WorkerDeploymentVersion `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + Version *v111.WorkerDeploymentVersion `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` // List of task queues to describe. VersionTaskQueues []*DescribeVersionedTaskQueuesRequest_VersionTaskQueue `protobuf:"bytes,5,rep,name=version_task_queues,json=versionTaskQueues,proto3" json:"version_task_queues,omitempty"` unknownFields protoimpl.UnknownFields @@ -1926,11 +1949,11 @@ func (x *DescribeVersionedTaskQueuesRequest) GetNamespaceId() string { return "" } -func (x *DescribeVersionedTaskQueuesRequest) GetTaskQueueType() v19.TaskQueueType { +func (x *DescribeVersionedTaskQueuesRequest) GetTaskQueueType() v110.TaskQueueType { if x != nil { return x.TaskQueueType } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } func (x *DescribeVersionedTaskQueuesRequest) GetTaskQueue() *v14.TaskQueue { @@ -1940,7 +1963,7 @@ func (x *DescribeVersionedTaskQueuesRequest) GetTaskQueue() *v14.TaskQueue { return nil } -func (x *DescribeVersionedTaskQueuesRequest) GetVersion() *v110.WorkerDeploymentVersion { +func (x *DescribeVersionedTaskQueuesRequest) GetVersion() *v111.WorkerDeploymentVersion { if x != nil { return x.Version } @@ -2001,7 +2024,7 @@ func (x *DescribeVersionedTaskQueuesResponse) GetVersionTaskQueues() []*Describe type DescribeTaskQueuePartitionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - TaskQueuePartition *v18.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` + TaskQueuePartition *v19.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` Versions *v14.TaskQueueVersionSelection `protobuf:"bytes,3,opt,name=versions,proto3" json:"versions,omitempty"` // Report task queue stats for the requested task queue types and versions ReportStats bool `protobuf:"varint,4,opt,name=report_stats,json=reportStats,proto3" json:"report_stats,omitempty"` @@ -2053,7 +2076,7 @@ func (x *DescribeTaskQueuePartitionRequest) GetNamespaceId() string { return "" } -func (x *DescribeTaskQueuePartitionRequest) GetTaskQueuePartition() *v18.TaskQueuePartition { +func (x *DescribeTaskQueuePartitionRequest) GetTaskQueuePartition() *v19.TaskQueuePartition { if x != nil { return x.TaskQueuePartition } @@ -2097,8 +2120,8 @@ func (x *DescribeTaskQueuePartitionRequest) GetOnlyIfLoaded() bool { type DescribeTaskQueuePartitionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - VersionsInfoInternal map[string]*v18.TaskQueueVersionInfoInternal `protobuf:"bytes,1,rep,name=versions_info_internal,json=versionsInfoInternal,proto3" json:"versions_info_internal,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - ScaleInfo *v18.PartitionScaleInfo `protobuf:"bytes,2,opt,name=scale_info,json=scaleInfo,proto3" json:"scale_info,omitempty"` + VersionsInfoInternal map[string]*v19.TaskQueueVersionInfoInternal `protobuf:"bytes,1,rep,name=versions_info_internal,json=versionsInfoInternal,proto3" json:"versions_info_internal,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + ScaleInfo *v19.PartitionScaleInfo `protobuf:"bytes,2,opt,name=scale_info,json=scaleInfo,proto3" json:"scale_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2133,14 +2156,14 @@ func (*DescribeTaskQueuePartitionResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_matchingservice_v1_request_response_proto_rawDescGZIP(), []int{24} } -func (x *DescribeTaskQueuePartitionResponse) GetVersionsInfoInternal() map[string]*v18.TaskQueueVersionInfoInternal { +func (x *DescribeTaskQueuePartitionResponse) GetVersionsInfoInternal() map[string]*v19.TaskQueueVersionInfoInternal { if x != nil { return x.VersionsInfoInternal } return nil } -func (x *DescribeTaskQueuePartitionResponse) GetScaleInfo() *v18.PartitionScaleInfo { +func (x *DescribeTaskQueuePartitionResponse) GetScaleInfo() *v19.PartitionScaleInfo { if x != nil { return x.ScaleInfo } @@ -2779,8 +2802,8 @@ type GetTaskQueueUserDataRequest struct { NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` // The task queue to fetch data from. The task queue is always considered as a normal // queue, since sticky queues have no user data. - TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - TaskQueueType v19.TaskQueueType `protobuf:"varint,5,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` + TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` + TaskQueueType v110.TaskQueueType `protobuf:"varint,5,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` // The value of the last known user data version. // If the requester has no data, it should set this to 0. // This value must not be set to a negative number (note that our linter suggests avoiding uint64). @@ -2843,11 +2866,11 @@ func (x *GetTaskQueueUserDataRequest) GetTaskQueue() string { return "" } -func (x *GetTaskQueueUserDataRequest) GetTaskQueueType() v19.TaskQueueType { +func (x *GetTaskQueueUserDataRequest) GetTaskQueueType() v110.TaskQueueType { if x != nil { return x.TaskQueueType } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } func (x *GetTaskQueueUserDataRequest) GetLastKnownUserDataVersion() int64 { @@ -2882,8 +2905,8 @@ type GetTaskQueueUserDataResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Versioned user data, set if the task queue has user data and the request's last_known_user_data_version is less // than the version cached in the root partition. - UserData *v111.VersionedTaskQueueUserData `protobuf:"bytes,2,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` - EphemeralData *v18.VersionedEphemeralData `protobuf:"bytes,3,opt,name=ephemeral_data,json=ephemeralData,proto3" json:"ephemeral_data,omitempty"` + UserData *v112.VersionedTaskQueueUserData `protobuf:"bytes,2,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` + EphemeralData *v19.VersionedEphemeralData `protobuf:"bytes,3,opt,name=ephemeral_data,json=ephemeralData,proto3" json:"ephemeral_data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2918,14 +2941,14 @@ func (*GetTaskQueueUserDataResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_matchingservice_v1_request_response_proto_rawDescGZIP(), []int{36} } -func (x *GetTaskQueueUserDataResponse) GetUserData() *v111.VersionedTaskQueueUserData { +func (x *GetTaskQueueUserDataResponse) GetUserData() *v112.VersionedTaskQueueUserData { if x != nil { return x.UserData } return nil } -func (x *GetTaskQueueUserDataResponse) GetEphemeralData() *v18.VersionedEphemeralData { +func (x *GetTaskQueueUserDataResponse) GetEphemeralData() *v19.VersionedEphemeralData { if x != nil { return x.EphemeralData } @@ -2940,8 +2963,8 @@ type SyncDeploymentUserDataRequest struct { // (-- api-linter: core::0203::required=disabled // // aip.dev/not-precedent: Not following Google API format --) - DeploymentName string `protobuf:"bytes,9,opt,name=deployment_name,json=deploymentName,proto3" json:"deployment_name,omitempty"` - TaskQueueTypes []v19.TaskQueueType `protobuf:"varint,8,rep,packed,name=task_queue_types,json=taskQueueTypes,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_types,omitempty"` + DeploymentName string `protobuf:"bytes,9,opt,name=deployment_name,json=deploymentName,proto3" json:"deployment_name,omitempty"` + TaskQueueTypes []v110.TaskQueueType `protobuf:"varint,8,rep,packed,name=task_queue_types,json=taskQueueTypes,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_types,omitempty"` // Types that are valid to be assigned to Operation: // // *SyncDeploymentUserDataRequest_UpdateVersionData @@ -2949,12 +2972,12 @@ type SyncDeploymentUserDataRequest struct { Operation isSyncDeploymentUserDataRequest_Operation `protobuf_oneof:"operation"` // Absent means no change. // Ignored by the task queue if new revision number is not greater that what it has. - UpdateRoutingConfig *v112.RoutingConfig `protobuf:"bytes,10,opt,name=update_routing_config,json=updateRoutingConfig,proto3" json:"update_routing_config,omitempty"` + UpdateRoutingConfig *v113.RoutingConfig `protobuf:"bytes,10,opt,name=update_routing_config,json=updateRoutingConfig,proto3" json:"update_routing_config,omitempty"` // Optional map of build id to upsert version data. // (-- api-linter: core::0203::required=disabled // // aip.dev/not-precedent: Not following Google API format --) - UpsertVersionsData map[string]*v110.WorkerDeploymentVersionData `protobuf:"bytes,11,rep,name=upsert_versions_data,json=upsertVersionsData,proto3" json:"upsert_versions_data,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + UpsertVersionsData map[string]*v111.WorkerDeploymentVersionData `protobuf:"bytes,11,rep,name=upsert_versions_data,json=upsertVersionsData,proto3" json:"upsert_versions_data,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // List of build ids to forget from task queue. ForgetVersions []string `protobuf:"bytes,12,rep,name=forget_versions,json=forgetVersions,proto3" json:"forget_versions,omitempty"` unknownFields protoimpl.UnknownFields @@ -3012,7 +3035,7 @@ func (x *SyncDeploymentUserDataRequest) GetDeploymentName() string { return "" } -func (x *SyncDeploymentUserDataRequest) GetTaskQueueTypes() []v19.TaskQueueType { +func (x *SyncDeploymentUserDataRequest) GetTaskQueueTypes() []v110.TaskQueueType { if x != nil { return x.TaskQueueTypes } @@ -3027,7 +3050,7 @@ func (x *SyncDeploymentUserDataRequest) GetOperation() isSyncDeploymentUserDataR } // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. -func (x *SyncDeploymentUserDataRequest) GetUpdateVersionData() *v110.DeploymentVersionData { +func (x *SyncDeploymentUserDataRequest) GetUpdateVersionData() *v111.DeploymentVersionData { if x != nil { if x, ok := x.Operation.(*SyncDeploymentUserDataRequest_UpdateVersionData); ok { return x.UpdateVersionData @@ -3037,7 +3060,7 @@ func (x *SyncDeploymentUserDataRequest) GetUpdateVersionData() *v110.DeploymentV } // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. -func (x *SyncDeploymentUserDataRequest) GetForgetVersion() *v110.WorkerDeploymentVersion { +func (x *SyncDeploymentUserDataRequest) GetForgetVersion() *v111.WorkerDeploymentVersion { if x != nil { if x, ok := x.Operation.(*SyncDeploymentUserDataRequest_ForgetVersion); ok { return x.ForgetVersion @@ -3046,14 +3069,14 @@ func (x *SyncDeploymentUserDataRequest) GetForgetVersion() *v110.WorkerDeploymen return nil } -func (x *SyncDeploymentUserDataRequest) GetUpdateRoutingConfig() *v112.RoutingConfig { +func (x *SyncDeploymentUserDataRequest) GetUpdateRoutingConfig() *v113.RoutingConfig { if x != nil { return x.UpdateRoutingConfig } return nil } -func (x *SyncDeploymentUserDataRequest) GetUpsertVersionsData() map[string]*v110.WorkerDeploymentVersionData { +func (x *SyncDeploymentUserDataRequest) GetUpsertVersionsData() map[string]*v111.WorkerDeploymentVersionData { if x != nil { return x.UpsertVersionsData } @@ -3075,14 +3098,14 @@ type SyncDeploymentUserDataRequest_UpdateVersionData struct { // The deployment version and its data that is being updated. // // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. - UpdateVersionData *v110.DeploymentVersionData `protobuf:"bytes,6,opt,name=update_version_data,json=updateVersionData,proto3,oneof"` + UpdateVersionData *v111.DeploymentVersionData `protobuf:"bytes,6,opt,name=update_version_data,json=updateVersionData,proto3,oneof"` } type SyncDeploymentUserDataRequest_ForgetVersion struct { // The version whose data should be cleaned from the task queue. // // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. - ForgetVersion *v110.WorkerDeploymentVersion `protobuf:"bytes,7,opt,name=forget_version,json=forgetVersion,proto3,oneof"` + ForgetVersion *v111.WorkerDeploymentVersion `protobuf:"bytes,7,opt,name=forget_version,json=forgetVersion,proto3,oneof"` } func (*SyncDeploymentUserDataRequest_UpdateVersionData) isSyncDeploymentUserDataRequest_Operation() {} @@ -3151,7 +3174,7 @@ type ApplyTaskQueueUserDataReplicationEventRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - UserData *v111.TaskQueueUserData `protobuf:"bytes,3,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` + UserData *v112.TaskQueueUserData `protobuf:"bytes,3,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3200,7 +3223,7 @@ func (x *ApplyTaskQueueUserDataReplicationEventRequest) GetTaskQueue() string { return "" } -func (x *ApplyTaskQueueUserDataReplicationEventRequest) GetUserData() *v111.TaskQueueUserData { +func (x *ApplyTaskQueueUserDataReplicationEventRequest) GetUserData() *v112.TaskQueueUserData { if x != nil { return x.UserData } @@ -3342,7 +3365,7 @@ func (x *GetBuildIdTaskQueueMappingResponse) GetTaskQueues() []string { type ForceLoadTaskQueuePartitionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - TaskQueuePartition *v18.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` + TaskQueuePartition *v19.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3384,7 +3407,7 @@ func (x *ForceLoadTaskQueuePartitionRequest) GetNamespaceId() string { return "" } -func (x *ForceLoadTaskQueuePartitionRequest) GetTaskQueuePartition() *v18.TaskQueuePartition { +func (x *ForceLoadTaskQueuePartitionRequest) GetTaskQueuePartition() *v19.TaskQueuePartition { if x != nil { return x.TaskQueuePartition } @@ -3440,7 +3463,7 @@ type ForceUnloadTaskQueueRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - TaskQueueType v19.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` + TaskQueueType v110.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3489,11 +3512,11 @@ func (x *ForceUnloadTaskQueueRequest) GetTaskQueue() string { return "" } -func (x *ForceUnloadTaskQueueRequest) GetTaskQueueType() v19.TaskQueueType { +func (x *ForceUnloadTaskQueueRequest) GetTaskQueueType() v110.TaskQueueType { if x != nil { return x.TaskQueueType } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } // TODO Shivam - Please remove this in 123 @@ -3544,7 +3567,7 @@ func (x *ForceUnloadTaskQueueResponse) GetWasLoaded() bool { type ForceUnloadTaskQueuePartitionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - TaskQueuePartition *v18.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` + TaskQueuePartition *v19.TaskQueuePartition `protobuf:"bytes,2,opt,name=task_queue_partition,json=taskQueuePartition,proto3" json:"task_queue_partition,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3586,7 +3609,7 @@ func (x *ForceUnloadTaskQueuePartitionRequest) GetNamespaceId() string { return "" } -func (x *ForceUnloadTaskQueuePartitionRequest) GetTaskQueuePartition() *v18.TaskQueuePartition { +func (x *ForceUnloadTaskQueuePartitionRequest) GetTaskQueuePartition() *v19.TaskQueuePartition { if x != nil { return x.TaskQueuePartition } @@ -3650,7 +3673,7 @@ type UpdateTaskQueueUserDataRequest struct { TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` // Versioned user data, set if the task queue has user data and the request's last_known_user_data_version is less // than the version cached in the root partition. - UserData *v111.VersionedTaskQueueUserData `protobuf:"bytes,3,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` + UserData *v112.VersionedTaskQueueUserData `protobuf:"bytes,3,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` // List of added build ids BuildIdsAdded []string `protobuf:"bytes,4,rep,name=build_ids_added,json=buildIdsAdded,proto3" json:"build_ids_added,omitempty"` // List of removed build ids @@ -3703,7 +3726,7 @@ func (x *UpdateTaskQueueUserDataRequest) GetTaskQueue() string { return "" } -func (x *UpdateTaskQueueUserDataRequest) GetUserData() *v111.VersionedTaskQueueUserData { +func (x *UpdateTaskQueueUserDataRequest) GetUserData() *v112.VersionedTaskQueueUserData { if x != nil { return x.UserData } @@ -3764,7 +3787,7 @@ type ReplicateTaskQueueUserDataRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - UserData *v111.TaskQueueUserData `protobuf:"bytes,3,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` + UserData *v112.TaskQueueUserData `protobuf:"bytes,3,opt,name=user_data,json=userData,proto3" json:"user_data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3813,7 +3836,7 @@ func (x *ReplicateTaskQueueUserDataRequest) GetTaskQueue() string { return "" } -func (x *ReplicateTaskQueueUserDataRequest) GetUserData() *v111.TaskQueueUserData { +func (x *ReplicateTaskQueueUserDataRequest) GetUserData() *v112.TaskQueueUserData { if x != nil { return x.UserData } @@ -3957,8 +3980,8 @@ type DispatchNexusTaskRequest struct { NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` TaskQueue *v14.TaskQueue `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` // Nexus request extracted by the frontend and translated into Temporal API format. - Request *v113.Request `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` - ForwardInfo *v18.TaskForwardInfo `protobuf:"bytes,4,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` + Request *v114.Request `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` + ForwardInfo *v19.TaskForwardInfo `protobuf:"bytes,4,opt,name=forward_info,json=forwardInfo,proto3" json:"forward_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -4007,14 +4030,14 @@ func (x *DispatchNexusTaskRequest) GetTaskQueue() *v14.TaskQueue { return nil } -func (x *DispatchNexusTaskRequest) GetRequest() *v113.Request { +func (x *DispatchNexusTaskRequest) GetRequest() *v114.Request { if x != nil { return x.Request } return nil } -func (x *DispatchNexusTaskRequest) GetForwardInfo() *v18.TaskForwardInfo { +func (x *DispatchNexusTaskRequest) GetForwardInfo() *v19.TaskForwardInfo { if x != nil { return x.ForwardInfo } @@ -4072,7 +4095,7 @@ func (x *DispatchNexusTaskResponse) GetOutcome() isDispatchNexusTaskResponse_Out } // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. -func (x *DispatchNexusTaskResponse) GetHandlerError() *v113.HandlerError { +func (x *DispatchNexusTaskResponse) GetHandlerError() *v114.HandlerError { if x != nil { if x, ok := x.Outcome.(*DispatchNexusTaskResponse_HandlerError); ok { return x.HandlerError @@ -4081,7 +4104,7 @@ func (x *DispatchNexusTaskResponse) GetHandlerError() *v113.HandlerError { return nil } -func (x *DispatchNexusTaskResponse) GetResponse() *v113.Response { +func (x *DispatchNexusTaskResponse) GetResponse() *v114.Response { if x != nil { if x, ok := x.Outcome.(*DispatchNexusTaskResponse_Response); ok { return x.Response @@ -4099,7 +4122,7 @@ func (x *DispatchNexusTaskResponse) GetRequestTimeout() *DispatchNexusTaskRespon return nil } -func (x *DispatchNexusTaskResponse) GetFailure() *v114.Failure { +func (x *DispatchNexusTaskResponse) GetFailure() *v115.Failure { if x != nil { if x, ok := x.Outcome.(*DispatchNexusTaskResponse_Failure); ok { return x.Failure @@ -4116,12 +4139,12 @@ type DispatchNexusTaskResponse_HandlerError struct { // Deprecated. Use failure field instead. // // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. - HandlerError *v113.HandlerError `protobuf:"bytes,1,opt,name=handler_error,json=handlerError,proto3,oneof"` + HandlerError *v114.HandlerError `protobuf:"bytes,1,opt,name=handler_error,json=handlerError,proto3,oneof"` } type DispatchNexusTaskResponse_Response struct { // Set if the worker's handler responded successfully to the nexus task. - Response *v113.Response `protobuf:"bytes,2,opt,name=response,proto3,oneof"` + Response *v114.Response `protobuf:"bytes,2,opt,name=response,proto3,oneof"` } type DispatchNexusTaskResponse_RequestTimeout struct { @@ -4130,7 +4153,7 @@ type DispatchNexusTaskResponse_RequestTimeout struct { type DispatchNexusTaskResponse_Failure struct { // Set if the worker's handler failed the nexus task. Must contain a NexusHandlerFailureInfo object. - Failure *v114.Failure `protobuf:"bytes,4,opt,name=failure,proto3,oneof"` + Failure *v115.Failure `protobuf:"bytes,4,opt,name=failure,proto3,oneof"` } func (*DispatchNexusTaskResponse_HandlerError) isDispatchNexusTaskResponse_Outcome() {} @@ -4491,7 +4514,7 @@ func (*RespondNexusTaskFailedResponse) Descriptor() ([]byte, []int) { // aip.dev/not-precedent: CreateNexusEndpoint RPC doesn't follow Google API format. --) type CreateNexusEndpointRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Spec *v111.NexusEndpointSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` + Spec *v112.NexusEndpointSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -4526,7 +4549,7 @@ func (*CreateNexusEndpointRequest) Descriptor() ([]byte, []int) { return file_temporal_server_api_matchingservice_v1_request_response_proto_rawDescGZIP(), []int{63} } -func (x *CreateNexusEndpointRequest) GetSpec() *v111.NexusEndpointSpec { +func (x *CreateNexusEndpointRequest) GetSpec() *v112.NexusEndpointSpec { if x != nil { return x.Spec } @@ -4535,7 +4558,7 @@ func (x *CreateNexusEndpointRequest) GetSpec() *v111.NexusEndpointSpec { type CreateNexusEndpointResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Entry *v111.NexusEndpointEntry `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"` + Entry *v112.NexusEndpointEntry `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -4570,7 +4593,7 @@ func (*CreateNexusEndpointResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_matchingservice_v1_request_response_proto_rawDescGZIP(), []int{64} } -func (x *CreateNexusEndpointResponse) GetEntry() *v111.NexusEndpointEntry { +func (x *CreateNexusEndpointResponse) GetEntry() *v112.NexusEndpointEntry { if x != nil { return x.Entry } @@ -4591,7 +4614,7 @@ type UpdateNexusEndpointRequest struct { // Version of the endpoint, used for optimistic concurrency. Must match current version in persistence or the // request will fail a FAILED_PRECONDITION error. Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - Spec *v111.NexusEndpointSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` + Spec *v112.NexusEndpointSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -4640,7 +4663,7 @@ func (x *UpdateNexusEndpointRequest) GetVersion() int64 { return 0 } -func (x *UpdateNexusEndpointRequest) GetSpec() *v111.NexusEndpointSpec { +func (x *UpdateNexusEndpointRequest) GetSpec() *v112.NexusEndpointSpec { if x != nil { return x.Spec } @@ -4649,7 +4672,7 @@ func (x *UpdateNexusEndpointRequest) GetSpec() *v111.NexusEndpointSpec { type UpdateNexusEndpointResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Entry *v111.NexusEndpointEntry `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"` + Entry *v112.NexusEndpointEntry `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -4684,7 +4707,7 @@ func (*UpdateNexusEndpointResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_matchingservice_v1_request_response_proto_rawDescGZIP(), []int{66} } -func (x *UpdateNexusEndpointResponse) GetEntry() *v111.NexusEndpointEntry { +func (x *UpdateNexusEndpointResponse) GetEntry() *v112.NexusEndpointEntry { if x != nil { return x.Entry } @@ -4863,7 +4886,7 @@ type ListNexusEndpointsResponse struct { // Token for getting the next page. NextPageToken []byte `protobuf:"bytes,1,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` TableVersion int64 `protobuf:"varint,2,opt,name=table_version,json=tableVersion,proto3" json:"table_version,omitempty"` - Entries []*v111.NexusEndpointEntry `protobuf:"bytes,3,rep,name=entries,proto3" json:"entries,omitempty"` + Entries []*v112.NexusEndpointEntry `protobuf:"bytes,3,rep,name=entries,proto3" json:"entries,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -4912,7 +4935,7 @@ func (x *ListNexusEndpointsResponse) GetTableVersion() int64 { return 0 } -func (x *ListNexusEndpointsResponse) GetEntries() []*v111.NexusEndpointEntry { +func (x *ListNexusEndpointsResponse) GetEntries() []*v112.NexusEndpointEntry { if x != nil { return x.Entries } @@ -5065,9 +5088,9 @@ type ListWorkersResponse struct { // includes expensive runtime metrics. We will stop populating this field in the future. // // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. - WorkersInfo []*v115.WorkerInfo `protobuf:"bytes,1,rep,name=workers_info,json=workersInfo,proto3" json:"workers_info,omitempty"` + WorkersInfo []*v116.WorkerInfo `protobuf:"bytes,1,rep,name=workers_info,json=workersInfo,proto3" json:"workers_info,omitempty"` NextPageToken []byte `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - Workers []*v115.WorkerListInfo `protobuf:"bytes,3,rep,name=workers,proto3" json:"workers,omitempty"` + Workers []*v116.WorkerListInfo `protobuf:"bytes,3,rep,name=workers,proto3" json:"workers,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5103,7 +5126,7 @@ func (*ListWorkersResponse) Descriptor() ([]byte, []int) { } // Deprecated: Marked as deprecated in temporal/server/api/matchingservice/v1/request_response.proto. -func (x *ListWorkersResponse) GetWorkersInfo() []*v115.WorkerInfo { +func (x *ListWorkersResponse) GetWorkersInfo() []*v116.WorkerInfo { if x != nil { return x.WorkersInfo } @@ -5117,7 +5140,7 @@ func (x *ListWorkersResponse) GetNextPageToken() []byte { return nil } -func (x *ListWorkersResponse) GetWorkers() []*v115.WorkerListInfo { +func (x *ListWorkersResponse) GetWorkers() []*v116.WorkerListInfo { if x != nil { return x.Workers } @@ -5382,7 +5405,7 @@ func (x *DescribeWorkerRequest) GetRequest() *v1.DescribeWorkerRequest { type DescribeWorkerResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - WorkerInfo *v115.WorkerInfo `protobuf:"bytes,1,opt,name=worker_info,json=workerInfo,proto3" json:"worker_info,omitempty"` + WorkerInfo *v116.WorkerInfo `protobuf:"bytes,1,opt,name=worker_info,json=workerInfo,proto3" json:"worker_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5417,7 +5440,7 @@ func (*DescribeWorkerResponse) Descriptor() ([]byte, []int) { return file_temporal_server_api_matchingservice_v1_request_response_proto_rawDescGZIP(), []int{80} } -func (x *DescribeWorkerResponse) GetWorkerInfo() *v115.WorkerInfo { +func (x *DescribeWorkerResponse) GetWorkerInfo() *v116.WorkerInfo { if x != nil { return x.WorkerInfo } @@ -5439,8 +5462,8 @@ type UpdateFairnessStateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - TaskQueueType v19.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` - FairnessState v116.FairnessState `protobuf:"varint,4,opt,name=fairness_state,json=fairnessState,proto3,enum=temporal.server.api.enums.v1.FairnessState" json:"fairness_state,omitempty"` + TaskQueueType v110.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` + FairnessState v117.FairnessState `protobuf:"varint,4,opt,name=fairness_state,json=fairnessState,proto3,enum=temporal.server.api.enums.v1.FairnessState" json:"fairness_state,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5489,18 +5512,18 @@ func (x *UpdateFairnessStateRequest) GetTaskQueue() string { return "" } -func (x *UpdateFairnessStateRequest) GetTaskQueueType() v19.TaskQueueType { +func (x *UpdateFairnessStateRequest) GetTaskQueueType() v110.TaskQueueType { if x != nil { return x.TaskQueueType } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } -func (x *UpdateFairnessStateRequest) GetFairnessState() v116.FairnessState { +func (x *UpdateFairnessStateRequest) GetFairnessState() v117.FairnessState { if x != nil { return x.FairnessState } - return v116.FairnessState(0) + return v117.FairnessState(0) } type UpdateFairnessStateResponse struct { @@ -5543,8 +5566,8 @@ type CheckTaskQueueVersionMembershipRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` TaskQueue string `protobuf:"bytes,2,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - TaskQueueType v19.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` - Version *v110.WorkerDeploymentVersion `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + TaskQueueType v110.TaskQueueType `protobuf:"varint,3,opt,name=task_queue_type,json=taskQueueType,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"task_queue_type,omitempty"` + Version *v111.WorkerDeploymentVersion `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5593,14 +5616,14 @@ func (x *CheckTaskQueueVersionMembershipRequest) GetTaskQueue() string { return "" } -func (x *CheckTaskQueueVersionMembershipRequest) GetTaskQueueType() v19.TaskQueueType { +func (x *CheckTaskQueueVersionMembershipRequest) GetTaskQueueType() v110.TaskQueueType { if x != nil { return x.TaskQueueType } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } -func (x *CheckTaskQueueVersionMembershipRequest) GetVersion() *v110.WorkerDeploymentVersion { +func (x *CheckTaskQueueVersionMembershipRequest) GetVersion() *v111.WorkerDeploymentVersion { if x != nil { return x.Version } @@ -5799,7 +5822,7 @@ func (x *CancelOutstandingWorkerPollsPartitionRequest_WorkerEntry) GetWorkerIden type DescribeVersionedTaskQueuesRequest_VersionTaskQueue struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type v19.TaskQueueType `protobuf:"varint,2,opt,name=type,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"type,omitempty"` + Type v110.TaskQueueType `protobuf:"varint,2,opt,name=type,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -5841,18 +5864,18 @@ func (x *DescribeVersionedTaskQueuesRequest_VersionTaskQueue) GetName() string { return "" } -func (x *DescribeVersionedTaskQueuesRequest_VersionTaskQueue) GetType() v19.TaskQueueType { +func (x *DescribeVersionedTaskQueuesRequest_VersionTaskQueue) GetType() v110.TaskQueueType { if x != nil { return x.Type } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } // (-- api-linter: core::0123::resource-annotation=disabled --) type DescribeVersionedTaskQueuesResponse_VersionTaskQueue struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type v19.TaskQueueType `protobuf:"varint,2,opt,name=type,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"type,omitempty"` + Type v110.TaskQueueType `protobuf:"varint,2,opt,name=type,proto3,enum=temporal.api.enums.v1.TaskQueueType" json:"type,omitempty"` Stats *v14.TaskQueueStats `protobuf:"bytes,3,opt,name=stats,proto3" json:"stats,omitempty"` // (-- api-linter: core::0140::prepositions=disabled // @@ -5899,11 +5922,11 @@ func (x *DescribeVersionedTaskQueuesResponse_VersionTaskQueue) GetName() string return "" } -func (x *DescribeVersionedTaskQueuesResponse_VersionTaskQueue) GetType() v19.TaskQueueType { +func (x *DescribeVersionedTaskQueuesResponse_VersionTaskQueue) GetType() v110.TaskQueueType { if x != nil { return x.Type } - return v19.TaskQueueType(0) + return v110.TaskQueueType(0) } func (x *DescribeVersionedTaskQueuesResponse_VersionTaskQueue) GetStats() *v14.TaskQueueStats { @@ -6060,7 +6083,7 @@ var File_temporal_server_api_matchingservice_v1_request_response_proto protorefl const file_temporal_server_api_matchingservice_v1_request_response_proto_rawDesc = "" + "\n" + - "=temporal/server/api/matchingservice/v1/request_response.proto\x12&temporal.server.api.matchingservice.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a%temporal/api/failure/v1/message.proto\x1a%temporal/api/history/v1/message.proto\x1a#temporal/api/nexus/v1/message.proto\x1a&temporal/api/protocol/v1/message.proto\x1a#temporal/api/query/v1/message.proto\x1a'temporal/api/taskqueue/v1/message.proto\x1a$temporal/api/worker/v1/message.proto\x1a6temporal/api/workflowservice/v1/request_response.proto\x1a*temporal/server/api/clock/v1/message.proto\x1a/temporal/server/api/deployment/v1/message.proto\x1a1temporal/server/api/enums/v1/fairness_state.proto\x1a,temporal/server/api/history/v1/message.proto\x1a.temporal/server/api/persistence/v1/nexus.proto\x1a4temporal/server/api/persistence/v1/task_queues.proto\x1a.temporal/server/api/taskqueue/v1/message.proto\"\xc3\x02\n" + + "=temporal/server/api/matchingservice/v1/request_response.proto\x12&temporal.server.api.matchingservice.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a%temporal/api/failure/v1/message.proto\x1a%temporal/api/history/v1/message.proto\x1a#temporal/api/nexus/v1/message.proto\x1a&temporal/api/protocol/v1/message.proto\x1a#temporal/api/query/v1/message.proto\x1a$temporal/api/stream/v1/message.proto\x1a'temporal/api/taskqueue/v1/message.proto\x1a$temporal/api/worker/v1/message.proto\x1a6temporal/api/workflowservice/v1/request_response.proto\x1a*temporal/server/api/clock/v1/message.proto\x1a/temporal/server/api/deployment/v1/message.proto\x1a1temporal/server/api/enums/v1/fairness_state.proto\x1a,temporal/server/api/history/v1/message.proto\x1a.temporal/server/api/persistence/v1/nexus.proto\x1a4temporal/server/api/persistence/v1/task_queues.proto\x1a.temporal/server/api/taskqueue/v1/message.proto\"\xc3\x02\n" + "\x1cPollWorkflowTaskQueueRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12\x1b\n" + "\tpoller_id\x18\x02 \x01(\tR\bpollerId\x12`\n" + @@ -6068,7 +6091,7 @@ const file_temporal_server_api_matchingservice_v1_request_response_proto_rawDesc "\x10forwarded_source\x18\x04 \x01(\tR\x0fforwardedSource\x12V\n" + "\n" + "conditions\x18\x05 \x01(\v26.temporal.server.api.matchingservice.v1.PollConditionsR\n" + - "conditions\"\xd1\v\n" + + "conditions\"\x9b\f\n" + "\x1dPollWorkflowTaskQueueResponse\x12\x1d\n" + "\n" + "task_token\x18\x01 \x01(\fR\ttaskToken\x12X\n" + @@ -6093,10 +6116,11 @@ const file_temporal_server_api_matchingservice_v1_request_response_proto_rawDesc "\x0fnext_page_token\x18\x14 \x01(\fR\rnextPageToken\x12h\n" + "\x17poller_scaling_decision\x18\x15 \x01(\v20.temporal.api.taskqueue.v1.PollerScalingDecisionR\x15pollerScalingDecision\x12A\n" + "\vraw_history\x18\x16 \x01(\v2 .temporal.api.history.v1.HistoryR\n" + - "rawHistory\x1a`\n" + + "rawHistory\x12H\n" + + "\rstream_slices\x18\x17 \x03(\v2#.temporal.api.stream.v1.StreamSliceR\fstreamSlices\x1a`\n" + "\fQueriesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + - "\x05value\x18\x02 \x01(\v2$.temporal.api.query.v1.WorkflowQueryR\x05value:\x028\x01J\x04\b\r\x10\x0e\"\xcb\v\n" + + "\x05value\x18\x02 \x01(\v2$.temporal.api.query.v1.WorkflowQueryR\x05value:\x028\x01J\x04\b\r\x10\x0e\"\x95\f\n" + "+PollWorkflowTaskQueueResponseWithRawHistory\x12\x1d\n" + "\n" + "task_token\x18\x01 \x01(\fR\ttaskToken\x12X\n" + @@ -6121,7 +6145,8 @@ const file_temporal_server_api_matchingservice_v1_request_response_proto_rawDesc "\x0fnext_page_token\x18\x14 \x01(\fR\rnextPageToken\x12h\n" + "\x17poller_scaling_decision\x18\x15 \x01(\v20.temporal.api.taskqueue.v1.PollerScalingDecisionR\x15pollerScalingDecision\x12\x1f\n" + "\vraw_history\x18\x16 \x03(\fR\n" + - "rawHistory\x1a`\n" + + "rawHistory\x12H\n" + + "\rstream_slices\x18\x17 \x03(\v2#.temporal.api.stream.v1.StreamSliceR\fstreamSlices\x1a`\n" + "\fQueriesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + "\x05value\x18\x02 \x01(\v2$.temporal.api.query.v1.WorkflowQueryR\x05value:\x028\x01J\x04\b\r\x10\x0e\"\xc3\x02\n" + @@ -6629,61 +6654,62 @@ var file_temporal_server_api_matchingservice_v1_request_response_proto_goTypes = (*v15.Message)(nil), // 104: temporal.api.protocol.v1.Message (*v16.History)(nil), // 105: temporal.api.history.v1.History (*v14.PollerScalingDecision)(nil), // 106: temporal.api.taskqueue.v1.PollerScalingDecision - (*v1.PollActivityTaskQueueRequest)(nil), // 107: temporal.api.workflowservice.v1.PollActivityTaskQueueRequest - (*v11.ActivityType)(nil), // 108: temporal.api.common.v1.ActivityType - (*v11.Payloads)(nil), // 109: temporal.api.common.v1.Payloads - (*durationpb.Duration)(nil), // 110: google.protobuf.Duration - (*v11.Header)(nil), // 111: temporal.api.common.v1.Header - (*v11.Priority)(nil), // 112: temporal.api.common.v1.Priority - (*v11.RetryPolicy)(nil), // 113: temporal.api.common.v1.RetryPolicy - (*v17.VectorClock)(nil), // 114: temporal.server.api.clock.v1.VectorClock - (*v18.TaskVersionDirective)(nil), // 115: temporal.server.api.taskqueue.v1.TaskVersionDirective - (*v18.TaskForwardInfo)(nil), // 116: temporal.server.api.taskqueue.v1.TaskForwardInfo - (*v1.QueryWorkflowRequest)(nil), // 117: temporal.api.workflowservice.v1.QueryWorkflowRequest - (*v12.QueryRejected)(nil), // 118: temporal.api.query.v1.QueryRejected - (*v1.RespondQueryTaskCompletedRequest)(nil), // 119: temporal.api.workflowservice.v1.RespondQueryTaskCompletedRequest - (v19.TaskQueueType)(0), // 120: temporal.api.enums.v1.TaskQueueType - (*v18.TaskQueuePartition)(nil), // 121: temporal.server.api.taskqueue.v1.TaskQueuePartition - (*v1.DescribeTaskQueueRequest)(nil), // 122: temporal.api.workflowservice.v1.DescribeTaskQueueRequest - (*v110.WorkerDeploymentVersion)(nil), // 123: temporal.server.api.deployment.v1.WorkerDeploymentVersion - (*v1.DescribeTaskQueueResponse)(nil), // 124: temporal.api.workflowservice.v1.DescribeTaskQueueResponse - (*v14.TaskQueueVersionSelection)(nil), // 125: temporal.api.taskqueue.v1.TaskQueueVersionSelection - (*v18.PartitionScaleInfo)(nil), // 126: temporal.server.api.taskqueue.v1.PartitionScaleInfo - (*v14.TaskQueuePartitionMetadata)(nil), // 127: temporal.api.taskqueue.v1.TaskQueuePartitionMetadata - (*v1.GetWorkerVersioningRulesRequest)(nil), // 128: temporal.api.workflowservice.v1.GetWorkerVersioningRulesRequest - (*v1.GetWorkerVersioningRulesResponse)(nil), // 129: temporal.api.workflowservice.v1.GetWorkerVersioningRulesResponse - (*v1.UpdateWorkerVersioningRulesRequest)(nil), // 130: temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest - (*v1.UpdateWorkerVersioningRulesResponse)(nil), // 131: temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesResponse - (*v1.GetWorkerBuildIdCompatibilityRequest)(nil), // 132: temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityRequest - (*v1.GetWorkerBuildIdCompatibilityResponse)(nil), // 133: temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityResponse - (*v111.VersionedTaskQueueUserData)(nil), // 134: temporal.server.api.persistence.v1.VersionedTaskQueueUserData - (*v18.VersionedEphemeralData)(nil), // 135: temporal.server.api.taskqueue.v1.VersionedEphemeralData - (*v110.DeploymentVersionData)(nil), // 136: temporal.server.api.deployment.v1.DeploymentVersionData - (*v112.RoutingConfig)(nil), // 137: temporal.api.deployment.v1.RoutingConfig - (*v111.TaskQueueUserData)(nil), // 138: temporal.server.api.persistence.v1.TaskQueueUserData - (*v113.Request)(nil), // 139: temporal.api.nexus.v1.Request - (*v113.HandlerError)(nil), // 140: temporal.api.nexus.v1.HandlerError - (*v113.Response)(nil), // 141: temporal.api.nexus.v1.Response - (*v114.Failure)(nil), // 142: temporal.api.failure.v1.Failure - (*v1.PollNexusTaskQueueRequest)(nil), // 143: temporal.api.workflowservice.v1.PollNexusTaskQueueRequest - (*v1.PollNexusTaskQueueResponse)(nil), // 144: temporal.api.workflowservice.v1.PollNexusTaskQueueResponse - (*v1.RespondNexusTaskCompletedRequest)(nil), // 145: temporal.api.workflowservice.v1.RespondNexusTaskCompletedRequest - (*v1.RespondNexusTaskFailedRequest)(nil), // 146: temporal.api.workflowservice.v1.RespondNexusTaskFailedRequest - (*v111.NexusEndpointSpec)(nil), // 147: temporal.server.api.persistence.v1.NexusEndpointSpec - (*v111.NexusEndpointEntry)(nil), // 148: temporal.server.api.persistence.v1.NexusEndpointEntry - (*v1.RecordWorkerHeartbeatRequest)(nil), // 149: temporal.api.workflowservice.v1.RecordWorkerHeartbeatRequest - (*v1.ListWorkersRequest)(nil), // 150: temporal.api.workflowservice.v1.ListWorkersRequest - (*v115.WorkerInfo)(nil), // 151: temporal.api.worker.v1.WorkerInfo - (*v115.WorkerListInfo)(nil), // 152: temporal.api.worker.v1.WorkerListInfo - (*v1.CountWorkersRequest)(nil), // 153: temporal.api.workflowservice.v1.CountWorkersRequest - (*v1.UpdateTaskQueueConfigRequest)(nil), // 154: temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest - (*v14.TaskQueueConfig)(nil), // 155: temporal.api.taskqueue.v1.TaskQueueConfig - (*v1.DescribeWorkerRequest)(nil), // 156: temporal.api.workflowservice.v1.DescribeWorkerRequest - (v116.FairnessState)(0), // 157: temporal.server.api.enums.v1.FairnessState - (*v14.TaskQueueStats)(nil), // 158: temporal.api.taskqueue.v1.TaskQueueStats - (*v18.TaskQueueVersionInfoInternal)(nil), // 159: temporal.server.api.taskqueue.v1.TaskQueueVersionInfoInternal - (*v1.UpdateWorkerBuildIdCompatibilityRequest)(nil), // 160: temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest - (*v110.WorkerDeploymentVersionData)(nil), // 161: temporal.server.api.deployment.v1.WorkerDeploymentVersionData + (*v17.StreamSlice)(nil), // 107: temporal.api.stream.v1.StreamSlice + (*v1.PollActivityTaskQueueRequest)(nil), // 108: temporal.api.workflowservice.v1.PollActivityTaskQueueRequest + (*v11.ActivityType)(nil), // 109: temporal.api.common.v1.ActivityType + (*v11.Payloads)(nil), // 110: temporal.api.common.v1.Payloads + (*durationpb.Duration)(nil), // 111: google.protobuf.Duration + (*v11.Header)(nil), // 112: temporal.api.common.v1.Header + (*v11.Priority)(nil), // 113: temporal.api.common.v1.Priority + (*v11.RetryPolicy)(nil), // 114: temporal.api.common.v1.RetryPolicy + (*v18.VectorClock)(nil), // 115: temporal.server.api.clock.v1.VectorClock + (*v19.TaskVersionDirective)(nil), // 116: temporal.server.api.taskqueue.v1.TaskVersionDirective + (*v19.TaskForwardInfo)(nil), // 117: temporal.server.api.taskqueue.v1.TaskForwardInfo + (*v1.QueryWorkflowRequest)(nil), // 118: temporal.api.workflowservice.v1.QueryWorkflowRequest + (*v12.QueryRejected)(nil), // 119: temporal.api.query.v1.QueryRejected + (*v1.RespondQueryTaskCompletedRequest)(nil), // 120: temporal.api.workflowservice.v1.RespondQueryTaskCompletedRequest + (v110.TaskQueueType)(0), // 121: temporal.api.enums.v1.TaskQueueType + (*v19.TaskQueuePartition)(nil), // 122: temporal.server.api.taskqueue.v1.TaskQueuePartition + (*v1.DescribeTaskQueueRequest)(nil), // 123: temporal.api.workflowservice.v1.DescribeTaskQueueRequest + (*v111.WorkerDeploymentVersion)(nil), // 124: temporal.server.api.deployment.v1.WorkerDeploymentVersion + (*v1.DescribeTaskQueueResponse)(nil), // 125: temporal.api.workflowservice.v1.DescribeTaskQueueResponse + (*v14.TaskQueueVersionSelection)(nil), // 126: temporal.api.taskqueue.v1.TaskQueueVersionSelection + (*v19.PartitionScaleInfo)(nil), // 127: temporal.server.api.taskqueue.v1.PartitionScaleInfo + (*v14.TaskQueuePartitionMetadata)(nil), // 128: temporal.api.taskqueue.v1.TaskQueuePartitionMetadata + (*v1.GetWorkerVersioningRulesRequest)(nil), // 129: temporal.api.workflowservice.v1.GetWorkerVersioningRulesRequest + (*v1.GetWorkerVersioningRulesResponse)(nil), // 130: temporal.api.workflowservice.v1.GetWorkerVersioningRulesResponse + (*v1.UpdateWorkerVersioningRulesRequest)(nil), // 131: temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest + (*v1.UpdateWorkerVersioningRulesResponse)(nil), // 132: temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesResponse + (*v1.GetWorkerBuildIdCompatibilityRequest)(nil), // 133: temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityRequest + (*v1.GetWorkerBuildIdCompatibilityResponse)(nil), // 134: temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityResponse + (*v112.VersionedTaskQueueUserData)(nil), // 135: temporal.server.api.persistence.v1.VersionedTaskQueueUserData + (*v19.VersionedEphemeralData)(nil), // 136: temporal.server.api.taskqueue.v1.VersionedEphemeralData + (*v111.DeploymentVersionData)(nil), // 137: temporal.server.api.deployment.v1.DeploymentVersionData + (*v113.RoutingConfig)(nil), // 138: temporal.api.deployment.v1.RoutingConfig + (*v112.TaskQueueUserData)(nil), // 139: temporal.server.api.persistence.v1.TaskQueueUserData + (*v114.Request)(nil), // 140: temporal.api.nexus.v1.Request + (*v114.HandlerError)(nil), // 141: temporal.api.nexus.v1.HandlerError + (*v114.Response)(nil), // 142: temporal.api.nexus.v1.Response + (*v115.Failure)(nil), // 143: temporal.api.failure.v1.Failure + (*v1.PollNexusTaskQueueRequest)(nil), // 144: temporal.api.workflowservice.v1.PollNexusTaskQueueRequest + (*v1.PollNexusTaskQueueResponse)(nil), // 145: temporal.api.workflowservice.v1.PollNexusTaskQueueResponse + (*v1.RespondNexusTaskCompletedRequest)(nil), // 146: temporal.api.workflowservice.v1.RespondNexusTaskCompletedRequest + (*v1.RespondNexusTaskFailedRequest)(nil), // 147: temporal.api.workflowservice.v1.RespondNexusTaskFailedRequest + (*v112.NexusEndpointSpec)(nil), // 148: temporal.server.api.persistence.v1.NexusEndpointSpec + (*v112.NexusEndpointEntry)(nil), // 149: temporal.server.api.persistence.v1.NexusEndpointEntry + (*v1.RecordWorkerHeartbeatRequest)(nil), // 150: temporal.api.workflowservice.v1.RecordWorkerHeartbeatRequest + (*v1.ListWorkersRequest)(nil), // 151: temporal.api.workflowservice.v1.ListWorkersRequest + (*v116.WorkerInfo)(nil), // 152: temporal.api.worker.v1.WorkerInfo + (*v116.WorkerListInfo)(nil), // 153: temporal.api.worker.v1.WorkerListInfo + (*v1.CountWorkersRequest)(nil), // 154: temporal.api.workflowservice.v1.CountWorkersRequest + (*v1.UpdateTaskQueueConfigRequest)(nil), // 155: temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest + (*v14.TaskQueueConfig)(nil), // 156: temporal.api.taskqueue.v1.TaskQueueConfig + (*v1.DescribeWorkerRequest)(nil), // 157: temporal.api.workflowservice.v1.DescribeWorkerRequest + (v117.FairnessState)(0), // 158: temporal.server.api.enums.v1.FairnessState + (*v14.TaskQueueStats)(nil), // 159: temporal.api.taskqueue.v1.TaskQueueStats + (*v19.TaskQueueVersionInfoInternal)(nil), // 160: temporal.server.api.taskqueue.v1.TaskQueueVersionInfoInternal + (*v1.UpdateWorkerBuildIdCompatibilityRequest)(nil), // 161: temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest + (*v111.WorkerDeploymentVersionData)(nil), // 162: temporal.server.api.deployment.v1.WorkerDeploymentVersionData } var file_temporal_server_api_matchingservice_v1_request_response_proto_depIdxs = []int32{ 97, // 0: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest @@ -6700,148 +6726,150 @@ var file_temporal_server_api_matchingservice_v1_request_response_proto_depIdxs = 105, // 11: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponse.history:type_name -> temporal.api.history.v1.History 106, // 12: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponse.poller_scaling_decision:type_name -> temporal.api.taskqueue.v1.PollerScalingDecision 105, // 13: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponse.raw_history:type_name -> temporal.api.history.v1.History - 98, // 14: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 99, // 15: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 100, // 16: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.query:type_name -> temporal.api.query.v1.WorkflowQuery - 101, // 17: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 102, // 18: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 103, // 19: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.scheduled_time:type_name -> google.protobuf.Timestamp - 103, // 20: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.started_time:type_name -> google.protobuf.Timestamp - 87, // 21: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.queries:type_name -> temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.QueriesEntry - 104, // 22: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message - 105, // 23: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History - 106, // 24: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.poller_scaling_decision:type_name -> temporal.api.taskqueue.v1.PollerScalingDecision - 107, // 25: temporal.server.api.matchingservice.v1.PollActivityTaskQueueRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest - 85, // 26: temporal.server.api.matchingservice.v1.PollActivityTaskQueueRequest.conditions:type_name -> temporal.server.api.matchingservice.v1.PollConditions - 98, // 27: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 108, // 28: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.activity_type:type_name -> temporal.api.common.v1.ActivityType - 109, // 29: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.input:type_name -> temporal.api.common.v1.Payloads - 103, // 30: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.scheduled_time:type_name -> google.protobuf.Timestamp - 110, // 31: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.schedule_to_close_timeout:type_name -> google.protobuf.Duration - 103, // 32: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.started_time:type_name -> google.protobuf.Timestamp - 110, // 33: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.start_to_close_timeout:type_name -> google.protobuf.Duration - 110, // 34: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.heartbeat_timeout:type_name -> google.protobuf.Duration - 103, // 35: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.current_attempt_scheduled_time:type_name -> google.protobuf.Timestamp - 109, // 36: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.heartbeat_details:type_name -> temporal.api.common.v1.Payloads - 99, // 37: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 111, // 38: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.header:type_name -> temporal.api.common.v1.Header - 106, // 39: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.poller_scaling_decision:type_name -> temporal.api.taskqueue.v1.PollerScalingDecision - 112, // 40: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.priority:type_name -> temporal.api.common.v1.Priority - 113, // 41: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy - 98, // 42: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 102, // 43: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 110, // 44: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.schedule_to_start_timeout:type_name -> google.protobuf.Duration - 114, // 45: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 115, // 46: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective - 116, // 47: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo - 112, // 48: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.priority:type_name -> temporal.api.common.v1.Priority - 98, // 49: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 102, // 50: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 110, // 51: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.schedule_to_start_timeout:type_name -> google.protobuf.Duration - 114, // 52: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 115, // 53: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective - 116, // 54: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo - 112, // 55: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.priority:type_name -> temporal.api.common.v1.Priority - 102, // 56: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 117, // 57: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.query_request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest - 115, // 58: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective - 116, // 59: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo - 112, // 60: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.priority:type_name -> temporal.api.common.v1.Priority - 109, // 61: temporal.server.api.matchingservice.v1.QueryWorkflowResponse.query_result:type_name -> temporal.api.common.v1.Payloads - 118, // 62: temporal.server.api.matchingservice.v1.QueryWorkflowResponse.query_rejected:type_name -> temporal.api.query.v1.QueryRejected - 102, // 63: temporal.server.api.matchingservice.v1.RespondQueryTaskCompletedRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 119, // 64: temporal.server.api.matchingservice.v1.RespondQueryTaskCompletedRequest.completed_request:type_name -> temporal.api.workflowservice.v1.RespondQueryTaskCompletedRequest - 120, // 65: temporal.server.api.matchingservice.v1.CancelOutstandingPollRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType - 102, // 66: temporal.server.api.matchingservice.v1.CancelOutstandingPollRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 102, // 67: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 120, // 68: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType - 121, // 69: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition - 121, // 70: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.partitions:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition - 88, // 71: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.workers:type_name -> temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.WorkerEntry - 122, // 72: temporal.server.api.matchingservice.v1.DescribeTaskQueueRequest.desc_request:type_name -> temporal.api.workflowservice.v1.DescribeTaskQueueRequest - 123, // 73: temporal.server.api.matchingservice.v1.DescribeTaskQueueRequest.version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion - 124, // 74: temporal.server.api.matchingservice.v1.DescribeTaskQueueResponse.desc_response:type_name -> temporal.api.workflowservice.v1.DescribeTaskQueueResponse - 120, // 75: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType - 102, // 76: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 123, // 77: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion - 89, // 78: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.version_task_queues:type_name -> temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.VersionTaskQueue - 90, // 79: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.version_task_queues:type_name -> temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue - 121, // 80: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition - 125, // 81: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionRequest.versions:type_name -> temporal.api.taskqueue.v1.TaskQueueVersionSelection - 92, // 82: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.versions_info_internal:type_name -> temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.VersionsInfoInternalEntry - 126, // 83: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.scale_info:type_name -> temporal.server.api.taskqueue.v1.PartitionScaleInfo - 102, // 84: temporal.server.api.matchingservice.v1.ListTaskQueuePartitionsRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 127, // 85: temporal.server.api.matchingservice.v1.ListTaskQueuePartitionsResponse.activity_task_queue_partitions:type_name -> temporal.api.taskqueue.v1.TaskQueuePartitionMetadata - 127, // 86: temporal.server.api.matchingservice.v1.ListTaskQueuePartitionsResponse.workflow_task_queue_partitions:type_name -> temporal.api.taskqueue.v1.TaskQueuePartitionMetadata - 93, // 87: temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.apply_public_request:type_name -> temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.ApplyPublicRequest - 94, // 88: temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.remove_build_ids:type_name -> temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.RemoveBuildIds - 128, // 89: temporal.server.api.matchingservice.v1.GetWorkerVersioningRulesRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkerVersioningRulesRequest - 129, // 90: temporal.server.api.matchingservice.v1.GetWorkerVersioningRulesResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkerVersioningRulesResponse - 130, // 91: temporal.server.api.matchingservice.v1.UpdateWorkerVersioningRulesRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest - 131, // 92: temporal.server.api.matchingservice.v1.UpdateWorkerVersioningRulesResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesResponse - 132, // 93: temporal.server.api.matchingservice.v1.GetWorkerBuildIdCompatibilityRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityRequest - 133, // 94: temporal.server.api.matchingservice.v1.GetWorkerBuildIdCompatibilityResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityResponse - 120, // 95: temporal.server.api.matchingservice.v1.GetTaskQueueUserDataRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType - 134, // 96: temporal.server.api.matchingservice.v1.GetTaskQueueUserDataResponse.user_data:type_name -> temporal.server.api.persistence.v1.VersionedTaskQueueUserData - 135, // 97: temporal.server.api.matchingservice.v1.GetTaskQueueUserDataResponse.ephemeral_data:type_name -> temporal.server.api.taskqueue.v1.VersionedEphemeralData - 120, // 98: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.task_queue_types:type_name -> temporal.api.enums.v1.TaskQueueType - 136, // 99: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.update_version_data:type_name -> temporal.server.api.deployment.v1.DeploymentVersionData - 123, // 100: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.forget_version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion - 137, // 101: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.update_routing_config:type_name -> temporal.api.deployment.v1.RoutingConfig - 95, // 102: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.upsert_versions_data:type_name -> temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.UpsertVersionsDataEntry - 138, // 103: temporal.server.api.matchingservice.v1.ApplyTaskQueueUserDataReplicationEventRequest.user_data:type_name -> temporal.server.api.persistence.v1.TaskQueueUserData - 121, // 104: temporal.server.api.matchingservice.v1.ForceLoadTaskQueuePartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition - 120, // 105: temporal.server.api.matchingservice.v1.ForceUnloadTaskQueueRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType - 121, // 106: temporal.server.api.matchingservice.v1.ForceUnloadTaskQueuePartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition - 134, // 107: temporal.server.api.matchingservice.v1.UpdateTaskQueueUserDataRequest.user_data:type_name -> temporal.server.api.persistence.v1.VersionedTaskQueueUserData - 138, // 108: temporal.server.api.matchingservice.v1.ReplicateTaskQueueUserDataRequest.user_data:type_name -> temporal.server.api.persistence.v1.TaskQueueUserData - 102, // 109: temporal.server.api.matchingservice.v1.DispatchNexusTaskRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 139, // 110: temporal.server.api.matchingservice.v1.DispatchNexusTaskRequest.request:type_name -> temporal.api.nexus.v1.Request - 116, // 111: temporal.server.api.matchingservice.v1.DispatchNexusTaskRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo - 140, // 112: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.handler_error:type_name -> temporal.api.nexus.v1.HandlerError - 141, // 113: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.response:type_name -> temporal.api.nexus.v1.Response - 96, // 114: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.request_timeout:type_name -> temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.Timeout - 142, // 115: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.failure:type_name -> temporal.api.failure.v1.Failure - 143, // 116: temporal.server.api.matchingservice.v1.PollNexusTaskQueueRequest.request:type_name -> temporal.api.workflowservice.v1.PollNexusTaskQueueRequest - 85, // 117: temporal.server.api.matchingservice.v1.PollNexusTaskQueueRequest.conditions:type_name -> temporal.server.api.matchingservice.v1.PollConditions - 144, // 118: temporal.server.api.matchingservice.v1.PollNexusTaskQueueResponse.response:type_name -> temporal.api.workflowservice.v1.PollNexusTaskQueueResponse - 102, // 119: temporal.server.api.matchingservice.v1.RespondNexusTaskCompletedRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 145, // 120: temporal.server.api.matchingservice.v1.RespondNexusTaskCompletedRequest.request:type_name -> temporal.api.workflowservice.v1.RespondNexusTaskCompletedRequest - 102, // 121: temporal.server.api.matchingservice.v1.RespondNexusTaskFailedRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 146, // 122: temporal.server.api.matchingservice.v1.RespondNexusTaskFailedRequest.request:type_name -> temporal.api.workflowservice.v1.RespondNexusTaskFailedRequest - 147, // 123: temporal.server.api.matchingservice.v1.CreateNexusEndpointRequest.spec:type_name -> temporal.server.api.persistence.v1.NexusEndpointSpec - 148, // 124: temporal.server.api.matchingservice.v1.CreateNexusEndpointResponse.entry:type_name -> temporal.server.api.persistence.v1.NexusEndpointEntry - 147, // 125: temporal.server.api.matchingservice.v1.UpdateNexusEndpointRequest.spec:type_name -> temporal.server.api.persistence.v1.NexusEndpointSpec - 148, // 126: temporal.server.api.matchingservice.v1.UpdateNexusEndpointResponse.entry:type_name -> temporal.server.api.persistence.v1.NexusEndpointEntry - 148, // 127: temporal.server.api.matchingservice.v1.ListNexusEndpointsResponse.entries:type_name -> temporal.server.api.persistence.v1.NexusEndpointEntry - 149, // 128: temporal.server.api.matchingservice.v1.RecordWorkerHeartbeatRequest.heartbeart_request:type_name -> temporal.api.workflowservice.v1.RecordWorkerHeartbeatRequest - 150, // 129: temporal.server.api.matchingservice.v1.ListWorkersRequest.list_request:type_name -> temporal.api.workflowservice.v1.ListWorkersRequest - 151, // 130: temporal.server.api.matchingservice.v1.ListWorkersResponse.workers_info:type_name -> temporal.api.worker.v1.WorkerInfo - 152, // 131: temporal.server.api.matchingservice.v1.ListWorkersResponse.workers:type_name -> temporal.api.worker.v1.WorkerListInfo - 153, // 132: temporal.server.api.matchingservice.v1.CountWorkersRequest.count_request:type_name -> temporal.api.workflowservice.v1.CountWorkersRequest - 154, // 133: temporal.server.api.matchingservice.v1.UpdateTaskQueueConfigRequest.update_taskqueue_config:type_name -> temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest - 155, // 134: temporal.server.api.matchingservice.v1.UpdateTaskQueueConfigResponse.updated_taskqueue_config:type_name -> temporal.api.taskqueue.v1.TaskQueueConfig - 156, // 135: temporal.server.api.matchingservice.v1.DescribeWorkerRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkerRequest - 151, // 136: temporal.server.api.matchingservice.v1.DescribeWorkerResponse.worker_info:type_name -> temporal.api.worker.v1.WorkerInfo - 120, // 137: temporal.server.api.matchingservice.v1.UpdateFairnessStateRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType - 157, // 138: temporal.server.api.matchingservice.v1.UpdateFairnessStateRequest.fairness_state:type_name -> temporal.server.api.enums.v1.FairnessState - 120, // 139: temporal.server.api.matchingservice.v1.CheckTaskQueueVersionMembershipRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType - 123, // 140: temporal.server.api.matchingservice.v1.CheckTaskQueueVersionMembershipRequest.version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion - 100, // 141: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 100, // 142: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 120, // 143: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.VersionTaskQueue.type:type_name -> temporal.api.enums.v1.TaskQueueType - 120, // 144: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.type:type_name -> temporal.api.enums.v1.TaskQueueType - 158, // 145: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.stats:type_name -> temporal.api.taskqueue.v1.TaskQueueStats - 91, // 146: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.stats_by_priority_key:type_name -> temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.StatsByPriorityKeyEntry - 158, // 147: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.StatsByPriorityKeyEntry.value:type_name -> temporal.api.taskqueue.v1.TaskQueueStats - 159, // 148: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.VersionsInfoInternalEntry.value:type_name -> temporal.server.api.taskqueue.v1.TaskQueueVersionInfoInternal - 160, // 149: temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.ApplyPublicRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest - 161, // 150: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.UpsertVersionsDataEntry.value:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersionData - 151, // [151:151] is the sub-list for method output_type - 151, // [151:151] is the sub-list for method input_type - 151, // [151:151] is the sub-list for extension type_name - 151, // [151:151] is the sub-list for extension extendee - 0, // [0:151] is the sub-list for field type_name + 107, // 14: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponse.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice + 98, // 15: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 99, // 16: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 100, // 17: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.query:type_name -> temporal.api.query.v1.WorkflowQuery + 101, // 18: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 102, // 19: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 103, // 20: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.scheduled_time:type_name -> google.protobuf.Timestamp + 103, // 21: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.started_time:type_name -> google.protobuf.Timestamp + 87, // 22: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.queries:type_name -> temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.QueriesEntry + 104, // 23: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message + 105, // 24: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History + 106, // 25: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.poller_scaling_decision:type_name -> temporal.api.taskqueue.v1.PollerScalingDecision + 107, // 26: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice + 108, // 27: temporal.server.api.matchingservice.v1.PollActivityTaskQueueRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest + 85, // 28: temporal.server.api.matchingservice.v1.PollActivityTaskQueueRequest.conditions:type_name -> temporal.server.api.matchingservice.v1.PollConditions + 98, // 29: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 109, // 30: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.activity_type:type_name -> temporal.api.common.v1.ActivityType + 110, // 31: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.input:type_name -> temporal.api.common.v1.Payloads + 103, // 32: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.scheduled_time:type_name -> google.protobuf.Timestamp + 111, // 33: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.schedule_to_close_timeout:type_name -> google.protobuf.Duration + 103, // 34: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.started_time:type_name -> google.protobuf.Timestamp + 111, // 35: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.start_to_close_timeout:type_name -> google.protobuf.Duration + 111, // 36: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.heartbeat_timeout:type_name -> google.protobuf.Duration + 103, // 37: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.current_attempt_scheduled_time:type_name -> google.protobuf.Timestamp + 110, // 38: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.heartbeat_details:type_name -> temporal.api.common.v1.Payloads + 99, // 39: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 112, // 40: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.header:type_name -> temporal.api.common.v1.Header + 106, // 41: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.poller_scaling_decision:type_name -> temporal.api.taskqueue.v1.PollerScalingDecision + 113, // 42: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.priority:type_name -> temporal.api.common.v1.Priority + 114, // 43: temporal.server.api.matchingservice.v1.PollActivityTaskQueueResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy + 98, // 44: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 102, // 45: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 111, // 46: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.schedule_to_start_timeout:type_name -> google.protobuf.Duration + 115, // 47: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 116, // 48: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 117, // 49: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo + 113, // 50: temporal.server.api.matchingservice.v1.AddWorkflowTaskRequest.priority:type_name -> temporal.api.common.v1.Priority + 98, // 51: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 102, // 52: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 111, // 53: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.schedule_to_start_timeout:type_name -> google.protobuf.Duration + 115, // 54: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 116, // 55: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 117, // 56: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo + 113, // 57: temporal.server.api.matchingservice.v1.AddActivityTaskRequest.priority:type_name -> temporal.api.common.v1.Priority + 102, // 58: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 118, // 59: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.query_request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest + 116, // 60: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 117, // 61: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo + 113, // 62: temporal.server.api.matchingservice.v1.QueryWorkflowRequest.priority:type_name -> temporal.api.common.v1.Priority + 110, // 63: temporal.server.api.matchingservice.v1.QueryWorkflowResponse.query_result:type_name -> temporal.api.common.v1.Payloads + 119, // 64: temporal.server.api.matchingservice.v1.QueryWorkflowResponse.query_rejected:type_name -> temporal.api.query.v1.QueryRejected + 102, // 65: temporal.server.api.matchingservice.v1.RespondQueryTaskCompletedRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 120, // 66: temporal.server.api.matchingservice.v1.RespondQueryTaskCompletedRequest.completed_request:type_name -> temporal.api.workflowservice.v1.RespondQueryTaskCompletedRequest + 121, // 67: temporal.server.api.matchingservice.v1.CancelOutstandingPollRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType + 102, // 68: temporal.server.api.matchingservice.v1.CancelOutstandingPollRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 102, // 69: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 121, // 70: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType + 122, // 71: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition + 122, // 72: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.partitions:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition + 88, // 73: temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.workers:type_name -> temporal.server.api.matchingservice.v1.CancelOutstandingWorkerPollsPartitionRequest.WorkerEntry + 123, // 74: temporal.server.api.matchingservice.v1.DescribeTaskQueueRequest.desc_request:type_name -> temporal.api.workflowservice.v1.DescribeTaskQueueRequest + 124, // 75: temporal.server.api.matchingservice.v1.DescribeTaskQueueRequest.version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion + 125, // 76: temporal.server.api.matchingservice.v1.DescribeTaskQueueResponse.desc_response:type_name -> temporal.api.workflowservice.v1.DescribeTaskQueueResponse + 121, // 77: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType + 102, // 78: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 124, // 79: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion + 89, // 80: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.version_task_queues:type_name -> temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.VersionTaskQueue + 90, // 81: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.version_task_queues:type_name -> temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue + 122, // 82: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition + 126, // 83: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionRequest.versions:type_name -> temporal.api.taskqueue.v1.TaskQueueVersionSelection + 92, // 84: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.versions_info_internal:type_name -> temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.VersionsInfoInternalEntry + 127, // 85: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.scale_info:type_name -> temporal.server.api.taskqueue.v1.PartitionScaleInfo + 102, // 86: temporal.server.api.matchingservice.v1.ListTaskQueuePartitionsRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 128, // 87: temporal.server.api.matchingservice.v1.ListTaskQueuePartitionsResponse.activity_task_queue_partitions:type_name -> temporal.api.taskqueue.v1.TaskQueuePartitionMetadata + 128, // 88: temporal.server.api.matchingservice.v1.ListTaskQueuePartitionsResponse.workflow_task_queue_partitions:type_name -> temporal.api.taskqueue.v1.TaskQueuePartitionMetadata + 93, // 89: temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.apply_public_request:type_name -> temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.ApplyPublicRequest + 94, // 90: temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.remove_build_ids:type_name -> temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.RemoveBuildIds + 129, // 91: temporal.server.api.matchingservice.v1.GetWorkerVersioningRulesRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkerVersioningRulesRequest + 130, // 92: temporal.server.api.matchingservice.v1.GetWorkerVersioningRulesResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkerVersioningRulesResponse + 131, // 93: temporal.server.api.matchingservice.v1.UpdateWorkerVersioningRulesRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest + 132, // 94: temporal.server.api.matchingservice.v1.UpdateWorkerVersioningRulesResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesResponse + 133, // 95: temporal.server.api.matchingservice.v1.GetWorkerBuildIdCompatibilityRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityRequest + 134, // 96: temporal.server.api.matchingservice.v1.GetWorkerBuildIdCompatibilityResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityResponse + 121, // 97: temporal.server.api.matchingservice.v1.GetTaskQueueUserDataRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType + 135, // 98: temporal.server.api.matchingservice.v1.GetTaskQueueUserDataResponse.user_data:type_name -> temporal.server.api.persistence.v1.VersionedTaskQueueUserData + 136, // 99: temporal.server.api.matchingservice.v1.GetTaskQueueUserDataResponse.ephemeral_data:type_name -> temporal.server.api.taskqueue.v1.VersionedEphemeralData + 121, // 100: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.task_queue_types:type_name -> temporal.api.enums.v1.TaskQueueType + 137, // 101: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.update_version_data:type_name -> temporal.server.api.deployment.v1.DeploymentVersionData + 124, // 102: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.forget_version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion + 138, // 103: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.update_routing_config:type_name -> temporal.api.deployment.v1.RoutingConfig + 95, // 104: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.upsert_versions_data:type_name -> temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.UpsertVersionsDataEntry + 139, // 105: temporal.server.api.matchingservice.v1.ApplyTaskQueueUserDataReplicationEventRequest.user_data:type_name -> temporal.server.api.persistence.v1.TaskQueueUserData + 122, // 106: temporal.server.api.matchingservice.v1.ForceLoadTaskQueuePartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition + 121, // 107: temporal.server.api.matchingservice.v1.ForceUnloadTaskQueueRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType + 122, // 108: temporal.server.api.matchingservice.v1.ForceUnloadTaskQueuePartitionRequest.task_queue_partition:type_name -> temporal.server.api.taskqueue.v1.TaskQueuePartition + 135, // 109: temporal.server.api.matchingservice.v1.UpdateTaskQueueUserDataRequest.user_data:type_name -> temporal.server.api.persistence.v1.VersionedTaskQueueUserData + 139, // 110: temporal.server.api.matchingservice.v1.ReplicateTaskQueueUserDataRequest.user_data:type_name -> temporal.server.api.persistence.v1.TaskQueueUserData + 102, // 111: temporal.server.api.matchingservice.v1.DispatchNexusTaskRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 140, // 112: temporal.server.api.matchingservice.v1.DispatchNexusTaskRequest.request:type_name -> temporal.api.nexus.v1.Request + 117, // 113: temporal.server.api.matchingservice.v1.DispatchNexusTaskRequest.forward_info:type_name -> temporal.server.api.taskqueue.v1.TaskForwardInfo + 141, // 114: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.handler_error:type_name -> temporal.api.nexus.v1.HandlerError + 142, // 115: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.response:type_name -> temporal.api.nexus.v1.Response + 96, // 116: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.request_timeout:type_name -> temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.Timeout + 143, // 117: temporal.server.api.matchingservice.v1.DispatchNexusTaskResponse.failure:type_name -> temporal.api.failure.v1.Failure + 144, // 118: temporal.server.api.matchingservice.v1.PollNexusTaskQueueRequest.request:type_name -> temporal.api.workflowservice.v1.PollNexusTaskQueueRequest + 85, // 119: temporal.server.api.matchingservice.v1.PollNexusTaskQueueRequest.conditions:type_name -> temporal.server.api.matchingservice.v1.PollConditions + 145, // 120: temporal.server.api.matchingservice.v1.PollNexusTaskQueueResponse.response:type_name -> temporal.api.workflowservice.v1.PollNexusTaskQueueResponse + 102, // 121: temporal.server.api.matchingservice.v1.RespondNexusTaskCompletedRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 146, // 122: temporal.server.api.matchingservice.v1.RespondNexusTaskCompletedRequest.request:type_name -> temporal.api.workflowservice.v1.RespondNexusTaskCompletedRequest + 102, // 123: temporal.server.api.matchingservice.v1.RespondNexusTaskFailedRequest.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 147, // 124: temporal.server.api.matchingservice.v1.RespondNexusTaskFailedRequest.request:type_name -> temporal.api.workflowservice.v1.RespondNexusTaskFailedRequest + 148, // 125: temporal.server.api.matchingservice.v1.CreateNexusEndpointRequest.spec:type_name -> temporal.server.api.persistence.v1.NexusEndpointSpec + 149, // 126: temporal.server.api.matchingservice.v1.CreateNexusEndpointResponse.entry:type_name -> temporal.server.api.persistence.v1.NexusEndpointEntry + 148, // 127: temporal.server.api.matchingservice.v1.UpdateNexusEndpointRequest.spec:type_name -> temporal.server.api.persistence.v1.NexusEndpointSpec + 149, // 128: temporal.server.api.matchingservice.v1.UpdateNexusEndpointResponse.entry:type_name -> temporal.server.api.persistence.v1.NexusEndpointEntry + 149, // 129: temporal.server.api.matchingservice.v1.ListNexusEndpointsResponse.entries:type_name -> temporal.server.api.persistence.v1.NexusEndpointEntry + 150, // 130: temporal.server.api.matchingservice.v1.RecordWorkerHeartbeatRequest.heartbeart_request:type_name -> temporal.api.workflowservice.v1.RecordWorkerHeartbeatRequest + 151, // 131: temporal.server.api.matchingservice.v1.ListWorkersRequest.list_request:type_name -> temporal.api.workflowservice.v1.ListWorkersRequest + 152, // 132: temporal.server.api.matchingservice.v1.ListWorkersResponse.workers_info:type_name -> temporal.api.worker.v1.WorkerInfo + 153, // 133: temporal.server.api.matchingservice.v1.ListWorkersResponse.workers:type_name -> temporal.api.worker.v1.WorkerListInfo + 154, // 134: temporal.server.api.matchingservice.v1.CountWorkersRequest.count_request:type_name -> temporal.api.workflowservice.v1.CountWorkersRequest + 155, // 135: temporal.server.api.matchingservice.v1.UpdateTaskQueueConfigRequest.update_taskqueue_config:type_name -> temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest + 156, // 136: temporal.server.api.matchingservice.v1.UpdateTaskQueueConfigResponse.updated_taskqueue_config:type_name -> temporal.api.taskqueue.v1.TaskQueueConfig + 157, // 137: temporal.server.api.matchingservice.v1.DescribeWorkerRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkerRequest + 152, // 138: temporal.server.api.matchingservice.v1.DescribeWorkerResponse.worker_info:type_name -> temporal.api.worker.v1.WorkerInfo + 121, // 139: temporal.server.api.matchingservice.v1.UpdateFairnessStateRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType + 158, // 140: temporal.server.api.matchingservice.v1.UpdateFairnessStateRequest.fairness_state:type_name -> temporal.server.api.enums.v1.FairnessState + 121, // 141: temporal.server.api.matchingservice.v1.CheckTaskQueueVersionMembershipRequest.task_queue_type:type_name -> temporal.api.enums.v1.TaskQueueType + 124, // 142: temporal.server.api.matchingservice.v1.CheckTaskQueueVersionMembershipRequest.version:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersion + 100, // 143: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 100, // 144: temporal.server.api.matchingservice.v1.PollWorkflowTaskQueueResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 121, // 145: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesRequest.VersionTaskQueue.type:type_name -> temporal.api.enums.v1.TaskQueueType + 121, // 146: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.type:type_name -> temporal.api.enums.v1.TaskQueueType + 159, // 147: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.stats:type_name -> temporal.api.taskqueue.v1.TaskQueueStats + 91, // 148: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.stats_by_priority_key:type_name -> temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.StatsByPriorityKeyEntry + 159, // 149: temporal.server.api.matchingservice.v1.DescribeVersionedTaskQueuesResponse.VersionTaskQueue.StatsByPriorityKeyEntry.value:type_name -> temporal.api.taskqueue.v1.TaskQueueStats + 160, // 150: temporal.server.api.matchingservice.v1.DescribeTaskQueuePartitionResponse.VersionsInfoInternalEntry.value:type_name -> temporal.server.api.taskqueue.v1.TaskQueueVersionInfoInternal + 161, // 151: temporal.server.api.matchingservice.v1.UpdateWorkerBuildIdCompatibilityRequest.ApplyPublicRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest + 162, // 152: temporal.server.api.matchingservice.v1.SyncDeploymentUserDataRequest.UpsertVersionsDataEntry.value:type_name -> temporal.server.api.deployment.v1.WorkerDeploymentVersionData + 153, // [153:153] is the sub-list for method output_type + 153, // [153:153] is the sub-list for method input_type + 153, // [153:153] is the sub-list for extension type_name + 153, // [153:153] is the sub-list for extension extendee + 0, // [0:153] is the sub-list for field type_name } func init() { file_temporal_server_api_matchingservice_v1_request_response_proto_init() } diff --git a/chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go b/chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go new file mode 100644 index 00000000000..28778a8fce9 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go @@ -0,0 +1,101 @@ +// Code generated by protoc-gen-go-helpers. DO NOT EDIT. +package streampb + +import ( + "fmt" + + "google.golang.org/protobuf/proto" +) + +// Marshal an object of type StreamMessage to the protobuf v3 wire format +func (val *StreamMessage) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamMessage from the protobuf v3 wire format +func (val *StreamMessage) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamMessage) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamMessage 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 *StreamMessage) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamMessage + switch t := that.(type) { + case *StreamMessage: + that1 = t + case StreamMessage: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type StreamMessageBatch to the protobuf v3 wire format +func (val *StreamMessageBatch) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamMessageBatch from the protobuf v3 wire format +func (val *StreamMessageBatch) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamMessageBatch) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamMessageBatch 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 *StreamMessageBatch) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamMessageBatch + switch t := that.(type) { + case *StreamMessageBatch: + that1 = t + case StreamMessageBatch: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +var ( + StreamMessageKind_shorthandValue = map[string]int32{ + "Unspecified": 0, + "Data": 1, + "Flush": 2, + } +) + +// StreamMessageKindFromString parses a StreamMessageKind value from either the protojson +// canonical SCREAMING_CASE enum or the traditional temporal PascalCase enum to StreamMessageKind +func StreamMessageKindFromString(s string) (StreamMessageKind, error) { + if v, ok := StreamMessageKind_value[s]; ok { + return StreamMessageKind(v), nil + } else if v, ok := StreamMessageKind_shorthandValue[s]; ok { + return StreamMessageKind(v), nil + } + return StreamMessageKind(0), fmt.Errorf("%s is not a valid StreamMessageKind", s) +} diff --git a/chasm/lib/stream/gen/streampb/v1/message.pb.go b/chasm/lib/stream/gen/streampb/v1/message.pb.go new file mode 100644 index 00000000000..6ab9fdab0a0 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/message.pb.go @@ -0,0 +1,305 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// plugins: +// protoc-gen-go +// protoc +// source: temporal/server/chasm/lib/stream/proto/v1/message.proto + +package streampb + +import ( + reflect "reflect" + "strconv" + sync "sync" + unsafe "unsafe" + + v1 "go.temporal.io/api/common/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StreamMessageKind int32 + +const ( + STREAM_MESSAGE_KIND_UNSPECIFIED StreamMessageKind = 0 + STREAM_MESSAGE_KIND_DATA StreamMessageKind = 1 + // Producer signalling a delivery boundary. Carries no body and consumes an + // offset like any other message. A consumer uses it to end a turn without + // waiting out an idle timeout. + STREAM_MESSAGE_KIND_FLUSH StreamMessageKind = 2 +) + +// Enum value maps for StreamMessageKind. +var ( + StreamMessageKind_name = map[int32]string{ + 0: "STREAM_MESSAGE_KIND_UNSPECIFIED", + 1: "STREAM_MESSAGE_KIND_DATA", + 2: "STREAM_MESSAGE_KIND_FLUSH", + } + StreamMessageKind_value = map[string]int32{ + "STREAM_MESSAGE_KIND_UNSPECIFIED": 0, + "STREAM_MESSAGE_KIND_DATA": 1, + "STREAM_MESSAGE_KIND_FLUSH": 2, + } +) + +func (x StreamMessageKind) Enum() *StreamMessageKind { + p := new(StreamMessageKind) + *p = x + return p +} + +func (x StreamMessageKind) String() string { + switch x { + case STREAM_MESSAGE_KIND_UNSPECIFIED: + return "Unspecified" + case STREAM_MESSAGE_KIND_DATA: + return "Data" + case STREAM_MESSAGE_KIND_FLUSH: + return "Flush" + default: + return strconv.Itoa(int(x)) + } + +} + +func (StreamMessageKind) Descriptor() protoreflect.EnumDescriptor { + return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_enumTypes[0].Descriptor() +} + +func (StreamMessageKind) Type() protoreflect.EnumType { + return &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_enumTypes[0] +} + +func (x StreamMessageKind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use StreamMessageKind.Descriptor instead. +func (StreamMessageKind) EnumDescriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDescGZIP(), []int{0} +} + +type StreamMessage struct { + state protoimpl.MessageState `protogen:"open.v1"` + Body *v1.Payload `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` + // Producer-supplied provenance. The server does not populate this today. + Metadata map[string]*v1.Payload `protobuf:"bytes,2,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Topic string `protobuf:"bytes,3,opt,name=topic,proto3" json:"topic,omitempty"` + // Position within this topic. The global offset orders the whole stream; + // this lets a consumer reason about one topic without decoding the rest. + TopicSequence int64 `protobuf:"varint,4,opt,name=topic_sequence,json=topicSequence,proto3" json:"topic_sequence,omitempty"` + Kind StreamMessageKind `protobuf:"varint,5,opt,name=kind,proto3,enum=temporal.server.chasm.lib.stream.proto.v1.StreamMessageKind" json:"kind,omitempty"` + // Position in the whole stream, set on read and never stored. A consumer + // that resumes at message granularity needs it, and a topic-filtered read + // leaves gaps that make it underivable from the response alone. + Offset int64 `protobuf:"varint,6,opt,name=offset,proto3" json:"offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamMessage) Reset() { + *x = StreamMessage{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamMessage) ProtoMessage() {} + +func (x *StreamMessage) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[0] + 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 StreamMessage.ProtoReflect.Descriptor instead. +func (*StreamMessage) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDescGZIP(), []int{0} +} + +func (x *StreamMessage) GetBody() *v1.Payload { + if x != nil { + return x.Body + } + return nil +} + +func (x *StreamMessage) GetMetadata() map[string]*v1.Payload { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *StreamMessage) GetTopic() string { + if x != nil { + return x.Topic + } + return "" +} + +func (x *StreamMessage) GetTopicSequence() int64 { + if x != nil { + return x.TopicSequence + } + return 0 +} + +func (x *StreamMessage) GetKind() StreamMessageKind { + if x != nil { + return x.Kind + } + return STREAM_MESSAGE_KIND_UNSPECIFIED +} + +func (x *StreamMessage) GetOffset() int64 { + if x != nil { + return x.Offset + } + return 0 +} + +// One append is one batch, and one batch is one log node. The server stores +// this serialized and opaque; it decodes only to trim a partial first page or +// to apply a topic filter. +type StreamMessageBatch struct { + state protoimpl.MessageState `protogen:"open.v1"` + Messages []*StreamMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamMessageBatch) Reset() { + *x = StreamMessageBatch{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamMessageBatch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamMessageBatch) ProtoMessage() {} + +func (x *StreamMessageBatch) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[1] + 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 StreamMessageBatch.ProtoReflect.Descriptor instead. +func (*StreamMessageBatch) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDescGZIP(), []int{1} +} + +func (x *StreamMessageBatch) GetMessages() []*StreamMessage { + if x != nil { + return x.Messages + } + return nil +} + +var File_temporal_server_chasm_lib_stream_proto_v1_message_proto protoreflect.FileDescriptor + +const file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDesc = "" + + "\n" + + "7temporal/server/chasm/lib/stream/proto/v1/message.proto\x12)temporal.server.chasm.lib.stream.proto.v1\x1a$temporal/api/common/v1/message.proto\"\xad\x03\n" + + "\rStreamMessage\x123\n" + + "\x04body\x18\x01 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\x04body\x12b\n" + + "\bmetadata\x18\x02 \x03(\v2F.temporal.server.chasm.lib.stream.proto.v1.StreamMessage.MetadataEntryR\bmetadata\x12\x14\n" + + "\x05topic\x18\x03 \x01(\tR\x05topic\x12%\n" + + "\x0etopic_sequence\x18\x04 \x01(\x03R\rtopicSequence\x12P\n" + + "\x04kind\x18\x05 \x01(\x0e2<.temporal.server.chasm.lib.stream.proto.v1.StreamMessageKindR\x04kind\x12\x16\n" + + "\x06offset\x18\x06 \x01(\x03R\x06offset\x1a\\\n" + + "\rMetadataEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x125\n" + + "\x05value\x18\x02 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\x05value:\x028\x01\"j\n" + + "\x12StreamMessageBatch\x12T\n" + + "\bmessages\x18\x01 \x03(\v28.temporal.server.chasm.lib.stream.proto.v1.StreamMessageR\bmessages*u\n" + + "\x11StreamMessageKind\x12#\n" + + "\x1fSTREAM_MESSAGE_KIND_UNSPECIFIED\x10\x00\x12\x1c\n" + + "\x18STREAM_MESSAGE_KIND_DATA\x10\x01\x12\x1d\n" + + "\x19STREAM_MESSAGE_KIND_FLUSH\x10\x02B>Z temporal.api.common.v1.Payload + 3, // 1: temporal.server.chasm.lib.stream.proto.v1.StreamMessage.metadata:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage.MetadataEntry + 0, // 2: temporal.server.chasm.lib.stream.proto.v1.StreamMessage.kind:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessageKind + 1, // 3: temporal.server.chasm.lib.stream.proto.v1.StreamMessageBatch.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage + 4, // 4: temporal.server.chasm.lib.stream.proto.v1.StreamMessage.MetadataEntry.value:type_name -> temporal.api.common.v1.Payload + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_temporal_server_chasm_lib_stream_proto_v1_message_proto_init() } +func file_temporal_server_chasm_lib_stream_proto_v1_message_proto_init() { + if File_temporal_server_chasm_lib_stream_proto_v1_message_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDesc), len(file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDesc)), + NumEnums: 1, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_goTypes, + DependencyIndexes: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_depIdxs, + EnumInfos: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_enumTypes, + MessageInfos: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes, + }.Build() + File_temporal_server_chasm_lib_stream_proto_v1_message_proto = out.File + file_temporal_server_chasm_lib_stream_proto_v1_message_proto_goTypes = nil + file_temporal_server_chasm_lib_stream_proto_v1_message_proto_depIdxs = nil +} diff --git a/chasm/lib/stream/gen/streampb/v1/namespace.go b/chasm/lib/stream/gen/streampb/v1/namespace.go new file mode 100644 index 00000000000..dd465ef065e --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/namespace.go @@ -0,0 +1,75 @@ +package streampb + +// Every RPC here carries its namespace inside `frontend_request` rather than at +// the top level, because the top-level field is the resolved namespace id the +// frontend fills in before routing. +// +// The server's interceptors find a request's namespace by asserting it to +// `interceptor.NamespaceNameGetter`, which wants `GetNamespace() string` on the +// request itself. Without these the assertion falls through to the id getter, +// which at the frontend is still empty, so namespace rate limits, request +// validation, the authorization target, redirection and the long-poll deadline +// all resolve to the empty namespace and silently do nothing. +// +// The generator has no way to express "read it from this nested field", so the +// methods are written here, next to the generated types they belong to. + +func (x *CreateStreamRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *AddMessagesRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *FinishWritingRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *SubscribeWorkflowRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *PollMessagesRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *DescribeStreamRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *PollWorkflowMessagesRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *DescribeWorkflowStreamRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *AddWorkflowMessagesRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *RegisterStreamConsumerRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *AdvanceConsumerHeadRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *CloseStreamRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *TruncateStreamRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *ListStreamsRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} + +func (x *DeleteStreamRequest) GetNamespace() string { + return x.GetFrontendRequest().GetNamespace() +} diff --git a/chasm/lib/stream/gen/streampb/v1/namespace_test.go b/chasm/lib/stream/gen/streampb/v1/namespace_test.go new file mode 100644 index 00000000000..b88bcaf856c --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/namespace_test.go @@ -0,0 +1,38 @@ +package streampb + +import ( + "testing" + + "go.temporal.io/server/common/rpc/interceptor" + "google.golang.org/protobuf/reflect/protoregistry" +) + +// Every request carrying a frontend_request must expose its namespace to the +// interceptors. Driven off the descriptor rather than a hand-written list so a +// new RPC fails here instead of silently opting out of namespace rate limits, +// validation, authorization and redirection. +func TestEveryRoutedRequestExposesNamespace(t *testing.T) { + fd := File_temporal_server_chasm_lib_stream_proto_v1_request_response_proto + messages := fd.Messages() + + checked := 0 + for i := 0; i < messages.Len(); i++ { + md := messages.Get(i) + if md.Fields().ByName("frontend_request") == nil { + continue + } + mt, err := protoregistry.GlobalTypes.FindMessageByName(md.FullName()) + if err != nil { + t.Fatalf("%s is not registered: %v", md.FullName(), err) + } + msg := mt.New().Interface() + if _, ok := msg.(interceptor.NamespaceNameGetter); !ok { + t.Errorf("%s has a frontend_request but no GetNamespace; add it in namespace.go", md.FullName()) + } + checked++ + } + + if checked == 0 { + t.Fatal("found no routed requests, so this test is not checking anything") + } +} diff --git a/chasm/lib/stream/gen/streampb/v1/request_response.go-helpers.pb.go b/chasm/lib/stream/gen/streampb/v1/request_response.go-helpers.pb.go new file mode 100644 index 00000000000..7c11d9434cd --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/request_response.go-helpers.pb.go @@ -0,0 +1,2152 @@ +// Code generated by protoc-gen-go-helpers. DO NOT EDIT. +package streampb + +import ( + "google.golang.org/protobuf/proto" +) + +// Marshal an object of type CreateStreamInput to the protobuf v3 wire format +func (val *CreateStreamInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CreateStreamInput from the protobuf v3 wire format +func (val *CreateStreamInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CreateStreamInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CreateStreamInput 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 *CreateStreamInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CreateStreamInput + switch t := that.(type) { + case *CreateStreamInput: + that1 = t + case CreateStreamInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type CreateStreamOutput to the protobuf v3 wire format +func (val *CreateStreamOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CreateStreamOutput from the protobuf v3 wire format +func (val *CreateStreamOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CreateStreamOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CreateStreamOutput 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 *CreateStreamOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CreateStreamOutput + switch t := that.(type) { + case *CreateStreamOutput: + that1 = t + case CreateStreamOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AddMessagesInput to the protobuf v3 wire format +func (val *AddMessagesInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AddMessagesInput from the protobuf v3 wire format +func (val *AddMessagesInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AddMessagesInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AddMessagesInput 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 *AddMessagesInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AddMessagesInput + switch t := that.(type) { + case *AddMessagesInput: + that1 = t + case AddMessagesInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AddMessagesOutput to the protobuf v3 wire format +func (val *AddMessagesOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AddMessagesOutput from the protobuf v3 wire format +func (val *AddMessagesOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AddMessagesOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AddMessagesOutput 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 *AddMessagesOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AddMessagesOutput + switch t := that.(type) { + case *AddMessagesOutput: + that1 = t + case AddMessagesOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type FinishWritingInput to the protobuf v3 wire format +func (val *FinishWritingInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type FinishWritingInput from the protobuf v3 wire format +func (val *FinishWritingInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *FinishWritingInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two FinishWritingInput 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 *FinishWritingInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *FinishWritingInput + switch t := that.(type) { + case *FinishWritingInput: + that1 = t + case FinishWritingInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type FinishWritingOutput to the protobuf v3 wire format +func (val *FinishWritingOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type FinishWritingOutput from the protobuf v3 wire format +func (val *FinishWritingOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *FinishWritingOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two FinishWritingOutput 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 *FinishWritingOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *FinishWritingOutput + switch t := that.(type) { + case *FinishWritingOutput: + that1 = t + case FinishWritingOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type SubscribeWorkflowInput to the protobuf v3 wire format +func (val *SubscribeWorkflowInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type SubscribeWorkflowInput from the protobuf v3 wire format +func (val *SubscribeWorkflowInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *SubscribeWorkflowInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two SubscribeWorkflowInput 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 *SubscribeWorkflowInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *SubscribeWorkflowInput + switch t := that.(type) { + case *SubscribeWorkflowInput: + that1 = t + case SubscribeWorkflowInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type SubscribeWorkflowOutput to the protobuf v3 wire format +func (val *SubscribeWorkflowOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type SubscribeWorkflowOutput from the protobuf v3 wire format +func (val *SubscribeWorkflowOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *SubscribeWorkflowOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two SubscribeWorkflowOutput 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 *SubscribeWorkflowOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *SubscribeWorkflowOutput + switch t := that.(type) { + case *SubscribeWorkflowOutput: + that1 = t + case SubscribeWorkflowOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type PollMessagesInput to the protobuf v3 wire format +func (val *PollMessagesInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type PollMessagesInput from the protobuf v3 wire format +func (val *PollMessagesInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *PollMessagesInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two PollMessagesInput 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 *PollMessagesInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *PollMessagesInput + switch t := that.(type) { + case *PollMessagesInput: + that1 = t + case PollMessagesInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type PollMessagesOutput to the protobuf v3 wire format +func (val *PollMessagesOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type PollMessagesOutput from the protobuf v3 wire format +func (val *PollMessagesOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *PollMessagesOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two PollMessagesOutput 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 *PollMessagesOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *PollMessagesOutput + switch t := that.(type) { + case *PollMessagesOutput: + that1 = t + case PollMessagesOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DescribeStreamInput to the protobuf v3 wire format +func (val *DescribeStreamInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DescribeStreamInput from the protobuf v3 wire format +func (val *DescribeStreamInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DescribeStreamInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DescribeStreamInput 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 *DescribeStreamInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DescribeStreamInput + switch t := that.(type) { + case *DescribeStreamInput: + that1 = t + case DescribeStreamInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type PollWorkflowMessagesInput to the protobuf v3 wire format +func (val *PollWorkflowMessagesInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type PollWorkflowMessagesInput from the protobuf v3 wire format +func (val *PollWorkflowMessagesInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *PollWorkflowMessagesInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two PollWorkflowMessagesInput 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 *PollWorkflowMessagesInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *PollWorkflowMessagesInput + switch t := that.(type) { + case *PollWorkflowMessagesInput: + that1 = t + case PollWorkflowMessagesInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DescribeWorkflowStreamInput to the protobuf v3 wire format +func (val *DescribeWorkflowStreamInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DescribeWorkflowStreamInput from the protobuf v3 wire format +func (val *DescribeWorkflowStreamInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DescribeWorkflowStreamInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DescribeWorkflowStreamInput 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 *DescribeWorkflowStreamInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DescribeWorkflowStreamInput + switch t := that.(type) { + case *DescribeWorkflowStreamInput: + that1 = t + case DescribeWorkflowStreamInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AddWorkflowMessagesInput to the protobuf v3 wire format +func (val *AddWorkflowMessagesInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AddWorkflowMessagesInput from the protobuf v3 wire format +func (val *AddWorkflowMessagesInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AddWorkflowMessagesInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AddWorkflowMessagesInput 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 *AddWorkflowMessagesInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AddWorkflowMessagesInput + switch t := that.(type) { + case *AddWorkflowMessagesInput: + that1 = t + case AddWorkflowMessagesInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DescribeStreamOutput to the protobuf v3 wire format +func (val *DescribeStreamOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DescribeStreamOutput from the protobuf v3 wire format +func (val *DescribeStreamOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DescribeStreamOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DescribeStreamOutput 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 *DescribeStreamOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DescribeStreamOutput + switch t := that.(type) { + case *DescribeStreamOutput: + that1 = t + case DescribeStreamOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type CloseStreamInput to the protobuf v3 wire format +func (val *CloseStreamInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CloseStreamInput from the protobuf v3 wire format +func (val *CloseStreamInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CloseStreamInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CloseStreamInput 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 *CloseStreamInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CloseStreamInput + switch t := that.(type) { + case *CloseStreamInput: + that1 = t + case CloseStreamInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type CloseStreamOutput to the protobuf v3 wire format +func (val *CloseStreamOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CloseStreamOutput from the protobuf v3 wire format +func (val *CloseStreamOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CloseStreamOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CloseStreamOutput 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 *CloseStreamOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CloseStreamOutput + switch t := that.(type) { + case *CloseStreamOutput: + that1 = t + case CloseStreamOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type TruncateStreamInput to the protobuf v3 wire format +func (val *TruncateStreamInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type TruncateStreamInput from the protobuf v3 wire format +func (val *TruncateStreamInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *TruncateStreamInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two TruncateStreamInput 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 *TruncateStreamInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *TruncateStreamInput + switch t := that.(type) { + case *TruncateStreamInput: + that1 = t + case TruncateStreamInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type TruncateStreamOutput to the protobuf v3 wire format +func (val *TruncateStreamOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type TruncateStreamOutput from the protobuf v3 wire format +func (val *TruncateStreamOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *TruncateStreamOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two TruncateStreamOutput 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 *TruncateStreamOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *TruncateStreamOutput + switch t := that.(type) { + case *TruncateStreamOutput: + that1 = t + case TruncateStreamOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DeleteStreamInput to the protobuf v3 wire format +func (val *DeleteStreamInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DeleteStreamInput from the protobuf v3 wire format +func (val *DeleteStreamInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DeleteStreamInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DeleteStreamInput 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 *DeleteStreamInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DeleteStreamInput + switch t := that.(type) { + case *DeleteStreamInput: + that1 = t + case DeleteStreamInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DeleteStreamOutput to the protobuf v3 wire format +func (val *DeleteStreamOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DeleteStreamOutput from the protobuf v3 wire format +func (val *DeleteStreamOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DeleteStreamOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DeleteStreamOutput 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 *DeleteStreamOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DeleteStreamOutput + switch t := that.(type) { + case *DeleteStreamOutput: + that1 = t + case DeleteStreamOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type CreateStreamRequest to the protobuf v3 wire format +func (val *CreateStreamRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CreateStreamRequest from the protobuf v3 wire format +func (val *CreateStreamRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CreateStreamRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CreateStreamRequest 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 *CreateStreamRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CreateStreamRequest + switch t := that.(type) { + case *CreateStreamRequest: + that1 = t + case CreateStreamRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type CreateStreamResponse to the protobuf v3 wire format +func (val *CreateStreamResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CreateStreamResponse from the protobuf v3 wire format +func (val *CreateStreamResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CreateStreamResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CreateStreamResponse 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 *CreateStreamResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CreateStreamResponse + switch t := that.(type) { + case *CreateStreamResponse: + that1 = t + case CreateStreamResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AddMessagesRequest to the protobuf v3 wire format +func (val *AddMessagesRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AddMessagesRequest from the protobuf v3 wire format +func (val *AddMessagesRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AddMessagesRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AddMessagesRequest 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 *AddMessagesRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AddMessagesRequest + switch t := that.(type) { + case *AddMessagesRequest: + that1 = t + case AddMessagesRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AddMessagesResponse to the protobuf v3 wire format +func (val *AddMessagesResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AddMessagesResponse from the protobuf v3 wire format +func (val *AddMessagesResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AddMessagesResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AddMessagesResponse 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 *AddMessagesResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AddMessagesResponse + switch t := that.(type) { + case *AddMessagesResponse: + that1 = t + case AddMessagesResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type FinishWritingRequest to the protobuf v3 wire format +func (val *FinishWritingRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type FinishWritingRequest from the protobuf v3 wire format +func (val *FinishWritingRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *FinishWritingRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two FinishWritingRequest 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 *FinishWritingRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *FinishWritingRequest + switch t := that.(type) { + case *FinishWritingRequest: + that1 = t + case FinishWritingRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type FinishWritingResponse to the protobuf v3 wire format +func (val *FinishWritingResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type FinishWritingResponse from the protobuf v3 wire format +func (val *FinishWritingResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *FinishWritingResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two FinishWritingResponse 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 *FinishWritingResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *FinishWritingResponse + switch t := that.(type) { + case *FinishWritingResponse: + that1 = t + case FinishWritingResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type SubscribeWorkflowRequest to the protobuf v3 wire format +func (val *SubscribeWorkflowRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type SubscribeWorkflowRequest from the protobuf v3 wire format +func (val *SubscribeWorkflowRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *SubscribeWorkflowRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two SubscribeWorkflowRequest 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 *SubscribeWorkflowRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *SubscribeWorkflowRequest + switch t := that.(type) { + case *SubscribeWorkflowRequest: + that1 = t + case SubscribeWorkflowRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type SubscribeWorkflowResponse to the protobuf v3 wire format +func (val *SubscribeWorkflowResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type SubscribeWorkflowResponse from the protobuf v3 wire format +func (val *SubscribeWorkflowResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *SubscribeWorkflowResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two SubscribeWorkflowResponse 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 *SubscribeWorkflowResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *SubscribeWorkflowResponse + switch t := that.(type) { + case *SubscribeWorkflowResponse: + that1 = t + case SubscribeWorkflowResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type PollMessagesRequest to the protobuf v3 wire format +func (val *PollMessagesRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type PollMessagesRequest from the protobuf v3 wire format +func (val *PollMessagesRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *PollMessagesRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two PollMessagesRequest 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 *PollMessagesRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *PollMessagesRequest + switch t := that.(type) { + case *PollMessagesRequest: + that1 = t + case PollMessagesRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type PollMessagesResponse to the protobuf v3 wire format +func (val *PollMessagesResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type PollMessagesResponse from the protobuf v3 wire format +func (val *PollMessagesResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *PollMessagesResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two PollMessagesResponse 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 *PollMessagesResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *PollMessagesResponse + switch t := that.(type) { + case *PollMessagesResponse: + that1 = t + case PollMessagesResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DescribeStreamRequest to the protobuf v3 wire format +func (val *DescribeStreamRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DescribeStreamRequest from the protobuf v3 wire format +func (val *DescribeStreamRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DescribeStreamRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DescribeStreamRequest 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 *DescribeStreamRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DescribeStreamRequest + switch t := that.(type) { + case *DescribeStreamRequest: + that1 = t + case DescribeStreamRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DescribeStreamResponse to the protobuf v3 wire format +func (val *DescribeStreamResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DescribeStreamResponse from the protobuf v3 wire format +func (val *DescribeStreamResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DescribeStreamResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DescribeStreamResponse 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 *DescribeStreamResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DescribeStreamResponse + switch t := that.(type) { + case *DescribeStreamResponse: + that1 = t + case DescribeStreamResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type PollWorkflowMessagesRequest to the protobuf v3 wire format +func (val *PollWorkflowMessagesRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type PollWorkflowMessagesRequest from the protobuf v3 wire format +func (val *PollWorkflowMessagesRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *PollWorkflowMessagesRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two PollWorkflowMessagesRequest 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 *PollWorkflowMessagesRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *PollWorkflowMessagesRequest + switch t := that.(type) { + case *PollWorkflowMessagesRequest: + that1 = t + case PollWorkflowMessagesRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type PollWorkflowMessagesResponse to the protobuf v3 wire format +func (val *PollWorkflowMessagesResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type PollWorkflowMessagesResponse from the protobuf v3 wire format +func (val *PollWorkflowMessagesResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *PollWorkflowMessagesResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two PollWorkflowMessagesResponse 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 *PollWorkflowMessagesResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *PollWorkflowMessagesResponse + switch t := that.(type) { + case *PollWorkflowMessagesResponse: + that1 = t + case PollWorkflowMessagesResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DescribeWorkflowStreamRequest to the protobuf v3 wire format +func (val *DescribeWorkflowStreamRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DescribeWorkflowStreamRequest from the protobuf v3 wire format +func (val *DescribeWorkflowStreamRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DescribeWorkflowStreamRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DescribeWorkflowStreamRequest 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 *DescribeWorkflowStreamRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DescribeWorkflowStreamRequest + switch t := that.(type) { + case *DescribeWorkflowStreamRequest: + that1 = t + case DescribeWorkflowStreamRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DescribeWorkflowStreamResponse to the protobuf v3 wire format +func (val *DescribeWorkflowStreamResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DescribeWorkflowStreamResponse from the protobuf v3 wire format +func (val *DescribeWorkflowStreamResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DescribeWorkflowStreamResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DescribeWorkflowStreamResponse 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 *DescribeWorkflowStreamResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DescribeWorkflowStreamResponse + switch t := that.(type) { + case *DescribeWorkflowStreamResponse: + that1 = t + case DescribeWorkflowStreamResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type RegisterStreamConsumerInput to the protobuf v3 wire format +func (val *RegisterStreamConsumerInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type RegisterStreamConsumerInput from the protobuf v3 wire format +func (val *RegisterStreamConsumerInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *RegisterStreamConsumerInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two RegisterStreamConsumerInput 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 *RegisterStreamConsumerInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *RegisterStreamConsumerInput + switch t := that.(type) { + case *RegisterStreamConsumerInput: + that1 = t + case RegisterStreamConsumerInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type RegisterStreamConsumerOutput to the protobuf v3 wire format +func (val *RegisterStreamConsumerOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type RegisterStreamConsumerOutput from the protobuf v3 wire format +func (val *RegisterStreamConsumerOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *RegisterStreamConsumerOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two RegisterStreamConsumerOutput 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 *RegisterStreamConsumerOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *RegisterStreamConsumerOutput + switch t := that.(type) { + case *RegisterStreamConsumerOutput: + that1 = t + case RegisterStreamConsumerOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AdvanceConsumerHeadInput to the protobuf v3 wire format +func (val *AdvanceConsumerHeadInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AdvanceConsumerHeadInput from the protobuf v3 wire format +func (val *AdvanceConsumerHeadInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AdvanceConsumerHeadInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AdvanceConsumerHeadInput 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 *AdvanceConsumerHeadInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AdvanceConsumerHeadInput + switch t := that.(type) { + case *AdvanceConsumerHeadInput: + that1 = t + case AdvanceConsumerHeadInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AdvanceConsumerHeadOutput to the protobuf v3 wire format +func (val *AdvanceConsumerHeadOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AdvanceConsumerHeadOutput from the protobuf v3 wire format +func (val *AdvanceConsumerHeadOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AdvanceConsumerHeadOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AdvanceConsumerHeadOutput 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 *AdvanceConsumerHeadOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AdvanceConsumerHeadOutput + switch t := that.(type) { + case *AdvanceConsumerHeadOutput: + that1 = t + case AdvanceConsumerHeadOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AddWorkflowMessagesRequest to the protobuf v3 wire format +func (val *AddWorkflowMessagesRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AddWorkflowMessagesRequest from the protobuf v3 wire format +func (val *AddWorkflowMessagesRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AddWorkflowMessagesRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AddWorkflowMessagesRequest 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 *AddWorkflowMessagesRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AddWorkflowMessagesRequest + switch t := that.(type) { + case *AddWorkflowMessagesRequest: + that1 = t + case AddWorkflowMessagesRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AddWorkflowMessagesResponse to the protobuf v3 wire format +func (val *AddWorkflowMessagesResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AddWorkflowMessagesResponse from the protobuf v3 wire format +func (val *AddWorkflowMessagesResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AddWorkflowMessagesResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AddWorkflowMessagesResponse 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 *AddWorkflowMessagesResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AddWorkflowMessagesResponse + switch t := that.(type) { + case *AddWorkflowMessagesResponse: + that1 = t + case AddWorkflowMessagesResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type RegisterStreamConsumerRequest to the protobuf v3 wire format +func (val *RegisterStreamConsumerRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type RegisterStreamConsumerRequest from the protobuf v3 wire format +func (val *RegisterStreamConsumerRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *RegisterStreamConsumerRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two RegisterStreamConsumerRequest 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 *RegisterStreamConsumerRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *RegisterStreamConsumerRequest + switch t := that.(type) { + case *RegisterStreamConsumerRequest: + that1 = t + case RegisterStreamConsumerRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type RegisterStreamConsumerResponse to the protobuf v3 wire format +func (val *RegisterStreamConsumerResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type RegisterStreamConsumerResponse from the protobuf v3 wire format +func (val *RegisterStreamConsumerResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *RegisterStreamConsumerResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two RegisterStreamConsumerResponse 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 *RegisterStreamConsumerResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *RegisterStreamConsumerResponse + switch t := that.(type) { + case *RegisterStreamConsumerResponse: + that1 = t + case RegisterStreamConsumerResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AdvanceConsumerHeadRequest to the protobuf v3 wire format +func (val *AdvanceConsumerHeadRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AdvanceConsumerHeadRequest from the protobuf v3 wire format +func (val *AdvanceConsumerHeadRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AdvanceConsumerHeadRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AdvanceConsumerHeadRequest 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 *AdvanceConsumerHeadRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AdvanceConsumerHeadRequest + switch t := that.(type) { + case *AdvanceConsumerHeadRequest: + that1 = t + case AdvanceConsumerHeadRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type AdvanceConsumerHeadResponse to the protobuf v3 wire format +func (val *AdvanceConsumerHeadResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type AdvanceConsumerHeadResponse from the protobuf v3 wire format +func (val *AdvanceConsumerHeadResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *AdvanceConsumerHeadResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two AdvanceConsumerHeadResponse 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 *AdvanceConsumerHeadResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *AdvanceConsumerHeadResponse + switch t := that.(type) { + case *AdvanceConsumerHeadResponse: + that1 = t + case AdvanceConsumerHeadResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type CloseStreamRequest to the protobuf v3 wire format +func (val *CloseStreamRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CloseStreamRequest from the protobuf v3 wire format +func (val *CloseStreamRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CloseStreamRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CloseStreamRequest 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 *CloseStreamRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CloseStreamRequest + switch t := that.(type) { + case *CloseStreamRequest: + that1 = t + case CloseStreamRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type CloseStreamResponse to the protobuf v3 wire format +func (val *CloseStreamResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type CloseStreamResponse from the protobuf v3 wire format +func (val *CloseStreamResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *CloseStreamResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two CloseStreamResponse 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 *CloseStreamResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *CloseStreamResponse + switch t := that.(type) { + case *CloseStreamResponse: + that1 = t + case CloseStreamResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type TruncateStreamRequest to the protobuf v3 wire format +func (val *TruncateStreamRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type TruncateStreamRequest from the protobuf v3 wire format +func (val *TruncateStreamRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *TruncateStreamRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two TruncateStreamRequest 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 *TruncateStreamRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *TruncateStreamRequest + switch t := that.(type) { + case *TruncateStreamRequest: + that1 = t + case TruncateStreamRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type TruncateStreamResponse to the protobuf v3 wire format +func (val *TruncateStreamResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type TruncateStreamResponse from the protobuf v3 wire format +func (val *TruncateStreamResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *TruncateStreamResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two TruncateStreamResponse 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 *TruncateStreamResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *TruncateStreamResponse + switch t := that.(type) { + case *TruncateStreamResponse: + that1 = t + case TruncateStreamResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type ListStreamsInput to the protobuf v3 wire format +func (val *ListStreamsInput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type ListStreamsInput from the protobuf v3 wire format +func (val *ListStreamsInput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *ListStreamsInput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two ListStreamsInput 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 *ListStreamsInput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *ListStreamsInput + switch t := that.(type) { + case *ListStreamsInput: + that1 = t + case ListStreamsInput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type StreamListEntry to the protobuf v3 wire format +func (val *StreamListEntry) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamListEntry from the protobuf v3 wire format +func (val *StreamListEntry) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamListEntry) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamListEntry 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 *StreamListEntry) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamListEntry + switch t := that.(type) { + case *StreamListEntry: + that1 = t + case StreamListEntry: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type ListStreamsOutput to the protobuf v3 wire format +func (val *ListStreamsOutput) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type ListStreamsOutput from the protobuf v3 wire format +func (val *ListStreamsOutput) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *ListStreamsOutput) Size() int { + return proto.Size(val) +} + +// Equal returns whether two ListStreamsOutput 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 *ListStreamsOutput) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *ListStreamsOutput + switch t := that.(type) { + case *ListStreamsOutput: + that1 = t + case ListStreamsOutput: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type ListStreamsRequest to the protobuf v3 wire format +func (val *ListStreamsRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type ListStreamsRequest from the protobuf v3 wire format +func (val *ListStreamsRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *ListStreamsRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two ListStreamsRequest 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 *ListStreamsRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *ListStreamsRequest + switch t := that.(type) { + case *ListStreamsRequest: + that1 = t + case ListStreamsRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type ListStreamsResponse to the protobuf v3 wire format +func (val *ListStreamsResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type ListStreamsResponse from the protobuf v3 wire format +func (val *ListStreamsResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *ListStreamsResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two ListStreamsResponse 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 *ListStreamsResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *ListStreamsResponse + switch t := that.(type) { + case *ListStreamsResponse: + that1 = t + case ListStreamsResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DeleteStreamRequest to the protobuf v3 wire format +func (val *DeleteStreamRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DeleteStreamRequest from the protobuf v3 wire format +func (val *DeleteStreamRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DeleteStreamRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DeleteStreamRequest 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 *DeleteStreamRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DeleteStreamRequest + switch t := that.(type) { + case *DeleteStreamRequest: + that1 = t + case DeleteStreamRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type DeleteStreamResponse to the protobuf v3 wire format +func (val *DeleteStreamResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type DeleteStreamResponse from the protobuf v3 wire format +func (val *DeleteStreamResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *DeleteStreamResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two DeleteStreamResponse 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 *DeleteStreamResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *DeleteStreamResponse + switch t := that.(type) { + case *DeleteStreamResponse: + that1 = t + case DeleteStreamResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} diff --git a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go new file mode 100644 index 00000000000..d941c63b908 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go @@ -0,0 +1,3620 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// plugins: +// protoc-gen-go +// protoc +// source: temporal/server/chasm/lib/stream/proto/v1/request_response.proto + +package streampb + +import ( + reflect "reflect" + sync "sync" + unsafe "unsafe" + + v1 "go.temporal.io/api/common/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CreateStreamInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + Lifecycle *StreamLifecycle `protobuf:"bytes,3,opt,name=lifecycle,proto3" json:"lifecycle,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateStreamInput) Reset() { + *x = CreateStreamInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateStreamInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateStreamInput) ProtoMessage() {} + +func (x *CreateStreamInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[0] + 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 CreateStreamInput.ProtoReflect.Descriptor instead. +func (*CreateStreamInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateStreamInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *CreateStreamInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *CreateStreamInput) GetLifecycle() *StreamLifecycle { + if x != nil { + return x.Lifecycle + } + return nil +} + +type CreateStreamOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + RunId string `protobuf:"bytes,1,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateStreamOutput) Reset() { + *x = CreateStreamOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateStreamOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateStreamOutput) ProtoMessage() {} + +func (x *CreateStreamOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[1] + 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 CreateStreamOutput.ProtoReflect.Descriptor instead. +func (*CreateStreamOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateStreamOutput) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +type AddMessagesInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + // Optional. Supplying it skips resolving the stream's current run, which is + // a persistence lookup on every call. CreateStream returns it. + RunId string `protobuf:"bytes,9,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + Messages []*StreamMessage `protobuf:"bytes,3,rep,name=messages,proto3" json:"messages,omitempty"` + // Idempotency, all optional. Supply a producer identity and sequence, or an + // expected offset, or neither and accept at-least-once. + ProducerId string `protobuf:"bytes,4,opt,name=producer_id,json=producerId,proto3" json:"producer_id,omitempty"` + Sequence int64 `protobuf:"varint,5,opt,name=sequence,proto3" json:"sequence,omitempty"` + // Guarded by use_expected_offset because proto3 optional is not supported + // by this repo's helper generator. + ExpectedOffset int64 `protobuf:"varint,6,opt,name=expected_offset,json=expectedOffset,proto3" json:"expected_offset,omitempty"` + UseExpectedOffset bool `protobuf:"varint,8,opt,name=use_expected_offset,json=useExpectedOffset,proto3" json:"use_expected_offset,omitempty"` + OwnerEpoch int64 `protobuf:"varint,7,opt,name=owner_epoch,json=ownerEpoch,proto3" json:"owner_epoch,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddMessagesInput) Reset() { + *x = AddMessagesInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddMessagesInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddMessagesInput) ProtoMessage() {} + +func (x *AddMessagesInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_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 AddMessagesInput.ProtoReflect.Descriptor instead. +func (*AddMessagesInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{2} +} + +func (x *AddMessagesInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *AddMessagesInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *AddMessagesInput) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +func (x *AddMessagesInput) GetMessages() []*StreamMessage { + if x != nil { + return x.Messages + } + return nil +} + +func (x *AddMessagesInput) GetProducerId() string { + if x != nil { + return x.ProducerId + } + return "" +} + +func (x *AddMessagesInput) GetSequence() int64 { + if x != nil { + return x.Sequence + } + return 0 +} + +func (x *AddMessagesInput) GetExpectedOffset() int64 { + if x != nil { + return x.ExpectedOffset + } + return 0 +} + +func (x *AddMessagesInput) GetUseExpectedOffset() bool { + if x != nil { + return x.UseExpectedOffset + } + return false +} + +func (x *AddMessagesInput) GetOwnerEpoch() int64 { + if x != nil { + return x.OwnerEpoch + } + return 0 +} + +type AddMessagesOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + FirstOffset int64 `protobuf:"varint,1,opt,name=first_offset,json=firstOffset,proto3" json:"first_offset,omitempty"` + NextOffset int64 `protobuf:"varint,2,opt,name=next_offset,json=nextOffset,proto3" json:"next_offset,omitempty"` + Count int64 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` + // True when a retry matched a recorded sequence and nothing was appended. + Deduplicated bool `protobuf:"varint,4,opt,name=deduplicated,proto3" json:"deduplicated,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddMessagesOutput) Reset() { + *x = AddMessagesOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddMessagesOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddMessagesOutput) ProtoMessage() {} + +func (x *AddMessagesOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddMessagesOutput.ProtoReflect.Descriptor instead. +func (*AddMessagesOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{3} +} + +func (x *AddMessagesOutput) GetFirstOffset() int64 { + if x != nil { + return x.FirstOffset + } + return 0 +} + +func (x *AddMessagesOutput) GetNextOffset() int64 { + if x != nil { + return x.NextOffset + } + return 0 +} + +func (x *AddMessagesOutput) GetCount() int64 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AddMessagesOutput) GetDeduplicated() bool { + if x != nil { + return x.Deduplicated + } + return false +} + +type FinishWritingInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + ProducerId string `protobuf:"bytes,3,opt,name=producer_id,json=producerId,proto3" json:"producer_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FinishWritingInput) Reset() { + *x = FinishWritingInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FinishWritingInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishWritingInput) ProtoMessage() {} + +func (x *FinishWritingInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FinishWritingInput.ProtoReflect.Descriptor instead. +func (*FinishWritingInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{4} +} + +func (x *FinishWritingInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *FinishWritingInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *FinishWritingInput) GetProducerId() string { + if x != nil { + return x.ProducerId + } + return "" +} + +type FinishWritingOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FinishWritingOutput) Reset() { + *x = FinishWritingOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FinishWritingOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishWritingOutput) ProtoMessage() {} + +func (x *FinishWritingOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[5] + 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 FinishWritingOutput.ProtoReflect.Descriptor instead. +func (*FinishWritingOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{5} +} + +// Registers a Workflow as a consumer of a stream it owns. The cursor lands in +// the Workflow's own state, so from then on each of its Workflow Tasks carries +// the next range and records what it consumed. +type SubscribeWorkflowInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + WorkflowId string `protobuf:"bytes,2,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"` + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + OwnerRunId string `protobuf:"bytes,6,opt,name=owner_run_id,json=ownerRunId,proto3" json:"owner_run_id,omitempty"` + // Name of the stream within the Workflow, for a stream it owns. + StreamName string `protobuf:"bytes,3,opt,name=stream_name,json=streamName,proto3" json:"stream_name,omitempty"` + // Id of a standalone stream in another execution. Exactly one of this and + // stream_name is set. + StreamId string `protobuf:"bytes,5,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + // Where to start. Resolved here rather than at delivery, so the first + // recorded range starts from a fact instead of a reading. + StartOffset int64 `protobuf:"varint,4,opt,name=start_offset,json=startOffset,proto3" json:"start_offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubscribeWorkflowInput) Reset() { + *x = SubscribeWorkflowInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubscribeWorkflowInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeWorkflowInput) ProtoMessage() {} + +func (x *SubscribeWorkflowInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[6] + 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 SubscribeWorkflowInput.ProtoReflect.Descriptor instead. +func (*SubscribeWorkflowInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{6} +} + +func (x *SubscribeWorkflowInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *SubscribeWorkflowInput) GetWorkflowId() string { + if x != nil { + return x.WorkflowId + } + return "" +} + +func (x *SubscribeWorkflowInput) GetOwnerRunId() string { + if x != nil { + return x.OwnerRunId + } + return "" +} + +func (x *SubscribeWorkflowInput) GetStreamName() string { + if x != nil { + return x.StreamName + } + return "" +} + +func (x *SubscribeWorkflowInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *SubscribeWorkflowInput) GetStartOffset() int64 { + if x != nil { + return x.StartOffset + } + return 0 +} + +type SubscribeWorkflowOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + StartOffset int64 `protobuf:"varint,1,opt,name=start_offset,json=startOffset,proto3" json:"start_offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubscribeWorkflowOutput) Reset() { + *x = SubscribeWorkflowOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubscribeWorkflowOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeWorkflowOutput) ProtoMessage() {} + +func (x *SubscribeWorkflowOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[7] + 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 SubscribeWorkflowOutput.ProtoReflect.Descriptor instead. +func (*SubscribeWorkflowOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{7} +} + +func (x *SubscribeWorkflowOutput) GetStartOffset() int64 { + if x != nil { + return x.StartOffset + } + return 0 +} + +type PollMessagesInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + // Optional, as on AddMessagesInput. + RunId string `protobuf:"bytes,7,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + FromOffset int64 `protobuf:"varint,3,opt,name=from_offset,json=fromOffset,proto3" json:"from_offset,omitempty"` + MaxMessages int32 `protobuf:"varint,4,opt,name=max_messages,json=maxMessages,proto3" json:"max_messages,omitempty"` + // Filters by exact topic. Offsets are assigned over the unfiltered stream, so + // next_offset advances past filtered-out messages too. + Topics []string `protobuf:"bytes,5,rep,name=topics,proto3" json:"topics,omitempty"` + // When set and the reader is caught up, block until something arrives, the + // stream closes, or the server's long-poll timeout elapses. A timeout returns + // an empty response rather than an error, so the caller simply polls again. + WaitNewMessages bool `protobuf:"varint,6,opt,name=wait_new_messages,json=waitNewMessages,proto3" json:"wait_new_messages,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollMessagesInput) Reset() { + *x = PollMessagesInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollMessagesInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollMessagesInput) ProtoMessage() {} + +func (x *PollMessagesInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[8] + 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 PollMessagesInput.ProtoReflect.Descriptor instead. +func (*PollMessagesInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{8} +} + +func (x *PollMessagesInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *PollMessagesInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *PollMessagesInput) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +func (x *PollMessagesInput) GetFromOffset() int64 { + if x != nil { + return x.FromOffset + } + return 0 +} + +func (x *PollMessagesInput) GetMaxMessages() int32 { + if x != nil { + return x.MaxMessages + } + return 0 +} + +func (x *PollMessagesInput) GetTopics() []string { + if x != nil { + return x.Topics + } + return nil +} + +func (x *PollMessagesInput) GetWaitNewMessages() bool { + if x != nil { + return x.WaitNewMessages + } + return false +} + +type PollMessagesOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Messages []*StreamMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + NextOffset int64 `protobuf:"varint,2,opt,name=next_offset,json=nextOffset,proto3" json:"next_offset,omitempty"` + HeadOffset int64 `protobuf:"varint,3,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` + Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` + CloseReason *v1.Payload `protobuf:"bytes,5,opt,name=close_reason,json=closeReason,proto3" json:"close_reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollMessagesOutput) Reset() { + *x = PollMessagesOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollMessagesOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollMessagesOutput) ProtoMessage() {} + +func (x *PollMessagesOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[9] + 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 PollMessagesOutput.ProtoReflect.Descriptor instead. +func (*PollMessagesOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{9} +} + +func (x *PollMessagesOutput) GetMessages() []*StreamMessage { + if x != nil { + return x.Messages + } + return nil +} + +func (x *PollMessagesOutput) GetNextOffset() int64 { + if x != nil { + return x.NextOffset + } + return 0 +} + +func (x *PollMessagesOutput) GetHeadOffset() int64 { + if x != nil { + return x.HeadOffset + } + return 0 +} + +func (x *PollMessagesOutput) GetClosed() bool { + if x != nil { + return x.Closed + } + return false +} + +func (x *PollMessagesOutput) GetCloseReason() *v1.Payload { + if x != nil { + return x.CloseReason + } + return nil +} + +type DescribeStreamInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DescribeStreamInput) Reset() { + *x = DescribeStreamInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DescribeStreamInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescribeStreamInput) ProtoMessage() {} + +func (x *DescribeStreamInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[10] + 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 DescribeStreamInput.ProtoReflect.Descriptor instead. +func (*DescribeStreamInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{10} +} + +func (x *DescribeStreamInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *DescribeStreamInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +// A stream a workflow owns lives inside that workflow's execution, so it has +// no standalone id to address it by. It is named by its owner and its name +// instead, and routed on the owner. +type PollWorkflowMessagesInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + WorkflowId string `protobuf:"bytes,2,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"` + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + OwnerRunId string `protobuf:"bytes,8,opt,name=owner_run_id,json=ownerRunId,proto3" json:"owner_run_id,omitempty"` + // Empty means the workflow's default output stream. + StreamName string `protobuf:"bytes,3,opt,name=stream_name,json=streamName,proto3" json:"stream_name,omitempty"` + FromOffset int64 `protobuf:"varint,4,opt,name=from_offset,json=fromOffset,proto3" json:"from_offset,omitempty"` + MaxMessages int32 `protobuf:"varint,5,opt,name=max_messages,json=maxMessages,proto3" json:"max_messages,omitempty"` + // Filters as on PollMessagesInput. + Topics []string `protobuf:"bytes,6,rep,name=topics,proto3" json:"topics,omitempty"` + WaitNewMessages bool `protobuf:"varint,7,opt,name=wait_new_messages,json=waitNewMessages,proto3" json:"wait_new_messages,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollWorkflowMessagesInput) Reset() { + *x = PollWorkflowMessagesInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollWorkflowMessagesInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollWorkflowMessagesInput) ProtoMessage() {} + +func (x *PollWorkflowMessagesInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[11] + 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 PollWorkflowMessagesInput.ProtoReflect.Descriptor instead. +func (*PollWorkflowMessagesInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{11} +} + +func (x *PollWorkflowMessagesInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *PollWorkflowMessagesInput) GetWorkflowId() string { + if x != nil { + return x.WorkflowId + } + return "" +} + +func (x *PollWorkflowMessagesInput) GetOwnerRunId() string { + if x != nil { + return x.OwnerRunId + } + return "" +} + +func (x *PollWorkflowMessagesInput) GetStreamName() string { + if x != nil { + return x.StreamName + } + return "" +} + +func (x *PollWorkflowMessagesInput) GetFromOffset() int64 { + if x != nil { + return x.FromOffset + } + return 0 +} + +func (x *PollWorkflowMessagesInput) GetMaxMessages() int32 { + if x != nil { + return x.MaxMessages + } + return 0 +} + +func (x *PollWorkflowMessagesInput) GetTopics() []string { + if x != nil { + return x.Topics + } + return nil +} + +func (x *PollWorkflowMessagesInput) GetWaitNewMessages() bool { + if x != nil { + return x.WaitNewMessages + } + return false +} + +type DescribeWorkflowStreamInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + WorkflowId string `protobuf:"bytes,2,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"` + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + OwnerRunId string `protobuf:"bytes,4,opt,name=owner_run_id,json=ownerRunId,proto3" json:"owner_run_id,omitempty"` + StreamName string `protobuf:"bytes,3,opt,name=stream_name,json=streamName,proto3" json:"stream_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DescribeWorkflowStreamInput) Reset() { + *x = DescribeWorkflowStreamInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DescribeWorkflowStreamInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescribeWorkflowStreamInput) ProtoMessage() {} + +func (x *DescribeWorkflowStreamInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[12] + 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 DescribeWorkflowStreamInput.ProtoReflect.Descriptor instead. +func (*DescribeWorkflowStreamInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{12} +} + +func (x *DescribeWorkflowStreamInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *DescribeWorkflowStreamInput) GetWorkflowId() string { + if x != nil { + return x.WorkflowId + } + return "" +} + +func (x *DescribeWorkflowStreamInput) GetOwnerRunId() string { + if x != nil { + return x.OwnerRunId + } + return "" +} + +func (x *DescribeWorkflowStreamInput) GetStreamName() string { + if x != nil { + return x.StreamName + } + return "" +} + +// Appending to a stream a workflow owns, from outside that workflow. The +// workflow's own publishes ride its Workflow Task instead. +type AddWorkflowMessagesInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + WorkflowId string `protobuf:"bytes,2,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"` + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + OwnerRunId string `protobuf:"bytes,7,opt,name=owner_run_id,json=ownerRunId,proto3" json:"owner_run_id,omitempty"` + // Empty means the workflow's default output stream. + StreamName string `protobuf:"bytes,3,opt,name=stream_name,json=streamName,proto3" json:"stream_name,omitempty"` + Messages []*StreamMessage `protobuf:"bytes,4,rep,name=messages,proto3" json:"messages,omitempty"` + // Optional idempotency, as on AddMessagesInput. + ProducerId string `protobuf:"bytes,5,opt,name=producer_id,json=producerId,proto3" json:"producer_id,omitempty"` + Sequence int64 `protobuf:"varint,6,opt,name=sequence,proto3" json:"sequence,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddWorkflowMessagesInput) Reset() { + *x = AddWorkflowMessagesInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddWorkflowMessagesInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddWorkflowMessagesInput) ProtoMessage() {} + +func (x *AddWorkflowMessagesInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[13] + 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 AddWorkflowMessagesInput.ProtoReflect.Descriptor instead. +func (*AddWorkflowMessagesInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{13} +} + +func (x *AddWorkflowMessagesInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *AddWorkflowMessagesInput) GetWorkflowId() string { + if x != nil { + return x.WorkflowId + } + return "" +} + +func (x *AddWorkflowMessagesInput) GetOwnerRunId() string { + if x != nil { + return x.OwnerRunId + } + return "" +} + +func (x *AddWorkflowMessagesInput) GetStreamName() string { + if x != nil { + return x.StreamName + } + return "" +} + +func (x *AddWorkflowMessagesInput) GetMessages() []*StreamMessage { + if x != nil { + return x.Messages + } + return nil +} + +func (x *AddWorkflowMessagesInput) GetProducerId() string { + if x != nil { + return x.ProducerId + } + return "" +} + +func (x *AddWorkflowMessagesInput) GetSequence() int64 { + if x != nil { + return x.Sequence + } + return 0 +} + +type DescribeStreamOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + State *StreamState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DescribeStreamOutput) Reset() { + *x = DescribeStreamOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DescribeStreamOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescribeStreamOutput) ProtoMessage() {} + +func (x *DescribeStreamOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[14] + 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 DescribeStreamOutput.ProtoReflect.Descriptor instead. +func (*DescribeStreamOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{14} +} + +func (x *DescribeStreamOutput) GetState() *StreamState { + if x != nil { + return x.State + } + return nil +} + +type CloseStreamInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + Reason *v1.Payload `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CloseStreamInput) Reset() { + *x = CloseStreamInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CloseStreamInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseStreamInput) ProtoMessage() {} + +func (x *CloseStreamInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[15] + 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 CloseStreamInput.ProtoReflect.Descriptor instead. +func (*CloseStreamInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{15} +} + +func (x *CloseStreamInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *CloseStreamInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *CloseStreamInput) GetReason() *v1.Payload { + if x != nil { + return x.Reason + } + return nil +} + +type CloseStreamOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CloseStreamOutput) Reset() { + *x = CloseStreamOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CloseStreamOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseStreamOutput) ProtoMessage() {} + +func (x *CloseStreamOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[16] + 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 CloseStreamOutput.ProtoReflect.Descriptor instead. +func (*CloseStreamOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{16} +} + +type TruncateStreamInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + NewBaseOffset int64 `protobuf:"varint,3,opt,name=new_base_offset,json=newBaseOffset,proto3" json:"new_base_offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TruncateStreamInput) Reset() { + *x = TruncateStreamInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TruncateStreamInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TruncateStreamInput) ProtoMessage() {} + +func (x *TruncateStreamInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[17] + 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 TruncateStreamInput.ProtoReflect.Descriptor instead. +func (*TruncateStreamInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{17} +} + +func (x *TruncateStreamInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *TruncateStreamInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *TruncateStreamInput) GetNewBaseOffset() int64 { + if x != nil { + return x.NewBaseOffset + } + return 0 +} + +type TruncateStreamOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TruncateStreamOutput) Reset() { + *x = TruncateStreamOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TruncateStreamOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TruncateStreamOutput) ProtoMessage() {} + +func (x *TruncateStreamOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[18] + 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 TruncateStreamOutput.ProtoReflect.Descriptor instead. +func (*TruncateStreamOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{18} +} + +type DeleteStreamInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteStreamInput) Reset() { + *x = DeleteStreamInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteStreamInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteStreamInput) ProtoMessage() {} + +func (x *DeleteStreamInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[19] + 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 DeleteStreamInput.ProtoReflect.Descriptor instead. +func (*DeleteStreamInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{19} +} + +func (x *DeleteStreamInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *DeleteStreamInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +type DeleteStreamOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteStreamOutput) Reset() { + *x = DeleteStreamOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteStreamOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteStreamOutput) ProtoMessage() {} + +func (x *DeleteStreamOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[20] + 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 DeleteStreamOutput.ProtoReflect.Descriptor instead. +func (*DeleteStreamOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{20} +} + +type CreateStreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *CreateStreamInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateStreamRequest) Reset() { + *x = CreateStreamRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateStreamRequest) ProtoMessage() {} + +func (x *CreateStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[21] + 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 CreateStreamRequest.ProtoReflect.Descriptor instead. +func (*CreateStreamRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{21} +} + +func (x *CreateStreamRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *CreateStreamRequest) GetFrontendRequest() *CreateStreamInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type CreateStreamResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *CreateStreamOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateStreamResponse) Reset() { + *x = CreateStreamResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateStreamResponse) ProtoMessage() {} + +func (x *CreateStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[22] + 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 CreateStreamResponse.ProtoReflect.Descriptor instead. +func (*CreateStreamResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{22} +} + +func (x *CreateStreamResponse) GetFrontendResponse() *CreateStreamOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type AddMessagesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *AddMessagesInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddMessagesRequest) Reset() { + *x = AddMessagesRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddMessagesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddMessagesRequest) ProtoMessage() {} + +func (x *AddMessagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[23] + 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 AddMessagesRequest.ProtoReflect.Descriptor instead. +func (*AddMessagesRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{23} +} + +func (x *AddMessagesRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *AddMessagesRequest) GetFrontendRequest() *AddMessagesInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type AddMessagesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *AddMessagesOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddMessagesResponse) Reset() { + *x = AddMessagesResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddMessagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddMessagesResponse) ProtoMessage() {} + +func (x *AddMessagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[24] + 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 AddMessagesResponse.ProtoReflect.Descriptor instead. +func (*AddMessagesResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{24} +} + +func (x *AddMessagesResponse) GetFrontendResponse() *AddMessagesOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type FinishWritingRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *FinishWritingInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FinishWritingRequest) Reset() { + *x = FinishWritingRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FinishWritingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishWritingRequest) ProtoMessage() {} + +func (x *FinishWritingRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[25] + 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 FinishWritingRequest.ProtoReflect.Descriptor instead. +func (*FinishWritingRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{25} +} + +func (x *FinishWritingRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *FinishWritingRequest) GetFrontendRequest() *FinishWritingInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type FinishWritingResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *FinishWritingOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FinishWritingResponse) Reset() { + *x = FinishWritingResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FinishWritingResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishWritingResponse) ProtoMessage() {} + +func (x *FinishWritingResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[26] + 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 FinishWritingResponse.ProtoReflect.Descriptor instead. +func (*FinishWritingResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{26} +} + +func (x *FinishWritingResponse) GetFrontendResponse() *FinishWritingOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type SubscribeWorkflowRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *SubscribeWorkflowInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubscribeWorkflowRequest) Reset() { + *x = SubscribeWorkflowRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubscribeWorkflowRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeWorkflowRequest) ProtoMessage() {} + +func (x *SubscribeWorkflowRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[27] + 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 SubscribeWorkflowRequest.ProtoReflect.Descriptor instead. +func (*SubscribeWorkflowRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{27} +} + +func (x *SubscribeWorkflowRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *SubscribeWorkflowRequest) GetFrontendRequest() *SubscribeWorkflowInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type SubscribeWorkflowResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *SubscribeWorkflowOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubscribeWorkflowResponse) Reset() { + *x = SubscribeWorkflowResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubscribeWorkflowResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeWorkflowResponse) ProtoMessage() {} + +func (x *SubscribeWorkflowResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[28] + 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 SubscribeWorkflowResponse.ProtoReflect.Descriptor instead. +func (*SubscribeWorkflowResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{28} +} + +func (x *SubscribeWorkflowResponse) GetFrontendResponse() *SubscribeWorkflowOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type PollMessagesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *PollMessagesInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollMessagesRequest) Reset() { + *x = PollMessagesRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollMessagesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollMessagesRequest) ProtoMessage() {} + +func (x *PollMessagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[29] + 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 PollMessagesRequest.ProtoReflect.Descriptor instead. +func (*PollMessagesRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{29} +} + +func (x *PollMessagesRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *PollMessagesRequest) GetFrontendRequest() *PollMessagesInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type PollMessagesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *PollMessagesOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollMessagesResponse) Reset() { + *x = PollMessagesResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollMessagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollMessagesResponse) ProtoMessage() {} + +func (x *PollMessagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[30] + 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 PollMessagesResponse.ProtoReflect.Descriptor instead. +func (*PollMessagesResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{30} +} + +func (x *PollMessagesResponse) GetFrontendResponse() *PollMessagesOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type DescribeStreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *DescribeStreamInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DescribeStreamRequest) Reset() { + *x = DescribeStreamRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DescribeStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescribeStreamRequest) ProtoMessage() {} + +func (x *DescribeStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[31] + 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 DescribeStreamRequest.ProtoReflect.Descriptor instead. +func (*DescribeStreamRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{31} +} + +func (x *DescribeStreamRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *DescribeStreamRequest) GetFrontendRequest() *DescribeStreamInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type DescribeStreamResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *DescribeStreamOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DescribeStreamResponse) Reset() { + *x = DescribeStreamResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DescribeStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescribeStreamResponse) ProtoMessage() {} + +func (x *DescribeStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[32] + 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 DescribeStreamResponse.ProtoReflect.Descriptor instead. +func (*DescribeStreamResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{32} +} + +func (x *DescribeStreamResponse) GetFrontendResponse() *DescribeStreamOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type PollWorkflowMessagesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *PollWorkflowMessagesInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollWorkflowMessagesRequest) Reset() { + *x = PollWorkflowMessagesRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollWorkflowMessagesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollWorkflowMessagesRequest) ProtoMessage() {} + +func (x *PollWorkflowMessagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[33] + 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 PollWorkflowMessagesRequest.ProtoReflect.Descriptor instead. +func (*PollWorkflowMessagesRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{33} +} + +func (x *PollWorkflowMessagesRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *PollWorkflowMessagesRequest) GetFrontendRequest() *PollWorkflowMessagesInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type PollWorkflowMessagesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *PollMessagesOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollWorkflowMessagesResponse) Reset() { + *x = PollWorkflowMessagesResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollWorkflowMessagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollWorkflowMessagesResponse) ProtoMessage() {} + +func (x *PollWorkflowMessagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[34] + 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 PollWorkflowMessagesResponse.ProtoReflect.Descriptor instead. +func (*PollWorkflowMessagesResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{34} +} + +func (x *PollWorkflowMessagesResponse) GetFrontendResponse() *PollMessagesOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type DescribeWorkflowStreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *DescribeWorkflowStreamInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DescribeWorkflowStreamRequest) Reset() { + *x = DescribeWorkflowStreamRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DescribeWorkflowStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescribeWorkflowStreamRequest) ProtoMessage() {} + +func (x *DescribeWorkflowStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[35] + 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 DescribeWorkflowStreamRequest.ProtoReflect.Descriptor instead. +func (*DescribeWorkflowStreamRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{35} +} + +func (x *DescribeWorkflowStreamRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *DescribeWorkflowStreamRequest) GetFrontendRequest() *DescribeWorkflowStreamInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type DescribeWorkflowStreamResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *DescribeStreamOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DescribeWorkflowStreamResponse) Reset() { + *x = DescribeWorkflowStreamResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DescribeWorkflowStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescribeWorkflowStreamResponse) ProtoMessage() {} + +func (x *DescribeWorkflowStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[36] + 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 DescribeWorkflowStreamResponse.ProtoReflect.Descriptor instead. +func (*DescribeWorkflowStreamResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{36} +} + +func (x *DescribeWorkflowStreamResponse) GetFrontendResponse() *DescribeStreamOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +// Registering a consumer on a stream in another execution. Split out from +// SubscribeWorkflow because the two halves live on different shards: the pin +// goes on the stream, the cursor goes on the consuming workflow, and a handler +// can only reach the shard it was routed to. +type RegisterStreamConsumerInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + // The workflow that will consume, which names the pin. + ConsumerWorkflowId string `protobuf:"bytes,3,opt,name=consumer_workflow_id,json=consumerWorkflowId,proto3" json:"consumer_workflow_id,omitempty"` + // Negative means from wherever the stream is when the pin is taken. Resolved + // here, where the frontier is, and returned so the cursor records a fact. + StartOffset int64 `protobuf:"varint,4,opt,name=start_offset,json=startOffset,proto3" json:"start_offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterStreamConsumerInput) Reset() { + *x = RegisterStreamConsumerInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterStreamConsumerInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterStreamConsumerInput) ProtoMessage() {} + +func (x *RegisterStreamConsumerInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[37] + 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 RegisterStreamConsumerInput.ProtoReflect.Descriptor instead. +func (*RegisterStreamConsumerInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{37} +} + +func (x *RegisterStreamConsumerInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *RegisterStreamConsumerInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *RegisterStreamConsumerInput) GetConsumerWorkflowId() string { + if x != nil { + return x.ConsumerWorkflowId + } + return "" +} + +func (x *RegisterStreamConsumerInput) GetStartOffset() int64 { + if x != nil { + return x.StartOffset + } + return 0 +} + +type RegisterStreamConsumerOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + StartOffset int64 `protobuf:"varint,1,opt,name=start_offset,json=startOffset,proto3" json:"start_offset,omitempty"` + // What the consumer needs to address the log, all decided by the stream. + CollectionId string `protobuf:"bytes,2,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"` + BucketSize int64 `protobuf:"varint,3,opt,name=bucket_size,json=bucketSize,proto3" json:"bucket_size,omitempty"` + KnownHead int64 `protobuf:"varint,4,opt,name=known_head,json=knownHead,proto3" json:"known_head,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterStreamConsumerOutput) Reset() { + *x = RegisterStreamConsumerOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterStreamConsumerOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterStreamConsumerOutput) ProtoMessage() {} + +func (x *RegisterStreamConsumerOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[38] + 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 RegisterStreamConsumerOutput.ProtoReflect.Descriptor instead. +func (*RegisterStreamConsumerOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{38} +} + +func (x *RegisterStreamConsumerOutput) GetStartOffset() int64 { + if x != nil { + return x.StartOffset + } + return 0 +} + +func (x *RegisterStreamConsumerOutput) GetCollectionId() string { + if x != nil { + return x.CollectionId + } + return "" +} + +func (x *RegisterStreamConsumerOutput) GetBucketSize() int64 { + if x != nil { + return x.BucketSize + } + return 0 +} + +func (x *RegisterStreamConsumerOutput) GetKnownHead() int64 { + if x != nil { + return x.KnownHead + } + return 0 +} + +// Telling one consumer that the frontier moved. Routed to the consumer, which +// is not where the stream lives. +type AdvanceConsumerHeadInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + WorkflowId string `protobuf:"bytes,2,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"` + // The run that subscribed. A successor from continue-as-new holds no cursor + // for this stream, so pushing the frontier at it would land nowhere. + OwnerRunId string `protobuf:"bytes,5,opt,name=owner_run_id,json=ownerRunId,proto3" json:"owner_run_id,omitempty"` + StreamId string `protobuf:"bytes,3,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + HeadOffset int64 `protobuf:"varint,4,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdvanceConsumerHeadInput) Reset() { + *x = AdvanceConsumerHeadInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdvanceConsumerHeadInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdvanceConsumerHeadInput) ProtoMessage() {} + +func (x *AdvanceConsumerHeadInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[39] + 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 AdvanceConsumerHeadInput.ProtoReflect.Descriptor instead. +func (*AdvanceConsumerHeadInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{39} +} + +func (x *AdvanceConsumerHeadInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *AdvanceConsumerHeadInput) GetWorkflowId() string { + if x != nil { + return x.WorkflowId + } + return "" +} + +func (x *AdvanceConsumerHeadInput) GetOwnerRunId() string { + if x != nil { + return x.OwnerRunId + } + return "" +} + +func (x *AdvanceConsumerHeadInput) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *AdvanceConsumerHeadInput) GetHeadOffset() int64 { + if x != nil { + return x.HeadOffset + } + return 0 +} + +type AdvanceConsumerHeadOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdvanceConsumerHeadOutput) Reset() { + *x = AdvanceConsumerHeadOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdvanceConsumerHeadOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdvanceConsumerHeadOutput) ProtoMessage() {} + +func (x *AdvanceConsumerHeadOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[40] + 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 AdvanceConsumerHeadOutput.ProtoReflect.Descriptor instead. +func (*AdvanceConsumerHeadOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{40} +} + +type AddWorkflowMessagesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *AddWorkflowMessagesInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddWorkflowMessagesRequest) Reset() { + *x = AddWorkflowMessagesRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddWorkflowMessagesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddWorkflowMessagesRequest) ProtoMessage() {} + +func (x *AddWorkflowMessagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[41] + 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 AddWorkflowMessagesRequest.ProtoReflect.Descriptor instead. +func (*AddWorkflowMessagesRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{41} +} + +func (x *AddWorkflowMessagesRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *AddWorkflowMessagesRequest) GetFrontendRequest() *AddWorkflowMessagesInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type AddWorkflowMessagesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *AddMessagesOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddWorkflowMessagesResponse) Reset() { + *x = AddWorkflowMessagesResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddWorkflowMessagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddWorkflowMessagesResponse) ProtoMessage() {} + +func (x *AddWorkflowMessagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[42] + 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 AddWorkflowMessagesResponse.ProtoReflect.Descriptor instead. +func (*AddWorkflowMessagesResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{42} +} + +func (x *AddWorkflowMessagesResponse) GetFrontendResponse() *AddMessagesOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type RegisterStreamConsumerRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *RegisterStreamConsumerInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterStreamConsumerRequest) Reset() { + *x = RegisterStreamConsumerRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterStreamConsumerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterStreamConsumerRequest) ProtoMessage() {} + +func (x *RegisterStreamConsumerRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[43] + 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 RegisterStreamConsumerRequest.ProtoReflect.Descriptor instead. +func (*RegisterStreamConsumerRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{43} +} + +func (x *RegisterStreamConsumerRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *RegisterStreamConsumerRequest) GetFrontendRequest() *RegisterStreamConsumerInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type RegisterStreamConsumerResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *RegisterStreamConsumerOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterStreamConsumerResponse) Reset() { + *x = RegisterStreamConsumerResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterStreamConsumerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterStreamConsumerResponse) ProtoMessage() {} + +func (x *RegisterStreamConsumerResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[44] + 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 RegisterStreamConsumerResponse.ProtoReflect.Descriptor instead. +func (*RegisterStreamConsumerResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{44} +} + +func (x *RegisterStreamConsumerResponse) GetFrontendResponse() *RegisterStreamConsumerOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type AdvanceConsumerHeadRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *AdvanceConsumerHeadInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdvanceConsumerHeadRequest) Reset() { + *x = AdvanceConsumerHeadRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdvanceConsumerHeadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdvanceConsumerHeadRequest) ProtoMessage() {} + +func (x *AdvanceConsumerHeadRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[45] + 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 AdvanceConsumerHeadRequest.ProtoReflect.Descriptor instead. +func (*AdvanceConsumerHeadRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{45} +} + +func (x *AdvanceConsumerHeadRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *AdvanceConsumerHeadRequest) GetFrontendRequest() *AdvanceConsumerHeadInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type AdvanceConsumerHeadResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *AdvanceConsumerHeadOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdvanceConsumerHeadResponse) Reset() { + *x = AdvanceConsumerHeadResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdvanceConsumerHeadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdvanceConsumerHeadResponse) ProtoMessage() {} + +func (x *AdvanceConsumerHeadResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[46] + 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 AdvanceConsumerHeadResponse.ProtoReflect.Descriptor instead. +func (*AdvanceConsumerHeadResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{46} +} + +func (x *AdvanceConsumerHeadResponse) GetFrontendResponse() *AdvanceConsumerHeadOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type CloseStreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *CloseStreamInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CloseStreamRequest) Reset() { + *x = CloseStreamRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CloseStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseStreamRequest) ProtoMessage() {} + +func (x *CloseStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[47] + 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 CloseStreamRequest.ProtoReflect.Descriptor instead. +func (*CloseStreamRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{47} +} + +func (x *CloseStreamRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *CloseStreamRequest) GetFrontendRequest() *CloseStreamInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type CloseStreamResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *CloseStreamOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CloseStreamResponse) Reset() { + *x = CloseStreamResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CloseStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseStreamResponse) ProtoMessage() {} + +func (x *CloseStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[48] + 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 CloseStreamResponse.ProtoReflect.Descriptor instead. +func (*CloseStreamResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{48} +} + +func (x *CloseStreamResponse) GetFrontendResponse() *CloseStreamOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type TruncateStreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *TruncateStreamInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TruncateStreamRequest) Reset() { + *x = TruncateStreamRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TruncateStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TruncateStreamRequest) ProtoMessage() {} + +func (x *TruncateStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[49] + 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 TruncateStreamRequest.ProtoReflect.Descriptor instead. +func (*TruncateStreamRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{49} +} + +func (x *TruncateStreamRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *TruncateStreamRequest) GetFrontendRequest() *TruncateStreamInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type TruncateStreamResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *TruncateStreamOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TruncateStreamResponse) Reset() { + *x = TruncateStreamResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TruncateStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TruncateStreamResponse) ProtoMessage() {} + +func (x *TruncateStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[50] + 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 TruncateStreamResponse.ProtoReflect.Descriptor instead. +func (*TruncateStreamResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{50} +} + +func (x *TruncateStreamResponse) GetFrontendResponse() *TruncateStreamOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type ListStreamsInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + NextPageToken []byte `protobuf:"bytes,3,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + Query string `protobuf:"bytes,4,opt,name=query,proto3" json:"query,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListStreamsInput) Reset() { + *x = ListStreamsInput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListStreamsInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStreamsInput) ProtoMessage() {} + +func (x *ListStreamsInput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[51] + 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 ListStreamsInput.ProtoReflect.Descriptor instead. +func (*ListStreamsInput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{51} +} + +func (x *ListStreamsInput) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *ListStreamsInput) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListStreamsInput) GetNextPageToken() []byte { + if x != nil { + return x.NextPageToken + } + return nil +} + +func (x *ListStreamsInput) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +type StreamListEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + StreamId string `protobuf:"bytes,1,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + RunId string `protobuf:"bytes,2,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamListEntry) Reset() { + *x = StreamListEntry{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamListEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamListEntry) ProtoMessage() {} + +func (x *StreamListEntry) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[52] + 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 StreamListEntry.ProtoReflect.Descriptor instead. +func (*StreamListEntry) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{52} +} + +func (x *StreamListEntry) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *StreamListEntry) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +type ListStreamsOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Streams []*StreamListEntry `protobuf:"bytes,1,rep,name=streams,proto3" json:"streams,omitempty"` + NextPageToken []byte `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListStreamsOutput) Reset() { + *x = ListStreamsOutput{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListStreamsOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStreamsOutput) ProtoMessage() {} + +func (x *ListStreamsOutput) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[53] + 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 ListStreamsOutput.ProtoReflect.Descriptor instead. +func (*ListStreamsOutput) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{53} +} + +func (x *ListStreamsOutput) GetStreams() []*StreamListEntry { + if x != nil { + return x.Streams + } + return nil +} + +func (x *ListStreamsOutput) GetNextPageToken() []byte { + if x != nil { + return x.NextPageToken + } + return nil +} + +type ListStreamsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *ListStreamsInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListStreamsRequest) Reset() { + *x = ListStreamsRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListStreamsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStreamsRequest) ProtoMessage() {} + +func (x *ListStreamsRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[54] + 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 ListStreamsRequest.ProtoReflect.Descriptor instead. +func (*ListStreamsRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{54} +} + +func (x *ListStreamsRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *ListStreamsRequest) GetFrontendRequest() *ListStreamsInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type ListStreamsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *ListStreamsOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListStreamsResponse) Reset() { + *x = ListStreamsResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListStreamsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStreamsResponse) ProtoMessage() {} + +func (x *ListStreamsResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[55] + 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 ListStreamsResponse.ProtoReflect.Descriptor instead. +func (*ListStreamsResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{55} +} + +func (x *ListStreamsResponse) GetFrontendResponse() *ListStreamsOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +type DeleteStreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + FrontendRequest *DeleteStreamInput `protobuf:"bytes,2,opt,name=frontend_request,json=frontendRequest,proto3" json:"frontend_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteStreamRequest) Reset() { + *x = DeleteStreamRequest{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteStreamRequest) ProtoMessage() {} + +func (x *DeleteStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[56] + 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 DeleteStreamRequest.ProtoReflect.Descriptor instead. +func (*DeleteStreamRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{56} +} + +func (x *DeleteStreamRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *DeleteStreamRequest) GetFrontendRequest() *DeleteStreamInput { + if x != nil { + return x.FrontendRequest + } + return nil +} + +type DeleteStreamResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + FrontendResponse *DeleteStreamOutput `protobuf:"bytes,1,opt,name=frontend_response,json=frontendResponse,proto3" json:"frontend_response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteStreamResponse) Reset() { + *x = DeleteStreamResponse{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteStreamResponse) ProtoMessage() {} + +func (x *DeleteStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes[57] + 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 DeleteStreamResponse.ProtoReflect.Descriptor instead. +func (*DeleteStreamResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{57} +} + +func (x *DeleteStreamResponse) GetFrontendResponse() *DeleteStreamOutput { + if x != nil { + return x.FrontendResponse + } + return nil +} + +var File_temporal_server_chasm_lib_stream_proto_v1_request_response_proto protoreflect.FileDescriptor + +const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDesc = "" + + "\n" + + "@temporal/server/chasm/lib/stream/proto/v1/request_response.proto\x12)temporal.server.chasm.lib.stream.proto.v1\x1a7temporal/server/chasm/lib/stream/proto/v1/message.proto\x1a.temporal.server.chasm.lib.stream.proto.v1.FinishWritingOutputR\x10frontendResponse\"\xab\x01\n" + + "\x18SubscribeWorkflowRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12l\n" + + "\x10frontend_request\x18\x02 \x01(\v2A.temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowInputR\x0ffrontendRequest\"\x8c\x01\n" + + "\x19SubscribeWorkflowResponse\x12o\n" + + "\x11frontend_response\x18\x01 \x01(\v2B.temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowOutputR\x10frontendResponse\"\xa1\x01\n" + + "\x13PollMessagesRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12g\n" + + "\x10frontend_request\x18\x02 \x01(\v2<.temporal.server.chasm.lib.stream.proto.v1.PollMessagesInputR\x0ffrontendRequest\"\x82\x01\n" + + "\x14PollMessagesResponse\x12j\n" + + "\x11frontend_response\x18\x01 \x01(\v2=.temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutputR\x10frontendResponse\"\xa5\x01\n" + + "\x15DescribeStreamRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12i\n" + + "\x10frontend_request\x18\x02 \x01(\v2>.temporal.server.chasm.lib.stream.proto.v1.DescribeStreamInputR\x0ffrontendRequest\"\x86\x01\n" + + "\x16DescribeStreamResponse\x12l\n" + + "\x11frontend_response\x18\x01 \x01(\v2?.temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutputR\x10frontendResponse\"\xb1\x01\n" + + "\x1bPollWorkflowMessagesRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12o\n" + + "\x10frontend_request\x18\x02 \x01(\v2D.temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesInputR\x0ffrontendRequest\"\x8a\x01\n" + + "\x1cPollWorkflowMessagesResponse\x12j\n" + + "\x11frontend_response\x18\x01 \x01(\v2=.temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutputR\x10frontendResponse\"\xb5\x01\n" + + "\x1dDescribeWorkflowStreamRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12q\n" + + "\x10frontend_request\x18\x02 \x01(\v2F.temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamInputR\x0ffrontendRequest\"\x8e\x01\n" + + "\x1eDescribeWorkflowStreamResponse\x12l\n" + + "\x11frontend_response\x18\x01 \x01(\v2?.temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutputR\x10frontendResponse\"\xad\x01\n" + + "\x1bRegisterStreamConsumerInput\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + + "\tstream_id\x18\x02 \x01(\tR\bstreamId\x120\n" + + "\x14consumer_workflow_id\x18\x03 \x01(\tR\x12consumerWorkflowId\x12!\n" + + "\fstart_offset\x18\x04 \x01(\x03R\vstartOffset\"\xa6\x01\n" + + "\x1cRegisterStreamConsumerOutput\x12!\n" + + "\fstart_offset\x18\x01 \x01(\x03R\vstartOffset\x12#\n" + + "\rcollection_id\x18\x02 \x01(\tR\fcollectionId\x12\x1f\n" + + "\vbucket_size\x18\x03 \x01(\x03R\n" + + "bucketSize\x12\x1d\n" + + "\n" + + "known_head\x18\x04 \x01(\x03R\tknownHead\"\xb9\x01\n" + + "\x18AdvanceConsumerHeadInput\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1f\n" + + "\vworkflow_id\x18\x02 \x01(\tR\n" + + "workflowId\x12 \n" + + "\fowner_run_id\x18\x05 \x01(\tR\n" + + "ownerRunId\x12\x1b\n" + + "\tstream_id\x18\x03 \x01(\tR\bstreamId\x12\x1f\n" + + "\vhead_offset\x18\x04 \x01(\x03R\n" + + "headOffset\"\x1b\n" + + "\x19AdvanceConsumerHeadOutput\"\xaf\x01\n" + + "\x1aAddWorkflowMessagesRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12n\n" + + "\x10frontend_request\x18\x02 \x01(\v2C.temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesInputR\x0ffrontendRequest\"\x88\x01\n" + + "\x1bAddWorkflowMessagesResponse\x12i\n" + + "\x11frontend_response\x18\x01 \x01(\v2<.temporal.server.chasm.lib.stream.proto.v1.AddMessagesOutputR\x10frontendResponse\"\xb5\x01\n" + + "\x1dRegisterStreamConsumerRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12q\n" + + "\x10frontend_request\x18\x02 \x01(\v2F.temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerInputR\x0ffrontendRequest\"\x96\x01\n" + + "\x1eRegisterStreamConsumerResponse\x12t\n" + + "\x11frontend_response\x18\x01 \x01(\v2G.temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerOutputR\x10frontendResponse\"\xaf\x01\n" + + "\x1aAdvanceConsumerHeadRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12n\n" + + "\x10frontend_request\x18\x02 \x01(\v2C.temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadInputR\x0ffrontendRequest\"\x90\x01\n" + + "\x1bAdvanceConsumerHeadResponse\x12q\n" + + "\x11frontend_response\x18\x01 \x01(\v2D.temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadOutputR\x10frontendResponse\"\x9f\x01\n" + + "\x12CloseStreamRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12f\n" + + "\x10frontend_request\x18\x02 \x01(\v2;.temporal.server.chasm.lib.stream.proto.v1.CloseStreamInputR\x0ffrontendRequest\"\x80\x01\n" + + "\x13CloseStreamResponse\x12i\n" + + "\x11frontend_response\x18\x01 \x01(\v2<.temporal.server.chasm.lib.stream.proto.v1.CloseStreamOutputR\x10frontendResponse\"\xa5\x01\n" + + "\x15TruncateStreamRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12i\n" + + "\x10frontend_request\x18\x02 \x01(\v2>.temporal.server.chasm.lib.stream.proto.v1.TruncateStreamInputR\x0ffrontendRequest\"\x86\x01\n" + + "\x16TruncateStreamResponse\x12l\n" + + "\x11frontend_response\x18\x01 \x01(\v2?.temporal.server.chasm.lib.stream.proto.v1.TruncateStreamOutputR\x10frontendResponse\"\x8b\x01\n" + + "\x10ListStreamsInput\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + + "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12&\n" + + "\x0fnext_page_token\x18\x03 \x01(\fR\rnextPageToken\x12\x14\n" + + "\x05query\x18\x04 \x01(\tR\x05query\"E\n" + + "\x0fStreamListEntry\x12\x1b\n" + + "\tstream_id\x18\x01 \x01(\tR\bstreamId\x12\x15\n" + + "\x06run_id\x18\x02 \x01(\tR\x05runId\"\x91\x01\n" + + "\x11ListStreamsOutput\x12T\n" + + "\astreams\x18\x01 \x03(\v2:.temporal.server.chasm.lib.stream.proto.v1.StreamListEntryR\astreams\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\fR\rnextPageToken\"\x9f\x01\n" + + "\x12ListStreamsRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12f\n" + + "\x10frontend_request\x18\x02 \x01(\v2;.temporal.server.chasm.lib.stream.proto.v1.ListStreamsInputR\x0ffrontendRequest\"\x80\x01\n" + + "\x13ListStreamsResponse\x12i\n" + + "\x11frontend_response\x18\x01 \x01(\v2<.temporal.server.chasm.lib.stream.proto.v1.ListStreamsOutputR\x10frontendResponse\"\xa1\x01\n" + + "\x13DeleteStreamRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12g\n" + + "\x10frontend_request\x18\x02 \x01(\v2<.temporal.server.chasm.lib.stream.proto.v1.DeleteStreamInputR\x0ffrontendRequest\"\x82\x01\n" + + "\x14DeleteStreamResponse\x12j\n" + + "\x11frontend_response\x18\x01 \x01(\v2=.temporal.server.chasm.lib.stream.proto.v1.DeleteStreamOutputR\x10frontendResponseB>Z temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle + 59, // 1: temporal.server.chasm.lib.stream.proto.v1.AddMessagesInput.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage + 59, // 2: temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutput.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage + 60, // 3: temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutput.close_reason:type_name -> temporal.api.common.v1.Payload + 59, // 4: temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesInput.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage + 61, // 5: temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutput.state:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState + 60, // 6: temporal.server.chasm.lib.stream.proto.v1.CloseStreamInput.reason:type_name -> temporal.api.common.v1.Payload + 0, // 7: temporal.server.chasm.lib.stream.proto.v1.CreateStreamRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.CreateStreamInput + 1, // 8: temporal.server.chasm.lib.stream.proto.v1.CreateStreamResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.CreateStreamOutput + 2, // 9: temporal.server.chasm.lib.stream.proto.v1.AddMessagesRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.AddMessagesInput + 3, // 10: temporal.server.chasm.lib.stream.proto.v1.AddMessagesResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.AddMessagesOutput + 4, // 11: temporal.server.chasm.lib.stream.proto.v1.FinishWritingRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.FinishWritingInput + 5, // 12: temporal.server.chasm.lib.stream.proto.v1.FinishWritingResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.FinishWritingOutput + 6, // 13: temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowInput + 7, // 14: temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowOutput + 8, // 15: temporal.server.chasm.lib.stream.proto.v1.PollMessagesRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.PollMessagesInput + 9, // 16: temporal.server.chasm.lib.stream.proto.v1.PollMessagesResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutput + 10, // 17: temporal.server.chasm.lib.stream.proto.v1.DescribeStreamRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.DescribeStreamInput + 14, // 18: temporal.server.chasm.lib.stream.proto.v1.DescribeStreamResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutput + 11, // 19: temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesInput + 9, // 20: temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutput + 12, // 21: temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamInput + 14, // 22: temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutput + 13, // 23: temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesInput + 3, // 24: temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.AddMessagesOutput + 37, // 25: temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerInput + 38, // 26: temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerOutput + 39, // 27: temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadInput + 40, // 28: temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadOutput + 15, // 29: temporal.server.chasm.lib.stream.proto.v1.CloseStreamRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.CloseStreamInput + 16, // 30: temporal.server.chasm.lib.stream.proto.v1.CloseStreamResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.CloseStreamOutput + 17, // 31: temporal.server.chasm.lib.stream.proto.v1.TruncateStreamRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.TruncateStreamInput + 18, // 32: temporal.server.chasm.lib.stream.proto.v1.TruncateStreamResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.TruncateStreamOutput + 52, // 33: temporal.server.chasm.lib.stream.proto.v1.ListStreamsOutput.streams:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamListEntry + 51, // 34: temporal.server.chasm.lib.stream.proto.v1.ListStreamsRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.ListStreamsInput + 53, // 35: temporal.server.chasm.lib.stream.proto.v1.ListStreamsResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.ListStreamsOutput + 19, // 36: temporal.server.chasm.lib.stream.proto.v1.DeleteStreamRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.DeleteStreamInput + 20, // 37: temporal.server.chasm.lib.stream.proto.v1.DeleteStreamResponse.frontend_response:type_name -> temporal.server.chasm.lib.stream.proto.v1.DeleteStreamOutput + 38, // [38:38] is the sub-list for method output_type + 38, // [38:38] is the sub-list for method input_type + 38, // [38:38] is the sub-list for extension type_name + 38, // [38:38] is the sub-list for extension extendee + 0, // [0:38] is the sub-list for field type_name +} + +func init() { file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_init() } +func file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_init() { + if File_temporal_server_chasm_lib_stream_proto_v1_request_response_proto != nil { + return + } + file_temporal_server_chasm_lib_stream_proto_v1_message_proto_init() + file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDesc), len(file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDesc)), + NumEnums: 0, + NumMessages: 58, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_goTypes, + DependencyIndexes: file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_depIdxs, + MessageInfos: file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_msgTypes, + }.Build() + File_temporal_server_chasm_lib_stream_proto_v1_request_response_proto = out.File + file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_goTypes = nil + file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_depIdxs = nil +} diff --git a/chasm/lib/stream/gen/streampb/v1/service.pb.go b/chasm/lib/stream/gen/streampb/v1/service.pb.go new file mode 100644 index 00000000000..e0bb7981366 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/service.pb.go @@ -0,0 +1,140 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// plugins: +// protoc-gen-go +// protoc +// source: temporal/server/chasm/lib/stream/proto/v1/service.proto + +package streampb + +import ( + reflect "reflect" + unsafe "unsafe" + + _ "go.temporal.io/server/api/common/v1" + _ "go.temporal.io/server/api/routing/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +var File_temporal_server_chasm_lib_stream_proto_v1_service_proto protoreflect.FileDescriptor + +const file_temporal_server_chasm_lib_stream_proto_v1_service_proto_rawDesc = "" + + "\n" + + "7temporal/server/chasm/lib/stream/proto/v1/service.proto\x12)temporal.server.chasm.lib.stream.proto.v1\x1a@temporal/server/chasm/lib/stream/proto/v1/request_response.proto\x1a0temporal/server/api/common/v1/api_category.proto\x1a.temporal/server/api/routing/v1/extension.proto2\xf8\x16\n" + + "\rStreamService\x12\xb7\x01\n" + + "\fCreateStream\x12>.temporal.server.chasm.lib.stream.proto.v1.CreateStreamRequest\x1a?.temporal.server.chasm.lib.stream.proto.v1.CreateStreamResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\xb4\x01\n" + + "\vAddMessages\x12=.temporal.server.chasm.lib.stream.proto.v1.AddMessagesRequest\x1a>.temporal.server.chasm.lib.stream.proto.v1.AddMessagesResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\xba\x01\n" + + "\rFinishWriting\x12?.temporal.server.chasm.lib.stream.proto.v1.FinishWritingRequest\x1a@.temporal.server.chasm.lib.stream.proto.v1.FinishWritingResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\xc8\x01\n" + + "\x11SubscribeWorkflow\x12C.temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowRequest\x1aD.temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowResponse\"(\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1e\x1a\x1cfrontend_request.workflow_id\x12\xb7\x01\n" + + "\fPollMessages\x12>.temporal.server.chasm.lib.stream.proto.v1.PollMessagesRequest\x1a?.temporal.server.chasm.lib.stream.proto.v1.PollMessagesResponse\"&\x8a\xb5\x18\x02\b\x02\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\xbd\x01\n" + + "\x0eDescribeStream\x12@.temporal.server.chasm.lib.stream.proto.v1.DescribeStreamRequest\x1aA.temporal.server.chasm.lib.stream.proto.v1.DescribeStreamResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\xd1\x01\n" + + "\x14PollWorkflowMessages\x12F.temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesRequest\x1aG.temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesResponse\"(\x8a\xb5\x18\x02\b\x02\xd2\xc3\x18\x1e\x1a\x1cfrontend_request.workflow_id\x12\xd7\x01\n" + + "\x16DescribeWorkflowStream\x12H.temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamRequest\x1aI.temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamResponse\"(\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1e\x1a\x1cfrontend_request.workflow_id\x12\xce\x01\n" + + "\x13AddWorkflowMessages\x12E.temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesRequest\x1aF.temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesResponse\"(\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1e\x1a\x1cfrontend_request.workflow_id\x12\xd5\x01\n" + + "\x16RegisterStreamConsumer\x12H.temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerRequest\x1aI.temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\xce\x01\n" + + "\x13AdvanceConsumerHead\x12E.temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadRequest\x1aF.temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadResponse\"(\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1e\x1a\x1cfrontend_request.workflow_id\x12\xb4\x01\n" + + "\vCloseStream\x12=.temporal.server.chasm.lib.stream.proto.v1.CloseStreamRequest\x1a>.temporal.server.chasm.lib.stream.proto.v1.CloseStreamResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\xbd\x01\n" + + "\x0eTruncateStream\x12@.temporal.server.chasm.lib.stream.proto.v1.TruncateStreamRequest\x1aA.temporal.server.chasm.lib.stream.proto.v1.TruncateStreamResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_id\x12\x9a\x01\n" + + "\vListStreams\x12=.temporal.server.chasm.lib.stream.proto.v1.ListStreamsRequest\x1a>.temporal.server.chasm.lib.stream.proto.v1.ListStreamsResponse\"\f\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x02\b\x01\x12\xb7\x01\n" + + "\fDeleteStream\x12>.temporal.server.chasm.lib.stream.proto.v1.DeleteStreamRequest\x1a?.temporal.server.chasm.lib.stream.proto.v1.DeleteStreamResponse\"&\x8a\xb5\x18\x02\b\x01\xd2\xc3\x18\x1c\x1a\x1afrontend_request.stream_idB>Z temporal.server.chasm.lib.stream.proto.v1.CreateStreamRequest + 1, // 1: temporal.server.chasm.lib.stream.proto.v1.StreamService.AddMessages:input_type -> temporal.server.chasm.lib.stream.proto.v1.AddMessagesRequest + 2, // 2: temporal.server.chasm.lib.stream.proto.v1.StreamService.FinishWriting:input_type -> temporal.server.chasm.lib.stream.proto.v1.FinishWritingRequest + 3, // 3: temporal.server.chasm.lib.stream.proto.v1.StreamService.SubscribeWorkflow:input_type -> temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowRequest + 4, // 4: temporal.server.chasm.lib.stream.proto.v1.StreamService.PollMessages:input_type -> temporal.server.chasm.lib.stream.proto.v1.PollMessagesRequest + 5, // 5: temporal.server.chasm.lib.stream.proto.v1.StreamService.DescribeStream:input_type -> temporal.server.chasm.lib.stream.proto.v1.DescribeStreamRequest + 6, // 6: temporal.server.chasm.lib.stream.proto.v1.StreamService.PollWorkflowMessages:input_type -> temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesRequest + 7, // 7: temporal.server.chasm.lib.stream.proto.v1.StreamService.DescribeWorkflowStream:input_type -> temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamRequest + 8, // 8: temporal.server.chasm.lib.stream.proto.v1.StreamService.AddWorkflowMessages:input_type -> temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesRequest + 9, // 9: temporal.server.chasm.lib.stream.proto.v1.StreamService.RegisterStreamConsumer:input_type -> temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerRequest + 10, // 10: temporal.server.chasm.lib.stream.proto.v1.StreamService.AdvanceConsumerHead:input_type -> temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadRequest + 11, // 11: temporal.server.chasm.lib.stream.proto.v1.StreamService.CloseStream:input_type -> temporal.server.chasm.lib.stream.proto.v1.CloseStreamRequest + 12, // 12: temporal.server.chasm.lib.stream.proto.v1.StreamService.TruncateStream:input_type -> temporal.server.chasm.lib.stream.proto.v1.TruncateStreamRequest + 13, // 13: temporal.server.chasm.lib.stream.proto.v1.StreamService.ListStreams:input_type -> temporal.server.chasm.lib.stream.proto.v1.ListStreamsRequest + 14, // 14: temporal.server.chasm.lib.stream.proto.v1.StreamService.DeleteStream:input_type -> temporal.server.chasm.lib.stream.proto.v1.DeleteStreamRequest + 15, // 15: temporal.server.chasm.lib.stream.proto.v1.StreamService.CreateStream:output_type -> temporal.server.chasm.lib.stream.proto.v1.CreateStreamResponse + 16, // 16: temporal.server.chasm.lib.stream.proto.v1.StreamService.AddMessages:output_type -> temporal.server.chasm.lib.stream.proto.v1.AddMessagesResponse + 17, // 17: temporal.server.chasm.lib.stream.proto.v1.StreamService.FinishWriting:output_type -> temporal.server.chasm.lib.stream.proto.v1.FinishWritingResponse + 18, // 18: temporal.server.chasm.lib.stream.proto.v1.StreamService.SubscribeWorkflow:output_type -> temporal.server.chasm.lib.stream.proto.v1.SubscribeWorkflowResponse + 19, // 19: temporal.server.chasm.lib.stream.proto.v1.StreamService.PollMessages:output_type -> temporal.server.chasm.lib.stream.proto.v1.PollMessagesResponse + 20, // 20: temporal.server.chasm.lib.stream.proto.v1.StreamService.DescribeStream:output_type -> temporal.server.chasm.lib.stream.proto.v1.DescribeStreamResponse + 21, // 21: temporal.server.chasm.lib.stream.proto.v1.StreamService.PollWorkflowMessages:output_type -> temporal.server.chasm.lib.stream.proto.v1.PollWorkflowMessagesResponse + 22, // 22: temporal.server.chasm.lib.stream.proto.v1.StreamService.DescribeWorkflowStream:output_type -> temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamResponse + 23, // 23: temporal.server.chasm.lib.stream.proto.v1.StreamService.AddWorkflowMessages:output_type -> temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesResponse + 24, // 24: temporal.server.chasm.lib.stream.proto.v1.StreamService.RegisterStreamConsumer:output_type -> temporal.server.chasm.lib.stream.proto.v1.RegisterStreamConsumerResponse + 25, // 25: temporal.server.chasm.lib.stream.proto.v1.StreamService.AdvanceConsumerHead:output_type -> temporal.server.chasm.lib.stream.proto.v1.AdvanceConsumerHeadResponse + 26, // 26: temporal.server.chasm.lib.stream.proto.v1.StreamService.CloseStream:output_type -> temporal.server.chasm.lib.stream.proto.v1.CloseStreamResponse + 27, // 27: temporal.server.chasm.lib.stream.proto.v1.StreamService.TruncateStream:output_type -> temporal.server.chasm.lib.stream.proto.v1.TruncateStreamResponse + 28, // 28: temporal.server.chasm.lib.stream.proto.v1.StreamService.ListStreams:output_type -> temporal.server.chasm.lib.stream.proto.v1.ListStreamsResponse + 29, // 29: temporal.server.chasm.lib.stream.proto.v1.StreamService.DeleteStream:output_type -> temporal.server.chasm.lib.stream.proto.v1.DeleteStreamResponse + 15, // [15:30] is the sub-list for method output_type + 0, // [0:15] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_temporal_server_chasm_lib_stream_proto_v1_service_proto_init() } +func file_temporal_server_chasm_lib_stream_proto_v1_service_proto_init() { + if File_temporal_server_chasm_lib_stream_proto_v1_service_proto != nil { + return + } + file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_stream_proto_v1_service_proto_rawDesc), len(file_temporal_server_chasm_lib_stream_proto_v1_service_proto_rawDesc)), + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_temporal_server_chasm_lib_stream_proto_v1_service_proto_goTypes, + DependencyIndexes: file_temporal_server_chasm_lib_stream_proto_v1_service_proto_depIdxs, + }.Build() + File_temporal_server_chasm_lib_stream_proto_v1_service_proto = out.File + file_temporal_server_chasm_lib_stream_proto_v1_service_proto_goTypes = nil + file_temporal_server_chasm_lib_stream_proto_v1_service_proto_depIdxs = nil +} diff --git a/chasm/lib/stream/gen/streampb/v1/service_client.pb.go b/chasm/lib/stream/gen/streampb/v1/service_client.pb.go new file mode 100644 index 00000000000..946c94e2831 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/service_client.pb.go @@ -0,0 +1,713 @@ +// Code generated by protoc-gen-go-chasm. DO NOT EDIT. +package streampb + +import ( + "context" + "math/rand" + "time" + + "go.temporal.io/server/client/history" + "go.temporal.io/server/common" + "go.temporal.io/server/common/backoff" + "go.temporal.io/server/common/config" + "go.temporal.io/server/common/dynamicconfig" + "go.temporal.io/server/common/headers" + "go.temporal.io/server/common/log" + "go.temporal.io/server/common/membership" + "go.temporal.io/server/common/metrics" + "go.temporal.io/server/common/primitives" + "go.uber.org/fx" + "google.golang.org/grpc" +) + +// StreamServiceLayeredClient is a client for StreamService. +type StreamServiceLayeredClient struct { + metricsHandler metrics.Handler + numShards int32 + redirector history.Redirector[StreamServiceClient] + retryPolicy backoff.RetryPolicy +} + +// NewStreamServiceLayeredClient initializes a new StreamServiceLayeredClient. +func NewStreamServiceLayeredClient( + lc fx.Lifecycle, + dc *dynamicconfig.Collection, + rpcFactory common.RPCFactory, + monitor membership.Monitor, + config *config.Persistence, + logger log.Logger, + metricsHandler metrics.Handler, +) (StreamServiceClient, error) { + resolver, err := monitor.GetResolver(primitives.HistoryService) + if err != nil { + return nil, err + } + connections := history.NewConnectionPool(resolver, rpcFactory, NewStreamServiceClient, logger, dynamicconfig.HistoryConnectionCloseDelay.Get(dc)) + var redirector history.Redirector[StreamServiceClient] + if dynamicconfig.HistoryClientOwnershipCachingEnabled.Get(dc)() { + redirector = history.NewCachingRedirector( + connections, + resolver, + logger, + dynamicconfig.HistoryClientOwnershipCachingStaleTTL.Get(dc), + ) + } else { + redirector = history.NewBasicRedirector(connections, resolver) + } + client := &StreamServiceLayeredClient{ + metricsHandler: metricsHandler, + redirector: redirector, + numShards: config.NumHistoryShards, + retryPolicy: common.CreateHistoryClientRetryPolicy(dynamicconfig.RetryUnboundedOnSystemResourceExhausted.Get(dc)), + } + lc.Append(fx.StopHook(client.Stop)) + return client, nil +} +func (c *StreamServiceLayeredClient) Stop() { + c.redirector.Close() +} +func (c *StreamServiceLayeredClient) callCreateStreamNoRetry( + ctx context.Context, + request *CreateStreamRequest, + opts ...grpc.CallOption, +) (*CreateStreamResponse, error) { + var response *CreateStreamResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.CreateStream"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.CreateStream(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) CreateStream( + ctx context.Context, + request *CreateStreamRequest, + opts ...grpc.CallOption, +) (*CreateStreamResponse, error) { + call := func(ctx context.Context) (*CreateStreamResponse, error) { + return c.callCreateStreamNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callAddMessagesNoRetry( + ctx context.Context, + request *AddMessagesRequest, + opts ...grpc.CallOption, +) (*AddMessagesResponse, error) { + var response *AddMessagesResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.AddMessages"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.AddMessages(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) AddMessages( + ctx context.Context, + request *AddMessagesRequest, + opts ...grpc.CallOption, +) (*AddMessagesResponse, error) { + call := func(ctx context.Context) (*AddMessagesResponse, error) { + return c.callAddMessagesNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callFinishWritingNoRetry( + ctx context.Context, + request *FinishWritingRequest, + opts ...grpc.CallOption, +) (*FinishWritingResponse, error) { + var response *FinishWritingResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.FinishWriting"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.FinishWriting(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) FinishWriting( + ctx context.Context, + request *FinishWritingRequest, + opts ...grpc.CallOption, +) (*FinishWritingResponse, error) { + call := func(ctx context.Context) (*FinishWritingResponse, error) { + return c.callFinishWritingNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callSubscribeWorkflowNoRetry( + ctx context.Context, + request *SubscribeWorkflowRequest, + opts ...grpc.CallOption, +) (*SubscribeWorkflowResponse, error) { + var response *SubscribeWorkflowResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.SubscribeWorkflow"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetWorkflowId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.SubscribeWorkflow(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) SubscribeWorkflow( + ctx context.Context, + request *SubscribeWorkflowRequest, + opts ...grpc.CallOption, +) (*SubscribeWorkflowResponse, error) { + call := func(ctx context.Context) (*SubscribeWorkflowResponse, error) { + return c.callSubscribeWorkflowNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callPollMessagesNoRetry( + ctx context.Context, + request *PollMessagesRequest, + opts ...grpc.CallOption, +) (*PollMessagesResponse, error) { + var response *PollMessagesResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.PollMessages"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.PollMessages(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) PollMessages( + ctx context.Context, + request *PollMessagesRequest, + opts ...grpc.CallOption, +) (*PollMessagesResponse, error) { + call := func(ctx context.Context) (*PollMessagesResponse, error) { + return c.callPollMessagesNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callDescribeStreamNoRetry( + ctx context.Context, + request *DescribeStreamRequest, + opts ...grpc.CallOption, +) (*DescribeStreamResponse, error) { + var response *DescribeStreamResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.DescribeStream"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.DescribeStream(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) DescribeStream( + ctx context.Context, + request *DescribeStreamRequest, + opts ...grpc.CallOption, +) (*DescribeStreamResponse, error) { + call := func(ctx context.Context) (*DescribeStreamResponse, error) { + return c.callDescribeStreamNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callPollWorkflowMessagesNoRetry( + ctx context.Context, + request *PollWorkflowMessagesRequest, + opts ...grpc.CallOption, +) (*PollWorkflowMessagesResponse, error) { + var response *PollWorkflowMessagesResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.PollWorkflowMessages"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetWorkflowId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.PollWorkflowMessages(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) PollWorkflowMessages( + ctx context.Context, + request *PollWorkflowMessagesRequest, + opts ...grpc.CallOption, +) (*PollWorkflowMessagesResponse, error) { + call := func(ctx context.Context) (*PollWorkflowMessagesResponse, error) { + return c.callPollWorkflowMessagesNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callDescribeWorkflowStreamNoRetry( + ctx context.Context, + request *DescribeWorkflowStreamRequest, + opts ...grpc.CallOption, +) (*DescribeWorkflowStreamResponse, error) { + var response *DescribeWorkflowStreamResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.DescribeWorkflowStream"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetWorkflowId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.DescribeWorkflowStream(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) DescribeWorkflowStream( + ctx context.Context, + request *DescribeWorkflowStreamRequest, + opts ...grpc.CallOption, +) (*DescribeWorkflowStreamResponse, error) { + call := func(ctx context.Context) (*DescribeWorkflowStreamResponse, error) { + return c.callDescribeWorkflowStreamNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callAddWorkflowMessagesNoRetry( + ctx context.Context, + request *AddWorkflowMessagesRequest, + opts ...grpc.CallOption, +) (*AddWorkflowMessagesResponse, error) { + var response *AddWorkflowMessagesResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.AddWorkflowMessages"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetWorkflowId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.AddWorkflowMessages(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) AddWorkflowMessages( + ctx context.Context, + request *AddWorkflowMessagesRequest, + opts ...grpc.CallOption, +) (*AddWorkflowMessagesResponse, error) { + call := func(ctx context.Context) (*AddWorkflowMessagesResponse, error) { + return c.callAddWorkflowMessagesNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callRegisterStreamConsumerNoRetry( + ctx context.Context, + request *RegisterStreamConsumerRequest, + opts ...grpc.CallOption, +) (*RegisterStreamConsumerResponse, error) { + var response *RegisterStreamConsumerResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.RegisterStreamConsumer"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.RegisterStreamConsumer(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) RegisterStreamConsumer( + ctx context.Context, + request *RegisterStreamConsumerRequest, + opts ...grpc.CallOption, +) (*RegisterStreamConsumerResponse, error) { + call := func(ctx context.Context) (*RegisterStreamConsumerResponse, error) { + return c.callRegisterStreamConsumerNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callAdvanceConsumerHeadNoRetry( + ctx context.Context, + request *AdvanceConsumerHeadRequest, + opts ...grpc.CallOption, +) (*AdvanceConsumerHeadResponse, error) { + var response *AdvanceConsumerHeadResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.AdvanceConsumerHead"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetWorkflowId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.AdvanceConsumerHead(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) AdvanceConsumerHead( + ctx context.Context, + request *AdvanceConsumerHeadRequest, + opts ...grpc.CallOption, +) (*AdvanceConsumerHeadResponse, error) { + call := func(ctx context.Context) (*AdvanceConsumerHeadResponse, error) { + return c.callAdvanceConsumerHeadNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callCloseStreamNoRetry( + ctx context.Context, + request *CloseStreamRequest, + opts ...grpc.CallOption, +) (*CloseStreamResponse, error) { + var response *CloseStreamResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.CloseStream"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.CloseStream(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) CloseStream( + ctx context.Context, + request *CloseStreamRequest, + opts ...grpc.CallOption, +) (*CloseStreamResponse, error) { + call := func(ctx context.Context) (*CloseStreamResponse, error) { + return c.callCloseStreamNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callTruncateStreamNoRetry( + ctx context.Context, + request *TruncateStreamRequest, + opts ...grpc.CallOption, +) (*TruncateStreamResponse, error) { + var response *TruncateStreamResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.TruncateStream"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.TruncateStream(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) TruncateStream( + ctx context.Context, + request *TruncateStreamRequest, + opts ...grpc.CallOption, +) (*TruncateStreamResponse, error) { + call := func(ctx context.Context) (*TruncateStreamResponse, error) { + return c.callTruncateStreamNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callListStreamsNoRetry( + ctx context.Context, + request *ListStreamsRequest, + opts ...grpc.CallOption, +) (*ListStreamsResponse, error) { + var response *ListStreamsResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.ListStreams"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := int32(rand.Intn(int(c.numShards)) + 1) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.ListStreams(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) ListStreams( + ctx context.Context, + request *ListStreamsRequest, + opts ...grpc.CallOption, +) (*ListStreamsResponse, error) { + call := func(ctx context.Context) (*ListStreamsResponse, error) { + return c.callListStreamsNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} +func (c *StreamServiceLayeredClient) callDeleteStreamNoRetry( + ctx context.Context, + request *DeleteStreamRequest, + opts ...grpc.CallOption, +) (*DeleteStreamResponse, error) { + var response *DeleteStreamResponse + var err error + startTime := time.Now().UTC() + // the caller is a namespace, hence the tag below. + caller := headers.GetCallerInfo(ctx).CallerName + metricsHandler := c.metricsHandler.WithTags( + metrics.OperationTag("StreamService.DeleteStream"), + metrics.NamespaceTag(caller), + metrics.ServiceRoleTag(metrics.HistoryRoleTagValue), + ) + metrics.ClientRequests.With(metricsHandler).Record(1) + defer func() { + if err != nil { + metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err)) + } + metrics.ClientLatency.With(metricsHandler).Record(time.Since(startTime)) + }() + shardID := common.WorkflowIDToHistoryShard(request.GetNamespaceId(), request.GetFrontendRequest().GetStreamId(), c.numShards) + op := func(ctx context.Context, client StreamServiceClient) error { + var err error + ctx, cancel := context.WithTimeout(ctx, history.DefaultTimeout) + defer cancel() + response, err = client.DeleteStream(ctx, request, opts...) + return err + } + err = c.redirector.Execute(ctx, shardID, op) + return response, err +} +func (c *StreamServiceLayeredClient) DeleteStream( + ctx context.Context, + request *DeleteStreamRequest, + opts ...grpc.CallOption, +) (*DeleteStreamResponse, error) { + call := func(ctx context.Context) (*DeleteStreamResponse, error) { + return c.callDeleteStreamNoRetry(ctx, request, opts...) + } + return backoff.ThrottleRetryContextWithReturn(ctx, call, c.retryPolicy, common.IsServiceClientTransientError) +} diff --git a/chasm/lib/stream/gen/streampb/v1/service_grpc.pb.go b/chasm/lib/stream/gen/streampb/v1/service_grpc.pb.go new file mode 100644 index 00000000000..0a9d2f93a28 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/service_grpc.pb.go @@ -0,0 +1,640 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// plugins: +// - protoc-gen-go-grpc +// - protoc +// source: temporal/server/chasm/lib/stream/proto/v1/service.proto + +package streampb + +import ( + context "context" + + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + StreamService_CreateStream_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/CreateStream" + StreamService_AddMessages_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/AddMessages" + StreamService_FinishWriting_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/FinishWriting" + StreamService_SubscribeWorkflow_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/SubscribeWorkflow" + StreamService_PollMessages_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/PollMessages" + StreamService_DescribeStream_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/DescribeStream" + StreamService_PollWorkflowMessages_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/PollWorkflowMessages" + StreamService_DescribeWorkflowStream_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/DescribeWorkflowStream" + StreamService_AddWorkflowMessages_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/AddWorkflowMessages" + StreamService_RegisterStreamConsumer_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/RegisterStreamConsumer" + StreamService_AdvanceConsumerHead_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/AdvanceConsumerHead" + StreamService_CloseStream_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/CloseStream" + StreamService_TruncateStream_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/TruncateStream" + StreamService_ListStreams_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/ListStreams" + StreamService_DeleteStream_FullMethodName = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/DeleteStream" +) + +// StreamServiceClient is the client API for StreamService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type StreamServiceClient interface { + CreateStream(ctx context.Context, in *CreateStreamRequest, opts ...grpc.CallOption) (*CreateStreamResponse, error) + AddMessages(ctx context.Context, in *AddMessagesRequest, opts ...grpc.CallOption) (*AddMessagesResponse, error) + FinishWriting(ctx context.Context, in *FinishWritingRequest, opts ...grpc.CallOption) (*FinishWritingResponse, error) + SubscribeWorkflow(ctx context.Context, in *SubscribeWorkflowRequest, opts ...grpc.CallOption) (*SubscribeWorkflowResponse, error) + PollMessages(ctx context.Context, in *PollMessagesRequest, opts ...grpc.CallOption) (*PollMessagesResponse, error) + DescribeStream(ctx context.Context, in *DescribeStreamRequest, opts ...grpc.CallOption) (*DescribeStreamResponse, error) + // Routed on the owner, because the stream it reads has no id of its own. + PollWorkflowMessages(ctx context.Context, in *PollWorkflowMessagesRequest, opts ...grpc.CallOption) (*PollWorkflowMessagesResponse, error) + DescribeWorkflowStream(ctx context.Context, in *DescribeWorkflowStreamRequest, opts ...grpc.CallOption) (*DescribeWorkflowStreamResponse, error) + AddWorkflowMessages(ctx context.Context, in *AddWorkflowMessagesRequest, opts ...grpc.CallOption) (*AddWorkflowMessagesResponse, error) + // Internal. History calls these on itself to reach a shard it does not own, + // which is the only way a step that spans two executions can work on a + // cluster with more than one history host. + RegisterStreamConsumer(ctx context.Context, in *RegisterStreamConsumerRequest, opts ...grpc.CallOption) (*RegisterStreamConsumerResponse, error) + AdvanceConsumerHead(ctx context.Context, in *AdvanceConsumerHeadRequest, opts ...grpc.CallOption) (*AdvanceConsumerHeadResponse, error) + CloseStream(ctx context.Context, in *CloseStreamRequest, opts ...grpc.CallOption) (*CloseStreamResponse, error) + TruncateStream(ctx context.Context, in *TruncateStreamRequest, opts ...grpc.CallOption) (*TruncateStreamResponse, error) + // Served on the frontend only: it queries visibility rather than a stream, + // so there is no business ID to route on and nothing for a shard to answer. + ListStreams(ctx context.Context, in *ListStreamsRequest, opts ...grpc.CallOption) (*ListStreamsResponse, error) + DeleteStream(ctx context.Context, in *DeleteStreamRequest, opts ...grpc.CallOption) (*DeleteStreamResponse, error) +} + +type streamServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewStreamServiceClient(cc grpc.ClientConnInterface) StreamServiceClient { + return &streamServiceClient{cc} +} + +func (c *streamServiceClient) CreateStream(ctx context.Context, in *CreateStreamRequest, opts ...grpc.CallOption) (*CreateStreamResponse, error) { + out := new(CreateStreamResponse) + err := c.cc.Invoke(ctx, StreamService_CreateStream_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) AddMessages(ctx context.Context, in *AddMessagesRequest, opts ...grpc.CallOption) (*AddMessagesResponse, error) { + out := new(AddMessagesResponse) + err := c.cc.Invoke(ctx, StreamService_AddMessages_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) FinishWriting(ctx context.Context, in *FinishWritingRequest, opts ...grpc.CallOption) (*FinishWritingResponse, error) { + out := new(FinishWritingResponse) + err := c.cc.Invoke(ctx, StreamService_FinishWriting_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) SubscribeWorkflow(ctx context.Context, in *SubscribeWorkflowRequest, opts ...grpc.CallOption) (*SubscribeWorkflowResponse, error) { + out := new(SubscribeWorkflowResponse) + err := c.cc.Invoke(ctx, StreamService_SubscribeWorkflow_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) PollMessages(ctx context.Context, in *PollMessagesRequest, opts ...grpc.CallOption) (*PollMessagesResponse, error) { + out := new(PollMessagesResponse) + err := c.cc.Invoke(ctx, StreamService_PollMessages_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) DescribeStream(ctx context.Context, in *DescribeStreamRequest, opts ...grpc.CallOption) (*DescribeStreamResponse, error) { + out := new(DescribeStreamResponse) + err := c.cc.Invoke(ctx, StreamService_DescribeStream_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) PollWorkflowMessages(ctx context.Context, in *PollWorkflowMessagesRequest, opts ...grpc.CallOption) (*PollWorkflowMessagesResponse, error) { + out := new(PollWorkflowMessagesResponse) + err := c.cc.Invoke(ctx, StreamService_PollWorkflowMessages_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) DescribeWorkflowStream(ctx context.Context, in *DescribeWorkflowStreamRequest, opts ...grpc.CallOption) (*DescribeWorkflowStreamResponse, error) { + out := new(DescribeWorkflowStreamResponse) + err := c.cc.Invoke(ctx, StreamService_DescribeWorkflowStream_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) AddWorkflowMessages(ctx context.Context, in *AddWorkflowMessagesRequest, opts ...grpc.CallOption) (*AddWorkflowMessagesResponse, error) { + out := new(AddWorkflowMessagesResponse) + err := c.cc.Invoke(ctx, StreamService_AddWorkflowMessages_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) RegisterStreamConsumer(ctx context.Context, in *RegisterStreamConsumerRequest, opts ...grpc.CallOption) (*RegisterStreamConsumerResponse, error) { + out := new(RegisterStreamConsumerResponse) + err := c.cc.Invoke(ctx, StreamService_RegisterStreamConsumer_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) AdvanceConsumerHead(ctx context.Context, in *AdvanceConsumerHeadRequest, opts ...grpc.CallOption) (*AdvanceConsumerHeadResponse, error) { + out := new(AdvanceConsumerHeadResponse) + err := c.cc.Invoke(ctx, StreamService_AdvanceConsumerHead_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) CloseStream(ctx context.Context, in *CloseStreamRequest, opts ...grpc.CallOption) (*CloseStreamResponse, error) { + out := new(CloseStreamResponse) + err := c.cc.Invoke(ctx, StreamService_CloseStream_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) TruncateStream(ctx context.Context, in *TruncateStreamRequest, opts ...grpc.CallOption) (*TruncateStreamResponse, error) { + out := new(TruncateStreamResponse) + err := c.cc.Invoke(ctx, StreamService_TruncateStream_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) ListStreams(ctx context.Context, in *ListStreamsRequest, opts ...grpc.CallOption) (*ListStreamsResponse, error) { + out := new(ListStreamsResponse) + err := c.cc.Invoke(ctx, StreamService_ListStreams_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *streamServiceClient) DeleteStream(ctx context.Context, in *DeleteStreamRequest, opts ...grpc.CallOption) (*DeleteStreamResponse, error) { + out := new(DeleteStreamResponse) + err := c.cc.Invoke(ctx, StreamService_DeleteStream_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// StreamServiceServer is the server API for StreamService service. +// All implementations must embed UnimplementedStreamServiceServer +// for forward compatibility +type StreamServiceServer interface { + CreateStream(context.Context, *CreateStreamRequest) (*CreateStreamResponse, error) + AddMessages(context.Context, *AddMessagesRequest) (*AddMessagesResponse, error) + FinishWriting(context.Context, *FinishWritingRequest) (*FinishWritingResponse, error) + SubscribeWorkflow(context.Context, *SubscribeWorkflowRequest) (*SubscribeWorkflowResponse, error) + PollMessages(context.Context, *PollMessagesRequest) (*PollMessagesResponse, error) + DescribeStream(context.Context, *DescribeStreamRequest) (*DescribeStreamResponse, error) + // Routed on the owner, because the stream it reads has no id of its own. + PollWorkflowMessages(context.Context, *PollWorkflowMessagesRequest) (*PollWorkflowMessagesResponse, error) + DescribeWorkflowStream(context.Context, *DescribeWorkflowStreamRequest) (*DescribeWorkflowStreamResponse, error) + AddWorkflowMessages(context.Context, *AddWorkflowMessagesRequest) (*AddWorkflowMessagesResponse, error) + // Internal. History calls these on itself to reach a shard it does not own, + // which is the only way a step that spans two executions can work on a + // cluster with more than one history host. + RegisterStreamConsumer(context.Context, *RegisterStreamConsumerRequest) (*RegisterStreamConsumerResponse, error) + AdvanceConsumerHead(context.Context, *AdvanceConsumerHeadRequest) (*AdvanceConsumerHeadResponse, error) + CloseStream(context.Context, *CloseStreamRequest) (*CloseStreamResponse, error) + TruncateStream(context.Context, *TruncateStreamRequest) (*TruncateStreamResponse, error) + // Served on the frontend only: it queries visibility rather than a stream, + // so there is no business ID to route on and nothing for a shard to answer. + ListStreams(context.Context, *ListStreamsRequest) (*ListStreamsResponse, error) + DeleteStream(context.Context, *DeleteStreamRequest) (*DeleteStreamResponse, error) + mustEmbedUnimplementedStreamServiceServer() +} + +// UnimplementedStreamServiceServer must be embedded to have forward compatible implementations. +type UnimplementedStreamServiceServer struct { +} + +func (UnimplementedStreamServiceServer) CreateStream(context.Context, *CreateStreamRequest) (*CreateStreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateStream not implemented") +} +func (UnimplementedStreamServiceServer) AddMessages(context.Context, *AddMessagesRequest) (*AddMessagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddMessages not implemented") +} +func (UnimplementedStreamServiceServer) FinishWriting(context.Context, *FinishWritingRequest) (*FinishWritingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinishWriting not implemented") +} +func (UnimplementedStreamServiceServer) SubscribeWorkflow(context.Context, *SubscribeWorkflowRequest) (*SubscribeWorkflowResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubscribeWorkflow not implemented") +} +func (UnimplementedStreamServiceServer) PollMessages(context.Context, *PollMessagesRequest) (*PollMessagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PollMessages not implemented") +} +func (UnimplementedStreamServiceServer) DescribeStream(context.Context, *DescribeStreamRequest) (*DescribeStreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DescribeStream not implemented") +} +func (UnimplementedStreamServiceServer) PollWorkflowMessages(context.Context, *PollWorkflowMessagesRequest) (*PollWorkflowMessagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PollWorkflowMessages not implemented") +} +func (UnimplementedStreamServiceServer) DescribeWorkflowStream(context.Context, *DescribeWorkflowStreamRequest) (*DescribeWorkflowStreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DescribeWorkflowStream not implemented") +} +func (UnimplementedStreamServiceServer) AddWorkflowMessages(context.Context, *AddWorkflowMessagesRequest) (*AddWorkflowMessagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddWorkflowMessages not implemented") +} +func (UnimplementedStreamServiceServer) RegisterStreamConsumer(context.Context, *RegisterStreamConsumerRequest) (*RegisterStreamConsumerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterStreamConsumer not implemented") +} +func (UnimplementedStreamServiceServer) AdvanceConsumerHead(context.Context, *AdvanceConsumerHeadRequest) (*AdvanceConsumerHeadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AdvanceConsumerHead not implemented") +} +func (UnimplementedStreamServiceServer) CloseStream(context.Context, *CloseStreamRequest) (*CloseStreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CloseStream not implemented") +} +func (UnimplementedStreamServiceServer) TruncateStream(context.Context, *TruncateStreamRequest) (*TruncateStreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TruncateStream not implemented") +} +func (UnimplementedStreamServiceServer) ListStreams(context.Context, *ListStreamsRequest) (*ListStreamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListStreams not implemented") +} +func (UnimplementedStreamServiceServer) DeleteStream(context.Context, *DeleteStreamRequest) (*DeleteStreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteStream not implemented") +} +func (UnimplementedStreamServiceServer) mustEmbedUnimplementedStreamServiceServer() {} + +// UnsafeStreamServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to StreamServiceServer will +// result in compilation errors. +type UnsafeStreamServiceServer interface { + mustEmbedUnimplementedStreamServiceServer() +} + +func RegisterStreamServiceServer(s grpc.ServiceRegistrar, srv StreamServiceServer) { + s.RegisterService(&StreamService_ServiceDesc, srv) +} + +func _StreamService_CreateStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).CreateStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_CreateStream_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).CreateStream(ctx, req.(*CreateStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_AddMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddMessagesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).AddMessages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_AddMessages_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).AddMessages(ctx, req.(*AddMessagesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_FinishWriting_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FinishWritingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).FinishWriting(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_FinishWriting_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).FinishWriting(ctx, req.(*FinishWritingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_SubscribeWorkflow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubscribeWorkflowRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).SubscribeWorkflow(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_SubscribeWorkflow_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).SubscribeWorkflow(ctx, req.(*SubscribeWorkflowRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_PollMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PollMessagesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).PollMessages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_PollMessages_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).PollMessages(ctx, req.(*PollMessagesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_DescribeStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DescribeStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).DescribeStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_DescribeStream_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).DescribeStream(ctx, req.(*DescribeStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_PollWorkflowMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PollWorkflowMessagesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).PollWorkflowMessages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_PollWorkflowMessages_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).PollWorkflowMessages(ctx, req.(*PollWorkflowMessagesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_DescribeWorkflowStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DescribeWorkflowStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).DescribeWorkflowStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_DescribeWorkflowStream_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).DescribeWorkflowStream(ctx, req.(*DescribeWorkflowStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_AddWorkflowMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddWorkflowMessagesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).AddWorkflowMessages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_AddWorkflowMessages_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).AddWorkflowMessages(ctx, req.(*AddWorkflowMessagesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_RegisterStreamConsumer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterStreamConsumerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).RegisterStreamConsumer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_RegisterStreamConsumer_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).RegisterStreamConsumer(ctx, req.(*RegisterStreamConsumerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_AdvanceConsumerHead_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AdvanceConsumerHeadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).AdvanceConsumerHead(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_AdvanceConsumerHead_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).AdvanceConsumerHead(ctx, req.(*AdvanceConsumerHeadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_CloseStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CloseStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).CloseStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_CloseStream_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).CloseStream(ctx, req.(*CloseStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_TruncateStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TruncateStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).TruncateStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_TruncateStream_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).TruncateStream(ctx, req.(*TruncateStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_ListStreams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListStreamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).ListStreams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_ListStreams_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).ListStreams(ctx, req.(*ListStreamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StreamService_DeleteStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StreamServiceServer).DeleteStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StreamService_DeleteStream_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StreamServiceServer).DeleteStream(ctx, req.(*DeleteStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// StreamService_ServiceDesc is the grpc.ServiceDesc for StreamService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var StreamService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "temporal.server.chasm.lib.stream.proto.v1.StreamService", + HandlerType: (*StreamServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateStream", + Handler: _StreamService_CreateStream_Handler, + }, + { + MethodName: "AddMessages", + Handler: _StreamService_AddMessages_Handler, + }, + { + MethodName: "FinishWriting", + Handler: _StreamService_FinishWriting_Handler, + }, + { + MethodName: "SubscribeWorkflow", + Handler: _StreamService_SubscribeWorkflow_Handler, + }, + { + MethodName: "PollMessages", + Handler: _StreamService_PollMessages_Handler, + }, + { + MethodName: "DescribeStream", + Handler: _StreamService_DescribeStream_Handler, + }, + { + MethodName: "PollWorkflowMessages", + Handler: _StreamService_PollWorkflowMessages_Handler, + }, + { + MethodName: "DescribeWorkflowStream", + Handler: _StreamService_DescribeWorkflowStream_Handler, + }, + { + MethodName: "AddWorkflowMessages", + Handler: _StreamService_AddWorkflowMessages_Handler, + }, + { + MethodName: "RegisterStreamConsumer", + Handler: _StreamService_RegisterStreamConsumer_Handler, + }, + { + MethodName: "AdvanceConsumerHead", + Handler: _StreamService_AdvanceConsumerHead_Handler, + }, + { + MethodName: "CloseStream", + Handler: _StreamService_CloseStream_Handler, + }, + { + MethodName: "TruncateStream", + Handler: _StreamService_TruncateStream_Handler, + }, + { + MethodName: "ListStreams", + Handler: _StreamService_ListStreams_Handler, + }, + { + MethodName: "DeleteStream", + Handler: _StreamService_DeleteStream_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "temporal/server/chasm/lib/stream/proto/v1/service.proto", +} diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go new file mode 100644 index 00000000000..99af3b53701 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go @@ -0,0 +1,191 @@ +// Code generated by protoc-gen-go-helpers. DO NOT EDIT. +package streampb + +import ( + "google.golang.org/protobuf/proto" +) + +// Marshal an object of type StreamState to the protobuf v3 wire format +func (val *StreamState) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamState from the protobuf v3 wire format +func (val *StreamState) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamState) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamState 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 *StreamState) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamState + switch t := that.(type) { + case *StreamState: + that1 = t + case StreamState: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type ProducerCursor to the protobuf v3 wire format +func (val *ProducerCursor) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type ProducerCursor from the protobuf v3 wire format +func (val *ProducerCursor) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *ProducerCursor) Size() int { + return proto.Size(val) +} + +// Equal returns whether two ProducerCursor 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 *ProducerCursor) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *ProducerCursor + switch t := that.(type) { + case *ProducerCursor: + that1 = t + case ProducerCursor: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type ConsumerCursor to the protobuf v3 wire format +func (val *ConsumerCursor) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type ConsumerCursor from the protobuf v3 wire format +func (val *ConsumerCursor) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *ConsumerCursor) Size() int { + return proto.Size(val) +} + +// Equal returns whether two ConsumerCursor 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 *ConsumerCursor) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *ConsumerCursor + switch t := that.(type) { + case *ConsumerCursor: + that1 = t + case ConsumerCursor: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type WorkflowStreamCursor to the protobuf v3 wire format +func (val *WorkflowStreamCursor) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type WorkflowStreamCursor from the protobuf v3 wire format +func (val *WorkflowStreamCursor) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *WorkflowStreamCursor) Size() int { + return proto.Size(val) +} + +// Equal returns whether two WorkflowStreamCursor 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 *WorkflowStreamCursor) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *WorkflowStreamCursor + switch t := that.(type) { + case *WorkflowStreamCursor: + that1 = t + case WorkflowStreamCursor: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type StreamLifecycle to the protobuf v3 wire format +func (val *StreamLifecycle) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamLifecycle from the protobuf v3 wire format +func (val *StreamLifecycle) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamLifecycle) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamLifecycle 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 *StreamLifecycle) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamLifecycle + switch t := that.(type) { + case *StreamLifecycle: + that1 = t + case StreamLifecycle: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go new file mode 100644 index 00000000000..bec3fb2467d --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go @@ -0,0 +1,665 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// plugins: +// protoc-gen-go +// protoc +// source: temporal/server/chasm/lib/stream/proto/v1/stream_state.proto + +package streampb + +import ( + reflect "reflect" + sync "sync" + unsafe "unsafe" + + v1 "go.temporal.io/api/common/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Size is O(producers + consumers), never O(messages). Payload bytes live in +// the log, not here, which is what keeps this off the CHASM partial-read path. +type StreamState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Visibility frontier. Readers never observe an offset at or past this. + HeadOffset int64 `protobuf:"varint,1,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` + // Truncation floor. Offsets below this are gone. + BaseOffset int64 `protobuf:"varint,2,opt,name=base_offset,json=baseOffset,proto3" json:"base_offset,omitempty"` // Chains log nodes so a stale node from an abandoned append is rejected on + Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` + CloseReason *v1.Payload `protobuf:"bytes,5,opt,name=close_reason,json=closeReason,proto3" json:"close_reason,omitempty"` + // Bumped on ownership change so a stale producer's write fails. + OwnerEpoch int64 `protobuf:"varint,6,opt,name=owner_epoch,json=ownerEpoch,proto3" json:"owner_epoch,omitempty"` + // Immutable once set. Offsets roll to a new log tree every bucket_size so no + // single storage partition grows with the stream. + BucketSize int64 `protobuf:"varint,7,opt,name=bucket_size,json=bucketSize,proto3" json:"bucket_size,omitempty"` + // Identity of the log this stream writes to. Bucket trees are derived from + // it, so there is no per-bucket index to store. + CollectionId string `protobuf:"bytes,8,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"` + Producers map[string]*ProducerCursor `protobuf:"bytes,9,rep,name=producers,proto3" json:"producers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Consumers map[string]*ConsumerCursor `protobuf:"bytes,10,rep,name=consumers,proto3" json:"consumers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Lifecycle *StreamLifecycle `protobuf:"bytes,11,opt,name=lifecycle,proto3" json:"lifecycle,omitempty"` + // Set when a successor run takes ownership, so an in-flight poll can follow + // the chain instead of stalling on a superseded run. + RedirectRunId string `protobuf:"bytes,12,opt,name=redirect_run_id,json=redirectRunId,proto3" json:"redirect_run_id,omitempty"` + // Wall-clock close time, used to schedule retention deletion. + CloseTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=close_time,json=closeTime,proto3" json:"close_time,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamState) Reset() { + *x = StreamState{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamState) ProtoMessage() {} + +func (x *StreamState) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[0] + 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 StreamState.ProtoReflect.Descriptor instead. +func (*StreamState) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{0} +} + +func (x *StreamState) GetHeadOffset() int64 { + if x != nil { + return x.HeadOffset + } + return 0 +} + +func (x *StreamState) GetBaseOffset() int64 { + if x != nil { + return x.BaseOffset + } + return 0 +} + +func (x *StreamState) GetClosed() bool { + if x != nil { + return x.Closed + } + return false +} + +func (x *StreamState) GetCloseReason() *v1.Payload { + if x != nil { + return x.CloseReason + } + return nil +} + +func (x *StreamState) GetOwnerEpoch() int64 { + if x != nil { + return x.OwnerEpoch + } + return 0 +} + +func (x *StreamState) GetBucketSize() int64 { + if x != nil { + return x.BucketSize + } + return 0 +} + +func (x *StreamState) GetCollectionId() string { + if x != nil { + return x.CollectionId + } + return "" +} + +func (x *StreamState) GetProducers() map[string]*ProducerCursor { + if x != nil { + return x.Producers + } + return nil +} + +func (x *StreamState) GetConsumers() map[string]*ConsumerCursor { + if x != nil { + return x.Consumers + } + return nil +} + +func (x *StreamState) GetLifecycle() *StreamLifecycle { + if x != nil { + return x.Lifecycle + } + return nil +} + +func (x *StreamState) GetRedirectRunId() string { + if x != nil { + return x.RedirectRunId + } + return "" +} + +func (x *StreamState) GetCloseTime() *timestamppb.Timestamp { + if x != nil { + return x.CloseTime + } + return nil +} + +type ProducerCursor struct { + state protoimpl.MessageState `protogen:"open.v1"` + Seq int64 `protobuf:"varint,1,opt,name=seq,proto3" json:"seq,omitempty"` + FirstOffset int64 `protobuf:"varint,2,opt,name=first_offset,json=firstOffset,proto3" json:"first_offset,omitempty"` + Count int64 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` + // Distinguishes a genuine retry from a client reusing a sequence with + // different content, which must be rejected rather than deduplicated. + ContentHash []byte `protobuf:"bytes,4,opt,name=content_hash,json=contentHash,proto3" json:"content_hash,omitempty"` + // Set by FinishWriting. Ends this producer's writes without closing the + // stream for anyone else. + Fenced bool `protobuf:"varint,5,opt,name=fenced,proto3" json:"fenced,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProducerCursor) Reset() { + *x = ProducerCursor{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProducerCursor) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProducerCursor) ProtoMessage() {} + +func (x *ProducerCursor) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[1] + 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 ProducerCursor.ProtoReflect.Descriptor instead. +func (*ProducerCursor) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{1} +} + +func (x *ProducerCursor) GetSeq() int64 { + if x != nil { + return x.Seq + } + return 0 +} + +func (x *ProducerCursor) GetFirstOffset() int64 { + if x != nil { + return x.FirstOffset + } + return 0 +} + +func (x *ProducerCursor) GetCount() int64 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *ProducerCursor) GetContentHash() []byte { + if x != nil { + return x.ContentHash + } + return nil +} + +func (x *ProducerCursor) GetFenced() bool { + if x != nil { + return x.Fenced + } + return false +} + +type ConsumerCursor struct { + state protoimpl.MessageState `protogen:"open.v1"` + WorkflowId string `protobuf:"bytes,1,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"` + RunId string `protobuf:"bytes,2,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + // How far this consumer has read. Used to decide whether it needs waking, + // not to decide what the stream may drop. + Offset int64 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` + Active bool `protobuf:"varint,4,opt,name=active,proto3" json:"active,omitempty"` + // Set when the consumer is a workflow in another execution, which is the + // only case that has to be told the frontier moved. A workflow consuming a + // stream it owns sees that while closing its own transaction. + External bool `protobuf:"varint,5,opt,name=external,proto3" json:"external,omitempty"` + // The oldest offset this consumer's History still depends on. + // + // It is where the subscription started, not where it has read to. A range + // this consumer already consumed is recorded in its History and has to be + // re-readable for the workflow to replay, so bytes below the read position + // are exactly the ones a replay needs most. It stays put while the consumer + // is active and is released when it deregisters. + ReplayFloor int64 `protobuf:"varint,6,opt,name=replay_floor,json=replayFloor,proto3" json:"replay_floor,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConsumerCursor) Reset() { + *x = ConsumerCursor{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConsumerCursor) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConsumerCursor) ProtoMessage() {} + +func (x *ConsumerCursor) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_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 ConsumerCursor.ProtoReflect.Descriptor instead. +func (*ConsumerCursor) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{2} +} + +func (x *ConsumerCursor) GetWorkflowId() string { + if x != nil { + return x.WorkflowId + } + return "" +} + +func (x *ConsumerCursor) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +func (x *ConsumerCursor) GetOffset() int64 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *ConsumerCursor) GetActive() bool { + if x != nil { + return x.Active + } + return false +} + +func (x *ConsumerCursor) GetExternal() bool { + if x != nil { + return x.External + } + return false +} + +func (x *ConsumerCursor) GetReplayFloor() int64 { + if x != nil { + return x.ReplayFloor + } + return 0 +} + +// A consuming Workflow's position in a stream. This lives in the consuming +// Workflow's own state rather than on the stream, so advancing it commits in +// the same transaction as the WorkflowTaskCompleted event that records the +// range. Keeping it on the stream would make the advance a cross-execution +// write, and a crash between the two would either redeliver or skip. +type WorkflowStreamCursor struct { + state protoimpl.MessageState `protogen:"open.v1"` + StreamId string `protobuf:"bytes,1,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + // Enough to address the log without reading the stream component first. + CollectionId string `protobuf:"bytes,2,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"` + BucketSize int64 `protobuf:"varint,3,opt,name=bucket_size,json=bucketSize,proto3" json:"bucket_size,omitempty"` + // Next offset to deliver. + Offset int64 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"` + // The stream's frontier as of the last delivery. A workflow consuming a + // stream in another execution cannot read the real frontier while closing its + // own transaction, so this is what tells it a capped slice left more behind. + KnownHead int64 `protobuf:"varint,8,opt,name=known_head,json=knownHead,proto3" json:"known_head,omitempty"` + // Set when the stream lives in another execution, which decides whether the + // frontier is read locally or taken from known_head. + External bool `protobuf:"varint,9,opt,name=external,proto3" json:"external,omitempty"` + // The range attached to the Workflow Task currently in flight. Recorded on + // the event that closes that task, then folded into offset. An empty range + // is still recorded: a task where the subscription saw nothing is a fact + // replay has to reproduce. + PendingFrom int64 `protobuf:"varint,5,opt,name=pending_from,json=pendingFrom,proto3" json:"pending_from,omitempty"` + PendingTo int64 `protobuf:"varint,6,opt,name=pending_to,json=pendingTo,proto3" json:"pending_to,omitempty"` + HasPending bool `protobuf:"varint,7,opt,name=has_pending,json=hasPending,proto3" json:"has_pending,omitempty"` + // Where this subscription began reading. Retained separately from offset, + // which advances, because replay has to tell "committed nothing yet" apart + // from "the events recording what was committed are not in this page". + StartOffset int64 `protobuf:"varint,10,opt,name=start_offset,json=startOffset,proto3" json:"start_offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkflowStreamCursor) Reset() { + *x = WorkflowStreamCursor{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkflowStreamCursor) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkflowStreamCursor) ProtoMessage() {} + +func (x *WorkflowStreamCursor) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkflowStreamCursor.ProtoReflect.Descriptor instead. +func (*WorkflowStreamCursor) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{3} +} + +func (x *WorkflowStreamCursor) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *WorkflowStreamCursor) GetCollectionId() string { + if x != nil { + return x.CollectionId + } + return "" +} + +func (x *WorkflowStreamCursor) GetBucketSize() int64 { + if x != nil { + return x.BucketSize + } + return 0 +} + +func (x *WorkflowStreamCursor) GetOffset() int64 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *WorkflowStreamCursor) GetKnownHead() int64 { + if x != nil { + return x.KnownHead + } + return 0 +} + +func (x *WorkflowStreamCursor) GetExternal() bool { + if x != nil { + return x.External + } + return false +} + +func (x *WorkflowStreamCursor) GetPendingFrom() int64 { + if x != nil { + return x.PendingFrom + } + return 0 +} + +func (x *WorkflowStreamCursor) GetPendingTo() int64 { + if x != nil { + return x.PendingTo + } + return 0 +} + +func (x *WorkflowStreamCursor) GetHasPending() bool { + if x != nil { + return x.HasPending + } + return false +} + +func (x *WorkflowStreamCursor) GetStartOffset() int64 { + if x != nil { + return x.StartOffset + } + return 0 +} + +type StreamLifecycle struct { + state protoimpl.MessageState `protogen:"open.v1"` + // How long a closed stream stays readable before it is deleted. + Retention *durationpb.Duration `protobuf:"bytes,1,opt,name=retention,proto3" json:"retention,omitempty"` + // Cap on readable messages. Older whole buckets are reclaimed once the floor + // passes them, so a capped stream has bounded storage. + MaxItems int64 `protobuf:"varint,2,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamLifecycle) Reset() { + *x = StreamLifecycle{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamLifecycle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamLifecycle) ProtoMessage() {} + +func (x *StreamLifecycle) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamLifecycle.ProtoReflect.Descriptor instead. +func (*StreamLifecycle) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{4} +} + +func (x *StreamLifecycle) GetRetention() *durationpb.Duration { + if x != nil { + return x.Retention + } + return nil +} + +func (x *StreamLifecycle) GetMaxItems() int64 { + if x != nil { + return x.MaxItems + } + return 0 +} + +var File_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto protoreflect.FileDescriptor + +const file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc = "" + + "\n" + + "Z temporal.api.common.v1.Payload + 5, // 1: temporal.server.chasm.lib.stream.proto.v1.StreamState.producers:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState.ProducersEntry + 6, // 2: temporal.server.chasm.lib.stream.proto.v1.StreamState.consumers:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState.ConsumersEntry + 4, // 3: temporal.server.chasm.lib.stream.proto.v1.StreamState.lifecycle:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle + 8, // 4: temporal.server.chasm.lib.stream.proto.v1.StreamState.close_time:type_name -> google.protobuf.Timestamp + 9, // 5: temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle.retention:type_name -> google.protobuf.Duration + 1, // 6: temporal.server.chasm.lib.stream.proto.v1.StreamState.ProducersEntry.value:type_name -> temporal.server.chasm.lib.stream.proto.v1.ProducerCursor + 2, // 7: temporal.server.chasm.lib.stream.proto.v1.StreamState.ConsumersEntry.value:type_name -> temporal.server.chasm.lib.stream.proto.v1.ConsumerCursor + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_init() } +func file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_init() { + if File_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc), len(file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc)), + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_goTypes, + DependencyIndexes: file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_depIdxs, + MessageInfos: file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes, + }.Build() + File_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto = out.File + file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_goTypes = nil + file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_depIdxs = nil +} diff --git a/chasm/lib/stream/gen/streampb/v1/tasks.go-helpers.pb.go b/chasm/lib/stream/gen/streampb/v1/tasks.go-helpers.pb.go new file mode 100644 index 00000000000..9644b7bbe02 --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/tasks.go-helpers.pb.go @@ -0,0 +1,80 @@ +// Code generated by protoc-gen-go-helpers. DO NOT EDIT. +package streampb + +import ( + "google.golang.org/protobuf/proto" +) + +// Marshal an object of type StreamRetentionTask to the protobuf v3 wire format +func (val *StreamRetentionTask) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamRetentionTask from the protobuf v3 wire format +func (val *StreamRetentionTask) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamRetentionTask) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamRetentionTask 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 *StreamRetentionTask) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamRetentionTask + switch t := that.(type) { + case *StreamRetentionTask: + that1 = t + case StreamRetentionTask: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type StreamNotifyConsumersTask to the protobuf v3 wire format +func (val *StreamNotifyConsumersTask) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamNotifyConsumersTask from the protobuf v3 wire format +func (val *StreamNotifyConsumersTask) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamNotifyConsumersTask) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamNotifyConsumersTask 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 *StreamNotifyConsumersTask) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamNotifyConsumersTask + switch t := that.(type) { + case *StreamNotifyConsumersTask: + that1 = t + case StreamNotifyConsumersTask: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} diff --git a/chasm/lib/stream/gen/streampb/v1/tasks.pb.go b/chasm/lib/stream/gen/streampb/v1/tasks.pb.go new file mode 100644 index 00000000000..2b944c7d24d --- /dev/null +++ b/chasm/lib/stream/gen/streampb/v1/tasks.pb.go @@ -0,0 +1,160 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// plugins: +// protoc-gen-go +// protoc +// source: temporal/server/chasm/lib/stream/proto/v1/tasks.proto + +package streampb + +import ( + reflect "reflect" + sync "sync" + unsafe "unsafe" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Fires at close_time plus retention. A closed stream stays readable until +// then, which is what removes the shutdown handshake the signal-based +// implementation forces on producers and consumers. +type StreamRetentionTask struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamRetentionTask) Reset() { + *x = StreamRetentionTask{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_tasks_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamRetentionTask) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamRetentionTask) ProtoMessage() {} + +func (x *StreamRetentionTask) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_tasks_proto_msgTypes[0] + 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 StreamRetentionTask.ProtoReflect.Descriptor instead. +func (*StreamRetentionTask) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_tasks_proto_rawDescGZIP(), []int{0} +} + +// Fires after an append that leaves a registered consumer behind. Appending +// never schedules a workflow task on its own, because a stream item is data an +// execution produced rather than a decision input to it. A workflow that +// subscribed is the exception: it asked to be told, and it cannot find out any +// other way, since nothing else it does would notice the stream moved. +type StreamNotifyConsumersTask struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamNotifyConsumersTask) Reset() { + *x = StreamNotifyConsumersTask{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_tasks_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamNotifyConsumersTask) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamNotifyConsumersTask) ProtoMessage() {} + +func (x *StreamNotifyConsumersTask) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_tasks_proto_msgTypes[1] + 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 StreamNotifyConsumersTask.ProtoReflect.Descriptor instead. +func (*StreamNotifyConsumersTask) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_tasks_proto_rawDescGZIP(), []int{1} +} + +var File_temporal_server_chasm_lib_stream_proto_v1_tasks_proto protoreflect.FileDescriptor + +const file_temporal_server_chasm_lib_stream_proto_v1_tasks_proto_rawDesc = "" + + "\n" + + "5temporal/server/chasm/lib/stream/proto/v1/tasks.proto\x12)temporal.server.chasm.lib.stream.proto.v1\"\x15\n" + + "\x13StreamRetentionTask\"\x1b\n" + + "\x19StreamNotifyConsumersTaskB>Z metadata = 2; + string topic = 3; + // Position within this topic. The global offset orders the whole stream; + // this lets a consumer reason about one topic without decoding the rest. + int64 topic_sequence = 4; + StreamMessageKind kind = 5; + + // Position in the whole stream, set on read and never stored. A consumer + // that resumes at message granularity needs it, and a topic-filtered read + // leaves gaps that make it underivable from the response alone. + int64 offset = 6; +} + +// One append is one batch, and one batch is one log node. The server stores +// this serialized and opaque; it decodes only to trim a partial first page or +// to apply a topic filter. +message StreamMessageBatch { + repeated StreamMessage messages = 1; +} diff --git a/chasm/lib/stream/proto/v1/request_response.proto b/chasm/lib/stream/proto/v1/request_response.proto new file mode 100644 index 00000000000..6a7cc7417f0 --- /dev/null +++ b/chasm/lib/stream/proto/v1/request_response.proto @@ -0,0 +1,364 @@ +syntax = "proto3"; + +package temporal.server.chasm.lib.stream.proto.v1; + +import "chasm/lib/stream/proto/v1/message.proto"; +import "chasm/lib/stream/proto/v1/stream_state.proto"; +import "temporal/api/common/v1/message.proto"; + +option go_package = "go.temporal.io/server/chasm/lib/stream/gen/streampb;streampb"; + +// The frontend-facing shapes are defined here rather than in the public API +// because streams have no public API yet. Keeping them in a nested +// frontend_request mirrors the other CHASM libraries, so promoting them later +// is a package move rather than a redesign. + +message CreateStreamInput { + string namespace = 1; + string stream_id = 2; + StreamLifecycle lifecycle = 3; +} + +message CreateStreamOutput { + string run_id = 1; +} + +message AddMessagesInput { + string namespace = 1; + string stream_id = 2; + // Optional. Supplying it skips resolving the stream's current run, which is + // a persistence lookup on every call. CreateStream returns it. + string run_id = 9; + repeated StreamMessage messages = 3; + + // Idempotency, all optional. Supply a producer identity and sequence, or an + // expected offset, or neither and accept at-least-once. + string producer_id = 4; + int64 sequence = 5; + // Guarded by use_expected_offset because proto3 optional is not supported + // by this repo's helper generator. + int64 expected_offset = 6; + bool use_expected_offset = 8; + + int64 owner_epoch = 7; +} + +message AddMessagesOutput { + int64 first_offset = 1; + int64 next_offset = 2; + int64 count = 3; + // True when a retry matched a recorded sequence and nothing was appended. + bool deduplicated = 4; +} + +message FinishWritingInput { + string namespace = 1; + string stream_id = 2; + string producer_id = 3; +} + +message FinishWritingOutput {} + +// Registers a Workflow as a consumer of a stream it owns. The cursor lands in +// the Workflow's own state, so from then on each of its Workflow Tasks carries +// the next range and records what it consumed. +message SubscribeWorkflowInput { + string namespace = 1; + string workflow_id = 2; + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + string owner_run_id = 6; + + // Name of the stream within the Workflow, for a stream it owns. + string stream_name = 3; + // Id of a standalone stream in another execution. Exactly one of this and + // stream_name is set. + string stream_id = 5; + // Where to start. Resolved here rather than at delivery, so the first + // recorded range starts from a fact instead of a reading. + int64 start_offset = 4; +} + +message SubscribeWorkflowOutput { + int64 start_offset = 1; +} + +message PollMessagesInput { + string namespace = 1; + string stream_id = 2; + // Optional, as on AddMessagesInput. + string run_id = 7; + int64 from_offset = 3; + int32 max_messages = 4; + // Filters by exact topic. Offsets are assigned over the unfiltered stream, so + // next_offset advances past filtered-out messages too. + repeated string topics = 5; + + // When set and the reader is caught up, block until something arrives, the + // stream closes, or the server's long-poll timeout elapses. A timeout returns + // an empty response rather than an error, so the caller simply polls again. + bool wait_new_messages = 6; +} + +message PollMessagesOutput { + repeated StreamMessage messages = 1; + int64 next_offset = 2; + int64 head_offset = 3; + bool closed = 4; + temporal.api.common.v1.Payload close_reason = 5; +} + +message DescribeStreamInput { + string namespace = 1; + string stream_id = 2; +} + +// A stream a workflow owns lives inside that workflow's execution, so it has +// no standalone id to address it by. It is named by its owner and its name +// instead, and routed on the owner. +message PollWorkflowMessagesInput { + string namespace = 1; + string workflow_id = 2; + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + string owner_run_id = 8; + + // Empty means the workflow's default output stream. + string stream_name = 3; + int64 from_offset = 4; + int32 max_messages = 5; + // Filters as on PollMessagesInput. + repeated string topics = 6; + bool wait_new_messages = 7; +} + +message DescribeWorkflowStreamInput { + string namespace = 1; + string workflow_id = 2; + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + string owner_run_id = 4; + + string stream_name = 3; +} + +// Appending to a stream a workflow owns, from outside that workflow. The +// workflow's own publishes ride its Workflow Task instead. +message AddWorkflowMessagesInput { + string namespace = 1; + string workflow_id = 2; + // Optional. Pins to one run, so a caller that has continued as new is not + // silently redirected to the successor's stream, which starts empty and at + // offset zero. Empty means whichever run is current. + string owner_run_id = 7; + + // Empty means the workflow's default output stream. + string stream_name = 3; + repeated StreamMessage messages = 4; + // Optional idempotency, as on AddMessagesInput. + string producer_id = 5; + int64 sequence = 6; +} + +message DescribeStreamOutput { + StreamState state = 1; +} + +message CloseStreamInput { + string namespace = 1; + string stream_id = 2; + temporal.api.common.v1.Payload reason = 3; +} + +message CloseStreamOutput {} + +message TruncateStreamInput { + string namespace = 1; + string stream_id = 2; + int64 new_base_offset = 3; +} + +message TruncateStreamOutput {} + +message DeleteStreamInput { + string namespace = 1; + string stream_id = 2; +} + +message DeleteStreamOutput {} + +message CreateStreamRequest { + string namespace_id = 1; + CreateStreamInput frontend_request = 2; +} +message CreateStreamResponse { + CreateStreamOutput frontend_response = 1; +} + +message AddMessagesRequest { + string namespace_id = 1; + AddMessagesInput frontend_request = 2; +} +message AddMessagesResponse { + AddMessagesOutput frontend_response = 1; +} + +message FinishWritingRequest { + string namespace_id = 1; + FinishWritingInput frontend_request = 2; +} +message FinishWritingResponse { + FinishWritingOutput frontend_response = 1; +} + +message SubscribeWorkflowRequest { + string namespace_id = 1; + SubscribeWorkflowInput frontend_request = 2; +} +message SubscribeWorkflowResponse { + SubscribeWorkflowOutput frontend_response = 1; +} + +message PollMessagesRequest { + string namespace_id = 1; + PollMessagesInput frontend_request = 2; +} +message PollMessagesResponse { + PollMessagesOutput frontend_response = 1; +} + +message DescribeStreamRequest { + string namespace_id = 1; + DescribeStreamInput frontend_request = 2; +} +message DescribeStreamResponse { + DescribeStreamOutput frontend_response = 1; +} + +message PollWorkflowMessagesRequest { + string namespace_id = 1; + PollWorkflowMessagesInput frontend_request = 2; +} +message PollWorkflowMessagesResponse { + PollMessagesOutput frontend_response = 1; +} + +message DescribeWorkflowStreamRequest { + string namespace_id = 1; + DescribeWorkflowStreamInput frontend_request = 2; +} +message DescribeWorkflowStreamResponse { + DescribeStreamOutput frontend_response = 1; +} + +// Registering a consumer on a stream in another execution. Split out from +// SubscribeWorkflow because the two halves live on different shards: the pin +// goes on the stream, the cursor goes on the consuming workflow, and a handler +// can only reach the shard it was routed to. +message RegisterStreamConsumerInput { + string namespace = 1; + string stream_id = 2; + // The workflow that will consume, which names the pin. + string consumer_workflow_id = 3; + // Negative means from wherever the stream is when the pin is taken. Resolved + // here, where the frontier is, and returned so the cursor records a fact. + int64 start_offset = 4; +} + +message RegisterStreamConsumerOutput { + int64 start_offset = 1; + // What the consumer needs to address the log, all decided by the stream. + string collection_id = 2; + int64 bucket_size = 3; + int64 known_head = 4; +} + +// Telling one consumer that the frontier moved. Routed to the consumer, which +// is not where the stream lives. +message AdvanceConsumerHeadInput { + string namespace = 1; + string workflow_id = 2; + // The run that subscribed. A successor from continue-as-new holds no cursor + // for this stream, so pushing the frontier at it would land nowhere. + string owner_run_id = 5; + string stream_id = 3; + int64 head_offset = 4; +} + +message AdvanceConsumerHeadOutput {} + +message AddWorkflowMessagesRequest { + string namespace_id = 1; + AddWorkflowMessagesInput frontend_request = 2; +} +message AddWorkflowMessagesResponse { + AddMessagesOutput frontend_response = 1; +} + +message RegisterStreamConsumerRequest { + string namespace_id = 1; + RegisterStreamConsumerInput frontend_request = 2; +} +message RegisterStreamConsumerResponse { + RegisterStreamConsumerOutput frontend_response = 1; +} + +message AdvanceConsumerHeadRequest { + string namespace_id = 1; + AdvanceConsumerHeadInput frontend_request = 2; +} +message AdvanceConsumerHeadResponse { + AdvanceConsumerHeadOutput frontend_response = 1; +} + +message CloseStreamRequest { + string namespace_id = 1; + CloseStreamInput frontend_request = 2; +} +message CloseStreamResponse { + CloseStreamOutput frontend_response = 1; +} + +message TruncateStreamRequest { + string namespace_id = 1; + TruncateStreamInput frontend_request = 2; +} +message TruncateStreamResponse { + TruncateStreamOutput frontend_response = 1; +} + +message ListStreamsInput { + string namespace = 1; + int32 page_size = 2; + bytes next_page_token = 3; + string query = 4; +} + +message StreamListEntry { + string stream_id = 1; + string run_id = 2; +} + +message ListStreamsOutput { + repeated StreamListEntry streams = 1; + bytes next_page_token = 2; +} + +message ListStreamsRequest { + string namespace_id = 1; + ListStreamsInput frontend_request = 2; +} +message ListStreamsResponse { + ListStreamsOutput frontend_response = 1; +} + +message DeleteStreamRequest { + string namespace_id = 1; + DeleteStreamInput frontend_request = 2; +} +message DeleteStreamResponse { + DeleteStreamOutput frontend_response = 1; +} diff --git a/chasm/lib/stream/proto/v1/service.proto b/chasm/lib/stream/proto/v1/service.proto new file mode 100644 index 00000000000..bdd0a918551 --- /dev/null +++ b/chasm/lib/stream/proto/v1/service.proto @@ -0,0 +1,92 @@ +syntax = "proto3"; + +package temporal.server.chasm.lib.stream.proto.v1; + +import "chasm/lib/stream/proto/v1/request_response.proto"; +import "temporal/server/api/common/v1/api_category.proto"; +import "temporal/server/api/routing/v1/extension.proto"; + +option go_package = "go.temporal.io/server/chasm/lib/stream/gen/streampb;streampb"; + +service StreamService { + rpc CreateStream(CreateStreamRequest) returns (CreateStreamResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc AddMessages(AddMessagesRequest) returns (AddMessagesResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc FinishWriting(FinishWritingRequest) returns (FinishWritingResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc SubscribeWorkflow(SubscribeWorkflowRequest) returns (SubscribeWorkflowResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.workflow_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc PollMessages(PollMessagesRequest) returns (PollMessagesResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_LONG_POLL; + } + + rpc DescribeStream(DescribeStreamRequest) returns (DescribeStreamResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + // Routed on the owner, because the stream it reads has no id of its own. + rpc PollWorkflowMessages(PollWorkflowMessagesRequest) returns (PollWorkflowMessagesResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.workflow_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_LONG_POLL; + } + + rpc DescribeWorkflowStream(DescribeWorkflowStreamRequest) returns (DescribeWorkflowStreamResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.workflow_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc AddWorkflowMessages(AddWorkflowMessagesRequest) returns (AddWorkflowMessagesResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.workflow_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + // Internal. History calls these on itself to reach a shard it does not own, + // which is the only way a step that spans two executions can work on a + // cluster with more than one history host. + rpc RegisterStreamConsumer(RegisterStreamConsumerRequest) returns (RegisterStreamConsumerResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc AdvanceConsumerHead(AdvanceConsumerHeadRequest) returns (AdvanceConsumerHeadResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.workflow_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc CloseStream(CloseStreamRequest) returns (CloseStreamResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc TruncateStream(TruncateStreamRequest) returns (TruncateStreamResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + // Served on the frontend only: it queries visibility rather than a stream, + // so there is no business ID to route on and nothing for a shard to answer. + rpc ListStreams(ListStreamsRequest) returns (ListStreamsResponse) { + option (temporal.server.api.routing.v1.routing).random = true; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + + rpc DeleteStream(DeleteStreamRequest) returns (DeleteStreamResponse) { + option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } +} diff --git a/chasm/lib/stream/proto/v1/stream_state.proto b/chasm/lib/stream/proto/v1/stream_state.proto new file mode 100644 index 00000000000..81c878947a6 --- /dev/null +++ b/chasm/lib/stream/proto/v1/stream_state.proto @@ -0,0 +1,123 @@ +syntax = "proto3"; + +package temporal.server.chasm.lib.stream.proto.v1; + +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "temporal/api/common/v1/message.proto"; + +option go_package = "go.temporal.io/server/chasm/lib/stream/gen/streampb;streampb"; + +// Size is O(producers + consumers), never O(messages). Payload bytes live in +// the log, not here, which is what keeps this off the CHASM partial-read path. +message StreamState { + // Visibility frontier. Readers never observe an offset at or past this. + int64 head_offset = 1; + // Truncation floor. Offsets below this are gone. + int64 base_offset = 2; + // Chains log nodes so a stale node from an abandoned append is rejected on + + bool closed = 4; + temporal.api.common.v1.Payload close_reason = 5; + + // Bumped on ownership change so a stale producer's write fails. + int64 owner_epoch = 6; + + // Immutable once set. Offsets roll to a new log tree every bucket_size so no + // single storage partition grows with the stream. + int64 bucket_size = 7; + + // Identity of the log this stream writes to. Bucket trees are derived from + // it, so there is no per-bucket index to store. + string collection_id = 8; + + map producers = 9; + map consumers = 10; + + StreamLifecycle lifecycle = 11; + + // Set when a successor run takes ownership, so an in-flight poll can follow + // the chain instead of stalling on a superseded run. + string redirect_run_id = 12; + + // Wall-clock close time, used to schedule retention deletion. + google.protobuf.Timestamp close_time = 13; +} + +message ProducerCursor { + int64 seq = 1; + int64 first_offset = 2; + int64 count = 3; + // Distinguishes a genuine retry from a client reusing a sequence with + // different content, which must be rejected rather than deduplicated. + bytes content_hash = 4; + // Set by FinishWriting. Ends this producer's writes without closing the + // stream for anyone else. + bool fenced = 5; +} + +message ConsumerCursor { + string workflow_id = 1; + string run_id = 2; + // How far this consumer has read. Used to decide whether it needs waking, + // not to decide what the stream may drop. + int64 offset = 3; + bool active = 4; + // Set when the consumer is a workflow in another execution, which is the + // only case that has to be told the frontier moved. A workflow consuming a + // stream it owns sees that while closing its own transaction. + bool external = 5; + // The oldest offset this consumer's History still depends on. + // + // It is where the subscription started, not where it has read to. A range + // this consumer already consumed is recorded in its History and has to be + // re-readable for the workflow to replay, so bytes below the read position + // are exactly the ones a replay needs most. It stays put while the consumer + // is active and is released when it deregisters. + int64 replay_floor = 6; +} + +// A consuming Workflow's position in a stream. This lives in the consuming +// Workflow's own state rather than on the stream, so advancing it commits in +// the same transaction as the WorkflowTaskCompleted event that records the +// range. Keeping it on the stream would make the advance a cross-execution +// write, and a crash between the two would either redeliver or skip. +message WorkflowStreamCursor { + string stream_id = 1; + // Enough to address the log without reading the stream component first. + string collection_id = 2; + int64 bucket_size = 3; + + // Next offset to deliver. + int64 offset = 4; + + // The stream's frontier as of the last delivery. A workflow consuming a + // stream in another execution cannot read the real frontier while closing its + // own transaction, so this is what tells it a capped slice left more behind. + int64 known_head = 8; + + // Set when the stream lives in another execution, which decides whether the + // frontier is read locally or taken from known_head. + bool external = 9; + + // The range attached to the Workflow Task currently in flight. Recorded on + // the event that closes that task, then folded into offset. An empty range + // is still recorded: a task where the subscription saw nothing is a fact + // replay has to reproduce. + int64 pending_from = 5; + int64 pending_to = 6; + bool has_pending = 7; + + // Where this subscription began reading. Retained separately from offset, + // which advances, because replay has to tell "committed nothing yet" apart + // from "the events recording what was committed are not in this page". + int64 start_offset = 10; +} + +message StreamLifecycle { + // How long a closed stream stays readable before it is deleted. + google.protobuf.Duration retention = 1; + // Cap on readable messages. Older whole buckets are reclaimed once the floor + // passes them, so a capped stream has bounded storage. + int64 max_items = 2; +} diff --git a/chasm/lib/stream/proto/v1/tasks.proto b/chasm/lib/stream/proto/v1/tasks.proto new file mode 100644 index 00000000000..c4b8e5e6c6a --- /dev/null +++ b/chasm/lib/stream/proto/v1/tasks.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package temporal.server.chasm.lib.stream.proto.v1; + +option go_package = "go.temporal.io/server/chasm/lib/stream/gen/streampb;streampb"; + +// Fires at close_time plus retention. A closed stream stays readable until +// then, which is what removes the shutdown handshake the signal-based +// implementation forces on producers and consumers. +message StreamRetentionTask {} + +// Fires after an append that leaves a registered consumer behind. Appending +// never schedules a workflow task on its own, because a stream item is data an +// execution produced rather than a decision input to it. A workflow that +// subscribed is the exception: it asked to be told, and it cannot find out any +// other way, since nothing else it does would notice the stream moved. +message StreamNotifyConsumersTask {} diff --git a/cmd/tools/getproto/files.go b/cmd/tools/getproto/files.go index 38b6ef67622..04b99e22706 100644 --- a/cmd/tools/getproto/files.go +++ b/cmd/tools/getproto/files.go @@ -27,6 +27,7 @@ import ( rules "go.temporal.io/api/rules/v1" schedule "go.temporal.io/api/schedule/v1" sdk "go.temporal.io/api/sdk/v1" + stream "go.temporal.io/api/stream/v1" taskqueue "go.temporal.io/api/taskqueue/v1" update "go.temporal.io/api/update/v1" version "go.temporal.io/api/version/v1" @@ -91,6 +92,7 @@ func init() { importMap["temporal/api/sdk/v1/task_complete_metadata.proto"] = sdk.File_temporal_api_sdk_v1_task_complete_metadata_proto importMap["temporal/api/sdk/v1/user_metadata.proto"] = sdk.File_temporal_api_sdk_v1_user_metadata_proto importMap["temporal/api/sdk/v1/worker_config.proto"] = sdk.File_temporal_api_sdk_v1_worker_config_proto + importMap["temporal/api/stream/v1/message.proto"] = stream.File_temporal_api_stream_v1_message_proto importMap["temporal/api/taskqueue/v1/message.proto"] = taskqueue.File_temporal_api_taskqueue_v1_message_proto importMap["temporal/api/update/v1/message.proto"] = update.File_temporal_api_update_v1_message_proto importMap["temporal/api/version/v1/message.proto"] = version.File_temporal_api_version_v1_message_proto diff --git a/cmd/tools/protogen/main.go b/cmd/tools/protogen/main.go index cf2c00b2e6e..251f2a26adb 100644 --- a/cmd/tools/protogen/main.go +++ b/cmd/tools/protogen/main.go @@ -165,16 +165,65 @@ func newGenerator() (*generator, error) { return &gen, nil } +// removeExistingGenDirs clears out the previous run's output so a proto that +// was deleted does not leave its Go behind. +// +// Only generated files go. A gen package sometimes needs a hand-written file +// beside the generated ones, because a method has to be declared in the same +// package as the type it is on, and wiping the directory deleted those without +// saying so. Empty directories are then pruned, which is what removing a proto +// used to rely on. func (g *generator) removeExistingGenDirs() error { for _, dir := range g.chasmLibDirs { genDir := filepath.Join(dir, "gen") - if err := os.RemoveAll(genDir); err != nil { - return fmt.Errorf("error removing directory %s: %w", genDir, err) + if !exists(genDir) { + continue + } + if err := filepath.Walk(genDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || !strings.HasSuffix(info.Name(), ".pb.go") { + return nil + } + return os.Remove(path) + }); err != nil { + return fmt.Errorf("error removing generated files under %s: %w", genDir, err) + } + if err := pruneEmptyDirs(genDir); err != nil { + return fmt.Errorf("error pruning empty directories under %s: %w", genDir, err) } } return nil } +// pruneEmptyDirs removes dir and any descendant left with nothing in it, +// deepest first. +func pruneEmptyDirs(dir string) error { + entries, err := os.ReadDir(dir) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + for _, entry := range entries { + if entry.IsDir() { + if err := pruneEmptyDirs(filepath.Join(dir, entry.Name())); err != nil { + return err + } + } + } + remaining, err := os.ReadDir(dir) + if err != nil { + return err + } + if len(remaining) == 0 { + return os.Remove(dir) + } + return nil +} + func (g *generator) backupProtos() error { if exists(g.protoOut) { if err := os.Rename(g.protoOut, g.protoBackup); err != nil { diff --git a/go.mod b/go.mod index 7a9d0965bba..b103fe08665 100644 --- a/go.mod +++ b/go.mod @@ -239,3 +239,5 @@ require ( ) tool golang.org/x/perf/cmd/benchstat + +replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260914225835-6facdf1d8f4d diff --git a/go.sum b/go.sum index 95797622c25..e974110cecf 100644 --- a/go.sum +++ b/go.sum @@ -321,6 +321,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/moedash/api-go v1.63.6-0.20260914225835-6facdf1d8f4d h1:4nOuHYutFB4EApEYChFZBkT0tmY2nTWcQCVXFCSMMis= +github.com/moedash/api-go v1.63.6-0.20260914225835-6facdf1d8f4d/go.mod h1:SrlW2JMwVlDP4nRWSNznUFqnSHd+YeMDS1BkYo63HCQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -479,8 +481,6 @@ go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0 h1:R go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0/go.mod h1:I89cynRj8y+383o7tEQVg2SVA6SRgDVIouWPUVXjx0U= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0 h1:CQvJSldHRUN6Z8jsUeYv8J0lXRvygALXIzsmAeCcZE0= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0/go.mod h1:xSQ+mEfJe/GjK1LXEyVOoSI1N9JV9ZI923X5kup43W4= -go.temporal.io/api v1.63.5 h1:c11+kPYHkXXL3UiShPdbMD+xtvqGsbTibUA9ypmiCa4= -go.temporal.io/api v1.63.5/go.mod h1:SrlW2JMwVlDP4nRWSNznUFqnSHd+YeMDS1BkYo63HCQ= go.temporal.io/auto-scaled-workers v0.0.0-20260811170210-91f6fe1d10ab h1:99wXW0317BBi49d6xgMdA0EZtvA+xbBUWV4HsTEGEcg= go.temporal.io/auto-scaled-workers v0.0.0-20260811170210-91f6fe1d10ab/go.mod h1:hhHijO9XRPIkAflLJJHix61M9FzbRPqk8fSydkcLkqw= go.temporal.io/sdk v1.44.0 h1:suitPDukX74rW3/N1FqvEbZTZVJJsxMKhv0KMa/j7pU= 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 de813adcf43..811b0dcc07c 100644 --- a/proto/internal/temporal/server/api/historyservice/v1/request_response.proto +++ b/proto/internal/temporal/server/api/historyservice/v1/request_response.proto @@ -14,6 +14,7 @@ import "temporal/api/history/v1/message.proto"; import "temporal/api/nexus/v1/message.proto"; import "temporal/api/protocol/v1/message.proto"; import "temporal/api/query/v1/message.proto"; +import "temporal/api/stream/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; import "temporal/api/workflow/v1/message.proto"; import "temporal/api/workflowservice/v1/request_response.proto"; @@ -301,6 +302,10 @@ message RecordWorkflowTaskStartedResponse { // as raw_history_bytes (field 21) will be the only field used. temporal.api.history.v1.History raw_history = 20 [deprecated = true]; repeated bytes raw_history_bytes = 21; + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + repeated temporal.api.stream.v1.StreamSlice stream_slices = 22; } // RecordWorkflowTaskStartedResponseWithRawHistory is wire-compatible with RecordWorkflowTaskStartedResponse. @@ -345,6 +350,10 @@ message RecordWorkflowTaskStartedResponseWithRawHistory { // instead of a proto-decoded History. This avoids matching service having to decode history. repeated bytes raw_history = 20 [deprecated = true]; repeated bytes raw_history_bytes = 21; + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + repeated temporal.api.stream.v1.StreamSlice stream_slices = 22; } message RecordActivityTaskStartedRequest { diff --git a/proto/internal/temporal/server/api/matchingservice/v1/request_response.proto b/proto/internal/temporal/server/api/matchingservice/v1/request_response.proto index 33bae6d5c71..afe058cef5b 100644 --- a/proto/internal/temporal/server/api/matchingservice/v1/request_response.proto +++ b/proto/internal/temporal/server/api/matchingservice/v1/request_response.proto @@ -12,6 +12,7 @@ import "temporal/api/history/v1/message.proto"; import "temporal/api/nexus/v1/message.proto"; import "temporal/api/protocol/v1/message.proto"; import "temporal/api/query/v1/message.proto"; +import "temporal/api/stream/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; import "temporal/api/worker/v1/message.proto"; import "temporal/api/workflowservice/v1/request_response.proto"; @@ -62,6 +63,10 @@ message PollWorkflowTaskQueueResponse { // Raw history bytes sent from matching service when history.sendRawHistoryBetweenInternalServices is enabled. // Matching client will deserialize this to History when it receives the response. temporal.api.history.v1.History raw_history = 22; + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + repeated temporal.api.stream.v1.StreamSlice stream_slices = 23; } // PollWorkflowTaskQueueResponseWithRawHistory is wire-compatible with PollWorkflowTaskQueueResponse. @@ -112,6 +117,10 @@ message PollWorkflowTaskQueueResponseWithRawHistory { // When matching client deserializes this to PollWorkflowTaskQueueResponse, this field // will be automatically deserialized to the raw_history field as History. repeated bytes raw_history = 22; + // Slices of the streams this Workflow is consuming, each covering the offset + // range the task may deliver. Payloads ride here and never enter History; + // only the consumed range is recorded, on WorkflowTaskCompleted. + repeated temporal.api.stream.v1.StreamSlice stream_slices = 23; } message PollActivityTaskQueueRequest { From 4e9314bc8b40a883a442d74b797db27e3edc8894 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 14 Sep 2026 15:59:55 -0700 Subject: [PATCH 02/47] Added the stream log store and kept the scavenger off it. --- common/metrics/metric_defs.go | 6 + common/persistence/cassandra/history_store.go | 99 +++++ common/persistence/data_interfaces.go | 24 ++ common/persistence/data_interfaces_mock.go | 43 ++ common/persistence/history_manager.go | 26 ++ common/persistence/mock/store_mock.go | 43 ++ common/persistence/persistence_interface.go | 52 +++ .../persistence/persistence_metric_clients.go | 42 ++ .../persistence_rate_limited_clients.go | 33 ++ .../persistence_retryable_clients.go | 37 ++ common/persistence/sql/history_store.go | 83 ++++ .../persistence/sql/sqlplugin/interfaces.go | 1 + .../sql/sqlplugin/mysql/stream_log.go | 64 +++ .../sql/sqlplugin/postgresql/stream_log.go | 65 +++ .../sql/sqlplugin/sqlite/stream_log.go | 72 ++++ .../persistence/sql/sqlplugin/stream_log.go | 66 +++ .../tests/history_store_stream_log.go | 385 ++++++++++++++++++ schema/cassandra/temporal/schema.cql | 15 + .../versioned/v1.10/add_stream_log.cql | 14 + .../temporal/versioned/v1.10/manifest.json | 6 +- schema/sqlite/v3/temporal/schema.sql | 12 + .../versioned/v0.10/add_stream_log.sql | 12 + .../v3/temporal/versioned/v0.10/manifest.json | 4 +- service/worker/scanner/history/scavenger.go | 12 + .../worker/scanner/history/scavenger_test.go | 58 +++ 25 files changed, 1270 insertions(+), 4 deletions(-) create mode 100644 common/persistence/sql/sqlplugin/mysql/stream_log.go create mode 100644 common/persistence/sql/sqlplugin/postgresql/stream_log.go create mode 100644 common/persistence/sql/sqlplugin/sqlite/stream_log.go create mode 100644 common/persistence/sql/sqlplugin/stream_log.go create mode 100644 common/persistence/tests/history_store_stream_log.go create mode 100644 schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql create mode 100644 schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql diff --git a/common/metrics/metric_defs.go b/common/metrics/metric_defs.go index 3b845ef2358..835400d996f 100644 --- a/common/metrics/metric_defs.go +++ b/common/metrics/metric_defs.go @@ -98,6 +98,12 @@ const ( PersistenceAppendHistoryNodesScope = "AppendHistoryNodes" // PersistenceAppendRawHistoryNodesScope tracks AppendRawHistoryNodes calls made by service to persistence layer PersistenceAppendRawHistoryNodesScope = "AppendRawHistoryNodes" + // PersistenceAppendStreamLogScope tracks AppendStreamLog calls made by service to persistence layer + PersistenceAppendStreamLogScope = "AppendStreamLog" + // PersistenceReadStreamLogScope tracks ReadStreamLog calls made by service to persistence layer + PersistenceReadStreamLogScope = "ReadStreamLog" + // PersistenceDeleteStreamLogBucketScope tracks DeleteStreamLogBucket calls made by service to persistence layer + PersistenceDeleteStreamLogBucketScope = "DeleteStreamLogBucket" // PersistenceReadHistoryBranchScope tracks ReadHistoryBranch calls made by service to persistence layer PersistenceReadHistoryBranchScope = "ReadHistoryBranch" // PersistenceReadHistoryBranchReverseScope tracks ReadHistoryBranchReverse calls made by service to persistence layer diff --git a/common/persistence/cassandra/history_store.go b/common/persistence/cassandra/history_store.go index a2069e5a5f4..9a5a5c3a538 100644 --- a/common/persistence/cassandra/history_store.go +++ b/common/persistence/cassandra/history_store.go @@ -429,3 +429,102 @@ func convertTimeoutError(err error) error { } return err } + +const ( + // Upsert by nature in Cassandra, which is exactly the semantics wanted: the + // key is the offset a batch starts at, so a retry replaces rather than + // competes. There is no transaction-id chain to order writers against. + templateUpsertStreamLog = `INSERT INTO stream_log (` + + `shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) ` + + `VALUES (?, ?, ?, ?, ?, ?, ?, ?) ` + + // Two reads rather than a subquery, which Cassandra has no notion of. The + // first is a reverse slice of one row to find the batch containing the + // requested offset; the second reads forward from there. + templateSelectStreamLogFloor = `SELECT start_offset FROM stream_log ` + + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` + + `AND start_offset <= ? ORDER BY start_offset DESC LIMIT 1 ` + + templateSelectStreamLog = `SELECT start_offset, data, data_encoding FROM stream_log ` + + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` + + `AND start_offset >= ? AND start_offset < ? ` + + templateDeleteStreamLogBucket = `DELETE FROM stream_log ` + + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` +) + +// AppendStreamLog writes one batch of a stream log. +func (h *HistoryStore) AppendStreamLog( + ctx context.Context, + request *p.InternalAppendStreamLogRequest, +) error { + query := h.Session.Query(templateUpsertStreamLog, + request.ShardID, + request.NamespaceID, + request.CollectionID, + request.Bucket, + request.StartOffset, + request.NextOffset, + request.Node.Data, + request.Node.EncodingType.String(), + ).WithContext(ctx) + if err := query.Exec(); err != nil { + return gocql.ConvertError("AppendStreamLog", err) + } + return nil +} + +// ReadStreamLog returns the batches covering the requested range, beginning +// with the batch that contains MinOffset. +func (h *HistoryStore) ReadStreamLog( + ctx context.Context, + request *p.InternalReadStreamLogRequest, +) (*p.InternalReadStreamLogResponse, error) { + from := request.MinOffset + var floor int64 + err := h.Session.Query(templateSelectStreamLogFloor, + request.ShardID, request.NamespaceID, request.CollectionID, request.Bucket, request.MinOffset, + ).WithContext(ctx).Scan(&floor) + switch { + case err == nil: + from = floor + case gocql.IsNotFoundError(err): + // Nothing at or below, so the range starts wherever it starts. + default: + return nil, gocql.ConvertError("ReadStreamLog", err) + } + + iter := h.Session.Query(templateSelectStreamLog, + request.ShardID, request.NamespaceID, request.CollectionID, request.Bucket, + from, request.MaxOffset, + ).WithContext(ctx).PageSize(request.PageSize).Iter() + + resp := &p.InternalReadStreamLogResponse{} + var startOffset int64 + var data []byte + var encoding string + for iter.Scan(&startOffset, &data, &encoding) { + resp.Batches = append(resp.Batches, p.NewDataBlob(data, encoding)) + resp.StartOffsets = append(resp.StartOffsets, startOffset) + data = nil + } + if err := iter.Close(); err != nil { + return nil, gocql.ConvertError("ReadStreamLog", err) + } + return resp, nil +} + +// DeleteStreamLogBucket drops a whole bucket, which on Cassandra is one +// partition and so one tombstone rather than a row-by-row delete. +func (h *HistoryStore) DeleteStreamLogBucket( + ctx context.Context, + request *p.InternalDeleteStreamLogBucketRequest, +) error { + query := h.Session.Query(templateDeleteStreamLogBucket, + request.ShardID, request.NamespaceID, request.CollectionID, request.Bucket, + ).WithContext(ctx) + if err := query.Exec(); err != nil { + return gocql.ConvertError("DeleteStreamLogBucket", err) + } + return nil +} diff --git a/common/persistence/data_interfaces.go b/common/persistence/data_interfaces.go index c818b447866..ab15e76cdb9 100644 --- a/common/persistence/data_interfaces.go +++ b/common/persistence/data_interfaces.go @@ -1162,6 +1162,13 @@ type ( AppendHistoryNodes(ctx context.Context, request *AppendHistoryNodesRequest) (*AppendHistoryNodesResponse, error) // AppendRawHistoryNodes add a node of raw histories to history node table AppendRawHistoryNodes(ctx context.Context, request *AppendRawHistoryNodesRequest) (*AppendHistoryNodesResponse, error) + + // Stream logs. Not history: an offset-addressed sequence a stream + // component owns, where a write is idempotent by the offset its batch + // starts at. + AppendStreamLog(ctx context.Context, request *InternalAppendStreamLogRequest) error + ReadStreamLog(ctx context.Context, request *InternalReadStreamLogRequest) (*InternalReadStreamLogResponse, error) + DeleteStreamLogBucket(ctx context.Context, request *InternalDeleteStreamLogBucketRequest) error // ReadHistoryBranch returns history node data for a branch ReadHistoryBranch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadHistoryBranchResponse, error) // ReadHistoryBranchByBatch returns history node data for a branch ByBatch @@ -1433,6 +1440,23 @@ func BuildHistoryGarbageCleanupInfo(namespaceID, workflowID, runID string) strin return fmt.Sprintf("%v:%v:%v", namespaceID, workflowID, runID) } +// NonExecutionGarbageCleanupInfoPrefix marks a history branch whose rows are +// not a workflow execution's history. +// +// The scavenger finds the execution named by a branch's cleanup info and +// deletes the branch when there is none, which is right for a workflow whose +// execution is gone and wrong for a branch that never had one. Anything +// storing rows in this table for its own purposes has to say so, or the +// scavenger reclaims live data. +const NonExecutionGarbageCleanupInfoPrefix = "non-execution:" + +// IsNonExecutionGarbageCleanupInfo reports whether a branch belongs to +// something other than a workflow execution, and so is not the scavenger's to +// collect. Whatever wrote it owns deleting it. +func IsNonExecutionGarbageCleanupInfo(info string) bool { + return strings.HasPrefix(info, NonExecutionGarbageCleanupInfoPrefix) +} + // SplitHistoryGarbageCleanupInfo returns workflow identity information func SplitHistoryGarbageCleanupInfo(info string) (namespaceID, workflowID, runID string, err error) { // Expect format: namespaceID:workflowID:runID, but workflowID may contain ':' so we diff --git a/common/persistence/data_interfaces_mock.go b/common/persistence/data_interfaces_mock.go index 9ba28072499..4f19aece8cf 100644 --- a/common/persistence/data_interfaces_mock.go +++ b/common/persistence/data_interfaces_mock.go @@ -214,6 +214,20 @@ func (mr *MockExecutionManagerMockRecorder) AppendRawHistoryNodes(ctx, request a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendRawHistoryNodes", reflect.TypeOf((*MockExecutionManager)(nil).AppendRawHistoryNodes), ctx, request) } +// AppendStreamLog mocks base method. +func (m *MockExecutionManager) AppendStreamLog(ctx context.Context, request *InternalAppendStreamLogRequest) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppendStreamLog", ctx, request) + ret0, _ := ret[0].(error) + return ret0 +} + +// AppendStreamLog indicates an expected call of AppendStreamLog. +func (mr *MockExecutionManagerMockRecorder) AppendStreamLog(ctx, request any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendStreamLog", reflect.TypeOf((*MockExecutionManager)(nil).AppendStreamLog), ctx, request) +} + // Close mocks base method. func (m *MockExecutionManager) Close() { m.ctrl.T.Helper() @@ -312,6 +326,20 @@ func (mr *MockExecutionManagerMockRecorder) DeleteReplicationTaskFromDLQ(ctx, re return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionManager)(nil).DeleteReplicationTaskFromDLQ), ctx, request) } +// DeleteStreamLogBucket mocks base method. +func (m *MockExecutionManager) DeleteStreamLogBucket(ctx context.Context, request *InternalDeleteStreamLogBucketRequest) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteStreamLogBucket", ctx, request) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteStreamLogBucket indicates an expected call of DeleteStreamLogBucket. +func (mr *MockExecutionManagerMockRecorder) DeleteStreamLogBucket(ctx, request any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStreamLogBucket", reflect.TypeOf((*MockExecutionManager)(nil).DeleteStreamLogBucket), ctx, request) +} + // DeleteWorkflowExecution mocks base method. func (m *MockExecutionManager) DeleteWorkflowExecution(ctx context.Context, request *DeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() @@ -576,6 +604,21 @@ func (mr *MockExecutionManagerMockRecorder) ReadRawHistoryBranch(ctx, request an return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadRawHistoryBranch", reflect.TypeOf((*MockExecutionManager)(nil).ReadRawHistoryBranch), ctx, request) } +// ReadStreamLog mocks base method. +func (m *MockExecutionManager) ReadStreamLog(ctx context.Context, request *InternalReadStreamLogRequest) (*InternalReadStreamLogResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadStreamLog", ctx, request) + ret0, _ := ret[0].(*InternalReadStreamLogResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadStreamLog indicates an expected call of ReadStreamLog. +func (mr *MockExecutionManagerMockRecorder) ReadStreamLog(ctx, request any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadStreamLog", reflect.TypeOf((*MockExecutionManager)(nil).ReadStreamLog), ctx, request) +} + // SetWorkflowExecution mocks base method. func (m *MockExecutionManager) SetWorkflowExecution(ctx context.Context, request *SetWorkflowExecutionRequest) (*SetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() diff --git a/common/persistence/history_manager.go b/common/persistence/history_manager.go index 9777739be73..33b46f67fd0 100644 --- a/common/persistence/history_manager.go +++ b/common/persistence/history_manager.go @@ -1138,3 +1138,29 @@ func (m *executionManagerImpl) serializeToken( return m.pagingTokenSerializer.Serialize(pagingToken) } + +// AppendStreamLog writes one batch of a stream log. A pass-through: there is no +// branch token to build, no chain to maintain and nothing to serialize, because +// the offset is the key and the blob is already encoded. +func (m *executionManagerImpl) AppendStreamLog( + ctx context.Context, + request *InternalAppendStreamLogRequest, +) error { + return m.persistence.AppendStreamLog(ctx, request) +} + +// ReadStreamLog returns the batches covering a range. +func (m *executionManagerImpl) ReadStreamLog( + ctx context.Context, + request *InternalReadStreamLogRequest, +) (*InternalReadStreamLogResponse, error) { + return m.persistence.ReadStreamLog(ctx, request) +} + +// DeleteStreamLogBucket drops a whole bucket. +func (m *executionManagerImpl) DeleteStreamLogBucket( + ctx context.Context, + request *InternalDeleteStreamLogBucketRequest, +) error { + return m.persistence.DeleteStreamLogBucket(ctx, request) +} diff --git a/common/persistence/mock/store_mock.go b/common/persistence/mock/store_mock.go index e78040bdaf3..397915450d6 100644 --- a/common/persistence/mock/store_mock.go +++ b/common/persistence/mock/store_mock.go @@ -908,6 +908,20 @@ func (mr *MockExecutionStoreMockRecorder) AppendHistoryNodes(ctx, request any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendHistoryNodes", reflect.TypeOf((*MockExecutionStore)(nil).AppendHistoryNodes), ctx, request) } +// AppendStreamLog mocks base method. +func (m *MockExecutionStore) AppendStreamLog(ctx context.Context, request *persistence.InternalAppendStreamLogRequest) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppendStreamLog", ctx, request) + ret0, _ := ret[0].(error) + return ret0 +} + +// AppendStreamLog indicates an expected call of AppendStreamLog. +func (mr *MockExecutionStoreMockRecorder) AppendStreamLog(ctx, request any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendStreamLog", reflect.TypeOf((*MockExecutionStore)(nil).AppendStreamLog), ctx, request) +} + // Close mocks base method. func (m *MockExecutionStore) Close() { m.ctrl.T.Helper() @@ -1019,6 +1033,20 @@ func (mr *MockExecutionStoreMockRecorder) DeleteReplicationTaskFromDLQ(ctx, requ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionStore)(nil).DeleteReplicationTaskFromDLQ), ctx, request) } +// DeleteStreamLogBucket mocks base method. +func (m *MockExecutionStore) DeleteStreamLogBucket(ctx context.Context, request *persistence.InternalDeleteStreamLogBucketRequest) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteStreamLogBucket", ctx, request) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteStreamLogBucket indicates an expected call of DeleteStreamLogBucket. +func (mr *MockExecutionStoreMockRecorder) DeleteStreamLogBucket(ctx, request any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStreamLogBucket", reflect.TypeOf((*MockExecutionStore)(nil).DeleteStreamLogBucket), ctx, request) +} + // DeleteWorkflowExecution mocks base method. func (m *MockExecutionStore) DeleteWorkflowExecution(ctx context.Context, request *persistence.DeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() @@ -1252,6 +1280,21 @@ func (mr *MockExecutionStoreMockRecorder) ReadHistoryBranch(ctx, request any) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadHistoryBranch", reflect.TypeOf((*MockExecutionStore)(nil).ReadHistoryBranch), ctx, request) } +// ReadStreamLog mocks base method. +func (m *MockExecutionStore) ReadStreamLog(ctx context.Context, request *persistence.InternalReadStreamLogRequest) (*persistence.InternalReadStreamLogResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadStreamLog", ctx, request) + ret0, _ := ret[0].(*persistence.InternalReadStreamLogResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadStreamLog indicates an expected call of ReadStreamLog. +func (mr *MockExecutionStoreMockRecorder) ReadStreamLog(ctx, request any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadStreamLog", reflect.TypeOf((*MockExecutionStore)(nil).ReadStreamLog), ctx, request) +} + // SetWorkflowExecution mocks base method. func (m *MockExecutionStore) SetWorkflowExecution(ctx context.Context, request *persistence.InternalSetWorkflowExecutionRequest) error { m.ctrl.T.Helper() diff --git a/common/persistence/persistence_interface.go b/common/persistence/persistence_interface.go index b95801ab8df..9793205c75c 100644 --- a/common/persistence/persistence_interface.go +++ b/common/persistence/persistence_interface.go @@ -160,6 +160,20 @@ type ( DeleteHistoryBranch(ctx context.Context, request *InternalDeleteHistoryBranchRequest) error // GetHistoryTreeContainingBranch returns all branch information of the tree containing the specified branch GetHistoryTreeContainingBranch(ctx context.Context, request *InternalGetHistoryTreeContainingBranchRequest) (*InternalGetHistoryTreeContainingBranchResponse, error) + // The below are the stream log APIs. A stream log is not history: it is + // an offset-addressed sequence a stream component owns, and a write is + // idempotent by the offset its batch starts at rather than ordered + // against other writers by a transaction id. + + // AppendStreamLog writes one batch, replacing any batch already at that + // offset. A retry of an append addresses the same row. + AppendStreamLog(ctx context.Context, request *InternalAppendStreamLogRequest) error + // ReadStreamLog returns the batches covering a range, beginning with the + // batch that contains the first offset asked for. + ReadStreamLog(ctx context.Context, request *InternalReadStreamLogRequest) (*InternalReadStreamLogResponse, error) + // DeleteStreamLogBucket drops a whole bucket, the unit of reclamation. + DeleteStreamLogBucket(ctx context.Context, request *InternalDeleteStreamLogBucketRequest) error + // GetAllHistoryTreeBranches returns all branches of all trees. // Note that branches may be skipped or duplicated across pages if there are branches created or deleted while // paginating through results. @@ -532,6 +546,44 @@ type ( Events *commonpb.DataBlob } + // InternalAppendStreamLogRequest writes one batch of a stream log. + InternalAppendStreamLogRequest struct { + ShardID int32 + NamespaceID string + CollectionID string + Bucket int64 + StartOffset int64 + NextOffset int64 + Node *commonpb.DataBlob + } + + // InternalReadStreamLogRequest reads the batches covering + // [MinOffset, MaxOffset) from one bucket. + InternalReadStreamLogRequest struct { + ShardID int32 + NamespaceID string + CollectionID string + Bucket int64 + MinOffset int64 + MaxOffset int64 + PageSize int + } + + // InternalReadStreamLogResponse returns batches with the offset each begins + // at, so a caller can drop the part of the first batch it did not ask for. + InternalReadStreamLogResponse struct { + Batches []*commonpb.DataBlob + StartOffsets []int64 + } + + // InternalDeleteStreamLogBucketRequest drops one bucket. + InternalDeleteStreamLogBucketRequest struct { + ShardID int32 + NamespaceID string + CollectionID string + Bucket int64 + } + // InternalAppendHistoryNodesRequest is used to append a batch of history nodes InternalAppendHistoryNodesRequest struct { // The raw branch token diff --git a/common/persistence/persistence_metric_clients.go b/common/persistence/persistence_metric_clients.go index b451295c0d4..fc971a2c850 100644 --- a/common/persistence/persistence_metric_clients.go +++ b/common/persistence/persistence_metric_clients.go @@ -1462,3 +1462,45 @@ func updateErrorMetric(handler metrics.Handler, logger log.Logger, operation str } } } + +// AppendStreamLog writes one batch of a stream log +func (p *executionPersistenceClient) AppendStreamLog( + ctx context.Context, + request *InternalAppendStreamLogRequest, +) (retErr error) { + caller := headers.GetCallerInfo(ctx).CallerName + startTime := time.Now().UTC() + defer func() { + p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr) + p.recordRequestMetrics(metrics.PersistenceAppendStreamLogScope, caller, time.Since(startTime), retErr) + }() + return p.persistence.AppendStreamLog(ctx, request) +} + +// ReadStreamLog returns the batches covering a range +func (p *executionPersistenceClient) ReadStreamLog( + ctx context.Context, + request *InternalReadStreamLogRequest, +) (_ *InternalReadStreamLogResponse, retErr error) { + caller := headers.GetCallerInfo(ctx).CallerName + startTime := time.Now().UTC() + defer func() { + p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr) + p.recordRequestMetrics(metrics.PersistenceReadStreamLogScope, caller, time.Since(startTime), retErr) + }() + return p.persistence.ReadStreamLog(ctx, request) +} + +// DeleteStreamLogBucket drops a whole bucket +func (p *executionPersistenceClient) DeleteStreamLogBucket( + ctx context.Context, + request *InternalDeleteStreamLogBucketRequest, +) (retErr error) { + caller := headers.GetCallerInfo(ctx).CallerName + startTime := time.Now().UTC() + defer func() { + p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr) + p.recordRequestMetrics(metrics.PersistenceDeleteStreamLogBucketScope, caller, time.Since(startTime), retErr) + }() + return p.persistence.DeleteStreamLogBucket(ctx, request) +} diff --git a/common/persistence/persistence_rate_limited_clients.go b/common/persistence/persistence_rate_limited_clients.go index 2cd8fbfb097..0bbefcd0abf 100644 --- a/common/persistence/persistence_rate_limited_clients.go +++ b/common/persistence/persistence_rate_limited_clients.go @@ -1184,3 +1184,36 @@ func ConstructHistoryTaskAPI( ) string { return baseAPI + taskCategory.Name() } + +// AppendStreamLog writes one batch of a stream log +func (p *executionRateLimitedPersistenceClient) AppendStreamLog( + ctx context.Context, + request *InternalAppendStreamLogRequest, +) error { + if err := allow(ctx, "AppendStreamLog", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil { + return err + } + return p.persistence.AppendStreamLog(ctx, request) +} + +// ReadStreamLog returns the batches covering a range +func (p *executionRateLimitedPersistenceClient) ReadStreamLog( + ctx context.Context, + request *InternalReadStreamLogRequest, +) (*InternalReadStreamLogResponse, error) { + if err := allow(ctx, "ReadStreamLog", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil { + return nil, err + } + return p.persistence.ReadStreamLog(ctx, request) +} + +// DeleteStreamLogBucket drops a whole bucket +func (p *executionRateLimitedPersistenceClient) DeleteStreamLogBucket( + ctx context.Context, + request *InternalDeleteStreamLogBucketRequest, +) error { + if err := allow(ctx, "DeleteStreamLogBucket", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil { + return err + } + return p.persistence.DeleteStreamLogBucket(ctx, request) +} diff --git a/common/persistence/persistence_retryable_clients.go b/common/persistence/persistence_retryable_clients.go index 6c413fbdb2d..e4533183e74 100644 --- a/common/persistence/persistence_retryable_clients.go +++ b/common/persistence/persistence_retryable_clients.go @@ -1260,3 +1260,40 @@ func (p *nexusEndpointRetryablePersistenceClient) DeleteNexusEndpoint( } return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) } + +// AppendStreamLog writes one batch of a stream log +func (p *executionRetryablePersistenceClient) AppendStreamLog( + ctx context.Context, + request *InternalAppendStreamLogRequest, +) error { + op := func(ctx context.Context) error { + return p.persistence.AppendStreamLog(ctx, request) + } + return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) +} + +// ReadStreamLog returns the batches covering a range +func (p *executionRetryablePersistenceClient) ReadStreamLog( + ctx context.Context, + request *InternalReadStreamLogRequest, +) (*InternalReadStreamLogResponse, error) { + var response *InternalReadStreamLogResponse + op := func(ctx context.Context) error { + var err error + response, err = p.persistence.ReadStreamLog(ctx, request) + return err + } + err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) + return response, err +} + +// DeleteStreamLogBucket drops a whole bucket +func (p *executionRetryablePersistenceClient) DeleteStreamLogBucket( + ctx context.Context, + request *InternalDeleteStreamLogBucketRequest, +) error { + op := func(ctx context.Context) error { + return p.persistence.DeleteStreamLogBucket(ctx, request) + } + return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) +} diff --git a/common/persistence/sql/history_store.go b/common/persistence/sql/history_store.go index f88582108d8..ae85799bcb0 100644 --- a/common/persistence/sql/history_store.go +++ b/common/persistence/sql/history_store.go @@ -475,3 +475,86 @@ func (m *sqlExecutionStore) GetHistoryTreeContainingBranch( TreeInfos: treeInfos, }, nil } + +// AppendStreamLog writes one batch of a stream log. +// +// A plain upsert, which is the whole point of this table. The key is the offset +// the batch starts at, so a retry of an append replaces the row it wrote before +// rather than competing with it, and there is no transaction-id chain for two +// writers to invert. What a reader may see is decided by the frontier the +// stream component commits, not by anything here. +func (m *sqlExecutionStore) AppendStreamLog( + ctx context.Context, + request *p.InternalAppendStreamLogRequest, +) error { + nsID, err := primitives.ParseUUID(request.NamespaceID) + if err != nil { + return err + } + _, err = m.DB.InsertIntoStreamLog(ctx, &sqlplugin.StreamLogRow{ + ShardID: request.ShardID, + NamespaceID: nsID, + CollectionID: request.CollectionID, + Bucket: request.Bucket, + StartOffset: request.StartOffset, + NextOffset: request.NextOffset, + Data: request.Node.Data, + DataEncoding: request.Node.EncodingType.String(), + }) + if err != nil { + return serviceerror.NewUnavailablef("AppendStreamLog: %v", err) + } + return nil +} + +// ReadStreamLog returns the batches covering the requested range, beginning +// with the batch that contains MinOffset rather than the one that starts at it. +func (m *sqlExecutionStore) ReadStreamLog( + ctx context.Context, + request *p.InternalReadStreamLogRequest, +) (*p.InternalReadStreamLogResponse, error) { + nsID, err := primitives.ParseUUID(request.NamespaceID) + if err != nil { + return nil, err + } + rows, err := m.DB.RangeSelectFromStreamLog(ctx, sqlplugin.StreamLogSelectFilter{ + ShardID: request.ShardID, + NamespaceID: nsID, + CollectionID: request.CollectionID, + Bucket: request.Bucket, + MinOffset: request.MinOffset, + MaxOffset: request.MaxOffset, + PageSize: request.PageSize, + }) + if err != nil { + return nil, serviceerror.NewUnavailablef("ReadStreamLog: %v", err) + } + + resp := &p.InternalReadStreamLogResponse{} + for _, row := range rows { + resp.Batches = append(resp.Batches, p.NewDataBlob(row.Data, row.DataEncoding)) + resp.StartOffsets = append(resp.StartOffsets, row.StartOffset) + } + return resp, nil +} + +// DeleteStreamLogBucket drops a whole bucket. +func (m *sqlExecutionStore) DeleteStreamLogBucket( + ctx context.Context, + request *p.InternalDeleteStreamLogBucketRequest, +) error { + nsID, err := primitives.ParseUUID(request.NamespaceID) + if err != nil { + return err + } + _, err = m.DB.DeleteFromStreamLog(ctx, sqlplugin.StreamLogDeleteFilter{ + ShardID: request.ShardID, + NamespaceID: nsID, + CollectionID: request.CollectionID, + Bucket: request.Bucket, + }) + if err != nil { + return serviceerror.NewUnavailablef("DeleteStreamLogBucket: %v", err) + } + return nil +} diff --git a/common/persistence/sql/sqlplugin/interfaces.go b/common/persistence/sql/sqlplugin/interfaces.go index 662845e9f67..fbff0ab02cf 100644 --- a/common/persistence/sql/sqlplugin/interfaces.go +++ b/common/persistence/sql/sqlplugin/interfaces.go @@ -53,6 +53,7 @@ type ( NexusEndpoints HistoryNode + StreamLog HistoryTree HistoryShard diff --git a/common/persistence/sql/sqlplugin/mysql/stream_log.go b/common/persistence/sql/sqlplugin/mysql/stream_log.go new file mode 100644 index 00000000000..c1bb005192e --- /dev/null +++ b/common/persistence/sql/sqlplugin/mysql/stream_log.go @@ -0,0 +1,64 @@ +package mysql + +import ( + "context" + "database/sql" + + "go.temporal.io/server/common/persistence/sql/sqlplugin" +) + +const ( + // Upsert: the key is the offset a batch starts at, so a rewrite of that + // offset is a retry of the same append rather than a competing one. + insertStreamLogQuery = `INSERT INTO stream_log + (shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) + VALUES (:shard_id, :namespace_id, :collection_id, :bucket, :start_offset, :next_offset, :data, :data_encoding) + ON DUPLICATE KEY UPDATE next_offset = VALUES(next_offset), + data = VALUES(data), + data_encoding = VALUES(data_encoding)` + + // The floor subquery finds the batch holding an arbitrary offset: the last + // row starting at or below it. + getStreamLogQuery = `SELECT shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding ` + + `FROM stream_log ` + + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` + + `AND start_offset >= COALESCE((SELECT MAX(start_offset) FROM stream_log ` + + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? AND start_offset <= ?), ?0) ` + + `AND start_offset < ?1 ORDER BY start_offset LIMIT ?2` + + deleteStreamLogQuery = `DELETE FROM stream_log ` + + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ?` +) + +func (mdb *db) InsertIntoStreamLog( + ctx context.Context, + row *sqlplugin.StreamLogRow, +) (sql.Result, error) { + return mdb.NamedExecContext(ctx, insertStreamLogQuery, row) +} + +func (mdb *db) RangeSelectFromStreamLog( + ctx context.Context, + filter sqlplugin.StreamLogSelectFilter, +) ([]sqlplugin.StreamLogRow, error) { + var rows []sqlplugin.StreamLogRow + if err := mdb.SelectContext(ctx, &rows, getStreamLogQuery, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, + filter.MinOffset, + filter.MinOffset, + filter.MaxOffset, + filter.PageSize, + ); err != nil { + return nil, err + } + return rows, nil +} + +func (mdb *db) DeleteFromStreamLog( + ctx context.Context, + filter sqlplugin.StreamLogDeleteFilter, +) (sql.Result, error) { + return mdb.ExecContext(ctx, deleteStreamLogQuery, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket) +} diff --git a/common/persistence/sql/sqlplugin/postgresql/stream_log.go b/common/persistence/sql/sqlplugin/postgresql/stream_log.go new file mode 100644 index 00000000000..acc2ea0a7e1 --- /dev/null +++ b/common/persistence/sql/sqlplugin/postgresql/stream_log.go @@ -0,0 +1,65 @@ +package postgresql + +import ( + "context" + "database/sql" + + "go.temporal.io/server/common/persistence/sql/sqlplugin" +) + +const ( + // Upsert: the key is the offset a batch starts at, so a rewrite of that + // offset is a retry of the same append rather than a competing one. + insertStreamLogQuery = `INSERT INTO stream_log + (shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) + VALUES (:shard_id, :namespace_id, :collection_id, :bucket, :start_offset, :next_offset, :data, :data_encoding) + ON CONFLICT (shard_id, namespace_id, collection_id, bucket, start_offset) + DO UPDATE SET next_offset = excluded.next_offset, + data = excluded.data, + data_encoding = excluded.data_encoding` + + // The floor subquery finds the batch holding an arbitrary offset: the last + // row starting at or below it. + getStreamLogQuery = `SELECT shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding ` + + `FROM stream_log ` + + `WHERE shard_id = $1 AND namespace_id = $2 AND collection_id = $3 AND bucket = $4 ` + + `AND start_offset >= COALESCE((SELECT MAX(start_offset) FROM stream_log ` + + `WHERE shard_id = $5 AND namespace_id = $6 AND collection_id = $7 AND bucket = $8 AND start_offset <= $9), $10) ` + + `AND start_offset < $11 ORDER BY start_offset LIMIT $12` + + deleteStreamLogQuery = `DELETE FROM stream_log ` + + `WHERE shard_id = $1 AND namespace_id = $2 AND collection_id = $3 AND bucket = $4` +) + +func (pdb *db) InsertIntoStreamLog( + ctx context.Context, + row *sqlplugin.StreamLogRow, +) (sql.Result, error) { + return pdb.NamedExecContext(ctx, insertStreamLogQuery, row) +} + +func (pdb *db) RangeSelectFromStreamLog( + ctx context.Context, + filter sqlplugin.StreamLogSelectFilter, +) ([]sqlplugin.StreamLogRow, error) { + var rows []sqlplugin.StreamLogRow + if err := pdb.SelectContext(ctx, &rows, getStreamLogQuery, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, + filter.MinOffset, + filter.MinOffset, + filter.MaxOffset, + filter.PageSize, + ); err != nil { + return nil, err + } + return rows, nil +} + +func (pdb *db) DeleteFromStreamLog( + ctx context.Context, + filter sqlplugin.StreamLogDeleteFilter, +) (sql.Result, error) { + return pdb.ExecContext(ctx, deleteStreamLogQuery, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket) +} diff --git a/common/persistence/sql/sqlplugin/sqlite/stream_log.go b/common/persistence/sql/sqlplugin/sqlite/stream_log.go new file mode 100644 index 00000000000..7979dd9d6d4 --- /dev/null +++ b/common/persistence/sql/sqlplugin/sqlite/stream_log.go @@ -0,0 +1,72 @@ +package sqlite + +import ( + "context" + "database/sql" + + "go.temporal.io/server/common/persistence/sql/sqlplugin" +) + +const ( + // Upsert, because the key is the offset a batch starts at and a rewrite of + // that offset is a retry of the same append rather than a competing one. + // This is the property the whole substrate exists for: there is nothing to + // order two writers against, so nothing to get wrong. + insertStreamLogQuery = `INSERT INTO stream_log + (shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) + VALUES (:shard_id, :namespace_id, :collection_id, :bucket, :start_offset, :next_offset, :data, :data_encoding) + ON CONFLICT (shard_id, namespace_id, collection_id, bucket, start_offset) + DO UPDATE SET next_offset = excluded.next_offset, + data = excluded.data, + data_encoding = excluded.data_encoding` + + // The floor subquery is what lets a reader ask for an arbitrary offset. A + // batch covers a range, so the row holding an offset is the last one that + // starts at or below it, and only the store can find that in one step. + getStreamLogQuery = `SELECT shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding + FROM stream_log + WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? + AND start_offset >= COALESCE(( + SELECT MAX(start_offset) FROM stream_log + WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? + AND start_offset <= ? + ), ?) + AND start_offset < ? + ORDER BY start_offset LIMIT ?` + + deleteStreamLogQuery = `DELETE FROM stream_log + WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ?` +) + +func (mdb *db) InsertIntoStreamLog( + ctx context.Context, + row *sqlplugin.StreamLogRow, +) (sql.Result, error) { + return mdb.conn.NamedExecContext(ctx, insertStreamLogQuery, row) +} + +func (mdb *db) RangeSelectFromStreamLog( + ctx context.Context, + filter sqlplugin.StreamLogSelectFilter, +) ([]sqlplugin.StreamLogRow, error) { + var rows []sqlplugin.StreamLogRow + if err := mdb.conn.SelectContext(ctx, &rows, getStreamLogQuery, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, + filter.MinOffset, + filter.MinOffset, + filter.MaxOffset, + filter.PageSize, + ); err != nil { + return nil, err + } + return rows, nil +} + +func (mdb *db) DeleteFromStreamLog( + ctx context.Context, + filter sqlplugin.StreamLogDeleteFilter, +) (sql.Result, error) { + return mdb.conn.ExecContext(ctx, deleteStreamLogQuery, + filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket) +} diff --git a/common/persistence/sql/sqlplugin/stream_log.go b/common/persistence/sql/sqlplugin/stream_log.go new file mode 100644 index 00000000000..9a8685a36b1 --- /dev/null +++ b/common/persistence/sql/sqlplugin/stream_log.go @@ -0,0 +1,66 @@ +package sqlplugin + +import ( + "context" + "database/sql" + + "go.temporal.io/server/common/primitives" +) + +type ( + // StreamLogRow is one appended batch, keyed by the offset its first message + // landed at. + // + // Keying by offset rather than by an opaque node id is what makes a write + // idempotent: a retry of the same append addresses the same row and + // replaces it. There is no chain to order two writers against, and no way + // for an uncommitted write to outrank a later one. The frontier the stream + // component holds is the only thing that decides what a reader may see. + StreamLogRow struct { + ShardID int32 + NamespaceID primitives.UUID + CollectionID string + Bucket int64 + StartOffset int64 + // One past the last offset this batch covers, so a reader knows whether + // the row holds the offset it asked for without decoding the blob. + // + // Not called end_offset: the schema loader scans for the SQL keyword + // `END` and a column beginning with it fails to parse. + NextOffset int64 + Data []byte + DataEncoding string + } + + // StreamLogSelectFilter reads the batches covering [MinOffset, MaxOffset). + // + // The read begins at the batch containing MinOffset, which is the greatest + // start_offset at or below it, not at MinOffset itself. A store resolves + // that itself; the caller does not have to guess how far back a batch may + // have begun. + StreamLogSelectFilter struct { + ShardID int32 + NamespaceID primitives.UUID + CollectionID string + Bucket int64 + MinOffset int64 + MaxOffset int64 + PageSize int + } + + // StreamLogDeleteFilter drops a whole bucket, which is the unit of + // reclamation. + StreamLogDeleteFilter struct { + ShardID int32 + NamespaceID primitives.UUID + CollectionID string + Bucket int64 + } + + // StreamLog is the SQL persistence interface for stream log batches. + StreamLog interface { + InsertIntoStreamLog(ctx context.Context, row *StreamLogRow) (sql.Result, error) + RangeSelectFromStreamLog(ctx context.Context, filter StreamLogSelectFilter) ([]StreamLogRow, error) + DeleteFromStreamLog(ctx context.Context, filter StreamLogDeleteFilter) (sql.Result, error) + } +) diff --git a/common/persistence/tests/history_store_stream_log.go b/common/persistence/tests/history_store_stream_log.go new file mode 100644 index 00000000000..9b59c57f242 --- /dev/null +++ b/common/persistence/tests/history_store_stream_log.go @@ -0,0 +1,385 @@ +package tests + +import ( + "time" + + "github.com/google/uuid" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + persistencespb "go.temporal.io/server/api/persistence/v1" + "go.temporal.io/server/chasm/lib/stream" + "go.temporal.io/server/common" + p "go.temporal.io/server/common/persistence" +) + +// Tests covering the history-node store's behaviour as a general append-only +// log, addressed by a branch whose tree ID is not a workflow run ID. +// +// These exist because a stream primitive reuses this store directly rather than +// standing up a parallel one. Two properties carry that decision: the node blob +// is never interpreted, and a stale node left behind by a shrinking retry is +// rejected on read even once the visibility frontier has moved past it. Neither +// was covered before, and the second is the one that decides whether the reuse +// is sound at all. + +// newLogBranch mints a branch whose tree ID is a fresh UUID rather than a run ID. +func (s *HistoryEventsSuite) newLogBranch() []byte { + branchID := uuid.NewString() + branchToken, err := s.store.GetHistoryBranchUtil().NewHistoryBranch( + uuid.NewString(), + uuid.NewString(), + uuid.NewString(), + uuid.NewString(), // tree ID: not a run ID + &branchID, + []*persistencespb.HistoryBranchRange{}, + time.Duration(0), + time.Duration(0), + time.Duration(0), + ) + s.NoError(err) + return branchToken +} + +func (s *HistoryEventsSuite) appendLogNode( + shardID int32, + branchToken []byte, + nodeID int64, + txnID int64, + prevTxnID int64, + blob *commonpb.DataBlob, +) { + _, err := s.store.AppendRawHistoryNodes(s.Ctx, &p.AppendRawHistoryNodesRequest{ + ShardID: shardID, + BranchToken: branchToken, + NodeID: nodeID, + TransactionID: txnID, + PrevTransactionID: prevTxnID, + IsNewBranch: nodeID == common.FirstEventID, + Info: "", + History: blob, + }) + s.NoError(err) +} + +func (s *HistoryEventsSuite) listRawLogNodes( + shardID int32, + branchToken []byte, + minNodeID int64, + maxNodeID int64, +) ([]*commonpb.DataBlob, []int64) { + var token []byte + var blobs []*commonpb.DataBlob + var nodeIDs []int64 + for doContinue := true; doContinue; doContinue = len(token) > 0 { + resp, err := s.store.ReadRawHistoryBranch(s.Ctx, &p.ReadHistoryBranchRequest{ + ShardID: shardID, + BranchToken: branchToken, + MinEventID: minNodeID, + MaxEventID: maxNodeID, + PageSize: 1, + NextPageToken: token, + }) + s.NoError(err) + token = resp.NextPageToken + blobs = append(blobs, resp.HistoryEventBlobs...) + nodeIDs = append(nodeIDs, resp.NodeIDs...) + } + return blobs, nodeIDs +} + +func (s *HistoryEventsSuite) eventIDsOf(events []*historypb.HistoryEvent) []int64 { + ids := make([]int64, len(events)) + for i, e := range events { + ids[i] = e.EventId + } + return ids +} + +// TestStreamLogBlobIsOpaque checks the store round-trips a node blob it cannot +// parse. A stream keeps application payloads here, so any attempt to interpret +// the bytes as history events would reject them. +func (s *HistoryEventsSuite) TestStreamLogBlobIsOpaque() { + branchToken := s.newLogBranch() + + payload := []byte("not a History proto \x00\x01\x02 arbitrary stream bytes") + blob := &commonpb.DataBlob{ + EncodingType: enumspb.ENCODING_TYPE_PROTO3, + Data: payload, + } + + s.appendLogNode(s.ShardID, branchToken, common.FirstEventID, 100, 0, blob) + + blobs, nodeIDs := s.listRawLogNodes(s.ShardID, branchToken, common.FirstEventID, common.FirstEventID+1) + s.Len(blobs, 1) + s.Equal([]int64{common.FirstEventID}, nodeIDs) + s.Equal(payload, blobs[0].Data) +} + +// TestStreamLogShrinkingRetryDropsStaleNode covers the case where a retry writes +// fewer nodes than the attempt it replaces, leaving a stale node past the +// retry's extent. Once later appends move the frontier beyond it, clipping the +// read range no longer hides it, so the store's transaction-ID chain has to +// reject it. +// +// Layout: node 1 commits, then a failed attempt writes nodes 11 and 13, then a +// smaller retry writes node 11 alone, then node 12 commits. Node 13 is stale +// and now sits below the frontier. +func (s *HistoryEventsSuite) TestStreamLogShrinkingRetryDropsStaleNode() { + branchToken := s.newLogBranch() + + committed := s.newHistoryEvents([]int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 100, 0) + s.appendRawHistoryBatches(s.ShardID, branchToken, committed) + + // Attempt that fails after writing both nodes; the frontier never advances. + staleFirst := s.newHistoryEvents([]int64{11, 12}, 200, 100) + s.appendRawHistoryBatches(s.ShardID, branchToken, staleFirst) + staleSecond := s.newHistoryEvents([]int64{13, 14}, 201, 200) + s.appendRawHistoryBatches(s.ShardID, branchToken, staleSecond) + + // Retry carries fewer messages, so it covers node 11 only. + retry := s.newHistoryEvents([]int64{11}, 300, 100) + s.appendRawHistoryBatches(s.ShardID, branchToken, retry) + + // Next append moves the frontier past the stale node at 13. + next := s.newHistoryEvents([]int64{12, 13, 14, 15}, 400, 300) + s.appendRawHistoryBatches(s.ShardID, branchToken, next) + + events := s.listHistoryEvents(s.ShardID, branchToken, common.FirstEventID, 16) + s.Equal( + []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + s.eventIDsOf(events), + "stale node 13 from the abandoned attempt must not be returned", + ) + + // The raw path carries no contiguity check of its own, so for a caller that + // never parses the blob the transaction-ID chain is the only thing standing + // between it and the stale node. + _, nodeIDs := s.listRawLogNodes(s.ShardID, branchToken, common.FirstEventID, 16) + s.Equal([]int64{1, 11, 12}, nodeIDs, "raw reads must drop the stale node too") +} + +// TestStreamLogTrimReclaimsStaleNodes checks that trimming against the committed +// frontier reclaims stale nodes without disturbing the valid chain. This is what +// lets a stream clean up after a failed append promptly instead of waiting on a +// background scavenger. +func (s *HistoryEventsSuite) TestStreamLogTrimReclaimsStaleNodes() { + branchToken := s.newLogBranch() + + committed := s.newHistoryEvents([]int64{1, 2, 3}, 100, 0) + s.appendRawHistoryBatches(s.ShardID, branchToken, committed) + + stale := s.newHistoryEvents([]int64{4, 5}, 200, 100) + s.appendRawHistoryBatches(s.ShardID, branchToken, stale) + + retry := s.newHistoryEvents([]int64{4}, 300, 100) + s.appendRawHistoryBatches(s.ShardID, branchToken, retry) + + s.trimHistoryBranch(s.ShardID, branchToken, 4, 300) + + events := s.listHistoryEvents(s.ShardID, branchToken, common.FirstEventID, 5) + s.Equal([]int64{1, 2, 3, 4}, s.eventIDsOf(events)) +} + +// The bucketing scheme in chasm/lib/stream splits a stream across one tree per +// fixed-size offset range, so a storage partition cannot grow with the stream. +// That also means a read restarts the transaction chain at every bucket, and +// the claim that this is still safe is the one worth testing rather than +// asserting: it is the same class of claim that turned out to be wrong about +// partition growth in the first place. + +func (s *HistoryEventsSuite) streamAppend( + collectionID string, + bucketSize int64, + firstOffset int64, + count int64, + body string, +) { + blob := &commonpb.DataBlob{ + EncodingType: enumspb.ENCODING_TYPE_PROTO3, + Data: []byte(body), + } + err := stream.WriteAppend(s.Ctx, s.store, s.ShardID, testStreamNamespaceID, collectionID, stream.LogAppend{ + Bucket: stream.BucketOf(firstOffset, bucketSize), + StartOffset: firstOffset, + NextOffset: firstOffset + count, + Blob: blob, + }) + s.NoError(err) +} + +func (s *HistoryEventsSuite) streamRead( + collectionID string, + bucketSize int64, + from int64, + to int64, +) []string { + blobs, _, err := stream.ReadRange( + s.Ctx, s.store, s.ShardID, testStreamNamespaceID, collectionID, bucketSize, from, to, 0) + s.NoError(err) + out := make([]string, len(blobs)) + for i, b := range blobs { + out[i] = string(b.Data) + } + return out +} + +const testStreamNamespaceID = "0b9d2c3a-1f4e-4a7b-9c8d-5e6f70819203" + +// TestStreamLogBucketedReadSpansTrees checks that a range read stitches buckets +// together, since each bucket is a separate tree and a caller only ever sees +// global offsets. +func (s *HistoryEventsSuite) TestStreamLogBucketedReadSpansTrees() { + collectionID := uuid.NewString() + const bucketSize = 4 + + s.streamAppend(collectionID, bucketSize, 0, 4, "bucket0") + s.streamAppend(collectionID, bucketSize, 4, 4, "bucket1") + s.streamAppend(collectionID, bucketSize, 8, 2, "bucket2") + + s.Equal([]string{"bucket0", "bucket1", "bucket2"}, s.streamRead(collectionID, bucketSize, 0, 10)) + s.Equal([]string{"bucket1"}, s.streamRead(collectionID, bucketSize, 4, 8)) +} + +// TestStreamLogBucketBoundaryDropsStaleNode is the bucket-aware form of +// TestStreamLogShrinkingRetryDropsStaleNode. An abandoned append straddles a +// bucket boundary, so the stale node lands in a tree whose chain a reader +// starts fresh. It must still be rejected once the frontier moves past it. +func (s *HistoryEventsSuite) TestStreamLogBucketBoundaryDropsStaleNode() { + collectionID := uuid.NewString() + const bucketSize = 4 + + s.streamAppend(collectionID, bucketSize, 0, 3, "committed") + + // Abandoned attempt: tail of bucket 0 plus the head of bucket 1. + s.streamAppend(collectionID, bucketSize, 3, 1, "stale-bucket0") + s.streamAppend(collectionID, bucketSize, 4, 2, "stale-bucket1") + + // Retry covers only bucket 0, so the bucket 1 node is orphaned. + s.streamAppend(collectionID, bucketSize, 3, 1, "retry") + + // A later append reaches into bucket 1, moving the frontier past the orphan. + s.streamAppend(collectionID, bucketSize, 4, 2, "real-bucket1") + + s.Equal( + []string{"committed", "retry", "real-bucket1"}, + s.streamRead(collectionID, bucketSize, 0, 6), + "the orphaned bucket 1 node must not surface once the frontier passes it", + ) +} + +// TestStreamLogOrphanFromAnotherSequenceShadowsLaterWrites is the failure the +// prototype shipped with: two producers numbering from two unrelated sequences. +// +// The chain rule keeps the highest transaction id it has seen and drops +// everything below it, which orders contested nodes correctly only while every +// writer draws from one sequence. A node left behind by a write that never +// committed still counts, because the rule reads storage and not the frontier. +// So an uncommitted node numbered from a sequence that runs ahead hides every +// later write numbered from the one that runs behind, and the reader is not +// told: it simply stops seeing new messages. +// +// The fix is one sequence per shard for every producer. This test pins the +// behaviour that made the bug invisible, so a second sequence cannot come back. +func (s *HistoryEventsSuite) TestStreamLogOrphanFromAnotherSequenceShadowsLaterWrites() { + branchToken := s.newLogBranch() + + // A committed append, numbered from the sequence the workflow used. + first := s.newHistoryEvents([]int64{1, 2}, 32, 0) + s.appendRawHistoryBatches(s.ShardID, branchToken, first) + + // A producer outside the workflow writes its node and then fails to commit, + // so the frontier never covers it. Its id comes from the shard generator and + // is far above anything the workflow task path produces. + orphan := s.newHistoryEvents([]int64{3, 4}, 5000, 32) + s.appendRawHistoryBatches(s.ShardID, branchToken, orphan) + + // Two more committed appends from the workflow. Both are numbered below the + // orphan, which is the whole problem. + second := s.newHistoryEvents([]int64{3, 4}, 33, 32) + s.appendRawHistoryBatches(s.ShardID, branchToken, second) + third := s.newHistoryEvents([]int64{5, 6}, 34, 33) + s.appendRawHistoryBatches(s.ShardID, branchToken, third) + + events := s.listHistoryEvents(s.ShardID, branchToken, common.FirstEventID, 7) + s.Equal( + []int64{1, 2, 3, 4}, + s.eventIDsOf(events), + "the orphan is served and both committed appends after it are dropped, "+ + "which is why a second transaction-id sequence loses data", + ) + + // The same log, written the way the fix writes it: every producer numbering + // from the shard, so the orphan is superseded rather than dominant. + fixed := s.newLogBranch() + s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{1, 2}, 5001, 0)) + s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{3, 4}, 5002, 5001)) + s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{3, 4}, 5003, 5001)) + s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{5, 6}, 5004, 5003)) + + events = s.listHistoryEvents(s.ShardID, fixed, common.FirstEventID, 7) + s.Equal( + []int64{1, 2, 3, 4, 5, 6}, + s.eventIDsOf(events), + "one sequence per shard: the uncommitted node is superseded and nothing is lost", + ) +} + +// TestStreamLogAppendIsIdempotentByOffset is the property the dedicated facet +// exists for. +// +// A row is keyed by the offset its batch starts at, so a retry of an append +// addresses the row it wrote before and replaces it. There is no chain to order +// two writers against, so there is nothing for an uncommitted write to outrank, +// which is the failure the previous substrate had. +func (s *HistoryEventsSuite) TestStreamLogAppendIsIdempotentByOffset() { + const collectionID = "idempotent-by-offset" + const bucketSize = 100 + + s.streamAppend(collectionID, bucketSize, 0, 2, "first") + + // An attempt that wrote and never committed, at offsets the next attempt + // will reuse. On the old substrate this could outrank what followed. + s.streamAppend(collectionID, bucketSize, 2, 3, "abandoned") + + // The retry, covering fewer offsets from the same start. + s.streamAppend(collectionID, bucketSize, 2, 1, "retry") + + // Whatever wrote last at that offset is what is there, and nothing before + // or after it was disturbed. + s.streamAppend(collectionID, bucketSize, 3, 1, "after") + + s.Equal( + []string{"first", "retry", "after"}, + s.streamRead(collectionID, bucketSize, 0, 4), + "a rewrite at an offset replaces that batch and shadows nothing", + ) +} + +// TestStreamLogReadFindsTheBatchHoldingAnOffset checks that a read starting +// inside a batch gets the batch containing it. +// +// The caller asks for an offset, not for a batch. Only the store can find the +// row holding it, which it does in one indexed lookup because the key is the +// offset a batch starts at. The previous substrate could not, and compensated +// by reading a whole batch's worth of rows backwards on every read. +func (s *HistoryEventsSuite) TestStreamLogReadFindsTheBatchHoldingAnOffset() { + const collectionID = "mid-batch-read" + const bucketSize = 100 + + s.streamAppend(collectionID, bucketSize, 0, 10, "wide") + s.streamAppend(collectionID, bucketSize, 10, 1, "narrow") + + // Offset 4 sits inside the first batch, which starts at 0. + s.Equal( + []string{"wide", "narrow"}, + s.streamRead(collectionID, bucketSize, 4, 11), + "a read landing mid-batch must be served the batch that holds it", + ) + + // And a read starting exactly on a boundary gets only what follows. + s.Equal( + []string{"narrow"}, + s.streamRead(collectionID, bucketSize, 10, 11), + ) +} diff --git a/schema/cassandra/temporal/schema.cql b/schema/cassandra/temporal/schema.cql index 9d664fe5981..eada7b95184 100644 --- a/schema/cassandra/temporal/schema.cql +++ b/schema/cassandra/temporal/schema.cql @@ -247,3 +247,18 @@ CREATE TABLE nexus_endpoints ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; + +CREATE TABLE stream_log ( + shard_id int, + namespace_id uuid, + collection_id text, + bucket bigint, -- one bucket is one partition, bounded by offsets and by bytes + start_offset bigint, -- first offset in this batch, and the key a retry addresses + next_offset bigint, -- exclusive, so a reader knows the span without decoding + data blob, + data_encoding text, + PRIMARY KEY ((shard_id, namespace_id, collection_id, bucket), start_offset) +) WITH CLUSTERING ORDER BY (start_offset ASC) + AND COMPACTION = { + 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' +}; diff --git a/schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql b/schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql new file mode 100644 index 00000000000..9b9f0ad0c6c --- /dev/null +++ b/schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql @@ -0,0 +1,14 @@ +CREATE TABLE stream_log ( + shard_id int, + namespace_id uuid, + collection_id text, + bucket bigint, -- one bucket is one partition, bounded by offsets and by bytes + start_offset bigint, -- first offset in this batch, and the key a retry addresses + next_offset bigint, -- exclusive, so a reader knows the span without decoding + data blob, + data_encoding text, + PRIMARY KEY ((shard_id, namespace_id, collection_id, bucket), start_offset) +) WITH CLUSTERING ORDER BY (start_offset ASC) + AND COMPACTION = { + 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' +}; diff --git a/schema/cassandra/temporal/versioned/v1.10/manifest.json b/schema/cassandra/temporal/versioned/v1.10/manifest.json index bab20aee84f..5afd4458e17 100644 --- a/schema/cassandra/temporal/versioned/v1.10/manifest.json +++ b/schema/cassandra/temporal/versioned/v1.10/manifest.json @@ -1,6 +1,8 @@ { "CurrVersion": "1.10", "MinCompatibleVersion": "1.0", - "Description": "create nexus_incoming_services table", - "SchemaUpdateCqlFiles": ["nexus_incoming_services.cql"] + "Description": "Add stream_log table for server-side streams", + "SchemaUpdateCqlFiles": [ + "add_stream_log.cql" + ] } diff --git a/schema/sqlite/v3/temporal/schema.sql b/schema/sqlite/v3/temporal/schema.sql index d7056640a14..f759d240cd5 100644 --- a/schema/sqlite/v3/temporal/schema.sql +++ b/schema/sqlite/v3/temporal/schema.sql @@ -319,6 +319,18 @@ CREATE TABLE history_node ( data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); +CREATE TABLE stream_log ( + shard_id INT NOT NULL, + namespace_id BINARY(16) NOT NULL, + collection_id VARCHAR(255) NOT NULL, + bucket BIGINT NOT NULL, + start_offset BIGINT NOT NULL, + -- + next_offset BIGINT NOT NULL, + data MEDIUMBLOB NOT NULL, + data_encoding VARCHAR(16) NOT NULL, + PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) +); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( diff --git a/schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql b/schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql new file mode 100644 index 00000000000..6c523d75ba2 --- /dev/null +++ b/schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql @@ -0,0 +1,12 @@ +CREATE TABLE stream_log ( + shard_id INT NOT NULL, + namespace_id BINARY(16) NOT NULL, + collection_id VARCHAR(255) NOT NULL, + bucket BIGINT NOT NULL, + start_offset BIGINT NOT NULL, + -- + next_offset BIGINT NOT NULL, + data MEDIUMBLOB NOT NULL, + data_encoding VARCHAR(16) NOT NULL, + PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) +); diff --git a/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json b/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json index 7bb9a2e7313..aa3cfecdb23 100644 --- a/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json +++ b/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json @@ -1,8 +1,8 @@ { "CurrVersion": "0.10", "MinCompatibleVersion": "0.1", - "Description": "Adds tasks_v2 table for fairness tasks", + "Description": "Add stream_log table for server-side streams", "SchemaUpdateCqlFiles": [ - "tasks_v2.sql" + "add_stream_log.sql" ] } diff --git a/service/worker/scanner/history/scavenger.go b/service/worker/scanner/history/scavenger.go index bc0543a18d1..a2cedbad82e 100644 --- a/service/worker/scanner/history/scavenger.go +++ b/service/worker/scanner/history/scavenger.go @@ -216,6 +216,18 @@ func (s *Scavenger) filterTask( return nil } + // Not a workflow execution's history, so its owner deletes it and this + // scavenger must not. Checked before the parse, because the tag parses + // into a namespace that does not exist and that reads as garbage. + if persistence.IsNonExecutionGarbageCleanupInfo(branch.Info) { + metrics.HistoryScavengerSkipCount.With(s.metricsHandler).Record(1) + + s.Lock() + defer s.Unlock() + s.hbd.SkipCount++ + return nil + } + namespaceID, workflowID, runID, err := persistence.SplitHistoryGarbageCleanupInfo(branch.Info) if err != nil { s.logger.Error("unable to parse the history cleanup info", tag.DetailInfo(branch.Info), tag.Error(err)) diff --git a/service/worker/scanner/history/scavenger_test.go b/service/worker/scanner/history/scavenger_test.go index d9e7e45e52c..5a12ea5a375 100644 --- a/service/worker/scanner/history/scavenger_test.go +++ b/service/worker/scanner/history/scavenger_test.go @@ -744,3 +744,61 @@ func (s *ScavengerTestSuite) TestDeleteWorkflowAfterRetention() { s.Equal(2, hbd.CurrentPage) s.Equal(0, len(hbd.NextPageToken)) } + +// A branch that is not a workflow execution's history must survive the +// scavenger. It deletes a branch whose execution it cannot find, and a stream +// log has no execution to find, so without the marker it reclaims live data +// once the branch passes the minimum age. +func (s *ScavengerTestSuite) TestSkipsBranchesThatAreNotExecutionHistory() { + streamInfo := persistence.NonExecutionGarbageCleanupInfoPrefix + "stream:namespaceID1:collection1" + + s.mockExecutionManager.EXPECT().GetAllHistoryTreeBranches(gomock.Any(), protomock.Eq(&persistence.GetAllHistoryTreeBranchesRequest{ + PageSize: pageSize, + })).Return(&persistence.GetAllHistoryTreeBranchesResponse{ + Branches: []persistence.HistoryBranchDetail{ + { + BranchInfo: &persistencespb.HistoryBranch{ + TreeId: treeID1, + BranchId: branchID1, + }, + ForkTime: timestamp.TimeNowPtrUtcAddDuration(-s.scavenger.historyDataMinAge() * 2), + Info: streamInfo, + }, + { + BranchInfo: &persistencespb.HistoryBranch{ + TreeId: treeID2, + BranchId: branchID2, + }, + ForkTime: timestamp.TimeNowPtrUtcAddDuration(-s.scavenger.historyDataMinAge() * 2), + Info: persistence.BuildHistoryGarbageCleanupInfo("namespaceID2", "workflowID2", "runID2"), + }, + }, + }, nil) + + // Only the workflow branch is looked up, and only it is deleted. The stream + // branch is never described, because describing it is what produced the + // not-found the scavenger read as garbage. + s.mockHistoryClient.EXPECT().DescribeMutableState(gomock.Any(), &historyservice.DescribeMutableStateRequest{ + NamespaceId: "namespaceID2", + Execution: &commonpb.WorkflowExecution{ + WorkflowId: "workflowID2", + RunId: "runID2", + }, + ArchetypeId: chasm.WorkflowArchetypeID, + }).Return(nil, serviceerror.NewNotFound("")) + branchToken2, err := s.scavenger.serializer.HistoryBranchToBlob(&persistencespb.HistoryBranch{ + TreeId: treeID2, + BranchId: branchID2, + }) + s.Require().NoError(err) + s.mockExecutionManager.EXPECT().DeleteHistoryBranch(gomock.Any(), protomock.Eq(&persistence.DeleteHistoryBranchRequest{ + ShardID: common.WorkflowIDToHistoryShard("namespaceID2", "workflowID2", s.scavenger.numShards), + BranchToken: branchToken2.Data, + })).Return(nil) + + hbd, err := s.scavenger.Run(context.Background()) + s.Require().NoError(err) + s.Equal(1, hbd.SkipCount, "the stream branch must be skipped, not collected") + s.Equal(1, hbd.SuccessCount) + s.Equal(0, hbd.ErrorCount) +} From 2ea6176e68c42ae5bce9a6b878a3e3625b4f6b0e Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 14 Sep 2026 15:59:55 -0700 Subject: [PATCH 03/47] Added the CHASM stream component with floor-aware retention. --- chasm/lib/stream/config.go | 69 +++ chasm/lib/stream/cursor.go | 167 ++++++ chasm/lib/stream/cursor_test.go | 118 ++++ chasm/lib/stream/log.go | 160 +++++ chasm/lib/stream/messages.go | 125 ++++ chasm/lib/stream/messages_test.go | 47 ++ chasm/lib/stream/service/events.go | 78 +++ chasm/lib/stream/service/frontend.go | 231 ++++++++ chasm/lib/stream/service/fx.go | 35 ++ chasm/lib/stream/service/handler.go | 839 +++++++++++++++++++++++++++ chasm/lib/stream/service/library.go | 85 +++ chasm/lib/stream/service/tasks.go | 209 +++++++ chasm/lib/stream/stream.go | 699 ++++++++++++++++++++++ chasm/lib/stream/stream_test.go | 523 +++++++++++++++++ 14 files changed, 3385 insertions(+) create mode 100644 chasm/lib/stream/config.go create mode 100644 chasm/lib/stream/cursor.go create mode 100644 chasm/lib/stream/cursor_test.go create mode 100644 chasm/lib/stream/log.go create mode 100644 chasm/lib/stream/messages.go create mode 100644 chasm/lib/stream/messages_test.go create mode 100644 chasm/lib/stream/service/events.go create mode 100644 chasm/lib/stream/service/frontend.go create mode 100644 chasm/lib/stream/service/fx.go create mode 100644 chasm/lib/stream/service/handler.go create mode 100644 chasm/lib/stream/service/library.go create mode 100644 chasm/lib/stream/service/tasks.go create mode 100644 chasm/lib/stream/stream.go create mode 100644 chasm/lib/stream/stream_test.go diff --git a/chasm/lib/stream/config.go b/chasm/lib/stream/config.go new file mode 100644 index 00000000000..7168d46b92d --- /dev/null +++ b/chasm/lib/stream/config.go @@ -0,0 +1,69 @@ +package stream + +import "time" + +// DefaultMaxMessagesPerPoll bounds a read page when the caller does not. +const DefaultMaxMessagesPerPoll = 1000 + +// MaxMessagesPerBatch bounds one append. It is not only an admission limit: a +// node ID is the first offset of its batch, so to serve a read starting inside +// a batch the reader has to find the node that contains it. Bounding the batch +// bounds how far back it has to start, which turns an unbounded scan into a +// fixed overread. +const MaxMessagesPerBatch = 1000 + +// LongPollTimeout matches the convention used by the history long polls: on +// expiry the caller gets an empty response and polls again, rather than an +// error it would have to special-case. +const LongPollTimeout = 20 * time.Second + +// LongPollBuffer leaves room to return an empty response before the caller's +// own deadline fires. +const LongPollBuffer = 3 * time.Second + +// MaxConsumeItemsPerTask bounds one Workflow Task's slice. A byte cap alone is +// not enough: a burst of tiny messages stays under it while still making one +// task's drain arbitrarily long. Whichever bound binds first, the rest is +// delivered on the following task. +const MaxConsumeItemsPerTask = 1000 + +// MaxConsumeBytesPerTask bounds one Workflow Task's slice by size. Paired with +// MaxConsumeItemsPerTask because neither bound alone is enough: a burst of tiny +// messages slips under the byte budget, and a few large ones slip under the +// item count. +const MaxConsumeBytesPerTask = 2 << 20 + +// MaxProducersPerStream bounds the per-producer dedup table. The table is part +// of the component state written on every append, so a caller that sends a +// fresh producer id per request would grow the state until the mutable-state +// size limit rejects every further append, leaving the stream unwritable for +// good. The bound turns that into a clear error on the offending call. +const MaxProducersPerStream = 1000 + +// MaxConsumersPerStream bounds the registered consumer table for the same +// reason. Each consumer also holds a truncation floor, so an unbounded table +// would pin storage as well as grow state. +const MaxConsumersPerStream = 1000 + +// MaxListPageSize bounds a visibility page when the caller does not. +const MaxListPageSize = 1000 + +// MaxMessageBytes bounds one message. A message is never split, so this is also +// the smallest unit a reader can be asked to materialise. +const MaxMessageBytes = 1 << 20 + +// MaxBatchBytes bounds one append. It is deliberately equal to +// MaxConsumeBytesPerTask: a batch is written as one node and read back whole, +// so a batch larger than a task's byte budget could never be delivered. +const MaxBatchBytes = MaxConsumeBytesPerTask + +// MaxOwnedStreamsPerWorkflow bounds how many named streams one execution can +// carry. Each is a component in the workflow's mutable state, so an unbounded +// count grows that state until the size limit terminates the execution. The +// name comes from the caller, and any caller in the namespace can pick a new +// one, which is what makes this reachable from outside. +const MaxOwnedStreamsPerWorkflow = 100 + +// MaxStreamNameLength bounds a name before it becomes a map key in mutable +// state, for the same reason. +const MaxStreamNameLength = 255 diff --git a/chasm/lib/stream/cursor.go b/chasm/lib/stream/cursor.go new file mode 100644 index 00000000000..265ec33daae --- /dev/null +++ b/chasm/lib/stream/cursor.go @@ -0,0 +1,167 @@ +package stream + +import ( + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" +) + +// Cursor is a consuming workflow's position in a stream. +// +// It is a subcomponent of the consumer, not of the stream. That placement is +// the whole point: the consumer's mutable state and its History events commit +// in one transaction, so folding a delivered range into the cursor lands +// atomically with the event that records the range. Holding the cursor on the +// stream instead would make every advance a cross-execution write, and a crash +// between the two writes would either redeliver a range or skip it silently. +type Cursor struct { + chasm.UnimplementedComponent + + State *streampb.WorkflowStreamCursor +} + +type NewCursorRequest struct { + StreamID string + // External marks a stream in another execution, whose frontier this + // workflow is told about rather than reads. + External bool + CollectionID string + BucketSize int64 + + // Where to start reading. Resolving "from the tail" against the stream's + // head happens before this is called, so the value recorded here is already + // a fact rather than a reading that would differ on replay. + StartOffset int64 +} + +func NewCursor(_ chasm.MutableContext, req NewCursorRequest) (*Cursor, error) { + if req.StreamID == "" { + return nil, serviceerror.NewInvalidArgument("stream id is required") + } + if req.CollectionID == "" { + return nil, serviceerror.NewInvalidArgument("collection id is required") + } + if req.BucketSize <= 0 { + return nil, serviceerror.NewInvalidArgument("bucket size must be positive") + } + if req.StartOffset < 0 { + return nil, serviceerror.NewInvalidArgument("start offset cannot be negative") + } + + return &Cursor{ + State: &streampb.WorkflowStreamCursor{ + StreamId: req.StreamID, + CollectionId: req.CollectionID, + BucketSize: req.BucketSize, + Offset: req.StartOffset, + StartOffset: req.StartOffset, + External: req.External, + KnownHead: req.StartOffset, + }, + }, nil +} + +// LifecycleState reports the cursor as running for as long as the workflow +// holding it exists. Deregistration is an explicit act, not a state the +// component reaches on its own. +func (c *Cursor) LifecycleState(_ chasm.Context) chasm.LifecycleState { + return chasm.LifecycleStateRunning +} + +// Offset is the next offset that has not yet been delivered and folded in. +func (c *Cursor) Offset() int64 { + return c.State.Offset +} + +func (c *Cursor) StreamID() string { + return c.State.StreamId +} + +func (c *Cursor) CollectionID() string { + return c.State.CollectionId +} + +func (c *Cursor) BucketSize() int64 { + return c.State.BucketSize +} + +// StagePending records the range attached to the workflow task now in flight. +// +// A redelivery overwrites whatever was staged before. That is safe because a +// range only becomes history when the task completes: if the previous task +// failed or timed out, nothing was recorded, so the replacement range is the +// first one the workflow will ever have observed at this point. +func (c *Cursor) StagePending(_ chasm.MutableContext, from int64, to int64) error { + if from < c.State.Offset { + return serviceerror.NewInvalidArgumentf( + "cannot deliver from offset %d, cursor is already at %d", from, c.State.Offset) + } + if to < from { + return serviceerror.NewInvalidArgumentf("range end %d precedes range start %d", to, from) + } + + c.State.PendingFrom = from + c.State.PendingTo = to + c.State.HasPending = true + return nil +} + +// Pending reports the staged range. The second result distinguishes "no task in +// flight" from "a task in flight that was given nothing", which are different +// facts: the latter must still be recorded. +func (c *Cursor) Pending() (from int64, to int64, ok bool) { + if !c.State.HasPending { + return 0, 0, false + } + return c.State.PendingFrom, c.State.PendingTo, true +} + +// Commit folds the staged range into the cursor and returns it for recording. +// Caller writes the returned range onto the event that closes the task, in the +// same transaction that persists this advance. +func (c *Cursor) Commit(_ chasm.MutableContext) (from int64, to int64, ok bool) { + if !c.State.HasPending { + return 0, 0, false + } + + from, to = c.State.PendingFrom, c.State.PendingTo + c.State.Offset = to + c.State.PendingFrom = 0 + c.State.PendingTo = 0 + c.State.HasPending = false + return from, to, true +} + +// Abandon drops a staged range without advancing, for a task that will never +// complete. The next delivery re-reads from the unchanged cursor. +func (c *Cursor) Abandon(_ chasm.MutableContext) { + c.State.PendingFrom = 0 + c.State.PendingTo = 0 + c.State.HasPending = false +} + +// IsExternal reports whether the stream lives in another execution. +func (c *Cursor) IsExternal() bool { + return c.State.External +} + +// KnownHead is the stream's frontier as last pushed to this workflow. +func (c *Cursor) KnownHead() int64 { + return c.State.KnownHead +} + +// AdvanceKnownHead moves the recorded frontier forward. It never moves back: a +// stale push arriving after a fresher one must not hide offsets already known +// to exist. +func (c *Cursor) AdvanceKnownHead(_ chasm.MutableContext, head int64) { + if head > c.State.KnownHead { + c.State.KnownHead = head + } +} + +// StartOffset is where this subscription began reading. Replay needs it to tell +// a consumer that has committed nothing apart from one whose recording events +// are simply not in the history page it was handed. +func (c *Cursor) StartOffset() int64 { + return c.State.StartOffset +} diff --git a/chasm/lib/stream/cursor_test.go b/chasm/lib/stream/cursor_test.go new file mode 100644 index 00000000000..a5ca5314500 --- /dev/null +++ b/chasm/lib/stream/cursor_test.go @@ -0,0 +1,118 @@ +package stream + +import ( + "testing" + + "github.com/stretchr/testify/require" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" +) + +func newTestCursor(offset int64) *Cursor { + return &Cursor{ + State: &streampb.WorkflowStreamCursor{ + StreamId: "s-1", + CollectionId: "col-1", + BucketSize: DefaultBucketSize, + Offset: offset, + }, + } +} + +func TestNewCursorRejectsIncompleteRequests(t *testing.T) { + cases := map[string]NewCursorRequest{ + "no stream id": {CollectionID: "col-1", BucketSize: 10}, + "no collection id": {StreamID: "s-1", BucketSize: 10}, + "zero bucket size": {StreamID: "s-1", CollectionID: "col-1"}, + "negative start": {StreamID: "s-1", CollectionID: "col-1", BucketSize: 10, StartOffset: -1}, + } + + for name, req := range cases { + t.Run(name, func(t *testing.T) { + _, err := NewCursor(nil, req) + require.Error(t, err) + }) + } +} + +func TestCursorCommitAdvancesAndClears(t *testing.T) { + c := newTestCursor(4) + + require.NoError(t, c.StagePending(nil, 4, 7)) + + from, to, ok := c.Pending() + require.True(t, ok) + require.Equal(t, int64(4), from) + require.Equal(t, int64(7), to) + + from, to, ok = c.Commit(nil) + require.True(t, ok) + require.Equal(t, int64(4), from) + require.Equal(t, int64(7), to) + require.Equal(t, int64(7), c.Offset()) + + _, _, ok = c.Pending() + require.False(t, ok, "a committed range must not be staged twice") + + _, _, ok = c.Commit(nil) + require.False(t, ok, "committing again must not re-record the range") +} + +// The distinction this pins is the one §8.2 of the design turns on: a task that +// observed nothing still has to be recorded, so an empty range is a pending +// range, not the absence of one. +func TestCursorTreatsAnEmptyRangeAsAFact(t *testing.T) { + c := newTestCursor(9) + + _, _, ok := c.Pending() + require.False(t, ok, "no task in flight yet") + + require.NoError(t, c.StagePending(nil, 9, 9)) + + from, to, ok := c.Pending() + require.True(t, ok, "a task given nothing is still a task that must be recorded") + require.Equal(t, from, to) + + from, to, ok = c.Commit(nil) + require.True(t, ok) + require.Equal(t, int64(9), from) + require.Equal(t, int64(9), to) + require.Equal(t, int64(9), c.Offset(), "an empty range must not move the cursor") +} + +func TestCursorRejectsARangeBehindItself(t *testing.T) { + c := newTestCursor(12) + + err := c.StagePending(nil, 11, 14) + require.ErrorContains(t, err, "cursor is already at 12") + + err = c.StagePending(nil, 12, 11) + require.ErrorContains(t, err, "precedes range start") +} + +// A task that failed recorded nothing, so the range it was given never became +// history and the replacement is free to differ. +func TestCursorRedeliveryReplacesTheStagedRange(t *testing.T) { + c := newTestCursor(2) + + require.NoError(t, c.StagePending(nil, 2, 5)) + require.NoError(t, c.StagePending(nil, 2, 9)) + + from, to, ok := c.Pending() + require.True(t, ok) + require.Equal(t, int64(2), from) + require.Equal(t, int64(9), to) + require.Equal(t, int64(2), c.Offset(), "staging alone must never advance the cursor") +} + +func TestCursorAbandonLeavesTheOffsetAlone(t *testing.T) { + c := newTestCursor(3) + + require.NoError(t, c.StagePending(nil, 3, 8)) + c.Abandon(nil) + + _, _, ok := c.Pending() + require.False(t, ok) + require.Equal(t, int64(3), c.Offset()) + + require.NoError(t, c.StagePending(nil, 3, 6), "the next delivery re-reads from the unchanged cursor") +} diff --git a/chasm/lib/stream/log.go b/chasm/lib/stream/log.go new file mode 100644 index 00000000000..ce5f0033229 --- /dev/null +++ b/chasm/lib/stream/log.go @@ -0,0 +1,160 @@ +package stream + +import ( + "context" + + commonpb "go.temporal.io/api/common/v1" + "go.temporal.io/server/common/persistence" +) + +// A dedicated store for stream payload bytes, keyed by collection and by the +// offset a batch starts at. Offsets roll to a new bucket every bucketSize so no +// single partition grows with the stream, and because the bucket is arithmetic +// there is no index to keep. +// +// The component holds its payload in a chasm.Map now, so nothing on the serving +// path comes through here. What is left is reached only by the storage-level +// test suite, and it goes when that does. + +// DefaultBucketSize bounds how many messages share one storage partition. +// Immutable per stream once chosen, because changing it renumbers offsets. +const DefaultBucketSize int64 = 100_000 + +// defaultReadPageSize applies when a caller does not cap the result. +const defaultReadPageSize = 256 + +// LogAppend is one node's worth of staged bytes. The component produces these +// during a transition; whoever drives the transaction writes them. Keeping the +// two apart is what lets the append ride a workflow's own commit later without +// the component knowing. +type LogAppend struct { + Bucket int64 + // The offsets this batch covers, end exclusive. The start is the key the + // row is written under, so a retry of this append addresses the same row + // and replaces it rather than racing it. + StartOffset int64 + NextOffset int64 + Blob *commonpb.DataBlob +} + +// BucketOf returns the bucket an offset belongs to. +func BucketOf(offset, bucketSize int64) int64 { + return offset / bucketSize +} + +// BucketStart is the first global offset in a bucket. +func BucketStart(bucket, bucketSize int64) int64 { + return bucket * bucketSize +} + +// WriteAppend persists one staged batch. +// +// Batches are written before the frontier advances, so a crash here leaves +// rows at or past the head offset that no reader can see, and a retry +// overwrites them because the offset is the key. +func WriteAppend( + ctx context.Context, + execMgr persistence.ExecutionManager, + shardID int32, + namespaceID string, + collectionID string, + op LogAppend, +) error { + return execMgr.AppendStreamLog(ctx, &persistence.InternalAppendStreamLogRequest{ + ShardID: shardID, + NamespaceID: namespaceID, + CollectionID: collectionID, + Bucket: op.Bucket, + StartOffset: op.StartOffset, + NextOffset: op.NextOffset, + Node: op.Blob, + }) +} + +// ReadRange returns the raw batches covering [fromOffset, toOffset), walking +// bucket by bucket. Blobs are returned unparsed: the server has no business +// decoding user payloads, and the codec runs in the SDK. +// +// The store begins each bucket at the batch containing the first offset asked +// for, not at that offset, so a read landing mid-batch gets the batch holding +// it. Finding that batch is one indexed lookup, because a row is keyed by the +// offset it starts at. +func ReadRange( + ctx context.Context, + execMgr persistence.ExecutionManager, + shardID int32, + namespaceID string, + collectionID string, + bucketSize int64, + fromOffset int64, + toOffset int64, + maxBatches int, +) ([]*commonpb.DataBlob, []int64, error) { + var blobs []*commonpb.DataBlob + var startOffsets []int64 + if fromOffset >= toOffset { + return blobs, startOffsets, nil + } + + pageSize := maxBatches + if pageSize <= 0 { + pageSize = defaultReadPageSize + } + + for bucket := BucketOf(fromOffset, bucketSize); BucketStart(bucket, bucketSize) < toOffset; bucket++ { + bucketStart := BucketStart(bucket, bucketSize) + bucketEnd := bucketStart + bucketSize + + resp, err := execMgr.ReadStreamLog(ctx, &persistence.InternalReadStreamLogRequest{ + ShardID: shardID, + NamespaceID: namespaceID, + CollectionID: collectionID, + Bucket: bucket, + MinOffset: max(fromOffset, bucketStart), + MaxOffset: min(toOffset, bucketEnd), + PageSize: pageSize, + }) + if err != nil { + return nil, nil, err + } + + blobs = append(blobs, resp.Batches...) + startOffsets = append(startOffsets, resp.StartOffsets...) + + if maxBatches > 0 && len(blobs) >= maxBatches { + return blobs[:maxBatches], startOffsets[:maxBatches], nil + } + } + return blobs, startOffsets, nil +} + +// DeleteBucket removes a whole bucket's tree. Reclaiming a truncated stream one +// bucket at a time is the point of bucketing: a partition is dropped outright +// rather than leaving a tombstone per message. +func DeleteBucket( + ctx context.Context, + execMgr persistence.ExecutionManager, + shardID int32, + namespaceID string, + collectionID string, + bucket int64, +) error { + return execMgr.DeleteStreamLogBucket(ctx, &persistence.InternalDeleteStreamLogBucketRequest{ + ShardID: shardID, + NamespaceID: namespaceID, + CollectionID: collectionID, + Bucket: bucket, + }) +} + +// ReclaimableBuckets lists buckets that lie entirely below the readable floor +// and can therefore be deleted. A bucket is only reclaimable once every offset +// it holds is unreadable, so this can never drop data a reader may still ask +// for. +func ReclaimableBuckets(previousBase, newBase, bucketSize int64) []int64 { + var out []int64 + for b := BucketOf(previousBase, bucketSize); BucketStart(b+1, bucketSize) <= newBase; b++ { + out = append(out, b) + } + return out +} diff --git a/chasm/lib/stream/messages.go b/chasm/lib/stream/messages.go new file mode 100644 index 00000000000..37fb96df504 --- /dev/null +++ b/chasm/lib/stream/messages.go @@ -0,0 +1,125 @@ +package stream + +import ( + commonpb "go.temporal.io/api/common/v1" + "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "google.golang.org/protobuf/proto" +) + +// CollectMessages decodes the batches covering a range and trims to the +// requested window. Decoding happens only here and only on the batches a read +// actually touches; the store never interprets them, and user payloads stay +// opaque because the codec runs in the SDK. +func CollectMessages( + blobs []*commonpb.DataBlob, + startOffsets []int64, + from int64, + head int64, + maxMessages int, + topics []string, +) ([]*streamlib.StreamMessage, int64, error) { + wanted := make(map[string]struct{}, len(topics)) + for _, t := range topics { + wanted[t] = struct{}{} + } + + var out []*streamlib.StreamMessage + next := from + for i, blob := range blobs { + var batch streamlib.StreamMessageBatch + if err := proto.Unmarshal(blob.GetData(), &batch); err != nil { + return nil, 0, err + } + for j, msg := range batch.GetMessages() { + offset := startOffsets[i] + int64(j) + if offset < from || offset >= head { + continue + } + if len(out) >= maxMessages { + return out, next, nil + } + next = offset + 1 + if len(wanted) > 0 { + if _, ok := wanted[msg.GetTopic()]; !ok { + continue + } + } + // Set here rather than stored: it is decided by where the + // message sits in the log, not by what the producer wrote. + msg.Offset = offset + out = append(out, msg) + } + } + return out, next, nil +} + +// ToAPIMessages converts stored messages to the shape carried on a Workflow +// Task. Control messages are dropped: they steer the log itself and mean +// nothing to a consumer. +func ToAPIMessages(in []*streamlib.StreamMessage) []*streampb.StreamMessage { + out := make([]*streampb.StreamMessage, 0, len(in)) + for _, m := range in { + if m.GetKind() != streamlib.STREAM_MESSAGE_KIND_DATA { + continue + } + out = append(out, &streampb.StreamMessage{ + Body: m.GetBody(), + Metadata: m.GetMetadata(), + Topic: m.GetTopic(), + TopicSequence: m.GetTopicSequence(), + }) + } + return out +} + +// CapByBytes trims a contiguous run of messages to a byte budget and returns +// the offset just past the last one kept. +// +// It always keeps the first message, however large. Dropping it would leave the +// cursor unable to advance, and since an unconsumed range now schedules a +// workflow task, a stream holding one oversized message would wake the workflow +// forever without ever delivering anything. +func CapByBytes( + messages []*streamlib.StreamMessage, + from int64, + maxBytes int, +) ([]*streamlib.StreamMessage, int64) { + if len(messages) == 0 { + return messages, from + } + + total := 0 + for i, m := range messages { + total += proto.Size(m) + if total > maxBytes && i > 0 { + return messages[:i], from + int64(i) + } + } + return messages, from + int64(len(messages)) +} + +// checkBatchBytes rejects an append that is too large to store or too large to +// ever hand back. +// +// The per-message bound matters on its own: a message is never split, so one +// that exceeds a consumer's byte budget can never be delivered, and CapByBytes +// would hand it over alone forever rather than reject it. The batch bound is +// the storage side, since a batch is written as a single node. +func checkBatchBytes(messages []*streamlib.StreamMessage) error { + total := 0 + for i, m := range messages { + size := proto.Size(m) + if size > MaxMessageBytes { + return serviceerror.NewInvalidArgumentf( + "message %d is %d bytes, over the %d byte limit", i, size, MaxMessageBytes) + } + total += size + } + if total > MaxBatchBytes { + return serviceerror.NewInvalidArgumentf( + "batch is %d bytes, over the %d byte limit", total, MaxBatchBytes) + } + return nil +} diff --git a/chasm/lib/stream/messages_test.go b/chasm/lib/stream/messages_test.go new file mode 100644 index 00000000000..41a23f4620f --- /dev/null +++ b/chasm/lib/stream/messages_test.go @@ -0,0 +1,47 @@ +package stream + +import ( + "testing" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" +) + +func sized(n int, bytes int) []*streampb.StreamMessage { + out := make([]*streampb.StreamMessage, n) + for i := range out { + out[i] = &streampb.StreamMessage{ + Body: &commonpb.Payload{Data: make([]byte, bytes)}, + Kind: streampb.STREAM_MESSAGE_KIND_DATA, + } + } + return out +} + +func TestCapByBytesTrimsToAPrefix(t *testing.T) { + messages, next := CapByBytes(sized(10, 100), 4, 250) + require.Len(t, messages, 2) + require.Equal(t, int64(6), next, "the recorded range must cover exactly what was kept") +} + +func TestCapByBytesKeepsEverythingUnderBudget(t *testing.T) { + messages, next := CapByBytes(sized(3, 10), 0, 1<<20) + require.Len(t, messages, 3) + require.Equal(t, int64(3), next) +} + +// A single oversized message must still go out. Held back it would stall the +// cursor, and an unconsumed range schedules a workflow task, so the workflow +// would wake forever and never receive anything. +func TestCapByBytesAlwaysDeliversTheFirstMessage(t *testing.T) { + messages, next := CapByBytes(sized(3, 5000), 7, 10) + require.Len(t, messages, 1) + require.Equal(t, int64(8), next) +} + +func TestCapByBytesOnAnEmptyRun(t *testing.T) { + messages, next := CapByBytes(nil, 9, 100) + require.Empty(t, messages) + require.Equal(t, int64(9), next) +} diff --git a/chasm/lib/stream/service/events.go b/chasm/lib/stream/service/events.go new file mode 100644 index 00000000000..0088b1eec5e --- /dev/null +++ b/chasm/lib/stream/service/events.go @@ -0,0 +1,78 @@ +package service + +import ( + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + "go.temporal.io/server/service/history/hsm" +) + +// streamSubscribedEventDefinition tells the history service how to treat the +// event a subscribe command writes. +// +// It applies nothing. The cursor lives in CHASM state, which is persisted and +// rebuilt with the execution, so replication and reset have nothing to +// reconstruct from the event. It exists so the command has an event at all: +// every SDK matches issued commands against command-generated events in order, +// and a command producing none puts that matching out of step. +type streamSubscribedEventDefinition struct{} + +func (streamSubscribedEventDefinition) Type() enumspb.EventType { + return enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED +} + +// Subscribing does not itself give the workflow anything to decide on. The +// range that follows does, and that wakes the workflow through its cursor. +func (streamSubscribedEventDefinition) IsWorkflowTaskTrigger() bool { return false } + +func (streamSubscribedEventDefinition) Apply(*hsm.Node, *historypb.HistoryEvent) error { + return nil +} + +// A command event, so never reapplied onto another branch: a workflow that +// still wants the subscription issues the command again on the new one. +func (streamSubscribedEventDefinition) CherryPick( + *hsm.Node, + *historypb.HistoryEvent, + map[enumspb.ResetReapplyExcludeType]struct{}, +) error { + return hsm.ErrNotCherryPickable +} + +// streamMessagesAddedEventDefinition tells the history service how to treat +// the event a publish command writes. +// +// Like the subscription event it applies nothing: the stream's frontier is +// CHASM state committed with the workflow task, and the bodies are in the +// stream's own log. What the event carries is the offset range, which is what +// lets anyone reading History find the batch without History having held it. +type streamMessagesAddedEventDefinition struct{} + +func (streamMessagesAddedEventDefinition) Type() enumspb.EventType { + return enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED +} + +// A workflow publishing to its own stream has nothing to be woken about. +func (streamMessagesAddedEventDefinition) IsWorkflowTaskTrigger() bool { return false } + +func (streamMessagesAddedEventDefinition) Apply(*hsm.Node, *historypb.HistoryEvent) error { + return nil +} + +// A command event, so never reapplied onto another branch. Reapplying would +// claim offsets in a log the new branch never wrote to. +func (streamMessagesAddedEventDefinition) CherryPick( + *hsm.Node, + *historypb.HistoryEvent, + map[enumspb.ResetReapplyExcludeType]struct{}, +) error { + return hsm.ErrNotCherryPickable +} + +// RegisterEventDefinitions makes the stream events known to the history +// service. +func RegisterEventDefinitions(reg *hsm.Registry) error { + if err := reg.RegisterEventDefinition(streamSubscribedEventDefinition{}); err != nil { + return err + } + return reg.RegisterEventDefinition(streamMessagesAddedEventDefinition{}) +} diff --git a/chasm/lib/stream/service/frontend.go b/chasm/lib/stream/service/frontend.go new file mode 100644 index 00000000000..34d6e18e644 --- /dev/null +++ b/chasm/lib/stream/service/frontend.go @@ -0,0 +1,231 @@ +package service + +import ( + "context" + + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/log" + "go.temporal.io/server/common/namespace" + "google.golang.org/protobuf/types/known/emptypb" +) + +// FrontendHandler serves StreamService on the frontend. It resolves the +// namespace name to an ID and forwards to the history shard that owns the +// stream; the layered client does the routing from the business ID. +type FrontendHandler struct { + streampb.UnimplementedStreamServiceServer + + client streampb.StreamServiceClient + namespaceRegistry namespace.Registry + logger log.Logger +} + +func NewFrontendHandler( + client streampb.StreamServiceClient, + namespaceRegistry namespace.Registry, + logger log.Logger, +) *FrontendHandler { + return &FrontendHandler{ + client: client, + namespaceRegistry: namespaceRegistry, + logger: logger, + } +} + +func (h *FrontendHandler) namespaceID(name string) (string, error) { + if name == "" { + return "", serviceerror.NewInvalidArgument("namespace is required") + } + id, err := h.namespaceRegistry.GetNamespaceID(namespace.Name(name)) + if err != nil { + return "", err + } + return id.String(), nil +} + +func (h *FrontendHandler) CreateStream( + ctx context.Context, req *streampb.CreateStreamRequest, +) (*streampb.CreateStreamResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.CreateStream(ctx, &streampb.CreateStreamRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) AddMessages( + ctx context.Context, req *streampb.AddMessagesRequest, +) (*streampb.AddMessagesResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.AddMessages(ctx, &streampb.AddMessagesRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) FinishWriting( + ctx context.Context, req *streampb.FinishWritingRequest, +) (*streampb.FinishWritingResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.FinishWriting(ctx, &streampb.FinishWritingRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) SubscribeWorkflow( + ctx context.Context, req *streampb.SubscribeWorkflowRequest, +) (*streampb.SubscribeWorkflowResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.SubscribeWorkflow(ctx, &streampb.SubscribeWorkflowRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) PollMessages( + ctx context.Context, req *streampb.PollMessagesRequest, +) (*streampb.PollMessagesResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.PollMessages(ctx, &streampb.PollMessagesRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) PollWorkflowMessages( + ctx context.Context, req *streampb.PollWorkflowMessagesRequest, +) (*streampb.PollWorkflowMessagesResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.PollWorkflowMessages(ctx, &streampb.PollWorkflowMessagesRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) DescribeWorkflowStream( + ctx context.Context, req *streampb.DescribeWorkflowStreamRequest, +) (*streampb.DescribeWorkflowStreamResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.DescribeWorkflowStream(ctx, &streampb.DescribeWorkflowStreamRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) AddWorkflowMessages( + ctx context.Context, req *streampb.AddWorkflowMessagesRequest, +) (*streampb.AddWorkflowMessagesResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.AddWorkflowMessages(ctx, &streampb.AddWorkflowMessagesRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) DescribeStream( + ctx context.Context, req *streampb.DescribeStreamRequest, +) (*streampb.DescribeStreamResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.DescribeStream(ctx, &streampb.DescribeStreamRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) CloseStream( + ctx context.Context, req *streampb.CloseStreamRequest, +) (*streampb.CloseStreamResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.CloseStream(ctx, &streampb.CloseStreamRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) TruncateStream( + ctx context.Context, req *streampb.TruncateStreamRequest, +) (*streampb.TruncateStreamResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.TruncateStream(ctx, &streampb.TruncateStreamRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +func (h *FrontendHandler) DeleteStream( + ctx context.Context, req *streampb.DeleteStreamRequest, +) (*streampb.DeleteStreamResponse, error) { + id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + if err != nil { + return nil, err + } + return h.client.DeleteStream(ctx, &streampb.DeleteStreamRequest{ + NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + }) +} + +// ListStreams answers from visibility rather than from any one stream, so it +// does not route to a shard and is served here rather than on the history side. +func (h *FrontendHandler) ListStreams( + ctx context.Context, req *streampb.ListStreamsRequest, +) (*streampb.ListStreamsResponse, error) { + in := req.GetFrontendRequest() + if in.GetNamespace() == "" { + return nil, serviceerror.NewInvalidArgument("namespace is required") + } + + pageSize := int(in.GetPageSize()) + if pageSize <= 0 || pageSize > stream.MaxListPageSize { + pageSize = stream.MaxListPageSize + } + + resp, err := chasm.ListExecutions[*stream.Stream, *emptypb.Empty](ctx, &chasm.ListExecutionsRequest{ + NamespaceName: in.GetNamespace(), + PageSize: pageSize, + NextPageToken: in.GetNextPageToken(), + Query: in.GetQuery(), + }) + if err != nil { + return nil, err + } + + entries := make([]*streampb.StreamListEntry, 0, len(resp.Executions)) + for _, e := range resp.Executions { + entries = append(entries, &streampb.StreamListEntry{ + StreamId: e.BusinessID, + RunId: e.RunID, + }) + } + return &streampb.ListStreamsResponse{ + FrontendResponse: &streampb.ListStreamsOutput{ + Streams: entries, + NextPageToken: resp.NextPageToken, + }, + }, nil +} diff --git a/chasm/lib/stream/service/fx.go b/chasm/lib/stream/service/fx.go new file mode 100644 index 00000000000..06ad4ffa779 --- /dev/null +++ b/chasm/lib/stream/service/fx.go @@ -0,0 +1,35 @@ +package service + +import ( + "go.temporal.io/server/chasm" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.uber.org/fx" +) + +var HistoryModule = fx.Module( + "stream-history", + fx.Provide( + // Routes a call to the host owning a shard. History needs it too, not + // just the frontend: a step that spans two executions has to reach a + // shard this host may not own. + streampb.NewStreamServiceLayeredClient, + newHandler, + newRetentionTaskHandler, + newNotifyConsumersTaskHandler, + newLibrary, + ), + fx.Invoke(func(l *library, registry *chasm.Registry) error { + return registry.Register(l) + }), + fx.Invoke(RegisterEventDefinitions), +) + +var FrontendModule = fx.Module( + "stream-frontend", + fx.Provide(streampb.NewStreamServiceLayeredClient), + fx.Provide(NewFrontendHandler), + fx.Provide(newComponentOnlyLibrary), + fx.Invoke(func(l *componentOnlyLibrary, registry *chasm.Registry) error { + return registry.Register(l) + }), +) diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go new file mode 100644 index 00000000000..41da753d9be --- /dev/null +++ b/chasm/lib/stream/service/handler.go @@ -0,0 +1,839 @@ +package service + +import ( + "context" + "sync" + + "github.com/dgryski/go-farm" + commonpb "go.temporal.io/api/common/v1" + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common" + "go.temporal.io/server/common/contextutil" + "go.temporal.io/server/common/headers" + "go.temporal.io/server/common/log" + "go.temporal.io/server/common/namespace" + "go.temporal.io/server/service/history/shard" +) + +type handler struct { + streampb.UnimplementedStreamServiceServer + + shardController shard.Controller + namespaceRegistry namespace.Registry + logger log.Logger + + // Routes a call to the host that owns a shard. A step spanning two + // executions cannot resolve both through the local controller, which + // refuses a shard this host does not own, so the far half goes back out + // through the service and lands wherever it belongs. + routed streampb.StreamServiceClient + + // Appends to one stream are serialized here. The node has to be durable + // before the frontier advances, which means writing it outside the + // transition that advances the frontier, and two concurrent writers could + // then commit in a different order than they wrote. Whichever node carried + // the higher transaction ID would win the read regardless of which writer + // actually committed. + // + // Serializing removes the interleaving. It is a stopgap: the real fix is to + // stage the node inside the CHASM transaction so write and commit order + // cannot diverge. Until then this also means appends are only safe within + // one process, which holds because these RPCs route to the shard owner. + // + // Striped rather than one lock per stream. The key comes from request input + // before the stream is known to exist, so a per-stream map would grow once + // per distinct id a caller names, including ids that resolve to nothing. + // Unrelated streams sharing a stripe only serialize with each other. + appendLk [appendStripes]sync.Mutex +} + +func newHandler( + shardController shard.Controller, + namespaceRegistry namespace.Registry, + logger log.Logger, + routed streampb.StreamServiceClient, +) *handler { + return &handler{ + shardController: shardController, + namespaceRegistry: namespaceRegistry, + logger: logger, + routed: routed, + } +} + +func streamKey(namespaceID, streamID string) string { + return namespaceID + "/" + streamID +} + +// withCallerInfo tags the context so the stream's direct persistence calls are +// attributed to the namespace that caused them. Without it they carry no caller +// name, which means they escape namespace rate limiting and priority as well as +// going uncounted in per-namespace metrics. The RPC path sets this via +// interceptors; calls made outside a request handler have to set it themselves. +func (h *handler) withCallerInfo(ctx context.Context, namespaceID string) context.Context { + name, err := h.namespaceRegistry.GetNamespaceName(namespace.ID(namespaceID)) + if err != nil { + return ctx + } + return headers.SetCallerInfo(ctx, headers.NewCallerInfo( + name.String(), headers.CallerTypeAPI, "")) +} + +func (h *handler) lockStream(namespaceID, streamID string) func() { + mu := &h.appendLk[appendStripe(streamKey(namespaceID, streamID))] + mu.Lock() + return mu.Unlock +} + +// Sized well above the per-host stream count that would make collisions matter. +// Appends to one stream are serialized anyway, so a collision costs only the +// unrelated stream's concurrency, never correctness. +const appendStripes = 2048 + +func appendStripe(key string) uint32 { + return farm.Fingerprint32([]byte(key)) % appendStripes +} + +// refFor builds a reference to a stream. A supplied run ID lets the engine skip +// resolving the current run, which is otherwise a persistence lookup on every +// call and dominates the cost of an otherwise cheap read. +func refFor(namespaceID, streamID string) chasm.ComponentRef { + return refForRun(namespaceID, streamID, "") +} + +func refForRun(namespaceID, streamID, runID string) chasm.ComponentRef { + return chasm.NewComponentRef[*stream.Stream](chasm.ExecutionKey{ + NamespaceID: namespaceID, + BusinessID: streamID, + RunID: runID, + }) +} + +// workflowRef builds a reference to the execution that owns an attached +// stream. An attached stream is a subcomponent, so it has no id of its own and +// everything about it is reached through its owner. +// +// An empty runID means the current run. A caller that supplies one is pinning: +// an owned stream does not carry across continue-as-new, so the successor's +// stream of the same name is a different, empty one, and a caller that meant +// the predecessor would otherwise be redirected to it without being told. +func workflowRef(namespaceID, workflowID, runID string) chasm.ComponentRef { + return chasm.NewComponentRef[*chasmworkflow.Workflow](chasm.ExecutionKey{ + NamespaceID: namespaceID, + BusinessID: workflowID, + RunID: runID, + }) +} + +// ownedStreamName resolves a name the caller left empty the same way a publish +// command does, so a reader addresses the default stream by omission just as a +// writer creates it by omission. +func ownedStreamName(name string) string { + if name == "" { + return chasmworkflow.DefaultStreamName + } + return name +} + +func (h *handler) CreateStream( + ctx context.Context, + req *streampb.CreateStreamRequest, +) (*streampb.CreateStreamResponse, error) { + in := req.GetFrontendRequest() + if in.GetStreamId() == "" { + return nil, serviceerror.NewInvalidArgument("stream id is required") + } + + result, err := chasm.StartExecution( + ctx, + chasm.ExecutionKey{NamespaceID: req.GetNamespaceId(), BusinessID: in.GetStreamId()}, + func(mctx chasm.MutableContext, input *streampb.CreateStreamInput) (*stream.Stream, error) { + return stream.NewStream(mctx, stream.NewStreamRequest{ + CollectionID: mctx.ExecutionKey().RunID, + Lifecycle: input.GetLifecycle(), + }) + }, + in, + ) + if err != nil { + return nil, err + } + return &streampb.CreateStreamResponse{ + FrontendResponse: &streampb.CreateStreamOutput{RunId: result.ExecutionKey.RunID}, + }, nil +} + +func (h *handler) AddMessages( + ctx context.Context, + req *streampb.AddMessagesRequest, +) (*streampb.AddMessagesResponse, error) { + in := req.GetFrontendRequest() + if len(in.GetMessages()) == 0 { + return nil, serviceerror.NewInvalidArgument("no messages to append") + } + + unlock := h.lockStream(req.GetNamespaceId(), in.GetStreamId()) + defer unlock() + + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + + ref := refForRun(req.GetNamespaceId(), in.GetStreamId(), in.GetRunId()) + state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return nil, err + } + + addReq := stream.AddMessagesRequest{ + Messages: in.GetMessages(), + ProducerID: in.GetProducerId(), + Sequence: in.GetSequence(), + OwnerEpoch: in.GetOwnerEpoch(), + } + if in.GetUseExpectedOffset() { + expected := in.GetExpectedOffset() + addReq.ExpectedOffset = &expected + } else { + // Without a caller-supplied expectation, pin to the head we just read. + // The transition then fails rather than appending at an offset whose + // node we did not write. + head := state.GetHeadOffset() + addReq.ExpectedOffset = &head + } + + result, _, err := chasm.UpdateComponent(ctx, ref, (*stream.Stream).AddMessages, addReq) + if err != nil { + return nil, err + } + + return &streampb.AddMessagesResponse{ + FrontendResponse: &streampb.AddMessagesOutput{ + FirstOffset: result.FirstOffset, + NextOffset: result.NextOffset, + Count: result.Count, + Deduplicated: result.Deduplicated, + }, + }, nil +} + +// AddWorkflowMessages appends to a stream a workflow owns, from outside that +// workflow. +// +// The workflow's own publishes ride its Workflow Task and cost no transition of +// their own. This producer is off-shard, so it pays one transition on the +// owning execution per batch, and batching is what keeps that cheap. It is the +// path a model activity streaming tokens takes, where the workflow is only +// bracketing what the activity produces. +func (h *handler) AddWorkflowMessages( + ctx context.Context, + req *streampb.AddWorkflowMessagesRequest, +) (*streampb.AddWorkflowMessagesResponse, error) { + in := req.GetFrontendRequest() + if len(in.GetMessages()) == 0 { + return nil, serviceerror.NewInvalidArgument("no messages to append") + } + + name := ownedStreamName(in.GetStreamName()) + + // Keyed on the owner and the name, which is what identifies the log here. + unlock := h.lockStream(req.GetNamespaceId(), in.GetWorkflowId()+"/"+name) + defer unlock() + + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + + ref := workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()) + + state, err := chasm.ReadComponent(ctx, ref, + func(wf *chasmworkflow.Workflow, cctx chasm.Context, streamName string) (*streampb.StreamState, error) { + return wf.OwnedStreamState(cctx, streamName) + }, name) + if err != nil { + return nil, err + } + if state == nil { + // Nothing has published yet, so the collection id and bucket size this + // write needs do not exist. Creating the stream is a transition, and + // only the first writer ever pays it. + state, _, err = chasm.UpdateComponent(ctx, ref, + (*chasmworkflow.Workflow).EnsureOwnedStream, name) + if err != nil { + return nil, err + } + } + + head := state.GetHeadOffset() + addReq := stream.AddMessagesRequest{ + Messages: in.GetMessages(), + ProducerID: in.GetProducerId(), + Sequence: in.GetSequence(), + // Pinned to the head just read, so a workflow task that published + // between the read and the commit fails this append rather than + // letting it claim offsets whose node it did not write. + ExpectedOffset: &head, + } + + result, _, err := chasm.UpdateComponent(ctx, ref, + func(wf *chasmworkflow.Workflow, mctx chasm.MutableContext, r stream.AddMessagesRequest) (stream.AddMessagesResult, error) { + return wf.AppendToOwnedStream(mctx, name, r) + }, addReq) + if err != nil { + return nil, err + } + + return &streampb.AddWorkflowMessagesResponse{ + FrontendResponse: &streampb.AddMessagesOutput{ + FirstOffset: result.FirstOffset, + NextOffset: result.NextOffset, + Count: result.Count, + Deduplicated: result.Deduplicated, + }, + }, nil +} + +func (h *handler) FinishWriting( + ctx context.Context, + req *streampb.FinishWritingRequest, +) (*streampb.FinishWritingResponse, error) { + in := req.GetFrontendRequest() + _, _, err := chasm.UpdateComponent( + ctx, + refFor(req.GetNamespaceId(), in.GetStreamId()), + func(s *stream.Stream, mctx chasm.MutableContext, producerID string) (struct{}, error) { + return struct{}{}, s.FinishWriting(mctx, producerID) + }, + in.GetProducerId(), + ) + if err != nil { + return nil, err + } + return &streampb.FinishWritingResponse{FrontendResponse: &streampb.FinishWritingOutput{}}, nil +} + +// SubscribeWorkflow registers a workflow as a consumer of a stream it owns. +// +// The cursor is written into the workflow's own state, not the stream's, which +// is what lets every later advance commit with the event that records it. Only +// a stream the workflow owns can be subscribed here: reaching one in another +// execution needs that stream's frontier, and reading it from inside the +// consuming workflow's transaction is a separate problem. +func (h *handler) SubscribeWorkflow( + ctx context.Context, + req *streampb.SubscribeWorkflowRequest, +) (*streampb.SubscribeWorkflowResponse, error) { + in := req.GetFrontendRequest() + + if in.GetStreamId() != "" { + return h.subscribeToExternalStream(ctx, req.GetNamespaceId(), in) + } + + startOffset, _, err := chasm.UpdateComponent( + ctx, + workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()), + func(wf *chasmworkflow.Workflow, mctx chasm.MutableContext, input *streampb.SubscribeWorkflowInput) (int64, error) { + return wf.SubscribeToOwnedStream( + mctx, ownedStreamName(input.GetStreamName()), input.GetStartOffset()) + }, + in, + ) + if err != nil { + return nil, err + } + + return &streampb.SubscribeWorkflowResponse{ + FrontendResponse: &streampb.SubscribeWorkflowOutput{StartOffset: startOffset}, + }, nil +} + +// subscribeToExternalStream registers a cursor against a stream in another +// execution. +// +// The pin goes on the stream before the cursor goes on the workflow, and the +// order is the guarantee: interrupted after the first write there is a pin +// holding storage nothing reads, which costs space. Interrupted after the +// other order there would be a cursor with no pin, and truncation would be free +// to take a range that cursor still points at. +func (h *handler) subscribeToExternalStream( + ctx context.Context, + namespaceID string, + in *streampb.SubscribeWorkflowInput, +) (*streampb.SubscribeWorkflowResponse, error) { + // The stream half goes out and comes back on the shard that owns it. This + // handler was routed to the consuming workflow, so the stream may well be + // somewhere else, and resolving it here would fail on any cluster with more + // than one history host. + // + // The pin still lands before the cursor, which is the guarantee: interrupted + // between them there is a pin holding storage nothing reads, which costs + // space, where the other order would leave a cursor with no pin and let + // truncation take a range it still points at. + registered, err := h.routed.RegisterStreamConsumer(ctx, &streampb.RegisterStreamConsumerRequest{ + NamespaceId: namespaceID, + FrontendRequest: &streampb.RegisterStreamConsumerInput{ + Namespace: in.GetNamespace(), + StreamId: in.GetStreamId(), + ConsumerWorkflowId: in.GetWorkflowId(), + StartOffset: in.GetStartOffset(), + }, + }) + if err != nil { + return nil, err + } + pin := registered.GetFrontendResponse() + + startOffset, _, err := chasm.UpdateComponent( + ctx, + workflowRef(namespaceID, in.GetWorkflowId(), in.GetOwnerRunId()), + func(wf *chasmworkflow.Workflow, mctx chasm.MutableContext, offset int64) (int64, error) { + return wf.SubscribeToExternalStream(mctx, chasmworkflow.ExternalStreamSubscription{ + StreamID: in.GetStreamId(), + CollectionID: pin.GetCollectionId(), + BucketSize: pin.GetBucketSize(), + StartOffset: offset, + KnownHead: pin.GetKnownHead(), + }) + }, + pin.GetStartOffset(), + ) + if err != nil { + return nil, err + } + + return &streampb.SubscribeWorkflowResponse{ + FrontendResponse: &streampb.SubscribeWorkflowOutput{StartOffset: startOffset}, + }, nil +} + +// RegisterStreamConsumer takes the pin, on the shard that owns the stream. +// +// Internal. Called by SubscribeWorkflow, which is routed to the consumer and so +// cannot reach the stream itself. It resolves a negative start offset here, +// where the frontier is, and hands back everything the cursor needs to address +// the log, so the consumer records facts rather than readings. +func (h *handler) RegisterStreamConsumer( + ctx context.Context, + req *streampb.RegisterStreamConsumerRequest, +) (*streampb.RegisterStreamConsumerResponse, error) { + in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + + streamID := in.GetStreamId() + ref := refFor(req.GetNamespaceId(), streamID) + + state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return nil, err + } + + startOffset := in.GetStartOffset() + if startOffset < 0 { + startOffset = state.GetHeadOffset() + } + if startOffset < state.GetBaseOffset() { + return nil, serviceerror.NewFailedPreconditionf( + "offset %d is below the stream's floor of %d", startOffset, state.GetBaseOffset()) + } + + consumerID := "workflow:" + in.GetConsumerWorkflowId() + if _, _, err := chasm.UpdateComponent( + ctx, + ref, + func(s *stream.Stream, mctx chasm.MutableContext, offset int64) (struct{}, error) { + return struct{}{}, s.RegisterConsumer( + mctx, consumerID, in.GetConsumerWorkflowId(), "", offset, true) + }, + startOffset, + ); err != nil { + return nil, err + } + + return &streampb.RegisterStreamConsumerResponse{ + FrontendResponse: &streampb.RegisterStreamConsumerOutput{ + StartOffset: startOffset, + CollectionId: state.GetCollectionId(), + BucketSize: state.GetBucketSize(), + KnownHead: state.GetHeadOffset(), + }, + }, nil +} + +// AdvanceConsumerHead tells one consumer that the frontier moved, on the shard +// that owns that consumer. +// +// Internal. Called by the notify task, which runs on the stream's shard and so +// cannot reach a consumer living anywhere else. +func (h *handler) AdvanceConsumerHead( + ctx context.Context, + req *streampb.AdvanceConsumerHeadRequest, +) (*streampb.AdvanceConsumerHeadResponse, error) { + in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + + if _, _, err := chasm.UpdateComponent( + ctx, + workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()), + func(wf *chasmworkflow.Workflow, mctx chasm.MutableContext, at int64) (struct{}, error) { + return struct{}{}, wf.AdvanceKnownHead(mctx, in.GetStreamId(), at) + }, + in.GetHeadOffset(), + ); err != nil { + return nil, err + } + return &streampb.AdvanceConsumerHeadResponse{ + FrontendResponse: &streampb.AdvanceConsumerHeadOutput{}, + }, nil +} + +func (h *handler) PollMessages( + ctx context.Context, + req *streampb.PollMessagesRequest, +) (*streampb.PollMessagesResponse, error) { + in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + + ref := refForRun(req.GetNamespaceId(), in.GetStreamId(), in.GetRunId()) + from := in.GetFromOffset() + + state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return nil, err + } + + // Blocking is only worth it once the reader is genuinely caught up. + if in.GetWaitNewMessages() && from == state.GetHeadOffset() && !state.GetClosed() { + // The window is re-read below, so only the blocking matters here. + if _, err := h.waitForMessages(ctx, ref, from, state); err != nil { + return nil, err + } + } + + wreq := stream.WindowRequest{From: from, MaxMessages: in.GetMaxMessages(), Topics: in.GetTopics()} + w, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).ReadWindow, wreq) + if err != nil { + return nil, err + } + out, err := formatWindow(w, wreq) + if err != nil { + return nil, err + } + return &streampb.PollMessagesResponse{FrontendResponse: out}, nil +} + +// PollWorkflowMessages reads a stream a workflow owns. +// +// Everything that addresses the stream comes from its owner: the shard, the +// frontier, and the collection the log nodes were written under. That is also +// why the log read below needs no special case. An attached stream's nodes are +// written under the owner's shard, which is the shard this call routed to. +func (h *handler) PollWorkflowMessages( + ctx context.Context, + req *streampb.PollWorkflowMessagesRequest, +) (*streampb.PollWorkflowMessagesResponse, error) { + in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + + ref := workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()) + name := ownedStreamName(in.GetStreamName()) + from := in.GetFromOffset() + + state, err := h.ownedStreamState(ctx, ref, name) + if err != nil { + return nil, err + } + + if in.GetWaitNewMessages() && from == state.GetHeadOffset() && !state.GetClosed() { + if _, err := h.waitForOwnedMessages(ctx, ref, name, from, state); err != nil { + return nil, err + } + } + + wreq := stream.WindowRequest{From: from, MaxMessages: in.GetMaxMessages(), Topics: in.GetTopics()} + w, err := chasm.ReadComponent(ctx, ref, readOwnedWindow, ownedWindowRequest{Name: name, Window: wreq}) + if err != nil { + return nil, err + } + out, err := formatWindow(w, wreq) + if err != nil { + return nil, err + } + return &streampb.PollWorkflowMessagesResponse{FrontendResponse: out}, nil +} + +// readWindow serves a reader's window out of a frontier the caller resolved. +// Standalone and attached streams differ only in where that frontier comes +// from, so nothing past it is aware of the difference. +// formatWindow turns a component read into the wire response. The read happens +// in the component, so the frontier and the bytes it was served with cannot +// disagree. +func formatWindow(w stream.Window, req stream.WindowRequest) (*streampb.PollMessagesOutput, error) { + out := &streampb.PollMessagesOutput{ + NextOffset: req.From, + HeadOffset: w.State.GetHeadOffset(), + Closed: w.State.GetClosed(), + CloseReason: w.State.GetCloseReason(), + } + if req.From == w.State.GetHeadOffset() { + return out, nil + } + + messages, next, err := stream.CollectMessages(w.Blobs, w.Starts, req.From, w.To, w.Limit, req.Topics) + if err != nil { + return nil, err + } + if next < w.To && len(messages) == 0 && len(req.Topics) > 0 { + // A page that filtered everything out still has to advance, or the + // caller loops forever on the same offsets. Limited to a filtered read + // on purpose: for any other reason a page comes back short, moving the + // reader past offsets it was never given would hide the short read. + next = w.To + } + out.Messages = messages + out.NextOffset = next + return out, nil +} + +// ownedWindowRequest names which attached stream to read and what to read. +type ownedWindowRequest struct { + Name string + Window stream.WindowRequest +} + +// readOwnedWindow reads a stream attached to a workflow. +// +// A closed execution can take no more publishes, from its own Workflow Task or +// from anywhere else, so its stream is finished whether or not a producer said +// so. Without that a reader tailing a workflow that ended stays parked forever. +func readOwnedWindow( + wf *chasmworkflow.Workflow, + cctx chasm.Context, + req ownedWindowRequest, +) (stream.Window, error) { + s := wf.OwnedStream(cctx, req.Name) + if s == nil { + // Nothing published yet, which reads as an empty stream so a reader can + // attach before the first append. + return stream.Window{State: &streampb.StreamState{Closed: !cctx.ExecutionInfo().CloseTime.IsZero()}, To: req.Window.From}, nil + } + w, err := s.ReadWindow(cctx, req.Window) + if err != nil { + return stream.Window{}, err + } + if !cctx.ExecutionInfo().CloseTime.IsZero() { + w.State.Closed = true + } + return w, nil +} + +// ownedStreamState snapshots an attached stream through the component that +// owns it. A stream the workflow has not published to yet reads as an empty +// one, so a reader may attach before the first event. +func (h *handler) ownedStreamState( + ctx context.Context, + ref chasm.ComponentRef, + name string, +) (*streampb.StreamState, error) { + state, err := chasm.ReadComponent(ctx, ref, readOwnedStream, name) + if err != nil { + return nil, err + } + return state, nil +} + +// readOwnedStream snapshots an attached stream and reports whether anything +// can still be added to it. +// +// A closed execution can take no more publishes, from its own Workflow Task or +// from anywhere else, so its stream is finished whether or not a producer said +// so. Without this a reader tailing a workflow that ended stays parked forever. +func readOwnedStream( + wf *chasmworkflow.Workflow, + cctx chasm.Context, + name string, +) (*streampb.StreamState, error) { + state, err := wf.OwnedStreamState(cctx, name) + if err != nil { + return nil, err + } + if state == nil { + state = &streampb.StreamState{} + } + if !cctx.ExecutionInfo().CloseTime.IsZero() { + state.Closed = true + } + return state, nil +} + +// waitForMessages blocks until the head passes the reader's offset or the +// stream closes. On the server's long-poll timeout it returns the state it last +// saw, so the caller gets an empty response and polls again rather than an +// error it would have to distinguish from a real failure. +func (h *handler) waitForMessages( + ctx context.Context, + ref chasm.ComponentRef, + from int64, + current *streampb.StreamState, +) (*streampb.StreamState, error) { + pollCtx, cancel := contextutil.WithDeadlineBuffer(ctx, stream.LongPollTimeout, stream.LongPollBuffer) + defer cancel() + + state, _, err := chasm.PollComponent(pollCtx, ref, + func(s *stream.Stream, _ chasm.Context, offset int64) (*streampb.StreamState, bool, error) { + // Monotonic, as PollComponent requires: the head only advances and + // closed never clears. + if !pollSatisfied(s.State, offset) { + return nil, false, nil + } + return common.CloneProto(s.State), true, nil + }, from) + return pollOutcome(pollCtx, ctx, state, err, current) +} + +// waitForOwnedMessages is waitForMessages against a stream reached through its +// owner. The predicate has to re-resolve the stream on every evaluation, +// because what the poll observes is the owning execution. +func (h *handler) waitForOwnedMessages( + ctx context.Context, + ref chasm.ComponentRef, + name string, + from int64, + current *streampb.StreamState, +) (*streampb.StreamState, error) { + pollCtx, cancel := contextutil.WithDeadlineBuffer(ctx, stream.LongPollTimeout, stream.LongPollBuffer) + defer cancel() + + state, _, err := chasm.PollComponent(pollCtx, ref, + func(wf *chasmworkflow.Workflow, cctx chasm.Context, offset int64) (*streampb.StreamState, bool, error) { + owned, err := readOwnedStream(wf, cctx, name) + if err != nil { + return nil, false, err + } + if !pollSatisfied(owned, offset) { + return nil, false, nil + } + return owned, true, nil + }, from) + return pollOutcome(pollCtx, ctx, state, err, current) +} + +// pollSatisfied is the monotonic condition PollComponent requires: the head +// only advances and closed never clears. +func pollSatisfied(state *streampb.StreamState, from int64) bool { + return state.GetHeadOffset() > from || state.GetClosed() +} + +// pollOutcome turns a long-poll result into the state the reader should be +// served. +func pollOutcome( + pollCtx, callerCtx context.Context, + state *streampb.StreamState, + err error, + current *streampb.StreamState, +) (*streampb.StreamState, error) { + if err != nil { + if pollCtx.Err() != nil && callerCtx.Err() == nil { + // Our long-poll budget expired, not the caller's. Hand back the + // state we already had so the reader gets an empty response and + // polls again, rather than an error it has to tell apart from a + // real failure. + return current, nil + } + return nil, err + } + if state == nil { + return current, nil + } + return state, nil +} + +func (h *handler) DescribeStream( + ctx context.Context, + req *streampb.DescribeStreamRequest, +) (*streampb.DescribeStreamResponse, error) { + in := req.GetFrontendRequest() + state, err := chasm.ReadComponent(ctx, + refFor(req.GetNamespaceId(), in.GetStreamId()), (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return nil, err + } + return &streampb.DescribeStreamResponse{ + FrontendResponse: &streampb.DescribeStreamOutput{State: state}, + }, nil +} + +// DescribeWorkflowStream reports the frontier of a stream a workflow owns. A +// reader needs it to start at the tail rather than at the beginning, which an +// attached stream offers no other way to find. +func (h *handler) DescribeWorkflowStream( + ctx context.Context, + req *streampb.DescribeWorkflowStreamRequest, +) (*streampb.DescribeWorkflowStreamResponse, error) { + in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + + state, err := h.ownedStreamState(ctx, + workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()), + ownedStreamName(in.GetStreamName())) + if err != nil { + return nil, err + } + return &streampb.DescribeWorkflowStreamResponse{ + FrontendResponse: &streampb.DescribeStreamOutput{State: state}, + }, nil +} + +func (h *handler) CloseStream( + ctx context.Context, + req *streampb.CloseStreamRequest, +) (*streampb.CloseStreamResponse, error) { + in := req.GetFrontendRequest() + _, _, err := chasm.UpdateComponent( + ctx, + refFor(req.GetNamespaceId(), in.GetStreamId()), + func(s *stream.Stream, mctx chasm.MutableContext, reason *commonpb.Payload) (struct{}, error) { + return struct{}{}, s.CloseAndSchedule(mctx, reason) + }, + in.GetReason(), + ) + if err != nil { + return nil, err + } + return &streampb.CloseStreamResponse{FrontendResponse: &streampb.CloseStreamOutput{}}, nil +} + +func (h *handler) TruncateStream( + ctx context.Context, + req *streampb.TruncateStreamRequest, +) (*streampb.TruncateStreamResponse, error) { + in := req.GetFrontendRequest() + + if _, _, err := chasm.UpdateComponent( + ctx, + refFor(req.GetNamespaceId(), in.GetStreamId()), + func(s *stream.Stream, mctx chasm.MutableContext, newBase int64) (struct{}, error) { + return struct{}{}, s.Truncate(mctx, newBase) + }, + in.GetNewBaseOffset(), + ); err != nil { + return nil, err + } + return &streampb.TruncateStreamResponse{FrontendResponse: &streampb.TruncateStreamOutput{}}, nil +} + +// ListStreams is intentionally not implemented here. It queries visibility, so +// it has no business ID to route on and the frontend answers it directly. + +func (h *handler) DeleteStream( + ctx context.Context, + req *streampb.DeleteStreamRequest, +) (*streampb.DeleteStreamResponse, error) { + in := req.GetFrontendRequest() + key := chasm.ExecutionKey{NamespaceID: req.GetNamespaceId(), BusinessID: in.GetStreamId()} + + // The payload is component state, so deleting the execution takes it too. + if err := chasm.DeleteExecution[*stream.Stream](ctx, key, chasm.DeleteExecutionRequest{}); err != nil { + return nil, err + } + return &streampb.DeleteStreamResponse{FrontendResponse: &streampb.DeleteStreamOutput{}}, nil +} diff --git a/chasm/lib/stream/service/library.go b/chasm/lib/stream/service/library.go new file mode 100644 index 00000000000..ffc6bb9a220 --- /dev/null +++ b/chasm/lib/stream/service/library.go @@ -0,0 +1,85 @@ +package service + +import ( + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "google.golang.org/grpc" +) + +const ( + libraryName = "stream" + componentName = "stream" +) + +var ( + Archetype = chasm.FullyQualifiedName(libraryName, componentName) + ArchetypeID = chasm.GenerateTypeID(Archetype) +) + +type library struct { + chasm.UnimplementedLibrary + handler *handler + retention *retentionTaskHandler + notifyConsumers *notifyConsumersTaskHandler +} + +func newLibrary(h *handler, retention *retentionTaskHandler, notifyConsumers *notifyConsumersTaskHandler) *library { + return &library{handler: h, retention: retention, notifyConsumers: notifyConsumers} +} + +// componentOnlyLibrary registers the component without the service, which is +// what the frontend needs in order to serialize component references. +type componentOnlyLibrary struct { + chasm.UnimplementedLibrary +} + +func newComponentOnlyLibrary() *componentOnlyLibrary { + return &componentOnlyLibrary{} +} + +func (l *componentOnlyLibrary) Name() string { + return libraryName +} + +func (l *componentOnlyLibrary) Components() []*chasm.RegistrableComponent { + return components() +} + +func components() []*chasm.RegistrableComponent { + return []*chasm.RegistrableComponent{ + chasm.NewRegistrableComponent[*stream.Stream]( + componentName, + chasm.WithBusinessIDAlias("StreamId"), + ), + // Registered here rather than with the workflow library because it is + // this package's type, even though it only ever hangs off a consuming + // workflow. + chasm.NewRegistrableComponent[*stream.Cursor]("streamCursor"), + } +} + +func (l *library) Name() string { + return libraryName +} + +func (l *library) Components() []*chasm.RegistrableComponent { + return components() +} + +func (l *library) Tasks() []*chasm.RegistrableTask { + return []*chasm.RegistrableTask{ + chasm.NewRegistrableSideEffectTask( + "streamRetention", + l.retention, + ), + chasm.NewRegistrableSideEffectTask( + "streamNotifyConsumers", + l.notifyConsumers, + ), + } +} + +func (l *library) RegisterServices(server *grpc.Server) { + streampb.RegisterStreamServiceServer(server, l.handler) +} diff --git a/chasm/lib/stream/service/tasks.go b/chasm/lib/stream/service/tasks.go new file mode 100644 index 00000000000..9e679e675b9 --- /dev/null +++ b/chasm/lib/stream/service/tasks.go @@ -0,0 +1,209 @@ +package service + +import ( + "context" + "errors" + + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/headers" + "go.temporal.io/server/common/log" + "go.temporal.io/server/common/log/tag" + "go.temporal.io/server/common/namespace" + "go.temporal.io/server/service/history/shard" +) + +// retentionTaskHandler deletes a stream once its retention has elapsed. Close +// only seals; deletion is deliberately later, so a consumer can still drain a +// finished stream without coordinating a shutdown with the producer. +type retentionTaskHandler struct { + chasm.SideEffectTaskHandlerBase[*streampb.StreamRetentionTask] + + shardController shard.Controller + namespaceRegistry namespace.Registry + logger log.Logger +} + +func newRetentionTaskHandler( + shardController shard.Controller, + namespaceRegistry namespace.Registry, + logger log.Logger, +) *retentionTaskHandler { + return &retentionTaskHandler{ + shardController: shardController, + namespaceRegistry: namespaceRegistry, + logger: logger, + } +} + +func (h *retentionTaskHandler) Validate( + _ chasm.Context, + s *stream.Stream, + _ chasm.TaskInvocation, + _ *streampb.StreamRetentionTask, +) (bool, error) { + // A stream that was reopened, or never closed, has nothing to expire. The + // task is scheduled at close and only meaningful while that still holds. + return s.State.GetClosed(), nil +} + +func (h *retentionTaskHandler) Execute( + ctx context.Context, + ref chasm.ComponentRef, + _ chasm.TaskAttributes, + _ *streampb.StreamRetentionTask, +) error { + namespaceID := ref.NamespaceID + + // Runs outside a request, so nothing has tagged the context yet. Deletions + // still have to be attributed to the namespace they belong to. + if name, nsErr := h.namespaceRegistry.GetNamespaceName(namespace.ID(namespaceID)); nsErr == nil { + ctx = headers.SetCallerInfo(ctx, headers.NewBackgroundLowCallerInfo(name.String())) + } + + // The payload is component state, so deleting the execution takes it too. + return chasm.DeleteExecution[*stream.Stream](ctx, ref.ExecutionKey, chasm.DeleteExecutionRequest{}) +} + +func (h *retentionTaskHandler) Discard( + _ context.Context, + _ chasm.ComponentRef, + _ chasm.TaskAttributes, + _ *streampb.StreamRetentionTask, +) error { + // Nothing to undo: the task carries no side effect until it executes. + return nil +} + +// notifyConsumersTaskHandler tells workflows in other executions that the +// stream moved. +// +// Appending never schedules a workflow task by itself, because a stream item is +// data an execution produced rather than a decision input to it. A workflow +// that subscribed is the exception, and it has no other way to find out: it +// cannot read another execution's frontier while closing its own transaction. +// So the frontier is pushed into its cursor, which dirties that execution and +// lets its own transaction close decide it owes a workflow task. +type notifyConsumersTaskHandler struct { + chasm.SideEffectTaskHandlerBase[*streampb.StreamNotifyConsumersTask] + + namespaceRegistry namespace.Registry + logger log.Logger + + // This task runs on the stream's shard. Its consumers live wherever their + // own executions do, so telling them goes back out through the service to + // be routed rather than resolved here. + routed streampb.StreamServiceClient +} + +func newNotifyConsumersTaskHandler( + namespaceRegistry namespace.Registry, + logger log.Logger, + routed streampb.StreamServiceClient, +) *notifyConsumersTaskHandler { + return ¬ifyConsumersTaskHandler{ + namespaceRegistry: namespaceRegistry, + logger: logger, + routed: routed, + } +} + +func (h *notifyConsumersTaskHandler) Validate( + _ chasm.Context, + s *stream.Stream, + _ chasm.TaskInvocation, + _ *streampb.StreamNotifyConsumersTask, +) (bool, error) { + for _, consumer := range s.State.GetConsumers() { + if consumer.GetExternal() && consumer.GetActive() { + return true, nil + } + } + return false, nil +} + +func (h *notifyConsumersTaskHandler) Execute( + ctx context.Context, + ref chasm.ComponentRef, + _ chasm.TaskAttributes, + _ *streampb.StreamNotifyConsumersTask, +) error { + namespaceID := ref.NamespaceID + streamID := ref.BusinessID + + if name, nsErr := h.namespaceRegistry.GetNamespaceName(namespace.ID(namespaceID)); nsErr == nil { + ctx = headers.SetCallerInfo(ctx, headers.NewBackgroundLowCallerInfo(name.String())) + } + + state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return err + } + head := state.GetHeadOffset() + + // Collected rather than returned at the first failure, so one unreachable + // consumer does not stop the others being told in this pass, and returned + // at the end so the task retries rather than dropping the notification. + // + // Dropping it is not survivable for a consumer on another host. The engine + // resolves this ref against the local shard controller, so a consumer whose + // shard lives elsewhere fails every time, its known head never advances, + // and delivery clips to it: Path C across executions never delivers at all. + // A retry does not fix that. It makes it visible, which a warning did not. + var notifyErrs []error + + for consumerID, consumer := range state.GetConsumers() { + if !consumer.GetExternal() || !consumer.GetActive() || consumer.GetOffset() >= head { + continue + } + + _, err := h.routed.AdvanceConsumerHead(ctx, &streampb.AdvanceConsumerHeadRequest{ + NamespaceId: namespaceID, + FrontendRequest: &streampb.AdvanceConsumerHeadInput{ + WorkflowId: consumer.GetWorkflowId(), + OwnerRunId: consumer.GetRunId(), + StreamId: streamID, + HeadOffset: head, + }, + }) + var gone *serviceerror.NotFound + switch { + case errors.As(err, &gone): + // The consumer's execution is gone, so its replay floor is holding + // storage for a recovery that can no longer be asked for. This is + // the one place that finds out: the probe happens exactly when the + // frontier has moved past the consumer, which is exactly when the + // floor starts to matter. + if _, _, releaseErr := chasm.UpdateComponent( + ctx, ref, + func(s *stream.Stream, mctx chasm.MutableContext, id string) (struct{}, error) { + s.DeregisterConsumer(mctx, id) + return struct{}{}, nil + }, + consumerID, + ); releaseErr != nil { + notifyErrs = append(notifyErrs, releaseErr) + } + case err != nil: + h.logger.Error("failed to tell a stream consumer that the frontier moved", + tag.NewStringTag("stream-id", streamID), + tag.NewStringTag("consumer-workflow-id", consumer.GetWorkflowId()), + tag.Error(err)) + notifyErrs = append(notifyErrs, err) + default: + // Told, and still there. Nothing to clean up. + } + } + return errors.Join(notifyErrs...) +} + +func (h *notifyConsumersTaskHandler) Discard( + _ context.Context, + _ chasm.ComponentRef, + _ chasm.TaskAttributes, + _ *streampb.StreamNotifyConsumersTask, +) error { + return nil +} diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go new file mode 100644 index 00000000000..1b0cabec88a --- /dev/null +++ b/chasm/lib/stream/stream.go @@ -0,0 +1,699 @@ +package stream + +import ( + "crypto/sha256" + "maps" + "slices" + "time" + + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" +) + +// Stream is a durable, offset-addressed append-only sequence. It holds only the +// frontier: the payload bytes live in the log (see log.go), so this state is +// O(producers + consumers) no matter how long the stream gets. +// +// Appending never schedules a workflow task. A stream item is data produced by +// an execution, not a decision input to it, so nothing in a workflow's state +// machine advances because one arrived. +type Stream struct { + chasm.UnimplementedComponent + + State *streampb.StreamState + + // Batches holds the payload, each keyed by the offset it starts at. They are + // data nodes, so they replicate with the component and are reclaimed with + // it, and a retry addresses the same key rather than racing it. + // + // They also live in mutable state, which caps a stream at the execution + // size limit. That is the trade for not owning a store. + Batches chasm.Map[int64, *commonpb.DataBlob] + + // Present so streams are listable. Operators need to find them the same way + // they find workflows, and without this the only way to reach a stream is + // to already know its ID. + Visibility chasm.Field[*chasm.Visibility] +} + +type NewStreamRequest struct { + CollectionID string + BucketSize int64 + Lifecycle *streampb.StreamLifecycle + + // Attached means the stream is a subcomponent of another execution rather + // than a root. CHASM requires a visibility component to be an immediate + // child of the root, so an attached stream carries none and is found + // through its owner instead of through ListStreams. + Attached bool +} + +type AddMessagesRequest struct { + Messages []*streampb.StreamMessage + + // Optional idempotency. A producer supplies either an identity and + // sequence, or an expected offset, or neither and accepts at-least-once. + ProducerID string + Sequence int64 + ExpectedOffset *int64 + + // Optional fencing. Rejected if below the stream's current epoch. + OwnerEpoch int64 +} + +type AddMessagesResult struct { + FirstOffset int64 + NextOffset int64 + Count int64 + + // True when a retry matched a recorded producer sequence, so nothing was + // appended and the original offsets are returned. + Deduplicated bool + + // The bytes this append wrote, so a caller can prime a cache without + // reading them back. Nil when deduplicated. + Blob *commonpb.DataBlob +} + +func NewStream(ctx chasm.MutableContext, req NewStreamRequest) (*Stream, error) { + bucketSize := req.BucketSize + if bucketSize <= 0 { + bucketSize = DefaultBucketSize + } + visibility := chasm.NewEmptyField[*chasm.Visibility]() + if !req.Attached { + visibility = chasm.NewComponentField(ctx, chasm.NewVisibility(ctx)) + } + return &Stream{ + Visibility: visibility, + Batches: make(chasm.Map[int64, *commonpb.DataBlob]), + State: &streampb.StreamState{ + CollectionId: req.CollectionID, + BucketSize: bucketSize, + Lifecycle: req.Lifecycle, + Producers: make(map[string]*streampb.ProducerCursor), + Consumers: make(map[string]*streampb.ConsumerCursor), + }, + }, nil +} + +// ContextMetadata satisfies chasm.RootComponent. A stream carries no metadata +// worth propagating to the request context. +func (s *Stream) ContextMetadata(_ chasm.Context) map[string]string { + return nil +} + +// Terminate seals the stream so a forced shutdown does not leave it accepting +// writes. Data already appended stays readable, because consumers may have read +// it and the stream is append-only. +func (s *Stream) Terminate( + mctx chasm.MutableContext, + req chasm.TerminateComponentRequest, +) (chasm.TerminateComponentResponse, error) { + reason := &commonpb.Payload{Data: []byte(req.Reason)} + return chasm.TerminateComponentResponse{}, s.CloseAndSchedule(mctx, reason) +} + +// Snapshot returns a copy of the frontier for read paths. It is a copy because +// the caller reads it outside the transition that produced it. +func (s *Stream) Snapshot(_ chasm.Context, _ struct{}) (*streampb.StreamState, error) { + return common.CloneProto(s.State), nil +} + +func (s *Stream) LifecycleState(_ chasm.Context) chasm.LifecycleState { + if s.State.Closed { + return chasm.LifecycleStateCompleted + } + return chasm.LifecycleStateRunning +} + +// AddMessages assigns a contiguous offset range and writes the bytes into the +// component, so the payload and the frontier commit in one transaction. A torn +// append is therefore not a state anyone can observe. +func (s *Stream) AddMessages( + mctx chasm.MutableContext, + req AddMessagesRequest, +) (AddMessagesResult, error) { + if s.State.Closed { + return AddMessagesResult{}, serviceerror.NewFailedPrecondition("stream is closed") + } + if len(req.Messages) == 0 { + return AddMessagesResult{}, serviceerror.NewInvalidArgument("no messages to append") + } + if len(req.Messages) > MaxMessagesPerBatch { + return AddMessagesResult{}, serviceerror.NewInvalidArgumentf( + "batch of %d exceeds the limit of %d messages", len(req.Messages), MaxMessagesPerBatch) + } + if err := checkBatchBytes(req.Messages); err != nil { + return AddMessagesResult{}, err + } + + blob, err := marshalBatch(req.Messages) + if err != nil { + return AddMessagesResult{}, err + } + hash := contentHash(blob.Data) + + if replay, err := s.checkProducer(req, hash); err != nil || replay != nil { + if err != nil { + return AddMessagesResult{}, err + } + return *replay, nil + } + + if req.OwnerEpoch != 0 && req.OwnerEpoch < s.State.OwnerEpoch { + return AddMessagesResult{}, serviceerror.NewFailedPrecondition("producer has been fenced") + } + + // After the retry check, so a known producer is never rejected for room. + if err := s.checkProducerRoom(req.ProducerID); err != nil { + return AddMessagesResult{}, err + } + + // Before anything is written, because the alternative is to write and then + // discover the cap can only be met by deleting bytes a consumer's committed + // History still refers to. Refusing the write is the honest half of that + // choice: capacity may constrain what is admitted, and may not quietly take + // back a workflow's ability to replay a decision it already made. + if err := s.checkCapRoom(int64(len(req.Messages))); err != nil { + return AddMessagesResult{}, err + } + + if req.ExpectedOffset != nil && *req.ExpectedOffset != s.State.HeadOffset { + return AddMessagesResult{}, serviceerror.NewAlreadyExistsf( + "expected offset %d but stream head is %d", *req.ExpectedOffset, s.State.HeadOffset) + } + + first := s.State.HeadOffset + count := int64(len(req.Messages)) + + if s.Batches == nil { + s.Batches = make(chasm.Map[int64, *commonpb.DataBlob]) + } + s.Batches[first] = chasm.NewDataField(mctx, blob) + + s.State.HeadOffset = first + count + if req.ProducerID != "" { + if s.State.Producers == nil { + s.State.Producers = make(map[string]*streampb.ProducerCursor) + } + s.State.Producers[req.ProducerID] = &streampb.ProducerCursor{ + Seq: req.Sequence, + FirstOffset: first, + Count: count, + ContentHash: hash, + } + } + + s.applyCap() + result := AddMessagesResult{ + FirstOffset: first, + NextOffset: s.State.HeadOffset, + Count: count, + Blob: blob, + } + s.notifyConsumers(mctx) + return result, nil +} + +// notifyConsumers schedules the wake for consumers this append left behind. +// +// Only a workflow in another execution needs it. One consuming a stream it owns +// sees the new frontier while closing its own transaction, so waking it through +// a task would only duplicate a decision already made locally. +func (s *Stream) notifyConsumers(mctx chasm.MutableContext) { + // The append path previews itself against a detached copy to work out which + // log node to write, and that preview has no transition to attach a task + // to. Only the real transition, which carries a context, schedules one. + if mctx == nil { + return + } + for _, consumer := range s.State.Consumers { + if consumer.GetExternal() && consumer.GetActive() && consumer.GetOffset() < s.State.HeadOffset { + mctx.AddTask(s, chasm.TaskAttributes{ScheduledTime: mctx.Now(s)}, &streampb.StreamNotifyConsumersTask{}) + return + } + } +} + +// checkProducerRoom keeps the dedup table bounded. +// +// Entries whose whole batch sits below the floor go first: a retry of a batch +// that truncation already removed cannot be served its recorded offsets +// anyway, so the entry has no use left. +func (s *Stream) checkProducerRoom(producerID string) error { + if producerID == "" { + return nil + } + if _, known := s.State.Producers[producerID]; known { + return nil + } + if len(s.State.Producers) < MaxProducersPerStream { + return nil + } + for id, cursor := range s.State.Producers { + if cursor.GetFirstOffset()+cursor.GetCount() <= s.State.GetBaseOffset() { + delete(s.State.Producers, id) + } + } + if len(s.State.Producers) >= MaxProducersPerStream { + return serviceerror.NewInvalidArgumentf( + "stream already tracks %d producers, which is the limit", MaxProducersPerStream) + } + return nil +} + +// checkProducer applies per-producer idempotency. It returns a replay result +// when the request is a genuine retry, and an error when it is not a retry but +// cannot be accepted either. +func (s *Stream) checkProducer(req AddMessagesRequest, hash []byte) (*AddMessagesResult, error) { + if req.ProducerID == "" { + return nil, nil + } + cursor := s.State.Producers[req.ProducerID] + if cursor == nil { + return nil, nil + } + if cursor.Fenced { + return nil, serviceerror.NewFailedPrecondition("producer has finished writing to this stream") + } + if req.Sequence > cursor.Seq { + return nil, nil + } + if req.Sequence < cursor.Seq { + return nil, serviceerror.NewInvalidArgumentf( + "stale producer sequence %d, last accepted is %d", req.Sequence, cursor.Seq) + } + // Same sequence. Identical content is a retry; different content is a + // client bug, and returning the recorded offsets would report success while + // silently dropping the caller's data. + if !equalHash(cursor.ContentHash, hash) { + return nil, serviceerror.NewInvalidArgumentf( + "producer sequence %d already used with different content", req.Sequence) + } + return &AddMessagesResult{ + FirstOffset: cursor.FirstOffset, + NextOffset: cursor.FirstOffset + cursor.Count, + Count: cursor.Count, + Deduplicated: true, + }, nil +} + +// FinishWriting ends one producer's writes without closing the stream, so other +// producers carry on. Weaker than Close on purpose. +func (s *Stream) FinishWriting(_ chasm.MutableContext, producerID string) error { + if producerID == "" { + return serviceerror.NewInvalidArgument("producer id is required") + } + if s.State.Producers == nil { + s.State.Producers = make(map[string]*streampb.ProducerCursor) + } + cursor := s.State.Producers[producerID] + if cursor == nil { + cursor = &streampb.ProducerCursor{Seq: -1} + s.State.Producers[producerID] = cursor + } + cursor.Fenced = true + return nil +} + +// Close seals the stream. It does not delete it: a closed stream stays readable +// through retention, which is what removes the shutdown handshake the current +// signal-based implementation forces on users. +// Close returns when retention deletion should be scheduled, or the zero time +// if the stream was already closed or has no retention configured. Scheduling +// is the caller's job, which keeps the component a pure state transition and +// testable without a live context. +func (s *Stream) Close(now time.Time, reason *commonpb.Payload) time.Time { + if s.State.Closed { + return time.Time{} + } + s.State.Closed = true + s.State.CloseReason = reason + s.State.CloseTime = timestamppb.New(now) + + retention := s.State.GetLifecycle().GetRetention().AsDuration() + if retention <= 0 { + return time.Time{} + } + return now.Add(retention) +} + +// CloseAndSchedule closes the stream and arms retention if it asked for it. +func (s *Stream) CloseAndSchedule(mctx chasm.MutableContext, reason *commonpb.Payload) error { + if at := s.Close(mctx.Now(s), reason); !at.IsZero() { + mctx.AddTask(s, chasm.TaskAttributes{ScheduledTime: at}, &streampb.StreamRetentionTask{}) + } + return nil +} + +// Truncate advances the readable floor. +// +// It stops at an active consumer's replay floor. A workflow that consumed a +// range recorded that range in its History and can be asked to replay from it, +// so those bytes are part of its recovery rather than spare capacity. Dropping +// them succeeds here and fails much later, during a replay nobody is watching, +// which is the worst place to find out. +// +// The floor is released by deregistering the consumer, which is an act someone +// takes deliberately. An operator who means to drop the bytes anyway does that +// first, and then this call goes through. +// +// A consumer that is behind but not active is not protected. Reading from +// below the base is an error naming where the stream now starts, the same +// answer a log with a retention window gives anywhere else. +func (s *Stream) Truncate(_ chasm.MutableContext, newBase int64) error { + if newBase < s.State.BaseOffset { + return serviceerror.NewInvalidArgumentf( + "cannot truncate backwards from %d to %d", s.State.BaseOffset, newBase) + } + if newBase > s.State.HeadOffset { + return serviceerror.NewInvalidArgumentf( + "cannot truncate past head offset %d", s.State.HeadOffset) + } + if floor, holder, pinned := s.replayFloor(); pinned && newBase > floor { + return serviceerror.NewFailedPreconditionf( + "cannot truncate to %d: consumer %q still depends on offset %d and above "+ + "to replay; deregister it first if those messages are no longer needed", + newBase, holder, floor) + } + s.State.BaseOffset = newBase + s.reclaim(newBase) + return nil +} + +// replayFloor is the oldest offset any active consumer's History still depends +// on, and who is holding it. Named, because a refusal that does not say which +// consumer to look at leaves the operator with nothing to act on. +func (s *Stream) replayFloor() (int64, string, bool) { + var floor int64 + var holder string + found := false + for id, c := range s.State.Consumers { + if !c.GetActive() { + continue + } + if !found || c.GetReplayFloor() < floor { + floor = c.GetReplayFloor() + holder = id + found = true + } + } + return floor, holder, found +} + +// reclaim drops batches lying entirely below the readable floor. A batch +// straddling the floor stays, because the offsets above it are still readable. +func (s *Stream) reclaim(newBase int64) { + starts := s.batchStarts() + for i, start := range starts { + end := s.State.HeadOffset + if i+1 < len(starts) { + end = starts[i+1] + } + if end > newBase { + return + } + delete(s.Batches, start) + } +} + +// batchStarts returns the batch keys in offset order. Reading and reclaiming +// both need where a batch ends, which is where the next one begins. +func (s *Stream) batchStarts() []int64 { + return slices.Sorted(maps.Keys(s.Batches)) +} + +// WindowRequest asks for whatever a reader can be given from an offset. +type WindowRequest struct { + From int64 + MaxMessages int32 + Topics []string +} + +// Window is one read's worth: the frontier it was served against, the batches +// covering the range, and the range itself. +type Window struct { + State *streampb.StreamState + Blobs []*commonpb.DataBlob + Starts []int64 + To int64 + Limit int +} + +// ReadWindow serves a read from the component, so the frontier and the bytes +// come from one view. Read separately they can disagree, because the frontier +// moves while the bytes are being fetched. +func (s *Stream) ReadWindow(ctx chasm.Context, req WindowRequest) (Window, error) { + if req.From < s.State.BaseOffset { + return Window{}, serviceerror.NewFailedPreconditionf( + "offset %d has been truncated, the stream starts at %d", req.From, s.State.BaseOffset) + } + if req.From > s.State.HeadOffset { + return Window{}, serviceerror.NewInvalidArgumentf( + "offset %d is past the stream head %d", req.From, s.State.HeadOffset) + } + + // Clamped at both ends. The caller picks the page size, and an unclamped + // one lets a single poll ask the store to materialise the whole stream. + limit := int(req.MaxMessages) + if limit <= 0 || limit > DefaultMaxMessagesPerPoll { + limit = DefaultMaxMessagesPerPoll + } + w := Window{State: common.CloneProto(s.State), To: req.From, Limit: limit} + if req.From == s.State.HeadOffset { + return w, nil + } + + // Clip to what the caller can actually be given. One offset is one message, + // so the bound is exact. Without it a poll for a single message off a long + // stream materialises every batch to the head before trimming. + w.To = min(s.State.HeadOffset, req.From+int64(limit)) + blobs, starts, err := s.ReadBatches(ctx, req.From, w.To, 0) + if err != nil { + return Window{}, err + } + w.Blobs, w.Starts = blobs, starts + return w, nil +} + +// ReadBatches returns the batches covering [from, to), oldest first, alongside +// the offset each one starts at. A read landing mid-batch gets the batch +// holding it, because a consumer asks for an offset rather than for a batch. +// Blobs come back unparsed: decoding user payloads is the SDK's job. +func (s *Stream) ReadBatches( + ctx chasm.Context, + from int64, + to int64, + maxBatches int, +) ([]*commonpb.DataBlob, []int64, error) { + if from >= to { + return nil, nil, nil + } + var blobs []*commonpb.DataBlob + var starts []int64 + all := s.batchStarts() + for i, start := range all { + end := s.State.HeadOffset + if i+1 < len(all) { + end = all[i+1] + } + if end <= from { + continue + } + if start >= to { + break + } + blobs = append(blobs, s.Batches[start].Get(ctx)) + starts = append(starts, start) + if maxBatches > 0 && len(blobs) >= maxBatches { + break + } + } + return blobs, starts, nil +} + +// applyCap advances the readable floor when the stream is over its message cap. +// Evaluated at the end of a successful append rather than by a sweeper: the +// append transition is already writing, so folding the check into it costs +// nothing and keeps the cap tight instead of eventually true. +func (s *Stream) applyCap() { + maxItems := s.State.GetLifecycle().GetMaxItems() + if maxItems <= 0 { + return + } + readable := s.State.HeadOffset - s.State.BaseOffset + if readable <= maxItems { + return + } + newBase := s.State.HeadOffset - maxItems + // Clamped rather than refused, because refusing belongs to admission and + // has already happened: checkCapRoom turned away the append that would have + // needed this. Reaching the clamp means a consumer registered after the + // messages were written, and keeping its bytes is still the right answer. + if floor, _, pinned := s.replayFloor(); pinned && newBase > floor { + newBase = floor + } + if newBase <= s.State.BaseOffset { + return + } + s.State.BaseOffset = newBase + s.reclaim(newBase) +} + +// checkCapRoom refuses an append the cap could only absorb by dropping bytes an +// active consumer still needs. +// +// A capped stream with no consumer behaves as before: the oldest messages go. +// The refusal only arrives when honouring the cap and honouring a recorded +// consumption are the same messages, and it names the consumer so the operator +// knows what to do about it. +func (s *Stream) checkCapRoom(count int64) error { + maxItems := s.State.GetLifecycle().GetMaxItems() + if maxItems <= 0 { + return nil + } + wantBase := s.State.HeadOffset + count - maxItems + if wantBase <= s.State.BaseOffset { + return nil + } + floor, holder, pinned := s.replayFloor() + if !pinned || wantBase <= floor { + return nil + } + return serviceerror.NewResourceExhaustedf( + enumspb.RESOURCE_EXHAUSTED_CAUSE_UNSPECIFIED, + "stream is at its cap of %d messages and consumer %q still depends on "+ + "offset %d and above to replay; the append would have to delete those "+ + "messages to make room", + maxItems, holder, floor) +} + +// RegisterConsumer records an in-workflow consumer, so appends know who to wake +// and retention knows what it may not delete. +// +// The floor it records is where the subscription started, not where it has read +// to. The ranges this consumer already took are written into its History, and a +// replay is asked to reproduce them, so the bytes behind the read position are +// the ones a recovery needs. Deregistering releases the floor. +func (s *Stream) RegisterConsumer( + _ chasm.MutableContext, + consumerID string, + workflowID string, + runID string, + offset int64, + external bool, +) error { + if consumerID == "" { + return serviceerror.NewInvalidArgument("consumer id is required") + } + if offset < s.State.BaseOffset { + return serviceerror.NewFailedPreconditionf( + "offset %d is below the stream's floor of %d", offset, s.State.BaseOffset) + } + if _, known := s.State.Consumers[consumerID]; !known && + len(s.State.Consumers) >= MaxConsumersPerStream { + return serviceerror.NewInvalidArgumentf( + "stream already has %d consumers, which is the limit", MaxConsumersPerStream) + } + if s.State.Consumers == nil { + s.State.Consumers = make(map[string]*streampb.ConsumerCursor) + } + if existing, ok := s.State.Consumers[consumerID]; ok { + // A consumer coming back after its floor was released can find the + // stream has moved past what its History refers to. Saying so here is + // the only chance to say it before the workflow depends on it again. + if existing.GetReplayFloor() < s.State.BaseOffset { + return serviceerror.NewFailedPreconditionf( + "consumer %q recorded offset %d, and the stream now starts at %d", + consumerID, existing.GetReplayFloor(), s.State.BaseOffset) + } + existing.Active = true + return nil + } + s.State.Consumers[consumerID] = &streampb.ConsumerCursor{ + WorkflowId: workflowID, + RunId: runID, + Offset: offset, + Active: true, + External: external, + ReplayFloor: offset, + } + return nil +} + +// AdvanceConsumer moves a consumer's pin forward as it reads. It never moves +// backwards: the floor is what lets a recorded range still be re-read, so +// lowering it would give back a guarantee already written to History. +func (s *Stream) AdvanceConsumer(_ chasm.MutableContext, consumerID string, offset int64) { + consumer, ok := s.State.Consumers[consumerID] + if !ok || offset <= consumer.Offset { + return + } + consumer.Offset = offset +} + +// consumerPin is the lowest offset any active consumer has reached. Nothing in +// the write path consults it, by the decision above; it is how the consumer +// table is read back, and what asserts that a re-registration cannot rewind a +// consumer's recorded position. +func (s *Stream) consumerPin() (int64, bool) { + var pin int64 + found := false + for _, c := range s.State.Consumers { + if !c.Active { + continue + } + if !found || c.Offset < pin { + pin = c.Offset + found = true + } + } + return pin, found +} + +// DeregisterConsumer releases the floor a consumer was holding, so retention +// and the message cap can reach its messages again. +func (s *Stream) DeregisterConsumer(_ chasm.MutableContext, consumerID string) { + if consumer, ok := s.State.Consumers[consumerID]; ok { + consumer.Active = false + } +} + +func marshalBatch(messages []*streampb.StreamMessage) (*commonpb.DataBlob, error) { + // The serialized batch is also the producer's deduplication fingerprint, and + // protobuf map iteration order is not stable. A record carrying payload or + // message metadata would otherwise hash differently on a retry and be + // refused as a conflicting duplicate of itself. + data, err := (proto.MarshalOptions{Deterministic: true}).Marshal(&streampb.StreamMessageBatch{Messages: messages}) + if err != nil { + return nil, err + } + return &commonpb.DataBlob{ + EncodingType: enumspb.ENCODING_TYPE_PROTO3, + Data: data, + }, nil +} + +func contentHash(data []byte) []byte { + sum := sha256.Sum256(data) + return sum[:] +} + +func equalHash(a, b []byte) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go new file mode 100644 index 00000000000..663d2dc1a02 --- /dev/null +++ b/chasm/lib/stream/stream_test.go @@ -0,0 +1,523 @@ +package stream + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "google.golang.org/protobuf/types/known/durationpb" +) + +// Built directly rather than through NewStream: these exercise state +// transitions, and NewStream also wires a visibility field that needs a live +// context. Construction through the real path is covered end to end in +// tests/stream_test.go. +func newTestStream(t *testing.T, bucketSize int64) *Stream { + t.Helper() + return &Stream{ + State: &streampb.StreamState{ + CollectionId: "col-1", + BucketSize: bucketSize, + Producers: make(map[string]*streampb.ProducerCursor), + Consumers: make(map[string]*streampb.ConsumerCursor), + }, + } +} + +func msgs(bodies ...string) []*streampb.StreamMessage { + out := make([]*streampb.StreamMessage, len(bodies)) + for i, b := range bodies { + out[i] = &streampb.StreamMessage{ + Body: &commonpb.Payload{Data: []byte(b)}, + Kind: streampb.STREAM_MESSAGE_KIND_DATA, + } + } + return out +} + +func TestAddMessagesAssignsContiguousOffsets(t *testing.T) { + s := newTestStream(t, 100) + + first, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) + require.NoError(t, err) + require.Equal(t, int64(0), first.FirstOffset) + require.Equal(t, int64(3), first.Count) + require.Equal(t, int64(3), first.NextOffset) + + second, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) + require.NoError(t, err) + require.Equal(t, int64(3), second.FirstOffset) + require.Equal(t, int64(5), second.NextOffset) + require.Equal(t, int64(5), s.State.HeadOffset) +} + +func TestAddMessagesWritesTheBatchIntoTheComponent(t *testing.T) { + s := newTestStream(t, 100) + + res, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + require.NoError(t, err) + require.NotEmpty(t, res.Blob.Data) + + // Keyed by the offset it starts at, which is the key a retry of this + // append writes under, so the retry replaces rather than races. + require.Len(t, s.Batches, 1) + _, ok := s.Batches[0] + require.True(t, ok, "the batch must be keyed by its first offset") + require.Equal(t, int64(2), s.State.HeadOffset) +} + +func TestDedupReturnsOriginalOffsets(t *testing.T) { + s := newTestStream(t, 100) + req := AddMessagesRequest{Messages: msgs("a", "b"), ProducerID: "p1", Sequence: 1} + + first, err := s.AddMessages(nil, req) + require.NoError(t, err) + require.False(t, first.Deduplicated) + + retry := req + again, err := s.AddMessages(nil, retry) + require.NoError(t, err) + require.True(t, again.Deduplicated) + require.Equal(t, first.FirstOffset, again.FirstOffset) + require.Nil(t, again.Blob, "a deduplicated retry writes nothing") + require.Equal(t, int64(2), s.State.HeadOffset, "a retry must not advance the head") +} + +func TestDedupIgnoresPayloadMetadataMapOrder(t *testing.T) { + s := newTestStream(t, 100) + for attempt := range 32 { + body := &commonpb.Payload{ + Data: []byte("encoded"), + Metadata: map[string][]byte{ + "encoding": []byte("test/envelope"), + "key-id": []byte("key-1"), + "nonce": []byte("fixed-for-this-record"), + }, + } + result, err := s.AddMessages(nil, AddMessagesRequest{ + Messages: []*streampb.StreamMessage{{ + Kind: streampb.STREAM_MESSAGE_KIND_DATA, + Body: body, + Metadata: map[string]*commonpb.Payload{"attempt": body, "checkpoint": body}, + }}, + ProducerID: "encoded-producer", Sequence: 1, + }) + require.NoError(t, err, "identical retry %d must ignore protobuf map iteration order", attempt) + require.Equal(t, attempt > 0, result.Deduplicated) + require.Equal(t, int64(0), result.FirstOffset) + require.Equal(t, int64(1), s.State.HeadOffset) + } +} + +func TestDedupRejectsDifferentContent(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("a"), ProducerID: "p1", Sequence: 1, + }) + require.NoError(t, err) + + // Returning the recorded offsets here would report success while dropping + // the caller's data, which is worse than failing. + _, err = s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("different"), ProducerID: "p1", Sequence: 1, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "different content") +} + +func TestExpectedOffsetMismatchReportsHead(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a")}) + require.NoError(t, err) + + stale := int64(0) + _, err = s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("b"), ExpectedOffset: &stale, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "stream head is 1") +} + +func TestOwnerEpochFencesStaleProducer(t *testing.T) { + s := newTestStream(t, 100) + s.State.OwnerEpoch = 5 + + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a"), OwnerEpoch: 4}) + require.Error(t, err) + + _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a"), OwnerEpoch: 5}) + require.NoError(t, err) +} + +func TestFinishWritingFencesOneProducerOnly(t *testing.T) { + s := newTestStream(t, 100) + require.NoError(t, s.FinishWriting(nil, "p1")) + + _, err := s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("a"), ProducerID: "p1", Sequence: 1, + }) + require.Error(t, err) + + // Another producer is unaffected: finishing is per-producer, not a close. + _, err = s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("a"), ProducerID: "p2", Sequence: 1, + }) + require.NoError(t, err) + require.False(t, s.State.Closed) +} + +func TestCloseRejectsFurtherAppends(t *testing.T) { + s := newTestStream(t, 100) + s.Close(time.Now(), nil) + + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a")}) + require.Error(t, err) + var precondition *serviceerror.FailedPrecondition + require.ErrorAs(t, err, &precondition) +} + +func TestReadSpansBatchesAndStartsAtTheBatchHoldingTheOffset(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) + require.NoError(t, err) + _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) + require.NoError(t, err) + require.Len(t, s.Batches, 2) + + // A read from offset 1 lands inside the first batch. It gets that batch + // whole, because a consumer asks for an offset and not for a batch, and + // the batch is the smallest thing stored. + blobs, starts, err := s.ReadBatches(nil, 1, 5, 0) + require.NoError(t, err) + require.Len(t, blobs, 2) + require.Equal(t, []int64{0, 3}, starts) + + // A read wholly inside the second batch does not drag the first along. + blobs, starts, err = s.ReadBatches(nil, 3, 5, 0) + require.NoError(t, err) + require.Len(t, blobs, 1) + require.Equal(t, []int64{3}, starts) +} + +func TestReclaimDropsOnlyBatchesFullyBelowTheFloor(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) + require.NoError(t, err) + _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) + require.NoError(t, err) + + // The floor lands mid-batch, so that batch stays: offsets above the floor + // are still readable and they live in it. + require.NoError(t, s.Truncate(nil, 1)) + require.Len(t, s.Batches, 2) + + // Now the whole first batch is below the floor and can go. + require.NoError(t, s.Truncate(nil, 3)) + require.Len(t, s.Batches, 1) + _, ok := s.Batches[3] + require.True(t, ok, "the batch holding readable offsets must survive") +} + +func TestTruncateStopsAtAnActiveConsumersReplayFloor(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + + require.NoError(t, s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false)) + s.AdvanceConsumer(nil, "wf-1", 2) + + // Reading to 2 is exactly what makes offsets 0 and 1 matter: they are in + // this consumer's History and a replay is asked to reproduce them. + err = s.Truncate(nil, 3) + require.ErrorContains(t, err, "still depends on offset 0") + require.Equal(t, int64(0), s.State.BaseOffset) + + // Whatever is above the floor is still spare capacity. + require.NoError(t, s.Truncate(nil, 0)) +} + +func TestTruncateBounds(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + require.NoError(t, err) + + err = s.Truncate(nil, 1) + require.NoError(t, err) + err = s.Truncate(nil, 0) + require.Error(t, err, "truncation must not go backwards") + err = s.Truncate(nil, 3) + require.Error(t, err, "truncation must not pass the head") +} + +func TestBucketArithmetic(t *testing.T) { + require.Equal(t, int64(0), BucketOf(0, 10)) + require.Equal(t, int64(0), BucketOf(9, 10)) + require.Equal(t, int64(1), BucketOf(10, 10)) + + // Node ids are bucket-relative and start at 1, because the store rejects 0. + require.Equal(t, int64(20), BucketStart(2, 10)) +} + +func TestReclaimableBuckets(t *testing.T) { + // Only buckets lying entirely below the floor are reclaimable, so nothing a + // reader can still ask for is ever dropped. + require.Empty(t, ReclaimableBuckets(0, 3, 4)) + require.Equal(t, []int64{0}, ReclaimableBuckets(0, 4, 4)) + require.Equal(t, []int64{0, 1}, ReclaimableBuckets(0, 8, 4)) + require.Equal(t, []int64{1}, ReclaimableBuckets(4, 8, 4)) + require.Empty(t, ReclaimableBuckets(4, 5, 4)) +} + +func TestCapTruncatesInline(t *testing.T) { + s := newTestStream(t, 4) + s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 4} + + for range 4 { + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + require.NoError(t, err) + } + + // Eight appended, four retained, so the floor sits at 4 and bucket 0 is + // entirely below it. + require.Equal(t, int64(8), s.State.HeadOffset) + require.Equal(t, int64(4), s.State.BaseOffset) +} + +func TestCapRefusesAnAppendItCouldOnlyAbsorbByDroppingReadRecords(t *testing.T) { + s := newTestStream(t, 100) + s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + require.NoError(t, s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false)) + + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.ErrorContains(t, err, "still depends on offset 0") + require.Equal(t, int64(0), s.State.HeadOffset, "a refused append writes nothing") + + // Nothing is stuck. The consumer going away is what makes room, and it is + // something someone does rather than something that happens quietly. + s.DeregisterConsumer(nil, "wf-1") + _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + require.Equal(t, int64(2), s.State.BaseOffset, "the cap applies once nobody needs the bytes") +} + +func TestCloseSchedulesRetentionOnlyWhenConfigured(t *testing.T) { + now := time.Now() + + plain := newTestStream(t, 100) + require.True(t, plain.Close(now, nil).IsZero(), "no retention configured, nothing to schedule") + + withRetention := newTestStream(t, 100) + withRetention.State.Lifecycle = &streampb.StreamLifecycle{ + Retention: durationpb.New(time.Hour), + } + at := withRetention.Close(now, nil) + require.Equal(t, now.Add(time.Hour), at) + require.NotNil(t, withRetention.State.CloseTime) + + // Closing twice must not re-arm deletion. + require.True(t, withRetention.Close(now, nil).IsZero()) +} + +func TestRegisterConsumerPinsFromWhereItSubscribed(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + + // Subscribing at 2 says nothing about offsets 0 and 1, so those stay + // droppable and everything from 2 up does not. + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 2, false)) + + require.NoError(t, s.Truncate(nil, 2)) + require.Equal(t, int64(2), s.State.BaseOffset) + require.ErrorContains(t, s.Truncate(nil, 3), "still depends on offset 2") +} + +func TestAdvanceConsumerTracksWhereAConsumerHasReached(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + + // The cursor is what decides whether this consumer is worth waking, and + // nothing else now depends on it. + s.AdvanceConsumer(nil, "workflow:output", 3) + require.Equal(t, int64(3), s.State.Consumers["workflow:output"].Offset) +} + +// Lowering the pin would hand back a guarantee already written to History: a +// recorded range has to stay re-readable. +func TestAdvanceConsumerNeverRewinds(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + + s.AdvanceConsumer(nil, "workflow:output", 3) + s.AdvanceConsumer(nil, "workflow:output", 1) + + pin, ok := s.consumerPin() + require.True(t, ok) + require.Equal(t, int64(3), pin) +} + +func TestRegisterConsumerRejectsAnOffsetBelowTheFloor(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + err = s.Truncate(nil, 2) + require.NoError(t, err) + + err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false) + require.ErrorContains(t, err, "below the stream's floor") +} + +// Resubscribing reactivates the existing pin rather than resetting it, so a +// consumer cannot rewind its own floor by subscribing again. +func TestRegisterConsumerTwiceKeepsThePin(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + s.AdvanceConsumer(nil, "workflow:output", 3) + + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + + pin, ok := s.consumerPin() + require.True(t, ok) + require.Equal(t, int64(3), pin) +} + +func TestDeregisterConsumerReleasesThePin(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false)) + + s.DeregisterConsumer(nil, "workflow:output") + + err = s.Truncate(nil, 4) + require.NoError(t, err) +} + +func TestMessageCapStillAppliesWithNoConsumerToProtect(t *testing.T) { + s := newTestStream(t, 100) + s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + + for range 3 { + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + require.NoError(t, err) + } + + // The refusal is about a consumer's recovery, so a stream with none behaves + // exactly as a capped log should. + require.Equal(t, int64(6), s.State.HeadOffset) + require.Equal(t, int64(4), s.State.BaseOffset) +} + +// A consumer that arrives after the messages were written cannot make the cap +// retroactively wrong, so the clamp keeps its bytes and the stream sits over +// its cap until it goes away. +func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { + s := newTestStream(t, 100) + s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + require.NoError(t, err) + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 1} + s.applyCap() + + require.Equal(t, int64(0), s.State.BaseOffset, "the clamp keeps what the consumer needs") +} + +// The reason the pin was taken out in the first place. It must not come back: +// a consumer that finished has to stop holding storage. +func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { + s := newTestStream(t, 100) + s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + s.DeregisterConsumer(nil, "workflow:output") + + for range 3 { + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + require.NoError(t, err) + } + require.Equal(t, int64(4), s.State.BaseOffset) +} + +// Coming back to a stream that moved past what its History refers to is the +// case the old code discovered during a replay instead. +func TestReregisteringBelowTheFloorIsRefused(t *testing.T) { + s := newTestStream(t, 100) + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + require.NoError(t, err) + require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + s.DeregisterConsumer(nil, "workflow:output") + require.NoError(t, s.Truncate(nil, 2)) + + // Resubscribing further along does not repair the gap. What this consumer + // already recorded starts at 0, and offsets 0 and 1 are gone. + err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 3, false) + require.ErrorContains(t, err, "the stream now starts at 2") +} + +// A caller sending a fresh producer id per request would otherwise grow the +// component state until no append fits, which leaves the stream unwritable for +// good rather than failing the call that caused it. +func TestStreamProducerTableIsBounded(t *testing.T) { + s := newTestStream(t, 100000) + + for i := range MaxProducersPerStream { + _, err := s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("m"), + ProducerID: fmt.Sprintf("p%d", i), + Sequence: 1, + }) + require.NoError(t, err) + } + + _, err := s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("one too many"), + ProducerID: "p-over", + Sequence: 1, + }) + var invalid *serviceerror.InvalidArgument + require.ErrorAs(t, err, &invalid) + + // A producer already tracked keeps working, so the cap cannot wedge the + // producers that filled it. + _, err = s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("still fine"), + ProducerID: "p0", + Sequence: 2, + }) + require.NoError(t, err) + + // An anonymous append is never blocked by the table. + _, err = s.AddMessages(nil, AddMessagesRequest{ + Messages: msgs("anon"), + }) + require.NoError(t, err) +} + +// Each consumer holds a truncation floor, so an unbounded table pins storage as +// well as growing state. +func TestStreamConsumerTableIsBounded(t *testing.T) { + s := newTestStream(t, 100000) + + for i := range MaxConsumersPerStream { + require.NoError(t, s.RegisterConsumer( + nil, fmt.Sprintf("c%d", i), "wf", "run", 0, true)) + } + + err := s.RegisterConsumer(nil, "c-over", "wf", "run", 0, true) + var invalid *serviceerror.InvalidArgument + require.ErrorAs(t, err, &invalid) + + // Re-registering an existing consumer is an update, not a new entry. + require.NoError(t, s.RegisterConsumer(nil, "c0", "wf", "run", 0, true)) +} From cd3307eff97dbcd94378b9d74eae4b7b95e06995 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 14 Sep 2026 15:59:55 -0700 Subject: [PATCH 04/47] Added the stream service RPCs and the frontend wiring. --- service/frontend/fx.go | 4 ++++ service/frontend/service.go | 6 ++++++ service/frontend/workflow_handler.go | 1 + 3 files changed, 11 insertions(+) diff --git a/service/frontend/fx.go b/service/frontend/fx.go index 651551de210..1546d14ad8d 100644 --- a/service/frontend/fx.go +++ b/service/frontend/fx.go @@ -14,6 +14,7 @@ import ( nexusoperationpb "go.temporal.io/server/chasm/lib/nexusoperation/gen/nexusoperationpb/v1" chasmscheduler "go.temporal.io/server/chasm/lib/scheduler" "go.temporal.io/server/chasm/lib/scheduler/gen/schedulerpb/v1" + chasmstream "go.temporal.io/server/chasm/lib/stream/service" chasmtests "go.temporal.io/server/chasm/lib/tests" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/client" @@ -146,6 +147,7 @@ var Module = fx.Options( chasmworkflow.Module, chasmcallback.Module, activity.FrontendModule, + chasmstream.FrontendModule, fx.Provide(visibility.ChasmVisibilityManagerProvider), fx.Provide(chasm.ChasmVisibilityInterceptorProvider), ) @@ -158,6 +160,7 @@ func NewServiceProvider( handler Handler, adminHandler *AdminHandler, operatorHandler *OperatorHandlerImpl, + streamHandler *chasmstream.FrontendHandler, versionChecker *VersionChecker, visibilityMgr manager.VisibilityManager, logger log.SnTaggedLogger, @@ -173,6 +176,7 @@ func NewServiceProvider( handler, adminHandler, operatorHandler, + streamHandler, versionChecker, visibilityMgr, logger, diff --git a/service/frontend/service.go b/service/frontend/service.go index f48d86c33bc..62da8849873 100644 --- a/service/frontend/service.go +++ b/service/frontend/service.go @@ -13,6 +13,8 @@ import ( "go.temporal.io/server/chasm/lib/activity" chasmcallback "go.temporal.io/server/chasm/lib/callback" chasmnexus "go.temporal.io/server/chasm/lib/nexusoperation" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmstream "go.temporal.io/server/chasm/lib/stream/service" "go.temporal.io/server/common/callbacks" "go.temporal.io/server/common/dynamicconfig" "go.temporal.io/server/common/log" @@ -458,6 +460,7 @@ type Service struct { handler Handler adminHandler *AdminHandler operatorHandler *OperatorHandlerImpl + streamHandler *chasmstream.FrontendHandler versionChecker *VersionChecker visibilityManager manager.VisibilityManager server *grpc.Server @@ -477,6 +480,7 @@ func NewService( handler Handler, adminHandler *AdminHandler, operatorHandler *OperatorHandlerImpl, + streamHandler *chasmstream.FrontendHandler, versionChecker *VersionChecker, visibilityMgr manager.VisibilityManager, logger log.Logger, @@ -492,6 +496,7 @@ func NewService( handler: handler, adminHandler: adminHandler, operatorHandler: operatorHandler, + streamHandler: streamHandler, versionChecker: versionChecker, visibilityManager: visibilityMgr, logger: logger, @@ -509,6 +514,7 @@ func (s *Service) Start() { workflowservice.RegisterWorkflowServiceServer(s.server, s.handler) adminservice.RegisterAdminServiceServer(s.server, s.adminHandler) operatorservice.RegisterOperatorServiceServer(s.server, s.operatorHandler) + streampb.RegisterStreamServiceServer(s.server, s.streamHandler) reflection.Register(s.server) diff --git a/service/frontend/workflow_handler.go b/service/frontend/workflow_handler.go index 2a1230a3c04..16c4c7d081a 100644 --- a/service/frontend/workflow_handler.go +++ b/service/frontend/workflow_handler.go @@ -1197,6 +1197,7 @@ func (wh *WorkflowHandler) PollWorkflowTaskQueue(ctx context.Context, request *w StartedTime: matchingResp.StartedTime, Queries: matchingResp.Queries, Messages: matchingResp.Messages, + StreamSlices: matchingResp.StreamSlices, PollerScalingDecision: matchingResp.PollerScalingDecision, }, nil } From d014cf33b56824a24733cf3b8e64390a0ce5c609 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 14 Sep 2026 15:59:55 -0700 Subject: [PATCH 05/47] Let a workflow publish and subscribe to its stream with commands. --- chasm/lib/workflow/fx.go | 3 + chasm/lib/workflow/stream_commands.go | 304 +++++++++++++++ chasm/lib/workflow/stream_cursor_test.go | 239 ++++++++++++ chasm/lib/workflow/workflow.go | 366 ++++++++++++++++++ service/history/api/command_attr_validator.go | 8 +- .../api/respondworkflowtaskcompleted/api.go | 31 ++ .../stream_appends.go | 117 ++++++ .../workflow_task_completed_handler.go | 9 + .../workflow_task_completed_handler_test.go | 3 + .../history/historybuilder/event_factory.go | 5 + service/history/historybuilder/event_store.go | 4 +- .../history/historybuilder/history_builder.go | 3 + .../history_builder_categorization_test.go | 1 + .../historybuilder/history_builder_test.go | 6 +- service/history/interfaces/mutable_state.go | 9 + .../history/interfaces/mutable_state_mock.go | 28 ++ .../history/workflow/mutable_state_impl.go | 116 ++++++ .../workflow/workflow_task_state_machine.go | 8 +- 18 files changed, 1256 insertions(+), 4 deletions(-) create mode 100644 chasm/lib/workflow/stream_commands.go create mode 100644 chasm/lib/workflow/stream_cursor_test.go create mode 100644 service/history/api/respondworkflowtaskcompleted/stream_appends.go diff --git a/chasm/lib/workflow/fx.go b/chasm/lib/workflow/fx.go index 52730794faa..dbcdedeceb1 100644 --- a/chasm/lib/workflow/fx.go +++ b/chasm/lib/workflow/fx.go @@ -22,6 +22,9 @@ var Module = fx.Module( ); err != nil { return err } + if err := library.registry.Register(&streamLibrary{}); err != nil { + return err + } return chasmRegistry.Register(library) }), ) diff --git a/chasm/lib/workflow/stream_commands.go b/chasm/lib/workflow/stream_commands.go new file mode 100644 index 00000000000..598d46b91e9 --- /dev/null +++ b/chasm/lib/workflow/stream_commands.go @@ -0,0 +1,304 @@ +package workflow + +import ( + commandpb "go.temporal.io/api/command/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" +) + +// DefaultStreamName is the stream a command addresses when it names none. +const DefaultStreamName = "output" + +// handleAddStreamMessagesCommand appends to a stream the workflow owns. +// +// The stream is a co-located subcomponent, so its frontier advances as part of +// the workflow task's own commit: no extra transition and no cross-execution +// write. The log bytes cannot be written here, because a command handler runs +// under the state lock with no context to do I/O from, so they are staged and +// flushed before the commit that makes them visible. +// +// The offsets are known here, unlike a subscription's, so the event is written +// here too rather than being staged for the flush. +func handleAddStreamMessagesCommand( + chasmCtx chasm.MutableContext, + wf *Workflow, + validator Validator, + command *commandpb.Command, + opts CommandHandlerOptions, +) error { + attrs := command.GetAddStreamMessagesCommandAttributes() + if attrs == nil { + return serviceerror.NewInvalidArgument("AddStreamMessagesCommandAttributes is not set") + } + if len(attrs.GetMessages()) == 0 { + return serviceerror.NewInvalidArgument("AddStreamMessages command carries no messages") + } + + // The batch becomes one log node, so the whole batch is what has to fit. + // Left unchecked it fails later in the flush, which surfaces as a + // persistence error out of a task the worker will replay and re-issue + // forever, with nothing naming the batch as the cause. + size := 0 + for _, m := range attrs.GetMessages() { + size += m.Size() + } + if !validator.IsValidPayloadSize(size) { + return FailWorkflowTaskError{ + Cause: enumspb.WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE, + Message: "AddStreamMessagesCommandAttributes.Messages exceeds size limit", + TerminateWorkflow: true, + } + } + + name := attrs.GetStreamId() + if name == "" { + name = DefaultStreamName + } + + s, err := wf.streamNamed(chasmCtx, name) + if err != nil { + return err + } + + result, err := s.AddMessages(chasmCtx, stream.AddMessagesRequest{ + Messages: toLibraryMessages(attrs.GetMessages()), + }) + if err != nil { + return err + } + // Written even when a producer sequence deduplicated the append, because + // the command was still issued and the event is what the replaying worker + // matches it against. It names the original offsets, which is what a + // deduplicated append resolves to. + wf.RecordStreamMessagesAdded( + name, result.FirstOffset, result.Count, opts.WorkflowTaskCompletedEventID) + return nil +} + +// handleSubscribeStreamCommand registers this workflow as a consumer. +// +// A stream the workflow owns is subscribed here and now, because everything the +// cursor needs is already in this execution. One in another execution cannot +// be: its collection id is the stream's run id and its bucket size is its own, +// and finding either means a lookup a command handler cannot do. Those are +// staged and resolved in the flush before commit, the same way log writes are. +func handleSubscribeStreamCommand( + chasmCtx chasm.MutableContext, + wf *Workflow, + _ Validator, + command *commandpb.Command, + opts CommandHandlerOptions, +) error { + attrs := command.GetSubscribeStreamCommandAttributes() + if attrs == nil { + return serviceerror.NewInvalidArgument("SubscribeStreamCommandAttributes is not set") + } + streamID := attrs.GetStreamId() + if streamID == "" { + return serviceerror.NewInvalidArgument("SubscribeStream command names no stream") + } + + // A second subscribe to the same stream registers nothing, but it still + // gets an event. Every SDK matches issued commands against + // command-generated events in order, so a command that produces none puts + // that matching out of step, which is the whole reason this event exists. + _, already := wf.StreamCursors[streamID] + + // Everything is staged, including a stream this workflow owns, so that the + // resolved start offset and the event recording it are produced in one + // place rather than two. + wf.StagePendingSubscription(PendingStreamSubscription{ + StreamID: streamID, + StartOffset: attrs.GetStartOffset(), + AlreadySubscribed: already, + Event: wf.ReserveStreamSubscribedEvent( + streamID, opts.WorkflowTaskCompletedEventID), + }) + return nil +} + +// streamSubscribedEvent is the event a subscription writes. +// +// It is recorded once per subscription, not per message: the offsets a task +// consumed ride WorkflowTaskCompleted, and payloads never enter History. The +// event exists because a command that produces none desynchronises the +// command-to-event matching every SDK's replay depends on, and because without +// it nothing in History explains why a workflow started receiving stream data. +type streamSubscribedEvent struct{} + +func (streamSubscribedEvent) Type() enumspb.EventType { + return enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED +} + +func (streamSubscribedEvent) IsWorkflowTaskTrigger() bool { return false } + +// The cursor lives in CHASM state, which is persisted and rebuilt with the +// execution, so there is nothing for replication or reset to reconstruct here. +func (streamSubscribedEvent) Apply(chasm.MutableContext, *Workflow, *historypb.HistoryEvent) error { + return nil +} + +// A command event, so it is never cherry-picked: the workflow reissues the +// subscribe command on the new branch if it still wants one. +func (streamSubscribedEvent) CherryPick( + chasm.MutableContext, + *Workflow, + *historypb.HistoryEvent, + map[enumspb.ResetReapplyExcludeType]struct{}, +) error { + return ErrEventNotCherryPickable +} + +// ReserveStreamSubscribedEvent writes the event a subscribe command owes, +// leaving the start offset for the flush to fill in. +// +// Written here rather than where the offset becomes known, because an SDK +// matches the commands a workflow issued against the events they produced by +// position. The flush runs after every command, so an event written there +// would sit behind the events of commands that were issued later, and the +// first replay of a workflow that subscribed before doing anything else would +// fail on the mismatch. +func (w *Workflow) ReserveStreamSubscribedEvent( + streamID string, + workflowTaskCompletedEventID int64, +) *historypb.HistoryEvent { + return w.AddHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, func(e *historypb.HistoryEvent) { + e.Attributes = &historypb.HistoryEvent_WorkflowStreamSubscribedEventAttributes{ + WorkflowStreamSubscribedEventAttributes: &historypb.WorkflowStreamSubscribedEventAttributes{ + WorkflowTaskCompletedEventId: workflowTaskCompletedEventID, + StreamId: streamID, + }, + } + }) +} + +// RecordStreamSubscribedOffset completes a reserved event. Safe to do after the +// fact because the builder serializes the batch at commit, which is after the +// flush that resolves the offset. +func RecordStreamSubscribedOffset(event *historypb.HistoryEvent, startOffset int64) { + event.GetWorkflowStreamSubscribedEventAttributes().StartOffset = startOffset +} + +// streamMessagesAddedEvent is the event a publish writes. +// +// One per batch, holding the offset range and nothing else. That is what makes +// it a fixed cost: a batch of one 20-byte message and a batch of a thousand +// 2KB messages write the same event, because the bodies went to the stream's +// log. It exists for the same reason the subscription event does, that a +// command producing no event desynchronises the command-to-event matching +// every SDK's replay depends on, and it doubles as the only record in History +// that the workflow published at all. +type streamMessagesAddedEvent struct{} + +func (streamMessagesAddedEvent) Type() enumspb.EventType { + return enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED +} + +func (streamMessagesAddedEvent) IsWorkflowTaskTrigger() bool { return false } + +// The frontier it describes is CHASM state, persisted and rebuilt with the +// execution, so there is nothing here to reconstruct. +func (streamMessagesAddedEvent) Apply(chasm.MutableContext, *Workflow, *historypb.HistoryEvent) error { + return nil +} + +// A command event, so it is never cherry-picked: the offsets belong to a log +// the new branch did not write. +func (streamMessagesAddedEvent) CherryPick( + chasm.MutableContext, + *Workflow, + *historypb.HistoryEvent, + map[enumspb.ResetReapplyExcludeType]struct{}, +) error { + return ErrEventNotCherryPickable +} + +// RecordStreamMessagesAdded writes the event for one published batch. +func (w *Workflow) RecordStreamMessagesAdded( + streamID string, + firstOffset int64, + count int64, + workflowTaskCompletedEventID int64, +) { + w.AddHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED, func(e *historypb.HistoryEvent) { + e.Attributes = &historypb.HistoryEvent_WorkflowStreamMessagesAddedEventAttributes{ + WorkflowStreamMessagesAddedEventAttributes: &historypb.WorkflowStreamMessagesAddedEventAttributes{ + WorkflowTaskCompletedEventId: workflowTaskCompletedEventID, + StreamId: streamID, + FirstOffset: firstOffset, + MessageCount: count, + }, + } + }) +} + +// streamNamed returns the workflow's stream of that name, creating it on first +// use. Implicit creation is deliberate: a workflow publishing to its own output +// should not have to coordinate with anyone about who creates it. +func (w *Workflow) streamNamed(ctx chasm.MutableContext, name string) (*stream.Stream, error) { + if w.Streams == nil { + w.Streams = make(chasm.Map[string, *stream.Stream]) + } + if field, ok := w.Streams[name]; ok { + return field.Get(ctx), nil + } + + // Checked only on the create path, so an existing stream is never refused + // for room. The name arrives from the caller and every distinct one adds a + // component to this execution's mutable state, so without a bound an + // outside writer can grow that state until the size limit terminates the + // workflow. + if len(name) > stream.MaxStreamNameLength { + return nil, serviceerror.NewInvalidArgumentf( + "stream name is %d characters, over the %d limit", len(name), stream.MaxStreamNameLength) + } + if len(w.Streams) >= stream.MaxOwnedStreamsPerWorkflow { + return nil, serviceerror.NewFailedPreconditionf( + "workflow already owns %d streams, the limit", stream.MaxOwnedStreamsPerWorkflow) + } + + // Keyed on the execution so the identity is stable for the workflow, and + // distinct from any other workflow reusing the same name. + created, err := stream.NewStream(ctx, stream.NewStreamRequest{ + CollectionID: ctx.ExecutionKey().RunID + "/" + name, + Attached: true, + }) + if err != nil { + return nil, err + } + w.Streams[name] = chasm.NewComponentField(ctx, created) + return created, nil +} + +func toLibraryMessages(in []*streampb.StreamMessage) []*streamlib.StreamMessage { + out := make([]*streamlib.StreamMessage, len(in)) + for i, m := range in { + out[i] = &streamlib.StreamMessage{ + Body: m.GetBody(), + Metadata: m.GetMetadata(), + Topic: m.GetTopic(), + TopicSequence: m.GetTopicSequence(), + Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + } + } + return out +} + +// streamLibrary registers the stream command with the workflow registry. +type streamLibrary struct{} + +func (l *streamLibrary) CommandHandlers() map[enumspb.CommandType]CommandHandler { + return map[enumspb.CommandType]CommandHandler{ + enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES: handleAddStreamMessagesCommand, + enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM: handleSubscribeStreamCommand, + } +} + +func (l *streamLibrary) EventDefinitions() []EventDefinition { + return []EventDefinition{streamSubscribedEvent{}, streamMessagesAddedEvent{}} +} diff --git a/chasm/lib/workflow/stream_cursor_test.go b/chasm/lib/workflow/stream_cursor_test.go new file mode 100644 index 00000000000..294b2c35db2 --- /dev/null +++ b/chasm/lib/workflow/stream_cursor_test.go @@ -0,0 +1,239 @@ +package workflow + +import ( + "testing" + + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" +) + +func newStreamCursorTestContext() chasm.MutableContext { + return &chasm.MockMutableContext{ + MockContext: chasm.MockContext{ + HandleExecutionKey: func() chasm.ExecutionKey { + return chasm.ExecutionKey{ + NamespaceID: "ns-1", + BusinessID: "wf-1", + RunID: "run-1", + } + }, + }, + } +} + +// Built directly rather than through NewStream, which wires a visibility field +// needing a live context. +func newAttachedStream(t *testing.T, ctx chasm.MutableContext, count int) *stream.Stream { + t.Helper() + + s := &stream.Stream{ + State: &streamlib.StreamState{ + CollectionId: "col-1", + BucketSize: stream.DefaultBucketSize, + Producers: make(map[string]*streamlib.ProducerCursor), + Consumers: make(map[string]*streamlib.ConsumerCursor), + }, + } + + messages := make([]*streamlib.StreamMessage, count) + for i := range messages { + messages[i] = &streamlib.StreamMessage{Kind: streamlib.STREAM_MESSAGE_KIND_DATA} + } + _, err := s.AddMessages(ctx, stream.AddMessagesRequest{Messages: messages}) + require.NoError(t, err) + + return s +} + +// Subscribing registers the consumer on the stream, which decides both whether +// an append is worth waking it for and what retention may not take. A range +// this workflow consumes goes into its History, and a replay is asked to +// reproduce it, so the stream holds those messages while the subscription is +// active and releases them when it is not. +func TestSubscribeRegistersTheConsumer(t *testing.T) { + ctx := newStreamCursorTestContext() + w := &Workflow{} + owned := newAttachedStream(t, ctx, 4) + w.Streams = chasm.Map[string, *stream.Stream]{ + DefaultStreamName: chasm.NewComponentField(ctx, owned), + } + + start, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + require.NoError(t, err) + require.Equal(t, int64(0), start) + + consumer := owned.State.GetConsumers()[streamConsumerID(DefaultStreamName)] + require.NotNil(t, consumer, "the stream has to know who is reading it") + require.Equal(t, int64(0), consumer.GetOffset()) + require.True(t, consumer.GetActive()) + + require.Equal(t, int64(0), consumer.GetReplayFloor()) + + // And it holds the floor for as long as it is subscribed. + require.ErrorContains(t, owned.Truncate(ctx, 1), "still depends on offset 0") +} + +func TestSubscribeFromTheTailResolvesToHead(t *testing.T) { + ctx := newStreamCursorTestContext() + w := &Workflow{} + owned := newAttachedStream(t, ctx, 4) + w.Streams = chasm.Map[string, *stream.Stream]{ + DefaultStreamName: chasm.NewComponentField(ctx, owned), + } + + start, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, -1) + require.NoError(t, err) + require.Equal(t, int64(4), start, "a negative offset means from wherever the stream is now") +} + +func TestSubscribeRejectsAStreamTheWorkflowDoesNotOwn(t *testing.T) { + ctx := newStreamCursorTestContext() + w := &Workflow{} + + _, err := w.SubscribeToOwnedStream(ctx, "absent", 0) + require.ErrorContains(t, err, "does not own a stream") +} + +// Committing a delivered range moves the consumer's cursor on the stream, so +// the stream knows how far this reader has got. +func TestCommitStreamCursorsAdvancesTheConsumer(t *testing.T) { + ctx := newStreamCursorTestContext() + w := &Workflow{} + owned := newAttachedStream(t, ctx, 4) + w.Streams = chasm.Map[string, *stream.Stream]{ + DefaultStreamName: chasm.NewComponentField(ctx, owned), + } + + _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + require.NoError(t, err) + + cursor := w.StreamCursors[DefaultStreamName].Get(ctx) + require.NoError(t, cursor.StagePending(ctx, 0, 3)) + + recorded := w.CommitStreamCursors(ctx) + require.Len(t, recorded, 1) + require.Equal(t, int64(0), recorded[0].GetFromOffset()) + require.Equal(t, int64(3), recorded[0].GetToOffset()) + + require.Equal(t, int64(3), + owned.State.GetConsumers()[streamConsumerID(DefaultStreamName)].GetOffset(), + "the stream must see how far the consumer has read") +} + +// An idle task records an empty range, which must leave the consumer alone. +func TestCommitStreamCursorsWithAnEmptyRangeHoldsTheConsumer(t *testing.T) { + ctx := newStreamCursorTestContext() + w := &Workflow{} + owned := newAttachedStream(t, ctx, 4) + w.Streams = chasm.Map[string, *stream.Stream]{ + DefaultStreamName: chasm.NewComponentField(ctx, owned), + } + + _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + require.NoError(t, err) + + cursor := w.StreamCursors[DefaultStreamName].Get(ctx) + require.NoError(t, cursor.StagePending(ctx, 0, 0)) + + recorded := w.CommitStreamCursors(ctx) + require.Len(t, recorded, 1, "an empty range is still recorded") + require.Equal(t, recorded[0].GetFromOffset(), recorded[0].GetToOffset()) + + require.Equal(t, int64(0), + owned.State.GetConsumers()[streamConsumerID(DefaultStreamName)].GetOffset(), + "consuming nothing must not move the consumer") +} + +// Two publishes in one workflow task each stage their own batch, at the offsets +// they landed at. +// +// The offset a batch starts at is the key its row is written under, so two +// publishes must not collide and a retry of either must address the same row +// it wrote before. There is no transaction id involved any more: the store +// resolves a rewrite by replacing, and the frontier decides what a reader sees. +func TestPublishStagesEachBatchAtItsOwnOffset(t *testing.T) { + ctx := newStreamCursorTestContext() + + // A backend, because the handler writes the publish event and that event is + // part of what the command owes. + backend := &chasm.MockNodeBackend{ + HandleAddHistoryEvent: func( + t enumspb.EventType, set func(*historypb.HistoryEvent), + ) *historypb.HistoryEvent { + e := &historypb.HistoryEvent{EventType: t} + set(e) + return e + }, + } + w := &Workflow{MSPointer: chasm.NewMSPointer(backend)} + opts := CommandHandlerOptions{WorkflowTaskCompletedEventID: 10} + + publish := &commandpb.Command{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("x")}}, + {Body: &commonpb.Payload{Data: []byte("y")}}, + }, + }, + }, + } + + require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts)) + require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts)) + + // Both publishes committed with the workflow task, so the batches are on + // the component keyed by the offsets they start at. + owned := w.Streams[DefaultStreamName].Get(ctx) + require.Len(t, owned.Batches, 2) + _, ok := owned.Batches[0] + require.True(t, ok) + _, ok = owned.Batches[2] + require.True(t, ok) + require.Equal(t, int64(4), owned.State.GetHeadOffset()) +} + +type allowAnySize struct{} + +func (allowAnySize) IsValidPayloadSize(int) bool { return true } + +// A consumer that falls behind a truncating stream must be told, not handed +// what is left with a hole in it. +// +// Being outrun is possible again once the subscription is released, which is +// deliberate: a floor that nothing ever gave up would keep every message for +// as long as a consumer had ever existed. The trade is that being outrun has +// to be loud. +func TestConsumerOutrunByTruncationIsToldSo(t *testing.T) { + ctx := newStreamCursorTestContext() + w := &Workflow{} + owned := newAttachedStream(t, ctx, 4) + w.Streams = chasm.Map[string, *stream.Stream]{ + DefaultStreamName: chasm.NewComponentField(ctx, owned), + } + + _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + require.NoError(t, err) + + // Someone decides these messages are no longer needed, and only then can + // the stream move past where this consumer is sitting. + owned.DeregisterConsumer(ctx, streamConsumerID(DefaultStreamName)) + err = owned.Truncate(ctx, 3) + require.NoError(t, err, "a released floor must not keep holding") + + cursor := w.StreamCursors[DefaultStreamName].Get(ctx) + require.Less(t, cursor.Offset(), owned.State.GetBaseOffset()) + + state, err := owned.Snapshot(ctx, struct{}{}) + require.NoError(t, err) + require.Equal(t, int64(3), state.GetBaseOffset(), + "the floor moved, which is what the consumer has to find out about") +} diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index 2b5409a4147..053bc452086 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -2,15 +2,20 @@ package workflow import ( "fmt" + "slices" + "strings" commonpb "go.temporal.io/api/common/v1" failurepb "go.temporal.io/api/failure/v1" historypb "go.temporal.io/api/history/v1" "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm" "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" + "go.temporal.io/server/chasm/lib/stream" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" 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" @@ -39,6 +44,294 @@ type Workflow struct { // Updates indexed by update ID, used to store the update components. Updates chasm.Map[string, *WorkflowUpdate] + + // Streams the workflow owns, keyed by stream name. Co-located with the + // workflow so publishing rides its commit rather than crossing executions. + Streams chasm.Map[string, *stream.Stream] + + // Positions in streams the workflow consumes, keyed by stream name. Held + // here rather than on the stream so that folding in a delivered range + // commits with the event that records it. + StreamCursors chasm.Map[string, *stream.Cursor] + + // Subscribe commands whose stream is in another execution, so the addressing + // has to be looked up before a cursor can be made. In memory only, drained + // by the flush before commit. + pendingStreamSubscriptions []PendingStreamSubscription +} + +// PendingStreamSubscription is a subscribe command whose stream lives in +// another execution, waiting for the flush to look up its addressing. +type PendingStreamSubscription struct { + StreamID string + StartOffset int64 + // The workflow already holds a cursor for this stream. The subscription + // itself is done, but the command still needs its event, because that is + // what a replaying worker matches the re-issued command against. + AlreadySubscribed bool + + // The event this command already wrote, waiting on its start offset. In + // memory only, like the rest of this struct. + Event *historypb.HistoryEvent +} + +// StagePendingSubscription records a subscription for the flush to resolve. +func (w *Workflow) StagePendingSubscription(sub PendingStreamSubscription) { + w.pendingStreamSubscriptions = append(w.pendingStreamSubscriptions, sub) +} + +// DrainStreamSubscriptions returns and clears the staged subscriptions. +func (w *Workflow) DrainStreamSubscriptions() []PendingStreamSubscription { + out := w.pendingStreamSubscriptions + w.pendingStreamSubscriptions = nil + return out +} + +// streamConsumerID names this workflow's pin on a stream it owns. An attached +// stream has exactly one consumer, but the stream's map is keyed by consumer, +// so the entry still needs a stable name. +func streamConsumerID(streamName string) string { + return "workflow:" + streamName +} + +// SubscribeToOwnedStream registers this workflow as a consumer of a stream it +// owns, returning the offset the subscription actually starts from. +// +// A negative start offset means "from wherever the stream is now". That is +// resolved here and stored, so the first recorded range begins at a fact rather +// than at a reading that would land somewhere else on replay. +func (w *Workflow) SubscribeToOwnedStream( + mctx chasm.MutableContext, + name string, + startOffset int64, +) (int64, error) { + field, ok := w.Streams[name] + if !ok { + return 0, serviceerror.NewNotFoundf("workflow does not own a stream named %q", name) + } + owned := field.Get(mctx) + state, err := owned.Snapshot(mctx, struct{}{}) + if err != nil { + return 0, err + } + + if startOffset < 0 { + startOffset = state.GetHeadOffset() + } + if startOffset < state.GetBaseOffset() { + return 0, serviceerror.NewFailedPreconditionf( + "offset %d is below the stream's floor of %d", startOffset, state.GetBaseOffset()) + } + + if w.StreamCursors == nil { + w.StreamCursors = make(chasm.Map[string, *stream.Cursor]) + } + if existing, ok := w.StreamCursors[name]; ok { + // Resubscribing must not rewind a cursor: ranges below it are already + // recorded in History, and moving back would replay them as new. + return existing.Get(mctx).Offset(), nil + } + + cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ + StreamID: name, + CollectionID: state.GetCollectionId(), + BucketSize: state.GetBucketSize(), + StartOffset: startOffset, + }) + if err != nil { + return 0, err + } + + // Pin the stream's floor in the same transaction. Registered separately it + // could be lost while the cursor survived, and truncation would then be + // free to take a range the cursor still points at. + key := mctx.ExecutionKey() + if err := owned.RegisterConsumer(mctx, streamConsumerID(name), key.BusinessID, key.RunID, startOffset, false); err != nil { + return 0, err + } + + w.StreamCursors[name] = chasm.NewComponentField(mctx, cursor) + return startOffset, nil +} + +// ExternalStreamSubscription describes a stream in another execution. The +// addressing is copied in at subscribe time so delivery never has to reach +// across executions to find the log. +type ExternalStreamSubscription struct { + StreamID string + CollectionID string + BucketSize int64 + StartOffset int64 + KnownHead int64 +} + +// SubscribeToExternalStream registers this workflow as a consumer of a stream +// it does not own, returning the offset the subscription starts from. +func (w *Workflow) SubscribeToExternalStream( + mctx chasm.MutableContext, + req ExternalStreamSubscription, +) (int64, error) { + if w.StreamCursors == nil { + w.StreamCursors = make(chasm.Map[string, *stream.Cursor]) + } + if existing, ok := w.StreamCursors[req.StreamID]; ok { + // Resubscribing must not rewind: ranges below the cursor are already + // recorded in History, and moving back would replay them as new. + return existing.Get(mctx).Offset(), nil + } + + cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ + StreamID: req.StreamID, + External: true, + CollectionID: req.CollectionID, + BucketSize: req.BucketSize, + StartOffset: req.StartOffset, + }) + if err != nil { + return 0, err + } + cursor.AdvanceKnownHead(mctx, req.KnownHead) + w.StreamCursors[req.StreamID] = chasm.NewComponentField(mctx, cursor) + return req.StartOffset, nil +} + +// ExportStreamSubscriptions returns the subscriptions a successor run has to +// inherit. +// +// Only subscriptions to streams in other executions are exported. A stream this +// workflow owns lives in this execution and does not itself survive the run +// transition yet (§8a), so carrying a cursor for one would leave the successor +// pointing at a stream it cannot reach. +func (w *Workflow) ExportStreamSubscriptions(ctx chasm.Context) []ExternalStreamSubscription { + var out []ExternalStreamSubscription + for _, field := range w.StreamCursors { + cursor := field.Get(ctx) + if !cursor.IsExternal() { + continue + } + out = append(out, ExternalStreamSubscription{ + StreamID: cursor.StreamID(), + CollectionID: cursor.CollectionID(), + BucketSize: cursor.BucketSize(), + StartOffset: cursor.Offset(), + KnownHead: cursor.KnownHead(), + }) + } + // Stable order, so a successor's state does not depend on map iteration. + slices.SortFunc(out, func(a, b ExternalStreamSubscription) int { + return strings.Compare(a.StreamID, b.StreamID) + }) + return out +} + +// ImportStreamSubscriptions installs subscriptions inherited from the run this +// one continues. The offset carries over unchanged: offsets are global to the +// stream, so the successor resumes exactly where its predecessor stopped and +// the stream itself is untouched. +func (w *Workflow) ImportStreamSubscriptions( + mctx chasm.MutableContext, + subscriptions []ExternalStreamSubscription, +) error { + for _, sub := range subscriptions { + if _, err := w.SubscribeToExternalStream(mctx, sub); err != nil { + return err + } + } + return nil +} + +// AdvanceKnownHead records how far a stream in another execution has moved. +// +// A workflow cannot read that frontier itself while closing its own +// transaction, so the stream pushes it here. Writing it dirties this execution, +// and the transaction close then sees the cursor is behind and schedules a +// workflow task, which is the same path an owned stream takes. +func (w *Workflow) AdvanceKnownHead(mctx chasm.MutableContext, streamID string, head int64) error { + field, ok := w.StreamCursors[streamID] + if !ok { + return serviceerror.NewNotFoundf("workflow does not consume stream %q", streamID) + } + field.Get(mctx).AdvanceKnownHead(mctx, head) + return nil +} + +// StreamCursorsBehind reports whether any subscription still has offsets it has +// not been given. +// +// This is the one place a stream wakes a workflow. Publishing deliberately +// never does, because a stream item is data produced by an execution rather +// than a decision input to it. An active subscription is different: the +// workflow asked to be told, so leaving it to wait for some unrelated task +// would make delivery depend on traffic that has nothing to do with the stream. +func (w *Workflow) StreamCursorsBehind(ctx chasm.Context) bool { + for name, field := range w.StreamCursors { + cursor := field.Get(ctx) + + // An external stream's frontier is whatever it last pushed here: this + // execution cannot read the real one without reaching into another. + if cursor.IsExternal() { + if cursor.Offset() < cursor.KnownHead() { + return true + } + continue + } + + owned, ok := w.Streams[name] + if !ok { + continue + } + state, err := owned.Get(ctx).Snapshot(ctx, struct{}{}) + if err != nil { + continue + } + if cursor.Offset() < state.GetHeadOffset() { + return true + } + } + return false +} + +// CommitStreamCursors folds every staged range into its cursor and returns the +// ranges to record. Called while the workflow task's transaction is open, so +// the advance and the event that carries the range land together. +// +// A cursor with nothing staged is skipped, but a cursor staged with an empty +// range is not: replay has to see that the subscription was live and observed +// nothing. +func (w *Workflow) CommitStreamCursors(mctx chasm.MutableContext) []*streampb.StreamCursor { + if w.StreamCursors == nil { + return nil + } + + names := make([]string, 0, len(w.StreamCursors)) + for name := range w.StreamCursors { + names = append(names, name) + } + // Recorded order has to be stable, or replay compares against a different + // event than the one the original execution wrote. + slices.Sort(names) + + var recorded []*streampb.StreamCursor + for _, name := range names { + cursor := w.StreamCursors[name].Get(mctx) + from, to, ok := cursor.Commit(mctx) + if !ok { + continue + } + + // Let the floor follow the cursor. Anything below it is recorded as + // consumed, so nothing needs to re-read it. + if field, ok := w.Streams[name]; ok { + field.Get(mctx).AdvanceConsumer(mctx, streamConsumerID(name), to) + } + + recorded = append(recorded, &streampb.StreamCursor{ + StreamId: cursor.StreamID(), + FromOffset: from, + ToOffset: to, + }) + } + return recorded } func NewWorkflow( @@ -317,3 +610,76 @@ func (w *Workflow) HasAnyBufferedEvent(filter historybuilder.BufferedEventFilter func (w *Workflow) WorkflowTypeName() string { return w.GetWorkflowTypeName() } + +// OwnedStreamState returns the state of a stream this workflow owns, or nil if +// it owns none by that name. +// +// An attached stream has no id of its own, so this is the only way to see its +// frontier from outside the execution. Reading it needs the owner's component, +// which is why it lives here rather than on the stream. +// +// Absent is not an error. An owned stream is created by the first publish to +// it, so a reader that arrives before the workflow has published anything is +// the ordinary case rather than a mistake, and from outside the execution +// "not created yet" and "never will be" are the same observation. +func (w *Workflow) OwnedStreamState( + ctx chasm.Context, + name string, +) (*streamlib.StreamState, error) { + field, ok := w.Streams[name] + if !ok { + return nil, nil + } + return field.Get(ctx).Snapshot(ctx, struct{}{}) +} + +// OwnedStream returns the attached stream itself, or nil when the workflow has +// not created it yet. Reads that need the payload and not just the frontier go +// through here, so both come from one view of the component. +func (w *Workflow) OwnedStream( + ctx chasm.Context, + name string, +) *stream.Stream { + field, ok := w.Streams[name] + if !ok { + return nil + } + return field.Get(ctx) +} + +// EnsureOwnedStream creates a stream this workflow owns if the first writer to +// it is not the workflow itself, and returns its state either way. +// +// An outside writer needs the stream's collection id and bucket size to write +// its log node, and both are decided when the stream is created. So the first +// append from outside costs one transition to create the stream and learn +// them, and none after that. +func (w *Workflow) EnsureOwnedStream( + mctx chasm.MutableContext, + name string, +) (*streamlib.StreamState, error) { + s, err := w.streamNamed(mctx, name) + if err != nil { + return nil, err + } + return s.Snapshot(mctx, struct{}{}) +} + +// AppendToOwnedStream appends to a stream this workflow owns on behalf of a +// writer outside the execution. +// +// The workflow's own publishes go through the command handler, which advances +// the frontier inside the Workflow Task's commit. This is the other producer: +// it advances the same frontier in a transition of its own, so the two are +// serialized by the execution rather than by anything the stream does. +func (w *Workflow) AppendToOwnedStream( + mctx chasm.MutableContext, + name string, + req stream.AddMessagesRequest, +) (stream.AddMessagesResult, error) { + s, err := w.streamNamed(mctx, name) + if err != nil { + return stream.AddMessagesResult{}, err + } + return s.AddMessages(mctx, req) +} diff --git a/service/history/api/command_attr_validator.go b/service/history/api/command_attr_validator.go index 8c48b0f99f1..5118a1d4ea8 100644 --- a/service/history/api/command_attr_validator.go +++ b/service/history/api/command_attr_validator.go @@ -661,7 +661,13 @@ func (v *CommandAttrValidator) ValidateCommandSequence( enumspb.COMMAND_TYPE_MODIFY_WORKFLOW_PROPERTIES, enumspb.COMMAND_TYPE_PROTOCOL_MESSAGE, enumspb.COMMAND_TYPE_SCHEDULE_NEXUS_OPERATION, - enumspb.COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION: + enumspb.COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION, + // Publishing to a stream the workflow owns. Not a close command: + // it appends and returns, scheduling nothing further. + enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + // Subscribing to a stream. Also not closing: it records a cursor + // and the workflow carries on. + enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM: // noop case enumspb.COMMAND_TYPE_CONTINUE_AS_NEW_WORKFLOW_EXECUTION, enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, diff --git a/service/history/api/respondworkflowtaskcompleted/api.go b/service/history/api/respondworkflowtaskcompleted/api.go index a569e0ecc26..61520b6330f 100644 --- a/service/history/api/respondworkflowtaskcompleted/api.go +++ b/service/history/api/respondworkflowtaskcompleted/api.go @@ -452,6 +452,28 @@ func (handler *WorkflowTaskCompletedHandler) Invoke( return nil, err } + // Subscriptions to streams in other executions, resolved here for the + // same reason: the command handler has nowhere to look the addressing + // up from, and by delivery time the cursor has to already exist. + // + // Skipped once the task has failed. Registering a consumer is a durable + // write on the stream's own execution, and this workflow's side of it + // is about to be rolled back, which would leave a pin on someone else's + // stream with no cursor behind it and nothing to release it. A failed + // command does not return an error, so this has to be checked here + // rather than inferred from err. + if workflowTaskHandler.workflowTaskFailedCause == nil && !workflowTaskHandler.stopProcessing { + if err = resolveStagedStreamSubscriptions( + ctx, + ms, + ms.GetWorkflowKey().NamespaceID, + completedEvent.GetEventId(), + workflowTaskHandler.stagedStreamSubscriptions, + ); err != nil { + return nil, err + } + } + // Worker must respond with Update Accepted or Update Rejected message on every Update Requested // message that were delivered on specific WT, when completing this WT. // If worker ignored the update request (old SDK or SDK bug), then server rejects this update. @@ -805,6 +827,14 @@ func (handler *WorkflowTaskCompletedHandler) Invoke( // sticky is always enabled when worker request for new workflow task from RespondWorkflowTaskCompleted resp.StartedResponse.StickyExecutionEnabled = true + // The poll path delivers from its own handler, so this one has to ask + // as well or a subscribed workflow gets an inline task with no data. + resp.StartedResponse.StreamSlices, err = recordworkflowtaskstarted.DeliverStreamSlices( + ctx, handler.shardContext, ms) + if err != nil { + return nil, err + } + resp.NewWorkflowTask, err = handler.withNewWorkflowTask(ctx, namespaceEntry.Name(), req, resp.StartedResponse) if err != nil { return nil, err @@ -977,6 +1007,7 @@ func (handler *WorkflowTaskCompletedHandler) createPollWorkflowTaskQueueResponse StartedTime: matchingResp.StartedTime, Queries: matchingResp.Queries, Messages: matchingResp.Messages, + StreamSlices: matchingResp.StreamSlices, } return resp, nil diff --git a/service/history/api/respondworkflowtaskcompleted/stream_appends.go b/service/history/api/respondworkflowtaskcompleted/stream_appends.go new file mode 100644 index 00000000000..de783932a95 --- /dev/null +++ b/service/history/api/respondworkflowtaskcompleted/stream_appends.go @@ -0,0 +1,117 @@ +package respondworkflowtaskcompleted + +import ( + "context" + + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + historyi "go.temporal.io/server/service/history/interfaces" +) + +// resolveStagedStreamSubscriptions turns subscribe commands for streams in +// other executions into cursors on this workflow. +// +// The lookup cannot happen in the command handler, which runs under the state +// lock with nowhere to do I/O from, and it cannot happen at delivery either, +// because by then the cursor has to already exist. So it happens here, between +// the commands and the commit. +// +// The pin goes on the stream before the cursor goes on the workflow, and that +// order is the guarantee. Interrupted after the first write there is a pin +// holding storage nothing reads, which costs space. The other order would leave +// a cursor no truncation floor protects, and truncation would be free to take a +// range it still points at. +func resolveStagedStreamSubscriptions( + ctx context.Context, + ms historyi.MutableState, + namespaceID string, + completedEventID int64, + staged []chasmworkflow.PendingStreamSubscription, +) error { + if len(staged) == 0 { + return nil + } + + wf, chasmCtx, err := ms.ChasmWorkflowComponent(ctx) + if err != nil { + return err + } + + for _, pending := range staged { + // Already subscribed, so only the event is owed. Re-registering would + // re-run the pin write with the original start offset, which would drag + // the stream's truncation floor back to where this consumer began. + if pending.AlreadySubscribed { + cursor, ok := wf.StreamCursors[pending.StreamID] + if !ok { + return serviceerror.NewInternalf( + "stream %q was marked already subscribed but has no cursor", pending.StreamID) + } + chasmworkflow.RecordStreamSubscribedOffset( + pending.Event, cursor.Get(chasmCtx).Offset()) + continue + } + + // A stream this workflow owns needs no lookup and no pin registration: + // it is in this execution, and its cursor commits with everything else. + if _, owned := wf.Streams[pending.StreamID]; owned { + startOffset, err := wf.SubscribeToOwnedStream( + chasmCtx, pending.StreamID, pending.StartOffset) + if err != nil { + return err + } + chasmworkflow.RecordStreamSubscribedOffset(pending.Event, startOffset) + continue + } + + ref := chasm.NewComponentRef[*stream.Stream](chasm.ExecutionKey{ + NamespaceID: namespaceID, + BusinessID: pending.StreamID, + }) + + // The engine rides the request context, installed by the interceptor. + state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return err + } + + startOffset := pending.StartOffset + if startOffset < 0 { + // Resolved once, here, and recorded. Left to delivery it would be a + // reading rather than a fact, and replay would resolve it again + // against a stream that has since moved. + startOffset = state.GetHeadOffset() + } + + consumerID := "workflow:" + ms.GetExecutionInfo().GetWorkflowId() + if _, _, err := chasm.UpdateComponent( + ctx, ref, + func(s *stream.Stream, mctx chasm.MutableContext, offset int64) (struct{}, error) { + return struct{}{}, s.RegisterConsumer( + mctx, consumerID, ms.GetExecutionInfo().GetWorkflowId(), "", offset, true) + }, + startOffset, + ); err != nil { + return err + } + + if _, err := wf.SubscribeToExternalStream(chasmCtx, chasmworkflow.ExternalStreamSubscription{ + StreamID: pending.StreamID, + CollectionID: state.GetCollectionId(), + BucketSize: state.GetBucketSize(), + StartOffset: startOffset, + KnownHead: state.GetHeadOffset(), + }); err != nil { + return err + } + + // Completed after the cursor exists. The event was written where the + // command was, but nothing outside this transaction sees either until + // the commit below, so a crash in between leaves no event claiming a + // subscription that was never made. + chasmworkflow.RecordStreamSubscribedOffset(pending.Event, startOffset) + } + return nil +} diff --git a/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go b/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go index 4e8da517eae..bb7e266681e 100644 --- a/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go +++ b/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go @@ -61,6 +61,9 @@ type ( workflowTaskDeployment *deploymentpb.Deployment // internal state + // Log writes staged by stream commands, flushed before this workflow + // task commits. + stagedStreamSubscriptions []chasmworkflow.PendingStreamSubscription hasBufferedEventsOrMessages bool workflowTaskFailedCause *workflowTaskFailedCause activityNotStartedCancelled bool @@ -354,6 +357,12 @@ func (handler *workflowTaskCompletedHandler) handleCommand( return nil, chasmErr } err = chasmHandler(chasmCtx, chasmWorkflow, validator, command, handlerOpts) + // A subscribe to a stream in another execution stages itself, + // since a command handler holds the state lock and has no + // context for the lookup it needs. Collect them for the + // resolution that has to precede this task's commit. + handler.stagedStreamSubscriptions = append( + handler.stagedStreamSubscriptions, chasmWorkflow.DrainStreamSubscriptions()...) // Fall back to the HSM handler either when the command type is not supported by CHASM (disabled // feature flag) or when the targeted entity is not owned by the CHASM tree (e.g. an operation // scheduled in HSM before the flag was flipped on). diff --git a/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler_test.go b/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler_test.go index 501cf4dc51e..d0293e1a2dc 100644 --- a/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler_test.go +++ b/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler_test.go @@ -81,6 +81,9 @@ func TestCommandProtocolMessage(t *testing.T) { out.ms.EXPECT().VisitUpdates(gomock.Any()).AnyTimes() out.ms.EXPECT().GetNamespaceEntry().Return(tests.LocalNamespaceEntry).AnyTimes() out.ms.EXPECT().GetCurrentVersion().Return(tests.LocalNamespaceEntry.FailoverVersion(tests.WorkflowID)).AnyTimes() + out.ms.EXPECT().GetExecutionInfo().Return(&persistencespb.WorkflowExecutionInfo{ + WorkflowTaskAttempt: 1, + }).AnyTimes() dcClient := dynamicconfig.StaticClient(nil) if opts.chasmEnabled { diff --git a/service/history/historybuilder/event_factory.go b/service/history/historybuilder/event_factory.go index 058cb55b4ed..4f2c2f059af 100644 --- a/service/history/historybuilder/event_factory.go +++ b/service/history/historybuilder/event_factory.go @@ -10,6 +10,7 @@ import ( failurepb "go.temporal.io/api/failure/v1" historypb "go.temporal.io/api/history/v1" sdkpb "go.temporal.io/api/sdk/v1" + streampb "go.temporal.io/api/stream/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" updatepb "go.temporal.io/api/update/v1" workflowpb "go.temporal.io/api/workflow/v1" @@ -162,6 +163,7 @@ func (b *EventFactory) CreateWorkflowTaskCompletedEvent( deploymentName string, deployment *deploymentpb.Deployment, behavior enumspb.VersioningBehavior, + streamCursors []*streampb.StreamCursor, ) *historypb.HistoryEvent { event := b.createHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_TASK_COMPLETED, b.timeSource.Now()) event.Attributes = &historypb.HistoryEvent_WorkflowTaskCompletedEventAttributes{ @@ -176,6 +178,9 @@ func (b *EventFactory) CreateWorkflowTaskCompletedEvent( WorkerDeploymentName: deploymentName, DeploymentVersion: worker_versioning.ExternalWorkerDeploymentVersionFromDeployment(deployment), VersioningBehavior: behavior, + // Offsets only. The payloads the task consumed rode the task + // response, so History grows with tasks rather than with messages. + StreamCursors: streamCursors, }, } diff --git a/service/history/historybuilder/event_store.go b/service/history/historybuilder/event_store.go index a6fb18a4b9e..ceb5d7b1d96 100644 --- a/service/history/historybuilder/event_store.go +++ b/service/history/historybuilder/event_store.go @@ -331,7 +331,9 @@ func (b *EventStore) bufferEvent( enumspb.EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES, enumspb.EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED, enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, - enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: + enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: // do not buffer event if event is directly generated from a corresponding command return false diff --git a/service/history/historybuilder/history_builder.go b/service/history/historybuilder/history_builder.go index 4690a0e92b1..85934808916 100644 --- a/service/history/historybuilder/history_builder.go +++ b/service/history/historybuilder/history_builder.go @@ -10,6 +10,7 @@ import ( failurepb "go.temporal.io/api/failure/v1" historypb "go.temporal.io/api/history/v1" sdkpb "go.temporal.io/api/sdk/v1" + streampb "go.temporal.io/api/stream/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" updatepb "go.temporal.io/api/update/v1" workflowpb "go.temporal.io/api/workflow/v1" @@ -236,6 +237,7 @@ func (b *HistoryBuilder) AddWorkflowTaskCompletedEvent( deploymentName string, deployment *deploymentpb.Deployment, behavior enumspb.VersioningBehavior, + streamCursors []*streampb.StreamCursor, ) *historypb.HistoryEvent { event := b.CreateWorkflowTaskCompletedEvent( scheduledEventID, @@ -248,6 +250,7 @@ func (b *HistoryBuilder) AddWorkflowTaskCompletedEvent( deploymentName, deployment, behavior, + streamCursors, ) event, _ = b.add(event) return event diff --git a/service/history/historybuilder/history_builder_categorization_test.go b/service/history/historybuilder/history_builder_categorization_test.go index 228824a50e5..43b6aed58e0 100644 --- a/service/history/historybuilder/history_builder_categorization_test.go +++ b/service/history/historybuilder/history_builder_categorization_test.go @@ -1277,6 +1277,7 @@ func (s *sutTestingAdapter) AddWorkflowTaskCompletedEvent(_ ...eventConfig) *his "", nil, enumspb.VERSIONING_BEHAVIOR_UNSPECIFIED, + nil, ) } diff --git a/service/history/historybuilder/history_builder_test.go b/service/history/historybuilder/history_builder_test.go index d7b13d5fd6c..a8b02c4eaf8 100644 --- a/service/history/historybuilder/history_builder_test.go +++ b/service/history/historybuilder/history_builder_test.go @@ -704,6 +704,7 @@ func (s *historyBuilderSuite) TestWorkflowTaskCompleted() { "", nil, enumspb.VERSIONING_BEHAVIOR_UNSPECIFIED, + nil, ) s.Equal(event, s.flush()) protorequire.ProtoEqual(s.T(), &historypb.HistoryEvent{ @@ -2272,6 +2273,8 @@ func (s *historyBuilderSuite) TestBufferEvent() { enumspb.EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: true, enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: true, enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: true, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: true, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: true, } // events corresponding to message from client will be assigned an event ID immediately @@ -2321,7 +2324,8 @@ func (s *historyBuilderSuite) TestBufferEvent() { commandType := enumspb.CommandType(ct) // Unspecified is not counted. // ProtocolMessage command doesn't have corresponding event. - if commandType == enumspb.COMMAND_TYPE_UNSPECIFIED || commandType == enumspb.COMMAND_TYPE_PROTOCOL_MESSAGE { + if commandType == enumspb.COMMAND_TYPE_UNSPECIFIED || + commandType == enumspb.COMMAND_TYPE_PROTOCOL_MESSAGE { continue } commandsWithEventsCount++ diff --git a/service/history/interfaces/mutable_state.go b/service/history/interfaces/mutable_state.go index 632c0fbfb38..b806c4097d0 100644 --- a/service/history/interfaces/mutable_state.go +++ b/service/history/interfaces/mutable_state.go @@ -366,6 +366,15 @@ type ( ChasmEnabled() bool ChasmSignalBacklinksEnabled() bool ChasmWorkflowComponent(ctx context.Context) (*chasmworkflow.Workflow, chasm.MutableContext, error) + // HasChasmWorkflowComponent reports whether a workflow component is + // actually reachable. Archetype alone does not answer this: a workflow + // predating CHASM reports the workflow archetype while carrying a tree + // that holds no components. + HasChasmWorkflowComponent() bool + // HasPendingStreamData reports whether a stream subscription still has + // offsets to deliver, which is the only case where stream traffic + // schedules a workflow task. + HasPendingStreamData() bool ChasmWorkflowComponentReadOnly(ctx context.Context) (*chasmworkflow.Workflow, chasm.Context, error) // Ensures that the chasm workflow component is installed in the mutable state CHASM tree. // Must be called before adding any components to the tree. diff --git a/service/history/interfaces/mutable_state_mock.go b/service/history/interfaces/mutable_state_mock.go index 15a5b7221da..4cf77df6e5d 100644 --- a/service/history/interfaces/mutable_state_mock.go +++ b/service/history/interfaces/mutable_state_mock.go @@ -3052,6 +3052,20 @@ func (mr *MockMutableStateMockRecorder) HasBufferedEvents() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasBufferedEvents", reflect.TypeOf((*MockMutableState)(nil).HasBufferedEvents)) } +// HasChasmWorkflowComponent mocks base method. +func (m *MockMutableState) HasChasmWorkflowComponent() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasChasmWorkflowComponent") + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasChasmWorkflowComponent indicates an expected call of HasChasmWorkflowComponent. +func (mr *MockMutableStateMockRecorder) HasChasmWorkflowComponent() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasChasmWorkflowComponent", reflect.TypeOf((*MockMutableState)(nil).HasChasmWorkflowComponent)) +} + // HasCompletedAnyWorkflowTask mocks base method. func (m *MockMutableState) HasCompletedAnyWorkflowTask() bool { m.ctrl.T.Helper() @@ -3080,6 +3094,20 @@ func (mr *MockMutableStateMockRecorder) HasParentExecution() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasParentExecution", reflect.TypeOf((*MockMutableState)(nil).HasParentExecution)) } +// HasPendingStreamData mocks base method. +func (m *MockMutableState) HasPendingStreamData() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasPendingStreamData") + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasPendingStreamData indicates an expected call of HasPendingStreamData. +func (mr *MockMutableStateMockRecorder) HasPendingStreamData() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPendingStreamData", reflect.TypeOf((*MockMutableState)(nil).HasPendingStreamData)) +} + // HasPendingWorkflowTask mocks base method. func (m *MockMutableState) HasPendingWorkflowTask() bool { m.ctrl.T.Helper() diff --git a/service/history/workflow/mutable_state_impl.go b/service/history/workflow/mutable_state_impl.go index 069c3e37a24..c1c0d277c6c 100644 --- a/service/history/workflow/mutable_state_impl.go +++ b/service/history/workflow/mutable_state_impl.go @@ -22,6 +22,7 @@ import ( historypb "go.temporal.io/api/history/v1" rulespb "go.temporal.io/api/rules/v1" "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" updatepb "go.temporal.io/api/update/v1" workerpb "go.temporal.io/api/worker/v1" @@ -672,6 +673,98 @@ func (ms *MutableStateImpl) mustInitHSM() { ms.stateMachineNode = stateMachineNode } +// commitStreamCursors folds each staged range into its cursor and returns the +// ranges to record. Called as the completed event is built, so the advance and +// the event carrying the range are in one transaction: split apart, a crash +// between them would either redeliver a range or skip it with nothing in +// History to say so. +func (ms *MutableStateImpl) commitStreamCursors() ([]*streampb.StreamCursor, error) { + if !ms.HasChasmWorkflowComponent() { + return nil, nil + } + // Reaching the component through a mutable context marks it dirty, which + // would add a node to the transaction of every workflow that has no + // subscription at all. Ask read-only first, and take the write path only + // when there is something to fold in. + // + // The background context matches EnsureChasmWorkflowComponent: it only + // reaches components already in memory. + wf, _, err := ms.ChasmWorkflowComponentReadOnly(context.Background()) + if err != nil { + return nil, err + } + if len(wf.StreamCursors) == 0 { + return nil, nil + } + + wf, chasmCtx, err := ms.ChasmWorkflowComponent(context.Background()) + if err != nil { + return nil, err + } + return wf.CommitStreamCursors(chasmCtx), nil +} + +// carryStreamSubscriptionsTo hands this run's subscriptions to the run that +// continues it. +// +// A cursor is workflow state, so without this a continue-as-new would silently +// end a subscription the workflow never cancelled: the stream would keep its +// consumer pin and keep pushing to the workflow id, and the successor would +// have nowhere to put it. +func (ms *MutableStateImpl) carryStreamSubscriptionsTo(newMutableState *MutableStateImpl) error { + if !ms.HasChasmWorkflowComponent() { + return nil + } + wf, chasmCtx, err := ms.ChasmWorkflowComponentReadOnly(context.Background()) + if err != nil { + return err + } + subscriptions := wf.ExportStreamSubscriptions(chasmCtx) + if len(subscriptions) == 0 { + return nil + } + + newMutableState.EnsureChasmWorkflowComponent(context.Background()) + newWorkflow, newChasmCtx, err := newMutableState.ChasmWorkflowComponent(context.Background()) + if err != nil { + return err + } + return newWorkflow.ImportStreamSubscriptions(newChasmCtx, subscriptions) +} + +// HasPendingStreamData reports whether a subscription of this workflow has +// offsets left to deliver, which is the one condition under which stream +// traffic schedules a workflow task. +// +// Called on every transaction close for every workflow, almost none of which +// have a subscription, so it resolves the root component once rather than +// asking whether it exists and then asking for it. +func (ms *MutableStateImpl) HasPendingStreamData() bool { + node, ok := ms.chasmTree.(*chasm.Node) + if !ok { + return false + } + chasmCtx := chasm.NewContext(context.Background(), node) + rootComponent, err := node.ComponentByPath(chasmCtx, nil) + if err != nil { + return false + } + wf, ok := rootComponent.(*chasmworkflow.Workflow) + if !ok { + return false + } + return wf.StreamCursorsBehind(chasmCtx) +} + +func (ms *MutableStateImpl) HasChasmWorkflowComponent() bool { + node, ok := ms.chasmTree.(*chasm.Node) + if !ok { + return false + } + _, err := node.ComponentByPath(chasm.NewContext(context.Background(), node), nil) + return err == nil +} + func (ms *MutableStateImpl) IsWorkflow() bool { return ms.chasmTree.ArchetypeID() == chasm.WorkflowArchetypeID } @@ -6394,6 +6487,10 @@ func (ms *MutableStateImpl) AddContinueAsNewEvent( return nil, nil, err } + if err = ms.carryStreamSubscriptionsTo(newMutableState); err != nil { + return nil, nil, err + } + if err = ms.ApplyWorkflowExecutionContinuedAsNewEvent( batchID, continueAsNewEvent, @@ -7939,6 +8036,25 @@ func (ms *MutableStateImpl) closeTransactionHandleWorkflowTaskScheduling( } } + // A stream subscription with offsets left to deliver. Handled here rather + // than at workflow task completion so it also covers the transaction that + // registers a subscription against a stream that already has data, which + // completes no workflow task of its own. + // + // The pending-task check comes first because it is cheap: a workflow that + // already owes a task needs no further reason to run, so the subscription + // state is only consulted when the answer could change something. + if !ms.HasPendingWorkflowTask() && + !ms.IsWorkflowExecutionStatusPaused() && + ms.HasPendingStreamData() { + if _, err := ms.AddWorkflowTaskScheduledEvent( + false, + enumsspb.WORKFLOW_TASK_TYPE_NORMAL, + ); err != nil { + return err + } + } + return nil } diff --git a/service/history/workflow/workflow_task_state_machine.go b/service/history/workflow/workflow_task_state_machine.go index 72123c71bfa..9ecc63b75ae 100644 --- a/service/history/workflow/workflow_task_state_machine.go +++ b/service/history/workflow/workflow_task_state_machine.go @@ -826,6 +826,11 @@ func (m *workflowTaskStateMachine) AddWorkflowTaskCompletedEvent( //nolint:staticcheck // SA1019 deprecated Deployment will clean up later wftDeployment := worker_versioning.DeploymentOrVersion(request.Deployment, wftDeploymentVersion) + streamCursors, err := m.ms.commitStreamCursors() + if err != nil { + return nil, err + } + // Now write the completed event event := m.ms.hBuilder.AddWorkflowTaskCompletedEvent( workflowTask.ScheduledEventID, @@ -838,6 +843,7 @@ func (m *workflowTaskStateMachine) AddWorkflowTaskCompletedEvent( deploymentName, wftDeployment, vb, + streamCursors, ) override := m.ms.GetExecutionInfo().GetVersioningInfo().GetVersioningOverride() @@ -849,7 +855,7 @@ func (m *workflowTaskStateMachine) AddWorkflowTaskCompletedEvent( } wftScheduleToClose := event.GetEventTime().AsTime().Sub(workflowTask.ScheduledTime) - err := m.afterAddWorkflowTaskCompletedEvent(event, limits, wftScheduleToClose) + err = m.afterAddWorkflowTaskCompletedEvent(event, limits, wftScheduleToClose) if err != nil { return nil, err } From 53e99e9a4dbf4c5918d30503fcba1d16bacb9172 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 14 Sep 2026 15:59:55 -0700 Subject: [PATCH 06/47] Delivered consumed ranges through Workflow Tasks, routed across hosts. --- common/util.go | 1 + .../api/recordworkflowtaskstarted/api.go | 19 + .../stream_routing.go | 78 +++ .../stream_routing_test.go | 127 +++++ .../stream_slices.go | 495 ++++++++++++++++++ service/history/fx.go | 2 + service/history/history_engine.go | 15 +- service/history/history_engine_factory.go | 3 + service/history/stream_routing_test.go | 18 + service/matching/matching_engine.go | 1 + service/matching/wire_compat_test.go | 173 ++++++ 11 files changed, 930 insertions(+), 2 deletions(-) create mode 100644 service/history/api/recordworkflowtaskstarted/stream_routing.go create mode 100644 service/history/api/recordworkflowtaskstarted/stream_routing_test.go create mode 100644 service/history/api/recordworkflowtaskstarted/stream_slices.go create mode 100644 service/history/stream_routing_test.go create mode 100644 service/matching/wire_compat_test.go diff --git a/common/util.go b/common/util.go index 87e02bbc63a..f67c0daa7e7 100644 --- a/common/util.go +++ b/common/util.go @@ -544,6 +544,7 @@ func CreateMatchingPollWorkflowTaskQueueResponse(historyResponse *historyservice StartedTime: historyResponse.StartedTime, Queries: historyResponse.Queries, Messages: historyResponse.Messages, + StreamSlices: historyResponse.StreamSlices, History: historyResponse.History, NextPageToken: historyResponse.NextPageToken, RawHistory: historyResponse.RawHistoryBytes, diff --git a/service/history/api/recordworkflowtaskstarted/api.go b/service/history/api/recordworkflowtaskstarted/api.go index e88a3c6c9c3..98650b444ab 100644 --- a/service/history/api/recordworkflowtaskstarted/api.go +++ b/service/history/api/recordworkflowtaskstarted/api.go @@ -51,6 +51,7 @@ func Invoke( var workflowKey definition.WorkflowKey var resp *historyservice.RecordWorkflowTaskStartedResponseWithRawHistory + var streamAddresses map[string]streamOrigin err = api.GetAndUpdateWorkflowWithNew( ctx, @@ -102,6 +103,12 @@ func Invoke( if err != nil { return nil, err } + // Redelivers whatever range is already staged, so a + // duplicate of the same request hands back the same slice. + resp.StreamSlices, streamAddresses, err = deliverStreamSlices(ctx, shardContext, mutableState) + if err != nil { + return nil, err + } updateAction.Noop = true return updateAction, nil } @@ -238,6 +245,11 @@ func Invoke( return nil, err } + resp.StreamSlices, streamAddresses, err = deliverStreamSlices(ctx, shardContext, mutableState) + if err != nil { + return nil, err + } + return updateAction, nil }, nil, @@ -264,6 +276,12 @@ func Invoke( if err != nil { return nil, err } + + // After the history is attached, because the ranges to re-supply are read + // out of the events being sent. + if err := attachReplaySlices(ctx, workflowKey, workflowKey.GetNamespaceID(), streamAddresses, resp); err != nil { + return nil, err + } return resp, nil } @@ -400,6 +418,7 @@ func CreateRecordWorkflowTaskStartedResponse( Queries: rawResp.Queries, Clock: rawResp.Clock, Messages: rawResp.Messages, + StreamSlices: rawResp.StreamSlices, Version: rawResp.Version, NextPageToken: rawResp.NextPageToken, }, nil diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing.go b/service/history/api/recordworkflowtaskstarted/stream_routing.go new file mode 100644 index 00000000000..770d1a2016d --- /dev/null +++ b/service/history/api/recordworkflowtaskstarted/stream_routing.go @@ -0,0 +1,78 @@ +package recordworkflowtaskstarted + +import ( + "context" + + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "google.golang.org/protobuf/proto" +) + +type streamClientContextKey struct{} + +// WithStreamClient supplies shard routing for standalone stream payload reads. +func WithStreamClient(ctx context.Context, client streamlib.StreamServiceClient) context.Context { + if client == nil { + return ctx + } + return context.WithValue(ctx, streamClientContextKey{}, client) +} + +func readExternalWindow(ctx context.Context, namespaceID, streamID string, from, to int64) (stream.Window, error) { + if to <= from { + return stream.Window{To: from}, nil + } + limit := int32(min(to-from, int64(stream.DefaultMaxMessagesPerPoll))) + client, ok := ctx.Value(streamClientContextKey{}).(streamlib.StreamServiceClient) + if !ok { + // Hand-built engines in component tests have no service client. + return chasm.ReadComponent(ctx, + chasm.NewComponentRef[*stream.Stream](chasm.ExecutionKey{ + NamespaceID: namespaceID, + BusinessID: streamID, + }), + (*stream.Stream).ReadWindow, + stream.WindowRequest{From: from, MaxMessages: limit}) + } + + response, err := client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + NamespaceId: namespaceID, + FrontendRequest: &streamlib.PollMessagesInput{ + StreamId: streamID, FromOffset: from, MaxMessages: limit, + }, + }) + if err != nil { + return stream.Window{}, err + } + out := response.GetFrontendResponse() + if out == nil || out.GetNextOffset() < from || out.GetNextOffset() > to || + out.GetNextOffset() > out.GetHeadOffset() || + int64(len(out.GetMessages())) != out.GetNextOffset()-from { + return stream.Window{}, serviceerror.NewDataLoss("stream read returned an invalid contiguous range") + } + for index, message := range out.GetMessages() { + if message == nil || message.GetOffset() != from+int64(index) { + return stream.Window{}, serviceerror.NewDataLoss("stream read returned a missing or reordered offset") + } + } + w := stream.Window{ + State: &streamlib.StreamState{ + HeadOffset: out.GetHeadOffset(), Closed: out.GetClosed(), CloseReason: out.GetCloseReason(), + }, + To: out.GetNextOffset(), Limit: int(limit), + } + if len(out.GetMessages()) == 0 { + return w, nil + } + data, err := proto.Marshal(&streamlib.StreamMessageBatch{Messages: out.GetMessages()}) + if err != nil { + return stream.Window{}, err + } + w.Blobs = []*commonpb.DataBlob{{EncodingType: enumspb.ENCODING_TYPE_PROTO3, Data: data}} + w.Starts = []int64{from} + return w, nil +} diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing_test.go b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go new file mode 100644 index 00000000000..a4150151c25 --- /dev/null +++ b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go @@ -0,0 +1,127 @@ +package recordworkflowtaskstarted + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common/definition" + "go.uber.org/mock/gomock" + "google.golang.org/grpc" +) + +type routedStreamClient struct { + streamlib.StreamServiceClient + poll func(context.Context, *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) +} + +func (c *routedStreamClient) PollMessages( + ctx context.Context, request *streamlib.PollMessagesRequest, _ ...grpc.CallOption, +) (*streamlib.PollMessagesResponse, error) { + return c.poll(ctx, request) +} + +func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { + for _, replay := range []bool{false, true} { + t.Run(map[bool]string{false: "live", true: "replay"}[replay], func(t *testing.T) { + calls := 0 + client := &routedStreamClient{poll: func(ctx context.Context, req *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + calls++ + require.Equal(t, "namespace-id", req.GetNamespaceId()) + require.Equal(t, "remote-source", req.GetFrontendRequest().GetStreamId()) + require.EqualValues(t, 4, req.GetFrontendRequest().GetFromOffset()) + require.EqualValues(t, 2, req.GetFrontendRequest().GetMaxMessages()) + require.False(t, req.GetFrontendRequest().GetWaitNewMessages()) + require.Empty(t, req.GetFrontendRequest().GetTopics()) + return &streamlib.PollMessagesResponse{FrontendResponse: &streamlib.PollMessagesOutput{ + NextOffset: 6, HeadOffset: 9, + Messages: []*streamlib.StreamMessage{ + {Offset: 4, Kind: streamlib.STREAM_MESSAGE_KIND_DATA, Body: &commonpb.Payload{Data: []byte("a")}}, + {Offset: 5, Kind: streamlib.STREAM_MESSAGE_KIND_DATA, Body: &commonpb.Payload{Data: []byte("b")}}, + }, + }}, nil + }} + // No local engine is installed. Reverting to the old local-controller + // path cannot accidentally satisfy this test's remote source. + ctx := WithStreamClient(context.Background(), client) + var window stream.Window + var err error + if replay { + window, err = readRecordedRange(ctx, + definition.NewWorkflowKey("namespace-id", "consumer", "consumer-run"), + streamOrigin{external: true}, "remote-source", 4, 6) + } else { + window, err = readWindowFor(ctx, nil, nil, "namespace-id", "input", true, "remote-source", 4, 6) + } + require.NoError(t, err) + require.Equal(t, 1, calls) + messages, next, err := stream.CollectMessages(window.Blobs, window.Starts, 4, window.To, window.Limit, nil) + require.NoError(t, err) + require.EqualValues(t, 6, next) + require.Len(t, messages, 2) + require.Equal(t, []byte("a"), messages[0].GetBody().GetData()) + require.Equal(t, []byte("b"), messages[1].GetBody().GetData()) + }) + } +} + +func TestExternalStreamRoutedReadRejectsCorruptOrExpandedRange(t *testing.T) { + cases := map[string]*streamlib.PollMessagesOutput{ + "missing-response": nil, + "skipped-offset": {NextOffset: 6, HeadOffset: 9, Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 6}}}, + "short-count": {NextOffset: 6, HeadOffset: 9, Messages: []*streamlib.StreamMessage{{Offset: 4}}}, + "past-requested-frontier": {NextOffset: 7, HeadOffset: 9, Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}, {Offset: 6}}}, + "past-committed-head": {NextOffset: 6, HeadOffset: 5, Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}}}, + } + for name, response := range cases { + t.Run(name, func(t *testing.T) { + client := &routedStreamClient{poll: func(context.Context, *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + return &streamlib.PollMessagesResponse{FrontendResponse: response}, nil + }} + _, err := readExternalWindow(WithStreamClient(context.Background(), client), "ns", "source", 4, 6) + var integrity *serviceerror.DataLoss + require.ErrorAs(t, err, &integrity) + }) + } +} + +func TestExternalStreamRoutedReadPropagatesSourceFailure(t *testing.T) { + want := errors.New("source shard unavailable") + client := &routedStreamClient{poll: func(context.Context, *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + return nil, want + }} + _, err := readExternalWindow(WithStreamClient(context.Background(), client), "ns", "source", 4, 6) + require.ErrorIs(t, err, want) +} + +func TestOwnedLiveStreamNeverReentersThroughRoutedClient(t *testing.T) { + client := &routedStreamClient{poll: func(context.Context, *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + t.Fatal("owned stream read re-entered the consumer through an RPC") + return nil, nil + }} + window, err := readWindowFor(WithStreamClient(context.Background(), client), nil, + &chasmworkflow.Workflow{}, "ns", "owned", false, "owned", 4, 6) + require.NoError(t, err) + require.EqualValues(t, 4, window.To) +} + +func TestOwnedReplayPinsConsumerRun(t *testing.T) { + engine := chasm.NewMockEngine(gomock.NewController(t)) + engine.EXPECT().ReadComponent(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, ref chasm.ComponentRef, _ func(chasm.Context, chasm.Component) error, _ ...chasm.TransitionOption) error { + require.Equal(t, "consumer-run", ref.RunID) + require.Equal(t, "consumer", ref.BusinessID) + return nil + }) + _, err := readRecordedRange(chasm.NewEngineContext(context.Background(), engine), + definition.NewWorkflowKey("ns", "consumer", "consumer-run"), + streamOrigin{name: "owned"}, "owned", 4, 6) + require.NoError(t, err) +} diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go new file mode 100644 index 00000000000..07d256e6028 --- /dev/null +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -0,0 +1,495 @@ +package recordworkflowtaskstarted + +import ( + "context" + "slices" + + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/server/api/historyservice/v1" + "go.temporal.io/server/chasm" + "go.temporal.io/server/chasm/lib/stream" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common/definition" + "go.temporal.io/server/common/persistence/serialization" + historyi "go.temporal.io/server/service/history/interfaces" + "google.golang.org/protobuf/proto" +) + +// deliverStreamSlices hands the next range of every stream this workflow +// consumes to the task being started, and stages that range on the cursor so +// the event closing the task can record what was delivered. +// +// The log read runs with the workflow lock held. That is the price of deciding +// a range and staging it in one transaction: staged first and read after, a +// failed read would leave a range that the worker never received but that the +// completion would still record as consumed. +// streamOrigin says where a subscribed stream lives, which decides how its +// payload is read. The payload is component state now, so an external stream is +// read from its own execution and an owned one from the consumer's. +type streamOrigin struct { + external bool + // The name the consumer knows the stream by, which is how an owned one is + // found on the consumer's own component. + name string + + // Where the subscription began, and how far its completed tasks have + // committed. Together they say exactly which offsets replay owes the + // worker, so a re-supply that comes up short can be detected instead of + // silently handing the workflow less than it originally saw. + start int64 + committed int64 +} + +// deliveryFrontier is the offset a delivery clips to. For a stream this +// execution owns it is read directly; for one in another execution it is +// whatever that stream last pushed here, because reading the real value would +// mean reaching across executions while this transaction is open. +func deliveryFrontier( + chasmCtx chasm.Context, + wf *chasmworkflow.Workflow, + name string, + cursor *stream.Cursor, +) (int64, error) { + if cursor.IsExternal() { + return cursor.KnownHead(), nil + } + field, ok := wf.Streams[name] + if !ok { + return 0, serviceerror.NewFailedPreconditionf( + "workflow consumes stream %q, which it neither owns nor subscribed to externally", name) + } + state, err := field.Get(chasmCtx).Snapshot(chasmCtx, struct{}{}) + if err != nil { + return 0, err + } + + // A consumer that fell behind a truncating stream is told so rather than + // handed the rest and left with a hole it cannot see. Nothing holds the + // floor for a consumer any more, on purpose: a floor that waited for the + // slowest reader was never released and turned every cap into a no-op. + if cursor.Offset() < state.GetBaseOffset() { + return 0, serviceerror.NewFailedPreconditionf( + "stream %q was truncated past this consumer: it is at offset %d and the stream now starts at %d", + name, cursor.Offset(), state.GetBaseOffset()) + } + return state.GetHeadOffset(), nil +} + +func readDeliverable( + ctx context.Context, + chasmCtx chasm.Context, + wf *chasmworkflow.Workflow, + namespaceID string, + name string, + cursor *stream.Cursor, + from, to int64, +) ([]*streampb.StreamMessage, int64, error) { + if to <= from { + return nil, from, nil + } + w, err := readWindowFor(ctx, chasmCtx, wf, namespaceID, name, cursor.IsExternal(), + cursor.StreamID(), from, to) + if err != nil { + return nil, 0, err + } + // The collected run is contiguous from `from`, so the byte cap recomputes + // the same end offset the read would have reported. + collected, _, err := stream.CollectMessages( + w.Blobs, w.Starts, from, w.To, stream.MaxConsumeItemsPerTask, nil) + if err != nil { + return nil, 0, err + } + // Cap before converting: the byte budget applies to the run as stored, and + // trimming decides how far the recorded range reaches. + collected, readTo := stream.CapByBytes(collected, from, stream.MaxConsumeBytesPerTask) + return stream.ToAPIMessages(collected), readTo, nil +} + +// readWindowFor reads a range from whichever component holds it. +// +// Standalone external streams use the routed service client. That RPC only +// reads the source execution and never calls back into the locked consumer. +// Owned streams must use the already-loaded component: re-entering this +// execution through an RPC would contend with the lock already held. +func readWindowFor( + ctx context.Context, + chasmCtx chasm.Context, + wf *chasmworkflow.Workflow, + namespaceID string, + name string, + external bool, + streamID string, + from, to int64, +) (stream.Window, error) { + req := stream.WindowRequest{From: from, MaxMessages: int32(to - from)} + if !external { + s := wf.OwnedStream(chasmCtx, name) + if s == nil { + return stream.Window{To: from}, nil + } + return s.ReadWindow(chasmCtx, req) + } + return readExternalWindow(ctx, namespaceID, streamID, from, to) +} + +// DeliverStreamSlices hands the next range to a task built outside this +// package. The inline task returned by RespondWorkflowTaskCompleted is built by +// its own handler, so without this a subscribed workflow gets no data on the +// dispatch path every current SDK asks for. +// +// Only the live range. That task is always sticky, so the worker still holds +// the execution and has no ranges to replay. +func DeliverStreamSlices( + ctx context.Context, + shardContext historyi.ShardContext, + ms historyi.MutableState, +) ([]*streampb.StreamSlice, error) { + live, _, err := deliverStreamSlices(ctx, shardContext, ms) + return live, err +} + +func deliverStreamSlices( + ctx context.Context, + shardContext historyi.ShardContext, + ms historyi.MutableState, +) ([]*streampb.StreamSlice, map[string]streamOrigin, error) { + if !ms.HasChasmWorkflowComponent() { + return nil, nil, nil + } + // Read-only first. Reaching the component mutably marks it dirty, and a + // workflow with no subscription should not pay a node in its transaction + // for every task it runs. + if readOnly, _, err := ms.ChasmWorkflowComponentReadOnly(ctx); err != nil { + return nil, nil, err + } else if len(readOnly.StreamCursors) == 0 { + return nil, nil, nil + } + + wf, chasmCtx, err := ms.ChasmWorkflowComponent(ctx) + if err != nil { + return nil, nil, err + } + + names := make([]string, 0, len(wf.StreamCursors)) + for name := range wf.StreamCursors { + names = append(names, name) + } + // Delivery order has to be stable, because the completion records these + // ranges in the order they were produced. + slices.Sort(names) + + maxItems := stream.MaxConsumeItemsPerTask + namespaceID := ms.GetExecutionInfo().GetNamespaceId() + + slicesOut := make([]*streampb.StreamSlice, 0, len(names)) + addresses := make(map[string]streamOrigin, len(names)) + for _, name := range names { + cursor := wf.StreamCursors[name].Get(chasmCtx) + + head, err := deliveryFrontier(chasmCtx, wf, name, cursor) + if err != nil { + return nil, nil, err + } + + // A range already staged is redelivered unchanged. The same task can be + // started more than once, and letting the second attempt pick up newer + // data would hand the workflow a different range than the one the + // completion is going to record. + from, to, restaged := cursor.Pending() + if !restaged { + from = cursor.Offset() + // Clip to the frontier. Bytes reach the log before the transaction + // that makes them visible commits, so reading past head risks + // delivering an offset whose content a retry could still replace. + to = min(from+int64(maxItems), head) + } + + messages, next, err := readDeliverable( + ctx, chasmCtx, wf, namespaceID, name, cursor, from, to) + if err != nil { + return nil, nil, err + } + + if restaged { + // The staged range is the one the completion will record, so the + // worker has to be handed exactly that. A re-read that comes back + // short would otherwise deliver less than history claims was + // consumed, and replay would then disagree with the original run. + if next != to { + return nil, nil, serviceerror.NewInternalf( + "stream %q staged range [%d,%d) re-read as [%d,%d)", + cursor.StreamID(), from, to, from, next) + } + } else if err := cursor.StagePending(chasmCtx, from, next); err != nil { + return nil, nil, err + } + + // Attached even when empty. A task that saw nothing still has to record + // that it saw nothing, and the slice is what the completion reads. + slicesOut = append(slicesOut, &streampb.StreamSlice{ + StreamId: cursor.StreamID(), + FromOffset: from, + ToOffset: next, + Messages: messages, + }) + addresses[cursor.StreamID()] = streamOrigin{ + external: cursor.IsExternal(), + name: name, + start: cursor.StartOffset(), + committed: cursor.Offset(), + } + } + return slicesOut, addresses, nil +} + +// Bounds on one cold replay's re-supply. History holds offsets and not payloads, +// so replaying a consumer means re-reading every range its completed tasks +// recorded, and that grows with the workflow's whole life rather than with the +// task being started. These cap one response; a consumer past them cannot be +// replayed by this path at all, which is a limit of the design rather than of +// the numbers. +const ( + maxReplayMessages = 100_000 + maxReplayBytes = 64 << 20 +) + +// ownedRange names a range of a stream the consumer owns. +type ownedRange struct { + name string + req stream.WindowRequest +} + +// readRecordedRange re-supplies a range a completed task recorded. +// +// It runs after the execution lock is released, so the consumer's own component +// is read back through the engine. Standalone sources use the routed service +// client, since their shards can belong to a different history host. +func readRecordedRange( + ctx context.Context, + consumer definition.WorkflowKey, + origin streamOrigin, + streamID string, + from, to int64, +) (stream.Window, error) { + req := stream.WindowRequest{From: from, MaxMessages: int32(to - from)} + if origin.external { + return readExternalWindow(ctx, consumer.NamespaceID, streamID, from, to) + } + return chasm.ReadComponent(ctx, + chasm.NewComponentRef[*chasmworkflow.Workflow](chasm.ExecutionKey{ + NamespaceID: consumer.NamespaceID, + BusinessID: consumer.WorkflowID, + RunID: consumer.RunID, + }), + func(wf *chasmworkflow.Workflow, cctx chasm.Context, r ownedRange) (stream.Window, error) { + s := wf.OwnedStream(cctx, r.name) + if s == nil { + return stream.Window{To: r.req.From}, nil + } + return s.ReadWindow(cctx, r.req) + }, + ownedRange{name: origin.name, req: req}) +} + +// attachReplaySlices re-supplies the payloads for ranges that earlier workflow +// tasks recorded, keyed by the event that recorded each one. +// +// History holds offsets and never payloads, which is the property the whole +// design rests on. The cost lands here: a worker replaying from History has to +// be handed the same bytes those tasks were given, and the only place they +// exist is the stream's log. The response field alone cannot carry this, +// because it is built once per delivery while a cache miss replays every prior +// task, so each range travels with the id of the event that recorded it. +func attachReplaySlices( + ctx context.Context, + consumer definition.WorkflowKey, + namespaceID string, + addresses map[string]streamOrigin, + resp *historyservice.RecordWorkflowTaskStartedResponseWithRawHistory, +) error { + // Only a workflow with a live subscription has anything to re-supply, and + // that is exactly when delivery attached a slice for the current task. So + // this costs nothing for every other workflow. + if len(addresses) == 0 { + return nil + } + + // A sticky task means the worker still holds the execution, so it has + // nothing to replay. Its history begins at the previous task's completion, + // and that event carries the range that task already consumed, so + // re-supplying it here would hand the workflow the same messages twice. + if resp.GetStickyExecutionEnabled() { + return nil + } + + events, err := eventsOfResponse(resp) + if err != nil { + return err + } + + budget := replayBudget{} + // How far the recorded ranges reach, per stream, so the coverage check + // below can tell a complete re-supply from a short one. + reached := make(map[string]int64, len(addresses)) + + for _, event := range events { + for _, recorded := range event.GetWorkflowTaskCompletedEventAttributes().GetStreamCursors() { + address, ok := addresses[recorded.GetStreamId()] + if !ok { + // A subscription the workflow has since dropped. The range is + // still part of its history, but nothing is consuming it now. + continue + } + + messages, err := replayMessagesFor(ctx, consumer, address, recorded, &budget) + if err != nil { + return err + } + + if to := recorded.GetToOffset(); to > reached[recorded.GetStreamId()] { + reached[recorded.GetStreamId()] = to + } + + // Attached even when empty: the task observed nothing, and replay + // has to reproduce that rather than infer it from an absence. + resp.StreamSlices = append(resp.StreamSlices, &streampb.StreamSlice{ + StreamId: recorded.GetStreamId(), + FromOffset: recorded.GetFromOffset(), + ToOffset: recorded.GetToOffset(), + Messages: messages, + WorkflowTaskCompletedEventId: event.GetEventId(), + }) + } + } + + return checkReplayCoverage(consumer, addresses, reached) +} + +// replayBudget accumulates what one response has already committed to +// re-supplying, across every stream and every recorded range in it. +type replayBudget struct { + messages int + bytes int +} + +// replayMessagesFor re-reads one recorded range, or returns nothing for a range +// that recorded an empty observation. +func replayMessagesFor( + ctx context.Context, + consumer definition.WorkflowKey, + address streamOrigin, + recorded *streampb.StreamCursor, + budget *replayBudget, +) ([]*streampb.StreamMessage, error) { + if recorded.GetToOffset() <= recorded.GetFromOffset() { + return nil, nil + } + + w, err := readRecordedRange(ctx, consumer, address, + recorded.GetStreamId(), recorded.GetFromOffset(), recorded.GetToOffset()) + if err != nil { + return nil, replayReadError(consumer, recorded, err) + } + collected, _, err := stream.CollectMessages( + w.Blobs, w.Starts, + recorded.GetFromOffset(), recorded.GetToOffset(), + int(recorded.GetToOffset()-recorded.GetFromOffset()), nil) + if err != nil { + return nil, err + } + messages := stream.ToAPIMessages(collected) + + budget.messages += len(messages) + for _, m := range messages { + budget.bytes += proto.Size(m) + } + // Bounded because every prior task's range is re-read into one response, so + // a long-lived consumer's cold replay grows with its whole history. Refused + // rather than trimmed: a short re-supply is what replay cannot survive. + if budget.messages > maxReplayMessages || budget.bytes > maxReplayBytes { + return nil, serviceerror.NewFailedPreconditionf( + "replaying workflow %q needs more than %d messages or %d bytes of stream history to re-supply; "+ + "the consumed ranges cannot be re-delivered in one response", + consumer.GetWorkflowID(), maxReplayMessages, maxReplayBytes) + } + return messages, nil +} + +// checkReplayCoverage refuses a re-supply that stops short of what History says +// the consumer consumed. +// +// The events carried on the response are one page. A consumer whose recording +// events run past it would be handed only part of what it originally saw, and +// would then replay against fewer messages than the first run had. The cursor +// knows how far it has committed, so that is checked rather than assumed. +func checkReplayCoverage( + consumer definition.WorkflowKey, + addresses map[string]streamOrigin, + reached map[string]int64, +) error { + for streamID, address := range addresses { + got, ok := reached[streamID] + if !ok { + got = address.start + } + if got < address.committed { + return serviceerror.NewFailedPreconditionf( + "workflow %q consumed stream %q through offset %d but its history page only records through %d; "+ + "re-supplying the rest needs the events beyond this page", + consumer.GetWorkflowID(), streamID, address.committed, got) + } + } + return nil +} + +// replayReadError says why a range a completed task recorded can no longer be +// read. Truncation and deletion are the reachable causes, and neither is +// recoverable for this workflow: without the bytes it can never replay, and +// without replaying it can never start another task. The bare read error names +// offsets and no workflow, which is not enough to act on. +func replayReadError( + consumer definition.WorkflowKey, + recorded *streampb.StreamCursor, + cause error, +) error { + return serviceerror.NewFailedPreconditionf( + "workflow %q cannot replay: stream %q no longer holds offsets [%d,%d) that one of its completed tasks consumed (%v)", + consumer.GetWorkflowID(), recorded.GetStreamId(), + recorded.GetFromOffset(), recorded.GetToOffset(), cause) +} + +// eventsOfResponse reads the events the response is carrying, whichever of the +// three representations it happens to be using. +func eventsOfResponse( + resp *historyservice.RecordWorkflowTaskStartedResponseWithRawHistory, +) ([]*historypb.HistoryEvent, error) { + if resp.GetHistory() != nil { + return resp.GetHistory().GetEvents(), nil + } + + raw := resp.GetRawHistoryBytes() + if len(raw) == 0 { + raw = resp.GetRawHistory() //nolint:staticcheck // SA1019: still populated while the newer field rolls out. + } + if len(raw) == 0 { + return nil, nil + } + + serializer := serialization.NewSerializer() + var events []*historypb.HistoryEvent + for _, batch := range raw { + batchEvents, err := serializer.DeserializeEvents(&commonpb.DataBlob{ + EncodingType: enumspb.ENCODING_TYPE_PROTO3, + Data: batch, + }) + if err != nil { + return nil, err + } + events = append(events, batchEvents...) + } + return events, nil +} diff --git a/service/history/fx.go b/service/history/fx.go index dcf233185fe..0b004c3ab3e 100644 --- a/service/history/fx.go +++ b/service/history/fx.go @@ -11,6 +11,7 @@ import ( "go.temporal.io/server/chasm/lib/callback" chasmnexus "go.temporal.io/server/chasm/lib/nexusoperation" "go.temporal.io/server/chasm/lib/scheduler" + chasmstream "go.temporal.io/server/chasm/lib/stream/service" chasmtests "go.temporal.io/server/chasm/lib/tests" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/common" @@ -123,6 +124,7 @@ var Module = fx.Options( hsmnexusoperations.Module, fx.Invoke(hsmnexusworkflow.RegisterCommandHandlers), activity.HistoryModule, + chasmstream.HistoryModule, scheduler.Module, callback.Module, chasmnexus.Module, diff --git a/service/history/history_engine.go b/service/history/history_engine.go index aab1aa4b67b..c561e8efc2f 100644 --- a/service/history/history_engine.go +++ b/service/history/history_engine.go @@ -14,6 +14,7 @@ import ( replicationspb "go.temporal.io/server/api/replication/v1" workflowspb "go.temporal.io/server/api/workflow/v1" "go.temporal.io/server/chasm" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/client" "go.temporal.io/server/common" @@ -106,6 +107,7 @@ import ( type ( engineOptions struct { workflowResendScheduler workflowresend.Scheduler + streamClient streampb.StreamServiceClient } // EngineOption adds optional history engine dependencies without adding required parameters. @@ -147,6 +149,7 @@ type ( workflowConsistencyChecker api.WorkflowConsistencyChecker workflowResendScheduler workflowresend.Scheduler chasmEngine chasm.Engine + streamClient streampb.StreamServiceClient versionChecker headers.VersionChecker versionCache worker_versioning.VersionMembershipAndReactivationStatusCache workerDeploymentClient workerdeployment.Client @@ -170,6 +173,13 @@ func WithWorkflowResendScheduler(scheduler workflowresend.Scheduler) EngineOptio } } +// WithStreamClient routes standalone stream reads to their history shard owner. +func WithStreamClient(streamClient streampb.StreamServiceClient) EngineOption { + return func(options *engineOptions) { + options.streamClient = streamClient + } +} + func applyEngineOptions(options []EngineOption) engineOptions { var result engineOptions for _, option := range options { @@ -264,6 +274,7 @@ func NewEngineWithShardContext( outboundQueueCBPool: outboundQueueCBPool, testHooks: testHooks, chasmEngine: chasmEngine, + streamClient: engineOptions.streamClient, versionCache: versionCache, workerDeploymentClient: workerDeploymentClient, routingInfoCache: routingInfoCache, @@ -601,7 +612,7 @@ func (e *historyEngineImpl) RecordWorkflowTaskStarted( request *historyservice.RecordWorkflowTaskStartedRequest, ) (*historyservice.RecordWorkflowTaskStartedResponseWithRawHistory, error) { return recordworkflowtaskstarted.Invoke( - ctx, + recordworkflowtaskstarted.WithStreamClient(ctx, e.streamClient), request, e.shardContext, e.config, @@ -628,7 +639,7 @@ func (e *historyEngineImpl) RespondWorkflowTaskCompleted( e.matchingClient, e.versionCache, ) - return h.Invoke(ctx, req) + return h.Invoke(recordworkflowtaskstarted.WithStreamClient(ctx, e.streamClient), req) } // RespondWorkflowTaskFailed fails a workflow task diff --git a/service/history/history_engine_factory.go b/service/history/history_engine_factory.go index a2dd26c2324..fb75ae56779 100644 --- a/service/history/history_engine_factory.go +++ b/service/history/history_engine_factory.go @@ -3,6 +3,7 @@ package history import ( "go.opentelemetry.io/otel/trace" "go.temporal.io/server/chasm" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/client" "go.temporal.io/server/common/persistence" @@ -53,6 +54,7 @@ type ( PersistenceRateLimiter replication.PersistenceRateLimiter TestHooks testhooks.TestHooks ChasmEngine chasm.Engine + StreamClient streampb.StreamServiceClient VersionMembershipCache worker_versioning.VersionMembershipAndReactivationStatusCache WorkerDeploymentClient workerdeployment.Client RoutingInfoCache worker_versioning.RoutingInfoCache @@ -97,5 +99,6 @@ func (f *historyEngineFactory) CreateEngine( f.TestHooks, f.ChasmEngine, WithWorkflowResendScheduler(f.WorkflowResendScheduler), + WithStreamClient(f.StreamClient), ) } diff --git a/service/history/stream_routing_test.go b/service/history/stream_routing_test.go new file mode 100644 index 00000000000..397829bc3ce --- /dev/null +++ b/service/history/stream_routing_test.go @@ -0,0 +1,18 @@ +package history + +import ( + "testing" + + "github.com/stretchr/testify/require" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" +) + +type testStreamRoutingClient struct { + streampb.StreamServiceClient +} + +func TestWithStreamClient(t *testing.T) { + client := &testStreamRoutingClient{} + options := applyEngineOptions([]EngineOption{WithStreamClient(client)}) + require.Same(t, client, options.streamClient) +} diff --git a/service/matching/matching_engine.go b/service/matching/matching_engine.go index 049da260866..bb60c55ce42 100644 --- a/service/matching/matching_engine.go +++ b/service/matching/matching_engine.go @@ -3367,6 +3367,7 @@ func (e *matchingEngineImpl) convertPollWorkflowTaskQueueResponse( StartedTime: resp.StartedTime, Queries: resp.Queries, Messages: resp.Messages, + StreamSlices: resp.StreamSlices, History: history, NextPageToken: resp.NextPageToken, PollerScalingDecision: resp.PollerScalingDecision, diff --git a/service/matching/wire_compat_test.go b/service/matching/wire_compat_test.go new file mode 100644 index 00000000000..56fcfa06524 --- /dev/null +++ b/service/matching/wire_compat_test.go @@ -0,0 +1,173 @@ +package matching + +import ( + "fmt" + "slices" + "testing" + + "github.com/stretchr/testify/require" + "go.temporal.io/server/api/historyservice/v1" + "go.temporal.io/server/api/matchingservice/v1" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +// Each started-task response exists twice, once carrying History and once +// carrying raw bytes, so matching can forward a task without deserializing it. +// The two are only interchangeable while every field number means the same +// thing on both sides. That invariant is stated in a proto comment, which is +// easy to satisfy on one copy and forget on the other, and a mismatch stays +// invisible until history.sendRawHistoryBetweenInternalServices is enabled. +func TestStartedResponsePairsStayWireCompatible(t *testing.T) { + t.Run("history service", func(t *testing.T) { + // raw_history is a History on one side and repeated bytes on the other, + // which is the whole point of keeping two messages. + require.Empty(t, wireCompatDiff( + &historyservice.RecordWorkflowTaskStartedResponse{}, + &historyservice.RecordWorkflowTaskStartedResponseWithRawHistory{}, + 20, + )) + }) + + t.Run("matching service", func(t *testing.T) { + require.Empty(t, wireCompatDiff( + &matchingservice.PollWorkflowTaskQueueResponse{}, + &matchingservice.PollWorkflowTaskQueueResponseWithRawHistory{}, + 22, + )) + }) +} + +// Without a negative control the check above passes just as happily when it has +// stopped inspecting anything. Drop the exemption and the one field the pair is +// allowed to disagree on has to be reported. +func TestWireCompatDiffDetectsAMismatch(t *testing.T) { + diff := wireCompatDiff( + &historyservice.RecordWorkflowTaskStartedResponse{}, + &historyservice.RecordWorkflowTaskStartedResponseWithRawHistory{}, + ) + require.Len(t, diff, 1) + require.Contains(t, diff[0], "field 20") + require.Contains(t, diff[0], "raw_history") +} + +// wireCompatDiff reports every way the two messages would disagree on the wire, +// ignoring the field numbers named in typeMayDiffer. +func wireCompatDiff( + a proto.Message, + b proto.Message, + typeMayDiffer ...protoreflect.FieldNumber, +) []string { + exempt := make(map[protoreflect.FieldNumber]struct{}, len(typeMayDiffer)) + for _, number := range typeMayDiffer { + exempt[number] = struct{}{} + } + + aName := a.ProtoReflect().Descriptor().Name() + bName := b.ProtoReflect().Descriptor().Name() + aFields := fieldsByNumber(a) + bFields := fieldsByNumber(b) + + var problems []string + report := func(format string, args ...any) { + problems = append(problems, fmt.Sprintf(format, args...)) + } + + for _, number := range sortedFieldNumbers(aFields) { + aField := aFields[number] + bField, ok := bFields[number] + if !ok { + report("field %d (%s) is on %s but missing from %s", number, aField.Name(), aName, bName) + continue + } + if aField.Name() != bField.Name() { + report("field %d is %s on %s and %s on %s", number, aField.Name(), aName, bField.Name(), bName) + continue + } + if _, ok := exempt[number]; ok { + continue + } + + // A map field's value type is a synthesized entry message nested in its + // parent, so its full name always differs across the pair. Compare what + // actually goes on the wire instead. + if aField.IsMap() { + if !bField.IsMap() { + report("field %d (%s) is a map on %s only", number, aField.Name(), aName) + continue + } + if aField.MapKey().Kind() != bField.MapKey().Kind() { + report("field %d (%s) has a different map key type on each side", number, aField.Name()) + } + if problem := valueTypeDiff(aField.MapValue(), bField.MapValue(), number, aField.Name()); problem != "" { + report("%s", problem) + } + continue + } + + if aField.Cardinality() != bField.Cardinality() { + report("field %d (%s) is %s on %s and %s on %s", + number, aField.Name(), aField.Cardinality(), aName, bField.Cardinality(), bName) + continue + } + if problem := valueTypeDiff(aField, bField, number, aField.Name()); problem != "" { + report("%s", problem) + } + } + + for _, number := range sortedFieldNumbers(bFields) { + if _, ok := aFields[number]; !ok { + report("field %d (%s) is on %s but missing from %s", number, bFields[number].Name(), bName, aName) + } + } + + return problems +} + +func valueTypeDiff( + a protoreflect.FieldDescriptor, + b protoreflect.FieldDescriptor, + number protoreflect.FieldNumber, + name protoreflect.Name, +) string { + if a.Kind() != b.Kind() { + return fmt.Sprintf("field %d (%s) is %s on one side and %s on the other", number, name, a.Kind(), b.Kind()) + } + + switch a.Kind() { + case protoreflect.MessageKind, protoreflect.GroupKind: + if a.Message().FullName() != b.Message().FullName() { + return fmt.Sprintf("field %d (%s) refers to %s on one side and %s on the other", + number, name, a.Message().FullName(), b.Message().FullName()) + } + case protoreflect.EnumKind: + if a.Enum().FullName() != b.Enum().FullName() { + return fmt.Sprintf("field %d (%s) refers to %s on one side and %s on the other", + number, name, a.Enum().FullName(), b.Enum().FullName()) + } + default: + // Scalars are fully described by the kind equality checked above. + } + return "" +} + +func fieldsByNumber(m proto.Message) map[protoreflect.FieldNumber]protoreflect.FieldDescriptor { + fields := m.ProtoReflect().Descriptor().Fields() + byNumber := make(map[protoreflect.FieldNumber]protoreflect.FieldDescriptor, fields.Len()) + for i := range fields.Len() { + field := fields.Get(i) + byNumber[field.Number()] = field + } + return byNumber +} + +// Field order drives the order of reported problems, which keeps a failure +// message stable between runs. +func sortedFieldNumbers(fields map[protoreflect.FieldNumber]protoreflect.FieldDescriptor) []protoreflect.FieldNumber { + numbers := make([]protoreflect.FieldNumber, 0, len(fields)) + for number := range fields { + numbers = append(numbers, number) + } + slices.Sort(numbers) + return numbers +} From 27ae8a3cc7d5cebb02c082f4150c0e96f0a4f2cd Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 14 Sep 2026 15:59:56 -0700 Subject: [PATCH 07/47] Added the functional tests and the SDK validation server host. --- tests/api_fork_test.go | 57 ++ tests/sdk_server_host_test.go | 66 ++ tests/stream_consume_test.go | 977 ++++++++++++++++++++++++ tests/stream_publish_cost_test.go | 293 +++++++ tests/stream_retention_test.go | 133 ++++ tests/stream_routing_cross_host_test.go | 207 +++++ tests/stream_test.go | 641 ++++++++++++++++ tests/stream_workflow_test.go | 409 ++++++++++ tests/streaming_baseline_test.go | 458 +++++++++++ tests/streaming_native_test.go | 260 +++++++ tests/testcore/history_task_recorder.go | 24 + tests/testcore/onebox.go | 60 +- 12 files changed, 3572 insertions(+), 13 deletions(-) create mode 100644 tests/api_fork_test.go create mode 100644 tests/sdk_server_host_test.go create mode 100644 tests/stream_consume_test.go create mode 100644 tests/stream_publish_cost_test.go create mode 100644 tests/stream_retention_test.go create mode 100644 tests/stream_routing_cross_host_test.go create mode 100644 tests/stream_test.go create mode 100644 tests/stream_workflow_test.go create mode 100644 tests/streaming_baseline_test.go create mode 100644 tests/streaming_native_test.go diff --git a/tests/api_fork_test.go b/tests/api_fork_test.go new file mode 100644 index 00000000000..6428272bf1b --- /dev/null +++ b/tests/api_fork_test.go @@ -0,0 +1,57 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/api/workflowservice/v1" + "google.golang.org/protobuf/proto" +) + +// The api fork has to round-trip the new shapes over the wire, not merely +// compile. A field added without its descriptor would compile and silently +// drop on marshal. +func TestApiForkCarriesStreamShapes(t *testing.T) { + require.Equal(t, enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, enumspb.CommandType(19)) + + cmd := &commandpb.Command{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + StreamId: "s1", + Messages: []*streampb.StreamMessage{{Topic: "tokens"}}, + }, + }, + } + b, err := proto.Marshal(cmd) + require.NoError(t, err) + var back commandpb.Command + require.NoError(t, proto.Unmarshal(b, &back)) + require.Equal(t, "s1", back.GetAddStreamMessagesCommandAttributes().GetStreamId()) + require.Equal(t, "tokens", back.GetAddStreamMessagesCommandAttributes().GetMessages()[0].GetTopic()) + + resp := &workflowservice.PollWorkflowTaskQueueResponse{ + StreamSlices: []*streampb.StreamSlice{{StreamId: "s1", FromOffset: 4, ToOffset: 7}}, + } + rb, err := proto.Marshal(resp) + require.NoError(t, err) + var rback workflowservice.PollWorkflowTaskQueueResponse + require.NoError(t, proto.Unmarshal(rb, &rback)) + require.Equal(t, int64(7), rback.GetStreamSlices()[0].GetToOffset()) + + attrs := &historypb.WorkflowTaskCompletedEventAttributes{ + StreamCursors: []*streampb.StreamCursor{{StreamId: "s1", FromOffset: 4, ToOffset: 4}}, + } + ab, err := proto.Marshal(attrs) + require.NoError(t, err) + var aback historypb.WorkflowTaskCompletedEventAttributes + require.NoError(t, proto.Unmarshal(ab, &aback)) + // An empty range has to survive the round trip: it is the fact that a + // subscription observed nothing, which replay must reproduce. + require.Len(t, aback.GetStreamCursors(), 1) + require.Equal(t, aback.GetStreamCursors()[0].GetFromOffset(), aback.GetStreamCursors()[0].GetToOffset()) +} diff --git a/tests/sdk_server_host_test.go b/tests/sdk_server_host_test.go new file mode 100644 index 00000000000..1bfb2291c05 --- /dev/null +++ b/tests/sdk_server_host_test.go @@ -0,0 +1,66 @@ +package tests + +import ( + "context" + "encoding/json" + "errors" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + enumspb "go.temporal.io/api/enums/v1" + "go.temporal.io/server/common/namespace" + "go.temporal.io/server/tests/testcore" +) + +func TestSDKValidationServerHost(t *testing.T) { + readyPath := os.Getenv("AI198_SDK_SERVER_READY_FILE") + stopPath := os.Getenv("AI198_SDK_SERVER_STOP_FILE") + if readyPath == "" || stopPath == "" { + t.Skip("SDK validation host needs explicit ready and shutdown paths") + } + _, err := os.Stat(stopPath) + require.ErrorIs(t, err, os.ErrNotExist, "a stale shutdown file must not silently stop this host") + env := testcore.NewEnv(t, testcore.WithDedicatedCluster()) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + _, err = env.RegisterNamespace(ctx, namespace.Name("default"), 1, enumspb.ARCHIVAL_STATE_DISABLED, "", "") + require.NoError(t, err) + config := env.GetTestClusterConfig() + metadata := map[string]any{ + "status": "running", "pid": os.Getpid(), "target": env.FrontendGRPCAddress(), + "namespace": "default", "persistence": "SQLite", "history_hosts": config.HistoryConfig.NumHistoryHosts, + "history_shards": config.HistoryConfig.NumHistoryShards, "started_utc": time.Now().UTC().Format(time.RFC3339), + "shutdown_file": stopPath, "source_provenance": "../server-source-provenance.json", + "configuration": "tests/testcore dedicated test-cluster defaults; namespace default added for SDK clients", + } + writeMetadata := func() { + data, marshalErr := json.MarshalIndent(metadata, "", " ") + require.NoError(t, marshalErr) + require.NoError(t, os.WriteFile(readyPath, append(data, '\n'), 0o644)) + } + writeMetadata() + t.Logf("SDK validation server ready at %s in namespace default; shutdown file %s", env.FrontendGRPCAddress(), stopPath) + ticker := time.NewTicker(500 * time.Millisecond) + defer ticker.Stop() + deadline := time.NewTimer(2 * time.Hour) + defer deadline.Stop() + for { + select { + case <-ticker.C: + if _, statErr := os.Stat(stopPath); statErr == nil { + metadata["status"] = "stopping" + metadata["stopped_utc"] = time.Now().UTC().Format(time.RFC3339) + writeMetadata() + return + } else if !errors.Is(statErr, os.ErrNotExist) { + require.NoError(t, statErr) + } + case <-deadline.C: + metadata["status"] = "maximum-runtime-reached" + writeMetadata() + return + } + } +} diff --git a/tests/stream_consume_test.go b/tests/stream_consume_test.go new file mode 100644 index 00000000000..8848f973991 --- /dev/null +++ b/tests/stream_consume_test.go @@ -0,0 +1,977 @@ +package tests + +import ( + "context" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + "go.temporal.io/api/workflowservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common/testing/await" + "go.temporal.io/server/tests/testcore" + "google.golang.org/protobuf/types/known/durationpb" +) + +// Path C: a workflow consuming a stream. The slice rides the workflow task and +// only the offsets it covered are written to History, so consumption costs no +// event of its own no matter how many messages it carried. +func TestStreamWorkflowConsumesWithoutHistoryPayloads(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-consume-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + // What each task was handed, in the order the tasks ran. + var delivered [][]*streampb.StreamSlice + task := 0 + + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + task++ + if task > 1 { + return nil, nil + } + // First task publishes; nothing is subscribed yet, so it consumes + // nothing. + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("first-token")}, Topic: "tokens"}, + {Body: &commonpb.Payload{Data: []byte("second-token")}, Topic: "tokens"}, + {Body: &commonpb.Payload{Data: []byte("third-token")}, Topic: "tokens"}, + }, + }, + }, + }}, nil + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Empty(t, delivered[0], "nothing is subscribed on the first task") + + // Subscribe from the start of the stream. + sub, err := s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, + WorkflowId: id, + StreamName: chasmworkflow.DefaultStreamName, + StartOffset: 0, + }, + }) + require.NoError(t, err) + require.Equal(t, int64(0), sub.GetFrontendResponse().GetStartOffset()) + + // Resubscribing must not rewind. Ranges below the cursor are already in + // History, and moving back would replay them as if they were new. + sub2, err := s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: id, + StreamName: chasmworkflow.DefaultStreamName, StartOffset: 2, + }, + }) + require.NoError(t, err) + require.Equal(t, int64(0), sub2.GetFrontendResponse().GetStartOffset(), + "a second subscribe must report the cursor already registered") + + // Second task: the published range should arrive on the task itself. + signalWorkflow(t, s, id, we.GetRunId()) + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + slice := currentSlice(t, delivered[1]) + require.Equal(t, int64(0), slice.GetFromOffset()) + require.Equal(t, int64(3), slice.GetToOffset()) + require.Len(t, slice.GetMessages(), 3) + require.Equal(t, "first-token", string(slice.GetMessages()[0].GetBody().GetData())) + require.Equal(t, "third-token", string(slice.GetMessages()[2].GetBody().GetData())) + + // Third task: the cursor is caught up, so the range is empty. An empty + // range is still delivered and still recorded, because a task that saw + // nothing is a fact replay has to reproduce. + signalWorkflow(t, s, id, we.GetRunId()) + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + idleSlice := currentSlice(t, delivered[2]) + require.Equal(t, int64(3), idleSlice.GetFromOffset(), "a caught-up subscription is still attached") + require.Equal(t, int64(3), idleSlice.GetToOffset()) + require.Empty(t, idleSlice.GetMessages()) + + events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}) + recorded := recordedCursors(events) + + // One record per completed task from the subscription onward, including the + // one that consumed nothing. + require.Len(t, recorded, 2) + require.Equal(t, int64(0), recorded[0].GetFromOffset()) + require.Equal(t, int64(3), recorded[0].GetToOffset()) + require.Equal(t, int64(3), recorded[1].GetFromOffset()) + require.Equal(t, int64(3), recorded[1].GetToOffset(), + "the idle task must record the empty range rather than omit it") + + // The point of the design: offsets are in History, payloads are not. + for _, e := range events { + require.NotContains(t, e.String(), "first-token", + "a consumed payload must never reach History, found in %v", e.GetEventType()) + require.NotContains(t, e.String(), "third-token", + "a consumed payload must never reach History, found in %v", e.GetEventType()) + } +} + +func recordedCursors(events []*historypb.HistoryEvent) []*streampb.StreamCursor { + var out []*streampb.StreamCursor + for _, e := range events { + attrs := e.GetWorkflowTaskCompletedEventAttributes() + out = append(out, attrs.GetStreamCursors()...) + } + return out +} + +func signalWorkflow(t *testing.T, s *streamTestEnv, workflowID, runID string) { + t.Helper() + _, err := s.env.FrontendClient().SignalWorkflowExecution(s.ctx(), &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: s.ns, + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, + SignalName: "wake", + Identity: "tester", + RequestId: uuid.NewString(), + }) + require.NoError(t, err) +} + +// Publishing never wakes a workflow, but an active subscription does. Without +// this the rest of a stream arrives only when something unrelated happens to +// schedule a task. +func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-wake-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + publish := func(bodies ...string) []*commandpb.Command { + messages := make([]*streampb.StreamMessage, len(bodies)) + for i, b := range bodies { + messages[i] = &streampb.StreamMessage{Body: &commonpb.Payload{Data: []byte(b)}, Topic: "tokens"} + } + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{Messages: messages}, + }, + }} + } + + var delivered [][]*streampb.StreamSlice + task := 0 + + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + task++ + switch task { + case 1: + return publish("warmup"), nil + case 2: + // Published while subscribed and caught up, so completing this + // task leaves the cursor behind head. + return publish("alpha", "beta"), nil + default: + return nil, nil + } + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + // Subscribe at the tail, so nothing is owed yet. + sub, err := s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: id, + StreamName: chasmworkflow.DefaultStreamName, StartOffset: -1, + }, + }) + require.NoError(t, err) + require.Equal(t, int64(1), sub.GetFrontendResponse().GetStartOffset()) + + signalWorkflow(t, s, id, "") + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Equal(t, int64(1), currentSlice(t, delivered[1]).GetToOffset(), "nothing new at the tail yet") + + // No signal this time. The subscription owes two offsets, so completing the + // previous task has to have scheduled this one. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Len(t, delivered, 3) + woken := currentSlice(t, delivered[2]) + require.Equal(t, int64(1), woken.GetFromOffset()) + require.Equal(t, int64(3), woken.GetToOffset()) + require.Len(t, woken.GetMessages(), 2) + require.Equal(t, "alpha", string(woken.GetMessages()[0].GetBody().GetData())) + + // And it has to stop. A wake condition that stayed true would spin the + // workflow on empty tasks forever. + pollCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + idle, err := env.FrontendClient().PollWorkflowTaskQueue(pollCtx, &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + }) + if err == nil { + require.Empty(t, idle.GetTaskToken(), + "a caught-up subscription must not keep scheduling tasks") + } +} + +// Subscribing to a stream that already has data is the other case where the +// workflow is owed a task it did not ask for by any other means. +func TestSubscribingToABacklogSchedulesAWorkflowTask(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-backlog-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + var delivered [][]*streampb.StreamSlice + task := 0 + + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + task++ + if task > 1 { + return nil, nil + } + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("backlog-1")}, Topic: "tokens"}, + {Body: &commonpb.Payload{Data: []byte("backlog-2")}, Topic: "tokens"}, + }, + }, + }, + }}, nil + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: id, + StreamName: chasmworkflow.DefaultStreamName, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // No signal. Subscribing behind the head is itself a reason to run. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Len(t, delivered, 2) + backlog := currentSlice(t, delivered[1]) + require.Equal(t, int64(0), backlog.GetFromOffset()) + require.Equal(t, int64(2), backlog.GetToOffset()) +} + +// History records the offsets a task consumed and never the payloads, so a +// worker replaying that task has to be handed the bytes again. The response +// field alone cannot do it: it is built once per delivery while a cache miss +// replays every prior task, so each re-supplied range travels with the id of +// the event that recorded it. +func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { + // Dedicated, because forcing the replay path means evicting the cached + // workflow context, and CloseShard is not allowed on a shared cluster. + env := testcore.NewEnv(t, testcore.WithDedicatedCluster()) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-replay-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + var delivered [][]*streampb.StreamSlice + task := 0 + + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + task++ + if task > 1 { + return nil, nil + } + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("replay-me-1")}, Topic: "tokens"}, + {Body: &commonpb.Payload{Data: []byte("replay-me-2")}, Topic: "tokens"}, + }, + }, + }, + }}, nil + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: id, + StreamName: chasmworkflow.DefaultStreamName, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // Consume the range. This is the task replay will have to reproduce. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Len(t, currentSlice(t, delivered[1]).GetMessages(), 2) + + consumedAt := completedEventWithCursors(t, env.GetHistory(s.ns, + &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()})) + + // Drop the cached context so the next task is served with full history, + // which is the replay path. + env.CloseShard(env.NamespaceID().String(), id) + + signalWorkflow(t, s, id, we.GetRunId()) + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + replayed := sliceForEvent(delivered[2], consumedAt) + require.NotNil(t, replayed, "the replayed task must carry the range recorded at event %d", consumedAt) + require.Equal(t, int64(0), replayed.GetFromOffset()) + require.Equal(t, int64(2), replayed.GetToOffset()) + require.Len(t, replayed.GetMessages(), 2, "the payloads have to come back from the stream") + require.Equal(t, "replay-me-1", string(replayed.GetMessages()[0].GetBody().GetData())) + require.Equal(t, "replay-me-2", string(replayed.GetMessages()[1].GetBody().GetData())) +} + +// completedEventWithCursors returns the id of the first WorkflowTaskCompleted +// event that recorded a non-empty consumed range. +func completedEventWithCursors(t *testing.T, events []*historypb.HistoryEvent) int64 { + t.Helper() + for _, e := range events { + for _, c := range e.GetWorkflowTaskCompletedEventAttributes().GetStreamCursors() { + if c.GetToOffset() > c.GetFromOffset() { + return e.GetEventId() + } + } + } + t.Fatal("no completed event recorded a consumed range") + return 0 +} + +// currentSlice picks the slice for the task about to run. Slices carrying an +// event id belong to tasks being replayed, and a response can hold both. +func currentSlice(t *testing.T, slices []*streampb.StreamSlice) *streampb.StreamSlice { + t.Helper() + for _, s := range slices { + if s.GetWorkflowTaskCompletedEventId() == 0 { + return s + } + } + t.Fatal("no slice for the current task") + return nil +} + +func sliceForEvent(slices []*streampb.StreamSlice, eventID int64) *streampb.StreamSlice { + for _, s := range slices { + if s.GetWorkflowTaskCompletedEventId() == eventID { + return s + } + } + return nil +} + +// The shape the feature exists for: a producer writing off-shard and a workflow +// consuming it, with no relationship between them beyond the stream. +// +// A consuming workflow cannot read another execution's frontier while closing +// its own transaction, so the stream pushes it. The push dirties the consumer, +// whose transaction close then sees it is behind and schedules a workflow task. +// +// The log is read from the stream's own shard rather than the consumer's. +// History nodes are stored per shard, so reading an external stream from the +// consumer's shard finds nothing, and the workflow task then fails to start +// and is dropped rather than reporting anything useful. +func TestWorkflowConsumesAStreamItDoesNotOwn(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "external-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + + id := "stream-wf-external-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + var delivered [][]*streampb.StreamSlice + + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: id, + StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // The subscription has to reach the stream, or truncation could take a + // range the cursor still points at. + desc, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.NoError(t, err) + consumers := desc.GetFrontendResponse().GetState().GetConsumers() + require.Len(t, consumers, 1) + for _, c := range consumers { + require.True(t, c.GetExternal()) + require.True(t, c.GetActive()) + require.Equal(t, id, c.GetWorkflowId()) + } + + // An off-shard producer, unrelated to the workflow. + _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, + Messages: []*streamlib.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("from-outside-1")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("from-outside-2")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + }, + }, + }) + require.NoError(t, err) + + // The append alone has to produce a workflow task, with no signal and no + // other traffic against the workflow. + await.RequireTruef(t, func() bool { + events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}) + scheduled := 0 + for _, e := range events { + if e.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_TASK_SCHEDULED { + scheduled++ + } + } + return scheduled >= 2 + }, 20*time.Second, 200*time.Millisecond, "the append must schedule a workflow task on its own") + + // And it must reach a worker. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + got := currentSlice(t, delivered[1]) + require.Equal(t, streamID, got.GetStreamId()) + require.Equal(t, int64(0), got.GetFromOffset()) + require.Equal(t, int64(2), got.GetToOffset()) + require.Len(t, got.GetMessages(), 2) + require.Equal(t, "from-outside-1", string(got.GetMessages()[0].GetBody().GetData())) +} + +// A cursor is workflow state, so a continue-as-new has to carry it. Without +// that the subscription ends silently: the stream keeps its consumer pin and +// keeps pushing to the workflow id, and the successor run has nowhere to put +// what arrives. +func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "carried-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + + id := "stream-wf-can-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + var delivered [][]*streampb.StreamSlice + task := 0 + + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + task++ + if task == 2 { + // Continue as new, carrying nothing of its own. + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_CONTINUE_AS_NEW_WORKFLOW_EXECUTION, + Attributes: &commandpb.Command_ContinueAsNewWorkflowExecutionCommandAttributes{ + ContinueAsNewWorkflowExecutionCommandAttributes: &commandpb.ContinueAsNewWorkflowExecutionCommandAttributes{ + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + }, + }, + }}, nil + } + return nil, nil + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: id, StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // First append, consumed by the original run. + _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, + Messages: []*streamlib.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("before-can")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + }, + }, + }) + require.NoError(t, err) + + // Task 2 receives it and continues as new. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + consumed := currentSlice(t, delivered[1]) + require.Equal(t, int64(1), consumed.GetToOffset()) + require.Len(t, consumed.GetMessages(), 1) + + // Drain the successor's first task, which carries nothing new. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + // Append again. Only a carried subscription can deliver this, and it must + // resume at offset 1 rather than replaying from the start. + _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, + Messages: []*streamlib.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("after-can")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + }, + }, + }) + require.NoError(t, err) + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + afterCAN := currentSlice(t, delivered[len(delivered)-1]) + require.Equal(t, int64(1), afterCAN.GetFromOffset(), "the successor resumes where its predecessor stopped") + require.Equal(t, int64(2), afterCAN.GetToOffset()) + require.Len(t, afterCAN.GetMessages(), 1) + require.Equal(t, "after-can", string(afterCAN.GetMessages()[0].GetBody().GetData())) +} + +// A workflow subscribing itself, rather than being subscribed out of band. +// +// The command carries only the stream id and a start offset. Everything else +// the cursor needs is looked up by the server: a workflow cannot read another +// execution's collection id or bucket size without doing I/O, and a value it +// carried would be a reading rather than a fact, so it could differ on replay. +func TestWorkflowSubscribesToAStreamItself(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "self-sub-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + + id := "stream-wf-selfsub-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + var delivered [][]*streampb.StreamSlice + task := 0 + + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + task++ + if task > 1 { + return nil, nil + } + // The workflow subscribes itself on its first task. + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM, + Attributes: &commandpb.Command_SubscribeStreamCommandAttributes{ + SubscribeStreamCommandAttributes: &commandpb.SubscribeStreamCommandAttributes{ + StreamId: streamID, + StartOffset: 0, + }, + }, + }}, nil + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Empty(t, delivered[0], "nothing is subscribed until this task completes") + + // The subscription has to have reached the stream, or truncation could take + // a range the cursor still points at. + desc, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.NoError(t, err) + require.Len(t, desc.GetFrontendResponse().GetState().GetConsumers(), 1, + "the command must have registered a consumer on the stream") + + // The subscription is recorded once, with the offset the server resolved. + // Without it nothing in History explains why this workflow starts receiving + // stream data, and a command that writes no event desynchronises the + // command-to-event matching every SDK's replay depends on. + subscribed := subscribedEvents(env.GetHistory(s.ns, + &commonpb.WorkflowExecution{WorkflowId: id})) + require.Len(t, subscribed, 1) + require.Equal(t, streamID, subscribed[0].GetStreamId()) + require.Equal(t, int64(0), subscribed[0].GetStartOffset()) + + // An off-shard producer, with no signal to the workflow. + _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, + Messages: []*streamlib.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("self-1")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("self-2")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + }, + }, + }) + require.NoError(t, err) + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + got := currentSlice(t, delivered[1]) + require.Equal(t, streamID, got.GetStreamId()) + require.Equal(t, int64(0), got.GetFromOffset()) + require.Equal(t, int64(2), got.GetToOffset()) + require.Len(t, got.GetMessages(), 2) + require.Equal(t, "self-1", string(got.GetMessages()[0].GetBody().GetData())) +} + +func subscribedEvents(events []*historypb.HistoryEvent) []*historypb.WorkflowStreamSubscribedEventAttributes { + var out []*historypb.WorkflowStreamSubscribedEventAttributes + for _, e := range events { + if attrs := e.GetWorkflowStreamSubscribedEventAttributes(); attrs != nil { + out = append(out, attrs) + } + } + return out +} + +// A second subscribe to a stream the workflow already holds registers nothing, +// but it still has to write an event. Every SDK matches issued commands against +// command-generated events by position, so a command producing no event leaves +// the machine that issued it unmatched and puts every later command out of step. +func TestResubscribingStillWritesItsEvent(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "resub-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + + id := "stream-wf-resub-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + subscribe := []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM, + Attributes: &commandpb.Command_SubscribeStreamCommandAttributes{ + SubscribeStreamCommandAttributes: &commandpb.SubscribeStreamCommandAttributes{ + StreamId: streamID, StartOffset: 0, + }, + }, + }} + + task := 0 + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + task++ + if task > 2 { + return nil, nil + } + return subscribe, nil + }, + Logger: env.Logger, + T: t, + } + + // Two tasks, each issuing the same subscribe. The second registers nothing. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + _, err = env.FrontendClient().SignalWorkflowExecution(s.ctx(), &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: s.ns, + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: id}, + SignalName: "wake", + Identity: "tester", + RequestId: uuid.NewString(), + }) + require.NoError(t, err) + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}) + var subscribed []*historypb.WorkflowStreamSubscribedEventAttributes + for _, e := range events { + if a := e.GetWorkflowStreamSubscribedEventAttributes(); a != nil { + subscribed = append(subscribed, a) + } + } + require.Len(t, subscribed, 2, "each subscribe command owes an event, even a repeat") + require.Equal(t, streamID, subscribed[1].GetStreamId()) + // The repeat reports where the cursor actually is, not the offset it asked + // for, so a replaying worker reads a fact rather than a request. + require.Equal(t, int64(0), subscribed[1].GetStartOffset()) +} + +// A subscribe followed by another command in the same Workflow Task. Every SDK +// matches issued commands against the events they produced by position, so the +// subscription's event has to sit where its command did. +// +// It used to be written in the flush, which runs after every command, so it +// landed behind the events of commands issued later and the first replay of +// any workflow that subscribed before doing anything else would fail. +func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "order-src-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + + id := "stream-order-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-order"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + //nolint:staticcheck // SA1019: deprecated poller is the only one that can emit the command. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + // Subscribe first, then publish. The publish event must come second. + return []*commandpb.Command{ + { + CommandType: enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM, + Attributes: &commandpb.Command_SubscribeStreamCommandAttributes{ + SubscribeStreamCommandAttributes: &commandpb.SubscribeStreamCommandAttributes{ + StreamId: streamID, StartOffset: 0, + }, + }, + }, + { + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("after subscribe")}}, + }, + }, + }, + }, + }, nil + }, + Logger: env.Logger, + T: t, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + var order []enumspb.EventType + for _, e := range env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id}) { + switch e.GetEventType() { + case enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: + order = append(order, e.GetEventType()) + default: + // Every other event is noise for this assertion. + } + } + require.Equal(t, []enumspb.EventType{ + enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED, + }, order, "the events must be in the order their commands were issued") +} diff --git a/tests/stream_publish_cost_test.go b/tests/stream_publish_cost_test.go new file mode 100644 index 00000000000..e76f1ea5538 --- /dev/null +++ b/tests/stream_publish_cost_test.go @@ -0,0 +1,293 @@ +package tests + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + streampb "go.temporal.io/api/stream/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/server/tests/testcore" + "google.golang.org/protobuf/types/known/durationpb" +) + +// What a history event per publish would cost. +// +// Publishing from workflow code is the one stream command with no history +// event, which is why no SDK can reach it: sdk-core matches commands to events +// positionally, so a command that produces none desynchronises replay. Giving +// it an event fixes that, and the objection is that unlike subscribing, which +// happens once, publishing happens per batch. This measures the per-batch +// price so the trade is decided on a number. +// +// The method is a marginal one. Each arm runs a workflow whose single workflow +// task carries N publish commands and then completes, so history holds the +// fixed opening and closing events plus exactly N publishes. Differencing +// against the N=0 arm cancels the fixed part and leaves the cost of one +// publish. Message size is varied against a fixed batch count to show whether +// payload bytes reach History at all. + +type publishCostArm struct { + name string + batches int + messagesPerBatch int + messageSize int + // Sends the same bytes as Signals instead, which is what a workflow + // streaming today has to do. The comparison is the point of the exercise. + viaSignal bool +} + +type publishCostResult struct { + arm publishCostArm + historyEvents int64 + historyBytes int64 +} + +func (r publishCostResult) messages() int { + return r.arm.batches * r.arm.messagesPerBatch +} + +// The published limits a per-batch event has to be judged against. +const ( + historyCountLimitError = 50 * 1024 + historySizeLimitError = 50 * 1024 * 1024 +) + +func TestStreamPublishHistoryCost(t *testing.T) { + if testing.Short() { + t.Skip("measurement, not a correctness check") + } + + arms := []publishCostArm{ + // The control. Everything is differenced against this. + {name: "control", batches: 0}, + + // Batch count at a fixed message shape: is the cost linear, and what + // is the slope. + {name: "stream-b1", batches: 1, messagesPerBatch: 1, messageSize: 20}, + {name: "stream-b10", batches: 10, messagesPerBatch: 1, messageSize: 20}, + {name: "stream-b100", batches: 100, messagesPerBatch: 1, messageSize: 20}, + {name: "stream-b500", batches: 500, messagesPerBatch: 1, messageSize: 20}, + + // Same batch count, more messages inside each: the per-batch event + // should not notice. + {name: "stream-b100-m10", batches: 100, messagesPerBatch: 10, messageSize: 20}, + + // Same batch count, bigger messages: this is the claim that payloads + // never enter History, stated as a measurement. + {name: "stream-b100-s200", batches: 100, messagesPerBatch: 1, messageSize: 200}, + {name: "stream-b100-s2000", batches: 100, messagesPerBatch: 1, messageSize: 2000}, + + // What the same traffic costs through Signals today. + {name: "signal-b100-s20", batches: 100, messagesPerBatch: 1, messageSize: 20, viaSignal: true}, + {name: "signal-b100-s2000", batches: 100, messagesPerBatch: 1, messageSize: 2000, viaSignal: true}, + } + + results := make([]publishCostResult, 0, len(arms)) + byName := map[string]publishCostResult{} + for _, arm := range arms { + r := runPublishCostArm(t, arm) + results = append(results, r) + byName[arm.name] = r + } + reportPublishCost(t, results) + assertPublishCost(t, byName) +} + +// The report is the deliverable, but the properties it shows are the ones the +// design rests on, so they are asserted rather than left to be eyeballed. +func assertPublishCost(t *testing.T, byName map[string]publishCostResult) { + control := byName["control"] + marginalBytes := func(name string) int64 { + return byName[name].historyBytes - control.historyBytes + } + marginalEvents := func(name string) int64 { + return byName[name].historyEvents - control.historyEvents + } + + // One event per batch, not per message. b100-m10 publishes ten times the + // messages of b100 through the same hundred commands. + require.Equal(t, int64(100), marginalEvents("stream-b100")) + require.Equal(t, int64(100), marginalEvents("stream-b100-m10")) + require.Equal(t, int64(500), marginalEvents("stream-b500")) + + // Ten times the payload must not move History, which is the claim that + // bodies never enter it. Judged against what a leak would cost rather than + // against zero: event sizes wobble by a byte or two on varint widths, while + // 1800 extra bytes per message across a hundred batches would be six + // figures. + sizeDrift := marginalBytes("stream-b100-s2000") - marginalBytes("stream-b100-s200") + require.Less(t, max(sizeDrift, -sizeDrift), int64(2000), + "10x the payload moved the history cost, so payload is reaching History") + + // Ten times the messages through the same batches, within a byte or two of + // noise from varint widths. + require.InDelta(t, marginalBytes("stream-b100"), marginalBytes("stream-b100-m10"), 200, + "cost tracked message count rather than batch count") + + // The comparison that justifies the feature. Same traffic, and Signals are + // the only way to do this today. + require.Less(t, marginalBytes("stream-b100-s2000"), marginalBytes("signal-b100-s2000")/20, + "a stream publish should be far cheaper than the Signal it replaces") +} + +func runPublishCostArm(t *testing.T, arm publishCostArm) publishCostResult { + t.Helper() + env := testcore.NewEnv(t, testcore.WithDisableTestloggerFailure()) + + // Fixed-width regardless of the arm, because the workflow id and the task + // queue name derived from it are both carried in the opening events. Naming + // the arms in there would put ten bytes of arm-name difference into the very + // figure being differenced. + id := "publish-cost-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + ctx := testcore.NewContext() + + _, err := env.FrontendClient().StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: env.Namespace().String(), + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "publish-cost"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(60 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + body := []byte(strings.Repeat("x", arm.messageSize)) + + // Sent before the first workflow task is polled, so they land in history + // ahead of it and the single task still closes the workflow. + if arm.viaSignal { + for range arm.batches { + payloads := make([]*commonpb.Payload, 0, arm.messagesPerBatch) + for range arm.messagesPerBatch { + payloads = append(payloads, &commonpb.Payload{Data: body}) + } + _, err := env.FrontendClient().SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: id}, + SignalName: "stream-item", + Input: &commonpb.Payloads{Payloads: payloads}, + Identity: "tester", + RequestId: uuid.NewString(), + }) + require.NoError(t, err) + } + } + + //nolint:staticcheck // SA1019: only the deprecated poller emits raw commands. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: env.Namespace().String(), + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + commands := make([]*commandpb.Command, 0, arm.batches+1) + if !arm.viaSignal { + for range arm.batches { + messages := make([]*streampb.StreamMessage, 0, arm.messagesPerBatch) + for range arm.messagesPerBatch { + messages = append(messages, &streampb.StreamMessage{ + Body: &commonpb.Payload{Data: body}, + Topic: "progress", + }) + } + commands = append(commands, &commandpb.Command{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: messages, + }, + }, + }) + } + } + return append(commands, &commandpb.Command{ + CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, + Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ + CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, + }, + }), nil + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + desc, err := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{WorkflowId: id}, + }) + require.NoError(t, err) + + return publishCostResult{ + arm: arm, + historyEvents: desc.GetWorkflowExecutionInfo().GetHistoryLength(), + historyBytes: desc.GetWorkflowExecutionInfo().GetHistorySizeBytes(), + } +} + +func reportPublishCost(t *testing.T, results []publishCostResult) { + var control publishCostResult + for _, r := range results { + if r.arm.name == "control" { + control = r + } + } + + t.Log("Cost of one history event per publish, measured by differencing against an empty run.") + t.Logf("Control: %d events, %d bytes.", control.historyEvents, control.historyBytes) + t.Log("") + t.Log("| arm | batches | msgs | msg size | events | bytes | events/batch | bytes/batch | bytes/msg |") + t.Log("|---|---|---|---|---|---|---|---|---|") + for _, r := range results { + if r.arm.batches == 0 { + continue + } + marginalEvents := r.historyEvents - control.historyEvents + marginalBytes := r.historyBytes - control.historyBytes + perMsg := "n/a" + if r.messages() > 0 { + perMsg = fmt.Sprintf("%.1f", float64(marginalBytes)/float64(r.messages())) + } + t.Logf("| %s | %d | %d | %d | %d | %d | %.2f | %.1f | %s |", + r.arm.name, r.arm.batches, r.messages(), r.arm.messageSize, + r.historyEvents, r.historyBytes, + float64(marginalEvents)/float64(r.arm.batches), + float64(marginalBytes)/float64(r.arm.batches), + perMsg) + } + t.Log("") + + // The ceilings the number has to be read against. An event per batch makes + // the event-count limit the binding one long before the size limit. + for _, r := range results { + if r.arm.batches == 0 || r.arm.viaSignal { + continue + } + marginalBytes := r.historyBytes - control.historyBytes + marginalEvents := r.historyEvents - control.historyEvents + if marginalEvents == 0 { + continue + } + bytesPerEvent := float64(marginalBytes) / float64(marginalEvents) + t.Logf("%s: %.0f bytes/event implies %d batches before the %d-event limit, "+ + "%d before the %dMB size limit", + r.arm.name, bytesPerEvent, + historyCountLimitError, + historyCountLimitError, + int(float64(historySizeLimitError)/bytesPerEvent), + historySizeLimitError/(1024*1024)) + } +} diff --git a/tests/stream_retention_test.go b/tests/stream_retention_test.go new file mode 100644 index 00000000000..d9d91ae604d --- /dev/null +++ b/tests/stream_retention_test.go @@ -0,0 +1,133 @@ +package tests + +import ( + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + "go.temporal.io/api/workflowservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/tests/testcore" + "google.golang.org/protobuf/types/known/durationpb" +) + +// A range a workflow consumed is part of that workflow's recovery: the offsets +// are in its History and a replay is asked to reproduce them from the stream. +// Deleting those messages succeeds immediately and fails much later, during a +// replay nobody is watching, so the stream refuses instead. +func TestTruncationRefusesToDropWhatAConsumerNeedsToReplay(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "retained-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + _, err := s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, + Messages: []*streamlib.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("one")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("two")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("three")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + }, + }, + }) + require.NoError(t, err) + + id := "stream-retention-wf-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + _, err = env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(_ *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: id, StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // Delivered and recorded, which is what makes these three messages part of + // the workflow's recovery rather than spare capacity. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.TruncateStream(s.ctx(), &streamlib.TruncateStreamRequest{ + FrontendRequest: &streamlib.TruncateStreamInput{ + Namespace: s.ns, StreamId: streamID, NewBaseOffset: 2, + }, + }) + require.ErrorContains(t, err, "still depends on offset 0") + + desc, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.NoError(t, err) + state := desc.GetFrontendResponse().GetState() + require.Equal(t, int64(0), state.GetBaseOffset(), "the messages must still be there") + for _, consumer := range state.GetConsumers() { + require.Equal(t, int64(0), consumer.GetReplayFloor(), + "the floor is where the subscription started, not where it has read to") + } +} + +// The refusal is about a consumer's recovery, so a stream nobody is consuming +// truncates exactly as before. Without this, the fix would be a storage leak +// wearing a correctness argument. +func TestTruncationStillWorksWithNoConsumer(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "unwatched-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + _, err := s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, + Messages: []*streamlib.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("one")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("two")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + }, + }, + }) + require.NoError(t, err) + + _, err = s.client.TruncateStream(s.ctx(), &streamlib.TruncateStreamRequest{ + FrontendRequest: &streamlib.TruncateStreamInput{ + Namespace: s.ns, StreamId: streamID, NewBaseOffset: 2, + }, + }) + require.NoError(t, err) + + desc, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.NoError(t, err) + require.Equal(t, int64(2), desc.GetFrontendResponse().GetState().GetBaseOffset()) +} diff --git a/tests/stream_routing_cross_host_test.go b/tests/stream_routing_cross_host_test.go new file mode 100644 index 00000000000..62b50a12090 --- /dev/null +++ b/tests/stream_routing_cross_host_test.go @@ -0,0 +1,207 @@ +package tests + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/server/api/adminservice/v1" + "go.temporal.io/server/api/historyservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common" + "go.temporal.io/server/common/testing/await" + "go.temporal.io/server/common/testing/testlogger" + "go.temporal.io/server/tests/testcore" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" +) + +func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { + output := os.Getenv("AI198_ROUTING_RESULT_DIR") + if output == "" { + output = t.TempDir() + } + require.NoError(t, os.MkdirAll(output, 0o755)) + writeProto := func(name string, message proto.Message) { + encoded, err := protojson.MarshalOptions{Indent: " "}.Marshal(message) + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(output, name+".json"), encoded, 0o644)) + binary, err := proto.Marshal(message) + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(output, name+".pb"), binary, 0o644)) + } + logger := testlogger.NewTestLogger(t, testlogger.FailOnExpectedErrorOnly) + config := &testcore.TestClusterConfig{ + HistoryConfig: testcore.HistoryConfig{NumHistoryHosts: 2, NumHistoryShards: 32}, + Persistence: testcore.GetPersistenceTestDefaults(), + WorkerConfig: testcore.WorkerConfig{DisableWorker: true}, + } + cluster, err := testcore.NewTestClusterFactory().NewCluster(t, config, logger) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, cluster.TearDownCluster()) }) + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + frontend := cluster.FrontendClient() + ns := "validation-cross-host-" + uuid.NewString() + _, err = frontend.RegisterNamespace(ctx, &workflowservice.RegisterNamespaceRequest{ + Namespace: ns, WorkflowExecutionRetentionPeriod: durationpb.New(24 * time.Hour), + }) + require.NoError(t, err) + namespace, err := frontend.DescribeNamespace(ctx, &workflowservice.DescribeNamespaceRequest{Namespace: ns}) + require.NoError(t, err) + nsID := namespace.GetNamespaceInfo().GetId() + hosts := map[string][]int32{} + for shard := int32(1); shard <= config.HistoryConfig.NumHistoryShards; shard++ { + host, describeErr := cluster.AdminClient().DescribeHistoryHost(ctx, &adminservice.DescribeHistoryHostRequest{ShardId: shard}) + require.NoError(t, describeErr) + require.NotEmpty(t, host.GetAddress()) + hosts[host.GetAddress()] = append(hosts[host.GetAddress()], shard) + } + require.Len(t, hosts, 2, "must verify two actual owners, not merely configure two services") + topology, err := json.MarshalIndent(hosts, "", " ") + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(output, "topology.json"), topology, 0o644)) + var consumerHost, remoteHost string + var consumerShard, localSourceShard, remoteSourceShard int32 + for host, shards := range hosts { + if consumerHost == "" && len(shards) >= 2 { + consumerHost, consumerShard, localSourceShard = host, shards[0], shards[1] + } + } + require.NotEmpty(t, consumerHost) + for host, shards := range hosts { + if host != consumerHost { + remoteHost, remoteSourceShard = host, shards[0] + } + } + t.Logf("Confirmed history topology: %v; consumer shard %d on %s, local source shard %d on %s, remote source shard %d on %s", + hosts, consumerShard, consumerHost, localSourceShard, consumerHost, remoteSourceShard, remoteHost) + findID := func(prefix string, target int32) string { + for i := 0; i < 10000; i++ { + id := fmt.Sprintf("%s-%d", prefix, i) + if common.WorkflowIDToHistoryShard(nsID, id, config.HistoryConfig.NumHistoryShards) == target { + return id + } + } + t.Fatal("could not select ID on target shard") + return "" + } + conn, err := grpc.NewClient(cluster.Host().FrontendGRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, conn.Close()) }) + streams := streamlib.NewStreamServiceClient(conn) + ownerConn, err := grpc.NewClient(consumerHost, grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, ownerConn.Close()) }) + owner := historyservice.NewHistoryServiceClient(ownerConn) + getHistory := func(execution *commonpb.WorkflowExecution) []*historypb.HistoryEvent { + history, getErr := frontend.GetWorkflowExecutionHistory(ctx, &workflowservice.GetWorkflowExecutionHistoryRequest{ + Namespace: ns, Execution: execution, + }) + require.NoError(t, getErr) + require.Empty(t, history.GetNextPageToken()) + return history.GetHistory().GetEvents() + } + for _, test := range []struct { + name string + sourceShard int32 + crossHost bool + }{{"same-host", localSourceShard, false}, {"cross-host", remoteSourceShard, true}} { + t.Run(test.name, func(t *testing.T) { + id := findID(test.name+"-consumer", consumerShard) + source := findID(test.name+"-source", test.sourceShard) + _, err := streams.CreateStream(ctx, &streamlib.CreateStreamRequest{FrontendRequest: &streamlib.CreateStreamInput{Namespace: ns, StreamId: source}}) + require.NoError(t, err) + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + started, err := frontend.StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), Namespace: ns, WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "cross-host-consumer"}, TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(2 * time.Minute), WorkflowTaskTimeout: durationpb.New(10 * time.Second), + }) + require.NoError(t, err) + execution := &commonpb.WorkflowExecution{WorkflowId: id, RunId: started.GetRunId()} + var delivered [][]*streampb.StreamSlice + //nolint:staticcheck // SA1019: this test isolates the server response from SDK replay. + poller := &testcore.TaskPoller{Client: frontend, Namespace: ns, TaskQueue: tq, Identity: "cross-host-validation", Logger: logger, T: t, + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + return nil, nil + }, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + for _, target := range []struct{ id, host string }{{id, consumerHost}, {source, map[bool]string{false: consumerHost, true: remoteHost}[test.crossHost]}} { + desc, describeErr := cluster.AdminClient().DescribeHistoryHost(ctx, &adminservice.DescribeHistoryHostRequest{ + Namespace: ns, WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: target.id}, + }) + require.NoError(t, describeErr) + require.Equal(t, target.host, desc.GetAddress()) + t.Logf("Resolved %s to %s, loaded shards %v", target.id, desc.GetAddress(), desc.GetShardIds()) + } + _, err = streams.SubscribeWorkflow(ctx, &streamlib.SubscribeWorkflowRequest{FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: ns, WorkflowId: id, StreamId: source, + }}) + require.NoError(t, err) + _, err = streams.AddMessages(ctx, &streamlib.AddMessagesRequest{FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: ns, StreamId: source, Messages: streamMsgs("tokens", "retained-input"), + }}) + require.NoError(t, err) + latestScheduled := func() int64 { + var scheduled int64 + for _, event := range getHistory(execution) { + if event.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_TASK_SCHEDULED { + scheduled = event.GetEventId() + } + } + return scheduled + } + await.RequireTrue(t, func() bool { return latestScheduled() > 2 }, 10*time.Second, 20*time.Millisecond) + { + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Equal(t, "retained-input", string(currentSlice(t, delivered[1]).GetMessages()[0].GetBody().GetData())) + writeProto(test.name+"-live-slice", currentSlice(t, delivered[1])) + writeProto(test.name+"-committed-history", &historypb.History{Events: getHistory(execution)}) + consumedAt := completedEventWithCursors(t, getHistory(execution)) + _, err = owner.CloseShard(ctx, &historyservice.CloseShardRequest{ShardId: consumerShard}) + require.NoError(t, err) + _, err = frontend.SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: ns, WorkflowExecution: execution, SignalName: "cold-replay-boundary", RequestId: uuid.NewString(), + }) + require.NoError(t, err) + response, err := owner.RecordWorkflowTaskStarted(ctx, &historyservice.RecordWorkflowTaskStartedRequest{ + NamespaceId: nsID, WorkflowExecution: execution, ScheduledEventId: latestScheduled(), RequestId: uuid.NewString(), + PollRequest: &workflowservice.PollWorkflowTaskQueueRequest{Namespace: ns, TaskQueue: tq, Identity: "cross-host-validation"}, + }) + require.NoError(t, err) + writeProto(test.name+"-cold-response", response) + found := false + for _, slice := range response.GetStreamSlices() { + if slice.GetWorkflowTaskCompletedEventId() == consumedAt { + require.Equal(t, "retained-input", string(slice.GetMessages()[0].GetBody().GetData())) + found = true + } + } + require.True(t, found, "cold replay must re-supply the committed input range") + t.Logf("%s delivered and committed the input, then re-supplied it after consumer CloseShard at completion %d; consumer=%s source=%s", test.name, consumedAt, consumerHost, map[bool]string{false: consumerHost, true: remoteHost}[test.crossHost]) + return + } + }) + } +} diff --git a/tests/stream_test.go b/tests/stream_test.go new file mode 100644 index 00000000000..cb261d4a7ec --- /dev/null +++ b/tests/stream_test.go @@ -0,0 +1,641 @@ +package tests + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + chasmstream "go.temporal.io/server/chasm/lib/stream" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/testing/await" + "go.temporal.io/server/tests/testcore" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// End-to-end coverage of the native stream path: append through the frontend, +// read back by offset, and the lifecycle transitions around it. This is the +// path the benchmark will eventually compare against the Signal-and-Update +// baseline in streaming_baseline_test.go. + +const streamMaxBatch = chasmstream.MaxMessagesPerBatch + +type streamTestEnv struct { + env *testcore.TestEnv + client streampb.StreamServiceClient + ns string + cleanup []func() +} + +func newStreamTestEnv(t *testing.T) *streamTestEnv { + return newStreamTestEnvFrom(t, testcore.NewEnv(t)) +} + +// newStreamTestEnvFrom lets a test that needs its own env, for example one +// driving the raw task poller, still reach the stream API. +func newStreamTestEnvFrom(t *testing.T, env *testcore.TestEnv) *streamTestEnv { + + conn, err := grpc.NewClient(env.FrontendGRPCAddress(), + grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + t.Cleanup(func() { _ = conn.Close() }) + + env2 := &streamTestEnv{env: env, client: streampb.NewStreamServiceClient(conn), ns: env.Namespace().String()} + t.Cleanup(func() { + for _, c := range env2.cleanup { + c() + } + }) + + return env2 +} + +func (s *streamTestEnv) ctx() context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + s.cleanup = append(s.cleanup, cancel) + return ctx +} + +func (s *streamTestEnv) create(ctx context.Context, t *testing.T, streamID string) { + t.Helper() + _, err := s.client.CreateStream(ctx, &streampb.CreateStreamRequest{ + FrontendRequest: &streampb.CreateStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.NoError(t, err) +} + +func (s *streamTestEnv) add( + ctx context.Context, t *testing.T, streamID string, in *streampb.AddMessagesInput, +) (*streampb.AddMessagesOutput, error) { + t.Helper() + in.Namespace = s.ns + in.StreamId = streamID + resp, err := s.client.AddMessages(ctx, &streampb.AddMessagesRequest{FrontendRequest: in}) + if err != nil { + return nil, err + } + return resp.GetFrontendResponse(), nil +} + +func (s *streamTestEnv) poll( + ctx context.Context, t *testing.T, streamID string, from int64, topics ...string, +) *streampb.PollMessagesOutput { + t.Helper() + resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{ + Namespace: s.ns, StreamId: streamID, FromOffset: from, Topics: topics, + }, + }) + require.NoError(t, err) + return resp.GetFrontendResponse() +} + +func streamMsgs(topic string, bodies ...string) []*streampb.StreamMessage { + out := make([]*streampb.StreamMessage, len(bodies)) + for i, b := range bodies { + out[i] = &streampb.StreamMessage{ + Body: &commonpb.Payload{Data: []byte(b)}, + Topic: topic, + Kind: streampb.STREAM_MESSAGE_KIND_DATA, + } + } + return out +} + +func bodies(msgs []*streampb.StreamMessage) []string { + out := make([]string, len(msgs)) + for i, m := range msgs { + out[i] = string(m.GetBody().GetData()) + } + return out +} + +func streamCtx(t *testing.T) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + t.Cleanup(cancel) + return ctx +} + +func TestStreamAppendAndRead(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-append-read" + s.create(ctx, t, id) + + first, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) + require.NoError(t, err) + require.Equal(t, int64(0), first.GetFirstOffset()) + require.Equal(t, int64(3), first.GetNextOffset()) + + second, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "d")}) + require.NoError(t, err) + require.Equal(t, int64(3), second.GetFirstOffset()) + + all := s.poll(ctx, t, id, 0) + require.Equal(t, []string{"a", "b", "c", "d"}, bodies(all.GetMessages())) + require.Equal(t, int64(4), all.GetNextOffset()) + require.Equal(t, int64(4), all.GetHeadOffset()) + require.False(t, all.GetClosed()) + + // A reader owns its cursor, so resuming mid-stream is just another read. + tail := s.poll(ctx, t, id, 2) + require.Equal(t, []string{"c", "d"}, bodies(tail.GetMessages())) + + // Caught up returns empty rather than erroring. + caughtUp := s.poll(ctx, t, id, 4) + require.Empty(t, caughtUp.GetMessages()) + require.Equal(t, int64(4), caughtUp.GetNextOffset()) +} + +func TestStreamManyReadersAreIndependent(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-many-readers" + s.create(ctx, t, id) + + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b")}) + require.NoError(t, err) + + // No durable per-subscriber state, so reader count is not a state-machine + // concern and there is no equivalent of the 10-subscriber ceiling the + // Signal-and-Update pattern hits. + for range 25 { + got := s.poll(ctx, t, id, 0) + require.Equal(t, []string{"a", "b"}, bodies(got.GetMessages())) + } +} + +func TestStreamProducerDedup(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-dedup" + s.create(ctx, t, id) + + in := &streampb.AddMessagesInput{ + Messages: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, + } + first, err := s.add(ctx, t, id, in) + require.NoError(t, err) + require.False(t, first.GetDeduplicated()) + + retry, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ + Messages: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, + }) + require.NoError(t, err) + require.True(t, retry.GetDeduplicated()) + require.Equal(t, first.GetFirstOffset(), retry.GetFirstOffset()) + + // The retry must not have appended a second copy. + got := s.poll(ctx, t, id, 0) + require.Equal(t, []string{"a", "b"}, bodies(got.GetMessages())) + + // Same sequence with different content is a client bug. Returning the + // recorded offsets would report success while dropping the data. + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ + Messages: streamMsgs("", "different"), ProducerId: "p1", Sequence: 1, + }) + require.ErrorContains(t, err, "different content") +} + +func TestStreamTopicFilter(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-topics" + s.create(ctx, t, id) + + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("tokens", "t1")}) + require.NoError(t, err) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("tools", "x1")}) + require.NoError(t, err) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("tokens", "t2")}) + require.NoError(t, err) + + got := s.poll(ctx, t, id, 0, "tokens") + require.Equal(t, []string{"t1", "t2"}, bodies(got.GetMessages())) + // Offsets are global, so a filtered read still advances past what it skipped. + require.Equal(t, int64(3), got.GetNextOffset()) +} + +func TestStreamFinishWritingIsPerProducer(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-finish" + s.create(ctx, t, id) + + _, err := s.client.FinishWriting(ctx, &streampb.FinishWritingRequest{ + FrontendRequest: &streampb.FinishWritingInput{ + Namespace: s.ns, StreamId: id, ProducerId: "p1", + }, + }) + require.NoError(t, err) + + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ + Messages: streamMsgs("", "a"), ProducerId: "p1", Sequence: 1, + }) + require.Error(t, err) + + // Finishing is per-producer, not a close, so others carry on. + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ + Messages: streamMsgs("", "b"), ProducerId: "p2", Sequence: 1, + }) + require.NoError(t, err) +} + +func TestStreamCloseAndTruncate(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-lifecycle" + s.create(ctx, t, id) + + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) + require.NoError(t, err) + + _, err = s.client.TruncateStream(ctx, &streampb.TruncateStreamRequest{ + FrontendRequest: &streampb.TruncateStreamInput{ + Namespace: s.ns, StreamId: id, NewBaseOffset: 1, + }, + }) + require.NoError(t, err) + + // A reader below the floor gets a distinguishable error carrying the floor, + // so it can jump forward rather than fail. + _, err = s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{Namespace: s.ns, StreamId: id, FromOffset: 0}, + }) + require.ErrorContains(t, err, "truncated") + + _, err = s.client.CloseStream(ctx, &streampb.CloseStreamRequest{ + FrontendRequest: &streampb.CloseStreamInput{Namespace: s.ns, StreamId: id}, + }) + require.NoError(t, err) + + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "d")}) + require.Error(t, err) + + // Closed is a state a reader observes, not an error, and the data stays + // readable rather than requiring a shutdown handshake with the producer. + got := s.poll(ctx, t, id, 1) + require.True(t, got.GetClosed()) + require.Equal(t, []string{"b", "c"}, bodies(got.GetMessages())) +} + +func TestStreamReadStartingInsideABatch(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-mid-batch" + s.create(ctx, t, id) + + // One batch covering 0..2, a second covering 3. A node is addressed by the + // first offset of its batch, so reading from 2 has to find the node that + // contains it rather than the node whose ID equals it. Getting that wrong + // silently drops the messages before the boundary. + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) + require.NoError(t, err) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "d")}) + require.NoError(t, err) + + for from, want := range map[int64][]string{ + 0: {"a", "b", "c", "d"}, + 1: {"b", "c", "d"}, + 2: {"c", "d"}, + 3: {"d"}, + } { + got := s.poll(ctx, t, id, from) + require.Equal(t, want, bodies(got.GetMessages()), "reading from offset %d", from) + require.Equal(t, int64(4), got.GetNextOffset()) + } +} + +func TestStreamBatchSizeIsBounded(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-batch-bound" + s.create(ctx, t, id) + + // The bound is not only admission control: it bounds how far a read has to + // step back to find the node containing an arbitrary offset. + tooMany := make([]string, streamMaxBatch+1) + for i := range tooMany { + tooMany[i] = "x" + } + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", tooMany...)}) + require.ErrorContains(t, err, "exceeds the limit") +} + +func (s *streamTestEnv) pollWait( + ctx context.Context, streamID string, from int64, +) (*streampb.PollMessagesOutput, error) { + resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{ + Namespace: s.ns, StreamId: streamID, FromOffset: from, WaitNewMessages: true, + }, + }) + if err != nil { + return nil, err + } + return resp.GetFrontendResponse(), nil +} + +func TestStreamLongPollWakesOnAppend(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-longpoll-append" + s.create(ctx, t, id) + + type result struct { + out *streampb.PollMessagesOutput + err error + } + done := make(chan result, 1) + go func() { + out, err := s.pollWait(ctx, id, 0) + done <- result{out, err} + }() + + // The poll is parked on an empty stream; the append is what releases it. + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a")}) + require.NoError(t, err) + + select { + case r := <-done: + require.NoError(t, r.err) + require.Equal(t, []string{"a"}, bodies(r.out.GetMessages())) + require.Equal(t, int64(1), r.out.GetNextOffset()) + case <-time.After(25 * time.Second): + t.Fatal("long poll did not wake on append") + } +} + +func TestStreamLongPollWakesOnClose(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-longpoll-close" + s.create(ctx, t, id) + + done := make(chan *streampb.PollMessagesOutput, 1) + go func() { + out, err := s.pollWait(ctx, id, 0) + if err == nil { + done <- out + } + }() + + _, err := s.client.CloseStream(ctx, &streampb.CloseStreamRequest{ + FrontendRequest: &streampb.CloseStreamInput{Namespace: s.ns, StreamId: id}, + }) + require.NoError(t, err) + + // Closing has to release a parked reader, otherwise a consumer of a + // finished stream waits out the full timeout for no reason. + select { + case out := <-done: + require.True(t, out.GetClosed()) + require.Empty(t, out.GetMessages()) + case <-time.After(25 * time.Second): + t.Fatal("long poll did not wake on close") + } +} + +func TestStreamLongPollReturnsEmptyOnTimeout(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-longpoll-timeout" + s.create(ctx, t, id) + + // A timeout is an empty response, not an error: the caller polls again + // rather than distinguishing a quiet stream from a failure. + start := time.Now() + out, err := s.pollWait(ctx, id, 0) + require.NoError(t, err) + require.Empty(t, out.GetMessages()) + require.Equal(t, int64(0), out.GetNextOffset()) + require.False(t, out.GetClosed()) + require.Greater(t, time.Since(start), 5*time.Second, "the poll should have parked, not returned immediately") +} + +func TestStreamLongPollReturnsImmediatelyWhenBehind(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-longpoll-behind" + s.create(ctx, t, id) + + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b")}) + require.NoError(t, err) + + // Waiting is only for a reader that is caught up. One that is behind must + // not be parked behind data that already exists. + start := time.Now() + out, err := s.pollWait(ctx, id, 0) + require.NoError(t, err) + require.Equal(t, []string{"a", "b"}, bodies(out.GetMessages())) + require.Less(t, time.Since(start), 5*time.Second) +} + +func TestStreamCapTruncatesAndReclaims(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-cap" + + _, err := s.client.CreateStream(ctx, &streampb.CreateStreamRequest{ + FrontendRequest: &streampb.CreateStreamInput{ + Namespace: s.ns, StreamId: id, + Lifecycle: &streampb.StreamLifecycle{MaxItems: 4}, + }, + }) + require.NoError(t, err) + + for _, batch := range [][]string{{"a", "b"}, {"c", "d"}, {"e", "f"}} { + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", batch...)}) + require.NoError(t, err) + } + + // Six appended against a cap of four, so the floor advanced without anyone + // asking. Reading from the floor still works and returns exactly what the + // cap retained. + got := s.poll(ctx, t, id, 2) + require.Equal(t, []string{"c", "d", "e", "f"}, bodies(got.GetMessages())) + + // Below the floor is a distinguishable error, not silence. + _, err = s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{Namespace: s.ns, StreamId: id, FromOffset: 0}, + }) + require.ErrorContains(t, err, "truncated") +} + +func TestStreamClosedStaysReadable(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-closed-readable" + s.create(ctx, t, id) + + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b")}) + require.NoError(t, err) + _, err = s.client.CloseStream(ctx, &streampb.CloseStreamRequest{ + FrontendRequest: &streampb.CloseStreamInput{Namespace: s.ns, StreamId: id}, + }) + require.NoError(t, err) + + // Close seals, it does not delete. A consumer can finish draining without + // coordinating a shutdown with the producer, which is the handshake the + // signal-based implementation forces today. + got := s.poll(ctx, t, id, 0) + require.Equal(t, []string{"a", "b"}, bodies(got.GetMessages())) + require.True(t, got.GetClosed()) + + desc, err := s.client.DescribeStream(ctx, &streampb.DescribeStreamRequest{ + FrontendRequest: &streampb.DescribeStreamInput{Namespace: s.ns, StreamId: id}, + }) + require.NoError(t, err) + require.NotNil(t, desc.GetFrontendResponse().GetState().GetCloseTime()) +} + +func TestStreamListStreams(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + + created := []string{"list-a", "list-b", "list-c"} + for _, id := range created { + s.create(ctx, t, id) + } + + // Visibility is written by a task after the create commits, so this is + // eventually consistent by design rather than by accident. + await.RequireTrue(t, func() bool { + resp, err := s.client.ListStreams(ctx, &streampb.ListStreamsRequest{ + FrontendRequest: &streampb.ListStreamsInput{Namespace: s.ns}, + }) + if err != nil { + return false + } + found := make(map[string]bool) + for _, e := range resp.GetFrontendResponse().GetStreams() { + found[e.GetStreamId()] = true + } + for _, id := range created { + if !found[id] { + return false + } + } + return true + }, 20*time.Second, 250*time.Millisecond) +} + +// A capped poll must not read the whole stream to answer. The read is clipped +// to the offsets the caller can be given, so a reader asking for one message +// off a long stream does not pull the rest into the history host on its way. +func TestStreamPollReadsOnlyWhatItReturns(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-poll-cap" + s.create(ctx, t, id) + + for i := range 40 { + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ + Messages: streamMsgs("", fmt.Sprintf("m%d", i)), + }) + require.NoError(t, err) + } + + got := s.pollMax(ctx, t, id, 0, 1) + require.Equal(t, []string{"m0"}, bodies(got.GetMessages())) + require.Equal(t, int64(1), got.GetNextOffset(), "a capped read advances only over what it gave") + require.Equal(t, int64(40), got.GetHeadOffset(), "the frontier is still reported in full") + + // Paging from there covers the rest, so the cap trims the read and not the stream. + got = s.pollMax(ctx, t, id, got.GetNextOffset(), 10) + require.Equal(t, []string{"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", "m10"}, + bodies(got.GetMessages())) + require.Equal(t, int64(11), got.GetNextOffset()) + + // A filter finding nothing in the page still has to leave the reader able to + // go on, and it must stop at the page rather than scanning to the head. + got = s.pollMaxTopics(ctx, t, id, 0, 5, "nothing-matches-this") + require.Empty(t, got.GetMessages()) + require.Equal(t, int64(5), got.GetNextOffset(), + "a filtered page advanced past its own bound, so the read ran to the head") +} + +// A stream id can be reused. The cached bytes belong to the log, not to the +// name, so a reader of the new stream must never be served the old one's. +func TestStreamPollAfterIdIsReusedServesTheNewStream(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-reused-id" + + s.create(ctx, t, id) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "old")}) + require.NoError(t, err) + // Read it back so the bytes are certain to be cached before the id is reused. + require.Equal(t, []string{"old"}, bodies(s.poll(ctx, t, id, 0).GetMessages())) + + _, err = s.client.DeleteStream(ctx, &streampb.DeleteStreamRequest{ + FrontendRequest: &streampb.DeleteStreamInput{Namespace: s.ns, StreamId: id}, + }) + require.NoError(t, err) + + s.create(ctx, t, id) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "new")}) + require.NoError(t, err) + + got := s.poll(ctx, t, id, 0) + require.Equal(t, []string{"new"}, bodies(got.GetMessages()), + "a reused id served bytes from the deleted stream") +} + +// A filtered read hands back messages whose offsets are not contiguous, which +// is why the offset rides on the message. A reader cannot count them. +func TestStreamFilteredReadReportsRealOffsets(t *testing.T) { + s := newStreamTestEnv(t) + ctx := streamCtx(t) + const id = "stream-filtered-offsets" + s.create(ctx, t, id) + + for i, topic := range []string{"a", "b", "a", "b", "a"} { + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ + Messages: streamMsgs(topic, fmt.Sprintf("m%d", i)), + }) + require.NoError(t, err) + } + + got := s.pollMaxTopics(ctx, t, id, 0, 0, "a") + require.Equal(t, []string{"m0", "m2", "m4"}, bodies(got.GetMessages())) + require.Equal(t, []int64{0, 2, 4}, offsets(got.GetMessages())) +} + +func (s *streamTestEnv) pollMaxTopics( + ctx context.Context, t *testing.T, streamID string, from int64, maxMessages int32, topics ...string, +) *streampb.PollMessagesOutput { + t.Helper() + resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{ + Namespace: s.ns, StreamId: streamID, FromOffset: from, + MaxMessages: maxMessages, Topics: topics, + }, + }) + require.NoError(t, err) + return resp.GetFrontendResponse() +} + +func (s *streamTestEnv) pollMax( + ctx context.Context, t *testing.T, streamID string, from int64, maxMessages int32, +) *streampb.PollMessagesOutput { + t.Helper() + resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{ + Namespace: s.ns, StreamId: streamID, FromOffset: from, MaxMessages: maxMessages, + }, + }) + require.NoError(t, err) + return resp.GetFrontendResponse() +} + +func offsets(msgs []*streampb.StreamMessage) []int64 { + out := make([]int64, len(msgs)) + for i, m := range msgs { + out[i] = m.GetOffset() + } + return out +} diff --git a/tests/stream_workflow_test.go b/tests/stream_workflow_test.go new file mode 100644 index 00000000000..6385e0d8907 --- /dev/null +++ b/tests/stream_workflow_test.go @@ -0,0 +1,409 @@ +package tests + +import ( + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + "go.temporal.io/api/workflowservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/tests/testcore" + "google.golang.org/protobuf/types/known/durationpb" +) + +// Path A: a workflow publishing to a stream it owns. The stream is co-located +// with the workflow, so the frontier advances in the workflow task's own +// commit, and History gets one fixed-size event naming the offset range rather +// than anything that was published. +// +// Driven through the raw task poller rather than an SDK, because emitting a new +// command type does not need one. +func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-publish-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + published := false + // The newer taskpoller cannot emit an arbitrary command type, which is + // the whole point here. + //nolint:staticcheck // SA1019: deprecated poller is the only one that can. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + if published { + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, + Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ + CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, + }, + }}, nil + } + published = true + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("planning")}, Topic: "progress"}, + {Body: &commonpb.Payload{Data: []byte("calling tool")}, Topic: "progress"}, + }, + }, + }, + }}, nil + }, + Logger: env.Logger, + T: t, + } + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + // One event for the batch, holding the range and none of the payload. Two + // messages were published, so it has to name both of them and stop there. + events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}) + var added []*historypb.WorkflowStreamMessagesAddedEventAttributes + for _, e := range events { + if a := e.GetWorkflowStreamMessagesAddedEventAttributes(); a != nil { + added = append(added, a) + } + } + require.Len(t, added, 1, "one publish command writes one event") + require.Equal(t, int64(0), added[0].GetFirstOffset()) + require.Equal(t, int64(2), added[0].GetMessageCount()) + require.Equal(t, chasmworkflow.DefaultStreamName, added[0].GetStreamId(), + "an unnamed stream resolves to the default before it is recorded") + + // The bodies stay out of History. Asserted on the serialized event rather + // than on its fields, because a field this test forgot to check would still + // be carrying them. + raw, err := added[0].Marshal() + require.NoError(t, err) + require.NotContains(t, string(raw), "calling tool", + "the event must name the range, never carry the payload") + + // The bodies are readable from outside the workflow, which is the point of + // publishing them: an attached stream has no id, so it is addressed by its + // owner and its name. + poll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id, FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Equal(t, []string{"planning", "calling tool"}, bodies(poll.GetFrontendResponse().GetMessages())) + require.Equal(t, []int64{0, 1}, offsets(poll.GetFrontendResponse().GetMessages()), + "each message carries where it sits in the log, so a reader can resume between them") + require.Equal(t, int64(2), poll.GetFrontendResponse().GetNextOffset()) + require.Equal(t, int64(2), poll.GetFrontendResponse().GetHeadOffset()) + + // The topic filter applies to an attached stream the same as to any other, + // and offsets stay assigned over the unfiltered stream. + filtered, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id, FromOffset: 0, Topics: []string{"nothing-here"}, + }, + }) + require.NoError(t, err) + require.Empty(t, filtered.GetFrontendResponse().GetMessages()) + require.Equal(t, int64(2), filtered.GetFrontendResponse().GetNextOffset(), + "a filtered page still advances the reader") + + // A stream the workflow has not published to reads as an empty one, which + // is what lets a reader attach before the first event. + unwritten, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id, StreamName: "not-published-to-yet", FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Empty(t, unwritten.GetFrontendResponse().GetMessages()) + require.Equal(t, int64(0), unwritten.GetFrontendResponse().GetHeadOffset()) + require.False(t, unwritten.GetFrontendResponse().GetClosed()) + + // A workflow that does not exist is still an error, so a mistyped owner is + // not mistaken for a stream with nothing in it. + _, err = s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id + "-does-not-exist", FromOffset: 0, + }, + }) + require.ErrorContains(t, err, "not found") + + // An attached stream still has no standalone id, so the id-addressed read + // must not reach it. + streamID := we.GetRunId() + "/" + chasmworkflow.DefaultStreamName + _, err = s.client.PollMessages(s.ctx(), &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{ + Namespace: s.ns, StreamId: streamID, FromOffset: 0, + }, + }) + require.ErrorContains(t, err, "stream not found", + "an attached stream is only reachable through its workflow") +} + +// A reader attached to a workflow's stream before the workflow published +// anything. This is the ordinary case for a UI that opens on a session and +// waits for the agent's first token, so the poll has to park on a stream that +// does not exist yet and wake when the workflow creates it. +func TestStreamWorkflowLongPollWakesOnPublish(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-longpoll-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + type result struct { + out *streamlib.PollMessagesOutput + err error + } + done := make(chan result, 1) + go func() { + resp, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id, FromOffset: 0, WaitNewMessages: true, + }, + }) + done <- result{resp.GetFrontendResponse(), err} + }() + + //nolint:staticcheck // SA1019: deprecated poller is the only one that can emit the command. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("first token")}}, + }, + }, + }, + }}, nil + }, + Logger: env.Logger, + T: t, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + select { + case r := <-done: + require.NoError(t, r.err) + require.Equal(t, []string{"first token"}, bodies(r.out.GetMessages())) + require.Equal(t, int64(1), r.out.GetNextOffset()) + case <-time.After(25 * time.Second): + t.Fatal("the parked reader did not wake when the workflow published") + } +} + +// Two producers on one stream: the workflow, whose publishes ride its Workflow +// Task, and something outside it. This is the shape of an agent session, where +// a model activity produces the tokens and the workflow only brackets them. +func TestStreamWorkflowTakesAppendsFromOutsideToo(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-mixed-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + appendOutside := func(body string) *streamlib.AddMessagesOutput { + t.Helper() + resp, err := s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ + FrontendRequest: &streamlib.AddWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id, + Messages: []*streamlib.StreamMessage{{ + Body: &commonpb.Payload{Data: []byte(body)}, + Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + }}, + }, + }) + require.NoError(t, err) + return resp.GetFrontendResponse() + } + + // The first writer creates the stream, and here that is not the workflow. + first := appendOutside("token one") + require.Equal(t, int64(0), first.GetFirstOffset()) + + //nolint:staticcheck // SA1019: deprecated poller is the only one that can emit the command. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("turn ended")}}, + }, + }, + }, + }}, nil + }, + Logger: env.Logger, + T: t, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + // The workflow appended after the outside writer, so it has to continue the + // same log rather than start its own. + require.Equal(t, int64(2), appendOutside("token two").GetFirstOffset()) + + poll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id, FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Equal(t, []string{"token one", "turn ended", "token two"}, + bodies(poll.GetFrontendResponse().GetMessages()), + "both producers write one ordered log") + + // The event names the offset the workflow's own publish landed at, which + // only holds if the workflow saw the outside writer's message first. + events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id}) + var added []*historypb.WorkflowStreamMessagesAddedEventAttributes + for _, e := range events { + if a := e.GetWorkflowStreamMessagesAddedEventAttributes(); a != nil { + added = append(added, a) + } + } + require.Len(t, added, 1) + require.Equal(t, int64(1), added[0].GetFirstOffset()) +} + +// A reader tailing a workflow that ends has to be released. Nothing can be +// added to a stream inside a closed execution, so a reader parked on it would +// otherwise wait for a message that can never come. +func TestStreamWorkflowStreamClosesWithItsWorkflow(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + id := "stream-wf-close-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + + _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + + describe := func() *streamlib.StreamState { + t.Helper() + resp, err := s.client.DescribeWorkflowStream(s.ctx(), &streamlib.DescribeWorkflowStreamRequest{ + FrontendRequest: &streamlib.DescribeWorkflowStreamInput{ + Namespace: s.ns, WorkflowId: id, + }, + }) + require.NoError(t, err) + return resp.GetFrontendResponse().GetState() + } + + //nolint:staticcheck // SA1019: deprecated poller is the only one that can emit the command. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + return []*commandpb.Command{ + { + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("last word")}}, + }, + }, + }, + }, + { + CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, + Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ + CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, + }, + }, + }, nil + }, + Logger: env.Logger, + T: t, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + require.True(t, describe().GetClosed(), "the stream of a completed workflow reads as closed") + + // Closed does not mean gone. Everything published before the workflow + // ended is still there to be read. + poll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: id, FromOffset: 0, WaitNewMessages: true, + }, + }) + require.NoError(t, err) + require.Equal(t, []string{"last word"}, bodies(poll.GetFrontendResponse().GetMessages())) + require.True(t, poll.GetFrontendResponse().GetClosed()) +} diff --git a/tests/streaming_baseline_test.go b/tests/streaming_baseline_test.go new file mode 100644 index 00000000000..a97817e01b2 --- /dev/null +++ b/tests/streaming_baseline_test.go @@ -0,0 +1,458 @@ +package tests + +import ( + "context" + "fmt" + "os" + "slices" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.temporal.io/api/workflowservice/v1" + sdkclient "go.temporal.io/sdk/client" + "go.temporal.io/sdk/workflow" + "go.temporal.io/server/common/metrics" + "go.temporal.io/server/common/metrics/metricstest" + "go.temporal.io/server/tests/testcore" +) + +// Baseline measurements for today's Workflow Streams pattern: an Activity +// batches items into Signals and a consumer long-polls with an Update. AI-198 +// proposes replacing it, and a replacement cannot be justified without knowing +// what the current pattern actually costs. +// +// The two dimensions that matter are the flush interval and the subscriber +// count. The shipped guidance is a ~2s flush; the Native Streams 1-pager asks +// for 100ms to be practical. Running both shows the cost of closing that gap +// under the current design, which is the number the September check-in needs. +// +// The full matrix takes minutes, so it is opt-in via TEMPORAL_STREAM_BENCH=1. +// Without it a short configuration still runs, which keeps the harness honest +// without slowing the suite. + +const ( + streamBatchSignal = "stream_batch" + streamDoneSignal = "stream_done" + streamPollUpdate = "poll_events" + streamMessageSize = 20 +) + +type streamBaselineParams struct { + name string + flushInterval time.Duration + subscribers int + messageRate int // messages per second + duration time.Duration +} + +type streamBaselineResult struct { + params streamBaselineParams + + messagesSent int + messagesReceived int + pollRejections int64 + + historyBytes int64 + historyEvents int64 + // Set when the workload has no workflow at all, so the history columns are + // an absence rather than a measurement. Rendering them as 0.00 next to a + // measured figure reads as a comparison that was never made. + historyNotApplicable bool + + persistenceRequests int64 + persistenceByOp map[string]int64 + + latencyP50 time.Duration + latencyP99 time.Duration + + // Set when the run could not complete, for example because the workflow + // exceeded a history limit. That is itself a result worth reporting. + failure string +} + +// streamBaselineWorkflow mirrors the shipped pattern: Signals carry batches in, +// an Update long-polls them back out, and the workflow holds the buffer. +func streamBaselineWorkflow(ctx workflow.Context) error { + var buffer []string + done := false + + err := workflow.SetUpdateHandler(ctx, streamPollUpdate, + func(ctx workflow.Context, lastSeen int) ([]string, error) { + // The shape that makes this expensive: every poll is a durable + // state transition, even when it returns nothing new. + if err := workflow.Await(ctx, func() bool { + return len(buffer) > lastSeen || done + }); err != nil { + return nil, err + } + if lastSeen >= len(buffer) { + return nil, nil + } + out := make([]string, len(buffer)-lastSeen) + copy(out, buffer[lastSeen:]) + return out, nil + }) + if err != nil { + return err + } + + batches := workflow.GetSignalChannel(ctx, streamBatchSignal) + finish := workflow.GetSignalChannel(ctx, streamDoneSignal) + + for !done { + sel := workflow.NewSelector(ctx) + sel.AddReceive(batches, func(c workflow.ReceiveChannel, _ bool) { + var batch []string + c.Receive(ctx, &batch) + buffer = append(buffer, batch...) + }) + sel.AddReceive(finish, func(c workflow.ReceiveChannel, _ bool) { + c.Receive(ctx, nil) + done = true + }) + sel.Select(ctx) + } + + // Let any parked pollers observe the terminal state before exiting. + return workflow.Await(ctx, func() bool { return true }) +} + +func TestStreamingBaseline(t *testing.T) { + matrix := shortStreamBaselineMatrix() + if os.Getenv("TEMPORAL_STREAM_BENCH") == "1" { + matrix = fullStreamBaselineMatrix() + } + + results := make([]streamBaselineResult, 0, len(matrix)) + for _, p := range matrix { + t.Run(p.name, func(t *testing.T) { + results = append(results, runStreamBaseline(t, p)) + }) + } + reportStreamBaseline(t, results) +} + +func shortStreamBaselineMatrix() []streamBaselineParams { + return []streamBaselineParams{ + {name: "flush2s_sub1", flushInterval: 2 * time.Second, subscribers: 1, messageRate: 40, duration: 6 * time.Second}, + {name: "flush100ms_sub1", flushInterval: 100 * time.Millisecond, subscribers: 1, messageRate: 40, duration: 6 * time.Second}, + } +} + +func fullStreamBaselineMatrix() []streamBaselineParams { + var out []streamBaselineParams + for _, flush := range []time.Duration{2 * time.Second, 100 * time.Millisecond} { + for _, subs := range []int{1, 5, 25} { + out = append(out, streamBaselineParams{ + name: fmt.Sprintf("flush%s_sub%d", flush, subs), + flushInterval: flush, + subscribers: subs, + messageRate: 40, + duration: 50 * time.Second, + }) + } + } + return out +} + +func runStreamBaseline(t *testing.T, p streamBaselineParams) streamBaselineResult { + // Dedicated cluster so competing test load does not distort the timings. + // Metric capture is namespace-scoped because persistence_requests carries a + // namespace tag, which also attributes the counts to this workload rather + // than to cluster background traffic. + // + // Testlogger failure is off because a sustained-load run torn down while + // queues are still draining always logs shard-status errors on shutdown. + // The cost is losing the safety net that would catch a genuine server error + // during the run, so treat an anomalous result as a reason to re-run with + // the option removed rather than trusting it. + env := testcore.NewEnv(t, testcore.WithDisableTestloggerFailure()) + res := streamBaselineResult{params: p, persistenceByOp: map[string]int64{}} + + env.SdkWorker().RegisterWorkflow(streamBaselineWorkflow) + + ctx, cancel := context.WithTimeout(context.Background(), p.duration+2*time.Minute) + defer cancel() + + wfID := env.Tv().WorkflowID() + run, err := env.SdkClient().ExecuteWorkflow(ctx, sdkclient.StartWorkflowOptions{ + ID: wfID, + TaskQueue: env.WorkerTaskQueue(), + }, streamBaselineWorkflow) + require.NoError(t, err) + + // Started after workflow creation so cluster, namespace, and start costs do + // not inflate the per-message figures. Both designs pay those equally. + capture := env.StartNamespaceMetricCapture() + + sentAt := &sync.Map{} // message sequence -> generation time + var receivedTotal atomic.Int64 + var pollRejections atomic.Int64 + var consumers sync.WaitGroup + consumerLatencies := make([][]time.Duration, p.subscribers) + consumerCounts := make([]int, p.subscribers) + consumerCtx, stopConsumers := context.WithCancel(ctx) + defer stopConsumers() + + for i := range p.subscribers { + consumers.Add(1) + go func(idx int) { + defer consumers.Done() + lat, n := runStreamConsumer(consumerCtx, env, wfID, run.GetRunID(), sentAt, &receivedTotal, &pollRejections) + consumerLatencies[idx] = lat + consumerCounts[idx] = n + }(i) + } + + res.messagesSent = runStreamProducer(ctx, t, env, wfID, run.GetRunID(), p, sentAt, &res) + + // Drain, then stop the consumers, and only then let the workflow finish, so + // that no consumer is ever polling an execution that is completing. One that + // is counts its own doomed polls as rejections and its client backoff as + // delivery latency, which measures the harness rather than the pattern. The + // workflow finishes last so the history numbers below are final rather than a + // mid-flight snapshot. A cell that fails to drain is reported rather than + // failed: hitting a limit is a real property of this pattern and is part of + // what the benchmark is measuring. + want := int64(res.messagesSent) * int64(p.subscribers) + if !waitForDrain(ctx, &receivedTotal, want, 15*time.Second) { + t.Logf("drained %d of %d expected deliveries before timeout", receivedTotal.Load(), want) + } + stopConsumers() + consumers.Wait() + require.NoError(t, env.SdkClient().SignalWorkflow(ctx, wfID, run.GetRunID(), streamDoneSignal, nil)) + if err := run.Get(ctx, nil); err != nil { + t.Logf("workflow did not complete cleanly: %v", err) + } + + var all []time.Duration + for i := range p.subscribers { + all = append(all, consumerLatencies[i]...) + res.messagesReceived += consumerCounts[i] + } + res.pollRejections = pollRejections.Load() + res.latencyP50 = percentile(all, 0.50) + res.latencyP99 = percentile(all, 0.99) + + desc, err := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: env.Tv().WithWorkflowID(wfID).WorkflowExecution(), + }) + if err == nil { + res.historyBytes = desc.GetWorkflowExecutionInfo().GetHistorySizeBytes() + res.historyEvents = desc.GetWorkflowExecutionInfo().GetHistoryLength() + } + + for _, rec := range capture.Metric(metrics.PersistenceRequests.Name()) { + res.persistenceRequests += recordingCount(rec) + if op, ok := rec.Tags["operation"]; ok { + res.persistenceByOp[op] += recordingCount(rec) + } + } + + return res +} + +func runStreamProducer( + ctx context.Context, + t *testing.T, + env *testcore.TestEnv, + wfID, runID string, + p streamBaselineParams, + sentAt *sync.Map, + res *streamBaselineResult, +) int { + payload := make([]byte, streamMessageSize) + for i := range payload { + payload[i] = 'x' + } + + // Messages are generated continuously and flushed on the interval, which is + // what an EventBatcher does. Latency is stamped at generation, not at + // flush: the time an item waits in the batcher is the dominant cost of a + // long flush interval, and stamping at flush would hide it entirely. + genTicker := time.NewTicker(time.Second / time.Duration(p.messageRate)) + defer genTicker.Stop() + flushTicker := time.NewTicker(p.flushInterval) + defer flushTicker.Stop() + deadline := time.Now().Add(p.duration) + + seq := 0 + var pending []string + + flush := func() bool { + if len(pending) == 0 { + return true + } + batch := pending + pending = nil + if err := env.SdkClient().SignalWorkflow(ctx, wfID, runID, streamBatchSignal, batch); err != nil { + // A history or signal limit is a legitimate outcome for this + // pattern, not a broken harness. Record it and stop. + res.failure = err.Error() + t.Logf("producer stopped after %d messages: %v", seq, err) + return false + } + return true + } + + for time.Now().Before(deadline) { + select { + case <-ctx.Done(): + return seq + case <-genTicker.C: + sentAt.Store(seq, time.Now()) + pending = append(pending, fmt.Sprintf("%d:%s", seq, payload)) + seq++ + case <-flushTicker.C: + if !flush() { + return seq + } + } + } + flush() + return seq +} + +func runStreamConsumer( + ctx context.Context, + env *testcore.TestEnv, + wfID, runID string, + sentAt *sync.Map, + receivedTotal *atomic.Int64, + rejections *atomic.Int64, +) ([]time.Duration, int) { + var latencies []time.Duration + var batch []string + lastSeen := 0 + + for ctx.Err() == nil { + handle, err := env.SdkClient().UpdateWorkflow(ctx, sdkclient.UpdateWorkflowOptions{ + WorkflowID: wfID, + RunID: runID, + UpdateName: streamPollUpdate, + Args: []any{lastSeen}, + WaitForStage: sdkclient.WorkflowUpdateStageCompleted, + }) + if err == nil { + err = handle.Get(ctx, &batch) + } + if err != nil { + // A rejected poll is a measurement, not a reason to stop. Killing + // the consumer here would report a server limit as consumer + // slowness, which is a different and much less useful claim. + rejections.Add(1) + select { + case <-ctx.Done(): + return latencies, lastSeen + case <-time.After(50 * time.Millisecond): + } + continue + } + received := time.Now() + for range batch { + if v, ok := sentAt.Load(lastSeen); ok { + latencies = append(latencies, received.Sub(v.(time.Time))) + } + lastSeen++ + receivedTotal.Add(1) + } + } + return latencies, lastSeen +} + +// historyPerMsg renders a per-message history figure, keeping a workload with +// no workflow distinct from one that measured zero. +func (r streamBaselineResult) historyPerMsg(v int64) string { + if r.historyNotApplicable { + return "no workflow" + } + if r.messagesSent == 0 { + return "n/a" + } + return fmt.Sprintf("%.2f", float64(v)/float64(r.messagesSent)) +} + +// waitForDrain polls until every consumer has caught up or the deadline passes. +// It reports rather than asserts, because a cell that cannot drain is a result. +func waitForDrain(ctx context.Context, got *atomic.Int64, want int64, timeout time.Duration) bool { + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + deadline := time.After(timeout) + for { + if got.Load() >= want { + return true + } + select { + case <-ctx.Done(): + return false + case <-deadline: + return false + case <-ticker.C: + } + } +} + +func recordingCount(rec *metricstest.CapturedRecording) int64 { + switch v := rec.Value.(type) { + case int64: + return v + case float64: + return int64(v) + default: + return 0 + } +} + +func percentile(d []time.Duration, q float64) time.Duration { + if len(d) == 0 { + return 0 + } + sorted := make([]time.Duration, len(d)) + copy(sorted, d) + slices.Sort(sorted) + idx := int(float64(len(sorted)-1) * q) + return sorted[idx] +} + +func reportStreamBaseline(t *testing.T, results []streamBaselineResult) { + // Emitted as markdown so the numbers can go straight into the design docs + // without being retyped, which is how transcription errors get in. + t.Log("Workflow Streams baseline: Signals in, polling Update out") + t.Log("") + t.Log("| scenario | msgs | delivered | rejected polls | hist events/msg | hist bytes/msg | persist ops/msg | p50 | p99 |") + t.Log("|---|---|---|---|---|---|---|---|---|") + for _, r := range results { + perMsg := func(v int64) string { + if r.messagesSent == 0 { + return "n/a" + } + return fmt.Sprintf("%.2f", float64(v)/float64(r.messagesSent)) + } + t.Logf("| %s | %d | %d | %d | %s | %s | %s | %s | %s |", + r.params.name, r.messagesSent, r.messagesReceived, r.pollRejections, + r.historyPerMsg(r.historyEvents), r.historyPerMsg(r.historyBytes), + perMsg(r.persistenceRequests), + r.latencyP50.Round(time.Millisecond), r.latencyP99.Round(time.Millisecond)) + } + t.Log("") + for _, r := range results { + if r.failure != "" { + t.Logf("%s did not complete: %s", r.params.name, r.failure) + } + if r.messagesSent > 0 && r.messagesReceived < r.messagesSent*r.params.subscribers { + t.Logf("%s under-delivered: %d of %d expected", + r.params.name, r.messagesReceived, r.messagesSent*r.params.subscribers) + } + } + t.Log("Totals are absolute, not rates: persist ops exclude cluster and workflow start.") + t.Log("Rejected polls are bounded by history.maxInFlightUpdates (default 10) and") + t.Log("history.maxTotalUpdates (default 2000), both per workflow execution.") + for _, r := range results { + t.Logf("%s raw: events=%d bytes=%d persistOps=%d byOp=%v", + r.params.name, r.historyEvents, r.historyBytes, r.persistenceRequests, r.persistenceByOp) + } +} diff --git a/tests/streaming_native_test.go b/tests/streaming_native_test.go new file mode 100644 index 00000000000..a7e400caee8 --- /dev/null +++ b/tests/streaming_native_test.go @@ -0,0 +1,260 @@ +package tests + +import ( + "context" + "fmt" + "os" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/metrics" + "go.temporal.io/server/tests/testcore" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// The native-stream half of the comparison. It deliberately shares the workload +// shape, latency stamping, and metric capture with the Signal-and-Update +// baseline in streaming_baseline_test.go, because a benchmark whose two halves +// generate load differently measures the harness rather than the design. + +func runNativeStream(t *testing.T, p streamBaselineParams) streamBaselineResult { + env := testcore.NewEnv(t, testcore.WithDisableTestloggerFailure()) + res := streamBaselineResult{params: p, persistenceByOp: map[string]int64{}} + + conn, err := grpc.NewClient(env.FrontendGRPCAddress(), + grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + defer func() { _ = conn.Close() }() + client := streampb.NewStreamServiceClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), p.duration+2*time.Minute) + defer cancel() + + ns := env.Namespace().String() + streamID := fmt.Sprintf("bench-%s", p.name) + created, err := client.CreateStream(ctx, &streampb.CreateStreamRequest{ + FrontendRequest: &streampb.CreateStreamInput{Namespace: ns, StreamId: streamID}, + }) + require.NoError(t, err) + runID := created.GetFrontendResponse().GetRunId() + + // Started after creation so cluster and namespace setup do not inflate the + // per-message figures, matching the baseline. + capture := env.StartNamespaceMetricCapture() + + sentAt := &sync.Map{} + var receivedTotal atomic.Int64 + var pollRejections atomic.Int64 + + consumerCtx, stopConsumers := context.WithCancel(ctx) + defer stopConsumers() + var consumers sync.WaitGroup + latencies := make([][]time.Duration, p.subscribers) + counts := make([]int, p.subscribers) + + for i := range p.subscribers { + consumers.Add(1) + go func(idx int) { + defer consumers.Done() + latencies[idx], counts[idx] = runNativeConsumer( + consumerCtx, client, ns, streamID, runID, sentAt, &receivedTotal, &pollRejections) + }(i) + } + + res.messagesSent = runNativeProducer(ctx, t, client, ns, streamID, runID, p, sentAt, &res) + + want := int64(res.messagesSent) * int64(p.subscribers) + if !waitForDrain(ctx, &receivedTotal, want, 15*time.Second) { + t.Logf("drained %d of %d expected deliveries before timeout", receivedTotal.Load(), want) + } + stopConsumers() + consumers.Wait() + + var all []time.Duration + for i := range p.subscribers { + all = append(all, latencies[i]...) + res.messagesReceived += counts[i] + } + res.pollRejections = pollRejections.Load() + res.latencyP50 = percentile(all, 0.50) + res.latencyP99 = percentile(all, 0.99) + + // This workload has no workflow: the producer and every consumer are external + // gRPC clients. So there is no execution to describe and no history figure to + // report, which is not the same claim as a measured zero and must not be + // rendered as one. What a publish from workflow code costs in history is + // measured against a control in stream_publish_cost_test.go. + res.historyNotApplicable = true + + for _, rec := range capture.Metric(metrics.PersistenceRequests.Name()) { + res.persistenceRequests += recordingCount(rec) + if op, ok := rec.Tags["operation"]; ok { + res.persistenceByOp[op] += recordingCount(rec) + } + } + return res +} + +func runNativeProducer( + ctx context.Context, + t *testing.T, + client streampb.StreamServiceClient, + ns, streamID, runID string, + p streamBaselineParams, + sentAt *sync.Map, + res *streamBaselineResult, +) int { + payload := make([]byte, streamMessageSize) + for i := range payload { + payload[i] = 'x' + } + + genTicker := time.NewTicker(time.Second / time.Duration(p.messageRate)) + defer genTicker.Stop() + flushTicker := time.NewTicker(p.flushInterval) + defer flushTicker.Stop() + deadline := time.Now().Add(p.duration) + + seq := 0 + var pending []*streampb.StreamMessage + sequence := int64(0) + + flush := func() bool { + if len(pending) == 0 { + return true + } + batch := pending + pending = nil + sequence++ + _, err := client.AddMessages(ctx, &streampb.AddMessagesRequest{ + FrontendRequest: &streampb.AddMessagesInput{ + Namespace: ns, StreamId: streamID, RunId: runID, Messages: batch, + ProducerId: "bench", Sequence: sequence, + }, + }) + if err != nil { + res.failure = err.Error() + t.Logf("producer stopped after %d messages: %v", seq, err) + return false + } + return true + } + + for time.Now().Before(deadline) { + select { + case <-ctx.Done(): + return seq + case <-genTicker.C: + sentAt.Store(seq, time.Now()) + pending = append(pending, &streampb.StreamMessage{ + Body: &commonpb.Payload{Data: payload}, + Kind: streampb.STREAM_MESSAGE_KIND_DATA, + }) + seq++ + case <-flushTicker.C: + if !flush() { + return seq + } + } + } + flush() + return seq +} + +func runNativeConsumer( + ctx context.Context, + client streampb.StreamServiceClient, + ns, streamID, runID string, + sentAt *sync.Map, + receivedTotal *atomic.Int64, + rejections *atomic.Int64, +) ([]time.Duration, int) { + var out []time.Duration + lastSeen := int64(0) + + for ctx.Err() == nil { + resp, err := client.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{ + Namespace: ns, StreamId: streamID, RunId: runID, + FromOffset: lastSeen, WaitNewMessages: true, + }, + }) + if err != nil { + rejections.Add(1) + select { + case <-ctx.Done(): + return out, int(lastSeen) + case <-time.After(50 * time.Millisecond): + } + continue + } + received := time.Now() + fr := resp.GetFrontendResponse() + for range fr.GetMessages() { + if v, ok := sentAt.Load(int(lastSeen)); ok { + out = append(out, received.Sub(v.(time.Time))) + } + lastSeen++ + receivedTotal.Add(1) + } + if next := fr.GetNextOffset(); next > lastSeen { + lastSeen = next + } + } + return out, int(lastSeen) +} + +// TestStreamingComparison runs both designs over the same workload and reports +// them together. This is the artifact the go/no-go decision needs. +func TestStreamingComparison(t *testing.T) { + matrix := shortStreamBaselineMatrix() + if os.Getenv("TEMPORAL_STREAM_BENCH") == "1" { + matrix = fullStreamBaselineMatrix() + } + + var baseline, native []streamBaselineResult + for _, p := range matrix { + t.Run("baseline/"+p.name, func(t *testing.T) { + baseline = append(baseline, runStreamBaseline(t, p)) + }) + t.Run("native/"+p.name, func(t *testing.T) { + native = append(native, runNativeStream(t, p)) + }) + } + + t.Log("Workflow Streams (Signals in, polling Update out) versus native streams") + t.Log("") + t.Log("| scenario | design | msgs | delivered | rejected | wf hist bytes/msg | persist ops/msg | p50 | p99 |") + t.Log("|---|---|---|---|---|---|---|---|---|") + for i := range baseline { + logComparisonRow(t, "signals+update", baseline[i]) + logComparisonRow(t, "native", native[i]) + } + + t.Log("") + for i := range baseline { + t.Logf("%s signals+update raw: persistOps=%d byOp=%v", + baseline[i].params.name, baseline[i].persistenceRequests, baseline[i].persistenceByOp) + t.Logf("%s native raw: persistOps=%d byOp=%v", + native[i].params.name, native[i].persistenceRequests, native[i].persistenceByOp) + } +} + +func logComparisonRow(t *testing.T, design string, r streamBaselineResult) { + perMsg := func(v int64) string { + if r.messagesSent == 0 { + return "n/a" + } + return fmt.Sprintf("%.2f", float64(v)/float64(r.messagesSent)) + } + t.Logf("| %s | %s | %d | %d | %d | %s | %s | %s | %s |", + r.params.name, design, r.messagesSent, r.messagesReceived, r.pollRejections, + r.historyPerMsg(r.historyBytes), perMsg(r.persistenceRequests), + r.latencyP50.Round(time.Millisecond), r.latencyP99.Round(time.Millisecond)) +} diff --git a/tests/testcore/history_task_recorder.go b/tests/testcore/history_task_recorder.go index 380fcdd8ecd..83fb46b1e67 100644 --- a/tests/testcore/history_task_recorder.go +++ b/tests/testcore/history_task_recorder.go @@ -619,3 +619,27 @@ func (r *HistoryTaskRecorder) GetAllHistoryTreeBranches( ) (*persistence.GetAllHistoryTreeBranchesResponse, error) { return r.delegate.GetAllHistoryTreeBranches(ctx, request) } + +// Stream log pass-throughs. This recorder only watches task generation, and a +// stream log write generates none. + +func (r *HistoryTaskRecorder) AppendStreamLog( + ctx context.Context, + request *persistence.InternalAppendStreamLogRequest, +) error { + return r.delegate.AppendStreamLog(ctx, request) +} + +func (r *HistoryTaskRecorder) ReadStreamLog( + ctx context.Context, + request *persistence.InternalReadStreamLogRequest, +) (*persistence.InternalReadStreamLogResponse, error) { + return r.delegate.ReadStreamLog(ctx, request) +} + +func (r *HistoryTaskRecorder) DeleteStreamLogBucket( + ctx context.Context, + request *persistence.InternalDeleteStreamLogBucketRequest, +) error { + return r.delegate.DeleteStreamLogBucket(ctx, request) +} diff --git a/tests/testcore/onebox.go b/tests/testcore/onebox.go index 908241e475e..7d1c812e0e2 100644 --- a/tests/testcore/onebox.go +++ b/tests/testcore/onebox.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "math/rand" "net" "os" @@ -42,7 +43,7 @@ import ( type ( temporalImpl struct { clients - server temporal.Server + servers []temporal.Server logger log.Logger serverConfig *config.Config @@ -218,7 +219,7 @@ func newTemporal(t *testing.T, params *temporalParams) *temporalImpl { return impl } -func (c *temporalImpl) Start() error { +func (c *temporalImpl) Start() (retErr error) { // Worker is optional in functional tests; the other services are always // needed for a usable in-process cluster. services := []primitives.ServiceName{ @@ -231,7 +232,7 @@ func (c *temporalImpl) Start() error { } for _, serviceName := range services { numHosts := len(c.hostsByProtocolByService[grpcProtocol][serviceName].All) - if numHosts != 1 { + if numHosts != 1 && serviceName != primitives.HistoryService { return fmt.Errorf("onebox requires exactly one %s service node, got %d", serviceName, numHosts) } } @@ -291,23 +292,56 @@ func (c *temporalImpl) Start() error { ) defer cleanupHooks() - server, err := temporal.NewServer(options...) - if err != nil { - return fmt.Errorf("unable to construct temporal server: %w", err) - } - - if err := server.Start(); err != nil { - return fmt.Errorf("unable to start temporal server: %w", err) + defer func() { + if retErr != nil { + for _, server := range c.servers { + retErr = multierr.Append(retErr, server.Stop()) + } + c.servers = nil + } + }() + for index, address := range hostsByService[primitives.HistoryService].All { + serverOptions := options + if index > 0 { + // Additional History graphs share membership and persistence while + // binding their own RPC address. Never expose an arbitrary graph's + // CHASM engine through ChasmContext in this multi-host configuration. + extraConfig := cfg + extraConfig.Persistence = copyPersistenceConfig(cfg.Persistence) + extraConfig.Services = maps.Clone(cfg.Services) + bindIP, port := mustSplitHostPort(address) + extraConfig.Services[string(primitives.HistoryService)] = config.Service{ + RPC: config.RPC{BindOnIP: bindIP, GRPCPort: int(port)}, + } + extraHosts := maps.Clone(hostsByService) + historyHosts := extraHosts[primitives.HistoryService] + historyHosts.Self = address + extraHosts[primitives.HistoryService] = historyHosts + serverOptions = []temporal.ServerOption{ + temporal.WithConfig(&extraConfig), + temporal.ForServices([]string{string(primitives.HistoryService)}), + temporal.WithStaticHosts(extraHosts), + } + serverOptions = append(serverOptions, c.baseServerOptions...) + } + server, err := temporal.NewServer(serverOptions...) + if err != nil { + return fmt.Errorf("unable to construct temporal server: %w", err) + } + c.servers = append(c.servers, server) + if err := server.Start(); err != nil { + return fmt.Errorf("unable to start temporal server: %w", err) + } } - c.server = server return nil } func (c *temporalImpl) Stop() error { var errs []error - if c.server != nil { - errs = append(errs, c.server.Stop()) + for index := len(c.servers) - 1; index >= 0; index-- { + errs = append(errs, c.servers[index].Stop()) } + c.servers = nil errs = append(errs, c.close()...) return multierr.Combine(errs...) } From 03a12b1865aa496fe7d60221a2f04a1e1c401ecb Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 14:38:47 -0700 Subject: [PATCH 08/47] Formatted the stream proto and the cross-host test. `make fmt` left these two files dirty, which fails the `fmt` job. The proto also carried a truncated comment for a field that is no longer there. --- chasm/lib/stream/proto/v1/stream_state.proto | 1 - tests/stream_routing_cross_host_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/chasm/lib/stream/proto/v1/stream_state.proto b/chasm/lib/stream/proto/v1/stream_state.proto index 81c878947a6..9343fd47857 100644 --- a/chasm/lib/stream/proto/v1/stream_state.proto +++ b/chasm/lib/stream/proto/v1/stream_state.proto @@ -15,7 +15,6 @@ message StreamState { int64 head_offset = 1; // Truncation floor. Offsets below this are gone. int64 base_offset = 2; - // Chains log nodes so a stale node from an abandoned append is rejected on bool closed = 4; temporal.api.common.v1.Payload close_reason = 5; diff --git a/tests/stream_routing_cross_host_test.go b/tests/stream_routing_cross_host_test.go index 62b50a12090..835e16c545a 100644 --- a/tests/stream_routing_cross_host_test.go +++ b/tests/stream_routing_cross_host_test.go @@ -93,7 +93,7 @@ func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { t.Logf("Confirmed history topology: %v; consumer shard %d on %s, local source shard %d on %s, remote source shard %d on %s", hosts, consumerShard, consumerHost, localSourceShard, consumerHost, remoteSourceShard, remoteHost) findID := func(prefix string, target int32) string { - for i := 0; i < 10000; i++ { + for i := range 10000 { id := fmt.Sprintf("%s-%d", prefix, i) if common.WorkflowIDToHistoryShard(nsID, id, config.HistoryConfig.NumHistoryShards) == target { return id From 083ad122b870a8d953980e0f4130608267b95deb Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 14:38:47 -0700 Subject: [PATCH 09/47] Regenerated the execution store wrappers. The stream log methods on `ExecutionStore` had no fault-injection or telemetry wrapper, so `make go-generate` left the tree dirty. --- .../faultinjection/execution_store_gen.go | 27 +++++ .../telemetry/execution_store_gen.go | 112 ++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/common/persistence/faultinjection/execution_store_gen.go b/common/persistence/faultinjection/execution_store_gen.go index a4eff149d55..1b87effeaa8 100644 --- a/common/persistence/faultinjection/execution_store_gen.go +++ b/common/persistence/faultinjection/execution_store_gen.go @@ -49,6 +49,15 @@ func (d faultInjectionExecutionStore) AppendHistoryNodes(ctx context.Context, re return } +// AppendStreamLog wraps ExecutionStore.AppendStreamLog. +func (d faultInjectionExecutionStore) AppendStreamLog(ctx context.Context, request *_sourcePersistence.InternalAppendStreamLogRequest) (err error) { + err = d.generator.generate("AppendStreamLog", request).inject(func() error { + err = d.ExecutionStore.AppendStreamLog(ctx, request) + return err + }) + return +} + // CompleteHistoryTask wraps ExecutionStore.CompleteHistoryTask. func (d faultInjectionExecutionStore) CompleteHistoryTask(ctx context.Context, request *_sourcePersistence.CompleteHistoryTaskRequest) (err error) { err = d.generator.generate("CompleteHistoryTask", request).inject(func() error { @@ -112,6 +121,15 @@ func (d faultInjectionExecutionStore) DeleteReplicationTaskFromDLQ(ctx context.C return } +// DeleteStreamLogBucket wraps ExecutionStore.DeleteStreamLogBucket. +func (d faultInjectionExecutionStore) DeleteStreamLogBucket(ctx context.Context, request *_sourcePersistence.InternalDeleteStreamLogBucketRequest) (err error) { + err = d.generator.generate("DeleteStreamLogBucket", request).inject(func() error { + err = d.ExecutionStore.DeleteStreamLogBucket(ctx, request) + return err + }) + return +} + // DeleteWorkflowExecution wraps ExecutionStore.DeleteWorkflowExecution. func (d faultInjectionExecutionStore) DeleteWorkflowExecution(ctx context.Context, request *_sourcePersistence.DeleteWorkflowExecutionRequest) (err error) { err = d.generator.generate("DeleteWorkflowExecution", request).inject(func() error { @@ -238,6 +256,15 @@ func (d faultInjectionExecutionStore) ReadHistoryBranch(ctx context.Context, req return } +// ReadStreamLog wraps ExecutionStore.ReadStreamLog. +func (d faultInjectionExecutionStore) ReadStreamLog(ctx context.Context, request *_sourcePersistence.InternalReadStreamLogRequest) (ip1 *_sourcePersistence.InternalReadStreamLogResponse, err error) { + err = d.generator.generate("ReadStreamLog", request).inject(func() error { + ip1, err = d.ExecutionStore.ReadStreamLog(ctx, request) + return err + }) + return +} + // SetWorkflowExecution wraps ExecutionStore.SetWorkflowExecution. func (d faultInjectionExecutionStore) SetWorkflowExecution(ctx context.Context, request *_sourcePersistence.InternalSetWorkflowExecutionRequest) (err error) { err = d.generator.generate("SetWorkflowExecution", request).inject(func() error { diff --git a/common/persistence/telemetry/execution_store_gen.go b/common/persistence/telemetry/execution_store_gen.go index a079fc895b9..896bebc03d5 100644 --- a/common/persistence/telemetry/execution_store_gen.go +++ b/common/persistence/telemetry/execution_store_gen.go @@ -110,6 +110,41 @@ func (d telemetryExecutionStore) AppendHistoryNodes(ctx context.Context, request return } +// AppendStreamLog wraps ExecutionStore.AppendStreamLog. +func (d telemetryExecutionStore) AppendStreamLog(ctx context.Context, request *_sourcePersistence.InternalAppendStreamLogRequest) (err error) { + ctx, span := d.tracer.Start( + ctx, + "persistence.ExecutionStore/AppendStreamLog", + trace.WithAttributes( + attribute.Key("persistence.store").String("ExecutionStore"), + attribute.Key("persistence.method").String("AppendStreamLog"), + )) + defer span.End() + + if deadline, ok := ctx.Deadline(); ok { + span.SetAttributes(attribute.String("deadline", deadline.Format(time.RFC3339Nano))) + span.SetAttributes(attribute.String("timeout", time.Until(deadline).String())) + } + + err = d.ExecutionStore.AppendStreamLog(ctx, request) + if err != nil { + span.RecordError(err) + } + + if d.debugMode { + + requestPayload, err := json.MarshalIndent(request, "", " ") + if err != nil { + d.logger.Error("failed to serialize *_sourcePersistence.InternalAppendStreamLogRequest for OTEL span", tag.Error(err)) + } else { + span.SetAttributes(attribute.Key("persistence.request.payload").String(string(requestPayload))) + } + + } + + return +} + // CompleteHistoryTask wraps ExecutionStore.CompleteHistoryTask. func (d telemetryExecutionStore) CompleteHistoryTask(ctx context.Context, request *_sourcePersistence.CompleteHistoryTaskRequest) (err error) { ctx, span := d.tracer.Start( @@ -362,6 +397,41 @@ func (d telemetryExecutionStore) DeleteReplicationTaskFromDLQ(ctx context.Contex return } +// DeleteStreamLogBucket wraps ExecutionStore.DeleteStreamLogBucket. +func (d telemetryExecutionStore) DeleteStreamLogBucket(ctx context.Context, request *_sourcePersistence.InternalDeleteStreamLogBucketRequest) (err error) { + ctx, span := d.tracer.Start( + ctx, + "persistence.ExecutionStore/DeleteStreamLogBucket", + trace.WithAttributes( + attribute.Key("persistence.store").String("ExecutionStore"), + attribute.Key("persistence.method").String("DeleteStreamLogBucket"), + )) + defer span.End() + + if deadline, ok := ctx.Deadline(); ok { + span.SetAttributes(attribute.String("deadline", deadline.Format(time.RFC3339Nano))) + span.SetAttributes(attribute.String("timeout", time.Until(deadline).String())) + } + + err = d.ExecutionStore.DeleteStreamLogBucket(ctx, request) + if err != nil { + span.RecordError(err) + } + + if d.debugMode { + + requestPayload, err := json.MarshalIndent(request, "", " ") + if err != nil { + d.logger.Error("failed to serialize *_sourcePersistence.InternalDeleteStreamLogBucketRequest for OTEL span", tag.Error(err)) + } else { + span.SetAttributes(attribute.Key("persistence.request.payload").String(string(requestPayload))) + } + + } + + return +} + // DeleteWorkflowExecution wraps ExecutionStore.DeleteWorkflowExecution. func (d telemetryExecutionStore) DeleteWorkflowExecution(ctx context.Context, request *_sourcePersistence.DeleteWorkflowExecutionRequest) (err error) { ctx, span := d.tracer.Start( @@ -915,6 +985,48 @@ func (d telemetryExecutionStore) ReadHistoryBranch(ctx context.Context, request return } +// ReadStreamLog wraps ExecutionStore.ReadStreamLog. +func (d telemetryExecutionStore) ReadStreamLog(ctx context.Context, request *_sourcePersistence.InternalReadStreamLogRequest) (ip1 *_sourcePersistence.InternalReadStreamLogResponse, err error) { + ctx, span := d.tracer.Start( + ctx, + "persistence.ExecutionStore/ReadStreamLog", + trace.WithAttributes( + attribute.Key("persistence.store").String("ExecutionStore"), + attribute.Key("persistence.method").String("ReadStreamLog"), + )) + defer span.End() + + if deadline, ok := ctx.Deadline(); ok { + span.SetAttributes(attribute.String("deadline", deadline.Format(time.RFC3339Nano))) + span.SetAttributes(attribute.String("timeout", time.Until(deadline).String())) + } + + ip1, err = d.ExecutionStore.ReadStreamLog(ctx, request) + if err != nil { + span.RecordError(err) + } + + if d.debugMode { + + requestPayload, err := json.MarshalIndent(request, "", " ") + if err != nil { + d.logger.Error("failed to serialize *_sourcePersistence.InternalReadStreamLogRequest for OTEL span", tag.Error(err)) + } else { + span.SetAttributes(attribute.Key("persistence.request.payload").String(string(requestPayload))) + } + + responsePayload, err := json.MarshalIndent(ip1, "", " ") + if err != nil { + d.logger.Error("failed to serialize *_sourcePersistence.InternalReadStreamLogResponse for OTEL span", tag.Error(err)) + } else { + span.SetAttributes(attribute.Key("persistence.response.payload").String(string(responsePayload))) + } + + } + + return +} + // SetWorkflowExecution wraps ExecutionStore.SetWorkflowExecution. func (d telemetryExecutionStore) SetWorkflowExecution(ctx context.Context, request *_sourcePersistence.InternalSetWorkflowExecutionRequest) (err error) { ctx, span := d.tracer.Start( From 968214778b0189dd76e8a32aa1cc1119cebae980 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 14:38:47 -0700 Subject: [PATCH 10/47] Restored the v1.10 and v0.10 schema manifests. Both were rewritten to point at `add_stream_log`, which dropped `nexus_incoming_services` from the Cassandra upgrade path and `tasks_v2` from the SQLite one. A released version has to stay what it was. --- .../temporal/versioned/v1.10/add_stream_log.cql | 14 -------------- .../temporal/versioned/v1.10/manifest.json | 6 ++---- .../v3/temporal/versioned/v0.10/add_stream_log.sql | 12 ------------ .../v3/temporal/versioned/v0.10/manifest.json | 4 ++-- 4 files changed, 4 insertions(+), 32 deletions(-) delete mode 100644 schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql delete mode 100644 schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql diff --git a/schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql b/schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql deleted file mode 100644 index 9b9f0ad0c6c..00000000000 --- a/schema/cassandra/temporal/versioned/v1.10/add_stream_log.cql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE stream_log ( - shard_id int, - namespace_id uuid, - collection_id text, - bucket bigint, -- one bucket is one partition, bounded by offsets and by bytes - start_offset bigint, -- first offset in this batch, and the key a retry addresses - next_offset bigint, -- exclusive, so a reader knows the span without decoding - data blob, - data_encoding text, - PRIMARY KEY ((shard_id, namespace_id, collection_id, bucket), start_offset) -) WITH CLUSTERING ORDER BY (start_offset ASC) - AND COMPACTION = { - 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' -}; diff --git a/schema/cassandra/temporal/versioned/v1.10/manifest.json b/schema/cassandra/temporal/versioned/v1.10/manifest.json index 5afd4458e17..bab20aee84f 100644 --- a/schema/cassandra/temporal/versioned/v1.10/manifest.json +++ b/schema/cassandra/temporal/versioned/v1.10/manifest.json @@ -1,8 +1,6 @@ { "CurrVersion": "1.10", "MinCompatibleVersion": "1.0", - "Description": "Add stream_log table for server-side streams", - "SchemaUpdateCqlFiles": [ - "add_stream_log.cql" - ] + "Description": "create nexus_incoming_services table", + "SchemaUpdateCqlFiles": ["nexus_incoming_services.cql"] } diff --git a/schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql b/schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql deleted file mode 100644 index 6c523d75ba2..00000000000 --- a/schema/sqlite/v3/temporal/versioned/v0.10/add_stream_log.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE stream_log ( - shard_id INT NOT NULL, - namespace_id BINARY(16) NOT NULL, - collection_id VARCHAR(255) NOT NULL, - bucket BIGINT NOT NULL, - start_offset BIGINT NOT NULL, - -- - next_offset BIGINT NOT NULL, - data MEDIUMBLOB NOT NULL, - data_encoding VARCHAR(16) NOT NULL, - PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) -); diff --git a/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json b/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json index aa3cfecdb23..7bb9a2e7313 100644 --- a/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json +++ b/schema/sqlite/v3/temporal/versioned/v0.10/manifest.json @@ -1,8 +1,8 @@ { "CurrVersion": "0.10", "MinCompatibleVersion": "0.1", - "Description": "Add stream_log table for server-side streams", + "Description": "Adds tasks_v2 table for fairness tasks", "SchemaUpdateCqlFiles": [ - "add_stream_log.sql" + "tasks_v2.sql" ] } From 197f87854cd8be01027bb6035b123d7a743b1320 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 14:38:51 -0700 Subject: [PATCH 11/47] Added the stream_log schema as a new version per database. Cassandra goes to 1.14, SQLite to 0.12, MySQL and PostgreSQL to 1.20. The MySQL and PostgreSQL stores already query `stream_log`, so their base schema needed the table too. --- schema/cassandra/temporal/schema.cql | 2 +- .../temporal/versioned/v1.14/add_stream_log.cql | 14 ++++++++++++++ .../temporal/versioned/v1.14/manifest.json | 6 ++++++ schema/cassandra/version.go | 2 +- schema/mysql/v8/temporal/schema.sql | 13 +++++++++++++ .../v8/temporal/versioned/v1.20/add_stream_log.sql | 12 ++++++++++++ .../v8/temporal/versioned/v1.20/manifest.json | 8 ++++++++ schema/mysql/v8/version.go | 2 +- schema/postgresql/v12/temporal/schema.sql | 13 +++++++++++++ .../temporal/versioned/v1.20/add_stream_log.sql | 12 ++++++++++++ .../v12/temporal/versioned/v1.20/manifest.json | 8 ++++++++ schema/postgresql/v12/version.go | 2 +- .../v3/temporal/versioned/v0.12/add_stream_log.sql | 12 ++++++++++++ .../v3/temporal/versioned/v0.12/manifest.json | 8 ++++++++ schema/sqlite/version.go | 2 +- 15 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql create mode 100644 schema/cassandra/temporal/versioned/v1.14/manifest.json create mode 100644 schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql create mode 100644 schema/mysql/v8/temporal/versioned/v1.20/manifest.json create mode 100644 schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql create mode 100644 schema/postgresql/v12/temporal/versioned/v1.20/manifest.json create mode 100644 schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql create mode 100644 schema/sqlite/v3/temporal/versioned/v0.12/manifest.json diff --git a/schema/cassandra/temporal/schema.cql b/schema/cassandra/temporal/schema.cql index eada7b95184..6a39ed08eef 100644 --- a/schema/cassandra/temporal/schema.cql +++ b/schema/cassandra/temporal/schema.cql @@ -254,7 +254,7 @@ CREATE TABLE stream_log ( collection_id text, bucket bigint, -- one bucket is one partition, bounded by offsets and by bytes start_offset bigint, -- first offset in this batch, and the key a retry addresses - next_offset bigint, -- exclusive, so a reader knows the span without decoding + next_offset bigint, -- exclusive, so a reader knows the span without decoding data blob, data_encoding text, PRIMARY KEY ((shard_id, namespace_id, collection_id, bucket), start_offset) diff --git a/schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql b/schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql new file mode 100644 index 00000000000..f4b59e29a9d --- /dev/null +++ b/schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql @@ -0,0 +1,14 @@ +CREATE TABLE stream_log ( + shard_id int, + namespace_id uuid, + collection_id text, + bucket bigint, -- one bucket is one partition, bounded by offsets and by bytes + start_offset bigint, -- first offset in this batch, and the key a retry addresses + next_offset bigint, -- exclusive, so a reader knows the span without decoding + data blob, + data_encoding text, + PRIMARY KEY ((shard_id, namespace_id, collection_id, bucket), start_offset) +) WITH CLUSTERING ORDER BY (start_offset ASC) + AND COMPACTION = { + 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' +}; diff --git a/schema/cassandra/temporal/versioned/v1.14/manifest.json b/schema/cassandra/temporal/versioned/v1.14/manifest.json new file mode 100644 index 00000000000..2df597d66c4 --- /dev/null +++ b/schema/cassandra/temporal/versioned/v1.14/manifest.json @@ -0,0 +1,6 @@ +{ + "CurrVersion": "1.14", + "MinCompatibleVersion": "1.0", + "Description": "Adds stream_log table for server-side streams", + "SchemaUpdateCqlFiles": ["add_stream_log.cql"] +} diff --git a/schema/cassandra/version.go b/schema/cassandra/version.go index 9f666a77d4c..af3774cbc9f 100644 --- a/schema/cassandra/version.go +++ b/schema/cassandra/version.go @@ -3,4 +3,4 @@ package cassandra // NOTE: whenever there is a new database schema update, plz update the following versions // Version is the Cassandra database release version -const Version = "1.13" +const Version = "1.14" diff --git a/schema/mysql/v8/temporal/schema.sql b/schema/mysql/v8/temporal/schema.sql index 56448ce0e90..24fa603c9cf 100644 --- a/schema/mysql/v8/temporal/schema.sql +++ b/schema/mysql/v8/temporal/schema.sql @@ -322,6 +322,19 @@ CREATE TABLE history_node ( PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); +CREATE TABLE stream_log ( + shard_id INT NOT NULL, + namespace_id BINARY(16) NOT NULL, + collection_id VARCHAR(255) NOT NULL, + bucket BIGINT NOT NULL, + start_offset BIGINT NOT NULL, + -- + next_offset BIGINT NOT NULL, + data MEDIUMBLOB NOT NULL, + data_encoding VARCHAR(16) NOT NULL, + PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) +); + -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INT NOT NULL, diff --git a/schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql b/schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql new file mode 100644 index 00000000000..8eefbe40b8e --- /dev/null +++ b/schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql @@ -0,0 +1,12 @@ +CREATE TABLE stream_log ( + shard_id INT NOT NULL, + namespace_id BINARY(16) NOT NULL, + collection_id VARCHAR(255) NOT NULL, + bucket BIGINT NOT NULL, + start_offset BIGINT NOT NULL, + -- + next_offset BIGINT NOT NULL, + data MEDIUMBLOB NOT NULL, + data_encoding VARCHAR(16) NOT NULL, + PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) +); diff --git a/schema/mysql/v8/temporal/versioned/v1.20/manifest.json b/schema/mysql/v8/temporal/versioned/v1.20/manifest.json new file mode 100644 index 00000000000..317a9f7c755 --- /dev/null +++ b/schema/mysql/v8/temporal/versioned/v1.20/manifest.json @@ -0,0 +1,8 @@ +{ + "CurrVersion": "1.20", + "MinCompatibleVersion": "1.0", + "Description": "Adds stream_log table for server-side streams", + "SchemaUpdateCqlFiles": [ + "add_stream_log.sql" + ] +} diff --git a/schema/mysql/v8/version.go b/schema/mysql/v8/version.go index a7cfc51791f..f347ffb580d 100644 --- a/schema/mysql/v8/version.go +++ b/schema/mysql/v8/version.go @@ -3,7 +3,7 @@ package v8 // NOTE: whenever there is a new database schema update, plz update the following versions // Version is the MySQL database release version -const Version = "1.19" +const Version = "1.20" // VisibilityVersion is the MySQL visibility database release version const VisibilityVersion = "1.14" diff --git a/schema/postgresql/v12/temporal/schema.sql b/schema/postgresql/v12/temporal/schema.sql index b0f4874b3cd..bf33f9a4b88 100644 --- a/schema/postgresql/v12/temporal/schema.sql +++ b/schema/postgresql/v12/temporal/schema.sql @@ -322,6 +322,19 @@ CREATE TABLE history_node ( PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); +CREATE TABLE stream_log ( + shard_id INTEGER NOT NULL, + namespace_id BYTEA NOT NULL, + collection_id VARCHAR(255) NOT NULL, + bucket BIGINT NOT NULL, + start_offset BIGINT NOT NULL, + -- + next_offset BIGINT NOT NULL, + data BYTEA NOT NULL, + data_encoding VARCHAR(16) NOT NULL, + PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) +); + -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INTEGER NOT NULL, diff --git a/schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql b/schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql new file mode 100644 index 00000000000..a263d4adf09 --- /dev/null +++ b/schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql @@ -0,0 +1,12 @@ +CREATE TABLE stream_log ( + shard_id INTEGER NOT NULL, + namespace_id BYTEA NOT NULL, + collection_id VARCHAR(255) NOT NULL, + bucket BIGINT NOT NULL, + start_offset BIGINT NOT NULL, + -- + next_offset BIGINT NOT NULL, + data BYTEA NOT NULL, + data_encoding VARCHAR(16) NOT NULL, + PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) +); diff --git a/schema/postgresql/v12/temporal/versioned/v1.20/manifest.json b/schema/postgresql/v12/temporal/versioned/v1.20/manifest.json new file mode 100644 index 00000000000..317a9f7c755 --- /dev/null +++ b/schema/postgresql/v12/temporal/versioned/v1.20/manifest.json @@ -0,0 +1,8 @@ +{ + "CurrVersion": "1.20", + "MinCompatibleVersion": "1.0", + "Description": "Adds stream_log table for server-side streams", + "SchemaUpdateCqlFiles": [ + "add_stream_log.sql" + ] +} diff --git a/schema/postgresql/v12/version.go b/schema/postgresql/v12/version.go index 77e0f761e09..6965d0ca879 100644 --- a/schema/postgresql/v12/version.go +++ b/schema/postgresql/v12/version.go @@ -4,7 +4,7 @@ package v12 // Version is the Postgres database release version // Temporal supports both MySQL and Postgres officially, so upgrade should be performed for both MySQL and Postgres -const Version = "1.19" +const Version = "1.20" // VisibilityVersion is the Postgres visibility database release version // Temporal supports both MySQL and Postgres officially, so upgrade should be performed for both MySQL and Postgres diff --git a/schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql b/schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql new file mode 100644 index 00000000000..6c523d75ba2 --- /dev/null +++ b/schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql @@ -0,0 +1,12 @@ +CREATE TABLE stream_log ( + shard_id INT NOT NULL, + namespace_id BINARY(16) NOT NULL, + collection_id VARCHAR(255) NOT NULL, + bucket BIGINT NOT NULL, + start_offset BIGINT NOT NULL, + -- + next_offset BIGINT NOT NULL, + data MEDIUMBLOB NOT NULL, + data_encoding VARCHAR(16) NOT NULL, + PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) +); diff --git a/schema/sqlite/v3/temporal/versioned/v0.12/manifest.json b/schema/sqlite/v3/temporal/versioned/v0.12/manifest.json new file mode 100644 index 00000000000..dc05b73ee7a --- /dev/null +++ b/schema/sqlite/v3/temporal/versioned/v0.12/manifest.json @@ -0,0 +1,8 @@ +{ + "CurrVersion": "0.12", + "MinCompatibleVersion": "0.1", + "Description": "Adds stream_log table for server-side streams", + "SchemaUpdateCqlFiles": [ + "add_stream_log.sql" + ] +} diff --git a/schema/sqlite/version.go b/schema/sqlite/version.go index c0d3a2ef880..93fa1a105a9 100644 --- a/schema/sqlite/version.go +++ b/schema/sqlite/version.go @@ -1,7 +1,7 @@ package sqlite // Version is the SQLite database release version -const Version = "0.11" +const Version = "0.12" // VisibilityVersion is the SQLite visibility database release version const VisibilityVersion = "0.1" From fac8b43dee45a2e374867a1933e7113b9a2910a4 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 14:38:57 -0700 Subject: [PATCH 12/47] Made the publish cost measurement opt-in. It holds one cluster per arm for the whole run, which outlasts the dedicated pool and parks the functional suite until the 35 minute timeout. --- tests/stream_publish_cost_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/stream_publish_cost_test.go b/tests/stream_publish_cost_test.go index e76f1ea5538..4b3b416faf3 100644 --- a/tests/stream_publish_cost_test.go +++ b/tests/stream_publish_cost_test.go @@ -2,6 +2,7 @@ package tests import ( "fmt" + "os" "strings" "testing" "time" @@ -64,6 +65,11 @@ func TestStreamPublishHistoryCost(t *testing.T) { if testing.Short() { t.Skip("measurement, not a correctness check") } + // Each arm holds a cluster of its own until the whole test returns, so ten + // arms outlast the dedicated pool. Opt in like the other measurements. + if os.Getenv("TEMPORAL_STREAM_BENCH") != "1" { + t.Skip("set TEMPORAL_STREAM_BENCH=1 to run") + } arms := []publishCostArm{ // The control. Everything is differenced against this. From 8f255cafba4777a926af15bc7e79a9b7e66b038c Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 14:57:37 -0700 Subject: [PATCH 13/47] Fixed the MySQL stream log read bounds. Three placeholders carried a trailing digit. MySQL connections interpolate parameters, so the digit was appended to each value and a read returned batches past the offset it asked for. --- .../sql/sqlplugin/mysql/stream_log.go | 4 ++-- .../tests/history_store_stream_log.go | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/common/persistence/sql/sqlplugin/mysql/stream_log.go b/common/persistence/sql/sqlplugin/mysql/stream_log.go index c1bb005192e..3c1fc4aa158 100644 --- a/common/persistence/sql/sqlplugin/mysql/stream_log.go +++ b/common/persistence/sql/sqlplugin/mysql/stream_log.go @@ -23,8 +23,8 @@ const ( `FROM stream_log ` + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` + `AND start_offset >= COALESCE((SELECT MAX(start_offset) FROM stream_log ` + - `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? AND start_offset <= ?), ?0) ` + - `AND start_offset < ?1 ORDER BY start_offset LIMIT ?2` + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? AND start_offset <= ?), ?) ` + + `AND start_offset < ? ORDER BY start_offset LIMIT ?` deleteStreamLogQuery = `DELETE FROM stream_log ` + `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ?` diff --git a/common/persistence/tests/history_store_stream_log.go b/common/persistence/tests/history_store_stream_log.go index 9b59c57f242..dfcee03b08f 100644 --- a/common/persistence/tests/history_store_stream_log.go +++ b/common/persistence/tests/history_store_stream_log.go @@ -383,3 +383,25 @@ func (s *HistoryEventsSuite) TestStreamLogReadFindsTheBatchHoldingAnOffset() { s.streamRead(collectionID, bucketSize, 10, 11), ) } + +// TestStreamLogReadStopsAtTheRequestedEnd checks that the exclusive end of a +// read is honoured inside a bucket. +// +// A bucket is one partition, so the bucket walk alone already bounds a read to +// the buckets the range touches. Nothing above the store trims what comes back, +// which makes the end offset the store's own responsibility and worth pinning +// separately from the floor lookup. +func (s *HistoryEventsSuite) TestStreamLogReadStopsAtTheRequestedEnd() { + collectionID := uuid.NewString() + const bucketSize = 100 + + s.streamAppend(collectionID, bucketSize, 0, 10, "first") + s.streamAppend(collectionID, bucketSize, 10, 10, "second") + s.streamAppend(collectionID, bucketSize, 20, 10, "third") + + s.Equal( + []string{"first", "second"}, + s.streamRead(collectionID, bucketSize, 0, 15), + "a read must stop at its end offset even when the bucket holds more", + ) +} From d56c98673e23b56030f7d89c5efc48648fb63764 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 15:04:02 -0700 Subject: [PATCH 14/47] Regenerated the stream state proto. The orphan comment dropped from the `.proto` still rode the generated field, which leaves `make proto` dirty. --- chasm/lib/stream/gen/streampb/v1/stream_state.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go index bec3fb2467d..1beb1584af8 100644 --- a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go @@ -32,7 +32,7 @@ type StreamState struct { // Visibility frontier. Readers never observe an offset at or past this. HeadOffset int64 `protobuf:"varint,1,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` // Truncation floor. Offsets below this are gone. - BaseOffset int64 `protobuf:"varint,2,opt,name=base_offset,json=baseOffset,proto3" json:"base_offset,omitempty"` // Chains log nodes so a stale node from an abandoned append is rejected on + BaseOffset int64 `protobuf:"varint,2,opt,name=base_offset,json=baseOffset,proto3" json:"base_offset,omitempty"` Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` CloseReason *v1.Payload `protobuf:"bytes,5,opt,name=close_reason,json=closeReason,proto3" json:"close_reason,omitempty"` // Bumped on ownership change so a stale producer's write fails. From 989b303328c9bd4696abebffe4c1747db8ee21b2 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 15:11:36 -0700 Subject: [PATCH 15/47] Re-pinned api-go to the branch head. The `api-go` branch was rebased onto its upstream `main`, which pulls in newer gRPC and OpenTelemetry requirements through the module graph. --- go.mod | 20 ++++++++++---------- go.sum | 42 ++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index b103fe08665..555e4d15873 100644 --- a/go.mod +++ b/go.mod @@ -63,8 +63,8 @@ require ( go.opentelemetry.io/otel/exporters/prometheus v0.56.0 go.opentelemetry.io/otel/log v0.20.0 go.opentelemetry.io/otel/metric v1.44.0 - go.opentelemetry.io/otel/sdk v1.43.0 - go.opentelemetry.io/otel/sdk/metric v1.43.0 + go.opentelemetry.io/otel/sdk v1.44.0 + go.opentelemetry.io/otel/sdk/metric v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 go.temporal.io/api v1.63.5 go.temporal.io/auto-scaled-workers v0.0.0-20260811170210-91f6fe1d10ab @@ -81,7 +81,7 @@ require ( golang.org/x/text v0.40.0 golang.org/x/time v0.15.0 google.golang.org/api v0.276.0 - google.golang.org/grpc v1.80.0 + google.golang.org/grpc v1.83.1 google.golang.org/protobuf v1.36.11 gopkg.in/validator.v2 v2.0.1 gopkg.in/yaml.v3 v3.0.1 @@ -109,7 +109,7 @@ require ( ) require ( - cel.dev/expr v0.25.1 // indirect + cel.dev/expr v0.25.2 // indirect cloud.google.com/go v0.123.0 // indirect; indirect e cloud.google.com/go/auth v0.20.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect @@ -120,7 +120,7 @@ require ( cloud.google.com/go/run v1.19.0 // indirect dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.2.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.56.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.56.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -199,14 +199,14 @@ require ( github.com/sirupsen/logrus v1.9.4 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.7.0 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/twmb/murmur3 v1.1.8 // indirect github.com/uber-common/bark v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 go.opentelemetry.io/proto/otlp v1.10.0 @@ -219,8 +219,8 @@ require ( golang.org/x/sys v0.47.0 // indirect golang.org/x/term v0.45.0 // indirect google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/api v0.35.4 // indirect @@ -240,4 +240,4 @@ require ( tool golang.org/x/perf/cmd/benchstat -replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260914225835-6facdf1d8f4d +replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260916223558-5ea9c676ee47 diff --git a/go.sum b/go.sum index e974110cecf..deef4d76eb0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= -cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs= +cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= @@ -31,8 +31,8 @@ filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 h1:l7+6kwRMJNwdCvYdDl7Eax+wzEYHSnNY7zrrfbhDdTA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0/go.mod h1:pJTkW8hEUIIi3Pf65lPZOnn4Y81yCllX6IWk2jNXdkM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.56.0 h1:O2sXMyJh8b7devAGdE+163xtRurt0RVpB6DIzX5vGfg= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.56.0/go.mod h1:hEpiGU18xf70qb3jbTcIggWAiEfX/cOIVc2OTe4OegA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.56.0 h1:ZIT85vKP7LBS84XJ0WdJ3dPOX3iz4j3c0+lpajGQMyo= @@ -321,8 +321,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moedash/api-go v1.63.6-0.20260914225835-6facdf1d8f4d h1:4nOuHYutFB4EApEYChFZBkT0tmY2nTWcQCVXFCSMMis= -github.com/moedash/api-go v1.63.6-0.20260914225835-6facdf1d8f4d/go.mod h1:SrlW2JMwVlDP4nRWSNznUFqnSHd+YeMDS1BkYo63HCQ= +github.com/moedash/api-go v1.63.6-0.20260916223558-5ea9c676ee47 h1:oSariLfjiF3pYlvthha99PG56vUU36zeU3GdDJpRU9M= +github.com/moedash/api-go v1.63.6-0.20260916223558-5ea9c676ee47/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -384,8 +384,8 @@ github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= -github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/spiffe/go-spiffe/v2 v2.7.0 h1:uXe1MflJoHw58wAUvxVlcM7WpKtijWG7I1UidcGh6g4= +github.com/spiffe/go-spiffe/v2 v2.7.0/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -445,8 +445,8 @@ go.opentelemetry.io/collector/internal/testutil v0.150.0 h1:J4PLQGPfbLVaL5eI1aMc go.opentelemetry.io/collector/internal/testutil v0.150.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE= go.opentelemetry.io/collector/pdata v1.56.0 h1:W+QAfN2Iz8SNss1T5JNzRWFnw+7oP1vXBQH9ZuOJkXY= go.opentelemetry.io/collector/pdata v1.56.0/go.mod h1:usR9utboXufbD1rp1oJy+3smQXXpZ+CsI3WN7QsiOs0= -go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU= -go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs= +go.opentelemetry.io/contrib/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9dPRWYAAbxhRCrKXPw= +go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= @@ -467,10 +467,12 @@ go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJ go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= @@ -632,12 +634,12 @@ google.golang.org/api v0.276.0 h1:nVArUtfLEihtW+b0DdcqRGK1xoEm2+ltAihyztq7MKY= google.golang.org/api v0.276.0/go.mod h1:Fnag/EWUPIcJXuIkP1pjoTgS5vdxlk3eeemL7Do6bvw= google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529 h1:QoMBg0moLIlB/eucPzc+ID5SgPZWuirtjAn3l8nW2Dg= google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529/go.mod h1:EjLmDZ8liSLBrCTK5vP+bGIxRQHE3ovGvOI0CzGk1PI= -google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 h1:zUWMZsvo/IJcD1t6MNCPO/azZTwz0TvwCBqr5aifoVY= -google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529/go.mod h1:a5OGAgyRr4lqco7AG9hQM9Fwh0N2ZV4grR0eXFEsXQg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 h1:XF8+t6QQiS0o9ArVan/HW8Q7cycNPGsJf6GA2nXxYAg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y= +google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From c9afe90a0a4a1c4c4396a2b17baed133a54cf806 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 16:47:32 -0700 Subject: [PATCH 16/47] Regenerated the getproto import map. The new api pin ships the nexusoperation proto, so the generated import map has to list it. --- cmd/tools/getproto/files.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/tools/getproto/files.go b/cmd/tools/getproto/files.go index 04b99e22706..3b1e31ca412 100644 --- a/cmd/tools/getproto/files.go +++ b/cmd/tools/getproto/files.go @@ -21,6 +21,7 @@ import ( history "go.temporal.io/api/history/v1" namespace "go.temporal.io/api/namespace/v1" nexus "go.temporal.io/api/nexus/v1" + nexusoperation "go.temporal.io/api/nexusoperation/v1" protocol "go.temporal.io/api/protocol/v1" query "go.temporal.io/api/query/v1" replication "go.temporal.io/api/replication/v1" @@ -83,6 +84,7 @@ func init() { importMap["temporal/api/history/v1/message.proto"] = history.File_temporal_api_history_v1_message_proto importMap["temporal/api/namespace/v1/message.proto"] = namespace.File_temporal_api_namespace_v1_message_proto importMap["temporal/api/nexus/v1/message.proto"] = nexus.File_temporal_api_nexus_v1_message_proto + importMap["temporal/api/nexusoperation/v1/message.proto"] = nexusoperation.File_temporal_api_nexusoperation_v1_message_proto importMap["temporal/api/protocol/v1/message.proto"] = protocol.File_temporal_api_protocol_v1_message_proto importMap["temporal/api/query/v1/message.proto"] = query.File_temporal_api_query_v1_message_proto importMap["temporal/api/replication/v1/message.proto"] = replication.File_temporal_api_replication_v1_message_proto From 8bb1ad0bed3fa119142471feb38c61ef6d7e997a Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 16:48:29 -0700 Subject: [PATCH 17/47] Tidied the mixed brain test module. The module pins its own grpc version and has to track the root module after the api pin bumped it. --- tests/mixedbrain/go.mod | 6 +++--- tests/mixedbrain/go.sum | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/mixedbrain/go.mod b/tests/mixedbrain/go.mod index dde79a7698e..c87866e7f25 100644 --- a/tests/mixedbrain/go.mod +++ b/tests/mixedbrain/go.mod @@ -9,7 +9,7 @@ require ( github.com/temporalio/omes v0.0.0-20260529203146-c6ee1f56c726 go.temporal.io/api v1.63.5 go.temporal.io/server v0.0.0-00010101000000-000000000000 - google.golang.org/grpc v1.80.0 + google.golang.org/grpc v1.83.1 google.golang.org/protobuf v1.36.11 ) @@ -26,8 +26,8 @@ require ( golang.org/x/net v0.57.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.40.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/tests/mixedbrain/go.sum b/tests/mixedbrain/go.sum index f235f9afc83..e04f1505577 100644 --- a/tests/mixedbrain/go.sum +++ b/tests/mixedbrain/go.sum @@ -49,10 +49,10 @@ go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.temporal.io/api v1.63.5 h1:c11+kPYHkXXL3UiShPdbMD+xtvqGsbTibUA9ypmiCa4= @@ -71,12 +71,12 @@ golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 h1:zUWMZsvo/IJcD1t6MNCPO/azZTwz0TvwCBqr5aifoVY= -google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529/go.mod h1:a5OGAgyRr4lqco7AG9hQM9Fwh0N2ZV4grR0eXFEsXQg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 h1:XF8+t6QQiS0o9ArVan/HW8Q7cycNPGsJf6GA2nXxYAg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y= +google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 04d7060616d03cb70c4872236c0488ce2a52b694 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 16:54:47 -0700 Subject: [PATCH 18/47] Fixed the build-tests target for the mixed brain module. The mixed brain tests live in their own module, so the root go test invocation cannot reach them. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ccb299f7f22..f706de53944 100644 --- a/Makefile +++ b/Makefile @@ -529,7 +529,8 @@ clean-test-output: build-tests: @printf $(COLOR) "Build tests..." - @CGO_ENABLED=$(CGO_ENABLED) go test $(TEST_TAG_FLAG) -exec="true" -count=0 $(TEST_DIRS) + @CGO_ENABLED=$(CGO_ENABLED) go test $(TEST_TAG_FLAG) -exec="true" -count=0 $(filter-out $(MIXED_BRAIN_TEST_ROOT)%,$(TEST_DIRS)) + @cd $(MIXED_BRAIN_TEST_ROOT) && CGO_ENABLED=1 go test $(TEST_TAG_FLAG) -exec="true" -count=0 ./... unit-test: clean-test-output @printf $(COLOR) "Run unit tests..." From e33d2d4840afa878ab945f398e6ed297e4804803 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 17:06:43 -0700 Subject: [PATCH 19/47] Made the otel handler test tolerate empty exemplar slices. The metric SDK returns a nil slice when no exemplar was recorded and an empty one when the reservoir was already allocated, which made the comparison flaky. --- common/metrics/otel_metrics_handler_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/metrics/otel_metrics_handler_test.go b/common/metrics/otel_metrics_handler_test.go index e186e79ec9f..9cfc5aa0acb 100644 --- a/common/metrics/otel_metrics_handler_test.go +++ b/common/metrics/otel_metrics_handler_test.go @@ -195,6 +195,10 @@ func TestMeter(t *testing.T) { cmpopts.IgnoreFields(metricdata.DataPoint[int64]{}, "StartTime", "Time"), cmpopts.IgnoreFields(metricdata.DataPoint[float64]{}, "StartTime", "Time"), cmpopts.IgnoreFields(metricdata.HistogramDataPoint[int64]{}, "StartTime", "Time", "Bounds"), + // The SDK hands back either a nil or an allocated-but-empty exemplar + // slice depending on whether its reservoir was ever touched, so the + // two have to compare equal here. + cmpopts.EquateEmpty(), ); diff != "" { t.Errorf("mismatch (-want, +got):\n%s", diff) } @@ -263,6 +267,7 @@ func TestMeter_TimerInSeconds(t *testing.T) { return a1.Equals(&a2) }), cmpopts.IgnoreFields(metricdata.HistogramDataPoint[float64]{}, "StartTime", "Time", "Bounds"), + cmpopts.EquateEmpty(), ); diff != "" { t.Errorf("mismatch (-want, +got):\n%s", diff) } From ed1d6126b140617303b1a106a212539313741209 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 17:11:07 -0700 Subject: [PATCH 20/47] Cleared the stale test log between local test runs. The log is appended to, so a failure from an earlier run kept failing every later verification. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index f706de53944..5f5625487a2 100644 --- a/Makefile +++ b/Makefile @@ -522,9 +522,12 @@ workflowcheck: $(WORKFLOWCHECK) check: lint shell-check ##### Tests ##### +# verify-test-log reads the whole file, so a log left behind by an earlier run +# would be judged as if it belonged to this one. clean-test-output: @printf $(COLOR) "Delete test output..." @rm -rf $(TEST_OUTPUT_ROOT) + @rm -f test.log @go clean -testcache build-tests: From 95719ede7adfc83d961c149a23f088f98278e71d Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Wed, 16 Sep 2026 17:35:27 -0700 Subject: [PATCH 21/47] Required protobuf payloads from the system Nexus endpoint. Taken from upstream temporalio/temporal #11906. The api version this branch pins rejects a system payload that is not binary/protobuf, so the endpoint has to refuse one instead of labelling it. --- chasm/lib/tests/nexus_service.go | 20 +++++++++++++++++--- service/history/handler.go | 16 ++++++++++++++++ service/history/handler_test.go | 28 ++++++++++++++++++++++++++++ tests/nexus_workflow_test.go | 19 +++++++++++++++---- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/chasm/lib/tests/nexus_service.go b/chasm/lib/tests/nexus_service.go index 0ae0d42905f..386073e1ec6 100644 --- a/chasm/lib/tests/nexus_service.go +++ b/chasm/lib/tests/nexus_service.go @@ -9,9 +9,14 @@ import ( "go.temporal.io/server/common/payload" ) -var TestOperation = nexus.NewSyncOperation("TestOperation", func(ctx context.Context, input string, options nexus.StartOperationOptions) (string, error) { - return "Hello, " + input, nil -}) +// TestOperation returns a failurepb.Failure so that it is a proper protobuf type. Its used for +// testing the System Nexus Endpoint, which only accepts protobuf-encoded payloads. +var TestOperation = nexus.NewSyncOperation( + "TestOperation", + func(ctx context.Context, input string, options nexus.StartOperationOptions) (*commonpb.DataBlob, error) { + d := []byte("Hello, " + input) + return &commonpb.DataBlob{Data: d}, nil + }) // TestOperationWithPayload is identical to TestOperation, except its response embeds a // nested *commonpb.Payload. It exists to exercise the commonnexus.SystemPayloadMetadataKey @@ -20,10 +25,18 @@ var TestOperationWithPayload = nexus.NewSyncOperation("TestOperationWithPayload" return &commonpb.Payloads{Payloads: []*commonpb.Payload{payload.EncodeString("Hello, " + input)}}, nil }) +// TestOperationStringOutput returns a string, which the data converter encodes as JSON rather +// than protobuf. It exists to exercise the System Nexus Endpoint's rejection of non-protobuf +// responses in service/history/handler.go's StartNexusOperation. +var TestOperationStringOutput = nexus.NewSyncOperation("TestOperationStringOutput", func(ctx context.Context, input string, options nexus.StartOperationOptions) (string, error) { + return "Hello, " + input, nil +}) + func NewTestServiceNexusService() *nexus.Service { service := nexus.NewService("TestService") service.MustRegister(TestOperation) service.MustRegister(TestOperationWithPayload) + service.MustRegister(TestOperationStringOutput) return service } @@ -43,5 +56,6 @@ func NewTestServiceNexusServiceProcessor() *chasm.NexusServiceProcessor { sp := chasm.NewNexusServiceProcessor("TestService") sp.MustRegisterOperation("TestOperation", chasm.NewRegisterableNexusOperationProcessor(testOperationProcessor{})) sp.MustRegisterOperation("TestOperationWithPayload", chasm.NewRegisterableNexusOperationProcessor(testOperationProcessor{})) + sp.MustRegisterOperation("TestOperationStringOutput", chasm.NewRegisterableNexusOperationProcessor(testOperationProcessor{})) return sp } diff --git a/service/history/handler.go b/service/history/handler.go index d25e52b32bc..d5012a959e5 100644 --- a/service/history/handler.go +++ b/service/history/handler.go @@ -2540,6 +2540,9 @@ func (h *Handler) UnpauseWorkflowExecution(ctx context.Context, request *history return unpauseResp, nil } +// StartNexusOperation is the History Service's StartNexusOperation endpoint is for dispatching requests +// to the System Nexus Endpoint, distinct from the Frontend Service's `nexus_handler.go` which +// starts Nexus operations by sending Nexus tasks directly to workers via the Matching Service. func (h *Handler) StartNexusOperation( ctx context.Context, req *historyservice.StartNexusOperationRequest, @@ -2602,16 +2605,29 @@ func (h *Handler) StartNexusOperation( h.logger.Error("failed to encode payload", tag.Error(err), tag.RequestID(requestID)) return nil, serviceerror.NewInternal("internal error (request ID: " + requestID + ")") } + var payload *commonpb.Payload if len(ps.GetPayloads()) == 1 { payload = ps.GetPayloads()[0] } + // Responses from the System Nexus Endpoint are server generated, so we must mark them as system payloads. if payload != nil { if payload.Metadata == nil { payload.Metadata = make(map[string][]byte, 1) } + + // For now, we require all responess from the System Nexus Endpoint be protobufs. + encoding := string(payload.Metadata["encoding"]) + if encoding != "binary/protobuf" { + return nil, serviceerror.NewFailedPreconditionf("system payload must be encoded as binary/protobuf but got %s", encoding) + } + if _, ok := payload.Metadata["messageType"]; !ok { + return nil, serviceerror.NewFailedPrecondition("system payload missing messageType metadata key") + } + payload.Metadata[commonnexus.SystemPayloadMetadataKey] = []byte("true") } + response.Variant = &nexuspb.StartOperationResponse_SyncSuccess{ SyncSuccess: &nexuspb.StartOperationResponse_Sync{ Payload: payload, diff --git a/service/history/handler_test.go b/service/history/handler_test.go index e0c3df1b9f0..3394f59206e 100644 --- a/service/history/handler_test.go +++ b/service/history/handler_test.go @@ -450,3 +450,31 @@ func TestStartNexusOperation_SystemNexusEndpointPayloadMetadataFlag(t *testing.T }) } } + +func TestStartNexusOperation_SystemNexusEndpointRejectsNonProtoResponse(t *testing.T) { + registry := nexus.NewServiceRegistry() + registry.MustRegister(chasmtests.NewTestServiceNexusService()) + nexusHandler, err := registry.NewHandler() + require.NoError(t, err) + + h := Handler{ + logger: log.NewNoopLogger(), + nexusHandler: nexusHandler, + } + + // TestOperationStringOutput returns a string, which the data converter encodes as JSON + // instead of protobuf. The System Nexus Endpoint only accepts protobuf-encoded responses. + resp, err := h.StartNexusOperation(context.Background(), &historyservice.StartNexusOperationRequest{ + Request: &nexuspb.StartOperationRequest{ + Service: "TestService", + Operation: "TestOperationStringOutput", + RequestId: "test-request-id", + Payload: payload.EncodeString("Temporal"), + }, + }) + require.Nil(t, resp) + + var failedPrecondition *serviceerror.FailedPrecondition + require.ErrorAs(t, err, &failedPrecondition) + require.ErrorContains(t, err, "system payload must be encoded as binary/protobuf but got json/plain") +} diff --git a/tests/nexus_workflow_test.go b/tests/nexus_workflow_test.go index 72a016951b9..78eff713361 100644 --- a/tests/nexus_workflow_test.go +++ b/tests/nexus_workflow_test.go @@ -54,6 +54,12 @@ import ( "google.golang.org/protobuf/types/known/durationpb" ) +const ( + dataBlobMessageType = "temporal.api.common.v1.DataBlob" + payloadsMessageType = "temporal.api.common.v1.Payloads" + protobufEncoding = "binary/protobuf" +) + type NexusWorkflowTestSuite struct { parallelsuite.Suite[*NexusWorkflowTestSuite] } @@ -3283,6 +3289,9 @@ func (s *NexusWorkflowTestSuite) TestNexusOperationSystemEndpoint(chasmEnabled b result := completedEvent.GetNexusOperationCompletedEventAttributes().Result s.NotNil(result) s.Equal([]byte("true"), result.GetMetadata()[commonnexus.SystemPayloadMetadataKey]) + // TestOperation returns a proto message, so the result must be proto encoded, not JSON. + s.Equal([]byte(dataBlobMessageType), result.GetMetadata()["messageType"]) + s.Equal([]byte(protobufEncoding), result.GetMetadata()["encoding"]) // Complete the workflow _, err = env.FrontendClient().RespondWorkflowTaskCompleted(ctx, &workflowservice.RespondWorkflowTaskCompletedRequest{ @@ -3302,9 +3311,10 @@ func (s *NexusWorkflowTestSuite) TestNexusOperationSystemEndpoint(chasmEnabled b }, }) s.NoError(err) - var response string + var response commonpb.DataBlob s.NoError(run.Get(ctx, &response)) - s.Equal("Hello, Temporal", response) + data := response.Data + s.Equal("Hello, Temporal", string(data)) } // NOTE: This test cannot use the SDK workflow package because there is a restriction that prevents setting the @@ -3361,9 +3371,10 @@ func (s *NexusWorkflowTestSuite) TestNexusOperationSystemEndpoint_PayloadMetadat completedEvent := s.RequireHistoryEvent(pollResp.History.Events, enumspb.EVENT_TYPE_NEXUS_OPERATION_COMPLETED) result := completedEvent.GetNexusOperationCompletedEventAttributes().Result s.NotNil(result) - // TestOperationWithPayload's response embeds a nested Payload, so the system payload metadata - // flag must be set. + // TestOperationWithPayload's response embeds a nested Payload, so the system payload metadata flag must be set. s.Equal([]byte("true"), result.GetMetadata()[commonnexus.SystemPayloadMetadataKey]) + s.Equal([]byte(payloadsMessageType), result.GetMetadata()["messageType"]) + s.Equal([]byte(protobufEncoding), result.GetMetadata()["encoding"]) // Complete the workflow _, err = env.FrontendClient().RespondWorkflowTaskCompleted(s.Context(), &workflowservice.RespondWorkflowTaskCompletedRequest{ From ed762e17bdfb75d662d96fe82ff5362c1e6047e5 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 16:31:52 -0700 Subject: [PATCH 22/47] Re-pinned api-go to the documented stream protos. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 555e4d15873..e3cde8649d9 100644 --- a/go.mod +++ b/go.mod @@ -240,4 +240,4 @@ require ( tool golang.org/x/perf/cmd/benchstat -replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260916223558-5ea9c676ee47 +replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260918232949-bc9a4a97be0b diff --git a/go.sum b/go.sum index deef4d76eb0..90ce90a71ea 100644 --- a/go.sum +++ b/go.sum @@ -321,8 +321,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moedash/api-go v1.63.6-0.20260916223558-5ea9c676ee47 h1:oSariLfjiF3pYlvthha99PG56vUU36zeU3GdDJpRU9M= -github.com/moedash/api-go v1.63.6-0.20260916223558-5ea9c676ee47/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= +github.com/moedash/api-go v1.63.6-0.20260918232949-bc9a4a97be0b h1:2N29jq0QI1cPq2OsAlcXvQIUTclVaaZ1AjUgeWhpnUQ= +github.com/moedash/api-go v1.63.6-0.20260918232949-bc9a4a97be0b/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= From 40f54b083afd3a43a9c5ed139e29febd97e55dca Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 16:40:39 -0700 Subject: [PATCH 23/47] Removed the unused stream log store and its schema. Payloads live in the CHASM component, so the table, the store methods, the four schema bumps and the scavenger marker had no caller on the serving path. The internal protos reserve the field numbers the log addressing used. --- chasm/lib/stream/cursor.go | 38 +- chasm/lib/stream/cursor_test.go | 30 +- .../gen/streampb/v1/request_response.pb.go | 44 +- .../stream/gen/streampb/v1/stream_state.pb.go | 81 +--- chasm/lib/stream/log.go | 160 ------- chasm/lib/stream/proto/v1/message.proto | 2 +- .../stream/proto/v1/request_response.proto | 9 +- chasm/lib/stream/proto/v1/stream_state.proto | 25 +- chasm/lib/stream/service/events.go | 2 +- chasm/lib/stream/service/handler.go | 36 +- chasm/lib/stream/stream.go | 72 +--- chasm/lib/stream/stream_test.go | 115 ++--- chasm/lib/workflow/stream_commands.go | 32 +- chasm/lib/workflow/stream_cursor_test.go | 16 +- chasm/lib/workflow/workflow.go | 47 +- common/metrics/metric_defs.go | 6 - common/persistence/cassandra/history_store.go | 99 ----- common/persistence/data_interfaces.go | 24 -- common/persistence/data_interfaces_mock.go | 43 -- .../faultinjection/execution_store_gen.go | 27 -- common/persistence/history_manager.go | 26 -- common/persistence/mock/store_mock.go | 43 -- common/persistence/persistence_interface.go | 52 --- .../persistence/persistence_metric_clients.go | 42 -- .../persistence_rate_limited_clients.go | 33 -- .../persistence_retryable_clients.go | 37 -- common/persistence/sql/history_store.go | 83 ---- .../persistence/sql/sqlplugin/interfaces.go | 1 - .../sql/sqlplugin/mysql/stream_log.go | 64 --- .../sql/sqlplugin/postgresql/stream_log.go | 65 --- .../sql/sqlplugin/sqlite/stream_log.go | 72 ---- .../persistence/sql/sqlplugin/stream_log.go | 66 --- .../telemetry/execution_store_gen.go | 112 ----- .../tests/history_store_stream_log.go | 407 ------------------ schema/cassandra/temporal/schema.cql | 15 - .../versioned/v1.14/add_stream_log.cql | 14 - .../temporal/versioned/v1.14/manifest.json | 6 - schema/cassandra/version.go | 2 +- schema/mysql/v8/temporal/schema.sql | 13 - .../versioned/v1.20/add_stream_log.sql | 12 - .../v8/temporal/versioned/v1.20/manifest.json | 8 - schema/mysql/v8/version.go | 2 +- schema/postgresql/v12/temporal/schema.sql | 13 - .../versioned/v1.20/add_stream_log.sql | 12 - .../temporal/versioned/v1.20/manifest.json | 8 - schema/postgresql/v12/version.go | 2 +- schema/sqlite/v3/temporal/schema.sql | 12 - .../versioned/v0.12/add_stream_log.sql | 12 - .../v3/temporal/versioned/v0.12/manifest.json | 8 - schema/sqlite/version.go | 2 +- .../stream_slices.go | 27 +- .../stream_appends.go | 8 +- .../workflow_task_completed_handler.go | 4 +- service/worker/scanner/history/scavenger.go | 12 - .../worker/scanner/history/scavenger_test.go | 58 --- tests/stream_consume_test.go | 15 +- tests/stream_test.go | 2 +- tests/testcore/history_task_recorder.go | 24 -- 58 files changed, 180 insertions(+), 2122 deletions(-) delete mode 100644 chasm/lib/stream/log.go delete mode 100644 common/persistence/sql/sqlplugin/mysql/stream_log.go delete mode 100644 common/persistence/sql/sqlplugin/postgresql/stream_log.go delete mode 100644 common/persistence/sql/sqlplugin/sqlite/stream_log.go delete mode 100644 common/persistence/sql/sqlplugin/stream_log.go delete mode 100644 common/persistence/tests/history_store_stream_log.go delete mode 100644 schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql delete mode 100644 schema/cassandra/temporal/versioned/v1.14/manifest.json delete mode 100644 schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql delete mode 100644 schema/mysql/v8/temporal/versioned/v1.20/manifest.json delete mode 100644 schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql delete mode 100644 schema/postgresql/v12/temporal/versioned/v1.20/manifest.json delete mode 100644 schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql delete mode 100644 schema/sqlite/v3/temporal/versioned/v0.12/manifest.json diff --git a/chasm/lib/stream/cursor.go b/chasm/lib/stream/cursor.go index 265ec33daae..adb433ce709 100644 --- a/chasm/lib/stream/cursor.go +++ b/chasm/lib/stream/cursor.go @@ -24,9 +24,7 @@ type NewCursorRequest struct { StreamID string // External marks a stream in another execution, whose frontier this // workflow is told about rather than reads. - External bool - CollectionID string - BucketSize int64 + External bool // Where to start reading. Resolving "from the tail" against the stream's // head happens before this is called, so the value recorded here is already @@ -38,25 +36,17 @@ func NewCursor(_ chasm.MutableContext, req NewCursorRequest) (*Cursor, error) { if req.StreamID == "" { return nil, serviceerror.NewInvalidArgument("stream id is required") } - if req.CollectionID == "" { - return nil, serviceerror.NewInvalidArgument("collection id is required") - } - if req.BucketSize <= 0 { - return nil, serviceerror.NewInvalidArgument("bucket size must be positive") - } if req.StartOffset < 0 { return nil, serviceerror.NewInvalidArgument("start offset cannot be negative") } return &Cursor{ State: &streampb.WorkflowStreamCursor{ - StreamId: req.StreamID, - CollectionId: req.CollectionID, - BucketSize: req.BucketSize, - Offset: req.StartOffset, - StartOffset: req.StartOffset, - External: req.External, - KnownHead: req.StartOffset, + StreamId: req.StreamID, + Offset: req.StartOffset, + StartOffset: req.StartOffset, + External: req.External, + KnownHead: req.StartOffset, }, }, nil } @@ -77,14 +67,6 @@ func (c *Cursor) StreamID() string { return c.State.StreamId } -func (c *Cursor) CollectionID() string { - return c.State.CollectionId -} - -func (c *Cursor) BucketSize() int64 { - return c.State.BucketSize -} - // StagePending records the range attached to the workflow task now in flight. // // A redelivery overwrites whatever was staged before. That is safe because a @@ -132,14 +114,6 @@ func (c *Cursor) Commit(_ chasm.MutableContext) (from int64, to int64, ok bool) return from, to, true } -// Abandon drops a staged range without advancing, for a task that will never -// complete. The next delivery re-reads from the unchanged cursor. -func (c *Cursor) Abandon(_ chasm.MutableContext) { - c.State.PendingFrom = 0 - c.State.PendingTo = 0 - c.State.HasPending = false -} - // IsExternal reports whether the stream lives in another execution. func (c *Cursor) IsExternal() bool { return c.State.External diff --git a/chasm/lib/stream/cursor_test.go b/chasm/lib/stream/cursor_test.go index a5ca5314500..deb2b95212c 100644 --- a/chasm/lib/stream/cursor_test.go +++ b/chasm/lib/stream/cursor_test.go @@ -10,20 +10,16 @@ import ( func newTestCursor(offset int64) *Cursor { return &Cursor{ State: &streampb.WorkflowStreamCursor{ - StreamId: "s-1", - CollectionId: "col-1", - BucketSize: DefaultBucketSize, - Offset: offset, + StreamId: "s-1", + Offset: offset, }, } } func TestNewCursorRejectsIncompleteRequests(t *testing.T) { cases := map[string]NewCursorRequest{ - "no stream id": {CollectionID: "col-1", BucketSize: 10}, - "no collection id": {StreamID: "s-1", BucketSize: 10}, - "zero bucket size": {StreamID: "s-1", CollectionID: "col-1"}, - "negative start": {StreamID: "s-1", CollectionID: "col-1", BucketSize: 10, StartOffset: -1}, + "no stream id": {}, + "negative start": {StreamID: "s-1", StartOffset: -1}, } for name, req := range cases { @@ -57,9 +53,8 @@ func TestCursorCommitAdvancesAndClears(t *testing.T) { require.False(t, ok, "committing again must not re-record the range") } -// The distinction this pins is the one §8.2 of the design turns on: a task that -// observed nothing still has to be recorded, so an empty range is a pending -// range, not the absence of one. +// A task that observed nothing still has to be recorded, so an empty range is a +// pending range, not the absence of one. Replay depends on the difference. func TestCursorTreatsAnEmptyRangeAsAFact(t *testing.T) { c := newTestCursor(9) @@ -103,16 +98,3 @@ func TestCursorRedeliveryReplacesTheStagedRange(t *testing.T) { require.Equal(t, int64(9), to) require.Equal(t, int64(2), c.Offset(), "staging alone must never advance the cursor") } - -func TestCursorAbandonLeavesTheOffsetAlone(t *testing.T) { - c := newTestCursor(3) - - require.NoError(t, c.StagePending(nil, 3, 8)) - c.Abandon(nil) - - _, _, ok := c.Pending() - require.False(t, ok) - require.Equal(t, int64(3), c.Offset()) - - require.NoError(t, c.StagePending(nil, 3, 6), "the next delivery re-reads from the unchanged cursor") -} diff --git a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go index d941c63b908..051af5c44af 100644 --- a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go @@ -143,7 +143,6 @@ type AddMessagesInput struct { // by this repo's helper generator. ExpectedOffset int64 `protobuf:"varint,6,opt,name=expected_offset,json=expectedOffset,proto3" json:"expected_offset,omitempty"` UseExpectedOffset bool `protobuf:"varint,8,opt,name=use_expected_offset,json=useExpectedOffset,proto3" json:"use_expected_offset,omitempty"` - OwnerEpoch int64 `protobuf:"varint,7,opt,name=owner_epoch,json=ownerEpoch,proto3" json:"owner_epoch,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -234,13 +233,6 @@ func (x *AddMessagesInput) GetUseExpectedOffset() bool { return false } -func (x *AddMessagesInput) GetOwnerEpoch() int64 { - if x != nil { - return x.OwnerEpoch - } - return 0 -} - type AddMessagesOutput struct { state protoimpl.MessageState `protogen:"open.v1"` FirstOffset int64 `protobuf:"varint,1,opt,name=first_offset,json=firstOffset,proto3" json:"first_offset,omitempty"` @@ -2219,10 +2211,9 @@ func (x *RegisterStreamConsumerInput) GetStartOffset() int64 { type RegisterStreamConsumerOutput struct { state protoimpl.MessageState `protogen:"open.v1"` StartOffset int64 `protobuf:"varint,1,opt,name=start_offset,json=startOffset,proto3" json:"start_offset,omitempty"` - // What the consumer needs to address the log, all decided by the stream. - CollectionId string `protobuf:"bytes,2,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"` - BucketSize int64 `protobuf:"varint,3,opt,name=bucket_size,json=bucketSize,proto3" json:"bucket_size,omitempty"` - KnownHead int64 `protobuf:"varint,4,opt,name=known_head,json=knownHead,proto3" json:"known_head,omitempty"` + // The frontier at registration, so the cursor starts with a known head + // instead of waiting for the first push. + KnownHead int64 `protobuf:"varint,4,opt,name=known_head,json=knownHead,proto3" json:"known_head,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2264,20 +2255,6 @@ func (x *RegisterStreamConsumerOutput) GetStartOffset() int64 { return 0 } -func (x *RegisterStreamConsumerOutput) GetCollectionId() string { - if x != nil { - return x.CollectionId - } - return "" -} - -func (x *RegisterStreamConsumerOutput) GetBucketSize() int64 { - if x != nil { - return x.BucketSize - } - return 0 -} - func (x *RegisterStreamConsumerOutput) GetKnownHead() int64 { if x != nil { return x.KnownHead @@ -3255,7 +3232,7 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\tstream_id\x18\x02 \x01(\tR\bstreamId\x12X\n" + "\tlifecycle\x18\x03 \x01(\v2:.temporal.server.chasm.lib.stream.proto.v1.StreamLifecycleR\tlifecycle\"+\n" + "\x12CreateStreamOutput\x12\x15\n" + - "\x06run_id\x18\x01 \x01(\tR\x05runId\"\xf1\x02\n" + + "\x06run_id\x18\x01 \x01(\tR\x05runId\"\xd6\x02\n" + "\x10AddMessagesInput\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + "\tstream_id\x18\x02 \x01(\tR\bstreamId\x12\x15\n" + @@ -3265,9 +3242,7 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "producerId\x12\x1a\n" + "\bsequence\x18\x05 \x01(\x03R\bsequence\x12'\n" + "\x0fexpected_offset\x18\x06 \x01(\x03R\x0eexpectedOffset\x12.\n" + - "\x13use_expected_offset\x18\b \x01(\bR\x11useExpectedOffset\x12\x1f\n" + - "\vowner_epoch\x18\a \x01(\x03R\n" + - "ownerEpoch\"\x91\x01\n" + + "\x13use_expected_offset\x18\b \x01(\bR\x11useExpectedOffsetJ\x04\b\a\x10\b\"\x91\x01\n" + "\x11AddMessagesOutput\x12!\n" + "\ffirst_offset\x18\x01 \x01(\x03R\vfirstOffset\x12\x1f\n" + "\vnext_offset\x18\x02 \x01(\x03R\n" + @@ -3405,14 +3380,11 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + "\tstream_id\x18\x02 \x01(\tR\bstreamId\x120\n" + "\x14consumer_workflow_id\x18\x03 \x01(\tR\x12consumerWorkflowId\x12!\n" + - "\fstart_offset\x18\x04 \x01(\x03R\vstartOffset\"\xa6\x01\n" + + "\fstart_offset\x18\x04 \x01(\x03R\vstartOffset\"l\n" + "\x1cRegisterStreamConsumerOutput\x12!\n" + - "\fstart_offset\x18\x01 \x01(\x03R\vstartOffset\x12#\n" + - "\rcollection_id\x18\x02 \x01(\tR\fcollectionId\x12\x1f\n" + - "\vbucket_size\x18\x03 \x01(\x03R\n" + - "bucketSize\x12\x1d\n" + + "\fstart_offset\x18\x01 \x01(\x03R\vstartOffset\x12\x1d\n" + "\n" + - "known_head\x18\x04 \x01(\x03R\tknownHead\"\xb9\x01\n" + + "known_head\x18\x04 \x01(\x03R\tknownHeadJ\x04\b\x02\x10\x03J\x04\b\x03\x10\x04\"\xb9\x01\n" + "\x18AdvanceConsumerHeadInput\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1f\n" + "\vworkflow_id\x18\x02 \x01(\tR\n" + diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go index 1beb1584af8..a5080701dd2 100644 --- a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go @@ -26,26 +26,19 @@ const ( ) // Size is O(producers + consumers), never O(messages). Payload bytes live in -// the log, not here, which is what keeps this off the CHASM partial-read path. +// the component's data nodes, not here, which keeps this off the CHASM +// partial-read path. type StreamState struct { state protoimpl.MessageState `protogen:"open.v1"` // Visibility frontier. Readers never observe an offset at or past this. HeadOffset int64 `protobuf:"varint,1,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` // Truncation floor. Offsets below this are gone. - BaseOffset int64 `protobuf:"varint,2,opt,name=base_offset,json=baseOffset,proto3" json:"base_offset,omitempty"` - Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` - CloseReason *v1.Payload `protobuf:"bytes,5,opt,name=close_reason,json=closeReason,proto3" json:"close_reason,omitempty"` - // Bumped on ownership change so a stale producer's write fails. - OwnerEpoch int64 `protobuf:"varint,6,opt,name=owner_epoch,json=ownerEpoch,proto3" json:"owner_epoch,omitempty"` - // Immutable once set. Offsets roll to a new log tree every bucket_size so no - // single storage partition grows with the stream. - BucketSize int64 `protobuf:"varint,7,opt,name=bucket_size,json=bucketSize,proto3" json:"bucket_size,omitempty"` - // Identity of the log this stream writes to. Bucket trees are derived from - // it, so there is no per-bucket index to store. - CollectionId string `protobuf:"bytes,8,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"` - Producers map[string]*ProducerCursor `protobuf:"bytes,9,rep,name=producers,proto3" json:"producers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - Consumers map[string]*ConsumerCursor `protobuf:"bytes,10,rep,name=consumers,proto3" json:"consumers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - Lifecycle *StreamLifecycle `protobuf:"bytes,11,opt,name=lifecycle,proto3" json:"lifecycle,omitempty"` + BaseOffset int64 `protobuf:"varint,2,opt,name=base_offset,json=baseOffset,proto3" json:"base_offset,omitempty"` + Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` + CloseReason *v1.Payload `protobuf:"bytes,5,opt,name=close_reason,json=closeReason,proto3" json:"close_reason,omitempty"` + Producers map[string]*ProducerCursor `protobuf:"bytes,9,rep,name=producers,proto3" json:"producers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Consumers map[string]*ConsumerCursor `protobuf:"bytes,10,rep,name=consumers,proto3" json:"consumers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Lifecycle *StreamLifecycle `protobuf:"bytes,11,opt,name=lifecycle,proto3" json:"lifecycle,omitempty"` // Set when a successor run takes ownership, so an in-flight poll can follow // the chain instead of stalling on a superseded run. RedirectRunId string `protobuf:"bytes,12,opt,name=redirect_run_id,json=redirectRunId,proto3" json:"redirect_run_id,omitempty"` @@ -113,27 +106,6 @@ func (x *StreamState) GetCloseReason() *v1.Payload { return nil } -func (x *StreamState) GetOwnerEpoch() int64 { - if x != nil { - return x.OwnerEpoch - } - return 0 -} - -func (x *StreamState) GetBucketSize() int64 { - if x != nil { - return x.BucketSize - } - return 0 -} - -func (x *StreamState) GetCollectionId() string { - if x != nil { - return x.CollectionId - } - return "" -} - func (x *StreamState) GetProducers() map[string]*ProducerCursor { if x != nil { return x.Producers @@ -353,9 +325,6 @@ func (x *ConsumerCursor) GetReplayFloor() int64 { type WorkflowStreamCursor struct { state protoimpl.MessageState `protogen:"open.v1"` StreamId string `protobuf:"bytes,1,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` - // Enough to address the log without reading the stream component first. - CollectionId string `protobuf:"bytes,2,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"` - BucketSize int64 `protobuf:"varint,3,opt,name=bucket_size,json=bucketSize,proto3" json:"bucket_size,omitempty"` // Next offset to deliver. Offset int64 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"` // The stream's frontier as of the last delivery. A workflow consuming a @@ -417,20 +386,6 @@ func (x *WorkflowStreamCursor) GetStreamId() string { return "" } -func (x *WorkflowStreamCursor) GetCollectionId() string { - if x != nil { - return x.CollectionId - } - return "" -} - -func (x *WorkflowStreamCursor) GetBucketSize() int64 { - if x != nil { - return x.BucketSize - } - return 0 -} - func (x *WorkflowStreamCursor) GetOffset() int64 { if x != nil { return x.Offset @@ -539,19 +494,14 @@ var File_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto protorefle const file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc = "" + "\n" + - "Z= toOffset { - return blobs, startOffsets, nil - } - - pageSize := maxBatches - if pageSize <= 0 { - pageSize = defaultReadPageSize - } - - for bucket := BucketOf(fromOffset, bucketSize); BucketStart(bucket, bucketSize) < toOffset; bucket++ { - bucketStart := BucketStart(bucket, bucketSize) - bucketEnd := bucketStart + bucketSize - - resp, err := execMgr.ReadStreamLog(ctx, &persistence.InternalReadStreamLogRequest{ - ShardID: shardID, - NamespaceID: namespaceID, - CollectionID: collectionID, - Bucket: bucket, - MinOffset: max(fromOffset, bucketStart), - MaxOffset: min(toOffset, bucketEnd), - PageSize: pageSize, - }) - if err != nil { - return nil, nil, err - } - - blobs = append(blobs, resp.Batches...) - startOffsets = append(startOffsets, resp.StartOffsets...) - - if maxBatches > 0 && len(blobs) >= maxBatches { - return blobs[:maxBatches], startOffsets[:maxBatches], nil - } - } - return blobs, startOffsets, nil -} - -// DeleteBucket removes a whole bucket's tree. Reclaiming a truncated stream one -// bucket at a time is the point of bucketing: a partition is dropped outright -// rather than leaving a tombstone per message. -func DeleteBucket( - ctx context.Context, - execMgr persistence.ExecutionManager, - shardID int32, - namespaceID string, - collectionID string, - bucket int64, -) error { - return execMgr.DeleteStreamLogBucket(ctx, &persistence.InternalDeleteStreamLogBucketRequest{ - ShardID: shardID, - NamespaceID: namespaceID, - CollectionID: collectionID, - Bucket: bucket, - }) -} - -// ReclaimableBuckets lists buckets that lie entirely below the readable floor -// and can therefore be deleted. A bucket is only reclaimable once every offset -// it holds is unreadable, so this can never drop data a reader may still ask -// for. -func ReclaimableBuckets(previousBase, newBase, bucketSize int64) []int64 { - var out []int64 - for b := BucketOf(previousBase, bucketSize); BucketStart(b+1, bucketSize) <= newBase; b++ { - out = append(out, b) - } - return out -} diff --git a/chasm/lib/stream/proto/v1/message.proto b/chasm/lib/stream/proto/v1/message.proto index 900ab36278a..ee63cdc7168 100644 --- a/chasm/lib/stream/proto/v1/message.proto +++ b/chasm/lib/stream/proto/v1/message.proto @@ -31,7 +31,7 @@ message StreamMessage { int64 offset = 6; } -// One append is one batch, and one batch is one log node. The server stores +// One append is one batch, and one batch is one data node. The server stores // this serialized and opaque; it decodes only to trim a partial first page or // to apply a topic filter. message StreamMessageBatch { diff --git a/chasm/lib/stream/proto/v1/request_response.proto b/chasm/lib/stream/proto/v1/request_response.proto index 6a7cc7417f0..c2ab498d415 100644 --- a/chasm/lib/stream/proto/v1/request_response.proto +++ b/chasm/lib/stream/proto/v1/request_response.proto @@ -40,7 +40,7 @@ message AddMessagesInput { int64 expected_offset = 6; bool use_expected_offset = 8; - int64 owner_epoch = 7; + reserved 7; } message AddMessagesOutput { @@ -269,10 +269,11 @@ message RegisterStreamConsumerInput { } message RegisterStreamConsumerOutput { + reserved 2, 3; + int64 start_offset = 1; - // What the consumer needs to address the log, all decided by the stream. - string collection_id = 2; - int64 bucket_size = 3; + // The frontier at registration, so the cursor starts with a known head + // instead of waiting for the first push. int64 known_head = 4; } diff --git a/chasm/lib/stream/proto/v1/stream_state.proto b/chasm/lib/stream/proto/v1/stream_state.proto index 9343fd47857..3005425df25 100644 --- a/chasm/lib/stream/proto/v1/stream_state.proto +++ b/chasm/lib/stream/proto/v1/stream_state.proto @@ -9,8 +9,13 @@ import "temporal/api/common/v1/message.proto"; option go_package = "go.temporal.io/server/chasm/lib/stream/gen/streampb;streampb"; // Size is O(producers + consumers), never O(messages). Payload bytes live in -// the log, not here, which is what keeps this off the CHASM partial-read path. +// the component's data nodes, not here, which keeps this off the CHASM +// partial-read path. message StreamState { + // Field numbers of removed fields, kept out of reuse so an old blob never + // decodes into a field with a different meaning. + reserved 3, 6, 7, 8; + // Visibility frontier. Readers never observe an offset at or past this. int64 head_offset = 1; // Truncation floor. Offsets below this are gone. @@ -19,17 +24,6 @@ message StreamState { bool closed = 4; temporal.api.common.v1.Payload close_reason = 5; - // Bumped on ownership change so a stale producer's write fails. - int64 owner_epoch = 6; - - // Immutable once set. Offsets roll to a new log tree every bucket_size so no - // single storage partition grows with the stream. - int64 bucket_size = 7; - - // Identity of the log this stream writes to. Bucket trees are derived from - // it, so there is no per-bucket index to store. - string collection_id = 8; - map producers = 9; map consumers = 10; @@ -82,10 +76,9 @@ message ConsumerCursor { // range. Keeping it on the stream would make the advance a cross-execution // write, and a crash between the two would either redeliver or skip. message WorkflowStreamCursor { + reserved 2, 3; + string stream_id = 1; - // Enough to address the log without reading the stream component first. - string collection_id = 2; - int64 bucket_size = 3; // Next offset to deliver. int64 offset = 4; @@ -116,7 +109,7 @@ message WorkflowStreamCursor { message StreamLifecycle { // How long a closed stream stays readable before it is deleted. google.protobuf.Duration retention = 1; - // Cap on readable messages. Older whole buckets are reclaimed once the floor + // Cap on readable messages. Whole batches are reclaimed once the floor // passes them, so a capped stream has bounded storage. int64 max_items = 2; } diff --git a/chasm/lib/stream/service/events.go b/chasm/lib/stream/service/events.go index 0088b1eec5e..69f0b104a51 100644 --- a/chasm/lib/stream/service/events.go +++ b/chasm/lib/stream/service/events.go @@ -43,7 +43,7 @@ func (streamSubscribedEventDefinition) CherryPick( // // Like the subscription event it applies nothing: the stream's frontier is // CHASM state committed with the workflow task, and the bodies are in the -// stream's own log. What the event carries is the offset range, which is what +// stream component. What the event carries is the offset range, which is what // lets anyone reading History find the batch without History having held it. type streamMessagesAddedEventDefinition struct{} diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index 41da753d9be..4b32702b777 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -152,10 +152,7 @@ func (h *handler) CreateStream( ctx, chasm.ExecutionKey{NamespaceID: req.GetNamespaceId(), BusinessID: in.GetStreamId()}, func(mctx chasm.MutableContext, input *streampb.CreateStreamInput) (*stream.Stream, error) { - return stream.NewStream(mctx, stream.NewStreamRequest{ - CollectionID: mctx.ExecutionKey().RunID, - Lifecycle: input.GetLifecycle(), - }) + return stream.NewStream(mctx, stream.NewStreamRequest{Lifecycle: input.GetLifecycle()}) }, in, ) @@ -191,7 +188,6 @@ func (h *handler) AddMessages( Messages: in.GetMessages(), ProducerID: in.GetProducerId(), Sequence: in.GetSequence(), - OwnerEpoch: in.GetOwnerEpoch(), } if in.GetUseExpectedOffset() { expected := in.GetExpectedOffset() @@ -238,7 +234,7 @@ func (h *handler) AddWorkflowMessages( name := ownedStreamName(in.GetStreamName()) - // Keyed on the owner and the name, which is what identifies the log here. + // Keyed on the owner and the name, which is what identifies the stream here. unlock := h.lockStream(req.GetNamespaceId(), in.GetWorkflowId()+"/"+name) defer unlock() @@ -254,8 +250,7 @@ func (h *handler) AddWorkflowMessages( return nil, err } if state == nil { - // Nothing has published yet, so the collection id and bucket size this - // write needs do not exist. Creating the stream is a transition, and + // Nothing has published yet. Creating the stream is a transition, and // only the first writer ever pays it. state, _, err = chasm.UpdateComponent(ctx, ref, (*chasmworkflow.Workflow).EnsureOwnedStream, name) @@ -388,11 +383,9 @@ func (h *handler) subscribeToExternalStream( workflowRef(namespaceID, in.GetWorkflowId(), in.GetOwnerRunId()), func(wf *chasmworkflow.Workflow, mctx chasm.MutableContext, offset int64) (int64, error) { return wf.SubscribeToExternalStream(mctx, chasmworkflow.ExternalStreamSubscription{ - StreamID: in.GetStreamId(), - CollectionID: pin.GetCollectionId(), - BucketSize: pin.GetBucketSize(), - StartOffset: offset, - KnownHead: pin.GetKnownHead(), + StreamID: in.GetStreamId(), + StartOffset: offset, + KnownHead: pin.GetKnownHead(), }) }, pin.GetStartOffset(), @@ -410,8 +403,8 @@ func (h *handler) subscribeToExternalStream( // // Internal. Called by SubscribeWorkflow, which is routed to the consumer and so // cannot reach the stream itself. It resolves a negative start offset here, -// where the frontier is, and hands back everything the cursor needs to address -// the log, so the consumer records facts rather than readings. +// where the frontier is, and hands back the resolved offset and the frontier, +// so the consumer records facts rather than readings. func (h *handler) RegisterStreamConsumer( ctx context.Context, req *streampb.RegisterStreamConsumerRequest, @@ -451,10 +444,8 @@ func (h *handler) RegisterStreamConsumer( return &streampb.RegisterStreamConsumerResponse{ FrontendResponse: &streampb.RegisterStreamConsumerOutput{ - StartOffset: startOffset, - CollectionId: state.GetCollectionId(), - BucketSize: state.GetBucketSize(), - KnownHead: state.GetHeadOffset(), + StartOffset: startOffset, + KnownHead: state.GetHeadOffset(), }, }, nil } @@ -523,10 +514,9 @@ func (h *handler) PollMessages( // PollWorkflowMessages reads a stream a workflow owns. // -// Everything that addresses the stream comes from its owner: the shard, the -// frontier, and the collection the log nodes were written under. That is also -// why the log read below needs no special case. An attached stream's nodes are -// written under the owner's shard, which is the shard this call routed to. +// An attached stream is reached through its owner, so this call routes on the +// workflow id and both the frontier and the batches come out of the owner's +// component. func (h *handler) PollWorkflowMessages( ctx context.Context, req *streampb.PollWorkflowMessagesRequest, diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index 1b0cabec88a..d1012bc253b 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -1,6 +1,7 @@ package stream import ( + "bytes" "crypto/sha256" "maps" "slices" @@ -16,9 +17,9 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -// Stream is a durable, offset-addressed append-only sequence. It holds only the -// frontier: the payload bytes live in the log (see log.go), so this state is -// O(producers + consumers) no matter how long the stream gets. +// Stream is a durable, offset-addressed append-only sequence. State holds the +// frontier and the producer and consumer tables, so it is O(producers + +// consumers) no matter how long the stream gets; the payload lives in Batches. // // Appending never schedules a workflow task. A stream item is data produced by // an execution, not a decision input to it, so nothing in a workflow's state @@ -32,8 +33,8 @@ type Stream struct { // data nodes, so they replicate with the component and are reclaimed with // it, and a retry addresses the same key rather than racing it. // - // They also live in mutable state, which caps a stream at the execution - // size limit. That is the trade for not owning a store. + // They also live in mutable state, so the payload counts against the + // execution size limit of whatever execution holds the stream. Batches chasm.Map[int64, *commonpb.DataBlob] // Present so streams are listable. Operators need to find them the same way @@ -43,9 +44,7 @@ type Stream struct { } type NewStreamRequest struct { - CollectionID string - BucketSize int64 - Lifecycle *streampb.StreamLifecycle + Lifecycle *streampb.StreamLifecycle // Attached means the stream is a subcomponent of another execution rather // than a root. CHASM requires a visibility component to be an immediate @@ -62,9 +61,6 @@ type AddMessagesRequest struct { ProducerID string Sequence int64 ExpectedOffset *int64 - - // Optional fencing. Rejected if below the stream's current epoch. - OwnerEpoch int64 } type AddMessagesResult struct { @@ -82,10 +78,6 @@ type AddMessagesResult struct { } func NewStream(ctx chasm.MutableContext, req NewStreamRequest) (*Stream, error) { - bucketSize := req.BucketSize - if bucketSize <= 0 { - bucketSize = DefaultBucketSize - } visibility := chasm.NewEmptyField[*chasm.Visibility]() if !req.Attached { visibility = chasm.NewComponentField(ctx, chasm.NewVisibility(ctx)) @@ -94,11 +86,9 @@ func NewStream(ctx chasm.MutableContext, req NewStreamRequest) (*Stream, error) Visibility: visibility, Batches: make(chasm.Map[int64, *commonpb.DataBlob]), State: &streampb.StreamState{ - CollectionId: req.CollectionID, - BucketSize: bucketSize, - Lifecycle: req.Lifecycle, - Producers: make(map[string]*streampb.ProducerCursor), - Consumers: make(map[string]*streampb.ConsumerCursor), + Lifecycle: req.Lifecycle, + Producers: make(map[string]*streampb.ProducerCursor), + Consumers: make(map[string]*streampb.ConsumerCursor), }, }, nil } @@ -167,10 +157,6 @@ func (s *Stream) AddMessages( return *replay, nil } - if req.OwnerEpoch != 0 && req.OwnerEpoch < s.State.OwnerEpoch { - return AddMessagesResult{}, serviceerror.NewFailedPrecondition("producer has been fenced") - } - // After the retry check, so a known producer is never rejected for room. if err := s.checkProducerRoom(req.ProducerID); err != nil { return AddMessagesResult{}, err @@ -228,9 +214,8 @@ func (s *Stream) AddMessages( // sees the new frontier while closing its own transaction, so waking it through // a task would only duplicate a decision already made locally. func (s *Stream) notifyConsumers(mctx chasm.MutableContext) { - // The append path previews itself against a detached copy to work out which - // log node to write, and that preview has no transition to attach a task - // to. Only the real transition, which carries a context, schedules one. + // Without a context there is no transition to attach the task to. The + // component's own unit tests drive the transitions that way. if mctx == nil { return } @@ -293,7 +278,7 @@ func (s *Stream) checkProducer(req AddMessagesRequest, hash []byte) (*AddMessage // Same sequence. Identical content is a retry; different content is a // client bug, and returning the recorded offsets would report success while // silently dropping the caller's data. - if !equalHash(cursor.ContentHash, hash) { + if !bytes.Equal(cursor.ContentHash, hash) { return nil, serviceerror.NewInvalidArgumentf( "producer sequence %d already used with different content", req.Sequence) } @@ -639,25 +624,6 @@ func (s *Stream) AdvanceConsumer(_ chasm.MutableContext, consumerID string, offs consumer.Offset = offset } -// consumerPin is the lowest offset any active consumer has reached. Nothing in -// the write path consults it, by the decision above; it is how the consumer -// table is read back, and what asserts that a re-registration cannot rewind a -// consumer's recorded position. -func (s *Stream) consumerPin() (int64, bool) { - var pin int64 - found := false - for _, c := range s.State.Consumers { - if !c.Active { - continue - } - if !found || c.Offset < pin { - pin = c.Offset - found = true - } - } - return pin, found -} - // DeregisterConsumer releases the floor a consumer was holding, so retention // and the message cap can reach its messages again. func (s *Stream) DeregisterConsumer(_ chasm.MutableContext, consumerID string) { @@ -685,15 +651,3 @@ func contentHash(data []byte) []byte { sum := sha256.Sum256(data) return sum[:] } - -func equalHash(a, b []byte) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go index 663d2dc1a02..f50b14aa54a 100644 --- a/chasm/lib/stream/stream_test.go +++ b/chasm/lib/stream/stream_test.go @@ -16,14 +16,12 @@ import ( // transitions, and NewStream also wires a visibility field that needs a live // context. Construction through the real path is covered end to end in // tests/stream_test.go. -func newTestStream(t *testing.T, bucketSize int64) *Stream { +func newTestStream(t *testing.T) *Stream { t.Helper() return &Stream{ State: &streampb.StreamState{ - CollectionId: "col-1", - BucketSize: bucketSize, - Producers: make(map[string]*streampb.ProducerCursor), - Consumers: make(map[string]*streampb.ConsumerCursor), + Producers: make(map[string]*streampb.ProducerCursor), + Consumers: make(map[string]*streampb.ConsumerCursor), }, } } @@ -40,7 +38,7 @@ func msgs(bodies ...string) []*streampb.StreamMessage { } func TestAddMessagesAssignsContiguousOffsets(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) first, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) require.NoError(t, err) @@ -56,7 +54,7 @@ func TestAddMessagesAssignsContiguousOffsets(t *testing.T) { } func TestAddMessagesWritesTheBatchIntoTheComponent(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) res, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) require.NoError(t, err) @@ -71,7 +69,7 @@ func TestAddMessagesWritesTheBatchIntoTheComponent(t *testing.T) { } func TestDedupReturnsOriginalOffsets(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) req := AddMessagesRequest{Messages: msgs("a", "b"), ProducerID: "p1", Sequence: 1} first, err := s.AddMessages(nil, req) @@ -88,7 +86,7 @@ func TestDedupReturnsOriginalOffsets(t *testing.T) { } func TestDedupIgnoresPayloadMetadataMapOrder(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) for attempt := range 32 { body := &commonpb.Payload{ Data: []byte("encoded"), @@ -114,7 +112,7 @@ func TestDedupIgnoresPayloadMetadataMapOrder(t *testing.T) { } func TestDedupRejectsDifferentContent(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{ Messages: msgs("a"), ProducerID: "p1", Sequence: 1, }) @@ -130,7 +128,7 @@ func TestDedupRejectsDifferentContent(t *testing.T) { } func TestExpectedOffsetMismatchReportsHead(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a")}) require.NoError(t, err) @@ -142,19 +140,8 @@ func TestExpectedOffsetMismatchReportsHead(t *testing.T) { require.Contains(t, err.Error(), "stream head is 1") } -func TestOwnerEpochFencesStaleProducer(t *testing.T) { - s := newTestStream(t, 100) - s.State.OwnerEpoch = 5 - - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a"), OwnerEpoch: 4}) - require.Error(t, err) - - _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a"), OwnerEpoch: 5}) - require.NoError(t, err) -} - func TestFinishWritingFencesOneProducerOnly(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) require.NoError(t, s.FinishWriting(nil, "p1")) _, err := s.AddMessages(nil, AddMessagesRequest{ @@ -171,7 +158,7 @@ func TestFinishWritingFencesOneProducerOnly(t *testing.T) { } func TestCloseRejectsFurtherAppends(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) s.Close(time.Now(), nil) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a")}) @@ -181,7 +168,7 @@ func TestCloseRejectsFurtherAppends(t *testing.T) { } func TestReadSpansBatchesAndStartsAtTheBatchHoldingTheOffset(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) require.NoError(t, err) _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) @@ -204,7 +191,7 @@ func TestReadSpansBatchesAndStartsAtTheBatchHoldingTheOffset(t *testing.T) { } func TestReclaimDropsOnlyBatchesFullyBelowTheFloor(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) require.NoError(t, err) _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) @@ -223,7 +210,7 @@ func TestReclaimDropsOnlyBatchesFullyBelowTheFloor(t *testing.T) { } func TestTruncateStopsAtAnActiveConsumersReplayFloor(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) @@ -241,7 +228,7 @@ func TestTruncateStopsAtAnActiveConsumersReplayFloor(t *testing.T) { } func TestTruncateBounds(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) require.NoError(t, err) @@ -253,27 +240,8 @@ func TestTruncateBounds(t *testing.T) { require.Error(t, err, "truncation must not pass the head") } -func TestBucketArithmetic(t *testing.T) { - require.Equal(t, int64(0), BucketOf(0, 10)) - require.Equal(t, int64(0), BucketOf(9, 10)) - require.Equal(t, int64(1), BucketOf(10, 10)) - - // Node ids are bucket-relative and start at 1, because the store rejects 0. - require.Equal(t, int64(20), BucketStart(2, 10)) -} - -func TestReclaimableBuckets(t *testing.T) { - // Only buckets lying entirely below the floor are reclaimable, so nothing a - // reader can still ask for is ever dropped. - require.Empty(t, ReclaimableBuckets(0, 3, 4)) - require.Equal(t, []int64{0}, ReclaimableBuckets(0, 4, 4)) - require.Equal(t, []int64{0, 1}, ReclaimableBuckets(0, 8, 4)) - require.Equal(t, []int64{1}, ReclaimableBuckets(4, 8, 4)) - require.Empty(t, ReclaimableBuckets(4, 5, 4)) -} - func TestCapTruncatesInline(t *testing.T) { - s := newTestStream(t, 4) + s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 4} for range 4 { @@ -281,14 +249,14 @@ func TestCapTruncatesInline(t *testing.T) { require.NoError(t, err) } - // Eight appended, four retained, so the floor sits at 4 and bucket 0 is - // entirely below it. + // Eight appended, four retained, so the floor sits at 4 and the first two + // batches are entirely below it. require.Equal(t, int64(8), s.State.HeadOffset) require.Equal(t, int64(4), s.State.BaseOffset) } func TestCapRefusesAnAppendItCouldOnlyAbsorbByDroppingReadRecords(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} require.NoError(t, s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false)) @@ -307,10 +275,10 @@ func TestCapRefusesAnAppendItCouldOnlyAbsorbByDroppingReadRecords(t *testing.T) func TestCloseSchedulesRetentionOnlyWhenConfigured(t *testing.T) { now := time.Now() - plain := newTestStream(t, 100) + plain := newTestStream(t) require.True(t, plain.Close(now, nil).IsZero(), "no retention configured, nothing to schedule") - withRetention := newTestStream(t, 100) + withRetention := newTestStream(t) withRetention.State.Lifecycle = &streampb.StreamLifecycle{ Retention: durationpb.New(time.Hour), } @@ -323,7 +291,7 @@ func TestCloseSchedulesRetentionOnlyWhenConfigured(t *testing.T) { } func TestRegisterConsumerPinsFromWhereItSubscribed(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) @@ -337,13 +305,13 @@ func TestRegisterConsumerPinsFromWhereItSubscribed(t *testing.T) { } func TestAdvanceConsumerTracksWhereAConsumerHasReached(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) - // The cursor is what decides whether this consumer is worth waking, and - // nothing else now depends on it. + // The read position decides whether this consumer is worth waking. The + // replay floor, not this, is what retention respects. s.AdvanceConsumer(nil, "workflow:output", 3) require.Equal(t, int64(3), s.State.Consumers["workflow:output"].Offset) } @@ -351,7 +319,7 @@ func TestAdvanceConsumerTracksWhereAConsumerHasReached(t *testing.T) { // Lowering the pin would hand back a guarantee already written to History: a // recorded range has to stay re-readable. func TestAdvanceConsumerNeverRewinds(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) @@ -359,13 +327,11 @@ func TestAdvanceConsumerNeverRewinds(t *testing.T) { s.AdvanceConsumer(nil, "workflow:output", 3) s.AdvanceConsumer(nil, "workflow:output", 1) - pin, ok := s.consumerPin() - require.True(t, ok) - require.Equal(t, int64(3), pin) + require.Equal(t, int64(3), s.State.Consumers["workflow:output"].GetOffset()) } func TestRegisterConsumerRejectsAnOffsetBelowTheFloor(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) err = s.Truncate(nil, 2) @@ -378,7 +344,7 @@ func TestRegisterConsumerRejectsAnOffsetBelowTheFloor(t *testing.T) { // Resubscribing reactivates the existing pin rather than resetting it, so a // consumer cannot rewind its own floor by subscribing again. func TestRegisterConsumerTwiceKeepsThePin(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) @@ -386,13 +352,11 @@ func TestRegisterConsumerTwiceKeepsThePin(t *testing.T) { require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) - pin, ok := s.consumerPin() - require.True(t, ok) - require.Equal(t, int64(3), pin) + require.Equal(t, int64(3), s.State.Consumers["workflow:output"].GetOffset()) } func TestDeregisterConsumerReleasesThePin(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false)) @@ -404,7 +368,7 @@ func TestDeregisterConsumerReleasesThePin(t *testing.T) { } func TestMessageCapStillAppliesWithNoConsumerToProtect(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} for range 3 { @@ -422,7 +386,7 @@ func TestMessageCapStillAppliesWithNoConsumerToProtect(t *testing.T) { // retroactively wrong, so the clamp keeps its bytes and the stream sits over // its cap until it goes away. func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) @@ -437,7 +401,7 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { // The reason the pin was taken out in the first place. It must not come back: // a consumer that finished has to stop holding storage. func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) s.DeregisterConsumer(nil, "workflow:output") @@ -449,10 +413,11 @@ func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { require.Equal(t, int64(4), s.State.BaseOffset) } -// Coming back to a stream that moved past what its History refers to is the -// case the old code discovered during a replay instead. +// Coming back to a stream that moved past what its History refers to has to be +// refused at registration, which is the last moment before the workflow +// depends on those offsets again. func TestReregisteringBelowTheFloorIsRefused(t *testing.T) { - s := newTestStream(t, 100) + s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) @@ -469,7 +434,7 @@ func TestReregisteringBelowTheFloorIsRefused(t *testing.T) { // component state until no append fits, which leaves the stream unwritable for // good rather than failing the call that caused it. func TestStreamProducerTableIsBounded(t *testing.T) { - s := newTestStream(t, 100000) + s := newTestStream(t) for i := range MaxProducersPerStream { _, err := s.AddMessages(nil, AddMessagesRequest{ @@ -507,7 +472,7 @@ func TestStreamProducerTableIsBounded(t *testing.T) { // Each consumer holds a truncation floor, so an unbounded table pins storage as // well as growing state. func TestStreamConsumerTableIsBounded(t *testing.T) { - s := newTestStream(t, 100000) + s := newTestStream(t) for i := range MaxConsumersPerStream { require.NoError(t, s.RegisterConsumer( diff --git a/chasm/lib/workflow/stream_commands.go b/chasm/lib/workflow/stream_commands.go index 598d46b91e9..20964d02bd4 100644 --- a/chasm/lib/workflow/stream_commands.go +++ b/chasm/lib/workflow/stream_commands.go @@ -16,14 +16,12 @@ const DefaultStreamName = "output" // handleAddStreamMessagesCommand appends to a stream the workflow owns. // -// The stream is a co-located subcomponent, so its frontier advances as part of -// the workflow task's own commit: no extra transition and no cross-execution -// write. The log bytes cannot be written here, because a command handler runs -// under the state lock with no context to do I/O from, so they are staged and -// flushed before the commit that makes them visible. +// The stream is a co-located subcomponent, so the batch and the frontier land +// in the workflow task's own commit: no extra transition, no cross-execution +// write, and a task that fails takes the publish with it. // // The offsets are known here, unlike a subscription's, so the event is written -// here too rather than being staged for the flush. +// here too rather than reserved and filled in later. func handleAddStreamMessagesCommand( chasmCtx chasm.MutableContext, wf *Workflow, @@ -39,7 +37,7 @@ func handleAddStreamMessagesCommand( return serviceerror.NewInvalidArgument("AddStreamMessages command carries no messages") } - // The batch becomes one log node, so the whole batch is what has to fit. + // The batch becomes one data node, so the whole batch is what has to fit. // Left unchecked it fails later in the flush, which surfaces as a // persistence error out of a task the worker will replay and re-issue // forever, with nothing naming the batch as the cause. @@ -82,11 +80,10 @@ func handleAddStreamMessagesCommand( // handleSubscribeStreamCommand registers this workflow as a consumer. // -// A stream the workflow owns is subscribed here and now, because everything the -// cursor needs is already in this execution. One in another execution cannot -// be: its collection id is the stream's run id and its bucket size is its own, -// and finding either means a lookup a command handler cannot do. Those are -// staged and resolved in the flush before commit, the same way log writes are. +// Nothing is registered here. A stream in another execution has to be pinned +// on its own shard, which a command handler cannot reach while holding the +// state lock, so the subscription is staged and resolved after the commands and +// before the commit. func handleSubscribeStreamCommand( chasmCtx chasm.MutableContext, wf *Workflow, @@ -188,8 +185,8 @@ func RecordStreamSubscribedOffset(event *historypb.HistoryEvent, startOffset int // // One per batch, holding the offset range and nothing else. That is what makes // it a fixed cost: a batch of one 20-byte message and a batch of a thousand -// 2KB messages write the same event, because the bodies went to the stream's -// log. It exists for the same reason the subscription event does, that a +// 2KB messages write the same event, because the bodies stay in the stream +// component. It exists for the same reason the subscription event does, that a // command producing no event desynchronises the command-to-event matching // every SDK's replay depends on, and it doubles as the only record in History // that the workflow published at all. @@ -262,12 +259,7 @@ func (w *Workflow) streamNamed(ctx chasm.MutableContext, name string) (*stream.S "workflow already owns %d streams, the limit", stream.MaxOwnedStreamsPerWorkflow) } - // Keyed on the execution so the identity is stable for the workflow, and - // distinct from any other workflow reusing the same name. - created, err := stream.NewStream(ctx, stream.NewStreamRequest{ - CollectionID: ctx.ExecutionKey().RunID + "/" + name, - Attached: true, - }) + created, err := stream.NewStream(ctx, stream.NewStreamRequest{Attached: true}) if err != nil { return nil, err } diff --git a/chasm/lib/workflow/stream_cursor_test.go b/chasm/lib/workflow/stream_cursor_test.go index 294b2c35db2..b61ee9a7d7c 100644 --- a/chasm/lib/workflow/stream_cursor_test.go +++ b/chasm/lib/workflow/stream_cursor_test.go @@ -35,10 +35,8 @@ func newAttachedStream(t *testing.T, ctx chasm.MutableContext, count int) *strea s := &stream.Stream{ State: &streamlib.StreamState{ - CollectionId: "col-1", - BucketSize: stream.DefaultBucketSize, - Producers: make(map[string]*streamlib.ProducerCursor), - Consumers: make(map[string]*streamlib.ConsumerCursor), + Producers: make(map[string]*streamlib.ProducerCursor), + Consumers: make(map[string]*streamlib.ConsumerCursor), }, } @@ -151,13 +149,9 @@ func TestCommitStreamCursorsWithAnEmptyRangeHoldsTheConsumer(t *testing.T) { "consuming nothing must not move the consumer") } -// Two publishes in one workflow task each stage their own batch, at the offsets -// they landed at. -// -// The offset a batch starts at is the key its row is written under, so two -// publishes must not collide and a retry of either must address the same row -// it wrote before. There is no transaction id involved any more: the store -// resolves a rewrite by replacing, and the frontier decides what a reader sees. +// Two publishes in one workflow task each write their own batch, keyed by the +// offset it starts at, so neither collides with the other and a retry of +// either addresses the same key it wrote before. func TestPublishStagesEachBatchAtItsOwnOffset(t *testing.T) { ctx := newStreamCursorTestContext() diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index 053bc452086..b01fd50bf8f 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -133,10 +133,8 @@ func (w *Workflow) SubscribeToOwnedStream( } cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ - StreamID: name, - CollectionID: state.GetCollectionId(), - BucketSize: state.GetBucketSize(), - StartOffset: startOffset, + StreamID: name, + StartOffset: startOffset, }) if err != nil { return 0, err @@ -154,15 +152,13 @@ func (w *Workflow) SubscribeToOwnedStream( return startOffset, nil } -// ExternalStreamSubscription describes a stream in another execution. The -// addressing is copied in at subscribe time so delivery never has to reach -// across executions to find the log. +// ExternalStreamSubscription describes a stream in another execution, with the +// start offset already resolved and the frontier as it was when the consumer +// was registered there. type ExternalStreamSubscription struct { - StreamID string - CollectionID string - BucketSize int64 - StartOffset int64 - KnownHead int64 + StreamID string + StartOffset int64 + KnownHead int64 } // SubscribeToExternalStream registers this workflow as a consumer of a stream @@ -181,11 +177,9 @@ func (w *Workflow) SubscribeToExternalStream( } cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ - StreamID: req.StreamID, - External: true, - CollectionID: req.CollectionID, - BucketSize: req.BucketSize, - StartOffset: req.StartOffset, + StreamID: req.StreamID, + External: true, + StartOffset: req.StartOffset, }) if err != nil { return 0, err @@ -199,9 +193,9 @@ func (w *Workflow) SubscribeToExternalStream( // inherit. // // Only subscriptions to streams in other executions are exported. A stream this -// workflow owns lives in this execution and does not itself survive the run -// transition yet (§8a), so carrying a cursor for one would leave the successor -// pointing at a stream it cannot reach. +// workflow owns lives in this execution and does not survive the run +// transition, so carrying a cursor for one would leave the successor pointing +// at a stream it cannot reach. func (w *Workflow) ExportStreamSubscriptions(ctx chasm.Context) []ExternalStreamSubscription { var out []ExternalStreamSubscription for _, field := range w.StreamCursors { @@ -210,11 +204,9 @@ func (w *Workflow) ExportStreamSubscriptions(ctx chasm.Context) []ExternalStream continue } out = append(out, ExternalStreamSubscription{ - StreamID: cursor.StreamID(), - CollectionID: cursor.CollectionID(), - BucketSize: cursor.BucketSize(), - StartOffset: cursor.Offset(), - KnownHead: cursor.KnownHead(), + StreamID: cursor.StreamID(), + StartOffset: cursor.Offset(), + KnownHead: cursor.KnownHead(), }) } // Stable order, so a successor's state does not depend on map iteration. @@ -649,11 +641,6 @@ func (w *Workflow) OwnedStream( // EnsureOwnedStream creates a stream this workflow owns if the first writer to // it is not the workflow itself, and returns its state either way. -// -// An outside writer needs the stream's collection id and bucket size to write -// its log node, and both are decided when the stream is created. So the first -// append from outside costs one transition to create the stream and learn -// them, and none after that. func (w *Workflow) EnsureOwnedStream( mctx chasm.MutableContext, name string, diff --git a/common/metrics/metric_defs.go b/common/metrics/metric_defs.go index 835400d996f..3b845ef2358 100644 --- a/common/metrics/metric_defs.go +++ b/common/metrics/metric_defs.go @@ -98,12 +98,6 @@ const ( PersistenceAppendHistoryNodesScope = "AppendHistoryNodes" // PersistenceAppendRawHistoryNodesScope tracks AppendRawHistoryNodes calls made by service to persistence layer PersistenceAppendRawHistoryNodesScope = "AppendRawHistoryNodes" - // PersistenceAppendStreamLogScope tracks AppendStreamLog calls made by service to persistence layer - PersistenceAppendStreamLogScope = "AppendStreamLog" - // PersistenceReadStreamLogScope tracks ReadStreamLog calls made by service to persistence layer - PersistenceReadStreamLogScope = "ReadStreamLog" - // PersistenceDeleteStreamLogBucketScope tracks DeleteStreamLogBucket calls made by service to persistence layer - PersistenceDeleteStreamLogBucketScope = "DeleteStreamLogBucket" // PersistenceReadHistoryBranchScope tracks ReadHistoryBranch calls made by service to persistence layer PersistenceReadHistoryBranchScope = "ReadHistoryBranch" // PersistenceReadHistoryBranchReverseScope tracks ReadHistoryBranchReverse calls made by service to persistence layer diff --git a/common/persistence/cassandra/history_store.go b/common/persistence/cassandra/history_store.go index 9a5a5c3a538..a2069e5a5f4 100644 --- a/common/persistence/cassandra/history_store.go +++ b/common/persistence/cassandra/history_store.go @@ -429,102 +429,3 @@ func convertTimeoutError(err error) error { } return err } - -const ( - // Upsert by nature in Cassandra, which is exactly the semantics wanted: the - // key is the offset a batch starts at, so a retry replaces rather than - // competes. There is no transaction-id chain to order writers against. - templateUpsertStreamLog = `INSERT INTO stream_log (` + - `shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) ` + - `VALUES (?, ?, ?, ?, ?, ?, ?, ?) ` - - // Two reads rather than a subquery, which Cassandra has no notion of. The - // first is a reverse slice of one row to find the batch containing the - // requested offset; the second reads forward from there. - templateSelectStreamLogFloor = `SELECT start_offset FROM stream_log ` + - `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` + - `AND start_offset <= ? ORDER BY start_offset DESC LIMIT 1 ` - - templateSelectStreamLog = `SELECT start_offset, data, data_encoding FROM stream_log ` + - `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` + - `AND start_offset >= ? AND start_offset < ? ` - - templateDeleteStreamLogBucket = `DELETE FROM stream_log ` + - `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` -) - -// AppendStreamLog writes one batch of a stream log. -func (h *HistoryStore) AppendStreamLog( - ctx context.Context, - request *p.InternalAppendStreamLogRequest, -) error { - query := h.Session.Query(templateUpsertStreamLog, - request.ShardID, - request.NamespaceID, - request.CollectionID, - request.Bucket, - request.StartOffset, - request.NextOffset, - request.Node.Data, - request.Node.EncodingType.String(), - ).WithContext(ctx) - if err := query.Exec(); err != nil { - return gocql.ConvertError("AppendStreamLog", err) - } - return nil -} - -// ReadStreamLog returns the batches covering the requested range, beginning -// with the batch that contains MinOffset. -func (h *HistoryStore) ReadStreamLog( - ctx context.Context, - request *p.InternalReadStreamLogRequest, -) (*p.InternalReadStreamLogResponse, error) { - from := request.MinOffset - var floor int64 - err := h.Session.Query(templateSelectStreamLogFloor, - request.ShardID, request.NamespaceID, request.CollectionID, request.Bucket, request.MinOffset, - ).WithContext(ctx).Scan(&floor) - switch { - case err == nil: - from = floor - case gocql.IsNotFoundError(err): - // Nothing at or below, so the range starts wherever it starts. - default: - return nil, gocql.ConvertError("ReadStreamLog", err) - } - - iter := h.Session.Query(templateSelectStreamLog, - request.ShardID, request.NamespaceID, request.CollectionID, request.Bucket, - from, request.MaxOffset, - ).WithContext(ctx).PageSize(request.PageSize).Iter() - - resp := &p.InternalReadStreamLogResponse{} - var startOffset int64 - var data []byte - var encoding string - for iter.Scan(&startOffset, &data, &encoding) { - resp.Batches = append(resp.Batches, p.NewDataBlob(data, encoding)) - resp.StartOffsets = append(resp.StartOffsets, startOffset) - data = nil - } - if err := iter.Close(); err != nil { - return nil, gocql.ConvertError("ReadStreamLog", err) - } - return resp, nil -} - -// DeleteStreamLogBucket drops a whole bucket, which on Cassandra is one -// partition and so one tombstone rather than a row-by-row delete. -func (h *HistoryStore) DeleteStreamLogBucket( - ctx context.Context, - request *p.InternalDeleteStreamLogBucketRequest, -) error { - query := h.Session.Query(templateDeleteStreamLogBucket, - request.ShardID, request.NamespaceID, request.CollectionID, request.Bucket, - ).WithContext(ctx) - if err := query.Exec(); err != nil { - return gocql.ConvertError("DeleteStreamLogBucket", err) - } - return nil -} diff --git a/common/persistence/data_interfaces.go b/common/persistence/data_interfaces.go index ab15e76cdb9..c818b447866 100644 --- a/common/persistence/data_interfaces.go +++ b/common/persistence/data_interfaces.go @@ -1162,13 +1162,6 @@ type ( AppendHistoryNodes(ctx context.Context, request *AppendHistoryNodesRequest) (*AppendHistoryNodesResponse, error) // AppendRawHistoryNodes add a node of raw histories to history node table AppendRawHistoryNodes(ctx context.Context, request *AppendRawHistoryNodesRequest) (*AppendHistoryNodesResponse, error) - - // Stream logs. Not history: an offset-addressed sequence a stream - // component owns, where a write is idempotent by the offset its batch - // starts at. - AppendStreamLog(ctx context.Context, request *InternalAppendStreamLogRequest) error - ReadStreamLog(ctx context.Context, request *InternalReadStreamLogRequest) (*InternalReadStreamLogResponse, error) - DeleteStreamLogBucket(ctx context.Context, request *InternalDeleteStreamLogBucketRequest) error // ReadHistoryBranch returns history node data for a branch ReadHistoryBranch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadHistoryBranchResponse, error) // ReadHistoryBranchByBatch returns history node data for a branch ByBatch @@ -1440,23 +1433,6 @@ func BuildHistoryGarbageCleanupInfo(namespaceID, workflowID, runID string) strin return fmt.Sprintf("%v:%v:%v", namespaceID, workflowID, runID) } -// NonExecutionGarbageCleanupInfoPrefix marks a history branch whose rows are -// not a workflow execution's history. -// -// The scavenger finds the execution named by a branch's cleanup info and -// deletes the branch when there is none, which is right for a workflow whose -// execution is gone and wrong for a branch that never had one. Anything -// storing rows in this table for its own purposes has to say so, or the -// scavenger reclaims live data. -const NonExecutionGarbageCleanupInfoPrefix = "non-execution:" - -// IsNonExecutionGarbageCleanupInfo reports whether a branch belongs to -// something other than a workflow execution, and so is not the scavenger's to -// collect. Whatever wrote it owns deleting it. -func IsNonExecutionGarbageCleanupInfo(info string) bool { - return strings.HasPrefix(info, NonExecutionGarbageCleanupInfoPrefix) -} - // SplitHistoryGarbageCleanupInfo returns workflow identity information func SplitHistoryGarbageCleanupInfo(info string) (namespaceID, workflowID, runID string, err error) { // Expect format: namespaceID:workflowID:runID, but workflowID may contain ':' so we diff --git a/common/persistence/data_interfaces_mock.go b/common/persistence/data_interfaces_mock.go index 4f19aece8cf..9ba28072499 100644 --- a/common/persistence/data_interfaces_mock.go +++ b/common/persistence/data_interfaces_mock.go @@ -214,20 +214,6 @@ func (mr *MockExecutionManagerMockRecorder) AppendRawHistoryNodes(ctx, request a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendRawHistoryNodes", reflect.TypeOf((*MockExecutionManager)(nil).AppendRawHistoryNodes), ctx, request) } -// AppendStreamLog mocks base method. -func (m *MockExecutionManager) AppendStreamLog(ctx context.Context, request *InternalAppendStreamLogRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppendStreamLog", ctx, request) - ret0, _ := ret[0].(error) - return ret0 -} - -// AppendStreamLog indicates an expected call of AppendStreamLog. -func (mr *MockExecutionManagerMockRecorder) AppendStreamLog(ctx, request any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendStreamLog", reflect.TypeOf((*MockExecutionManager)(nil).AppendStreamLog), ctx, request) -} - // Close mocks base method. func (m *MockExecutionManager) Close() { m.ctrl.T.Helper() @@ -326,20 +312,6 @@ func (mr *MockExecutionManagerMockRecorder) DeleteReplicationTaskFromDLQ(ctx, re return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionManager)(nil).DeleteReplicationTaskFromDLQ), ctx, request) } -// DeleteStreamLogBucket mocks base method. -func (m *MockExecutionManager) DeleteStreamLogBucket(ctx context.Context, request *InternalDeleteStreamLogBucketRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteStreamLogBucket", ctx, request) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteStreamLogBucket indicates an expected call of DeleteStreamLogBucket. -func (mr *MockExecutionManagerMockRecorder) DeleteStreamLogBucket(ctx, request any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStreamLogBucket", reflect.TypeOf((*MockExecutionManager)(nil).DeleteStreamLogBucket), ctx, request) -} - // DeleteWorkflowExecution mocks base method. func (m *MockExecutionManager) DeleteWorkflowExecution(ctx context.Context, request *DeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() @@ -604,21 +576,6 @@ func (mr *MockExecutionManagerMockRecorder) ReadRawHistoryBranch(ctx, request an return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadRawHistoryBranch", reflect.TypeOf((*MockExecutionManager)(nil).ReadRawHistoryBranch), ctx, request) } -// ReadStreamLog mocks base method. -func (m *MockExecutionManager) ReadStreamLog(ctx context.Context, request *InternalReadStreamLogRequest) (*InternalReadStreamLogResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReadStreamLog", ctx, request) - ret0, _ := ret[0].(*InternalReadStreamLogResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ReadStreamLog indicates an expected call of ReadStreamLog. -func (mr *MockExecutionManagerMockRecorder) ReadStreamLog(ctx, request any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadStreamLog", reflect.TypeOf((*MockExecutionManager)(nil).ReadStreamLog), ctx, request) -} - // SetWorkflowExecution mocks base method. func (m *MockExecutionManager) SetWorkflowExecution(ctx context.Context, request *SetWorkflowExecutionRequest) (*SetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() diff --git a/common/persistence/faultinjection/execution_store_gen.go b/common/persistence/faultinjection/execution_store_gen.go index 1b87effeaa8..a4eff149d55 100644 --- a/common/persistence/faultinjection/execution_store_gen.go +++ b/common/persistence/faultinjection/execution_store_gen.go @@ -49,15 +49,6 @@ func (d faultInjectionExecutionStore) AppendHistoryNodes(ctx context.Context, re return } -// AppendStreamLog wraps ExecutionStore.AppendStreamLog. -func (d faultInjectionExecutionStore) AppendStreamLog(ctx context.Context, request *_sourcePersistence.InternalAppendStreamLogRequest) (err error) { - err = d.generator.generate("AppendStreamLog", request).inject(func() error { - err = d.ExecutionStore.AppendStreamLog(ctx, request) - return err - }) - return -} - // CompleteHistoryTask wraps ExecutionStore.CompleteHistoryTask. func (d faultInjectionExecutionStore) CompleteHistoryTask(ctx context.Context, request *_sourcePersistence.CompleteHistoryTaskRequest) (err error) { err = d.generator.generate("CompleteHistoryTask", request).inject(func() error { @@ -121,15 +112,6 @@ func (d faultInjectionExecutionStore) DeleteReplicationTaskFromDLQ(ctx context.C return } -// DeleteStreamLogBucket wraps ExecutionStore.DeleteStreamLogBucket. -func (d faultInjectionExecutionStore) DeleteStreamLogBucket(ctx context.Context, request *_sourcePersistence.InternalDeleteStreamLogBucketRequest) (err error) { - err = d.generator.generate("DeleteStreamLogBucket", request).inject(func() error { - err = d.ExecutionStore.DeleteStreamLogBucket(ctx, request) - return err - }) - return -} - // DeleteWorkflowExecution wraps ExecutionStore.DeleteWorkflowExecution. func (d faultInjectionExecutionStore) DeleteWorkflowExecution(ctx context.Context, request *_sourcePersistence.DeleteWorkflowExecutionRequest) (err error) { err = d.generator.generate("DeleteWorkflowExecution", request).inject(func() error { @@ -256,15 +238,6 @@ func (d faultInjectionExecutionStore) ReadHistoryBranch(ctx context.Context, req return } -// ReadStreamLog wraps ExecutionStore.ReadStreamLog. -func (d faultInjectionExecutionStore) ReadStreamLog(ctx context.Context, request *_sourcePersistence.InternalReadStreamLogRequest) (ip1 *_sourcePersistence.InternalReadStreamLogResponse, err error) { - err = d.generator.generate("ReadStreamLog", request).inject(func() error { - ip1, err = d.ExecutionStore.ReadStreamLog(ctx, request) - return err - }) - return -} - // SetWorkflowExecution wraps ExecutionStore.SetWorkflowExecution. func (d faultInjectionExecutionStore) SetWorkflowExecution(ctx context.Context, request *_sourcePersistence.InternalSetWorkflowExecutionRequest) (err error) { err = d.generator.generate("SetWorkflowExecution", request).inject(func() error { diff --git a/common/persistence/history_manager.go b/common/persistence/history_manager.go index 33b46f67fd0..9777739be73 100644 --- a/common/persistence/history_manager.go +++ b/common/persistence/history_manager.go @@ -1138,29 +1138,3 @@ func (m *executionManagerImpl) serializeToken( return m.pagingTokenSerializer.Serialize(pagingToken) } - -// AppendStreamLog writes one batch of a stream log. A pass-through: there is no -// branch token to build, no chain to maintain and nothing to serialize, because -// the offset is the key and the blob is already encoded. -func (m *executionManagerImpl) AppendStreamLog( - ctx context.Context, - request *InternalAppendStreamLogRequest, -) error { - return m.persistence.AppendStreamLog(ctx, request) -} - -// ReadStreamLog returns the batches covering a range. -func (m *executionManagerImpl) ReadStreamLog( - ctx context.Context, - request *InternalReadStreamLogRequest, -) (*InternalReadStreamLogResponse, error) { - return m.persistence.ReadStreamLog(ctx, request) -} - -// DeleteStreamLogBucket drops a whole bucket. -func (m *executionManagerImpl) DeleteStreamLogBucket( - ctx context.Context, - request *InternalDeleteStreamLogBucketRequest, -) error { - return m.persistence.DeleteStreamLogBucket(ctx, request) -} diff --git a/common/persistence/mock/store_mock.go b/common/persistence/mock/store_mock.go index 397915450d6..e78040bdaf3 100644 --- a/common/persistence/mock/store_mock.go +++ b/common/persistence/mock/store_mock.go @@ -908,20 +908,6 @@ func (mr *MockExecutionStoreMockRecorder) AppendHistoryNodes(ctx, request any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendHistoryNodes", reflect.TypeOf((*MockExecutionStore)(nil).AppendHistoryNodes), ctx, request) } -// AppendStreamLog mocks base method. -func (m *MockExecutionStore) AppendStreamLog(ctx context.Context, request *persistence.InternalAppendStreamLogRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppendStreamLog", ctx, request) - ret0, _ := ret[0].(error) - return ret0 -} - -// AppendStreamLog indicates an expected call of AppendStreamLog. -func (mr *MockExecutionStoreMockRecorder) AppendStreamLog(ctx, request any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendStreamLog", reflect.TypeOf((*MockExecutionStore)(nil).AppendStreamLog), ctx, request) -} - // Close mocks base method. func (m *MockExecutionStore) Close() { m.ctrl.T.Helper() @@ -1033,20 +1019,6 @@ func (mr *MockExecutionStoreMockRecorder) DeleteReplicationTaskFromDLQ(ctx, requ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionStore)(nil).DeleteReplicationTaskFromDLQ), ctx, request) } -// DeleteStreamLogBucket mocks base method. -func (m *MockExecutionStore) DeleteStreamLogBucket(ctx context.Context, request *persistence.InternalDeleteStreamLogBucketRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteStreamLogBucket", ctx, request) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteStreamLogBucket indicates an expected call of DeleteStreamLogBucket. -func (mr *MockExecutionStoreMockRecorder) DeleteStreamLogBucket(ctx, request any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStreamLogBucket", reflect.TypeOf((*MockExecutionStore)(nil).DeleteStreamLogBucket), ctx, request) -} - // DeleteWorkflowExecution mocks base method. func (m *MockExecutionStore) DeleteWorkflowExecution(ctx context.Context, request *persistence.DeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() @@ -1280,21 +1252,6 @@ func (mr *MockExecutionStoreMockRecorder) ReadHistoryBranch(ctx, request any) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadHistoryBranch", reflect.TypeOf((*MockExecutionStore)(nil).ReadHistoryBranch), ctx, request) } -// ReadStreamLog mocks base method. -func (m *MockExecutionStore) ReadStreamLog(ctx context.Context, request *persistence.InternalReadStreamLogRequest) (*persistence.InternalReadStreamLogResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReadStreamLog", ctx, request) - ret0, _ := ret[0].(*persistence.InternalReadStreamLogResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ReadStreamLog indicates an expected call of ReadStreamLog. -func (mr *MockExecutionStoreMockRecorder) ReadStreamLog(ctx, request any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadStreamLog", reflect.TypeOf((*MockExecutionStore)(nil).ReadStreamLog), ctx, request) -} - // SetWorkflowExecution mocks base method. func (m *MockExecutionStore) SetWorkflowExecution(ctx context.Context, request *persistence.InternalSetWorkflowExecutionRequest) error { m.ctrl.T.Helper() diff --git a/common/persistence/persistence_interface.go b/common/persistence/persistence_interface.go index 9793205c75c..b95801ab8df 100644 --- a/common/persistence/persistence_interface.go +++ b/common/persistence/persistence_interface.go @@ -160,20 +160,6 @@ type ( DeleteHistoryBranch(ctx context.Context, request *InternalDeleteHistoryBranchRequest) error // GetHistoryTreeContainingBranch returns all branch information of the tree containing the specified branch GetHistoryTreeContainingBranch(ctx context.Context, request *InternalGetHistoryTreeContainingBranchRequest) (*InternalGetHistoryTreeContainingBranchResponse, error) - // The below are the stream log APIs. A stream log is not history: it is - // an offset-addressed sequence a stream component owns, and a write is - // idempotent by the offset its batch starts at rather than ordered - // against other writers by a transaction id. - - // AppendStreamLog writes one batch, replacing any batch already at that - // offset. A retry of an append addresses the same row. - AppendStreamLog(ctx context.Context, request *InternalAppendStreamLogRequest) error - // ReadStreamLog returns the batches covering a range, beginning with the - // batch that contains the first offset asked for. - ReadStreamLog(ctx context.Context, request *InternalReadStreamLogRequest) (*InternalReadStreamLogResponse, error) - // DeleteStreamLogBucket drops a whole bucket, the unit of reclamation. - DeleteStreamLogBucket(ctx context.Context, request *InternalDeleteStreamLogBucketRequest) error - // GetAllHistoryTreeBranches returns all branches of all trees. // Note that branches may be skipped or duplicated across pages if there are branches created or deleted while // paginating through results. @@ -546,44 +532,6 @@ type ( Events *commonpb.DataBlob } - // InternalAppendStreamLogRequest writes one batch of a stream log. - InternalAppendStreamLogRequest struct { - ShardID int32 - NamespaceID string - CollectionID string - Bucket int64 - StartOffset int64 - NextOffset int64 - Node *commonpb.DataBlob - } - - // InternalReadStreamLogRequest reads the batches covering - // [MinOffset, MaxOffset) from one bucket. - InternalReadStreamLogRequest struct { - ShardID int32 - NamespaceID string - CollectionID string - Bucket int64 - MinOffset int64 - MaxOffset int64 - PageSize int - } - - // InternalReadStreamLogResponse returns batches with the offset each begins - // at, so a caller can drop the part of the first batch it did not ask for. - InternalReadStreamLogResponse struct { - Batches []*commonpb.DataBlob - StartOffsets []int64 - } - - // InternalDeleteStreamLogBucketRequest drops one bucket. - InternalDeleteStreamLogBucketRequest struct { - ShardID int32 - NamespaceID string - CollectionID string - Bucket int64 - } - // InternalAppendHistoryNodesRequest is used to append a batch of history nodes InternalAppendHistoryNodesRequest struct { // The raw branch token diff --git a/common/persistence/persistence_metric_clients.go b/common/persistence/persistence_metric_clients.go index fc971a2c850..b451295c0d4 100644 --- a/common/persistence/persistence_metric_clients.go +++ b/common/persistence/persistence_metric_clients.go @@ -1462,45 +1462,3 @@ func updateErrorMetric(handler metrics.Handler, logger log.Logger, operation str } } } - -// AppendStreamLog writes one batch of a stream log -func (p *executionPersistenceClient) AppendStreamLog( - ctx context.Context, - request *InternalAppendStreamLogRequest, -) (retErr error) { - caller := headers.GetCallerInfo(ctx).CallerName - startTime := time.Now().UTC() - defer func() { - p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr) - p.recordRequestMetrics(metrics.PersistenceAppendStreamLogScope, caller, time.Since(startTime), retErr) - }() - return p.persistence.AppendStreamLog(ctx, request) -} - -// ReadStreamLog returns the batches covering a range -func (p *executionPersistenceClient) ReadStreamLog( - ctx context.Context, - request *InternalReadStreamLogRequest, -) (_ *InternalReadStreamLogResponse, retErr error) { - caller := headers.GetCallerInfo(ctx).CallerName - startTime := time.Now().UTC() - defer func() { - p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr) - p.recordRequestMetrics(metrics.PersistenceReadStreamLogScope, caller, time.Since(startTime), retErr) - }() - return p.persistence.ReadStreamLog(ctx, request) -} - -// DeleteStreamLogBucket drops a whole bucket -func (p *executionPersistenceClient) DeleteStreamLogBucket( - ctx context.Context, - request *InternalDeleteStreamLogBucketRequest, -) (retErr error) { - caller := headers.GetCallerInfo(ctx).CallerName - startTime := time.Now().UTC() - defer func() { - p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr) - p.recordRequestMetrics(metrics.PersistenceDeleteStreamLogBucketScope, caller, time.Since(startTime), retErr) - }() - return p.persistence.DeleteStreamLogBucket(ctx, request) -} diff --git a/common/persistence/persistence_rate_limited_clients.go b/common/persistence/persistence_rate_limited_clients.go index 0bbefcd0abf..2cd8fbfb097 100644 --- a/common/persistence/persistence_rate_limited_clients.go +++ b/common/persistence/persistence_rate_limited_clients.go @@ -1184,36 +1184,3 @@ func ConstructHistoryTaskAPI( ) string { return baseAPI + taskCategory.Name() } - -// AppendStreamLog writes one batch of a stream log -func (p *executionRateLimitedPersistenceClient) AppendStreamLog( - ctx context.Context, - request *InternalAppendStreamLogRequest, -) error { - if err := allow(ctx, "AppendStreamLog", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil { - return err - } - return p.persistence.AppendStreamLog(ctx, request) -} - -// ReadStreamLog returns the batches covering a range -func (p *executionRateLimitedPersistenceClient) ReadStreamLog( - ctx context.Context, - request *InternalReadStreamLogRequest, -) (*InternalReadStreamLogResponse, error) { - if err := allow(ctx, "ReadStreamLog", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil { - return nil, err - } - return p.persistence.ReadStreamLog(ctx, request) -} - -// DeleteStreamLogBucket drops a whole bucket -func (p *executionRateLimitedPersistenceClient) DeleteStreamLogBucket( - ctx context.Context, - request *InternalDeleteStreamLogBucketRequest, -) error { - if err := allow(ctx, "DeleteStreamLogBucket", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil { - return err - } - return p.persistence.DeleteStreamLogBucket(ctx, request) -} diff --git a/common/persistence/persistence_retryable_clients.go b/common/persistence/persistence_retryable_clients.go index e4533183e74..6c413fbdb2d 100644 --- a/common/persistence/persistence_retryable_clients.go +++ b/common/persistence/persistence_retryable_clients.go @@ -1260,40 +1260,3 @@ func (p *nexusEndpointRetryablePersistenceClient) DeleteNexusEndpoint( } return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) } - -// AppendStreamLog writes one batch of a stream log -func (p *executionRetryablePersistenceClient) AppendStreamLog( - ctx context.Context, - request *InternalAppendStreamLogRequest, -) error { - op := func(ctx context.Context) error { - return p.persistence.AppendStreamLog(ctx, request) - } - return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) -} - -// ReadStreamLog returns the batches covering a range -func (p *executionRetryablePersistenceClient) ReadStreamLog( - ctx context.Context, - request *InternalReadStreamLogRequest, -) (*InternalReadStreamLogResponse, error) { - var response *InternalReadStreamLogResponse - op := func(ctx context.Context) error { - var err error - response, err = p.persistence.ReadStreamLog(ctx, request) - return err - } - err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) - return response, err -} - -// DeleteStreamLogBucket drops a whole bucket -func (p *executionRetryablePersistenceClient) DeleteStreamLogBucket( - ctx context.Context, - request *InternalDeleteStreamLogBucketRequest, -) error { - op := func(ctx context.Context) error { - return p.persistence.DeleteStreamLogBucket(ctx, request) - } - return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable) -} diff --git a/common/persistence/sql/history_store.go b/common/persistence/sql/history_store.go index ae85799bcb0..f88582108d8 100644 --- a/common/persistence/sql/history_store.go +++ b/common/persistence/sql/history_store.go @@ -475,86 +475,3 @@ func (m *sqlExecutionStore) GetHistoryTreeContainingBranch( TreeInfos: treeInfos, }, nil } - -// AppendStreamLog writes one batch of a stream log. -// -// A plain upsert, which is the whole point of this table. The key is the offset -// the batch starts at, so a retry of an append replaces the row it wrote before -// rather than competing with it, and there is no transaction-id chain for two -// writers to invert. What a reader may see is decided by the frontier the -// stream component commits, not by anything here. -func (m *sqlExecutionStore) AppendStreamLog( - ctx context.Context, - request *p.InternalAppendStreamLogRequest, -) error { - nsID, err := primitives.ParseUUID(request.NamespaceID) - if err != nil { - return err - } - _, err = m.DB.InsertIntoStreamLog(ctx, &sqlplugin.StreamLogRow{ - ShardID: request.ShardID, - NamespaceID: nsID, - CollectionID: request.CollectionID, - Bucket: request.Bucket, - StartOffset: request.StartOffset, - NextOffset: request.NextOffset, - Data: request.Node.Data, - DataEncoding: request.Node.EncodingType.String(), - }) - if err != nil { - return serviceerror.NewUnavailablef("AppendStreamLog: %v", err) - } - return nil -} - -// ReadStreamLog returns the batches covering the requested range, beginning -// with the batch that contains MinOffset rather than the one that starts at it. -func (m *sqlExecutionStore) ReadStreamLog( - ctx context.Context, - request *p.InternalReadStreamLogRequest, -) (*p.InternalReadStreamLogResponse, error) { - nsID, err := primitives.ParseUUID(request.NamespaceID) - if err != nil { - return nil, err - } - rows, err := m.DB.RangeSelectFromStreamLog(ctx, sqlplugin.StreamLogSelectFilter{ - ShardID: request.ShardID, - NamespaceID: nsID, - CollectionID: request.CollectionID, - Bucket: request.Bucket, - MinOffset: request.MinOffset, - MaxOffset: request.MaxOffset, - PageSize: request.PageSize, - }) - if err != nil { - return nil, serviceerror.NewUnavailablef("ReadStreamLog: %v", err) - } - - resp := &p.InternalReadStreamLogResponse{} - for _, row := range rows { - resp.Batches = append(resp.Batches, p.NewDataBlob(row.Data, row.DataEncoding)) - resp.StartOffsets = append(resp.StartOffsets, row.StartOffset) - } - return resp, nil -} - -// DeleteStreamLogBucket drops a whole bucket. -func (m *sqlExecutionStore) DeleteStreamLogBucket( - ctx context.Context, - request *p.InternalDeleteStreamLogBucketRequest, -) error { - nsID, err := primitives.ParseUUID(request.NamespaceID) - if err != nil { - return err - } - _, err = m.DB.DeleteFromStreamLog(ctx, sqlplugin.StreamLogDeleteFilter{ - ShardID: request.ShardID, - NamespaceID: nsID, - CollectionID: request.CollectionID, - Bucket: request.Bucket, - }) - if err != nil { - return serviceerror.NewUnavailablef("DeleteStreamLogBucket: %v", err) - } - return nil -} diff --git a/common/persistence/sql/sqlplugin/interfaces.go b/common/persistence/sql/sqlplugin/interfaces.go index fbff0ab02cf..662845e9f67 100644 --- a/common/persistence/sql/sqlplugin/interfaces.go +++ b/common/persistence/sql/sqlplugin/interfaces.go @@ -53,7 +53,6 @@ type ( NexusEndpoints HistoryNode - StreamLog HistoryTree HistoryShard diff --git a/common/persistence/sql/sqlplugin/mysql/stream_log.go b/common/persistence/sql/sqlplugin/mysql/stream_log.go deleted file mode 100644 index 3c1fc4aa158..00000000000 --- a/common/persistence/sql/sqlplugin/mysql/stream_log.go +++ /dev/null @@ -1,64 +0,0 @@ -package mysql - -import ( - "context" - "database/sql" - - "go.temporal.io/server/common/persistence/sql/sqlplugin" -) - -const ( - // Upsert: the key is the offset a batch starts at, so a rewrite of that - // offset is a retry of the same append rather than a competing one. - insertStreamLogQuery = `INSERT INTO stream_log - (shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) - VALUES (:shard_id, :namespace_id, :collection_id, :bucket, :start_offset, :next_offset, :data, :data_encoding) - ON DUPLICATE KEY UPDATE next_offset = VALUES(next_offset), - data = VALUES(data), - data_encoding = VALUES(data_encoding)` - - // The floor subquery finds the batch holding an arbitrary offset: the last - // row starting at or below it. - getStreamLogQuery = `SELECT shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding ` + - `FROM stream_log ` + - `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? ` + - `AND start_offset >= COALESCE((SELECT MAX(start_offset) FROM stream_log ` + - `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? AND start_offset <= ?), ?) ` + - `AND start_offset < ? ORDER BY start_offset LIMIT ?` - - deleteStreamLogQuery = `DELETE FROM stream_log ` + - `WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ?` -) - -func (mdb *db) InsertIntoStreamLog( - ctx context.Context, - row *sqlplugin.StreamLogRow, -) (sql.Result, error) { - return mdb.NamedExecContext(ctx, insertStreamLogQuery, row) -} - -func (mdb *db) RangeSelectFromStreamLog( - ctx context.Context, - filter sqlplugin.StreamLogSelectFilter, -) ([]sqlplugin.StreamLogRow, error) { - var rows []sqlplugin.StreamLogRow - if err := mdb.SelectContext(ctx, &rows, getStreamLogQuery, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, - filter.MinOffset, - filter.MinOffset, - filter.MaxOffset, - filter.PageSize, - ); err != nil { - return nil, err - } - return rows, nil -} - -func (mdb *db) DeleteFromStreamLog( - ctx context.Context, - filter sqlplugin.StreamLogDeleteFilter, -) (sql.Result, error) { - return mdb.ExecContext(ctx, deleteStreamLogQuery, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket) -} diff --git a/common/persistence/sql/sqlplugin/postgresql/stream_log.go b/common/persistence/sql/sqlplugin/postgresql/stream_log.go deleted file mode 100644 index acc2ea0a7e1..00000000000 --- a/common/persistence/sql/sqlplugin/postgresql/stream_log.go +++ /dev/null @@ -1,65 +0,0 @@ -package postgresql - -import ( - "context" - "database/sql" - - "go.temporal.io/server/common/persistence/sql/sqlplugin" -) - -const ( - // Upsert: the key is the offset a batch starts at, so a rewrite of that - // offset is a retry of the same append rather than a competing one. - insertStreamLogQuery = `INSERT INTO stream_log - (shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) - VALUES (:shard_id, :namespace_id, :collection_id, :bucket, :start_offset, :next_offset, :data, :data_encoding) - ON CONFLICT (shard_id, namespace_id, collection_id, bucket, start_offset) - DO UPDATE SET next_offset = excluded.next_offset, - data = excluded.data, - data_encoding = excluded.data_encoding` - - // The floor subquery finds the batch holding an arbitrary offset: the last - // row starting at or below it. - getStreamLogQuery = `SELECT shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding ` + - `FROM stream_log ` + - `WHERE shard_id = $1 AND namespace_id = $2 AND collection_id = $3 AND bucket = $4 ` + - `AND start_offset >= COALESCE((SELECT MAX(start_offset) FROM stream_log ` + - `WHERE shard_id = $5 AND namespace_id = $6 AND collection_id = $7 AND bucket = $8 AND start_offset <= $9), $10) ` + - `AND start_offset < $11 ORDER BY start_offset LIMIT $12` - - deleteStreamLogQuery = `DELETE FROM stream_log ` + - `WHERE shard_id = $1 AND namespace_id = $2 AND collection_id = $3 AND bucket = $4` -) - -func (pdb *db) InsertIntoStreamLog( - ctx context.Context, - row *sqlplugin.StreamLogRow, -) (sql.Result, error) { - return pdb.NamedExecContext(ctx, insertStreamLogQuery, row) -} - -func (pdb *db) RangeSelectFromStreamLog( - ctx context.Context, - filter sqlplugin.StreamLogSelectFilter, -) ([]sqlplugin.StreamLogRow, error) { - var rows []sqlplugin.StreamLogRow - if err := pdb.SelectContext(ctx, &rows, getStreamLogQuery, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, - filter.MinOffset, - filter.MinOffset, - filter.MaxOffset, - filter.PageSize, - ); err != nil { - return nil, err - } - return rows, nil -} - -func (pdb *db) DeleteFromStreamLog( - ctx context.Context, - filter sqlplugin.StreamLogDeleteFilter, -) (sql.Result, error) { - return pdb.ExecContext(ctx, deleteStreamLogQuery, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket) -} diff --git a/common/persistence/sql/sqlplugin/sqlite/stream_log.go b/common/persistence/sql/sqlplugin/sqlite/stream_log.go deleted file mode 100644 index 7979dd9d6d4..00000000000 --- a/common/persistence/sql/sqlplugin/sqlite/stream_log.go +++ /dev/null @@ -1,72 +0,0 @@ -package sqlite - -import ( - "context" - "database/sql" - - "go.temporal.io/server/common/persistence/sql/sqlplugin" -) - -const ( - // Upsert, because the key is the offset a batch starts at and a rewrite of - // that offset is a retry of the same append rather than a competing one. - // This is the property the whole substrate exists for: there is nothing to - // order two writers against, so nothing to get wrong. - insertStreamLogQuery = `INSERT INTO stream_log - (shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding) - VALUES (:shard_id, :namespace_id, :collection_id, :bucket, :start_offset, :next_offset, :data, :data_encoding) - ON CONFLICT (shard_id, namespace_id, collection_id, bucket, start_offset) - DO UPDATE SET next_offset = excluded.next_offset, - data = excluded.data, - data_encoding = excluded.data_encoding` - - // The floor subquery is what lets a reader ask for an arbitrary offset. A - // batch covers a range, so the row holding an offset is the last one that - // starts at or below it, and only the store can find that in one step. - getStreamLogQuery = `SELECT shard_id, namespace_id, collection_id, bucket, start_offset, next_offset, data, data_encoding - FROM stream_log - WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? - AND start_offset >= COALESCE(( - SELECT MAX(start_offset) FROM stream_log - WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ? - AND start_offset <= ? - ), ?) - AND start_offset < ? - ORDER BY start_offset LIMIT ?` - - deleteStreamLogQuery = `DELETE FROM stream_log - WHERE shard_id = ? AND namespace_id = ? AND collection_id = ? AND bucket = ?` -) - -func (mdb *db) InsertIntoStreamLog( - ctx context.Context, - row *sqlplugin.StreamLogRow, -) (sql.Result, error) { - return mdb.conn.NamedExecContext(ctx, insertStreamLogQuery, row) -} - -func (mdb *db) RangeSelectFromStreamLog( - ctx context.Context, - filter sqlplugin.StreamLogSelectFilter, -) ([]sqlplugin.StreamLogRow, error) { - var rows []sqlplugin.StreamLogRow - if err := mdb.conn.SelectContext(ctx, &rows, getStreamLogQuery, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket, - filter.MinOffset, - filter.MinOffset, - filter.MaxOffset, - filter.PageSize, - ); err != nil { - return nil, err - } - return rows, nil -} - -func (mdb *db) DeleteFromStreamLog( - ctx context.Context, - filter sqlplugin.StreamLogDeleteFilter, -) (sql.Result, error) { - return mdb.conn.ExecContext(ctx, deleteStreamLogQuery, - filter.ShardID, filter.NamespaceID, filter.CollectionID, filter.Bucket) -} diff --git a/common/persistence/sql/sqlplugin/stream_log.go b/common/persistence/sql/sqlplugin/stream_log.go deleted file mode 100644 index 9a8685a36b1..00000000000 --- a/common/persistence/sql/sqlplugin/stream_log.go +++ /dev/null @@ -1,66 +0,0 @@ -package sqlplugin - -import ( - "context" - "database/sql" - - "go.temporal.io/server/common/primitives" -) - -type ( - // StreamLogRow is one appended batch, keyed by the offset its first message - // landed at. - // - // Keying by offset rather than by an opaque node id is what makes a write - // idempotent: a retry of the same append addresses the same row and - // replaces it. There is no chain to order two writers against, and no way - // for an uncommitted write to outrank a later one. The frontier the stream - // component holds is the only thing that decides what a reader may see. - StreamLogRow struct { - ShardID int32 - NamespaceID primitives.UUID - CollectionID string - Bucket int64 - StartOffset int64 - // One past the last offset this batch covers, so a reader knows whether - // the row holds the offset it asked for without decoding the blob. - // - // Not called end_offset: the schema loader scans for the SQL keyword - // `END` and a column beginning with it fails to parse. - NextOffset int64 - Data []byte - DataEncoding string - } - - // StreamLogSelectFilter reads the batches covering [MinOffset, MaxOffset). - // - // The read begins at the batch containing MinOffset, which is the greatest - // start_offset at or below it, not at MinOffset itself. A store resolves - // that itself; the caller does not have to guess how far back a batch may - // have begun. - StreamLogSelectFilter struct { - ShardID int32 - NamespaceID primitives.UUID - CollectionID string - Bucket int64 - MinOffset int64 - MaxOffset int64 - PageSize int - } - - // StreamLogDeleteFilter drops a whole bucket, which is the unit of - // reclamation. - StreamLogDeleteFilter struct { - ShardID int32 - NamespaceID primitives.UUID - CollectionID string - Bucket int64 - } - - // StreamLog is the SQL persistence interface for stream log batches. - StreamLog interface { - InsertIntoStreamLog(ctx context.Context, row *StreamLogRow) (sql.Result, error) - RangeSelectFromStreamLog(ctx context.Context, filter StreamLogSelectFilter) ([]StreamLogRow, error) - DeleteFromStreamLog(ctx context.Context, filter StreamLogDeleteFilter) (sql.Result, error) - } -) diff --git a/common/persistence/telemetry/execution_store_gen.go b/common/persistence/telemetry/execution_store_gen.go index 896bebc03d5..a079fc895b9 100644 --- a/common/persistence/telemetry/execution_store_gen.go +++ b/common/persistence/telemetry/execution_store_gen.go @@ -110,41 +110,6 @@ func (d telemetryExecutionStore) AppendHistoryNodes(ctx context.Context, request return } -// AppendStreamLog wraps ExecutionStore.AppendStreamLog. -func (d telemetryExecutionStore) AppendStreamLog(ctx context.Context, request *_sourcePersistence.InternalAppendStreamLogRequest) (err error) { - ctx, span := d.tracer.Start( - ctx, - "persistence.ExecutionStore/AppendStreamLog", - trace.WithAttributes( - attribute.Key("persistence.store").String("ExecutionStore"), - attribute.Key("persistence.method").String("AppendStreamLog"), - )) - defer span.End() - - if deadline, ok := ctx.Deadline(); ok { - span.SetAttributes(attribute.String("deadline", deadline.Format(time.RFC3339Nano))) - span.SetAttributes(attribute.String("timeout", time.Until(deadline).String())) - } - - err = d.ExecutionStore.AppendStreamLog(ctx, request) - if err != nil { - span.RecordError(err) - } - - if d.debugMode { - - requestPayload, err := json.MarshalIndent(request, "", " ") - if err != nil { - d.logger.Error("failed to serialize *_sourcePersistence.InternalAppendStreamLogRequest for OTEL span", tag.Error(err)) - } else { - span.SetAttributes(attribute.Key("persistence.request.payload").String(string(requestPayload))) - } - - } - - return -} - // CompleteHistoryTask wraps ExecutionStore.CompleteHistoryTask. func (d telemetryExecutionStore) CompleteHistoryTask(ctx context.Context, request *_sourcePersistence.CompleteHistoryTaskRequest) (err error) { ctx, span := d.tracer.Start( @@ -397,41 +362,6 @@ func (d telemetryExecutionStore) DeleteReplicationTaskFromDLQ(ctx context.Contex return } -// DeleteStreamLogBucket wraps ExecutionStore.DeleteStreamLogBucket. -func (d telemetryExecutionStore) DeleteStreamLogBucket(ctx context.Context, request *_sourcePersistence.InternalDeleteStreamLogBucketRequest) (err error) { - ctx, span := d.tracer.Start( - ctx, - "persistence.ExecutionStore/DeleteStreamLogBucket", - trace.WithAttributes( - attribute.Key("persistence.store").String("ExecutionStore"), - attribute.Key("persistence.method").String("DeleteStreamLogBucket"), - )) - defer span.End() - - if deadline, ok := ctx.Deadline(); ok { - span.SetAttributes(attribute.String("deadline", deadline.Format(time.RFC3339Nano))) - span.SetAttributes(attribute.String("timeout", time.Until(deadline).String())) - } - - err = d.ExecutionStore.DeleteStreamLogBucket(ctx, request) - if err != nil { - span.RecordError(err) - } - - if d.debugMode { - - requestPayload, err := json.MarshalIndent(request, "", " ") - if err != nil { - d.logger.Error("failed to serialize *_sourcePersistence.InternalDeleteStreamLogBucketRequest for OTEL span", tag.Error(err)) - } else { - span.SetAttributes(attribute.Key("persistence.request.payload").String(string(requestPayload))) - } - - } - - return -} - // DeleteWorkflowExecution wraps ExecutionStore.DeleteWorkflowExecution. func (d telemetryExecutionStore) DeleteWorkflowExecution(ctx context.Context, request *_sourcePersistence.DeleteWorkflowExecutionRequest) (err error) { ctx, span := d.tracer.Start( @@ -985,48 +915,6 @@ func (d telemetryExecutionStore) ReadHistoryBranch(ctx context.Context, request return } -// ReadStreamLog wraps ExecutionStore.ReadStreamLog. -func (d telemetryExecutionStore) ReadStreamLog(ctx context.Context, request *_sourcePersistence.InternalReadStreamLogRequest) (ip1 *_sourcePersistence.InternalReadStreamLogResponse, err error) { - ctx, span := d.tracer.Start( - ctx, - "persistence.ExecutionStore/ReadStreamLog", - trace.WithAttributes( - attribute.Key("persistence.store").String("ExecutionStore"), - attribute.Key("persistence.method").String("ReadStreamLog"), - )) - defer span.End() - - if deadline, ok := ctx.Deadline(); ok { - span.SetAttributes(attribute.String("deadline", deadline.Format(time.RFC3339Nano))) - span.SetAttributes(attribute.String("timeout", time.Until(deadline).String())) - } - - ip1, err = d.ExecutionStore.ReadStreamLog(ctx, request) - if err != nil { - span.RecordError(err) - } - - if d.debugMode { - - requestPayload, err := json.MarshalIndent(request, "", " ") - if err != nil { - d.logger.Error("failed to serialize *_sourcePersistence.InternalReadStreamLogRequest for OTEL span", tag.Error(err)) - } else { - span.SetAttributes(attribute.Key("persistence.request.payload").String(string(requestPayload))) - } - - responsePayload, err := json.MarshalIndent(ip1, "", " ") - if err != nil { - d.logger.Error("failed to serialize *_sourcePersistence.InternalReadStreamLogResponse for OTEL span", tag.Error(err)) - } else { - span.SetAttributes(attribute.Key("persistence.response.payload").String(string(responsePayload))) - } - - } - - return -} - // SetWorkflowExecution wraps ExecutionStore.SetWorkflowExecution. func (d telemetryExecutionStore) SetWorkflowExecution(ctx context.Context, request *_sourcePersistence.InternalSetWorkflowExecutionRequest) (err error) { ctx, span := d.tracer.Start( diff --git a/common/persistence/tests/history_store_stream_log.go b/common/persistence/tests/history_store_stream_log.go deleted file mode 100644 index dfcee03b08f..00000000000 --- a/common/persistence/tests/history_store_stream_log.go +++ /dev/null @@ -1,407 +0,0 @@ -package tests - -import ( - "time" - - "github.com/google/uuid" - commonpb "go.temporal.io/api/common/v1" - enumspb "go.temporal.io/api/enums/v1" - historypb "go.temporal.io/api/history/v1" - persistencespb "go.temporal.io/server/api/persistence/v1" - "go.temporal.io/server/chasm/lib/stream" - "go.temporal.io/server/common" - p "go.temporal.io/server/common/persistence" -) - -// Tests covering the history-node store's behaviour as a general append-only -// log, addressed by a branch whose tree ID is not a workflow run ID. -// -// These exist because a stream primitive reuses this store directly rather than -// standing up a parallel one. Two properties carry that decision: the node blob -// is never interpreted, and a stale node left behind by a shrinking retry is -// rejected on read even once the visibility frontier has moved past it. Neither -// was covered before, and the second is the one that decides whether the reuse -// is sound at all. - -// newLogBranch mints a branch whose tree ID is a fresh UUID rather than a run ID. -func (s *HistoryEventsSuite) newLogBranch() []byte { - branchID := uuid.NewString() - branchToken, err := s.store.GetHistoryBranchUtil().NewHistoryBranch( - uuid.NewString(), - uuid.NewString(), - uuid.NewString(), - uuid.NewString(), // tree ID: not a run ID - &branchID, - []*persistencespb.HistoryBranchRange{}, - time.Duration(0), - time.Duration(0), - time.Duration(0), - ) - s.NoError(err) - return branchToken -} - -func (s *HistoryEventsSuite) appendLogNode( - shardID int32, - branchToken []byte, - nodeID int64, - txnID int64, - prevTxnID int64, - blob *commonpb.DataBlob, -) { - _, err := s.store.AppendRawHistoryNodes(s.Ctx, &p.AppendRawHistoryNodesRequest{ - ShardID: shardID, - BranchToken: branchToken, - NodeID: nodeID, - TransactionID: txnID, - PrevTransactionID: prevTxnID, - IsNewBranch: nodeID == common.FirstEventID, - Info: "", - History: blob, - }) - s.NoError(err) -} - -func (s *HistoryEventsSuite) listRawLogNodes( - shardID int32, - branchToken []byte, - minNodeID int64, - maxNodeID int64, -) ([]*commonpb.DataBlob, []int64) { - var token []byte - var blobs []*commonpb.DataBlob - var nodeIDs []int64 - for doContinue := true; doContinue; doContinue = len(token) > 0 { - resp, err := s.store.ReadRawHistoryBranch(s.Ctx, &p.ReadHistoryBranchRequest{ - ShardID: shardID, - BranchToken: branchToken, - MinEventID: minNodeID, - MaxEventID: maxNodeID, - PageSize: 1, - NextPageToken: token, - }) - s.NoError(err) - token = resp.NextPageToken - blobs = append(blobs, resp.HistoryEventBlobs...) - nodeIDs = append(nodeIDs, resp.NodeIDs...) - } - return blobs, nodeIDs -} - -func (s *HistoryEventsSuite) eventIDsOf(events []*historypb.HistoryEvent) []int64 { - ids := make([]int64, len(events)) - for i, e := range events { - ids[i] = e.EventId - } - return ids -} - -// TestStreamLogBlobIsOpaque checks the store round-trips a node blob it cannot -// parse. A stream keeps application payloads here, so any attempt to interpret -// the bytes as history events would reject them. -func (s *HistoryEventsSuite) TestStreamLogBlobIsOpaque() { - branchToken := s.newLogBranch() - - payload := []byte("not a History proto \x00\x01\x02 arbitrary stream bytes") - blob := &commonpb.DataBlob{ - EncodingType: enumspb.ENCODING_TYPE_PROTO3, - Data: payload, - } - - s.appendLogNode(s.ShardID, branchToken, common.FirstEventID, 100, 0, blob) - - blobs, nodeIDs := s.listRawLogNodes(s.ShardID, branchToken, common.FirstEventID, common.FirstEventID+1) - s.Len(blobs, 1) - s.Equal([]int64{common.FirstEventID}, nodeIDs) - s.Equal(payload, blobs[0].Data) -} - -// TestStreamLogShrinkingRetryDropsStaleNode covers the case where a retry writes -// fewer nodes than the attempt it replaces, leaving a stale node past the -// retry's extent. Once later appends move the frontier beyond it, clipping the -// read range no longer hides it, so the store's transaction-ID chain has to -// reject it. -// -// Layout: node 1 commits, then a failed attempt writes nodes 11 and 13, then a -// smaller retry writes node 11 alone, then node 12 commits. Node 13 is stale -// and now sits below the frontier. -func (s *HistoryEventsSuite) TestStreamLogShrinkingRetryDropsStaleNode() { - branchToken := s.newLogBranch() - - committed := s.newHistoryEvents([]int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 100, 0) - s.appendRawHistoryBatches(s.ShardID, branchToken, committed) - - // Attempt that fails after writing both nodes; the frontier never advances. - staleFirst := s.newHistoryEvents([]int64{11, 12}, 200, 100) - s.appendRawHistoryBatches(s.ShardID, branchToken, staleFirst) - staleSecond := s.newHistoryEvents([]int64{13, 14}, 201, 200) - s.appendRawHistoryBatches(s.ShardID, branchToken, staleSecond) - - // Retry carries fewer messages, so it covers node 11 only. - retry := s.newHistoryEvents([]int64{11}, 300, 100) - s.appendRawHistoryBatches(s.ShardID, branchToken, retry) - - // Next append moves the frontier past the stale node at 13. - next := s.newHistoryEvents([]int64{12, 13, 14, 15}, 400, 300) - s.appendRawHistoryBatches(s.ShardID, branchToken, next) - - events := s.listHistoryEvents(s.ShardID, branchToken, common.FirstEventID, 16) - s.Equal( - []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - s.eventIDsOf(events), - "stale node 13 from the abandoned attempt must not be returned", - ) - - // The raw path carries no contiguity check of its own, so for a caller that - // never parses the blob the transaction-ID chain is the only thing standing - // between it and the stale node. - _, nodeIDs := s.listRawLogNodes(s.ShardID, branchToken, common.FirstEventID, 16) - s.Equal([]int64{1, 11, 12}, nodeIDs, "raw reads must drop the stale node too") -} - -// TestStreamLogTrimReclaimsStaleNodes checks that trimming against the committed -// frontier reclaims stale nodes without disturbing the valid chain. This is what -// lets a stream clean up after a failed append promptly instead of waiting on a -// background scavenger. -func (s *HistoryEventsSuite) TestStreamLogTrimReclaimsStaleNodes() { - branchToken := s.newLogBranch() - - committed := s.newHistoryEvents([]int64{1, 2, 3}, 100, 0) - s.appendRawHistoryBatches(s.ShardID, branchToken, committed) - - stale := s.newHistoryEvents([]int64{4, 5}, 200, 100) - s.appendRawHistoryBatches(s.ShardID, branchToken, stale) - - retry := s.newHistoryEvents([]int64{4}, 300, 100) - s.appendRawHistoryBatches(s.ShardID, branchToken, retry) - - s.trimHistoryBranch(s.ShardID, branchToken, 4, 300) - - events := s.listHistoryEvents(s.ShardID, branchToken, common.FirstEventID, 5) - s.Equal([]int64{1, 2, 3, 4}, s.eventIDsOf(events)) -} - -// The bucketing scheme in chasm/lib/stream splits a stream across one tree per -// fixed-size offset range, so a storage partition cannot grow with the stream. -// That also means a read restarts the transaction chain at every bucket, and -// the claim that this is still safe is the one worth testing rather than -// asserting: it is the same class of claim that turned out to be wrong about -// partition growth in the first place. - -func (s *HistoryEventsSuite) streamAppend( - collectionID string, - bucketSize int64, - firstOffset int64, - count int64, - body string, -) { - blob := &commonpb.DataBlob{ - EncodingType: enumspb.ENCODING_TYPE_PROTO3, - Data: []byte(body), - } - err := stream.WriteAppend(s.Ctx, s.store, s.ShardID, testStreamNamespaceID, collectionID, stream.LogAppend{ - Bucket: stream.BucketOf(firstOffset, bucketSize), - StartOffset: firstOffset, - NextOffset: firstOffset + count, - Blob: blob, - }) - s.NoError(err) -} - -func (s *HistoryEventsSuite) streamRead( - collectionID string, - bucketSize int64, - from int64, - to int64, -) []string { - blobs, _, err := stream.ReadRange( - s.Ctx, s.store, s.ShardID, testStreamNamespaceID, collectionID, bucketSize, from, to, 0) - s.NoError(err) - out := make([]string, len(blobs)) - for i, b := range blobs { - out[i] = string(b.Data) - } - return out -} - -const testStreamNamespaceID = "0b9d2c3a-1f4e-4a7b-9c8d-5e6f70819203" - -// TestStreamLogBucketedReadSpansTrees checks that a range read stitches buckets -// together, since each bucket is a separate tree and a caller only ever sees -// global offsets. -func (s *HistoryEventsSuite) TestStreamLogBucketedReadSpansTrees() { - collectionID := uuid.NewString() - const bucketSize = 4 - - s.streamAppend(collectionID, bucketSize, 0, 4, "bucket0") - s.streamAppend(collectionID, bucketSize, 4, 4, "bucket1") - s.streamAppend(collectionID, bucketSize, 8, 2, "bucket2") - - s.Equal([]string{"bucket0", "bucket1", "bucket2"}, s.streamRead(collectionID, bucketSize, 0, 10)) - s.Equal([]string{"bucket1"}, s.streamRead(collectionID, bucketSize, 4, 8)) -} - -// TestStreamLogBucketBoundaryDropsStaleNode is the bucket-aware form of -// TestStreamLogShrinkingRetryDropsStaleNode. An abandoned append straddles a -// bucket boundary, so the stale node lands in a tree whose chain a reader -// starts fresh. It must still be rejected once the frontier moves past it. -func (s *HistoryEventsSuite) TestStreamLogBucketBoundaryDropsStaleNode() { - collectionID := uuid.NewString() - const bucketSize = 4 - - s.streamAppend(collectionID, bucketSize, 0, 3, "committed") - - // Abandoned attempt: tail of bucket 0 plus the head of bucket 1. - s.streamAppend(collectionID, bucketSize, 3, 1, "stale-bucket0") - s.streamAppend(collectionID, bucketSize, 4, 2, "stale-bucket1") - - // Retry covers only bucket 0, so the bucket 1 node is orphaned. - s.streamAppend(collectionID, bucketSize, 3, 1, "retry") - - // A later append reaches into bucket 1, moving the frontier past the orphan. - s.streamAppend(collectionID, bucketSize, 4, 2, "real-bucket1") - - s.Equal( - []string{"committed", "retry", "real-bucket1"}, - s.streamRead(collectionID, bucketSize, 0, 6), - "the orphaned bucket 1 node must not surface once the frontier passes it", - ) -} - -// TestStreamLogOrphanFromAnotherSequenceShadowsLaterWrites is the failure the -// prototype shipped with: two producers numbering from two unrelated sequences. -// -// The chain rule keeps the highest transaction id it has seen and drops -// everything below it, which orders contested nodes correctly only while every -// writer draws from one sequence. A node left behind by a write that never -// committed still counts, because the rule reads storage and not the frontier. -// So an uncommitted node numbered from a sequence that runs ahead hides every -// later write numbered from the one that runs behind, and the reader is not -// told: it simply stops seeing new messages. -// -// The fix is one sequence per shard for every producer. This test pins the -// behaviour that made the bug invisible, so a second sequence cannot come back. -func (s *HistoryEventsSuite) TestStreamLogOrphanFromAnotherSequenceShadowsLaterWrites() { - branchToken := s.newLogBranch() - - // A committed append, numbered from the sequence the workflow used. - first := s.newHistoryEvents([]int64{1, 2}, 32, 0) - s.appendRawHistoryBatches(s.ShardID, branchToken, first) - - // A producer outside the workflow writes its node and then fails to commit, - // so the frontier never covers it. Its id comes from the shard generator and - // is far above anything the workflow task path produces. - orphan := s.newHistoryEvents([]int64{3, 4}, 5000, 32) - s.appendRawHistoryBatches(s.ShardID, branchToken, orphan) - - // Two more committed appends from the workflow. Both are numbered below the - // orphan, which is the whole problem. - second := s.newHistoryEvents([]int64{3, 4}, 33, 32) - s.appendRawHistoryBatches(s.ShardID, branchToken, second) - third := s.newHistoryEvents([]int64{5, 6}, 34, 33) - s.appendRawHistoryBatches(s.ShardID, branchToken, third) - - events := s.listHistoryEvents(s.ShardID, branchToken, common.FirstEventID, 7) - s.Equal( - []int64{1, 2, 3, 4}, - s.eventIDsOf(events), - "the orphan is served and both committed appends after it are dropped, "+ - "which is why a second transaction-id sequence loses data", - ) - - // The same log, written the way the fix writes it: every producer numbering - // from the shard, so the orphan is superseded rather than dominant. - fixed := s.newLogBranch() - s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{1, 2}, 5001, 0)) - s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{3, 4}, 5002, 5001)) - s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{3, 4}, 5003, 5001)) - s.appendRawHistoryBatches(s.ShardID, fixed, s.newHistoryEvents([]int64{5, 6}, 5004, 5003)) - - events = s.listHistoryEvents(s.ShardID, fixed, common.FirstEventID, 7) - s.Equal( - []int64{1, 2, 3, 4, 5, 6}, - s.eventIDsOf(events), - "one sequence per shard: the uncommitted node is superseded and nothing is lost", - ) -} - -// TestStreamLogAppendIsIdempotentByOffset is the property the dedicated facet -// exists for. -// -// A row is keyed by the offset its batch starts at, so a retry of an append -// addresses the row it wrote before and replaces it. There is no chain to order -// two writers against, so there is nothing for an uncommitted write to outrank, -// which is the failure the previous substrate had. -func (s *HistoryEventsSuite) TestStreamLogAppendIsIdempotentByOffset() { - const collectionID = "idempotent-by-offset" - const bucketSize = 100 - - s.streamAppend(collectionID, bucketSize, 0, 2, "first") - - // An attempt that wrote and never committed, at offsets the next attempt - // will reuse. On the old substrate this could outrank what followed. - s.streamAppend(collectionID, bucketSize, 2, 3, "abandoned") - - // The retry, covering fewer offsets from the same start. - s.streamAppend(collectionID, bucketSize, 2, 1, "retry") - - // Whatever wrote last at that offset is what is there, and nothing before - // or after it was disturbed. - s.streamAppend(collectionID, bucketSize, 3, 1, "after") - - s.Equal( - []string{"first", "retry", "after"}, - s.streamRead(collectionID, bucketSize, 0, 4), - "a rewrite at an offset replaces that batch and shadows nothing", - ) -} - -// TestStreamLogReadFindsTheBatchHoldingAnOffset checks that a read starting -// inside a batch gets the batch containing it. -// -// The caller asks for an offset, not for a batch. Only the store can find the -// row holding it, which it does in one indexed lookup because the key is the -// offset a batch starts at. The previous substrate could not, and compensated -// by reading a whole batch's worth of rows backwards on every read. -func (s *HistoryEventsSuite) TestStreamLogReadFindsTheBatchHoldingAnOffset() { - const collectionID = "mid-batch-read" - const bucketSize = 100 - - s.streamAppend(collectionID, bucketSize, 0, 10, "wide") - s.streamAppend(collectionID, bucketSize, 10, 1, "narrow") - - // Offset 4 sits inside the first batch, which starts at 0. - s.Equal( - []string{"wide", "narrow"}, - s.streamRead(collectionID, bucketSize, 4, 11), - "a read landing mid-batch must be served the batch that holds it", - ) - - // And a read starting exactly on a boundary gets only what follows. - s.Equal( - []string{"narrow"}, - s.streamRead(collectionID, bucketSize, 10, 11), - ) -} - -// TestStreamLogReadStopsAtTheRequestedEnd checks that the exclusive end of a -// read is honoured inside a bucket. -// -// A bucket is one partition, so the bucket walk alone already bounds a read to -// the buckets the range touches. Nothing above the store trims what comes back, -// which makes the end offset the store's own responsibility and worth pinning -// separately from the floor lookup. -func (s *HistoryEventsSuite) TestStreamLogReadStopsAtTheRequestedEnd() { - collectionID := uuid.NewString() - const bucketSize = 100 - - s.streamAppend(collectionID, bucketSize, 0, 10, "first") - s.streamAppend(collectionID, bucketSize, 10, 10, "second") - s.streamAppend(collectionID, bucketSize, 20, 10, "third") - - s.Equal( - []string{"first", "second"}, - s.streamRead(collectionID, bucketSize, 0, 15), - "a read must stop at its end offset even when the bucket holds more", - ) -} diff --git a/schema/cassandra/temporal/schema.cql b/schema/cassandra/temporal/schema.cql index 6a39ed08eef..9d664fe5981 100644 --- a/schema/cassandra/temporal/schema.cql +++ b/schema/cassandra/temporal/schema.cql @@ -247,18 +247,3 @@ CREATE TABLE nexus_endpoints ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; - -CREATE TABLE stream_log ( - shard_id int, - namespace_id uuid, - collection_id text, - bucket bigint, -- one bucket is one partition, bounded by offsets and by bytes - start_offset bigint, -- first offset in this batch, and the key a retry addresses - next_offset bigint, -- exclusive, so a reader knows the span without decoding - data blob, - data_encoding text, - PRIMARY KEY ((shard_id, namespace_id, collection_id, bucket), start_offset) -) WITH CLUSTERING ORDER BY (start_offset ASC) - AND COMPACTION = { - 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' -}; diff --git a/schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql b/schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql deleted file mode 100644 index f4b59e29a9d..00000000000 --- a/schema/cassandra/temporal/versioned/v1.14/add_stream_log.cql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE stream_log ( - shard_id int, - namespace_id uuid, - collection_id text, - bucket bigint, -- one bucket is one partition, bounded by offsets and by bytes - start_offset bigint, -- first offset in this batch, and the key a retry addresses - next_offset bigint, -- exclusive, so a reader knows the span without decoding - data blob, - data_encoding text, - PRIMARY KEY ((shard_id, namespace_id, collection_id, bucket), start_offset) -) WITH CLUSTERING ORDER BY (start_offset ASC) - AND COMPACTION = { - 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' -}; diff --git a/schema/cassandra/temporal/versioned/v1.14/manifest.json b/schema/cassandra/temporal/versioned/v1.14/manifest.json deleted file mode 100644 index 2df597d66c4..00000000000 --- a/schema/cassandra/temporal/versioned/v1.14/manifest.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "CurrVersion": "1.14", - "MinCompatibleVersion": "1.0", - "Description": "Adds stream_log table for server-side streams", - "SchemaUpdateCqlFiles": ["add_stream_log.cql"] -} diff --git a/schema/cassandra/version.go b/schema/cassandra/version.go index af3774cbc9f..9f666a77d4c 100644 --- a/schema/cassandra/version.go +++ b/schema/cassandra/version.go @@ -3,4 +3,4 @@ package cassandra // NOTE: whenever there is a new database schema update, plz update the following versions // Version is the Cassandra database release version -const Version = "1.14" +const Version = "1.13" diff --git a/schema/mysql/v8/temporal/schema.sql b/schema/mysql/v8/temporal/schema.sql index 24fa603c9cf..56448ce0e90 100644 --- a/schema/mysql/v8/temporal/schema.sql +++ b/schema/mysql/v8/temporal/schema.sql @@ -322,19 +322,6 @@ CREATE TABLE history_node ( PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -CREATE TABLE stream_log ( - shard_id INT NOT NULL, - namespace_id BINARY(16) NOT NULL, - collection_id VARCHAR(255) NOT NULL, - bucket BIGINT NOT NULL, - start_offset BIGINT NOT NULL, - -- - next_offset BIGINT NOT NULL, - data MEDIUMBLOB NOT NULL, - data_encoding VARCHAR(16) NOT NULL, - PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) -); - -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INT NOT NULL, diff --git a/schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql b/schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql deleted file mode 100644 index 8eefbe40b8e..00000000000 --- a/schema/mysql/v8/temporal/versioned/v1.20/add_stream_log.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE stream_log ( - shard_id INT NOT NULL, - namespace_id BINARY(16) NOT NULL, - collection_id VARCHAR(255) NOT NULL, - bucket BIGINT NOT NULL, - start_offset BIGINT NOT NULL, - -- - next_offset BIGINT NOT NULL, - data MEDIUMBLOB NOT NULL, - data_encoding VARCHAR(16) NOT NULL, - PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) -); diff --git a/schema/mysql/v8/temporal/versioned/v1.20/manifest.json b/schema/mysql/v8/temporal/versioned/v1.20/manifest.json deleted file mode 100644 index 317a9f7c755..00000000000 --- a/schema/mysql/v8/temporal/versioned/v1.20/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CurrVersion": "1.20", - "MinCompatibleVersion": "1.0", - "Description": "Adds stream_log table for server-side streams", - "SchemaUpdateCqlFiles": [ - "add_stream_log.sql" - ] -} diff --git a/schema/mysql/v8/version.go b/schema/mysql/v8/version.go index f347ffb580d..a7cfc51791f 100644 --- a/schema/mysql/v8/version.go +++ b/schema/mysql/v8/version.go @@ -3,7 +3,7 @@ package v8 // NOTE: whenever there is a new database schema update, plz update the following versions // Version is the MySQL database release version -const Version = "1.20" +const Version = "1.19" // VisibilityVersion is the MySQL visibility database release version const VisibilityVersion = "1.14" diff --git a/schema/postgresql/v12/temporal/schema.sql b/schema/postgresql/v12/temporal/schema.sql index bf33f9a4b88..b0f4874b3cd 100644 --- a/schema/postgresql/v12/temporal/schema.sql +++ b/schema/postgresql/v12/temporal/schema.sql @@ -322,19 +322,6 @@ CREATE TABLE history_node ( PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -CREATE TABLE stream_log ( - shard_id INTEGER NOT NULL, - namespace_id BYTEA NOT NULL, - collection_id VARCHAR(255) NOT NULL, - bucket BIGINT NOT NULL, - start_offset BIGINT NOT NULL, - -- - next_offset BIGINT NOT NULL, - data BYTEA NOT NULL, - data_encoding VARCHAR(16) NOT NULL, - PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) -); - -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INTEGER NOT NULL, diff --git a/schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql b/schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql deleted file mode 100644 index a263d4adf09..00000000000 --- a/schema/postgresql/v12/temporal/versioned/v1.20/add_stream_log.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE stream_log ( - shard_id INTEGER NOT NULL, - namespace_id BYTEA NOT NULL, - collection_id VARCHAR(255) NOT NULL, - bucket BIGINT NOT NULL, - start_offset BIGINT NOT NULL, - -- - next_offset BIGINT NOT NULL, - data BYTEA NOT NULL, - data_encoding VARCHAR(16) NOT NULL, - PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) -); diff --git a/schema/postgresql/v12/temporal/versioned/v1.20/manifest.json b/schema/postgresql/v12/temporal/versioned/v1.20/manifest.json deleted file mode 100644 index 317a9f7c755..00000000000 --- a/schema/postgresql/v12/temporal/versioned/v1.20/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CurrVersion": "1.20", - "MinCompatibleVersion": "1.0", - "Description": "Adds stream_log table for server-side streams", - "SchemaUpdateCqlFiles": [ - "add_stream_log.sql" - ] -} diff --git a/schema/postgresql/v12/version.go b/schema/postgresql/v12/version.go index 6965d0ca879..77e0f761e09 100644 --- a/schema/postgresql/v12/version.go +++ b/schema/postgresql/v12/version.go @@ -4,7 +4,7 @@ package v12 // Version is the Postgres database release version // Temporal supports both MySQL and Postgres officially, so upgrade should be performed for both MySQL and Postgres -const Version = "1.20" +const Version = "1.19" // VisibilityVersion is the Postgres visibility database release version // Temporal supports both MySQL and Postgres officially, so upgrade should be performed for both MySQL and Postgres diff --git a/schema/sqlite/v3/temporal/schema.sql b/schema/sqlite/v3/temporal/schema.sql index f759d240cd5..d7056640a14 100644 --- a/schema/sqlite/v3/temporal/schema.sql +++ b/schema/sqlite/v3/temporal/schema.sql @@ -319,18 +319,6 @@ CREATE TABLE history_node ( data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -CREATE TABLE stream_log ( - shard_id INT NOT NULL, - namespace_id BINARY(16) NOT NULL, - collection_id VARCHAR(255) NOT NULL, - bucket BIGINT NOT NULL, - start_offset BIGINT NOT NULL, - -- - next_offset BIGINT NOT NULL, - data MEDIUMBLOB NOT NULL, - data_encoding VARCHAR(16) NOT NULL, - PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) -); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( diff --git a/schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql b/schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql deleted file mode 100644 index 6c523d75ba2..00000000000 --- a/schema/sqlite/v3/temporal/versioned/v0.12/add_stream_log.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE stream_log ( - shard_id INT NOT NULL, - namespace_id BINARY(16) NOT NULL, - collection_id VARCHAR(255) NOT NULL, - bucket BIGINT NOT NULL, - start_offset BIGINT NOT NULL, - -- - next_offset BIGINT NOT NULL, - data MEDIUMBLOB NOT NULL, - data_encoding VARCHAR(16) NOT NULL, - PRIMARY KEY (shard_id, namespace_id, collection_id, bucket, start_offset) -); diff --git a/schema/sqlite/v3/temporal/versioned/v0.12/manifest.json b/schema/sqlite/v3/temporal/versioned/v0.12/manifest.json deleted file mode 100644 index dc05b73ee7a..00000000000 --- a/schema/sqlite/v3/temporal/versioned/v0.12/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CurrVersion": "0.12", - "MinCompatibleVersion": "0.1", - "Description": "Adds stream_log table for server-side streams", - "SchemaUpdateCqlFiles": [ - "add_stream_log.sql" - ] -} diff --git a/schema/sqlite/version.go b/schema/sqlite/version.go index 93fa1a105a9..c0d3a2ef880 100644 --- a/schema/sqlite/version.go +++ b/schema/sqlite/version.go @@ -1,7 +1,7 @@ package sqlite // Version is the SQLite database release version -const Version = "0.12" +const Version = "0.11" // VisibilityVersion is the SQLite visibility database release version const VisibilityVersion = "0.1" diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go index 07d256e6028..7adbb331816 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_slices.go +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -19,17 +19,9 @@ import ( "google.golang.org/protobuf/proto" ) -// deliverStreamSlices hands the next range of every stream this workflow -// consumes to the task being started, and stages that range on the cursor so -// the event closing the task can record what was delivered. -// -// The log read runs with the workflow lock held. That is the price of deciding -// a range and staging it in one transaction: staged first and read after, a -// failed read would leave a range that the worker never received but that the -// completion would still record as consumed. // streamOrigin says where a subscribed stream lives, which decides how its -// payload is read. The payload is component state now, so an external stream is -// read from its own execution and an owned one from the consumer's. +// payload is read: an external stream from its own execution, an owned one +// from the consumer's. type streamOrigin struct { external bool // The name the consumer knows the stream by, which is how an owned one is @@ -152,6 +144,14 @@ func DeliverStreamSlices( return live, err } +// deliverStreamSlices hands the next range of every stream this workflow +// consumes to the task being started, and stages that range on the cursor so +// the event closing the task can record what was delivered. +// +// The payload read runs with the workflow lock held. That is the price of +// deciding a range and staging it in one transaction: staged first and read +// after, a failed read would leave a range that the worker never received but +// that the completion would still record as consumed. func deliverStreamSlices( ctx context.Context, shardContext historyi.ShardContext, @@ -202,9 +202,8 @@ func deliverStreamSlices( from, to, restaged := cursor.Pending() if !restaged { from = cursor.Offset() - // Clip to the frontier. Bytes reach the log before the transaction - // that makes them visible commits, so reading past head risks - // delivering an offset whose content a retry could still replace. + // Clip to the frontier, which is the committed head for an owned + // stream and the last pushed head for an external one. to = min(from+int64(maxItems), head) } @@ -301,7 +300,7 @@ func readRecordedRange( // History holds offsets and never payloads, which is the property the whole // design rests on. The cost lands here: a worker replaying from History has to // be handed the same bytes those tasks were given, and the only place they -// exist is the stream's log. The response field alone cannot carry this, +// exist is the stream itself. The response field alone cannot carry this, // because it is built once per delivery while a cache miss replays every prior // task, so each range travels with the id of the event that recorded it. func attachReplaySlices( diff --git a/service/history/api/respondworkflowtaskcompleted/stream_appends.go b/service/history/api/respondworkflowtaskcompleted/stream_appends.go index de783932a95..9a04913d5a0 100644 --- a/service/history/api/respondworkflowtaskcompleted/stream_appends.go +++ b/service/history/api/respondworkflowtaskcompleted/stream_appends.go @@ -98,11 +98,9 @@ func resolveStagedStreamSubscriptions( } if _, err := wf.SubscribeToExternalStream(chasmCtx, chasmworkflow.ExternalStreamSubscription{ - StreamID: pending.StreamID, - CollectionID: state.GetCollectionId(), - BucketSize: state.GetBucketSize(), - StartOffset: startOffset, - KnownHead: state.GetHeadOffset(), + StreamID: pending.StreamID, + StartOffset: startOffset, + KnownHead: state.GetHeadOffset(), }); err != nil { return err } diff --git a/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go b/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go index bb7e266681e..8baf1728e6d 100644 --- a/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go +++ b/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go @@ -61,8 +61,8 @@ type ( workflowTaskDeployment *deploymentpb.Deployment // internal state - // Log writes staged by stream commands, flushed before this workflow - // task commits. + // Subscribe commands for streams in other executions, resolved before + // this workflow task commits. stagedStreamSubscriptions []chasmworkflow.PendingStreamSubscription hasBufferedEventsOrMessages bool workflowTaskFailedCause *workflowTaskFailedCause diff --git a/service/worker/scanner/history/scavenger.go b/service/worker/scanner/history/scavenger.go index a2cedbad82e..bc0543a18d1 100644 --- a/service/worker/scanner/history/scavenger.go +++ b/service/worker/scanner/history/scavenger.go @@ -216,18 +216,6 @@ func (s *Scavenger) filterTask( return nil } - // Not a workflow execution's history, so its owner deletes it and this - // scavenger must not. Checked before the parse, because the tag parses - // into a namespace that does not exist and that reads as garbage. - if persistence.IsNonExecutionGarbageCleanupInfo(branch.Info) { - metrics.HistoryScavengerSkipCount.With(s.metricsHandler).Record(1) - - s.Lock() - defer s.Unlock() - s.hbd.SkipCount++ - return nil - } - namespaceID, workflowID, runID, err := persistence.SplitHistoryGarbageCleanupInfo(branch.Info) if err != nil { s.logger.Error("unable to parse the history cleanup info", tag.DetailInfo(branch.Info), tag.Error(err)) diff --git a/service/worker/scanner/history/scavenger_test.go b/service/worker/scanner/history/scavenger_test.go index 5a12ea5a375..d9e7e45e52c 100644 --- a/service/worker/scanner/history/scavenger_test.go +++ b/service/worker/scanner/history/scavenger_test.go @@ -744,61 +744,3 @@ func (s *ScavengerTestSuite) TestDeleteWorkflowAfterRetention() { s.Equal(2, hbd.CurrentPage) s.Equal(0, len(hbd.NextPageToken)) } - -// A branch that is not a workflow execution's history must survive the -// scavenger. It deletes a branch whose execution it cannot find, and a stream -// log has no execution to find, so without the marker it reclaims live data -// once the branch passes the minimum age. -func (s *ScavengerTestSuite) TestSkipsBranchesThatAreNotExecutionHistory() { - streamInfo := persistence.NonExecutionGarbageCleanupInfoPrefix + "stream:namespaceID1:collection1" - - s.mockExecutionManager.EXPECT().GetAllHistoryTreeBranches(gomock.Any(), protomock.Eq(&persistence.GetAllHistoryTreeBranchesRequest{ - PageSize: pageSize, - })).Return(&persistence.GetAllHistoryTreeBranchesResponse{ - Branches: []persistence.HistoryBranchDetail{ - { - BranchInfo: &persistencespb.HistoryBranch{ - TreeId: treeID1, - BranchId: branchID1, - }, - ForkTime: timestamp.TimeNowPtrUtcAddDuration(-s.scavenger.historyDataMinAge() * 2), - Info: streamInfo, - }, - { - BranchInfo: &persistencespb.HistoryBranch{ - TreeId: treeID2, - BranchId: branchID2, - }, - ForkTime: timestamp.TimeNowPtrUtcAddDuration(-s.scavenger.historyDataMinAge() * 2), - Info: persistence.BuildHistoryGarbageCleanupInfo("namespaceID2", "workflowID2", "runID2"), - }, - }, - }, nil) - - // Only the workflow branch is looked up, and only it is deleted. The stream - // branch is never described, because describing it is what produced the - // not-found the scavenger read as garbage. - s.mockHistoryClient.EXPECT().DescribeMutableState(gomock.Any(), &historyservice.DescribeMutableStateRequest{ - NamespaceId: "namespaceID2", - Execution: &commonpb.WorkflowExecution{ - WorkflowId: "workflowID2", - RunId: "runID2", - }, - ArchetypeId: chasm.WorkflowArchetypeID, - }).Return(nil, serviceerror.NewNotFound("")) - branchToken2, err := s.scavenger.serializer.HistoryBranchToBlob(&persistencespb.HistoryBranch{ - TreeId: treeID2, - BranchId: branchID2, - }) - s.Require().NoError(err) - s.mockExecutionManager.EXPECT().DeleteHistoryBranch(gomock.Any(), protomock.Eq(&persistence.DeleteHistoryBranchRequest{ - ShardID: common.WorkflowIDToHistoryShard("namespaceID2", "workflowID2", s.scavenger.numShards), - BranchToken: branchToken2.Data, - })).Return(nil) - - hbd, err := s.scavenger.Run(context.Background()) - s.Require().NoError(err) - s.Equal(1, hbd.SkipCount, "the stream branch must be skipped, not collected") - s.Equal(1, hbd.SuccessCount) - s.Equal(0, hbd.ErrorCount) -} diff --git a/tests/stream_consume_test.go b/tests/stream_consume_test.go index 8848f973991..95981eda92e 100644 --- a/tests/stream_consume_test.go +++ b/tests/stream_consume_test.go @@ -487,10 +487,9 @@ func sliceForEvent(slices []*streampb.StreamSlice, eventID int64) *streampb.Stre // its own transaction, so the stream pushes it. The push dirties the consumer, // whose transaction close then sees it is behind and schedules a workflow task. // -// The log is read from the stream's own shard rather than the consumer's. -// History nodes are stored per shard, so reading an external stream from the -// consumer's shard finds nothing, and the workflow task then fails to start -// and is dropped rather than reporting anything useful. +// The payload is read from the stream's own execution, which can live on +// another shard, so delivery has to route the read rather than resolve the +// stream locally. func TestWorkflowConsumesAStreamItDoesNotOwn(t *testing.T) { env := testcore.NewEnv(t) s := newStreamTestEnvFrom(t, env) @@ -703,10 +702,10 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { // A workflow subscribing itself, rather than being subscribed out of band. // -// The command carries only the stream id and a start offset. Everything else -// the cursor needs is looked up by the server: a workflow cannot read another -// execution's collection id or bucket size without doing I/O, and a value it -// carried would be a reading rather than a fact, so it could differ on replay. +// The command carries only the stream id and a start offset. The server +// resolves the offset against the stream's frontier: a workflow cannot read +// another execution's frontier without doing I/O, and a value it carried would +// be a reading rather than a fact, so it could differ on replay. func TestWorkflowSubscribesToAStreamItself(t *testing.T) { env := testcore.NewEnv(t) s := newStreamTestEnvFrom(t, env) diff --git a/tests/stream_test.go b/tests/stream_test.go index cb261d4a7ec..2d8b50c6c3a 100644 --- a/tests/stream_test.go +++ b/tests/stream_test.go @@ -558,7 +558,7 @@ func TestStreamPollReadsOnlyWhatItReturns(t *testing.T) { "a filtered page advanced past its own bound, so the read ran to the head") } -// A stream id can be reused. The cached bytes belong to the log, not to the +// A stream id can be reused. The bytes belong to the execution, not to the // name, so a reader of the new stream must never be served the old one's. func TestStreamPollAfterIdIsReusedServesTheNewStream(t *testing.T) { s := newStreamTestEnv(t) diff --git a/tests/testcore/history_task_recorder.go b/tests/testcore/history_task_recorder.go index 83fb46b1e67..380fcdd8ecd 100644 --- a/tests/testcore/history_task_recorder.go +++ b/tests/testcore/history_task_recorder.go @@ -619,27 +619,3 @@ func (r *HistoryTaskRecorder) GetAllHistoryTreeBranches( ) (*persistence.GetAllHistoryTreeBranchesResponse, error) { return r.delegate.GetAllHistoryTreeBranches(ctx, request) } - -// Stream log pass-throughs. This recorder only watches task generation, and a -// stream log write generates none. - -func (r *HistoryTaskRecorder) AppendStreamLog( - ctx context.Context, - request *persistence.InternalAppendStreamLogRequest, -) error { - return r.delegate.AppendStreamLog(ctx, request) -} - -func (r *HistoryTaskRecorder) ReadStreamLog( - ctx context.Context, - request *persistence.InternalReadStreamLogRequest, -) (*persistence.InternalReadStreamLogResponse, error) { - return r.delegate.ReadStreamLog(ctx, request) -} - -func (r *HistoryTaskRecorder) DeleteStreamLogBucket( - ctx context.Context, - request *persistence.InternalDeleteStreamLogBucketRequest, -) error { - return r.delegate.DeleteStreamLogBucket(ctx, request) -} From bf407688943f05a5d1e6362166fe82d047114dd5 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 16:55:09 -0700 Subject: [PATCH 24/47] Routed the command-path subscription through the stream service. The completion path reached the stream with the request context's engine, which only serves shards this host owns. Registration now resolves its start offset in one transition, and routed calls made under the workflow lock carry a bounded deadline. --- chasm/lib/stream/config.go | 5 + chasm/lib/stream/service/handler.go | 56 +-- chasm/lib/stream/stream.go | 25 +- chasm/lib/stream/stream_test.go | 48 +- chasm/lib/workflow/workflow.go | 31 +- .../stream_routing.go | 39 +- .../api/respondworkflowtaskcompleted/api.go | 1 - .../stream_appends.go | 76 +-- tests/stream_routing_cross_host_test.go | 434 ++++++++++++------ 9 files changed, 459 insertions(+), 256 deletions(-) diff --git a/chasm/lib/stream/config.go b/chasm/lib/stream/config.go index 7168d46b92d..28970bd9c47 100644 --- a/chasm/lib/stream/config.go +++ b/chasm/lib/stream/config.go @@ -21,6 +21,11 @@ const LongPollTimeout = 20 * time.Second // own deadline fires. const LongPollBuffer = 3 * time.Second +// RoutedCallTimeout bounds a call to another shard made while a workflow's lock +// is held. The request's own deadline can be far longer, and a slow stream +// shard would otherwise stretch the lock hold to match it. +const RoutedCallTimeout = 5 * time.Second + // MaxConsumeItemsPerTask bounds one Workflow Task's slice. A byte cap alone is // not enough: a burst of tiny messages stays under it while still making one // task's drain arbitrarily long. Whichever bound binds first, the rest is diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index 4b32702b777..300a885be3f 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -401,10 +401,11 @@ func (h *handler) subscribeToExternalStream( // RegisterStreamConsumer takes the pin, on the shard that owns the stream. // -// Internal. Called by SubscribeWorkflow, which is routed to the consumer and so -// cannot reach the stream itself. It resolves a negative start offset here, -// where the frontier is, and hands back the resolved offset and the frontier, -// so the consumer records facts rather than readings. +// Internal. Called by SubscribeWorkflow and by the workflow task completion +// path, both of which run on the consumer's shard and so cannot reach the +// stream themselves. The start offset is resolved inside the transition that +// records it, against the same frontier it hands back, so the consumer records +// facts rather than readings. func (h *handler) RegisterStreamConsumer( ctx context.Context, req *streampb.RegisterStreamConsumerRequest, @@ -412,42 +413,29 @@ func (h *handler) RegisterStreamConsumer( in := req.GetFrontendRequest() ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) - streamID := in.GetStreamId() - ref := refFor(req.GetNamespaceId(), streamID) - - state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) - if err != nil { - return nil, err - } - - startOffset := in.GetStartOffset() - if startOffset < 0 { - startOffset = state.GetHeadOffset() - } - if startOffset < state.GetBaseOffset() { - return nil, serviceerror.NewFailedPreconditionf( - "offset %d is below the stream's floor of %d", startOffset, state.GetBaseOffset()) - } - consumerID := "workflow:" + in.GetConsumerWorkflowId() - if _, _, err := chasm.UpdateComponent( + pin, _, err := chasm.UpdateComponent( ctx, - ref, - func(s *stream.Stream, mctx chasm.MutableContext, offset int64) (struct{}, error) { - return struct{}{}, s.RegisterConsumer( + refFor(req.GetNamespaceId(), in.GetStreamId()), + func( + s *stream.Stream, mctx chasm.MutableContext, offset int64, + ) (*streampb.RegisterStreamConsumerOutput, error) { + startOffset, err := s.RegisterConsumer( mctx, consumerID, in.GetConsumerWorkflowId(), "", offset, true) + if err != nil { + return nil, err + } + return &streampb.RegisterStreamConsumerOutput{ + StartOffset: startOffset, + KnownHead: s.State.GetHeadOffset(), + }, nil }, - startOffset, - ); err != nil { + in.GetStartOffset(), + ) + if err != nil { return nil, err } - - return &streampb.RegisterStreamConsumerResponse{ - FrontendResponse: &streampb.RegisterStreamConsumerOutput{ - StartOffset: startOffset, - KnownHead: state.GetHeadOffset(), - }, - }, nil + return &streampb.RegisterStreamConsumerResponse{FrontendResponse: pin}, nil } // AdvanceConsumerHead tells one consumer that the frontier moved, on the shard diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index d1012bc253b..39ed0a1ad99 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -561,7 +561,13 @@ func (s *Stream) checkCapRoom(count int64) error { } // RegisterConsumer records an in-workflow consumer, so appends know who to wake -// and retention knows what it may not delete. +// and retention knows what it may not delete. It returns the offset the +// consumer reads from: the resolved start for a new consumer, and the current +// read position for one that is already registered. +// +// A negative offset means the head as of this transition. Resolving it here, +// against the frontier the same transaction sees, is what makes the recorded +// start a fact rather than a reading taken a moment earlier. // // The floor it records is where the subscription started, not where it has read // to. The ranges this consumer already took are written into its History, and a @@ -574,17 +580,20 @@ func (s *Stream) RegisterConsumer( runID string, offset int64, external bool, -) error { +) (int64, error) { if consumerID == "" { - return serviceerror.NewInvalidArgument("consumer id is required") + return 0, serviceerror.NewInvalidArgument("consumer id is required") + } + if offset < 0 { + offset = s.State.HeadOffset } if offset < s.State.BaseOffset { - return serviceerror.NewFailedPreconditionf( + return 0, serviceerror.NewFailedPreconditionf( "offset %d is below the stream's floor of %d", offset, s.State.BaseOffset) } if _, known := s.State.Consumers[consumerID]; !known && len(s.State.Consumers) >= MaxConsumersPerStream { - return serviceerror.NewInvalidArgumentf( + return 0, serviceerror.NewInvalidArgumentf( "stream already has %d consumers, which is the limit", MaxConsumersPerStream) } if s.State.Consumers == nil { @@ -595,12 +604,12 @@ func (s *Stream) RegisterConsumer( // stream has moved past what its History refers to. Saying so here is // the only chance to say it before the workflow depends on it again. if existing.GetReplayFloor() < s.State.BaseOffset { - return serviceerror.NewFailedPreconditionf( + return 0, serviceerror.NewFailedPreconditionf( "consumer %q recorded offset %d, and the stream now starts at %d", consumerID, existing.GetReplayFloor(), s.State.BaseOffset) } existing.Active = true - return nil + return existing.GetOffset(), nil } s.State.Consumers[consumerID] = &streampb.ConsumerCursor{ WorkflowId: workflowID, @@ -610,7 +619,7 @@ func (s *Stream) RegisterConsumer( External: external, ReplayFloor: offset, } - return nil + return offset, nil } // AdvanceConsumer moves a consumer's pin forward as it reads. It never moves diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go index f50b14aa54a..5df0a983650 100644 --- a/chasm/lib/stream/stream_test.go +++ b/chasm/lib/stream/stream_test.go @@ -214,7 +214,8 @@ func TestTruncateStopsAtAnActiveConsumersReplayFloor(t *testing.T) { _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - require.NoError(t, s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false)) + _, err = s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false) + require.NoError(t, err) s.AdvanceConsumer(nil, "wf-1", 2) // Reading to 2 is exactly what makes offsets 0 and 1 matter: they are in @@ -258,9 +259,10 @@ func TestCapTruncatesInline(t *testing.T) { func TestCapRefusesAnAppendItCouldOnlyAbsorbByDroppingReadRecords(t *testing.T) { s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} - require.NoError(t, s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false)) + _, err := s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false) + require.NoError(t, err) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.ErrorContains(t, err, "still depends on offset 0") require.Equal(t, int64(0), s.State.HeadOffset, "a refused append writes nothing") @@ -297,7 +299,8 @@ func TestRegisterConsumerPinsFromWhereItSubscribed(t *testing.T) { // Subscribing at 2 says nothing about offsets 0 and 1, so those stay // droppable and everything from 2 up does not. - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 2, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 2, false) + require.NoError(t, err) require.NoError(t, s.Truncate(nil, 2)) require.Equal(t, int64(2), s.State.BaseOffset) @@ -308,7 +311,8 @@ func TestAdvanceConsumerTracksWhereAConsumerHasReached(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + require.NoError(t, err) // The read position decides whether this consumer is worth waking. The // replay floor, not this, is what retention respects. @@ -322,7 +326,8 @@ func TestAdvanceConsumerNeverRewinds(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + require.NoError(t, err) s.AdvanceConsumer(nil, "workflow:output", 3) s.AdvanceConsumer(nil, "workflow:output", 1) @@ -337,7 +342,7 @@ func TestRegisterConsumerRejectsAnOffsetBelowTheFloor(t *testing.T) { err = s.Truncate(nil, 2) require.NoError(t, err) - err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false) require.ErrorContains(t, err, "below the stream's floor") } @@ -347,10 +352,12 @@ func TestRegisterConsumerTwiceKeepsThePin(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + require.NoError(t, err) s.AdvanceConsumer(nil, "workflow:output", 3) - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + require.NoError(t, err) require.Equal(t, int64(3), s.State.Consumers["workflow:output"].GetOffset()) } @@ -359,7 +366,8 @@ func TestDeregisterConsumerReleasesThePin(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false) + require.NoError(t, err) s.DeregisterConsumer(nil, "workflow:output") @@ -391,7 +399,8 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) require.NoError(t, err) - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + require.NoError(t, err) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 1} s.applyCap() @@ -403,7 +412,8 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + _, err := s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + require.NoError(t, err) s.DeregisterConsumer(nil, "workflow:output") for range 3 { @@ -420,13 +430,14 @@ func TestReregisteringBelowTheFloorIsRefused(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - require.NoError(t, s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false)) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + require.NoError(t, err) s.DeregisterConsumer(nil, "workflow:output") require.NoError(t, s.Truncate(nil, 2)) // Resubscribing further along does not repair the gap. What this consumer // already recorded starts at 0, and offsets 0 and 1 are gone. - err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 3, false) + _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 3, false) require.ErrorContains(t, err, "the stream now starts at 2") } @@ -475,14 +486,15 @@ func TestStreamConsumerTableIsBounded(t *testing.T) { s := newTestStream(t) for i := range MaxConsumersPerStream { - require.NoError(t, s.RegisterConsumer( - nil, fmt.Sprintf("c%d", i), "wf", "run", 0, true)) + _, err := s.RegisterConsumer(nil, fmt.Sprintf("c%d", i), "wf", "run", 0, true) + require.NoError(t, err) } - err := s.RegisterConsumer(nil, "c-over", "wf", "run", 0, true) + _, err := s.RegisterConsumer(nil, "c-over", "wf", "run", 0, true) var invalid *serviceerror.InvalidArgument require.ErrorAs(t, err, &invalid) // Re-registering an existing consumer is an update, not a new entry. - require.NoError(t, s.RegisterConsumer(nil, "c0", "wf", "run", 0, true)) + _, err = s.RegisterConsumer(nil, "c0", "wf", "run", 0, true) + require.NoError(t, err) } diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index b01fd50bf8f..478fbe2282f 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -110,18 +110,6 @@ func (w *Workflow) SubscribeToOwnedStream( return 0, serviceerror.NewNotFoundf("workflow does not own a stream named %q", name) } owned := field.Get(mctx) - state, err := owned.Snapshot(mctx, struct{}{}) - if err != nil { - return 0, err - } - - if startOffset < 0 { - startOffset = state.GetHeadOffset() - } - if startOffset < state.GetBaseOffset() { - return 0, serviceerror.NewFailedPreconditionf( - "offset %d is below the stream's floor of %d", startOffset, state.GetBaseOffset()) - } if w.StreamCursors == nil { w.StreamCursors = make(chasm.Map[string, *stream.Cursor]) @@ -132,6 +120,16 @@ func (w *Workflow) SubscribeToOwnedStream( return existing.Get(mctx).Offset(), nil } + // Pin the stream's floor in the same transaction as the cursor. Registered + // separately it could be lost while the cursor survived, and truncation + // would then be free to take a range the cursor still points at. + key := mctx.ExecutionKey() + startOffset, err := owned.RegisterConsumer( + mctx, streamConsumerID(name), key.BusinessID, key.RunID, startOffset, false) + if err != nil { + return 0, err + } + cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ StreamID: name, StartOffset: startOffset, @@ -139,15 +137,6 @@ func (w *Workflow) SubscribeToOwnedStream( if err != nil { return 0, err } - - // Pin the stream's floor in the same transaction. Registered separately it - // could be lost while the cursor survived, and truncation would then be - // free to take a range the cursor still points at. - key := mctx.ExecutionKey() - if err := owned.RegisterConsumer(mctx, streamConsumerID(name), key.BusinessID, key.RunID, startOffset, false); err != nil { - return 0, err - } - w.StreamCursors[name] = chasm.NewComponentField(mctx, cursor) return startOffset, nil } diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing.go b/service/history/api/recordworkflowtaskstarted/stream_routing.go index 770d1a2016d..4cb4a0aae50 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_routing.go +++ b/service/history/api/recordworkflowtaskstarted/stream_routing.go @@ -14,7 +14,8 @@ import ( type streamClientContextKey struct{} -// WithStreamClient supplies shard routing for standalone stream payload reads. +// WithStreamClient supplies shard routing for calls that reach a stream in +// another execution from inside a workflow task's transaction. func WithStreamClient(ctx context.Context, client streamlib.StreamServiceClient) context.Context { if client == nil { return ctx @@ -22,12 +23,27 @@ func WithStreamClient(ctx context.Context, client streamlib.StreamServiceClient) return context.WithValue(ctx, streamClientContextKey{}, client) } -func readExternalWindow(ctx context.Context, namespaceID, streamID string, from, to int64) (stream.Window, error) { +// StreamClientFromContext returns the routed client installed by the engine. +func StreamClientFromContext(ctx context.Context) (streamlib.StreamServiceClient, bool) { + client, ok := ctx.Value(streamClientContextKey{}).(streamlib.StreamServiceClient) + return client, ok +} + +// WithRoutedDeadline bounds a routed call made while the workflow lock is held. +func WithRoutedDeadline(ctx context.Context) (context.Context, context.CancelFunc) { + return context.WithTimeout(ctx, stream.RoutedCallTimeout) +} + +func readExternalWindow( + ctx context.Context, + namespaceID, streamID string, + from, to int64, +) (stream.Window, error) { if to <= from { return stream.Window{To: from}, nil } limit := int32(min(to-from, int64(stream.DefaultMaxMessagesPerPoll))) - client, ok := ctx.Value(streamClientContextKey{}).(streamlib.StreamServiceClient) + client, ok := StreamClientFromContext(ctx) if !ok { // Hand-built engines in component tests have no service client. return chasm.ReadComponent(ctx, @@ -39,7 +55,9 @@ func readExternalWindow(ctx context.Context, namespaceID, streamID string, from, stream.WindowRequest{From: from, MaxMessages: limit}) } - response, err := client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + callCtx, cancel := WithRoutedDeadline(ctx) + defer cancel() + response, err := client.PollMessages(callCtx, &streamlib.PollMessagesRequest{ NamespaceId: namespaceID, FrontendRequest: &streamlib.PollMessagesInput{ StreamId: streamID, FromOffset: from, MaxMessages: limit, @@ -52,18 +70,23 @@ func readExternalWindow(ctx context.Context, namespaceID, streamID string, from, if out == nil || out.GetNextOffset() < from || out.GetNextOffset() > to || out.GetNextOffset() > out.GetHeadOffset() || int64(len(out.GetMessages())) != out.GetNextOffset()-from { - return stream.Window{}, serviceerror.NewDataLoss("stream read returned an invalid contiguous range") + return stream.Window{}, serviceerror.NewDataLoss( + "stream read returned an invalid contiguous range") } for index, message := range out.GetMessages() { if message == nil || message.GetOffset() != from+int64(index) { - return stream.Window{}, serviceerror.NewDataLoss("stream read returned a missing or reordered offset") + return stream.Window{}, serviceerror.NewDataLoss( + "stream read returned a missing or reordered offset") } } w := stream.Window{ State: &streamlib.StreamState{ - HeadOffset: out.GetHeadOffset(), Closed: out.GetClosed(), CloseReason: out.GetCloseReason(), + HeadOffset: out.GetHeadOffset(), + Closed: out.GetClosed(), + CloseReason: out.GetCloseReason(), }, - To: out.GetNextOffset(), Limit: int(limit), + To: out.GetNextOffset(), + Limit: int(limit), } if len(out.GetMessages()) == 0 { return w, nil diff --git a/service/history/api/respondworkflowtaskcompleted/api.go b/service/history/api/respondworkflowtaskcompleted/api.go index 61520b6330f..8c4657ddae1 100644 --- a/service/history/api/respondworkflowtaskcompleted/api.go +++ b/service/history/api/respondworkflowtaskcompleted/api.go @@ -467,7 +467,6 @@ func (handler *WorkflowTaskCompletedHandler) Invoke( ctx, ms, ms.GetWorkflowKey().NamespaceID, - completedEvent.GetEventId(), workflowTaskHandler.stagedStreamSubscriptions, ); err != nil { return nil, err diff --git a/service/history/api/respondworkflowtaskcompleted/stream_appends.go b/service/history/api/respondworkflowtaskcompleted/stream_appends.go index 9a04913d5a0..a4b5d69ec88 100644 --- a/service/history/api/respondworkflowtaskcompleted/stream_appends.go +++ b/service/history/api/respondworkflowtaskcompleted/stream_appends.go @@ -4,9 +4,9 @@ import ( "context" "go.temporal.io/api/serviceerror" - "go.temporal.io/server/chasm" - "go.temporal.io/server/chasm/lib/stream" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/service/history/api/recordworkflowtaskstarted" historyi "go.temporal.io/server/service/history/interfaces" ) @@ -23,11 +23,15 @@ import ( // holding storage nothing reads, which costs space. The other order would leave // a cursor no truncation floor protects, and truncation would be free to take a // range it still points at. +// +// The pin is taken through the routed service client. The engine on this +// request context resolves shards through the local controller, which refuses +// any shard this host does not own, so a stream living elsewhere in the +// cluster can only be reached by going back out through the service. func resolveStagedStreamSubscriptions( ctx context.Context, ms historyi.MutableState, namespaceID string, - completedEventID int64, staged []chasmworkflow.PendingStreamSubscription, ) error { if len(staged) == 0 { @@ -66,41 +70,15 @@ func resolveStagedStreamSubscriptions( continue } - ref := chasm.NewComponentRef[*stream.Stream](chasm.ExecutionKey{ - NamespaceID: namespaceID, - BusinessID: pending.StreamID, - }) - - // The engine rides the request context, installed by the interceptor. - state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) + pin, err := registerExternalConsumer(ctx, ms, namespaceID, pending) if err != nil { return err } - startOffset := pending.StartOffset - if startOffset < 0 { - // Resolved once, here, and recorded. Left to delivery it would be a - // reading rather than a fact, and replay would resolve it again - // against a stream that has since moved. - startOffset = state.GetHeadOffset() - } - - consumerID := "workflow:" + ms.GetExecutionInfo().GetWorkflowId() - if _, _, err := chasm.UpdateComponent( - ctx, ref, - func(s *stream.Stream, mctx chasm.MutableContext, offset int64) (struct{}, error) { - return struct{}{}, s.RegisterConsumer( - mctx, consumerID, ms.GetExecutionInfo().GetWorkflowId(), "", offset, true) - }, - startOffset, - ); err != nil { - return err - } - if _, err := wf.SubscribeToExternalStream(chasmCtx, chasmworkflow.ExternalStreamSubscription{ StreamID: pending.StreamID, - StartOffset: startOffset, - KnownHead: state.GetHeadOffset(), + StartOffset: pin.GetStartOffset(), + KnownHead: pin.GetKnownHead(), }); err != nil { return err } @@ -109,7 +87,39 @@ func resolveStagedStreamSubscriptions( // command was, but nothing outside this transaction sees either until // the commit below, so a crash in between leaves no event claiming a // subscription that was never made. - chasmworkflow.RecordStreamSubscribedOffset(pending.Event, startOffset) + chasmworkflow.RecordStreamSubscribedOffset(pending.Event, pin.GetStartOffset()) } return nil } + +// registerExternalConsumer takes the pin on the stream's own shard and returns +// the resolved start offset and the frontier as of registration. +func registerExternalConsumer( + ctx context.Context, + ms historyi.MutableState, + namespaceID string, + pending chasmworkflow.PendingStreamSubscription, +) (*streamlib.RegisterStreamConsumerOutput, error) { + client, ok := recordworkflowtaskstarted.StreamClientFromContext(ctx) + if !ok { + return nil, serviceerror.NewInternal( + "stream service client is not available on the workflow task completion path") + } + + key := ms.GetWorkflowKey() + callCtx, cancel := recordworkflowtaskstarted.WithRoutedDeadline(ctx) + defer cancel() + registered, err := client.RegisterStreamConsumer(callCtx, &streamlib.RegisterStreamConsumerRequest{ + NamespaceId: namespaceID, + FrontendRequest: &streamlib.RegisterStreamConsumerInput{ + Namespace: ms.GetNamespaceEntry().Name().String(), + StreamId: pending.StreamID, + ConsumerWorkflowId: key.WorkflowID, + StartOffset: pending.StartOffset, + }, + }) + if err != nil { + return nil, err + } + return registered.GetFrontendResponse(), nil +} diff --git a/tests/stream_routing_cross_host_test.go b/tests/stream_routing_cross_host_test.go index 835e16c545a..a209d6007db 100644 --- a/tests/stream_routing_cross_host_test.go +++ b/tests/stream_routing_cross_host_test.go @@ -32,176 +32,344 @@ import ( "google.golang.org/protobuf/types/known/durationpb" ) +// crossHostTopology is a two-host History cluster with the shards it was +// confirmed to split, plus the consumer and source shards each case uses. +type crossHostTopology struct { + cluster *testcore.TestCluster + config *testcore.TestClusterConfig + ns string + nsID string + consumerHost string + remoteHost string + consumerShard int32 + localSource int32 + remoteSource int32 +} + +// A consumer on one History host reading a stream that lives on another. Every +// step that spans the two executions has to route rather than resolve the far +// shard locally: registering the pin, being told the frontier moved, reading +// the payload for the live task, and re-reading it for a cold replay. +// +// The subscription is made two ways, over the RPC and from the workflow's own +// command, because they take different paths to the stream's shard. func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { - output := os.Getenv("AI198_ROUTING_RESULT_DIR") - if output == "" { - output = t.TempDir() - } - require.NoError(t, os.MkdirAll(output, 0o755)) - writeProto := func(name string, message proto.Message) { - encoded, err := protojson.MarshalOptions{Indent: " "}.Marshal(message) - require.NoError(t, err) - require.NoError(t, os.WriteFile(filepath.Join(output, name+".json"), encoded, 0o644)) - binary, err := proto.Marshal(message) - require.NoError(t, err) - require.NoError(t, os.WriteFile(filepath.Join(output, name+".pb"), binary, 0o644)) - } logger := testlogger.NewTestLogger(t, testlogger.FailOnExpectedErrorOnly) - config := &testcore.TestClusterConfig{ - HistoryConfig: testcore.HistoryConfig{NumHistoryHosts: 2, NumHistoryShards: 32}, - Persistence: testcore.GetPersistenceTestDefaults(), - WorkerConfig: testcore.WorkerConfig{DisableWorker: true}, - } - cluster, err := testcore.NewTestClusterFactory().NewCluster(t, config, logger) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, cluster.TearDownCluster()) }) + topo := newCrossHostTopology(t, logger) + artifacts := newRoutingArtifacts(t) + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) defer cancel() - frontend := cluster.FrontendClient() - ns := "validation-cross-host-" + uuid.NewString() - _, err = frontend.RegisterNamespace(ctx, &workflowservice.RegisterNamespaceRequest{ - Namespace: ns, WorkflowExecutionRetentionPeriod: durationpb.New(24 * time.Hour), - }) - require.NoError(t, err) - namespace, err := frontend.DescribeNamespace(ctx, &workflowservice.DescribeNamespaceRequest{Namespace: ns}) - require.NoError(t, err) - nsID := namespace.GetNamespaceInfo().GetId() - hosts := map[string][]int32{} - for shard := int32(1); shard <= config.HistoryConfig.NumHistoryShards; shard++ { - host, describeErr := cluster.AdminClient().DescribeHistoryHost(ctx, &adminservice.DescribeHistoryHostRequest{ShardId: shard}) - require.NoError(t, describeErr) - require.NotEmpty(t, host.GetAddress()) - hosts[host.GetAddress()] = append(hosts[host.GetAddress()], shard) - } - require.Len(t, hosts, 2, "must verify two actual owners, not merely configure two services") - topology, err := json.MarshalIndent(hosts, "", " ") - require.NoError(t, err) - require.NoError(t, os.WriteFile(filepath.Join(output, "topology.json"), topology, 0o644)) - var consumerHost, remoteHost string - var consumerShard, localSourceShard, remoteSourceShard int32 - for host, shards := range hosts { - if consumerHost == "" && len(shards) >= 2 { - consumerHost, consumerShard, localSourceShard = host, shards[0], shards[1] - } - } - require.NotEmpty(t, consumerHost) - for host, shards := range hosts { - if host != consumerHost { - remoteHost, remoteSourceShard = host, shards[0] - } - } - t.Logf("Confirmed history topology: %v; consumer shard %d on %s, local source shard %d on %s, remote source shard %d on %s", - hosts, consumerShard, consumerHost, localSourceShard, consumerHost, remoteSourceShard, remoteHost) - findID := func(prefix string, target int32) string { - for i := range 10000 { - id := fmt.Sprintf("%s-%d", prefix, i) - if common.WorkflowIDToHistoryShard(nsID, id, config.HistoryConfig.NumHistoryShards) == target { - return id - } - } - t.Fatal("could not select ID on target shard") - return "" - } - conn, err := grpc.NewClient(cluster.Host().FrontendGRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) + artifacts.writeJSON("topology", topo.shardsByHost(ctx, t)) + + frontend := topo.cluster.FrontendClient() + conn, err := grpc.NewClient(topo.cluster.Host().FrontendGRPCAddress(), + grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, conn.Close()) }) streams := streamlib.NewStreamServiceClient(conn) - ownerConn, err := grpc.NewClient(consumerHost, grpc.WithTransportCredentials(insecure.NewCredentials())) + + ownerConn, err := grpc.NewClient(topo.consumerHost, + grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, ownerConn.Close()) }) owner := historyservice.NewHistoryServiceClient(ownerConn) - getHistory := func(execution *commonpb.WorkflowExecution) []*historypb.HistoryEvent { - history, getErr := frontend.GetWorkflowExecutionHistory(ctx, &workflowservice.GetWorkflowExecutionHistoryRequest{ - Namespace: ns, Execution: execution, - }) - require.NoError(t, getErr) - require.Empty(t, history.GetNextPageToken()) - return history.GetHistory().GetEvents() + + cases := []struct { + name string + crossHost bool + viaCommand bool + }{ + {name: "same-host", crossHost: false}, + {name: "cross-host", crossHost: true}, + {name: "cross-host-command", crossHost: true, viaCommand: true}, } - for _, test := range []struct { - name string - sourceShard int32 - crossHost bool - }{{"same-host", localSourceShard, false}, {"cross-host", remoteSourceShard, true}} { - t.Run(test.name, func(t *testing.T) { - id := findID(test.name+"-consumer", consumerShard) - source := findID(test.name+"-source", test.sourceShard) - _, err := streams.CreateStream(ctx, &streamlib.CreateStreamRequest{FrontendRequest: &streamlib.CreateStreamInput{Namespace: ns, StreamId: source}}) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + id := topo.idOnShard(t, tc.name+"-consumer", topo.consumerShard) + source := topo.idOnShard(t, tc.name+"-source", topo.sourceShard(tc.crossHost)) + _, err := streams.CreateStream(ctx, &streamlib.CreateStreamRequest{ + FrontendRequest: &streamlib.CreateStreamInput{Namespace: topo.ns, StreamId: source}, + }) require.NoError(t, err) + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} started, err := frontend.StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), Namespace: ns, WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "cross-host-consumer"}, TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(2 * time.Minute), WorkflowTaskTimeout: durationpb.New(10 * time.Second), + RequestId: uuid.NewString(), + Namespace: topo.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "cross-host-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(2 * time.Minute), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), }) require.NoError(t, err) execution := &commonpb.WorkflowExecution{WorkflowId: id, RunId: started.GetRunId()} + var delivered [][]*streampb.StreamSlice //nolint:staticcheck // SA1019: this test isolates the server response from SDK replay. - poller := &testcore.TaskPoller{Client: frontend, Namespace: ns, TaskQueue: tq, Identity: "cross-host-validation", Logger: logger, T: t, - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + poller := &testcore.TaskPoller{ + Client: frontend, + Namespace: topo.ns, + TaskQueue: tq, + Identity: "cross-host-validation", + Logger: logger, + T: t, + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) + if tc.viaCommand && len(delivered) == 1 { + return subscribeCommand(source), nil + } return nil, nil }, } _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - for _, target := range []struct{ id, host string }{{id, consumerHost}, {source, map[bool]string{false: consumerHost, true: remoteHost}[test.crossHost]}} { - desc, describeErr := cluster.AdminClient().DescribeHistoryHost(ctx, &adminservice.DescribeHistoryHostRequest{ - Namespace: ns, WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: target.id}, + + topo.requireOwner(ctx, t, id, topo.consumerHost) + topo.requireOwner(ctx, t, source, topo.hostFor(tc.crossHost)) + + if !tc.viaCommand { + _, err = streams.SubscribeWorkflow(ctx, &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: topo.ns, WorkflowId: id, StreamId: source, + }, }) - require.NoError(t, describeErr) - require.Equal(t, target.host, desc.GetAddress()) - t.Logf("Resolved %s to %s, loaded shards %v", target.id, desc.GetAddress(), desc.GetShardIds()) + require.NoError(t, err) } - _, err = streams.SubscribeWorkflow(ctx, &streamlib.SubscribeWorkflowRequest{FrontendRequest: &streamlib.SubscribeWorkflowInput{ - Namespace: ns, WorkflowId: id, StreamId: source, - }}) + // Either way the pin has to have landed on the stream's own shard. + desc, err := streams.DescribeStream(ctx, &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: topo.ns, StreamId: source}, + }) require.NoError(t, err) - _, err = streams.AddMessages(ctx, &streamlib.AddMessagesRequest{FrontendRequest: &streamlib.AddMessagesInput{ - Namespace: ns, StreamId: source, Messages: streamMsgs("tokens", "retained-input"), - }}) + require.Len(t, desc.GetFrontendResponse().GetState().GetConsumers(), 1) + + _, err = streams.AddMessages(ctx, &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: topo.ns, StreamId: source, + Messages: streamMsgs("tokens", "retained-input"), + }, + }) require.NoError(t, err) + + history := func() []*historypb.HistoryEvent { + return topo.history(ctx, t, execution) + } latestScheduled := func() int64 { var scheduled int64 - for _, event := range getHistory(execution) { + for _, event := range history() { if event.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_TASK_SCHEDULED { scheduled = event.GetEventId() } } return scheduled } - await.RequireTrue(t, func() bool { return latestScheduled() > 2 }, 10*time.Second, 20*time.Millisecond) - { - _, err = poller.PollAndProcessWorkflowTask() - require.NoError(t, err) - require.Equal(t, "retained-input", string(currentSlice(t, delivered[1]).GetMessages()[0].GetBody().GetData())) - writeProto(test.name+"-live-slice", currentSlice(t, delivered[1])) - writeProto(test.name+"-committed-history", &historypb.History{Events: getHistory(execution)}) - consumedAt := completedEventWithCursors(t, getHistory(execution)) - _, err = owner.CloseShard(ctx, &historyservice.CloseShardRequest{ShardId: consumerShard}) - require.NoError(t, err) - _, err = frontend.SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{ - Namespace: ns, WorkflowExecution: execution, SignalName: "cold-replay-boundary", RequestId: uuid.NewString(), - }) - require.NoError(t, err) - response, err := owner.RecordWorkflowTaskStarted(ctx, &historyservice.RecordWorkflowTaskStartedRequest{ - NamespaceId: nsID, WorkflowExecution: execution, ScheduledEventId: latestScheduled(), RequestId: uuid.NewString(), - PollRequest: &workflowservice.PollWorkflowTaskQueueRequest{Namespace: ns, TaskQueue: tq, Identity: "cross-host-validation"}, - }) - require.NoError(t, err) - writeProto(test.name+"-cold-response", response) - found := false - for _, slice := range response.GetStreamSlices() { - if slice.GetWorkflowTaskCompletedEventId() == consumedAt { - require.Equal(t, "retained-input", string(slice.GetMessages()[0].GetBody().GetData())) - found = true - } - } - require.True(t, found, "cold replay must re-supply the committed input range") - t.Logf("%s delivered and committed the input, then re-supplied it after consumer CloseShard at completion %d; consumer=%s source=%s", test.name, consumedAt, consumerHost, map[bool]string{false: consumerHost, true: remoteHost}[test.crossHost]) - return - } + // The append alone has to produce the task, through the routed push. + await.RequireTrue(t, func() bool { return latestScheduled() > 2 }, + 10*time.Second, 20*time.Millisecond) + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + live := currentSlice(t, delivered[1]) + require.Equal(t, "retained-input", string(live.GetMessages()[0].GetBody().GetData())) + artifacts.writeProto(tc.name+"-live-slice", live) + artifacts.writeProto(tc.name+"-committed-history", &historypb.History{Events: history()}) + consumedAt := completedEventWithCursors(t, history()) + + // Evict the consumer, then start its next task by hand so the cold + // response can be inspected before matching hands it to a poller. + _, err = owner.CloseShard(ctx, &historyservice.CloseShardRequest{ShardId: topo.consumerShard}) + require.NoError(t, err) + _, err = frontend.SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: topo.ns, + WorkflowExecution: execution, + SignalName: "cold-replay-boundary", + RequestId: uuid.NewString(), + }) + require.NoError(t, err) + response, err := owner.RecordWorkflowTaskStarted(ctx, &historyservice.RecordWorkflowTaskStartedRequest{ + NamespaceId: topo.nsID, + WorkflowExecution: execution, + ScheduledEventId: latestScheduled(), + RequestId: uuid.NewString(), + PollRequest: &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: topo.ns, TaskQueue: tq, Identity: "cross-host-validation", + }, + }) + require.NoError(t, err) + artifacts.writeProto(tc.name+"-cold-response", response) + + replayed := sliceForEvent(response.GetStreamSlices(), consumedAt) + require.NotNil(t, replayed, "cold replay must re-supply the committed input range") + require.Equal(t, "retained-input", string(replayed.GetMessages()[0].GetBody().GetData())) }) } } + +func subscribeCommand(streamID string) []*commandpb.Command { + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM, + Attributes: &commandpb.Command_SubscribeStreamCommandAttributes{ + SubscribeStreamCommandAttributes: &commandpb.SubscribeStreamCommandAttributes{ + StreamId: streamID, StartOffset: 0, + }, + }, + }} +} + +func newCrossHostTopology(t *testing.T, logger *testlogger.TestLogger) *crossHostTopology { + t.Helper() + config := &testcore.TestClusterConfig{ + HistoryConfig: testcore.HistoryConfig{NumHistoryHosts: 2, NumHistoryShards: 32}, + Persistence: testcore.GetPersistenceTestDefaults(), + WorkerConfig: testcore.WorkerConfig{DisableWorker: true}, + } + cluster, err := testcore.NewTestClusterFactory().NewCluster(t, config, logger) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, cluster.TearDownCluster()) }) + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + frontend := cluster.FrontendClient() + ns := "validation-cross-host-" + uuid.NewString() + _, err = frontend.RegisterNamespace(ctx, &workflowservice.RegisterNamespaceRequest{ + Namespace: ns, WorkflowExecutionRetentionPeriod: durationpb.New(24 * time.Hour), + }) + require.NoError(t, err) + described, err := frontend.DescribeNamespace(ctx, &workflowservice.DescribeNamespaceRequest{ + Namespace: ns, + }) + require.NoError(t, err) + + topo := &crossHostTopology{ + cluster: cluster, + config: config, + ns: ns, + nsID: described.GetNamespaceInfo().GetId(), + } + hosts := topo.shardsByHost(ctx, t) + require.Len(t, hosts, 2, "must verify two actual owners, not merely configure two services") + for host, shards := range hosts { + if topo.consumerHost == "" && len(shards) >= 2 { + topo.consumerHost, topo.consumerShard, topo.localSource = host, shards[0], shards[1] + } + } + require.NotEmpty(t, topo.consumerHost) + for host, shards := range hosts { + if host != topo.consumerHost { + topo.remoteHost, topo.remoteSource = host, shards[0] + } + } + t.Logf("consumer shard %d on %s, local source shard %d, remote source shard %d on %s", + topo.consumerShard, topo.consumerHost, topo.localSource, topo.remoteSource, topo.remoteHost) + return topo +} + +// shardsByHost asks the admin service who owns every shard. +func (topo *crossHostTopology) shardsByHost(ctx context.Context, t *testing.T) map[string][]int32 { + t.Helper() + hosts := map[string][]int32{} + for shard := int32(1); shard <= topo.config.HistoryConfig.NumHistoryShards; shard++ { + host, err := topo.cluster.AdminClient().DescribeHistoryHost(ctx, + &adminservice.DescribeHistoryHostRequest{ShardId: shard}) + require.NoError(t, err) + require.NotEmpty(t, host.GetAddress()) + hosts[host.GetAddress()] = append(hosts[host.GetAddress()], shard) + } + return hosts +} + +func (topo *crossHostTopology) hostFor(crossHost bool) string { + if crossHost { + return topo.remoteHost + } + return topo.consumerHost +} + +func (topo *crossHostTopology) sourceShard(crossHost bool) int32 { + if crossHost { + return topo.remoteSource + } + return topo.localSource +} + +// idOnShard finds a business id that hashes to the wanted shard. +func (topo *crossHostTopology) idOnShard(t *testing.T, prefix string, target int32) string { + t.Helper() + shards := topo.config.HistoryConfig.NumHistoryShards + for i := range 10000 { + id := fmt.Sprintf("%s-%d", prefix, i) + if common.WorkflowIDToHistoryShard(topo.nsID, id, shards) == target { + return id + } + } + t.Fatal("could not select an id on the target shard") + return "" +} + +func (topo *crossHostTopology) requireOwner( + ctx context.Context, t *testing.T, businessID string, host string, +) { + t.Helper() + desc, err := topo.cluster.AdminClient().DescribeHistoryHost(ctx, &adminservice.DescribeHistoryHostRequest{ + Namespace: topo.ns, + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: businessID}, + }) + require.NoError(t, err) + require.Equal(t, host, desc.GetAddress()) +} + +func (topo *crossHostTopology) history( + ctx context.Context, t *testing.T, execution *commonpb.WorkflowExecution, +) []*historypb.HistoryEvent { + t.Helper() + resp, err := topo.cluster.FrontendClient().GetWorkflowExecutionHistory(ctx, + &workflowservice.GetWorkflowExecutionHistoryRequest{Namespace: topo.ns, Execution: execution}) + require.NoError(t, err) + require.Empty(t, resp.GetNextPageToken()) + return resp.GetHistory().GetEvents() +} + +// routingArtifacts writes the responses this test observed to a directory, for +// the SDK validation runs that compare them against what a worker receives. Off +// unless the measurement switch is set, like the other evidence collectors. +type routingArtifacts struct { + t *testing.T + dir string +} + +func newRoutingArtifacts(t *testing.T) *routingArtifacts { + t.Helper() + if os.Getenv("TEMPORAL_STREAM_BENCH") != "1" { + return &routingArtifacts{t: t} + } + dir := os.Getenv("AI198_ROUTING_RESULT_DIR") + if dir == "" { + dir = t.TempDir() + } + require.NoError(t, os.MkdirAll(dir, 0o755)) + return &routingArtifacts{t: t, dir: dir} +} + +func (a *routingArtifacts) writeJSON(name string, value any) { + if a.dir == "" { + return + } + encoded, err := json.MarshalIndent(value, "", " ") + require.NoError(a.t, err) + require.NoError(a.t, os.WriteFile(filepath.Join(a.dir, name+".json"), encoded, 0o644)) +} + +func (a *routingArtifacts) writeProto(name string, message proto.Message) { + if a.dir == "" { + return + } + encoded, err := protojson.MarshalOptions{Indent: " "}.Marshal(message) + require.NoError(a.t, err) + require.NoError(a.t, os.WriteFile(filepath.Join(a.dir, name+".json"), encoded, 0o644)) + binary, err := proto.Marshal(message) + require.NoError(a.t, err) + require.NoError(a.t, os.WriteFile(filepath.Join(a.dir, name+".pb"), binary, 0o644)) +} From dee9c68065f9d3209ad137e9712829aacc0720b7 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:13:44 -0700 Subject: [PATCH 25/47] Failed the workflow task when a stream range cannot be served. Cold replay walks every page of history for the recorded ranges instead of stopping at the first. A range that is truncated, deleted or over the replay budget fails the task with its own cause rather than handing matching a retry. Slices now name the run that holds the stream. --- .../gen/streampb/v1/request_response.pb.go | 27 +- .../stream/proto/v1/request_response.proto | 3 + chasm/lib/stream/service/handler.go | 1 + chasm/lib/stream/stream.go | 10 +- .../api/recordworkflowtaskstarted/api.go | 79 ++++- .../stream_routing.go | 1 + .../stream_slices.go | 325 ++++++++++++----- tests/stream_replay_test.go | 332 ++++++++++++++++++ 8 files changed, 670 insertions(+), 108 deletions(-) create mode 100644 tests/stream_replay_test.go diff --git a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go index 051af5c44af..7222ea799b4 100644 --- a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go @@ -636,12 +636,15 @@ func (x *PollMessagesInput) GetWaitNewMessages() bool { } type PollMessagesOutput struct { - state protoimpl.MessageState `protogen:"open.v1"` - Messages []*StreamMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` - NextOffset int64 `protobuf:"varint,2,opt,name=next_offset,json=nextOffset,proto3" json:"next_offset,omitempty"` - HeadOffset int64 `protobuf:"varint,3,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` - Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` - CloseReason *v1.Payload `protobuf:"bytes,5,opt,name=close_reason,json=closeReason,proto3" json:"close_reason,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Messages []*StreamMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + NextOffset int64 `protobuf:"varint,2,opt,name=next_offset,json=nextOffset,proto3" json:"next_offset,omitempty"` + HeadOffset int64 `protobuf:"varint,3,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` + Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` + CloseReason *v1.Payload `protobuf:"bytes,5,opt,name=close_reason,json=closeReason,proto3" json:"close_reason,omitempty"` + // The execution holding the stream. A workflow task slice built from this + // read names the run it came from. + RunId string `protobuf:"bytes,6,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -711,6 +714,13 @@ func (x *PollMessagesOutput) GetCloseReason() *v1.Payload { return nil } +func (x *PollMessagesOutput) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + type DescribeStreamInput struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` @@ -3275,7 +3285,7 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "fromOffset\x12!\n" + "\fmax_messages\x18\x04 \x01(\x05R\vmaxMessages\x12\x16\n" + "\x06topics\x18\x05 \x03(\tR\x06topics\x12*\n" + - "\x11wait_new_messages\x18\x06 \x01(\bR\x0fwaitNewMessages\"\x88\x02\n" + + "\x11wait_new_messages\x18\x06 \x01(\bR\x0fwaitNewMessages\"\x9f\x02\n" + "\x12PollMessagesOutput\x12T\n" + "\bmessages\x18\x01 \x03(\v28.temporal.server.chasm.lib.stream.proto.v1.StreamMessageR\bmessages\x12\x1f\n" + "\vnext_offset\x18\x02 \x01(\x03R\n" + @@ -3283,7 +3293,8 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\vhead_offset\x18\x03 \x01(\x03R\n" + "headOffset\x12\x16\n" + "\x06closed\x18\x04 \x01(\bR\x06closed\x12B\n" + - "\fclose_reason\x18\x05 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\vcloseReason\"P\n" + + "\fclose_reason\x18\x05 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\vcloseReason\x12\x15\n" + + "\x06run_id\x18\x06 \x01(\tR\x05runId\"P\n" + "\x13DescribeStreamInput\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + "\tstream_id\x18\x02 \x01(\tR\bstreamId\"\xa5\x02\n" + diff --git a/chasm/lib/stream/proto/v1/request_response.proto b/chasm/lib/stream/proto/v1/request_response.proto index c2ab498d415..12ab6a4df1f 100644 --- a/chasm/lib/stream/proto/v1/request_response.proto +++ b/chasm/lib/stream/proto/v1/request_response.proto @@ -107,6 +107,9 @@ message PollMessagesOutput { int64 head_offset = 3; bool closed = 4; temporal.api.common.v1.Payload close_reason = 5; + // The execution holding the stream. A workflow task slice built from this + // read names the run it came from. + string run_id = 6; } message DescribeStreamInput { diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index 300a885be3f..98de1199aa0 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -551,6 +551,7 @@ func formatWindow(w stream.Window, req stream.WindowRequest) (*streampb.PollMess HeadOffset: w.State.GetHeadOffset(), Closed: w.State.GetClosed(), CloseReason: w.State.GetCloseReason(), + RunId: w.RunID, } if req.From == w.State.GetHeadOffset() { return out, nil diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index 39ed0a1ad99..f041209a6de 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -430,6 +430,9 @@ type Window struct { Starts []int64 To int64 Limit int + // The execution holding the stream, so a slice built from this window can + // say which run it came from. + RunID string } // ReadWindow serves a read from the component, so the frontier and the bytes @@ -451,7 +454,12 @@ func (s *Stream) ReadWindow(ctx chasm.Context, req WindowRequest) (Window, error if limit <= 0 || limit > DefaultMaxMessagesPerPoll { limit = DefaultMaxMessagesPerPoll } - w := Window{State: common.CloneProto(s.State), To: req.From, Limit: limit} + w := Window{ + State: common.CloneProto(s.State), + To: req.From, + Limit: limit, + RunID: ctx.ExecutionKey().RunID, + } if req.From == s.State.HeadOffset { return w, nil } diff --git a/service/history/api/recordworkflowtaskstarted/api.go b/service/history/api/recordworkflowtaskstarted/api.go index 98650b444ab..2c096720dc0 100644 --- a/service/history/api/recordworkflowtaskstarted/api.go +++ b/service/history/api/recordworkflowtaskstarted/api.go @@ -52,6 +52,10 @@ func Invoke( var workflowKey definition.WorkflowKey var resp *historyservice.RecordWorkflowTaskStartedResponseWithRawHistory var streamAddresses map[string]streamOrigin + // Set when a stream range the task depends on cannot be served. The task + // is failed inside the transaction, so the failure has to be persisted + // and reported to matching afterwards rather than returned as the error. + var streamFailure *rangeUnavailable err = api.GetAndUpdateWorkflowWithNew( ctx, @@ -105,7 +109,14 @@ func Invoke( } // Redelivers whatever range is already staged, so a // duplicate of the same request hands back the same slice. - resp.StreamSlices, streamAddresses, err = deliverStreamSlices(ctx, shardContext, mutableState) + resp.StreamSlices, streamAddresses, err = deliverStreamSlices( + ctx, shardContext, mutableState) + if errors.As(err, &streamFailure) { + if err := failTaskForStreams(mutableState, workflowTask, streamFailure); err != nil { + return nil, err + } + return updateAction, nil + } if err != nil { return nil, err } @@ -245,7 +256,15 @@ func Invoke( return nil, err } - resp.StreamSlices, streamAddresses, err = deliverStreamSlices(ctx, shardContext, mutableState) + resp.StreamSlices, streamAddresses, err = deliverStreamSlices( + ctx, shardContext, mutableState) + if errors.As(err, &streamFailure) { + if err := failTaskForStreams(mutableState, workflowTask, streamFailure); err != nil { + return nil, err + } + updateAction.Noop = false + return updateAction, nil + } if err != nil { return nil, err } @@ -260,6 +279,11 @@ func Invoke( if err != nil { return nil, err } + if streamFailure != nil { + // The failure is in History and the next attempt is scheduled, so this + // task is stale; matching drops it rather than retrying it. + return nil, serviceerrors.NewObsoleteMatchingTask(streamFailure.Error()) + } maxHistoryPageSize := int32(config.HistoryMaxPageSize(namespaceEntry.Name().String())) err = setHistoryForRecordWfTaskStartedResp( @@ -279,12 +303,61 @@ func Invoke( // After the history is attached, because the ranges to re-supply are read // out of the events being sent. - if err := attachReplaySlices(ctx, workflowKey, workflowKey.GetNamespaceID(), streamAddresses, resp); err != nil { + err = attachReplaySlices(ctx, shardContext, workflowKey, streamAddresses, maxHistoryPageSize, resp) + if errors.As(err, &streamFailure) { + // The task is already started and its lock released, so failing it is + // a transaction of its own. + if err := failStartedTaskForStreams( + ctx, shardContext, workflowConsistencyChecker, workflowKey, + scheduledEventID, requestID, streamFailure, + ); err != nil { + return nil, err + } + return nil, serviceerrors.NewObsoleteMatchingTask(streamFailure.Error()) + } + if err != nil { return nil, err } return resp, nil } +// failStartedTaskForStreams fails a task that started but cannot be replayed, +// provided it is still the task this request started. +func failStartedTaskForStreams( + ctx context.Context, + shardContext historyi.ShardContext, + workflowConsistencyChecker api.WorkflowConsistencyChecker, + workflowKey definition.WorkflowKey, + scheduledEventID int64, + requestID string, + cause *rangeUnavailable, +) error { + return api.GetAndUpdateWorkflowWithNew( + ctx, + nil, + workflowKey, + func(workflowLease api.WorkflowLease) (*api.UpdateWorkflowAction, error) { + mutableState := workflowLease.GetMutableState() + if !mutableState.IsWorkflowExecutionRunning() { + return nil, consts.ErrWorkflowCompleted + } + workflowTask := mutableState.GetWorkflowTaskByID(scheduledEventID) + if workflowTask == nil || workflowTask.RequestID != requestID || + workflowTask.StartedEventID == common.EmptyEventID { + // Something else already resolved the task. + return &api.UpdateWorkflowAction{Noop: true}, nil + } + if err := failTaskForStreams(mutableState, workflowTask, cause); err != nil { + return nil, err + } + return &api.UpdateWorkflowAction{}, nil + }, + nil, + shardContext, + workflowConsistencyChecker, + ) +} + func setHistoryForRecordWfTaskStartedResp( ctx context.Context, shardContext historyi.ShardContext, diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing.go b/service/history/api/recordworkflowtaskstarted/stream_routing.go index 4cb4a0aae50..1071fd29af6 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_routing.go +++ b/service/history/api/recordworkflowtaskstarted/stream_routing.go @@ -87,6 +87,7 @@ func readExternalWindow( }, To: out.GetNextOffset(), Limit: int(limit), + RunID: out.GetRunId(), } if len(out.GetMessages()) == 0 { return w, nil diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go index 7adbb331816..56a1684a9b0 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_slices.go +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -2,6 +2,8 @@ package recordworkflowtaskstarted import ( "context" + "errors" + "fmt" "slices" commonpb "go.temporal.io/api/common/v1" @@ -14,11 +16,69 @@ import ( "go.temporal.io/server/chasm/lib/stream" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/common/definition" + "go.temporal.io/server/common/failure" + "go.temporal.io/server/common/persistence" "go.temporal.io/server/common/persistence/serialization" + "go.temporal.io/server/service/history/api" + "go.temporal.io/server/service/history/consts" historyi "go.temporal.io/server/service/history/interfaces" + "go.temporal.io/server/service/history/workflow" "google.golang.org/protobuf/proto" ) +// rangeUnavailable says a stream range this consumer depends on can no longer be +// served: the stream was truncated or deleted past it, or re-supplying it would +// exceed what one response may carry. Retrying the request would only ask the +// same stream again, so the workflow task is failed with it instead. +type rangeUnavailable struct { + msg string +} + +func (e *rangeUnavailable) Error() string { + return e.msg +} + +func unavailablef(format string, args ...any) error { + return &rangeUnavailable{msg: fmt.Sprintf(format, args...)} +} + +// unavailableIfGone tells a read failure that means the range is gone for good +// from one worth retrying. A shard that is moving or a store that timed out is +// the second kind and keeps its own error. +func unavailableIfGone(err error, format string, args ...any) error { + var precondition *serviceerror.FailedPrecondition + var notFound *serviceerror.NotFound + if errors.As(err, &precondition) || errors.As(err, ¬Found) { + return unavailablef(format+": %v", append(args, err)...) + } + return err +} + +// failTaskForStreams records why the task cannot be started and schedules the +// next attempt. The event is what an operator sees; a bare error to matching +// would only make the task come back. +func failTaskForStreams( + ms historyi.MutableState, + workflowTask *historyi.WorkflowTaskInfo, + cause *rangeUnavailable, +) error { + if _, err := ms.AddWorkflowTaskFailedEvent( + workflowTask, + enumspb.WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE, + failure.NewServerFailure(cause.Error(), true), + consts.IdentityHistoryService, + nil, + "", + "", + "", + 0, + ); err != nil { + return err + } + ms.FlushBufferedEvents() + return workflow.ScheduleWorkflowTask(ms) +} + // streamOrigin says where a subscribed stream lives, which decides how its // payload is read: an external stream from its own execution, an owned one // from the consumer's. @@ -60,17 +120,19 @@ func deliveryFrontier( } // A consumer that fell behind a truncating stream is told so rather than - // handed the rest and left with a hole it cannot see. Nothing holds the - // floor for a consumer any more, on purpose: a floor that waited for the - // slowest reader was never released and turned every cap into a no-op. + // handed the rest and left with a hole it cannot see. The floor protects + // an active consumer, so reaching this means the subscription was released + // and the stream moved on. if cursor.Offset() < state.GetBaseOffset() { - return 0, serviceerror.NewFailedPreconditionf( - "stream %q was truncated past this consumer: it is at offset %d and the stream now starts at %d", - name, cursor.Offset(), state.GetBaseOffset()) + return 0, unavailablef( + "stream %q was truncated past this consumer: it is at offset %d and the stream "+ + "now starts at %d", name, cursor.Offset(), state.GetBaseOffset()) } return state.GetHeadOffset(), nil } +// readDeliverable reads the next run of a stream for the task being started +// and returns it with the run id of the execution that holds the stream. func readDeliverable( ctx context.Context, chasmCtx chasm.Context, @@ -79,26 +141,28 @@ func readDeliverable( name string, cursor *stream.Cursor, from, to int64, -) ([]*streampb.StreamMessage, int64, error) { +) ([]*streampb.StreamMessage, int64, string, error) { if to <= from { - return nil, from, nil + return nil, from, "", nil } w, err := readWindowFor(ctx, chasmCtx, wf, namespaceID, name, cursor.IsExternal(), cursor.StreamID(), from, to) if err != nil { - return nil, 0, err + return nil, 0, "", unavailableIfGone(err, + "stream %q no longer holds offsets [%d,%d) this consumer is due", + cursor.StreamID(), from, to) } // The collected run is contiguous from `from`, so the byte cap recomputes // the same end offset the read would have reported. collected, _, err := stream.CollectMessages( w.Blobs, w.Starts, from, w.To, stream.MaxConsumeItemsPerTask, nil) if err != nil { - return nil, 0, err + return nil, 0, "", err } // Cap before converting: the byte budget applies to the run as stored, and // trimming decides how far the recorded range reaches. collected, readTo := stream.CapByBytes(collected, from, stream.MaxConsumeBytesPerTask) - return stream.ToAPIMessages(collected), readTo, nil + return stream.ToAPIMessages(collected), readTo, w.RunID, nil } // readWindowFor reads a range from whichever component holds it. @@ -183,7 +247,7 @@ func deliverStreamSlices( slices.Sort(names) maxItems := stream.MaxConsumeItemsPerTask - namespaceID := ms.GetExecutionInfo().GetNamespaceId() + consumer := ms.GetWorkflowKey() slicesOut := make([]*streampb.StreamSlice, 0, len(names)) addresses := make(map[string]streamOrigin, len(names)) @@ -207,11 +271,14 @@ func deliverStreamSlices( to = min(from+int64(maxItems), head) } - messages, next, err := readDeliverable( - ctx, chasmCtx, wf, namespaceID, name, cursor, from, to) + messages, next, ownerRunID, err := readDeliverable( + ctx, chasmCtx, wf, consumer.NamespaceID, name, cursor, from, to) if err != nil { return nil, nil, err } + if !cursor.IsExternal() { + ownerRunID = consumer.RunID + } if restaged { // The staged range is the one the completion will record, so the @@ -231,6 +298,7 @@ func deliverStreamSlices( // that it saw nothing, and the slice is what the completion reads. slicesOut = append(slicesOut, &streampb.StreamSlice{ StreamId: cursor.StreamID(), + RunId: ownerRunID, FromOffset: from, ToOffset: next, Messages: messages, @@ -248,12 +316,14 @@ func deliverStreamSlices( // Bounds on one cold replay's re-supply. History holds offsets and not payloads, // so replaying a consumer means re-reading every range its completed tasks // recorded, and that grows with the workflow's whole life rather than with the -// task being started. These cap one response; a consumer past them cannot be -// replayed by this path at all, which is a limit of the design rather than of -// the numbers. +// task being started. A consumer past these bounds cannot be replayed by this +// path; its task is failed with a cause that says so rather than retried. const ( maxReplayMessages = 100_000 maxReplayBytes = 64 << 20 + // Pages of history walked to find the recorded ranges. Empty ranges cost + // nothing to attach but each page is a store read under a request deadline. + maxReplayPages = 256 ) // ownedRange names a range of a stream the consumer owns. @@ -294,6 +364,22 @@ func readRecordedRange( ownedRange{name: origin.name, req: req}) } +// replaySupply collects what one cold replay owes the worker: the ranges every +// completed task recorded, re-read from their streams, within one response's +// budget. +type replaySupply struct { + ctx context.Context + consumer definition.WorkflowKey + addresses map[string]streamOrigin + + messages int + bytes int + // How far the recorded ranges reach, per stream, so the coverage check + // can tell a complete re-supply from a short one. + reached map[string]int64 + slices []*streampb.StreamSlice +} + // attachReplaySlices re-supplies the payloads for ranges that earlier workflow // tasks recorded, keyed by the event that recorded each one. // @@ -303,11 +389,17 @@ func readRecordedRange( // exist is the stream itself. The response field alone cannot carry this, // because it is built once per delivery while a cache miss replays every prior // task, so each range travels with the id of the event that recorded it. +// +// The response carries one page of history. The recording events of a consumer +// that has lived longer than a page are on the pages after it, so every page is +// read here. Empty ranges are recorded too and have to travel with their +// events, which is why the walk cannot stop once the committed offset is found. func attachReplaySlices( ctx context.Context, + shardContext historyi.ShardContext, consumer definition.WorkflowKey, - namespaceID string, addresses map[string]streamOrigin, + pageSize int32, resp *historyservice.RecordWorkflowTaskStartedResponseWithRawHistory, ) error { // Only a workflow with a live subscription has anything to re-supply, and @@ -329,34 +421,91 @@ func attachReplaySlices( if err != nil { return err } + supply := &replaySupply{ + ctx: ctx, + consumer: consumer, + addresses: addresses, + reached: make(map[string]int64, len(addresses)), + } + if err := supply.collect(events); err != nil { + return err + } - budget := replayBudget{} - // How far the recorded ranges reach, per stream, so the coverage check - // below can tell a complete re-supply from a short one. - reached := make(map[string]int64, len(addresses)) + pages := 1 + token := resp.GetNextPageToken() + for len(token) > 0 { + pages++ + if pages > maxReplayPages { + return unavailablef( + "replaying workflow %q needs more than %d pages of history to find the stream "+ + "ranges its tasks consumed", consumer.GetWorkflowID(), maxReplayPages) + } + continuation, err := api.DeserializeHistoryToken(token) + if err != nil { + return err + } + // Read raw and decode here. The first page may have been read either + // way, and only the raw reader accepts the token both produce. + blobs, _, next, err := persistence.ReadFullPageRawEvents( + ctx, shardContext.GetExecutionManager(), &persistence.ReadHistoryBranchRequest{ + BranchToken: continuation.GetBranchToken(), + MinEventID: continuation.GetFirstEventId(), + MaxEventID: continuation.GetNextEventId(), + PageSize: int(pageSize), + NextPageToken: continuation.GetPersistenceToken(), + ShardID: shardContext.GetShardID(), + }) + if err != nil { + return err + } + pageEvents, err := decodeEventBlobs(blobs) + if err != nil { + return err + } + if err := supply.collect(pageEvents); err != nil { + return err + } + if len(next) == 0 { + break + } + continuation.PersistenceToken = next + if token, err = api.SerializeHistoryToken(continuation); err != nil { + return err + } + } + + if err := supply.checkCoverage(); err != nil { + return err + } + resp.StreamSlices = append(resp.StreamSlices, supply.slices...) + return nil +} +// collect re-reads every range the events on one page recorded. +func (s *replaySupply) collect(events []*historypb.HistoryEvent) error { for _, event := range events { for _, recorded := range event.GetWorkflowTaskCompletedEventAttributes().GetStreamCursors() { - address, ok := addresses[recorded.GetStreamId()] + address, ok := s.addresses[recorded.GetStreamId()] if !ok { // A subscription the workflow has since dropped. The range is // still part of its history, but nothing is consuming it now. continue } - messages, err := replayMessagesFor(ctx, consumer, address, recorded, &budget) + messages, ownerRunID, err := s.messagesFor(address, recorded) if err != nil { return err } - if to := recorded.GetToOffset(); to > reached[recorded.GetStreamId()] { - reached[recorded.GetStreamId()] = to + if to := recorded.GetToOffset(); to > s.reached[recorded.GetStreamId()] { + s.reached[recorded.GetStreamId()] = to } // Attached even when empty: the task observed nothing, and replay // has to reproduce that rather than infer it from an absence. - resp.StreamSlices = append(resp.StreamSlices, &streampb.StreamSlice{ + s.slices = append(s.slices, &streampb.StreamSlice{ StreamId: recorded.GetStreamId(), + RunId: ownerRunID, FromOffset: recorded.GetFromOffset(), ToOffset: recorded.GetToOffset(), Messages: messages, @@ -364,103 +513,83 @@ func attachReplaySlices( }) } } - - return checkReplayCoverage(consumer, addresses, reached) -} - -// replayBudget accumulates what one response has already committed to -// re-supplying, across every stream and every recorded range in it. -type replayBudget struct { - messages int - bytes int + return nil } -// replayMessagesFor re-reads one recorded range, or returns nothing for a range -// that recorded an empty observation. -func replayMessagesFor( - ctx context.Context, - consumer definition.WorkflowKey, +// messagesFor re-reads one recorded range, or returns nothing for a range that +// recorded an empty observation. The run id is the execution holding the +// stream, which for an owned stream is the consumer itself. +func (s *replaySupply) messagesFor( address streamOrigin, recorded *streampb.StreamCursor, - budget *replayBudget, -) ([]*streampb.StreamMessage, error) { +) ([]*streampb.StreamMessage, string, error) { + ownerRunID := s.consumer.RunID if recorded.GetToOffset() <= recorded.GetFromOffset() { - return nil, nil + if address.external { + ownerRunID = "" + } + return nil, ownerRunID, nil } - w, err := readRecordedRange(ctx, consumer, address, + w, err := readRecordedRange(s.ctx, s.consumer, address, recorded.GetStreamId(), recorded.GetFromOffset(), recorded.GetToOffset()) if err != nil { - return nil, replayReadError(consumer, recorded, err) + // Truncation and deletion are the reachable causes, and neither is + // recoverable for this workflow: without the bytes it can never + // replay, and without replaying it can never start another task. + return nil, "", unavailableIfGone(err, + "workflow %q cannot replay: stream %q no longer holds offsets [%d,%d) that one "+ + "of its completed tasks consumed", + s.consumer.GetWorkflowID(), recorded.GetStreamId(), + recorded.GetFromOffset(), recorded.GetToOffset()) + } + if address.external { + ownerRunID = w.RunID } collected, _, err := stream.CollectMessages( w.Blobs, w.Starts, recorded.GetFromOffset(), recorded.GetToOffset(), int(recorded.GetToOffset()-recorded.GetFromOffset()), nil) if err != nil { - return nil, err + return nil, "", err } messages := stream.ToAPIMessages(collected) - budget.messages += len(messages) + s.messages += len(messages) for _, m := range messages { - budget.bytes += proto.Size(m) + s.bytes += proto.Size(m) } // Bounded because every prior task's range is re-read into one response, so // a long-lived consumer's cold replay grows with its whole history. Refused // rather than trimmed: a short re-supply is what replay cannot survive. - if budget.messages > maxReplayMessages || budget.bytes > maxReplayBytes { - return nil, serviceerror.NewFailedPreconditionf( - "replaying workflow %q needs more than %d messages or %d bytes of stream history to re-supply; "+ - "the consumed ranges cannot be re-delivered in one response", - consumer.GetWorkflowID(), maxReplayMessages, maxReplayBytes) + if s.messages > maxReplayMessages || s.bytes > maxReplayBytes { + return nil, "", unavailablef( + "replaying workflow %q needs more than %d messages or %d bytes of stream history "+ + "to re-supply; the consumed ranges cannot be re-delivered in one response", + s.consumer.GetWorkflowID(), maxReplayMessages, maxReplayBytes) } - return messages, nil + return messages, ownerRunID, nil } -// checkReplayCoverage refuses a re-supply that stops short of what History says -// the consumer consumed. -// -// The events carried on the response are one page. A consumer whose recording -// events run past it would be handed only part of what it originally saw, and -// would then replay against fewer messages than the first run had. The cursor -// knows how far it has committed, so that is checked rather than assumed. -func checkReplayCoverage( - consumer definition.WorkflowKey, - addresses map[string]streamOrigin, - reached map[string]int64, -) error { - for streamID, address := range addresses { - got, ok := reached[streamID] +// checkCoverage refuses a re-supply that stops short of what the cursor says +// the consumer consumed. With every page read, a shortfall means History and +// the cursor disagree, and handing the worker less than the first run saw is +// the one outcome replay cannot survive. +func (s *replaySupply) checkCoverage() error { + for streamID, address := range s.addresses { + got, ok := s.reached[streamID] if !ok { got = address.start } if got < address.committed { - return serviceerror.NewFailedPreconditionf( - "workflow %q consumed stream %q through offset %d but its history page only records through %d; "+ - "re-supplying the rest needs the events beyond this page", - consumer.GetWorkflowID(), streamID, address.committed, got) + return unavailablef( + "workflow %q consumed stream %q through offset %d but its history only records "+ + "through %d", s.consumer.GetWorkflowID(), streamID, address.committed, got) } } return nil } -// replayReadError says why a range a completed task recorded can no longer be -// read. Truncation and deletion are the reachable causes, and neither is -// recoverable for this workflow: without the bytes it can never replay, and -// without replaying it can never start another task. The bare read error names -// offsets and no workflow, which is not enough to act on. -func replayReadError( - consumer definition.WorkflowKey, - recorded *streampb.StreamCursor, - cause error, -) error { - return serviceerror.NewFailedPreconditionf( - "workflow %q cannot replay: stream %q no longer holds offsets [%d,%d) that one of its completed tasks consumed (%v)", - consumer.GetWorkflowID(), recorded.GetStreamId(), - recorded.GetFromOffset(), recorded.GetToOffset(), cause) -} - // eventsOfResponse reads the events the response is carrying, whichever of the // three representations it happens to be using. func eventsOfResponse( @@ -474,17 +603,21 @@ func eventsOfResponse( if len(raw) == 0 { raw = resp.GetRawHistory() //nolint:staticcheck // SA1019: still populated while the newer field rolls out. } - if len(raw) == 0 { - return nil, nil - } - - serializer := serialization.NewSerializer() - var events []*historypb.HistoryEvent + blobs := make([]*commonpb.DataBlob, 0, len(raw)) for _, batch := range raw { - batchEvents, err := serializer.DeserializeEvents(&commonpb.DataBlob{ + blobs = append(blobs, &commonpb.DataBlob{ EncodingType: enumspb.ENCODING_TYPE_PROTO3, Data: batch, }) + } + return decodeEventBlobs(blobs) +} + +func decodeEventBlobs(blobs []*commonpb.DataBlob) ([]*historypb.HistoryEvent, error) { + serializer := serialization.NewSerializer() + var events []*historypb.HistoryEvent + for _, blob := range blobs { + batchEvents, err := serializer.DeserializeEvents(blob) if err != nil { return nil, err } diff --git a/tests/stream_replay_test.go b/tests/stream_replay_test.go new file mode 100644 index 00000000000..e22d9a45bf6 --- /dev/null +++ b/tests/stream_replay_test.go @@ -0,0 +1,332 @@ +package tests + +import ( + "context" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + "go.temporal.io/api/workflowservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common/dynamicconfig" + "go.temporal.io/server/common/testing/await" + "go.temporal.io/server/tests/testcore" + "google.golang.org/protobuf/types/known/durationpb" +) + +// startConsumer starts a workflow on its own task queue and returns the +// execution and the queue. +func startConsumer( + t *testing.T, s *streamTestEnv, prefix string, +) (*commonpb.WorkflowExecution, *taskqueuepb.TaskQueue) { + t.Helper() + id := prefix + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + we, err := s.env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + require.NoError(t, err) + return &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}, tq +} + +func publishCommand(bodies ...string) []*commandpb.Command { + messages := make([]*streampb.StreamMessage, len(bodies)) + for i, b := range bodies { + messages[i] = &streampb.StreamMessage{Body: &commonpb.Payload{Data: []byte(b)}, Topic: "tokens"} + } + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: messages, + }, + }, + }} +} + +func apiBodies(msgs []*streampb.StreamMessage) []string { + out := make([]string, len(msgs)) + for i, m := range msgs { + out[i] = string(m.GetBody().GetData()) + } + return out +} + +// completedEventsWithCursors returns the ids of every WorkflowTaskCompleted +// event that recorded a consumed range, empty ones included. +func completedEventsWithCursors(events []*historypb.HistoryEvent) []int64 { + var out []int64 + for _, e := range events { + if len(e.GetWorkflowTaskCompletedEventAttributes().GetStreamCursors()) > 0 { + out = append(out, e.GetEventId()) + } + } + return out +} + +// The response to a started task carries one page of history. A consumer that +// has run longer than a page recorded its ranges on the pages after it, and a +// cold replay has to find every one of them or the worker replays against less +// than the first run saw. +func TestReplayFollowsHistoryPastTheFirstPage(t *testing.T) { + env := testcore.NewEnv(t, + testcore.WithDedicatedCluster(), + testcore.WithDynamicConfig(dynamicconfig.HistoryMaxPageSize, 4), + ) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-paged-replay-") + + var delivered [][]*streampb.StreamSlice + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + if len(delivered) == 1 { + return publishCommand("page-1", "page-2"), nil + } + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + StreamName: chasmworkflow.DefaultStreamName, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // One task consumes the range, then several idle tasks push the event that + // recorded it well behind the first page of history. + for range 4 { + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + } + require.Len(t, currentSlice(t, delivered[1]).GetMessages(), 2) + + events := env.GetHistory(s.ns, execution) + recordedAt := completedEventsWithCursors(events) + require.Len(t, recordedAt, 4, "every task since the subscription recorded a range") + consumedAt := completedEventWithCursors(t, events) + require.Greater(t, len(events), 8, "the history has to span more than one page for this to mean anything") + + env.CloseShard(env.NamespaceID().String(), execution.GetWorkflowId()) + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + replay := delivered[len(delivered)-1] + replayed := sliceForEvent(replay, consumedAt) + require.NotNil(t, replayed, "the range recorded on a later page must be re-supplied") + require.Equal(t, []string{"page-1", "page-2"}, apiBodies(replayed.GetMessages())) + require.Equal(t, execution.GetRunId(), replayed.GetRunId(), + "an owned stream's slice names the run that holds it") + for _, eventID := range recordedAt { + require.NotNil(t, sliceForEvent(replay, eventID), + "every recorded range, empty ones included, has to travel with its event") + } +} + +// pollInBackground keeps a poller on the queue until the returned stop +// function is called. Used where a task is expected never to reach the worker, +// because matching only asks history to start a task while someone polls. +func pollInBackground(t *testing.T, env *testcore.TestEnv, ns string, tq *taskqueuepb.TaskQueue) func() { + t.Helper() + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + go func() { + defer close(done) + for ctx.Err() == nil { + pollCtx, pollCancel := context.WithTimeout(ctx, 5*time.Second) + _, _ = env.FrontendClient().PollWorkflowTaskQueue(pollCtx, &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: ns, TaskQueue: tq, Identity: "tester", + }) + pollCancel() + } + }() + return func() { + cancel() + <-done + } +} + +func workflowTaskFailedWith( + events []*historypb.HistoryEvent, + cause enumspb.WorkflowTaskFailedCause, +) *historypb.WorkflowTaskFailedEventAttributes { + for _, e := range events { + if attrs := e.GetWorkflowTaskFailedEventAttributes(); attrs != nil && attrs.GetCause() == cause { + return attrs + } + } + return nil +} + +// deleteStreamAndWait removes a standalone stream and waits until reads of it +// fail, since deletion completes in a task after the call returns. +func deleteStreamAndWait(t *testing.T, s *streamTestEnv, streamID string) { + t.Helper() + _, err := s.client.DeleteStream(s.ctx(), &streamlib.DeleteStreamRequest{ + FrontendRequest: &streamlib.DeleteStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.NoError(t, err) + await.RequireTrue(t, func() bool { + _, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + return err != nil + }, 20*time.Second, 100*time.Millisecond) +} + +// A consumer whose stream is gone before its next task starts cannot be handed +// the range it is due. That has to land in History as a failed task with a +// cause, not as a request matching retries forever. +func TestConsumerOfADeletedStreamFailsItsTaskLoudly(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "gone-live-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + execution, tq := startConsumer(t, s, "stream-wf-gone-live-") + + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // The append schedules the task that is owed the range. + _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, Messages: streamMsgs("tokens", "never-delivered"), + }, + }) + require.NoError(t, err) + await.RequireTrue(t, func() bool { + scheduled := 0 + for _, e := range env.GetHistory(s.ns, execution) { + if e.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_TASK_SCHEDULED { + scheduled++ + } + } + return scheduled >= 2 + }, 20*time.Second, 100*time.Millisecond) + + deleteStreamAndWait(t, s, streamID) + + stop := pollInBackground(t, env, s.ns, tq) + defer stop() + await.RequireTrue(t, func() bool { + return workflowTaskFailedWith(env.GetHistory(s.ns, execution), + enumspb.WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE) != nil + }, 30*time.Second, 200*time.Millisecond) + + failed := workflowTaskFailedWith(env.GetHistory(s.ns, execution), + enumspb.WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE) + require.Contains(t, failed.GetFailure().GetMessage(), streamID, + "the failure names the stream so an operator knows where to look") +} + +// A consumer whose recorded range can no longer be re-read cannot replay. The +// replay runs after the task started, so the failure is a transaction of its +// own, and it still has to end up in History with the cause. +func TestReplayOfADeletedStreamFailsTheTaskLoudly(t *testing.T) { + env := testcore.NewEnv(t, testcore.WithDedicatedCluster()) + s := newStreamTestEnvFrom(t, env) + + streamID := "gone-replay-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + execution, tq := startConsumer(t, s, "stream-wf-gone-replay-") + + var delivered [][]*streampb.StreamSlice + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, Messages: streamMsgs("tokens", "consumed-once"), + }, + }) + require.NoError(t, err) + + // Consumed and recorded, which is what a replay will have to reproduce. + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.Len(t, currentSlice(t, delivered[1]).GetMessages(), 1) + + deleteStreamAndWait(t, s, streamID) + + env.CloseShard(env.NamespaceID().String(), execution.GetWorkflowId()) + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + + stop := pollInBackground(t, env, s.ns, tq) + defer stop() + await.RequireTrue(t, func() bool { + return workflowTaskFailedWith(env.GetHistory(s.ns, execution), + enumspb.WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE) != nil + }, 30*time.Second, 200*time.Millisecond) + + failed := workflowTaskFailedWith(env.GetHistory(s.ns, execution), + enumspb.WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE) + require.Contains(t, failed.GetFailure().GetMessage(), "cannot replay") + require.Contains(t, failed.GetFailure().GetMessage(), streamID) +} From 524dcec3e9e5ef23fc9661b7130a7fa1e45369b8 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:14:08 -0700 Subject: [PATCH 26/47] Failed the workflow task on a refused stream command. A refused publish or subscribe came back as a plain error, which failed the completion call and left the worker retrying the same command with nothing in History saying why. --- chasm/lib/workflow/stream_commands.go | 44 ++++++- .../api/respondworkflowtaskcompleted/api.go | 10 +- .../stream_appends.go | 13 +- tests/stream_rejection_test.go | 120 ++++++++++++++++++ 4 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 tests/stream_rejection_test.go diff --git a/chasm/lib/workflow/stream_commands.go b/chasm/lib/workflow/stream_commands.go index 20964d02bd4..8e4706bff7f 100644 --- a/chasm/lib/workflow/stream_commands.go +++ b/chasm/lib/workflow/stream_commands.go @@ -1,6 +1,8 @@ package workflow import ( + "errors" + commandpb "go.temporal.io/api/command/v1" enumspb "go.temporal.io/api/enums/v1" historypb "go.temporal.io/api/history/v1" @@ -14,6 +16,26 @@ import ( // DefaultStreamName is the stream a command addresses when it names none. const DefaultStreamName = "output" +// StreamAdmissionFailure turns a refusal of a stream command into a workflow +// task failure with the given cause. +// +// A refusal returned as a plain error fails the RespondWorkflowTaskCompleted +// call instead, and the worker then retries the same commands against the same +// limits until the task times out, with nothing in History saying why. Only +// refusals are converted: an internal or storage error is still the request's +// to report, because a retry can succeed. +func StreamAdmissionFailure(cause enumspb.WorkflowTaskFailedCause, err error) error { + var invalid *serviceerror.InvalidArgument + var precondition *serviceerror.FailedPrecondition + var exhausted *serviceerror.ResourceExhausted + var notFound *serviceerror.NotFound + if errors.As(err, &invalid) || errors.As(err, &precondition) || + errors.As(err, &exhausted) || errors.As(err, ¬Found) { + return FailWorkflowTaskError{Cause: cause, Message: err.Error()} + } + return err +} + // handleAddStreamMessagesCommand appends to a stream the workflow owns. // // The stream is a co-located subcomponent, so the batch and the frontier land @@ -29,12 +51,17 @@ func handleAddStreamMessagesCommand( command *commandpb.Command, opts CommandHandlerOptions, ) error { + badAttributes := enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES attrs := command.GetAddStreamMessagesCommandAttributes() if attrs == nil { - return serviceerror.NewInvalidArgument("AddStreamMessagesCommandAttributes is not set") + return FailWorkflowTaskError{ + Cause: badAttributes, Message: "AddStreamMessagesCommandAttributes is not set", + } } if len(attrs.GetMessages()) == 0 { - return serviceerror.NewInvalidArgument("AddStreamMessages command carries no messages") + return FailWorkflowTaskError{ + Cause: badAttributes, Message: "AddStreamMessages command carries no messages", + } } // The batch becomes one data node, so the whole batch is what has to fit. @@ -60,14 +87,14 @@ func handleAddStreamMessagesCommand( s, err := wf.streamNamed(chasmCtx, name) if err != nil { - return err + return StreamAdmissionFailure(badAttributes, err) } result, err := s.AddMessages(chasmCtx, stream.AddMessagesRequest{ Messages: toLibraryMessages(attrs.GetMessages()), }) if err != nil { - return err + return StreamAdmissionFailure(badAttributes, err) } // Written even when a producer sequence deduplicated the append, because // the command was still issued and the event is what the replaying worker @@ -91,13 +118,18 @@ func handleSubscribeStreamCommand( command *commandpb.Command, opts CommandHandlerOptions, ) error { + badAttributes := enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES attrs := command.GetSubscribeStreamCommandAttributes() if attrs == nil { - return serviceerror.NewInvalidArgument("SubscribeStreamCommandAttributes is not set") + return FailWorkflowTaskError{ + Cause: badAttributes, Message: "SubscribeStreamCommandAttributes is not set", + } } streamID := attrs.GetStreamId() if streamID == "" { - return serviceerror.NewInvalidArgument("SubscribeStream command names no stream") + return FailWorkflowTaskError{ + Cause: badAttributes, Message: "SubscribeStream command names no stream", + } } // A second subscribe to the same stream registers nothing, but it still diff --git a/service/history/api/respondworkflowtaskcompleted/api.go b/service/history/api/respondworkflowtaskcompleted/api.go index 8c4657ddae1..b7767f9b62e 100644 --- a/service/history/api/respondworkflowtaskcompleted/api.go +++ b/service/history/api/respondworkflowtaskcompleted/api.go @@ -463,12 +463,18 @@ func (handler *WorkflowTaskCompletedHandler) Invoke( // command does not return an error, so this has to be checked here // rather than inferred from err. if workflowTaskHandler.workflowTaskFailedCause == nil && !workflowTaskHandler.stopProcessing { - if err = resolveStagedStreamSubscriptions( + err = resolveStagedStreamSubscriptions( ctx, ms, ms.GetWorkflowKey().NamespaceID, workflowTaskHandler.stagedStreamSubscriptions, - ); err != nil { + ) + if failWFTErr, ok := errors.AsType[chasmworkflow.FailWorkflowTaskError](err); ok { + // A refused subscription fails the task the way a refused + // command does, so the cause lands in History. + err = workflowTaskHandler.failWorkflowTask(failWFTErr.Cause, failWFTErr) + } + if err != nil { return nil, err } } diff --git a/service/history/api/respondworkflowtaskcompleted/stream_appends.go b/service/history/api/respondworkflowtaskcompleted/stream_appends.go index a4b5d69ec88..d6611dc40db 100644 --- a/service/history/api/respondworkflowtaskcompleted/stream_appends.go +++ b/service/history/api/respondworkflowtaskcompleted/stream_appends.go @@ -3,6 +3,7 @@ package respondworkflowtaskcompleted import ( "context" + enumspb "go.temporal.io/api/enums/v1" "go.temporal.io/api/serviceerror" streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" @@ -28,6 +29,12 @@ import ( // request context resolves shards through the local controller, which refuses // any shard this host does not own, so a stream living elsewhere in the // cluster can only be reached by going back out through the service. +// +// A refusal, such as a stream that does not exist or an offset below its floor, +// comes back as a workflow task failure so the worker sees a cause rather than +// retrying the same command. A pin already taken for an earlier subscription +// in the same task stays on its stream; the notify task releases it when it +// finds this workflow does not consume that stream. func resolveStagedStreamSubscriptions( ctx context.Context, ms historyi.MutableState, @@ -64,7 +71,8 @@ func resolveStagedStreamSubscriptions( startOffset, err := wf.SubscribeToOwnedStream( chasmCtx, pending.StreamID, pending.StartOffset) if err != nil { - return err + return chasmworkflow.StreamAdmissionFailure( + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES, err) } chasmworkflow.RecordStreamSubscribedOffset(pending.Event, startOffset) continue @@ -72,7 +80,8 @@ func resolveStagedStreamSubscriptions( pin, err := registerExternalConsumer(ctx, ms, namespaceID, pending) if err != nil { - return err + return chasmworkflow.StreamAdmissionFailure( + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES, err) } if _, err := wf.SubscribeToExternalStream(chasmCtx, chasmworkflow.ExternalStreamSubscription{ diff --git a/tests/stream_rejection_test.go b/tests/stream_rejection_test.go new file mode 100644 index 00000000000..352d3e75e1a --- /dev/null +++ b/tests/stream_rejection_test.go @@ -0,0 +1,120 @@ +package tests + +import ( + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + enumspb "go.temporal.io/api/enums/v1" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/api/workflowservice/v1" + chasmstream "go.temporal.io/server/chasm/lib/stream" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/tests/testcore" +) + +// A refused publish has to fail the workflow task with a cause, not the +// completion call. Failing the call leaves the worker retrying the same command +// against the same limit until the task times out, with nothing in History +// saying why. +func TestOverLimitPublishFailsTheWorkflowTask(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-reject-publish-") + + tooMany := make([]string, chasmstream.MaxMessagesPerBatch+1) + for i := range tooMany { + tooMany[i] = "x" + } + task := 0 + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + task++ + if task == 1 { + return publishCommand(tooMany...), nil + } + return publishCommand("fits"), nil + }, + Logger: env.Logger, + T: t, + } + + _, err := poller.PollAndProcessWorkflowTask() + require.Error(t, err, "the completion is rejected") + require.ErrorContains(t, err, "exceeds the limit") + + events := env.GetHistory(s.ns, execution) + failed := workflowTaskFailedWith(events, + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES) + require.NotNil(t, failed, "the refusal is a workflow task failure with its own cause") + require.ErrorContains(t, err, failed.GetFailure().GetMessage()) + + desc, err := s.client.DescribeWorkflowStream(s.ctx(), &streamlib.DescribeWorkflowStreamRequest{ + FrontendRequest: &streamlib.DescribeWorkflowStreamInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + }, + }) + require.NoError(t, err) + require.Equal(t, int64(0), desc.GetFrontendResponse().GetState().GetHeadOffset(), + "a refused publish appends nothing") + + // The next attempt is a fresh task, and a publish that fits lands at the + // start of the stream. + _, err = poller.PollAndProcessWorkflowTask(testcore.WithExpectedAttemptCount(2)) + require.NoError(t, err) + poll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Equal(t, []string{"fits"}, bodies(poll.GetFrontendResponse().GetMessages())) + require.Equal(t, []int64{0}, offsets(poll.GetFrontendResponse().GetMessages())) +} + +// A subscribe naming a stream that does not exist is refused where the stream +// is looked up, which is after the commands and before the commit. It still has +// to surface as a failed task with a cause, and leave no half-made subscription. +func TestSubscribeToAMissingStreamFailsTheWorkflowTask(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-reject-subscribe-") + + missing := "no-such-stream-" + uuid.NewString() + var delivered [][]*streampb.StreamSlice + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + if len(delivered) == 1 { + return subscribeCommand(missing), nil + } + return nil, nil + }, + Logger: env.Logger, + T: t, + } + + _, err := poller.PollAndProcessWorkflowTask() + require.Error(t, err) + + events := env.GetHistory(s.ns, execution) + require.NotNil(t, workflowTaskFailedWith(events, + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES)) + require.Empty(t, subscribedEvents(events), "a refused subscribe writes no event") + + // The retry carries no slice, because no cursor was made. + _, err = poller.PollAndProcessWorkflowTask(testcore.WithExpectedAttemptCount(2)) + require.NoError(t, err) + require.Empty(t, delivered[1]) +} From 73fccdf3550ab6b56578c9b792c5c512a2036f46 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:14:20 -0700 Subject: [PATCH 27/47] Proved that a failed workflow task publishes nothing. The publish commits with the task, so a task that fails after publishing has to leave no event, no offset and no message behind. --- tests/stream_rejection_test.go | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/stream_rejection_test.go b/tests/stream_rejection_test.go index 352d3e75e1a..508a3f6c93d 100644 --- a/tests/stream_rejection_test.go +++ b/tests/stream_rejection_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" streampb "go.temporal.io/api/stream/v1" "go.temporal.io/api/workflowservice/v1" @@ -118,3 +119,77 @@ func TestSubscribeToAMissingStreamFailsTheWorkflowTask(t *testing.T) { require.NoError(t, err) require.Empty(t, delivered[1]) } + +// The guarantee the design is sold on: a publish commits with the workflow +// task, so a task that fails after publishing publishes nothing. +func TestPublishOnAFailedTaskLeavesNoTrace(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-reject-atomic-") + + // A ScheduleActivityTask with no task queue fails command validation, so the + // whole task is failed after the publish command already ran. + invalidTrailer := &commandpb.Command{ + CommandType: enumspb.COMMAND_TYPE_SCHEDULE_ACTIVITY_TASK, + Attributes: &commandpb.Command_ScheduleActivityTaskCommandAttributes{ + ScheduleActivityTaskCommandAttributes: &commandpb.ScheduleActivityTaskCommandAttributes{ + ActivityId: "no-queue", + ActivityType: &commonpb.ActivityType{Name: "noop"}, + }, + }, + } + task := 0 + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + task++ + if task == 1 { + return append(publishCommand("lost-with-the-task"), invalidTrailer), nil + } + return publishCommand("first-to-land"), nil + }, + Logger: env.Logger, + T: t, + } + + _, err := poller.PollAndProcessWorkflowTask() + require.Error(t, err) + + events := env.GetHistory(s.ns, execution) + require.NotNil(t, workflowTaskFailedWith(events, + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_ACTIVITY_ATTRIBUTES)) + for _, e := range events { + require.Nil(t, e.GetWorkflowStreamMessagesAddedEventAttributes(), + "a publish on a failed task leaves no event") + } + desc, err := s.client.DescribeWorkflowStream(s.ctx(), &streamlib.DescribeWorkflowStreamRequest{ + FrontendRequest: &streamlib.DescribeWorkflowStreamInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + }, + }) + require.NoError(t, err) + require.Equal(t, int64(0), desc.GetFrontendResponse().GetState().GetHeadOffset()) + poll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Empty(t, poll.GetFrontendResponse().GetMessages()) + + // The retry's publish is the first thing the stream ever holds. + _, err = poller.PollAndProcessWorkflowTask(testcore.WithExpectedAttemptCount(2)) + require.NoError(t, err) + poll, err = s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Equal(t, []string{"first-to-land"}, bodies(poll.GetFrontendResponse().GetMessages())) + require.Equal(t, []int64{0}, offsets(poll.GetFrontendResponse().GetMessages())) +} From 18a053d9d6be0adece3638f12a4e65a0a61b0b86 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:14:20 -0700 Subject: [PATCH 28/47] Regenerated the stream protos after the comment edits. --- chasm/lib/stream/gen/streampb/v1/message.pb.go | 2 +- chasm/lib/stream/gen/streampb/v1/stream_state.pb.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chasm/lib/stream/gen/streampb/v1/message.pb.go b/chasm/lib/stream/gen/streampb/v1/message.pb.go index 6ab9fdab0a0..9e1470f4d3a 100644 --- a/chasm/lib/stream/gen/streampb/v1/message.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/message.pb.go @@ -176,7 +176,7 @@ func (x *StreamMessage) GetOffset() int64 { return 0 } -// One append is one batch, and one batch is one log node. The server stores +// One append is one batch, and one batch is one data node. The server stores // this serialized and opaque; it decodes only to trim a partial first page or // to apply a topic filter. type StreamMessageBatch struct { diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go index a5080701dd2..57e0b6d6c3f 100644 --- a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go @@ -439,7 +439,7 @@ type StreamLifecycle struct { state protoimpl.MessageState `protogen:"open.v1"` // How long a closed stream stays readable before it is deleted. Retention *durationpb.Duration `protobuf:"bytes,1,opt,name=retention,proto3" json:"retention,omitempty"` - // Cap on readable messages. Older whole buckets are reclaimed once the floor + // Cap on readable messages. Whole batches are reclaimed once the floor // passes them, so a capped stream has bounded storage. MaxItems int64 `protobuf:"varint,2,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` unknownFields protoimpl.UnknownFields From ee8988623891bd42975f5f4a5abdf235fde101dc Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:18:51 -0700 Subject: [PATCH 29/47] Treated a stream message with no kind as data. Delivery to a workflow drops anything that is not data, so a producer leaving the field unset got an offset for a message no subscriber ever saw. --- chasm/lib/stream/stream.go | 9 ++++++ chasm/lib/stream/stream_test.go | 32 +++++++++++++++++++ tests/stream_consume_test.go | 54 +++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index f041209a6de..20cf72e6e36 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -143,6 +143,15 @@ func (s *Stream) AddMessages( if err := checkBatchBytes(req.Messages); err != nil { return AddMessagesResult{}, err } + // A message with no kind is data. Delivery to a workflow drops anything + // that is not, so a producer leaving the field at its zero value would get + // an offset for a message no subscriber ever sees. Settled before the batch + // is marshalled, so a retry hashes the same bytes. + for _, m := range req.Messages { + if m.GetKind() == streampb.STREAM_MESSAGE_KIND_UNSPECIFIED { + m.Kind = streampb.STREAM_MESSAGE_KIND_DATA + } + } blob, err := marshalBatch(req.Messages) if err != nil { diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go index 5df0a983650..2c4a56d694e 100644 --- a/chasm/lib/stream/stream_test.go +++ b/chasm/lib/stream/stream_test.go @@ -498,3 +498,35 @@ func TestStreamConsumerTableIsBounded(t *testing.T) { _, err = s.RegisterConsumer(nil, "c0", "wf", "run", 0, true) require.NoError(t, err) } + +// A producer that leaves the kind unset means data. Delivery to a workflow +// drops anything that is not data, so without this the message would take an +// offset and never be seen by a subscriber. +func TestAddMessagesTreatsAnUnsetKindAsData(t *testing.T) { + s := newTestStream(t) + unset := []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("a")}}, + {Body: &commonpb.Payload{Data: []byte("b")}}, + } + _, err := s.AddMessages(nil, AddMessagesRequest{ + Messages: unset, ProducerID: "p", Sequence: 1, + }) + require.NoError(t, err) + + blobs, starts, err := s.ReadBatches(nil, 0, 2, 0) + require.NoError(t, err) + collected, _, err := CollectMessages(blobs, starts, 0, 2, 10, nil) + require.NoError(t, err) + require.Len(t, ToAPIMessages(collected), 2, "both messages must reach a subscriber") + + // The retry carries the same unset kind and has to hash the same. + retry, err := s.AddMessages(nil, AddMessagesRequest{ + Messages: []*streampb.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("a")}}, + {Body: &commonpb.Payload{Data: []byte("b")}}, + }, + ProducerID: "p", Sequence: 1, + }) + require.NoError(t, err) + require.True(t, retry.Deduplicated) +} diff --git a/tests/stream_consume_test.go b/tests/stream_consume_test.go index 95981eda92e..92bb3457d05 100644 --- a/tests/stream_consume_test.go +++ b/tests/stream_consume_test.go @@ -974,3 +974,57 @@ func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED, }, order, "the events must be in the order their commands were issued") } + +// An activity streaming tokens over the RPC has no reason to know about message +// kinds. A message appended with the kind left unset has to reach a subscribed +// workflow like any other, rather than take an offset and vanish. +func TestMessagesAppendedWithoutAKindReachTheWorkflow(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "kindless-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + execution, tq := startConsumer(t, s, "stream-wf-kindless-") + + var delivered [][]*streampb.StreamSlice + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + + _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ + Namespace: s.ns, StreamId: streamID, + Messages: []*streamlib.StreamMessage{ + {Body: &commonpb.Payload{Data: []byte("no-kind-1")}}, + {Body: &commonpb.Payload{Data: []byte("no-kind-2")}}, + }, + }, + }) + require.NoError(t, err) + + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + got := currentSlice(t, delivered[1]) + require.Equal(t, int64(2), got.GetToOffset()) + require.Equal(t, []string{"no-kind-1", "no-kind-2"}, apiBodies(got.GetMessages()), + "the slice must carry the messages, not just advance past them") +} From cf37d212242e0cf29ab6e1b2899ec45bb0584bae Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:18:51 -0700 Subject: [PATCH 30/47] Dropped the implicit head pin from the RPC append handlers. The batch and the frontier commit in one transition and the execution serializes transitions, so the pin only made an outside producer fail when the workflow published in between. The stripe lock and the separate create step went with it. --- chasm/lib/stream/service/handler.go | 107 +++++----------------------- chasm/lib/workflow/workflow.go | 13 ---- tests/stream_workflow_test.go | 73 +++++++++++++++++++ 3 files changed, 90 insertions(+), 103 deletions(-) diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index 98de1199aa0..592f8f33cb4 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -2,9 +2,7 @@ package service import ( "context" - "sync" - "github.com/dgryski/go-farm" commonpb "go.temporal.io/api/common/v1" "go.temporal.io/api/serviceerror" "go.temporal.io/server/chasm" @@ -31,24 +29,6 @@ type handler struct { // refuses a shard this host does not own, so the far half goes back out // through the service and lands wherever it belongs. routed streampb.StreamServiceClient - - // Appends to one stream are serialized here. The node has to be durable - // before the frontier advances, which means writing it outside the - // transition that advances the frontier, and two concurrent writers could - // then commit in a different order than they wrote. Whichever node carried - // the higher transaction ID would win the read regardless of which writer - // actually committed. - // - // Serializing removes the interleaving. It is a stopgap: the real fix is to - // stage the node inside the CHASM transaction so write and commit order - // cannot diverge. Until then this also means appends are only safe within - // one process, which holds because these RPCs route to the shard owner. - // - // Striped rather than one lock per stream. The key comes from request input - // before the stream is known to exist, so a per-stream map would grow once - // per distinct id a caller names, including ids that resolve to nothing. - // Unrelated streams sharing a stripe only serialize with each other. - appendLk [appendStripes]sync.Mutex } func newHandler( @@ -65,10 +45,6 @@ func newHandler( } } -func streamKey(namespaceID, streamID string) string { - return namespaceID + "/" + streamID -} - // withCallerInfo tags the context so the stream's direct persistence calls are // attributed to the namespace that caused them. Without it they carry no caller // name, which means they escape namespace rate limiting and priority as well as @@ -83,21 +59,6 @@ func (h *handler) withCallerInfo(ctx context.Context, namespaceID string) contex name.String(), headers.CallerTypeAPI, "")) } -func (h *handler) lockStream(namespaceID, streamID string) func() { - mu := &h.appendLk[appendStripe(streamKey(namespaceID, streamID))] - mu.Lock() - return mu.Unlock -} - -// Sized well above the per-host stream count that would make collisions matter. -// Appends to one stream are serialized anyway, so a collision costs only the -// unrelated stream's concurrency, never correctness. -const appendStripes = 2048 - -func appendStripe(key string) uint32 { - return farm.Fingerprint32([]byte(key)) % appendStripes -} - // refFor builds a reference to a stream. A supplied run ID lets the engine skip // resolving the current run, which is otherwise a persistence lookup on every // call and dominates the cost of an otherwise cheap read. @@ -172,18 +133,12 @@ func (h *handler) AddMessages( if len(in.GetMessages()) == 0 { return nil, serviceerror.NewInvalidArgument("no messages to append") } - - unlock := h.lockStream(req.GetNamespaceId(), in.GetStreamId()) - defer unlock() - ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) - ref := refForRun(req.GetNamespaceId(), in.GetStreamId(), in.GetRunId()) - state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) - if err != nil { - return nil, err - } - + // The batch and the frontier commit in one transition, and the execution + // serializes transitions, so a producer that names no expected offset takes + // whatever offset it lands at. A retried sequence is answered by the + // producer table, not by a pin on the head. addReq := stream.AddMessagesRequest{ Messages: in.GetMessages(), ProducerID: in.GetProducerId(), @@ -192,15 +147,11 @@ func (h *handler) AddMessages( if in.GetUseExpectedOffset() { expected := in.GetExpectedOffset() addReq.ExpectedOffset = &expected - } else { - // Without a caller-supplied expectation, pin to the head we just read. - // The transition then fails rather than appending at an offset whose - // node we did not write. - head := state.GetHeadOffset() - addReq.ExpectedOffset = &head } - result, _, err := chasm.UpdateComponent(ctx, ref, (*stream.Stream).AddMessages, addReq) + result, _, err := chasm.UpdateComponent(ctx, + refForRun(req.GetNamespaceId(), in.GetStreamId(), in.GetRunId()), + (*stream.Stream).AddMessages, addReq) if err != nil { return nil, err } @@ -223,6 +174,10 @@ func (h *handler) AddMessages( // owning execution per batch, and batching is what keeps that cheap. It is the // path a model activity streaming tokens takes, where the workflow is only // bracketing what the activity produces. +// +// One transition does everything: the stream is created on first write, and +// the workflow's own publishes are serialized against this append by the +// execution, so neither producer needs to pin the head against the other. func (h *handler) AddWorkflowMessages( ctx context.Context, req *streampb.AddWorkflowMessagesRequest, @@ -231,47 +186,19 @@ func (h *handler) AddWorkflowMessages( if len(in.GetMessages()) == 0 { return nil, serviceerror.NewInvalidArgument("no messages to append") } - - name := ownedStreamName(in.GetStreamName()) - - // Keyed on the owner and the name, which is what identifies the stream here. - unlock := h.lockStream(req.GetNamespaceId(), in.GetWorkflowId()+"/"+name) - defer unlock() - ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) - ref := workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()) - - state, err := chasm.ReadComponent(ctx, ref, - func(wf *chasmworkflow.Workflow, cctx chasm.Context, streamName string) (*streampb.StreamState, error) { - return wf.OwnedStreamState(cctx, streamName) - }, name) - if err != nil { - return nil, err - } - if state == nil { - // Nothing has published yet. Creating the stream is a transition, and - // only the first writer ever pays it. - state, _, err = chasm.UpdateComponent(ctx, ref, - (*chasmworkflow.Workflow).EnsureOwnedStream, name) - if err != nil { - return nil, err - } - } - - head := state.GetHeadOffset() + name := ownedStreamName(in.GetStreamName()) addReq := stream.AddMessagesRequest{ Messages: in.GetMessages(), ProducerID: in.GetProducerId(), Sequence: in.GetSequence(), - // Pinned to the head just read, so a workflow task that published - // between the read and the commit fails this append rather than - // letting it claim offsets whose node it did not write. - ExpectedOffset: &head, } - - result, _, err := chasm.UpdateComponent(ctx, ref, - func(wf *chasmworkflow.Workflow, mctx chasm.MutableContext, r stream.AddMessagesRequest) (stream.AddMessagesResult, error) { + result, _, err := chasm.UpdateComponent(ctx, + workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()), + func( + wf *chasmworkflow.Workflow, mctx chasm.MutableContext, r stream.AddMessagesRequest, + ) (stream.AddMessagesResult, error) { return wf.AppendToOwnedStream(mctx, name, r) }, addReq) if err != nil { diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index 478fbe2282f..99d0347b1b9 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -628,19 +628,6 @@ func (w *Workflow) OwnedStream( return field.Get(ctx) } -// EnsureOwnedStream creates a stream this workflow owns if the first writer to -// it is not the workflow itself, and returns its state either way. -func (w *Workflow) EnsureOwnedStream( - mctx chasm.MutableContext, - name string, -) (*streamlib.StreamState, error) { - s, err := w.streamNamed(mctx, name) - if err != nil { - return nil, err - } - return s.Snapshot(mctx, struct{}{}) -} - // AppendToOwnedStream appends to a stream this workflow owns on behalf of a // writer outside the execution. // diff --git a/tests/stream_workflow_test.go b/tests/stream_workflow_test.go index 6385e0d8907..54e976931f3 100644 --- a/tests/stream_workflow_test.go +++ b/tests/stream_workflow_test.go @@ -1,6 +1,8 @@ package tests import ( + "fmt" + "sync" "testing" "time" @@ -407,3 +409,74 @@ func TestStreamWorkflowStreamClosesWithItsWorkflow(t *testing.T) { require.Equal(t, []string{"last word"}, bodies(poll.GetFrontendResponse().GetMessages())) require.True(t, poll.GetFrontendResponse().GetClosed()) } + +// An outside producer and the workflow's own publishes land in one log, with +// neither pinning the head against the other. Run concurrently, every append +// has to succeed and every message has to appear once, in a contiguous range. +func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-race-") + + const producers = 8 + const perProducer = 10 + const workflowTasks = 5 + + //nolint:staticcheck // SA1019: deprecated poller is the only one that can emit the command. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + return publishCommand("from-workflow"), nil + }, + Logger: env.Logger, + T: t, + } + + errs := make(chan error, producers*perProducer) + var wg sync.WaitGroup + for p := range producers { + wg.Add(1) + go func() { + defer wg.Done() + for i := range perProducer { + _, err := s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ + FrontendRequest: &streamlib.AddWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + Messages: []*streamlib.StreamMessage{{ + Body: &commonpb.Payload{Data: []byte(fmt.Sprintf("outside-%d-%d", p, i))}, + }}, + }, + }) + errs <- err + } + }() + } + for range workflowTasks { + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + } + wg.Wait() + close(errs) + for err := range errs { + require.NoError(t, err, "an outside append must not fail because the workflow published") + } + + total := producers*perProducer + workflowTasks + poll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Equal(t, int64(total), poll.GetFrontendResponse().GetHeadOffset()) + seen := make(map[string]int, total) + for _, body := range bodies(poll.GetFrontendResponse().GetMessages()) { + seen[body]++ + } + require.Len(t, seen, producers*perProducer+1, "every outside message appears exactly once") + require.Equal(t, workflowTasks, seen["from-workflow"]) +} From 31ab971cce9f3b06f9ab71cd807febcc9967ff2b Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:26:06 -0700 Subject: [PATCH 31/47] Moved the stream limits to dynamic config and budgeted owned streams. The caps that bound resource use are namespace-scoped settings with the old constants as defaults. A stream a workflow owns now carries a byte and item budget well under the mutable state limit, so the append is refused instead of the workflow being terminated. --- chasm/lib/stream/config.go | 246 ++++++++++++++---- .../streampb/v1/stream_state.go-helpers.pb.go | 37 +++ .../stream/gen/streampb/v1/stream_state.pb.go | 162 +++++++++--- chasm/lib/stream/messages.go | 10 +- chasm/lib/stream/proto/v1/stream_state.proto | 16 ++ chasm/lib/stream/service/fx.go | 3 +- chasm/lib/stream/service/handler.go | 33 ++- chasm/lib/stream/stream.go | 98 +++++-- chasm/lib/stream/stream_test.go | 92 +++++-- chasm/lib/workflow/fx.go | 7 +- chasm/lib/workflow/stream_commands.go | 47 +++- chasm/lib/workflow/stream_cursor_test.go | 17 +- chasm/lib/workflow/workflow.go | 12 +- .../stream_slices.go | 10 +- .../api/respondworkflowtaskcompleted/api.go | 1 + .../stream_appends.go | 4 +- service/history/configs/config.go | 3 + tests/stream_workflow_test.go | 58 +++++ 18 files changed, 696 insertions(+), 160 deletions(-) diff --git a/chasm/lib/stream/config.go b/chasm/lib/stream/config.go index 28970bd9c47..2dd9f47d4df 100644 --- a/chasm/lib/stream/config.go +++ b/chasm/lib/stream/config.go @@ -1,14 +1,18 @@ package stream -import "time" +import ( + "time" + + "go.temporal.io/server/common/dynamicconfig" +) // DefaultMaxMessagesPerPoll bounds a read page when the caller does not. const DefaultMaxMessagesPerPoll = 1000 // MaxMessagesPerBatch bounds one append. It is not only an admission limit: a -// node ID is the first offset of its batch, so to serve a read starting inside -// a batch the reader has to find the node that contains it. Bounding the batch -// bounds how far back it has to start, which turns an unbounded scan into a +// batch is keyed by its first offset, so to serve a read starting inside a +// batch the reader has to find the batch that contains it. Bounding the batch +// bounds how far back it has to look, which turns an unbounded scan into a // fixed overread. const MaxMessagesPerBatch = 1000 @@ -26,49 +30,203 @@ const LongPollBuffer = 3 * time.Second // shard would otherwise stretch the lock hold to match it. const RoutedCallTimeout = 5 * time.Second -// MaxConsumeItemsPerTask bounds one Workflow Task's slice. A byte cap alone is -// not enough: a burst of tiny messages stays under it while still making one -// task's drain arbitrarily long. Whichever bound binds first, the rest is -// delivered on the following task. -const MaxConsumeItemsPerTask = 1000 - -// MaxConsumeBytesPerTask bounds one Workflow Task's slice by size. Paired with -// MaxConsumeItemsPerTask because neither bound alone is enough: a burst of tiny -// messages slips under the byte budget, and a few large ones slip under the -// item count. -const MaxConsumeBytesPerTask = 2 << 20 - -// MaxProducersPerStream bounds the per-producer dedup table. The table is part -// of the component state written on every append, so a caller that sends a -// fresh producer id per request would grow the state until the mutable-state -// size limit rejects every further append, leaving the stream unwritable for -// good. The bound turns that into a clear error on the offending call. -const MaxProducersPerStream = 1000 - -// MaxConsumersPerStream bounds the registered consumer table for the same -// reason. Each consumer also holds a truncation floor, so an unbounded table -// would pin storage as well as grow state. -const MaxConsumersPerStream = 1000 - // MaxListPageSize bounds a visibility page when the caller does not. const MaxListPageSize = 1000 -// MaxMessageBytes bounds one message. A message is never split, so this is also -// the smallest unit a reader can be asked to materialise. -const MaxMessageBytes = 1 << 20 +// MaxStreamNameLength bounds a name before it becomes a map key in mutable +// state. The name comes from the caller, and any caller in the namespace can +// pick a new one. +const MaxStreamNameLength = 255 -// MaxBatchBytes bounds one append. It is deliberately equal to -// MaxConsumeBytesPerTask: a batch is written as one node and read back whole, -// so a batch larger than a task's byte budget could never be delivered. -const MaxBatchBytes = MaxConsumeBytesPerTask +// The limits below bound resource use and are namespace-scoped dynamic config, +// with these values as the defaults. They stay as constants too so component +// code driven without a config, as the unit tests do, has something to fall +// back on. +const ( + // MaxConsumeItemsPerTask bounds one Workflow Task's slice. A byte cap alone + // is not enough: a burst of tiny messages stays under it while still making + // one task's drain arbitrarily long. Whichever bound binds first, the rest + // is delivered on the following task. + MaxConsumeItemsPerTask = 1000 -// MaxOwnedStreamsPerWorkflow bounds how many named streams one execution can -// carry. Each is a component in the workflow's mutable state, so an unbounded -// count grows that state until the size limit terminates the execution. The -// name comes from the caller, and any caller in the namespace can pick a new -// one, which is what makes this reachable from outside. -const MaxOwnedStreamsPerWorkflow = 100 + // MaxConsumeBytesPerTask bounds one Workflow Task's slice by size. Paired + // with MaxConsumeItemsPerTask because neither bound alone is enough: a + // burst of tiny messages slips under the byte budget, and a few large ones + // slip under the item count. + MaxConsumeBytesPerTask = 2 << 20 -// MaxStreamNameLength bounds a name before it becomes a map key in mutable -// state, for the same reason. -const MaxStreamNameLength = 255 + // MaxProducersPerStream bounds the per-producer dedup table. The table is + // part of the component state written on every append, so a caller that + // sends a fresh producer id per request would grow the state until the + // mutable-state size limit rejects every further append, leaving the + // stream unwritable for good. The bound turns that into a clear error on + // the offending call. + MaxProducersPerStream = 1000 + + // MaxConsumersPerStream bounds the registered consumer table for the same + // reason. Each consumer also holds a truncation floor, so an unbounded + // table would pin storage as well as grow state. + MaxConsumersPerStream = 1000 + + // MaxMessageBytes bounds one message. A message is never split, so this is + // also the smallest unit a reader can be asked to materialise. + MaxMessageBytes = 1 << 20 + + // MaxBatchBytes bounds one append. It is deliberately equal to + // MaxConsumeBytesPerTask: a batch is written as one node and read back + // whole, so a batch larger than a task's byte budget could never be + // delivered. + MaxBatchBytes = MaxConsumeBytesPerTask + + // MaxOwnedStreamsPerWorkflow bounds how many named streams one execution + // can carry. Each is a component in the workflow's mutable state, so an + // unbounded count grows that state until the size limit terminates the + // execution, and any caller in the namespace can name a new one. + MaxOwnedStreamsPerWorkflow = 100 + + // OwnedStreamMaxBytes is the byte budget of a stream a workflow owns. Its + // batches are the owning execution's mutable state, and the execution size + // limit terminates the workflow rather than refusing an append, so the + // stream refuses first. The sum over a workflow's streams is still bounded + // by that limit. + OwnedStreamMaxBytes = 2 << 20 + + // OwnedStreamMaxItems is the item budget of a stream a workflow owns, for + // the same reason: each batch is a node, and many small ones cost state + // that the byte budget alone does not see. + OwnedStreamMaxItems = 10_000 +) + +var ( + MaxConsumeItemsPerTaskSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.maxConsumeItemsPerTask", + MaxConsumeItemsPerTask, + `Most stream messages one workflow task carries per subscription.`, + ) + MaxConsumeBytesPerTaskSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.maxConsumeBytesPerTask", + MaxConsumeBytesPerTask, + `Most stream message bytes one workflow task carries per subscription.`, + ) + MaxProducersPerStreamSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.maxProducersPerStream", + MaxProducersPerStream, + `Most producer ids a stream tracks for deduplication.`, + ) + MaxConsumersPerStreamSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.maxConsumersPerStream", + MaxConsumersPerStream, + `Most workflow consumers a stream registers.`, + ) + MaxMessageBytesSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.maxMessageBytes", + MaxMessageBytes, + `Largest single stream message accepted.`, + ) + MaxBatchBytesSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.maxBatchBytes", + MaxBatchBytes, + `Largest stream append accepted, summed over its messages.`, + ) + MaxOwnedStreamsPerWorkflowSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.maxOwnedStreamsPerWorkflow", + MaxOwnedStreamsPerWorkflow, + `Most named streams one workflow execution can own.`, + ) + OwnedStreamMaxBytesSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.ownedStreamMaxBytes", + OwnedStreamMaxBytes, + `Byte budget of a stream a workflow owns. Appends past it are refused. Keep it well +under limit.mutableStateSize.error, which would otherwise terminate the workflow.`, + ) + OwnedStreamMaxItemsSetting = dynamicconfig.NewNamespaceIntSetting( + "stream.ownedStreamMaxItems", + OwnedStreamMaxItems, + `Message budget of a stream a workflow owns. Appends past it are refused.`, + ) +) + +// Config holds the namespace-scoped limits as live property functions. +type Config struct { + MaxConsumeItemsPerTask dynamicconfig.IntPropertyFnWithNamespaceFilter + MaxConsumeBytesPerTask dynamicconfig.IntPropertyFnWithNamespaceFilter + MaxProducersPerStream dynamicconfig.IntPropertyFnWithNamespaceFilter + MaxConsumersPerStream dynamicconfig.IntPropertyFnWithNamespaceFilter + MaxMessageBytes dynamicconfig.IntPropertyFnWithNamespaceFilter + MaxBatchBytes dynamicconfig.IntPropertyFnWithNamespaceFilter + MaxOwnedStreamsPerWorkflow dynamicconfig.IntPropertyFnWithNamespaceFilter + OwnedStreamMaxBytes dynamicconfig.IntPropertyFnWithNamespaceFilter + OwnedStreamMaxItems dynamicconfig.IntPropertyFnWithNamespaceFilter +} + +func NewConfig(dc *dynamicconfig.Collection) *Config { + return &Config{ + MaxConsumeItemsPerTask: MaxConsumeItemsPerTaskSetting.Get(dc), + MaxConsumeBytesPerTask: MaxConsumeBytesPerTaskSetting.Get(dc), + MaxProducersPerStream: MaxProducersPerStreamSetting.Get(dc), + MaxConsumersPerStream: MaxConsumersPerStreamSetting.Get(dc), + MaxMessageBytes: MaxMessageBytesSetting.Get(dc), + MaxBatchBytes: MaxBatchBytesSetting.Get(dc), + MaxOwnedStreamsPerWorkflow: MaxOwnedStreamsPerWorkflowSetting.Get(dc), + OwnedStreamMaxBytes: OwnedStreamMaxBytesSetting.Get(dc), + OwnedStreamMaxItems: OwnedStreamMaxItemsSetting.Get(dc), + } +} + +// Limits is one namespace's resolved limits, read once per request so a +// transition sees one consistent set. +type Limits struct { + MaxConsumeItemsPerTask int + MaxConsumeBytesPerTask int + MaxProducersPerStream int + MaxConsumersPerStream int + MaxMessageBytes int + MaxBatchBytes int + MaxOwnedStreamsPerWorkflow int + OwnedStreamMaxBytes int + OwnedStreamMaxItems int +} + +// LimitsFor resolves the limits for a namespace. A nil Config, which is what +// code driven without a service gets, resolves to the defaults. +func (c *Config) LimitsFor(namespaceName string) Limits { + if c == nil { + return DefaultLimits() + } + return Limits{ + MaxConsumeItemsPerTask: c.MaxConsumeItemsPerTask(namespaceName), + MaxConsumeBytesPerTask: c.MaxConsumeBytesPerTask(namespaceName), + MaxProducersPerStream: c.MaxProducersPerStream(namespaceName), + MaxConsumersPerStream: c.MaxConsumersPerStream(namespaceName), + MaxMessageBytes: c.MaxMessageBytes(namespaceName), + MaxBatchBytes: c.MaxBatchBytes(namespaceName), + MaxOwnedStreamsPerWorkflow: c.MaxOwnedStreamsPerWorkflow(namespaceName), + OwnedStreamMaxBytes: c.OwnedStreamMaxBytes(namespaceName), + OwnedStreamMaxItems: c.OwnedStreamMaxItems(namespaceName), + }.withDefaults() +} + +// DefaultLimits is the constant set above. +func DefaultLimits() Limits { + return Limits{}.withDefaults() +} + +// withDefaults fills any limit left at zero, so a zero Limits value means the +// defaults rather than a stream that accepts nothing. +func (l Limits) withDefaults() Limits { + fill := func(v *int, def int) { + if *v <= 0 { + *v = def + } + } + fill(&l.MaxConsumeItemsPerTask, MaxConsumeItemsPerTask) + fill(&l.MaxConsumeBytesPerTask, MaxConsumeBytesPerTask) + fill(&l.MaxProducersPerStream, MaxProducersPerStream) + fill(&l.MaxConsumersPerStream, MaxConsumersPerStream) + fill(&l.MaxMessageBytes, MaxMessageBytes) + fill(&l.MaxBatchBytes, MaxBatchBytes) + fill(&l.MaxOwnedStreamsPerWorkflow, MaxOwnedStreamsPerWorkflow) + fill(&l.OwnedStreamMaxBytes, OwnedStreamMaxBytes) + fill(&l.OwnedStreamMaxItems, OwnedStreamMaxItems) + return l +} diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go index 99af3b53701..c97d64015d9 100644 --- a/chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.go-helpers.pb.go @@ -42,6 +42,43 @@ func (this *StreamState) Equal(that interface{}) bool { return proto.Equal(this, that1) } +// Marshal an object of type StreamBudget to the protobuf v3 wire format +func (val *StreamBudget) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type StreamBudget from the protobuf v3 wire format +func (val *StreamBudget) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *StreamBudget) Size() int { + return proto.Size(val) +} + +// Equal returns whether two StreamBudget 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 *StreamBudget) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *StreamBudget + switch t := that.(type) { + case *StreamBudget: + that1 = t + case StreamBudget: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + // Marshal an object of type ProducerCursor to the protobuf v3 wire format func (val *ProducerCursor) Marshal() ([]byte, error) { return proto.Marshal(val) diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go index 57e0b6d6c3f..682bbeea0ae 100644 --- a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go @@ -43,7 +43,14 @@ type StreamState struct { // the chain instead of stalling on a superseded run. RedirectRunId string `protobuf:"bytes,12,opt,name=redirect_run_id,json=redirectRunId,proto3" json:"redirect_run_id,omitempty"` // Wall-clock close time, used to schedule retention deletion. - CloseTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=close_time,json=closeTime,proto3" json:"close_time,omitempty"` + CloseTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=close_time,json=closeTime,proto3" json:"close_time,omitempty"` + // Set on a stream a workflow owns. Its batches are the owning execution's + // mutable state, so appends past the budget are refused rather than left to + // the execution size limit, which terminates the workflow. + Budget *StreamBudget `protobuf:"bytes,14,opt,name=budget,proto3" json:"budget,omitempty"` + // Bytes appended over the stream's life, kept for the budget check. A stream + // with a budget never reclaims, so this is also what it holds. + AppendedBytes int64 `protobuf:"varint,15,opt,name=appended_bytes,json=appendedBytes,proto3" json:"appended_bytes,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -141,6 +148,74 @@ func (x *StreamState) GetCloseTime() *timestamppb.Timestamp { return nil } +func (x *StreamState) GetBudget() *StreamBudget { + if x != nil { + return x.Budget + } + return nil +} + +func (x *StreamState) GetAppendedBytes() int64 { + if x != nil { + return x.AppendedBytes + } + return 0 +} + +// Hard bounds on what a stream may hold. Distinct from StreamLifecycle.max_items, +// which reclaims the oldest messages: a budget refuses the newest. +type StreamBudget struct { + state protoimpl.MessageState `protogen:"open.v1"` + MaxItems int64 `protobuf:"varint,1,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` + MaxBytes int64 `protobuf:"varint,2,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamBudget) Reset() { + *x = StreamBudget{} + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamBudget) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamBudget) ProtoMessage() {} + +func (x *StreamBudget) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[1] + 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 StreamBudget.ProtoReflect.Descriptor instead. +func (*StreamBudget) Descriptor() ([]byte, []int) { + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{1} +} + +func (x *StreamBudget) GetMaxItems() int64 { + if x != nil { + return x.MaxItems + } + return 0 +} + +func (x *StreamBudget) GetMaxBytes() int64 { + if x != nil { + return x.MaxBytes + } + return 0 +} + type ProducerCursor struct { state protoimpl.MessageState `protogen:"open.v1"` Seq int64 `protobuf:"varint,1,opt,name=seq,proto3" json:"seq,omitempty"` @@ -158,7 +233,7 @@ type ProducerCursor struct { func (x *ProducerCursor) Reset() { *x = ProducerCursor{} - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[1] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -170,7 +245,7 @@ func (x *ProducerCursor) String() string { func (*ProducerCursor) ProtoMessage() {} func (x *ProducerCursor) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[1] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -183,7 +258,7 @@ func (x *ProducerCursor) ProtoReflect() protoreflect.Message { // Deprecated: Use ProducerCursor.ProtoReflect.Descriptor instead. func (*ProducerCursor) Descriptor() ([]byte, []int) { - return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{1} + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{2} } func (x *ProducerCursor) GetSeq() int64 { @@ -247,7 +322,7 @@ type ConsumerCursor struct { func (x *ConsumerCursor) Reset() { *x = ConsumerCursor{} - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[2] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -259,7 +334,7 @@ func (x *ConsumerCursor) String() string { func (*ConsumerCursor) ProtoMessage() {} func (x *ConsumerCursor) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[2] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -272,7 +347,7 @@ func (x *ConsumerCursor) ProtoReflect() protoreflect.Message { // Deprecated: Use ConsumerCursor.ProtoReflect.Descriptor instead. func (*ConsumerCursor) Descriptor() ([]byte, []int) { - return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{2} + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{3} } func (x *ConsumerCursor) GetWorkflowId() string { @@ -351,7 +426,7 @@ type WorkflowStreamCursor struct { func (x *WorkflowStreamCursor) Reset() { *x = WorkflowStreamCursor{} - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[3] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -363,7 +438,7 @@ func (x *WorkflowStreamCursor) String() string { func (*WorkflowStreamCursor) ProtoMessage() {} func (x *WorkflowStreamCursor) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[3] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -376,7 +451,7 @@ func (x *WorkflowStreamCursor) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowStreamCursor.ProtoReflect.Descriptor instead. func (*WorkflowStreamCursor) Descriptor() ([]byte, []int) { - return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{3} + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{4} } func (x *WorkflowStreamCursor) GetStreamId() string { @@ -448,7 +523,7 @@ type StreamLifecycle struct { func (x *StreamLifecycle) Reset() { *x = StreamLifecycle{} - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[4] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -460,7 +535,7 @@ func (x *StreamLifecycle) String() string { func (*StreamLifecycle) ProtoMessage() {} func (x *StreamLifecycle) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[4] + mi := &file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -473,7 +548,7 @@ func (x *StreamLifecycle) ProtoReflect() protoreflect.Message { // Deprecated: Use StreamLifecycle.ProtoReflect.Descriptor instead. func (*StreamLifecycle) Descriptor() ([]byte, []int) { - return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{4} + return file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDescGZIP(), []int{5} } func (x *StreamLifecycle) GetRetention() *durationpb.Duration { @@ -494,7 +569,7 @@ var File_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto protorefle const file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc = "" + "\n" + - " temporal.api.common.v1.Payload - 5, // 1: temporal.server.chasm.lib.stream.proto.v1.StreamState.producers:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState.ProducersEntry - 6, // 2: temporal.server.chasm.lib.stream.proto.v1.StreamState.consumers:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState.ConsumersEntry - 4, // 3: temporal.server.chasm.lib.stream.proto.v1.StreamState.lifecycle:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle - 8, // 4: temporal.server.chasm.lib.stream.proto.v1.StreamState.close_time:type_name -> google.protobuf.Timestamp - 9, // 5: temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle.retention:type_name -> google.protobuf.Duration - 1, // 6: temporal.server.chasm.lib.stream.proto.v1.StreamState.ProducersEntry.value:type_name -> temporal.server.chasm.lib.stream.proto.v1.ProducerCursor - 2, // 7: temporal.server.chasm.lib.stream.proto.v1.StreamState.ConsumersEntry.value:type_name -> temporal.server.chasm.lib.stream.proto.v1.ConsumerCursor - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 8, // 0: temporal.server.chasm.lib.stream.proto.v1.StreamState.close_reason:type_name -> temporal.api.common.v1.Payload + 6, // 1: temporal.server.chasm.lib.stream.proto.v1.StreamState.producers:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState.ProducersEntry + 7, // 2: temporal.server.chasm.lib.stream.proto.v1.StreamState.consumers:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState.ConsumersEntry + 5, // 3: temporal.server.chasm.lib.stream.proto.v1.StreamState.lifecycle:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle + 9, // 4: temporal.server.chasm.lib.stream.proto.v1.StreamState.close_time:type_name -> google.protobuf.Timestamp + 1, // 5: temporal.server.chasm.lib.stream.proto.v1.StreamState.budget:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamBudget + 10, // 6: temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle.retention:type_name -> google.protobuf.Duration + 2, // 7: temporal.server.chasm.lib.stream.proto.v1.StreamState.ProducersEntry.value:type_name -> temporal.server.chasm.lib.stream.proto.v1.ProducerCursor + 3, // 8: temporal.server.chasm.lib.stream.proto.v1.StreamState.ConsumersEntry.value:type_name -> temporal.server.chasm.lib.stream.proto.v1.ConsumerCursor + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_init() } @@ -598,7 +680,7 @@ func file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc), len(file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc)), NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/chasm/lib/stream/messages.go b/chasm/lib/stream/messages.go index 37fb96df504..efb29e1aab8 100644 --- a/chasm/lib/stream/messages.go +++ b/chasm/lib/stream/messages.go @@ -107,19 +107,19 @@ func CapByBytes( // that exceeds a consumer's byte budget can never be delivered, and CapByBytes // would hand it over alone forever rather than reject it. The batch bound is // the storage side, since a batch is written as a single node. -func checkBatchBytes(messages []*streamlib.StreamMessage) error { +func checkBatchBytes(messages []*streamlib.StreamMessage, limits Limits) error { total := 0 for i, m := range messages { size := proto.Size(m) - if size > MaxMessageBytes { + if size > limits.MaxMessageBytes { return serviceerror.NewInvalidArgumentf( - "message %d is %d bytes, over the %d byte limit", i, size, MaxMessageBytes) + "message %d is %d bytes, over the %d byte limit", i, size, limits.MaxMessageBytes) } total += size } - if total > MaxBatchBytes { + if total > limits.MaxBatchBytes { return serviceerror.NewInvalidArgumentf( - "batch is %d bytes, over the %d byte limit", total, MaxBatchBytes) + "batch is %d bytes, over the %d byte limit", total, limits.MaxBatchBytes) } return nil } diff --git a/chasm/lib/stream/proto/v1/stream_state.proto b/chasm/lib/stream/proto/v1/stream_state.proto index 3005425df25..b623ea39a12 100644 --- a/chasm/lib/stream/proto/v1/stream_state.proto +++ b/chasm/lib/stream/proto/v1/stream_state.proto @@ -35,6 +35,22 @@ message StreamState { // Wall-clock close time, used to schedule retention deletion. google.protobuf.Timestamp close_time = 13; + + // Set on a stream a workflow owns. Its batches are the owning execution's + // mutable state, so appends past the budget are refused rather than left to + // the execution size limit, which terminates the workflow. + StreamBudget budget = 14; + + // Bytes appended over the stream's life, kept for the budget check. A stream + // with a budget never reclaims, so this is also what it holds. + int64 appended_bytes = 15; +} + +// Hard bounds on what a stream may hold. Distinct from StreamLifecycle.max_items, +// which reclaims the oldest messages: a budget refuses the newest. +message StreamBudget { + int64 max_items = 1; + int64 max_bytes = 2; } message ProducerCursor { diff --git a/chasm/lib/stream/service/fx.go b/chasm/lib/stream/service/fx.go index 06ad4ffa779..b8592129f53 100644 --- a/chasm/lib/stream/service/fx.go +++ b/chasm/lib/stream/service/fx.go @@ -11,7 +11,8 @@ var HistoryModule = fx.Module( fx.Provide( // Routes a call to the host owning a shard. History needs it too, not // just the frontend: a step that spans two executions has to reach a - // shard this host may not own. + // shard this host may not own. The stream config comes from the + // workflow library module, which every service running this one has. streampb.NewStreamServiceLayeredClient, newHandler, newRetentionTaskHandler, diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index 592f8f33cb4..3bb2f50069b 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -23,6 +23,7 @@ type handler struct { shardController shard.Controller namespaceRegistry namespace.Registry logger log.Logger + config *stream.Config // Routes a call to the host that owns a shard. A step spanning two // executions cannot resolve both through the local controller, which @@ -35,16 +36,29 @@ func newHandler( shardController shard.Controller, namespaceRegistry namespace.Registry, logger log.Logger, + config *stream.Config, routed streampb.StreamServiceClient, ) *handler { return &handler{ shardController: shardController, namespaceRegistry: namespaceRegistry, logger: logger, + config: config, routed: routed, } } +// limitsFor resolves the namespace's limits. An id the registry cannot name +// falls back to the defaults; the interceptors have already refused requests +// for namespaces that do not exist. +func (h *handler) limitsFor(namespaceID string) stream.Limits { + name, err := h.namespaceRegistry.GetNamespaceName(namespace.ID(namespaceID)) + if err != nil { + return stream.DefaultLimits() + } + return h.config.LimitsFor(name.String()) +} + // withCallerInfo tags the context so the stream's direct persistence calls are // attributed to the namespace that caused them. Without it they carry no caller // name, which means they escape namespace rate limiting and priority as well as @@ -143,6 +157,7 @@ func (h *handler) AddMessages( Messages: in.GetMessages(), ProducerID: in.GetProducerId(), Sequence: in.GetSequence(), + Limits: h.limitsFor(req.GetNamespaceId()), } if in.GetUseExpectedOffset() { expected := in.GetExpectedOffset() @@ -193,6 +208,7 @@ func (h *handler) AddWorkflowMessages( Messages: in.GetMessages(), ProducerID: in.GetProducerId(), Sequence: in.GetSequence(), + Limits: h.limitsFor(req.GetNamespaceId()), } result, _, err := chasm.UpdateComponent(ctx, workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()), @@ -251,12 +267,15 @@ func (h *handler) SubscribeWorkflow( return h.subscribeToExternalStream(ctx, req.GetNamespaceId(), in) } + limits := h.limitsFor(req.GetNamespaceId()) startOffset, _, err := chasm.UpdateComponent( ctx, workflowRef(req.GetNamespaceId(), in.GetWorkflowId(), in.GetOwnerRunId()), - func(wf *chasmworkflow.Workflow, mctx chasm.MutableContext, input *streampb.SubscribeWorkflowInput) (int64, error) { + func( + wf *chasmworkflow.Workflow, mctx chasm.MutableContext, input *streampb.SubscribeWorkflowInput, + ) (int64, error) { return wf.SubscribeToOwnedStream( - mctx, ownedStreamName(input.GetStreamName()), input.GetStartOffset()) + mctx, ownedStreamName(input.GetStreamName()), input.GetStartOffset(), limits) }, in, ) @@ -341,14 +360,20 @@ func (h *handler) RegisterStreamConsumer( ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) consumerID := "workflow:" + in.GetConsumerWorkflowId() + limits := h.limitsFor(req.GetNamespaceId()) pin, _, err := chasm.UpdateComponent( ctx, refFor(req.GetNamespaceId(), in.GetStreamId()), func( s *stream.Stream, mctx chasm.MutableContext, offset int64, ) (*streampb.RegisterStreamConsumerOutput, error) { - startOffset, err := s.RegisterConsumer( - mctx, consumerID, in.GetConsumerWorkflowId(), "", offset, true) + startOffset, err := s.RegisterConsumer(mctx, stream.ConsumerRegistration{ + ConsumerID: consumerID, + WorkflowID: in.GetConsumerWorkflowId(), + Offset: offset, + External: true, + MaxConsumers: limits.MaxConsumersPerStream, + }) if err != nil { return nil, err } diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index 20cf72e6e36..2fce0da0b6f 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -46,6 +46,10 @@ type Stream struct { type NewStreamRequest struct { Lifecycle *streampb.StreamLifecycle + // Budget bounds what the stream may hold. Set for a stream a workflow + // owns, whose batches are the owning execution's mutable state. + Budget *streampb.StreamBudget + // Attached means the stream is a subcomponent of another execution rather // than a root. CHASM requires a visibility component to be an immediate // child of the root, so an attached stream carries none and is found @@ -61,6 +65,10 @@ type AddMessagesRequest struct { ProducerID string Sequence int64 ExpectedOffset *int64 + + // The namespace's limits, resolved by the caller. A zero value means the + // defaults. + Limits Limits } type AddMessagesResult struct { @@ -87,6 +95,7 @@ func NewStream(ctx chasm.MutableContext, req NewStreamRequest) (*Stream, error) Batches: make(chasm.Map[int64, *commonpb.DataBlob]), State: &streampb.StreamState{ Lifecycle: req.Lifecycle, + Budget: req.Budget, Producers: make(map[string]*streampb.ProducerCursor), Consumers: make(map[string]*streampb.ConsumerCursor), }, @@ -140,7 +149,8 @@ func (s *Stream) AddMessages( return AddMessagesResult{}, serviceerror.NewInvalidArgumentf( "batch of %d exceeds the limit of %d messages", len(req.Messages), MaxMessagesPerBatch) } - if err := checkBatchBytes(req.Messages); err != nil { + limits := req.Limits.withDefaults() + if err := checkBatchBytes(req.Messages, limits); err != nil { return AddMessagesResult{}, err } // A message with no kind is data. Delivery to a workflow drops anything @@ -167,7 +177,10 @@ func (s *Stream) AddMessages( } // After the retry check, so a known producer is never rejected for room. - if err := s.checkProducerRoom(req.ProducerID); err != nil { + if err := s.checkProducerRoom(req.ProducerID, limits.MaxProducersPerStream); err != nil { + return AddMessagesResult{}, err + } + if err := s.checkBudget(int64(len(req.Messages)), int64(len(blob.Data))); err != nil { return AddMessagesResult{}, err } @@ -194,6 +207,7 @@ func (s *Stream) AddMessages( s.Batches[first] = chasm.NewDataField(mctx, blob) s.State.HeadOffset = first + count + s.State.AppendedBytes += int64(len(blob.Data)) if req.ProducerID != "" { if s.State.Producers == nil { s.State.Producers = make(map[string]*streampb.ProducerCursor) @@ -241,14 +255,14 @@ func (s *Stream) notifyConsumers(mctx chasm.MutableContext) { // Entries whose whole batch sits below the floor go first: a retry of a batch // that truncation already removed cannot be served its recorded offsets // anyway, so the entry has no use left. -func (s *Stream) checkProducerRoom(producerID string) error { +func (s *Stream) checkProducerRoom(producerID string, maxProducers int) error { if producerID == "" { return nil } if _, known := s.State.Producers[producerID]; known { return nil } - if len(s.State.Producers) < MaxProducersPerStream { + if len(s.State.Producers) < maxProducers { return nil } for id, cursor := range s.State.Producers { @@ -256,9 +270,35 @@ func (s *Stream) checkProducerRoom(producerID string) error { delete(s.State.Producers, id) } } - if len(s.State.Producers) >= MaxProducersPerStream { + if len(s.State.Producers) >= maxProducers { return serviceerror.NewInvalidArgumentf( - "stream already tracks %d producers, which is the limit", MaxProducersPerStream) + "stream already tracks %d producers, which is the limit", maxProducers) + } + return nil +} + +// checkBudget refuses an append a budgeted stream cannot hold. +// +// Refused rather than reclaimed: the budget exists because the batches are +// mutable state of the execution that owns the stream, and the alternative to +// refusing here is the execution size limit terminating that workflow later, +// with nothing naming the stream as the cause. +func (s *Stream) checkBudget(count int64, size int64) error { + budget := s.State.GetBudget() + if budget == nil { + return nil + } + if max := budget.GetMaxItems(); max > 0 && s.State.HeadOffset+count > max { + return serviceerror.NewResourceExhaustedf( + enumspb.RESOURCE_EXHAUSTED_CAUSE_PERSISTENCE_STORAGE_LIMIT, + "stream holds %d of its budget of %d messages; the append of %d does not fit", + s.State.HeadOffset, max, count) + } + if max := budget.GetMaxBytes(); max > 0 && s.State.AppendedBytes+size > max { + return serviceerror.NewResourceExhaustedf( + enumspb.RESOURCE_EXHAUSTED_CAUSE_PERSISTENCE_STORAGE_LIMIT, + "stream holds %d of its budget of %d bytes; the append of %d does not fit", + s.State.AppendedBytes, max, size) } return nil } @@ -577,6 +617,18 @@ func (s *Stream) checkCapRoom(count int64) error { maxItems, holder, floor) } +// ConsumerRegistration describes a consumer being registered on a stream. +type ConsumerRegistration struct { + ConsumerID string + WorkflowID string + RunID string + // Negative means the head as of the registering transition. + Offset int64 + External bool + // Zero means the default. + MaxConsumers int +} + // RegisterConsumer records an in-workflow consumer, so appends know who to wake // and retention knows what it may not delete. It returns the offset the // consumer reads from: the resolved start for a new consumer, and the current @@ -590,17 +642,15 @@ func (s *Stream) checkCapRoom(count int64) error { // to. The ranges this consumer already took are written into its History, and a // replay is asked to reproduce them, so the bytes behind the read position are // the ones a recovery needs. Deregistering releases the floor. -func (s *Stream) RegisterConsumer( - _ chasm.MutableContext, - consumerID string, - workflowID string, - runID string, - offset int64, - external bool, -) (int64, error) { - if consumerID == "" { +func (s *Stream) RegisterConsumer(_ chasm.MutableContext, reg ConsumerRegistration) (int64, error) { + if reg.ConsumerID == "" { return 0, serviceerror.NewInvalidArgument("consumer id is required") } + maxConsumers := reg.MaxConsumers + if maxConsumers <= 0 { + maxConsumers = MaxConsumersPerStream + } + offset := reg.Offset if offset < 0 { offset = s.State.HeadOffset } @@ -608,32 +658,32 @@ func (s *Stream) RegisterConsumer( return 0, serviceerror.NewFailedPreconditionf( "offset %d is below the stream's floor of %d", offset, s.State.BaseOffset) } - if _, known := s.State.Consumers[consumerID]; !known && - len(s.State.Consumers) >= MaxConsumersPerStream { + if _, known := s.State.Consumers[reg.ConsumerID]; !known && + len(s.State.Consumers) >= maxConsumers { return 0, serviceerror.NewInvalidArgumentf( - "stream already has %d consumers, which is the limit", MaxConsumersPerStream) + "stream already has %d consumers, which is the limit", maxConsumers) } if s.State.Consumers == nil { s.State.Consumers = make(map[string]*streampb.ConsumerCursor) } - if existing, ok := s.State.Consumers[consumerID]; ok { + if existing, ok := s.State.Consumers[reg.ConsumerID]; ok { // A consumer coming back after its floor was released can find the // stream has moved past what its History refers to. Saying so here is // the only chance to say it before the workflow depends on it again. if existing.GetReplayFloor() < s.State.BaseOffset { return 0, serviceerror.NewFailedPreconditionf( "consumer %q recorded offset %d, and the stream now starts at %d", - consumerID, existing.GetReplayFloor(), s.State.BaseOffset) + reg.ConsumerID, existing.GetReplayFloor(), s.State.BaseOffset) } existing.Active = true return existing.GetOffset(), nil } - s.State.Consumers[consumerID] = &streampb.ConsumerCursor{ - WorkflowId: workflowID, - RunId: runID, + s.State.Consumers[reg.ConsumerID] = &streampb.ConsumerCursor{ + WorkflowId: reg.WorkflowID, + RunId: reg.RunID, Offset: offset, Active: true, - External: external, + External: reg.External, ReplayFloor: offset, } return offset, nil diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go index 2c4a56d694e..36d73bee946 100644 --- a/chasm/lib/stream/stream_test.go +++ b/chasm/lib/stream/stream_test.go @@ -214,7 +214,9 @@ func TestTruncateStopsAtAnActiveConsumersReplayFloor(t *testing.T) { _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "wf-1", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) s.AdvanceConsumer(nil, "wf-1", 2) @@ -259,7 +261,9 @@ func TestCapTruncatesInline(t *testing.T) { func TestCapRefusesAnAppendItCouldOnlyAbsorbByDroppingReadRecords(t *testing.T) { s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} - _, err := s.RegisterConsumer(nil, "wf-1", "wf-1", "run-1", 0, false) + _, err := s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "wf-1", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) @@ -299,7 +303,9 @@ func TestRegisterConsumerPinsFromWhereItSubscribed(t *testing.T) { // Subscribing at 2 says nothing about offsets 0 and 1, so those stay // droppable and everything from 2 up does not. - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 2, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 2, + }) require.NoError(t, err) require.NoError(t, s.Truncate(nil, 2)) @@ -311,7 +317,9 @@ func TestAdvanceConsumerTracksWhereAConsumerHasReached(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) // The read position decides whether this consumer is worth waking. The @@ -326,7 +334,9 @@ func TestAdvanceConsumerNeverRewinds(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) s.AdvanceConsumer(nil, "workflow:output", 3) @@ -342,7 +352,9 @@ func TestRegisterConsumerRejectsAnOffsetBelowTheFloor(t *testing.T) { err = s.Truncate(nil, 2) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 1, + }) require.ErrorContains(t, err, "below the stream's floor") } @@ -352,11 +364,15 @@ func TestRegisterConsumerTwiceKeepsThePin(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) s.AdvanceConsumer(nil, "workflow:output", 3) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) require.Equal(t, int64(3), s.State.Consumers["workflow:output"].GetOffset()) @@ -366,7 +382,9 @@ func TestDeregisterConsumerReleasesThePin(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 1, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 1, + }) require.NoError(t, err) s.DeregisterConsumer(nil, "workflow:output") @@ -399,7 +417,9 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 1} s.applyCap() @@ -412,7 +432,9 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} - _, err := s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + _, err := s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) s.DeregisterConsumer(nil, "workflow:output") @@ -430,14 +452,18 @@ func TestReregisteringBelowTheFloorIsRefused(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) require.NoError(t, err) - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 0, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, + }) require.NoError(t, err) s.DeregisterConsumer(nil, "workflow:output") require.NoError(t, s.Truncate(nil, 2)) // Resubscribing further along does not repair the gap. What this consumer // already recorded starts at 0, and offsets 0 and 1 are gone. - _, err = s.RegisterConsumer(nil, "workflow:output", "wf-1", "run-1", 3, false) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 3, + }) require.ErrorContains(t, err, "the stream now starts at 2") } @@ -486,16 +512,22 @@ func TestStreamConsumerTableIsBounded(t *testing.T) { s := newTestStream(t) for i := range MaxConsumersPerStream { - _, err := s.RegisterConsumer(nil, fmt.Sprintf("c%d", i), "wf", "run", 0, true) + _, err := s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: fmt.Sprintf("c%d", i), WorkflowID: "wf", RunID: "run", External: true, + }) require.NoError(t, err) } - _, err := s.RegisterConsumer(nil, "c-over", "wf", "run", 0, true) + _, err := s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "c-over", WorkflowID: "wf", RunID: "run", Offset: 0, External: true, + }) var invalid *serviceerror.InvalidArgument require.ErrorAs(t, err, &invalid) // Re-registering an existing consumer is an update, not a new entry. - _, err = s.RegisterConsumer(nil, "c0", "wf", "run", 0, true) + _, err = s.RegisterConsumer(nil, ConsumerRegistration{ + ConsumerID: "c0", WorkflowID: "wf", RunID: "run", Offset: 0, External: true, + }) require.NoError(t, err) } @@ -530,3 +562,31 @@ func TestAddMessagesTreatsAnUnsetKindAsData(t *testing.T) { require.NoError(t, err) require.True(t, retry.Deduplicated) } + +// A budgeted stream refuses the append that would not fit rather than dropping +// older messages: its batches are someone's mutable state, and the alternative +// to refusing is the execution size limit terminating that workflow later. +func TestBudgetRefusesAnAppendThatDoesNotFit(t *testing.T) { + s := newTestStream(t) + s.State.Budget = &streampb.StreamBudget{MaxItems: 3} + + _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + require.NoError(t, err) + + _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("c", "d")}) + var exhausted *serviceerror.ResourceExhausted + require.ErrorAs(t, err, &exhausted) + require.Equal(t, int64(2), s.State.HeadOffset, "a refused append writes nothing") + + // The last slot is still there for an append that fits. + _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("c")}) + require.NoError(t, err) + + bytesOnly := newTestStream(t) + bytesOnly.State.Budget = &streampb.StreamBudget{MaxBytes: 16} + _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Messages: msgs("small")}) + require.NoError(t, err) + _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Messages: msgs("another one")}) + require.ErrorAs(t, err, &exhausted) + require.Equal(t, int64(1), bytesOnly.State.HeadOffset) +} diff --git a/chasm/lib/workflow/fx.go b/chasm/lib/workflow/fx.go index dbcdedeceb1..8e69ef2f8b8 100644 --- a/chasm/lib/workflow/fx.go +++ b/chasm/lib/workflow/fx.go @@ -4,6 +4,7 @@ import ( "go.temporal.io/server/api/historyservice/v1" "go.temporal.io/server/chasm" "go.temporal.io/server/chasm/lib/nexusoperation" + "go.temporal.io/server/chasm/lib/stream" "go.uber.org/fx" ) @@ -12,17 +13,21 @@ var Module = fx.Module( fx.Provide(NewConfig), fx.Provide(NewRegistry), fx.Provide(newLibrary), + // Provided here rather than by the stream service module, because the + // command handlers need it in every service that runs this library. + fx.Provide(stream.NewConfig), fx.Invoke(func( chasmRegistry *chasm.Registry, library *library, config *nexusoperation.Config, + streamConfig *stream.Config, ) error { if err := library.registry.Register( newNexusLibrary(config, chasmRegistry.NexusEndpointProcessor), ); err != nil { return err } - if err := library.registry.Register(&streamLibrary{}); err != nil { + if err := library.registry.Register(newStreamLibrary(streamConfig)); err != nil { return err } return chasmRegistry.Register(library) diff --git a/chasm/lib/workflow/stream_commands.go b/chasm/lib/workflow/stream_commands.go index 8e4706bff7f..c06784bb0bf 100644 --- a/chasm/lib/workflow/stream_commands.go +++ b/chasm/lib/workflow/stream_commands.go @@ -50,6 +50,7 @@ func handleAddStreamMessagesCommand( validator Validator, command *commandpb.Command, opts CommandHandlerOptions, + limits stream.Limits, ) error { badAttributes := enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES attrs := command.GetAddStreamMessagesCommandAttributes() @@ -85,13 +86,14 @@ func handleAddStreamMessagesCommand( name = DefaultStreamName } - s, err := wf.streamNamed(chasmCtx, name) + s, err := wf.streamNamed(chasmCtx, name, limits) if err != nil { return StreamAdmissionFailure(badAttributes, err) } result, err := s.AddMessages(chasmCtx, stream.AddMessagesRequest{ Messages: toLibraryMessages(attrs.GetMessages()), + Limits: limits, }) if err != nil { return StreamAdmissionFailure(badAttributes, err) @@ -269,7 +271,11 @@ func (w *Workflow) RecordStreamMessagesAdded( // streamNamed returns the workflow's stream of that name, creating it on first // use. Implicit creation is deliberate: a workflow publishing to its own output // should not have to coordinate with anyone about who creates it. -func (w *Workflow) streamNamed(ctx chasm.MutableContext, name string) (*stream.Stream, error) { +func (w *Workflow) streamNamed( + ctx chasm.MutableContext, + name string, + limits stream.Limits, +) (*stream.Stream, error) { if w.Streams == nil { w.Streams = make(chasm.Map[string, *stream.Stream]) } @@ -286,12 +292,20 @@ func (w *Workflow) streamNamed(ctx chasm.MutableContext, name string) (*stream.S return nil, serviceerror.NewInvalidArgumentf( "stream name is %d characters, over the %d limit", len(name), stream.MaxStreamNameLength) } - if len(w.Streams) >= stream.MaxOwnedStreamsPerWorkflow { + if len(w.Streams) >= limits.MaxOwnedStreamsPerWorkflow { return nil, serviceerror.NewFailedPreconditionf( - "workflow already owns %d streams, the limit", stream.MaxOwnedStreamsPerWorkflow) + "workflow already owns %d streams, the limit", limits.MaxOwnedStreamsPerWorkflow) } - created, err := stream.NewStream(ctx, stream.NewStreamRequest{Attached: true}) + // Budgeted, because the batches live in this execution's mutable state and + // the size limit on that terminates the workflow instead of refusing. + created, err := stream.NewStream(ctx, stream.NewStreamRequest{ + Attached: true, + Budget: &streamlib.StreamBudget{ + MaxItems: int64(limits.OwnedStreamMaxItems), + MaxBytes: int64(limits.OwnedStreamMaxBytes), + }, + }) if err != nil { return nil, err } @@ -313,13 +327,28 @@ func toLibraryMessages(in []*streampb.StreamMessage) []*streamlib.StreamMessage return out } -// streamLibrary registers the stream command with the workflow registry. -type streamLibrary struct{} +// streamLibrary registers the stream commands with the workflow registry. +type streamLibrary struct { + config *stream.Config +} + +func newStreamLibrary(config *stream.Config) *streamLibrary { + return &streamLibrary{config: config} +} func (l *streamLibrary) CommandHandlers() map[enumspb.CommandType]CommandHandler { return map[enumspb.CommandType]CommandHandler{ - enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES: handleAddStreamMessagesCommand, - enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM: handleSubscribeStreamCommand, + enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES: func( + chasmCtx chasm.MutableContext, + wf *Workflow, + validator Validator, + command *commandpb.Command, + opts CommandHandlerOptions, + ) error { + limits := l.config.LimitsFor(chasmCtx.NamespaceEntry().Name().String()) + return handleAddStreamMessagesCommand(chasmCtx, wf, validator, command, opts, limits) + }, + enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM: handleSubscribeStreamCommand, } } diff --git a/chasm/lib/workflow/stream_cursor_test.go b/chasm/lib/workflow/stream_cursor_test.go index b61ee9a7d7c..9b5fc3ad1fa 100644 --- a/chasm/lib/workflow/stream_cursor_test.go +++ b/chasm/lib/workflow/stream_cursor_test.go @@ -63,7 +63,7 @@ func TestSubscribeRegistersTheConsumer(t *testing.T) { DefaultStreamName: chasm.NewComponentField(ctx, owned), } - start, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + start, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0, stream.DefaultLimits()) require.NoError(t, err) require.Equal(t, int64(0), start) @@ -86,7 +86,7 @@ func TestSubscribeFromTheTailResolvesToHead(t *testing.T) { DefaultStreamName: chasm.NewComponentField(ctx, owned), } - start, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, -1) + start, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, -1, stream.DefaultLimits()) require.NoError(t, err) require.Equal(t, int64(4), start, "a negative offset means from wherever the stream is now") } @@ -95,7 +95,7 @@ func TestSubscribeRejectsAStreamTheWorkflowDoesNotOwn(t *testing.T) { ctx := newStreamCursorTestContext() w := &Workflow{} - _, err := w.SubscribeToOwnedStream(ctx, "absent", 0) + _, err := w.SubscribeToOwnedStream(ctx, "absent", 0, stream.DefaultLimits()) require.ErrorContains(t, err, "does not own a stream") } @@ -109,7 +109,7 @@ func TestCommitStreamCursorsAdvancesTheConsumer(t *testing.T) { DefaultStreamName: chasm.NewComponentField(ctx, owned), } - _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0, stream.DefaultLimits()) require.NoError(t, err) cursor := w.StreamCursors[DefaultStreamName].Get(ctx) @@ -134,7 +134,7 @@ func TestCommitStreamCursorsWithAnEmptyRangeHoldsTheConsumer(t *testing.T) { DefaultStreamName: chasm.NewComponentField(ctx, owned), } - _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0, stream.DefaultLimits()) require.NoError(t, err) cursor := w.StreamCursors[DefaultStreamName].Get(ctx) @@ -181,8 +181,9 @@ func TestPublishStagesEachBatchAtItsOwnOffset(t *testing.T) { }, } - require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts)) - require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts)) + limits := stream.DefaultLimits() + require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts, limits)) + require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts, limits)) // Both publishes committed with the workflow task, so the batches are on // the component keyed by the offsets they start at. @@ -214,7 +215,7 @@ func TestConsumerOutrunByTruncationIsToldSo(t *testing.T) { DefaultStreamName: chasm.NewComponentField(ctx, owned), } - _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0) + _, err := w.SubscribeToOwnedStream(ctx, DefaultStreamName, 0, stream.DefaultLimits()) require.NoError(t, err) // Someone decides these messages are no longer needed, and only then can diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index 99d0347b1b9..aae62f51248 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -104,6 +104,7 @@ func (w *Workflow) SubscribeToOwnedStream( mctx chasm.MutableContext, name string, startOffset int64, + limits stream.Limits, ) (int64, error) { field, ok := w.Streams[name] if !ok { @@ -124,8 +125,13 @@ func (w *Workflow) SubscribeToOwnedStream( // separately it could be lost while the cursor survived, and truncation // would then be free to take a range the cursor still points at. key := mctx.ExecutionKey() - startOffset, err := owned.RegisterConsumer( - mctx, streamConsumerID(name), key.BusinessID, key.RunID, startOffset, false) + startOffset, err := owned.RegisterConsumer(mctx, stream.ConsumerRegistration{ + ConsumerID: streamConsumerID(name), + WorkflowID: key.BusinessID, + RunID: key.RunID, + Offset: startOffset, + MaxConsumers: limits.MaxConsumersPerStream, + }) if err != nil { return 0, err } @@ -640,7 +646,7 @@ func (w *Workflow) AppendToOwnedStream( name string, req stream.AddMessagesRequest, ) (stream.AddMessagesResult, error) { - s, err := w.streamNamed(mctx, name) + s, err := w.streamNamed(mctx, name, req.Limits) if err != nil { return stream.AddMessagesResult{}, err } diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go index 56a1684a9b0..44afaf12383 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_slices.go +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -141,6 +141,7 @@ func readDeliverable( name string, cursor *stream.Cursor, from, to int64, + limits stream.Limits, ) ([]*streampb.StreamMessage, int64, string, error) { if to <= from { return nil, from, "", nil @@ -155,13 +156,13 @@ func readDeliverable( // The collected run is contiguous from `from`, so the byte cap recomputes // the same end offset the read would have reported. collected, _, err := stream.CollectMessages( - w.Blobs, w.Starts, from, w.To, stream.MaxConsumeItemsPerTask, nil) + w.Blobs, w.Starts, from, w.To, limits.MaxConsumeItemsPerTask, nil) if err != nil { return nil, 0, "", err } // Cap before converting: the byte budget applies to the run as stored, and // trimming decides how far the recorded range reaches. - collected, readTo := stream.CapByBytes(collected, from, stream.MaxConsumeBytesPerTask) + collected, readTo := stream.CapByBytes(collected, from, limits.MaxConsumeBytesPerTask) return stream.ToAPIMessages(collected), readTo, w.RunID, nil } @@ -246,7 +247,8 @@ func deliverStreamSlices( // ranges in the order they were produced. slices.Sort(names) - maxItems := stream.MaxConsumeItemsPerTask + limits := shardContext.GetConfig().Stream.LimitsFor(ms.GetNamespaceEntry().Name().String()) + maxItems := limits.MaxConsumeItemsPerTask consumer := ms.GetWorkflowKey() slicesOut := make([]*streampb.StreamSlice, 0, len(names)) @@ -272,7 +274,7 @@ func deliverStreamSlices( } messages, next, ownerRunID, err := readDeliverable( - ctx, chasmCtx, wf, consumer.NamespaceID, name, cursor, from, to) + ctx, chasmCtx, wf, consumer.NamespaceID, name, cursor, from, to, limits) if err != nil { return nil, nil, err } diff --git a/service/history/api/respondworkflowtaskcompleted/api.go b/service/history/api/respondworkflowtaskcompleted/api.go index b7767f9b62e..46386b602d6 100644 --- a/service/history/api/respondworkflowtaskcompleted/api.go +++ b/service/history/api/respondworkflowtaskcompleted/api.go @@ -467,6 +467,7 @@ func (handler *WorkflowTaskCompletedHandler) Invoke( ctx, ms, ms.GetWorkflowKey().NamespaceID, + handler.config.Stream.LimitsFor(namespaceEntry.Name().String()), workflowTaskHandler.stagedStreamSubscriptions, ) if failWFTErr, ok := errors.AsType[chasmworkflow.FailWorkflowTaskError](err); ok { diff --git a/service/history/api/respondworkflowtaskcompleted/stream_appends.go b/service/history/api/respondworkflowtaskcompleted/stream_appends.go index d6611dc40db..44133d566ab 100644 --- a/service/history/api/respondworkflowtaskcompleted/stream_appends.go +++ b/service/history/api/respondworkflowtaskcompleted/stream_appends.go @@ -5,6 +5,7 @@ import ( enumspb "go.temporal.io/api/enums/v1" "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm/lib/stream" streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/service/history/api/recordworkflowtaskstarted" @@ -39,6 +40,7 @@ func resolveStagedStreamSubscriptions( ctx context.Context, ms historyi.MutableState, namespaceID string, + limits stream.Limits, staged []chasmworkflow.PendingStreamSubscription, ) error { if len(staged) == 0 { @@ -69,7 +71,7 @@ func resolveStagedStreamSubscriptions( // it is in this execution, and its cursor commits with everything else. if _, owned := wf.Streams[pending.StreamID]; owned { startOffset, err := wf.SubscribeToOwnedStream( - chasmCtx, pending.StreamID, pending.StartOffset) + chasmCtx, pending.StreamID, pending.StartOffset, limits) if err != nil { return chasmworkflow.StreamAdmissionFailure( enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES, err) diff --git a/service/history/configs/config.go b/service/history/configs/config.go index 2e197e0e43c..f802367292b 100644 --- a/service/history/configs/config.go +++ b/service/history/configs/config.go @@ -3,6 +3,7 @@ package configs import ( "go.temporal.io/server/chasm/lib/callback" "go.temporal.io/server/chasm/lib/nexusoperation" + "go.temporal.io/server/chasm/lib/stream" "go.temporal.io/server/common" "go.temporal.io/server/common/dynamicconfig" "go.temporal.io/server/common/health" @@ -253,6 +254,7 @@ type Config struct { HistoryCountLimitWarn dynamicconfig.IntPropertyFnWithNamespaceFilter HistoryCountSuggestContinueAsNew dynamicconfig.IntPropertyFnWithNamespaceFilter HistoryMaxPageSize dynamicconfig.IntPropertyFnWithNamespaceFilter + Stream *stream.Config MutableStateActivityFailureSizeLimitError dynamicconfig.IntPropertyFnWithNamespaceFilter MutableStateActivityFailureSizeLimitWarn dynamicconfig.IntPropertyFnWithNamespaceFilter MutableStateSizeLimitError dynamicconfig.IntPropertyFn @@ -711,6 +713,7 @@ func NewConfig( HistoryCountLimitWarn: dynamicconfig.HistoryCountLimitWarn.Get(dc), HistoryCountSuggestContinueAsNew: dynamicconfig.HistoryCountSuggestContinueAsNew.Get(dc), HistoryMaxPageSize: dynamicconfig.HistoryMaxPageSize.Get(dc), + Stream: stream.NewConfig(dc), MutableStateActivityFailureSizeLimitError: dynamicconfig.MutableStateActivityFailureSizeLimitError.Get(dc), MutableStateActivityFailureSizeLimitWarn: dynamicconfig.MutableStateActivityFailureSizeLimitWarn.Get(dc), MutableStateSizeLimitError: dynamicconfig.MutableStateSizeLimitError.Get(dc), diff --git a/tests/stream_workflow_test.go b/tests/stream_workflow_test.go index 54e976931f3..08c1b877857 100644 --- a/tests/stream_workflow_test.go +++ b/tests/stream_workflow_test.go @@ -15,9 +15,12 @@ import ( streampb "go.temporal.io/api/stream/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" "go.temporal.io/api/workflowservice/v1" + chasmstream "go.temporal.io/server/chasm/lib/stream" streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" "go.temporal.io/server/tests/testcore" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/durationpb" ) @@ -480,3 +483,58 @@ func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { require.Len(t, seen, producers*perProducer+1, "every outside message appears exactly once") require.Equal(t, workflowTasks, seen["from-workflow"]) } + +// A stream a workflow owns has a budget, because its batches are the workflow's +// own mutable state. Past it the workflow's publish fails its task with a cause +// and an outside producer is told the stream is full, instead of the execution +// size limit terminating the workflow later. +func TestOwnedStreamRefusesAppendsPastItsBudget(t *testing.T) { + env := testcore.NewEnv(t, testcore.WithDynamicConfig(chasmstream.OwnedStreamMaxItemsSetting, 3)) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-budget-") + + task := 0 + //nolint:staticcheck // SA1019: deprecated poller is the only one that can emit the command. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + task++ + if task == 1 { + return publishCommand("one", "two"), nil + } + return publishCommand("three", "four"), nil + }, + Logger: env.Logger, + T: t, + } + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + _, err = poller.PollAndProcessWorkflowTask() + require.Error(t, err, "two more do not fit in a budget of three") + failed := workflowTaskFailedWith(env.GetHistory(s.ns, execution), + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES) + require.NotNil(t, failed) + require.Contains(t, failed.GetFailure().GetMessage(), "budget") + + appendOutside := func(bodies ...string) error { + messages := make([]*streamlib.StreamMessage, len(bodies)) + for i, b := range bodies { + messages[i] = &streamlib.StreamMessage{Body: &commonpb.Payload{Data: []byte(b)}} + } + _, err := s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ + FrontendRequest: &streamlib.AddWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), Messages: messages, + }, + }) + return err + } + // The test client is a bare gRPC connection, so the code is what it sees. + require.Equal(t, codes.ResourceExhausted, status.Code(appendOutside("three", "four"))) + require.NoError(t, appendOutside("three"), "the last slot is still there for one that fits") + require.Equal(t, codes.ResourceExhausted, status.Code(appendOutside("four"))) +} From 774751d08f817a83b1df06c7e6a5290149fafccd Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:31:51 -0700 Subject: [PATCH 32/47] Keyed consumer pins by run and released them when the run closes. A pin keyed by workflow id alone outlived its run, refused a later run of the same id and kept the notify task pushing at closed workflows. The push now reports a closed or continued run, so the stream drops or re-keys the pin, and appends coalesce into one outstanding notify task. --- .../gen/streampb/v1/request_response.pb.go | 59 +++++- .../stream/gen/streampb/v1/stream_state.pb.go | 16 +- .../stream/proto/v1/request_response.proto | 16 +- chasm/lib/stream/proto/v1/stream_state.proto | 5 + chasm/lib/stream/service/handler.go | 125 +++++++++++-- chasm/lib/stream/service/tasks.go | 77 +++++--- chasm/lib/stream/stream.go | 36 +++- chasm/lib/stream/stream_test.go | 60 ++++++ .../stream_appends.go | 1 + tests/stream_consume_test.go | 16 ++ tests/stream_consumer_lifecycle_test.go | 174 ++++++++++++++++++ 11 files changed, 534 insertions(+), 51 deletions(-) create mode 100644 tests/stream_consumer_lifecycle_test.go diff --git a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go index 7222ea799b4..d8a52d2f05a 100644 --- a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go @@ -2151,8 +2151,11 @@ type RegisterStreamConsumerInput struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` - // The workflow that will consume, which names the pin. + // The workflow that will consume. Together with the run it names the pin, + // so a later run of the same workflow id registers fresh rather than + // inheriting a closed run's floor. ConsumerWorkflowId string `protobuf:"bytes,3,opt,name=consumer_workflow_id,json=consumerWorkflowId,proto3" json:"consumer_workflow_id,omitempty"` + ConsumerRunId string `protobuf:"bytes,5,opt,name=consumer_run_id,json=consumerRunId,proto3" json:"consumer_run_id,omitempty"` // Negative means from wherever the stream is when the pin is taken. Resolved // here, where the frontier is, and returned so the cursor records a fact. StartOffset int64 `protobuf:"varint,4,opt,name=start_offset,json=startOffset,proto3" json:"start_offset,omitempty"` @@ -2211,6 +2214,13 @@ func (x *RegisterStreamConsumerInput) GetConsumerWorkflowId() string { return "" } +func (x *RegisterStreamConsumerInput) GetConsumerRunId() string { + if x != nil { + return x.ConsumerRunId + } + return "" +} + func (x *RegisterStreamConsumerInput) GetStartOffset() int64 { if x != nil { return x.StartOffset @@ -2353,9 +2363,17 @@ func (x *AdvanceConsumerHeadInput) GetHeadOffset() int64 { } type AdvanceConsumerHeadOutput struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + // The run that held the pin is closed and no current run of the workflow + // consumes the stream, so the stream can release the pin. + ConsumerClosed bool `protobuf:"varint,1,opt,name=consumer_closed,json=consumerClosed,proto3" json:"consumer_closed,omitempty"` + // The run that held the pin is closed but a successor carries the + // subscription, so the stream re-keys the pin to it, with the floor the + // successor's cursor started from. + SuccessorRunId string `protobuf:"bytes,2,opt,name=successor_run_id,json=successorRunId,proto3" json:"successor_run_id,omitempty"` + SuccessorStartOffset int64 `protobuf:"varint,3,opt,name=successor_start_offset,json=successorStartOffset,proto3" json:"successor_start_offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AdvanceConsumerHeadOutput) Reset() { @@ -2388,6 +2406,27 @@ func (*AdvanceConsumerHeadOutput) Descriptor() ([]byte, []int) { return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{40} } +func (x *AdvanceConsumerHeadOutput) GetConsumerClosed() bool { + if x != nil { + return x.ConsumerClosed + } + return false +} + +func (x *AdvanceConsumerHeadOutput) GetSuccessorRunId() string { + if x != nil { + return x.SuccessorRunId + } + return "" +} + +func (x *AdvanceConsumerHeadOutput) GetSuccessorStartOffset() int64 { + if x != nil { + return x.SuccessorStartOffset + } + return 0 +} + type AddWorkflowMessagesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` @@ -3386,11 +3425,12 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12q\n" + "\x10frontend_request\x18\x02 \x01(\v2F.temporal.server.chasm.lib.stream.proto.v1.DescribeWorkflowStreamInputR\x0ffrontendRequest\"\x8e\x01\n" + "\x1eDescribeWorkflowStreamResponse\x12l\n" + - "\x11frontend_response\x18\x01 \x01(\v2?.temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutputR\x10frontendResponse\"\xad\x01\n" + + "\x11frontend_response\x18\x01 \x01(\v2?.temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutputR\x10frontendResponse\"\xd5\x01\n" + "\x1bRegisterStreamConsumerInput\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + "\tstream_id\x18\x02 \x01(\tR\bstreamId\x120\n" + - "\x14consumer_workflow_id\x18\x03 \x01(\tR\x12consumerWorkflowId\x12!\n" + + "\x14consumer_workflow_id\x18\x03 \x01(\tR\x12consumerWorkflowId\x12&\n" + + "\x0fconsumer_run_id\x18\x05 \x01(\tR\rconsumerRunId\x12!\n" + "\fstart_offset\x18\x04 \x01(\x03R\vstartOffset\"l\n" + "\x1cRegisterStreamConsumerOutput\x12!\n" + "\fstart_offset\x18\x01 \x01(\x03R\vstartOffset\x12\x1d\n" + @@ -3404,8 +3444,11 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "ownerRunId\x12\x1b\n" + "\tstream_id\x18\x03 \x01(\tR\bstreamId\x12\x1f\n" + "\vhead_offset\x18\x04 \x01(\x03R\n" + - "headOffset\"\x1b\n" + - "\x19AdvanceConsumerHeadOutput\"\xaf\x01\n" + + "headOffset\"\xa4\x01\n" + + "\x19AdvanceConsumerHeadOutput\x12'\n" + + "\x0fconsumer_closed\x18\x01 \x01(\bR\x0econsumerClosed\x12(\n" + + "\x10successor_run_id\x18\x02 \x01(\tR\x0esuccessorRunId\x124\n" + + "\x16successor_start_offset\x18\x03 \x01(\x03R\x14successorStartOffset\"\xaf\x01\n" + "\x1aAddWorkflowMessagesRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12n\n" + "\x10frontend_request\x18\x02 \x01(\v2C.temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesInputR\x0ffrontendRequest\"\x88\x01\n" + diff --git a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go index 682bbeea0ae..ac663fb790d 100644 --- a/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/stream_state.pb.go @@ -51,6 +51,10 @@ type StreamState struct { // Bytes appended over the stream's life, kept for the budget check. A stream // with a budget never reclaims, so this is also what it holds. AppendedBytes int64 `protobuf:"varint,15,opt,name=appended_bytes,json=appendedBytes,proto3" json:"appended_bytes,omitempty"` + // A notify task is scheduled and has not run yet. Appends while it is set + // schedule none of their own; the task reads the head when it runs, so it + // carries every append that landed before it. + NotifyPending bool `protobuf:"varint,16,opt,name=notify_pending,json=notifyPending,proto3" json:"notify_pending,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -162,6 +166,13 @@ func (x *StreamState) GetAppendedBytes() int64 { return 0 } +func (x *StreamState) GetNotifyPending() bool { + if x != nil { + return x.NotifyPending + } + return false +} + // Hard bounds on what a stream may hold. Distinct from StreamLifecycle.max_items, // which reclaims the oldest messages: a budget refuses the newest. type StreamBudget struct { @@ -569,7 +580,7 @@ var File_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto protorefle const file_temporal_server_chasm_lib_stream_proto_v1_stream_state_proto_rawDesc = "" + "\n" + - " Date: Fri, 18 Sep 2026 17:35:37 -0700 Subject: [PATCH 33/47] Made retention and deletion respect an active consumer. Retention now probes the consumers of a closed stream and re-arms while one is running, and DeleteStream refuses unless force is set. The truncation test also asserts the floor from the moment of subscribing, which is when it is taken. --- chasm/lib/stream/config.go | 10 +- .../gen/streampb/v1/request_response.pb.go | 22 +- .../stream/proto/v1/request_response.proto | 4 + chasm/lib/stream/service/handler.go | 20 ++ chasm/lib/stream/service/tasks.go | 258 +++++++++++------- tests/stream_replay_test.go | 4 +- tests/stream_retention_test.go | 146 +++++++++- 7 files changed, 344 insertions(+), 120 deletions(-) diff --git a/chasm/lib/stream/config.go b/chasm/lib/stream/config.go index 2dd9f47d4df..f3f598a9dc7 100644 --- a/chasm/lib/stream/config.go +++ b/chasm/lib/stream/config.go @@ -144,10 +144,17 @@ under limit.mutableStateSize.error, which would otherwise terminate the workflow OwnedStreamMaxItems, `Message budget of a stream a workflow owns. Appends past it are refused.`, ) + RetentionRecheckIntervalSetting = dynamicconfig.NewGlobalDurationSetting( + "stream.retentionRecheckInterval", + time.Minute, + `How long a closed stream past its retention waits before asking again whether the +consumers holding it are still running.`, + ) ) -// Config holds the namespace-scoped limits as live property functions. +// Config holds the settings as live property functions. type Config struct { + RetentionRecheckInterval dynamicconfig.DurationPropertyFn MaxConsumeItemsPerTask dynamicconfig.IntPropertyFnWithNamespaceFilter MaxConsumeBytesPerTask dynamicconfig.IntPropertyFnWithNamespaceFilter MaxProducersPerStream dynamicconfig.IntPropertyFnWithNamespaceFilter @@ -161,6 +168,7 @@ type Config struct { func NewConfig(dc *dynamicconfig.Collection) *Config { return &Config{ + RetentionRecheckInterval: RetentionRecheckIntervalSetting.Get(dc), MaxConsumeItemsPerTask: MaxConsumeItemsPerTaskSetting.Get(dc), MaxConsumeBytesPerTask: MaxConsumeBytesPerTaskSetting.Get(dc), MaxProducersPerStream: MaxProducersPerStreamSetting.Get(dc), diff --git a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go index d8a52d2f05a..3f20fe64c7b 100644 --- a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go @@ -1288,9 +1288,13 @@ func (*TruncateStreamOutput) Descriptor() ([]byte, []int) { } type DeleteStreamInput struct { - state protoimpl.MessageState `protogen:"open.v1"` - Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` - StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + // Delete even while a workflow consumer is active. Without it the call is + // refused, because the consumer's History depends on ranges the deletion + // takes with it. + Force bool `protobuf:"varint,3,opt,name=force,proto3" json:"force,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1339,6 +1343,13 @@ func (x *DeleteStreamInput) GetStreamId() string { return "" } +func (x *DeleteStreamInput) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + type DeleteStreamOutput struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -3381,10 +3392,11 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + "\tstream_id\x18\x02 \x01(\tR\bstreamId\x12&\n" + "\x0fnew_base_offset\x18\x03 \x01(\x03R\rnewBaseOffset\"\x16\n" + - "\x14TruncateStreamOutput\"N\n" + + "\x14TruncateStreamOutput\"d\n" + "\x11DeleteStreamInput\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + - "\tstream_id\x18\x02 \x01(\tR\bstreamId\"\x14\n" + + "\tstream_id\x18\x02 \x01(\tR\bstreamId\x12\x14\n" + + "\x05force\x18\x03 \x01(\bR\x05force\"\x14\n" + "\x12DeleteStreamOutput\"\xa1\x01\n" + "\x13CreateStreamRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12g\n" + diff --git a/chasm/lib/stream/proto/v1/request_response.proto b/chasm/lib/stream/proto/v1/request_response.proto index 40f0d8edb7a..03bfead3b87 100644 --- a/chasm/lib/stream/proto/v1/request_response.proto +++ b/chasm/lib/stream/proto/v1/request_response.proto @@ -189,6 +189,10 @@ message TruncateStreamOutput {} message DeleteStreamInput { string namespace = 1; string stream_id = 2; + // Delete even while a workflow consumer is active. Without it the call is + // refused, because the consumer's History depends on ranges the deletion + // takes with it. + bool force = 3; } message DeleteStreamOutput {} diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index dc036a87e98..833906ac787 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -857,6 +857,26 @@ func (h *handler) DeleteStream( in := req.GetFrontendRequest() key := chasm.ExecutionKey{NamespaceID: req.GetNamespaceId(), BusinessID: in.GetStreamId()} + // A workflow consuming the stream recorded ranges its replay will ask for. + // Deleting under it succeeds now and fails that workflow later, so the + // caller has to say it means it. Checked and then deleted rather than in + // one transition, which leaves a window for a subscription that lands in + // between; that consumer finds out at its next task, with a cause. + if !in.GetForce() { + state, err := chasm.ReadComponent(ctx, refFor(key.NamespaceID, key.BusinessID), + (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return nil, err + } + for id, consumer := range state.GetConsumers() { + if consumer.GetActive() { + return nil, serviceerror.NewFailedPreconditionf( + "stream %q is consumed by workflow %q (%s); set force to delete it anyway", + key.BusinessID, consumer.GetWorkflowId(), id) + } + } + } + // The payload is component state, so deleting the execution takes it too. if err := chasm.DeleteExecution[*stream.Stream](ctx, key, chasm.DeleteExecutionRequest{}); err != nil { return nil, err diff --git a/chasm/lib/stream/service/tasks.go b/chasm/lib/stream/service/tasks.go index 5c750cf9116..4b63bba9321 100644 --- a/chasm/lib/stream/service/tasks.go +++ b/chasm/lib/stream/service/tasks.go @@ -15,6 +15,120 @@ import ( "go.temporal.io/server/service/history/shard" ) +// consumerNotifier pushes a stream's frontier at its workflow consumers and +// keeps the consumer table honest about which of them still exist. +// +// A consumer lives wherever its own execution does, so telling it goes back +// out through the service to be routed rather than resolved on this shard. The +// answer also says when the run that subscribed is over: the pin is then +// dropped, or moved to the run that continued it. +type consumerNotifier struct { + logger log.Logger + routed streampb.StreamServiceClient +} + +// notify tells every active external consumer behind head that it moved. With +// probeAll it also tells the caught-up ones, which is how a closed stream +// learns whether anyone still holds it. Errors are collected rather than +// returned at the first, so one unreachable consumer does not stop the others +// being told, and returned at the end so the task retries. +func (n *consumerNotifier) notify( + ctx context.Context, + ref chasm.ComponentRef, + state *streampb.StreamState, + probeAll bool, +) error { + head := state.GetHeadOffset() + var notifyErrs []error + for consumerID, consumer := range state.GetConsumers() { + if !consumer.GetExternal() || !consumer.GetActive() { + continue + } + if !probeAll && consumer.GetOffset() >= head { + continue + } + + response, err := n.routed.AdvanceConsumerHead(ctx, &streampb.AdvanceConsumerHeadRequest{ + NamespaceId: ref.NamespaceID, + FrontendRequest: &streampb.AdvanceConsumerHeadInput{ + WorkflowId: consumer.GetWorkflowId(), + OwnerRunId: consumer.GetRunId(), + StreamId: ref.BusinessID, + HeadOffset: head, + }, + }) + var gone *serviceerror.NotFound + out := response.GetFrontendResponse() + switch { + case errors.As(err, &gone), err == nil && out.GetConsumerClosed(): + // The run that subscribed is over and nothing carries the + // subscription on. Its replay floor is holding storage for a + // recovery that can no longer be asked for, and this probe is the + // one place that finds out. + notifyErrs = append(notifyErrs, forgetConsumer(ctx, ref, consumerID)) + case err != nil: + n.logger.Error("failed to tell a stream consumer that the frontier moved", + tag.NewStringTag("stream-id", ref.BusinessID), + tag.NewStringTag("consumer-workflow-id", consumer.GetWorkflowId()), + tag.Error(err)) + notifyErrs = append(notifyErrs, err) + case out.GetSuccessorRunId() != "": + // A continue-as-new moved the subscription to a new run. The pin + // follows it, with the floor the successor's cursor began at; the + // predecessor's ranges are in a closed history that never replays. + notifyErrs = append(notifyErrs, rekeyConsumer(ctx, ref, consumer, out)) + default: + // Told, and still there. Nothing to clean up. + } + } + return errors.Join(notifyErrs...) +} + +func forgetConsumer(ctx context.Context, ref chasm.ComponentRef, consumerID string) error { + _, _, err := chasm.UpdateComponent(ctx, ref, + func(s *stream.Stream, mctx chasm.MutableContext, id string) (struct{}, error) { + s.ForgetConsumer(mctx, id) + return struct{}{}, nil + }, consumerID) + return err +} + +func rekeyConsumer( + ctx context.Context, + ref chasm.ComponentRef, + consumer *streampb.ConsumerCursor, + out *streampb.AdvanceConsumerHeadOutput, +) error { + registration := stream.ConsumerRegistration{ + ConsumerID: externalConsumerID(consumer.GetWorkflowId(), out.GetSuccessorRunId()), + WorkflowID: consumer.GetWorkflowId(), + RunID: out.GetSuccessorRunId(), + Offset: out.GetSuccessorStartOffset(), + External: true, + } + _, _, err := chasm.UpdateComponent(ctx, ref, + func(s *stream.Stream, mctx chasm.MutableContext, reg stream.ConsumerRegistration) (struct{}, error) { + // Registering the successor drops the predecessor's entry, since + // they share a workflow id and differ in run. + _, err := s.RegisterConsumer(mctx, reg) + return struct{}{}, err + }, registration) + return err +} + +// backgroundCallerContext tags a task's context with the namespace, since the +// task runs outside any request and nothing else has done it. +func backgroundCallerContext( + ctx context.Context, + registry namespace.Registry, + namespaceID string, +) context.Context { + if name, err := registry.GetNamespaceName(namespace.ID(namespaceID)); err == nil { + return headers.SetCallerInfo(ctx, headers.NewBackgroundLowCallerInfo(name.String())) + } + return ctx +} + // retentionTaskHandler deletes a stream once its retention has elapsed. Close // only seals; deletion is deliberately later, so a consumer can still drain a // finished stream without coordinating a shutdown with the producer. @@ -24,17 +138,23 @@ type retentionTaskHandler struct { shardController shard.Controller namespaceRegistry namespace.Registry logger log.Logger + config *stream.Config + notifier consumerNotifier } func newRetentionTaskHandler( shardController shard.Controller, namespaceRegistry namespace.Registry, logger log.Logger, + config *stream.Config, + routed streampb.StreamServiceClient, ) *retentionTaskHandler { return &retentionTaskHandler{ shardController: shardController, namespaceRegistry: namespaceRegistry, logger: logger, + config: config, + notifier: consumerNotifier{logger: logger, routed: routed}, } } @@ -49,18 +169,43 @@ func (h *retentionTaskHandler) Validate( return s.State.GetClosed(), nil } +// Execute deletes the stream unless a workflow still consumes it. +// +// A consumer's History depends on the ranges it consumed, and deleting them +// turns its next replay into a failure. So while any consumer is active the +// deletion waits and asks again later. A closed stream gets no appends, and +// appends are what otherwise make the stream discover that a consumer is +// gone, so the probe here is what lets a held stream ever be released. func (h *retentionTaskHandler) Execute( ctx context.Context, ref chasm.ComponentRef, _ chasm.TaskAttributes, _ *streampb.StreamRetentionTask, ) error { - namespaceID := ref.NamespaceID + ctx = backgroundCallerContext(ctx, h.namespaceRegistry, ref.NamespaceID) - // Runs outside a request, so nothing has tagged the context yet. Deletions - // still have to be attributed to the namespace they belong to. - if name, nsErr := h.namespaceRegistry.GetNamespaceName(namespace.ID(namespaceID)); nsErr == nil { - ctx = headers.SetCallerInfo(ctx, headers.NewBackgroundLowCallerInfo(name.String())) + state, err := chasm.ReadComponent(ctx, ref, (*stream.Stream).Snapshot, struct{}{}) + if err != nil { + return err + } + if err := h.notifier.notify(ctx, ref, state, true); err != nil { + return err + } + + held, _, err := chasm.UpdateComponent(ctx, ref, + func(s *stream.Stream, mctx chasm.MutableContext, _ struct{}) (bool, error) { + for _, consumer := range s.State.GetConsumers() { + if consumer.GetActive() { + mctx.AddTask(s, chasm.TaskAttributes{ + ScheduledTime: mctx.Now(s).Add(h.config.RetentionRecheckInterval()), + }, &streampb.StreamRetentionTask{}) + return true, nil + } + } + return false, nil + }, struct{}{}) + if err != nil || held { + return err } // The payload is component state, so deleting the execution takes it too. @@ -90,12 +235,7 @@ type notifyConsumersTaskHandler struct { chasm.SideEffectTaskHandlerBase[*streampb.StreamNotifyConsumersTask] namespaceRegistry namespace.Registry - logger log.Logger - - // This task runs on the stream's shard. Its consumers live wherever their - // own executions do, so telling them goes back out through the service to - // be routed rather than resolved here. - routed streampb.StreamServiceClient + notifier consumerNotifier } func newNotifyConsumersTaskHandler( @@ -105,8 +245,7 @@ func newNotifyConsumersTaskHandler( ) *notifyConsumersTaskHandler { return ¬ifyConsumersTaskHandler{ namespaceRegistry: namespaceRegistry, - logger: logger, - routed: routed, + notifier: consumerNotifier{logger: logger, routed: routed}, } } @@ -130,12 +269,7 @@ func (h *notifyConsumersTaskHandler) Execute( _ chasm.TaskAttributes, _ *streampb.StreamNotifyConsumersTask, ) error { - namespaceID := ref.NamespaceID - streamID := ref.BusinessID - - if name, nsErr := h.namespaceRegistry.GetNamespaceName(namespace.ID(namespaceID)); nsErr == nil { - ctx = headers.SetCallerInfo(ctx, headers.NewBackgroundLowCallerInfo(name.String())) - } + ctx = backgroundCallerContext(ctx, h.namespaceRegistry, ref.NamespaceID) // A write rather than a read, because it also lowers the coalescing flag. // Appends that commit after this transition schedule their own task. @@ -143,91 +277,7 @@ func (h *notifyConsumersTaskHandler) Execute( if err != nil { return err } - head := state.GetHeadOffset() - - // Collected rather than returned at the first failure, so one unreachable - // consumer does not stop the others being told in this pass, and returned - // at the end so the task retries rather than dropping the notification. - // - // Dropping it is not survivable for a consumer on another host: its known - // head would never advance, and delivery clips to it. - var notifyErrs []error - - for consumerID, consumer := range state.GetConsumers() { - if !consumer.GetExternal() || !consumer.GetActive() || consumer.GetOffset() >= head { - continue - } - - response, err := h.routed.AdvanceConsumerHead(ctx, &streampb.AdvanceConsumerHeadRequest{ - NamespaceId: namespaceID, - FrontendRequest: &streampb.AdvanceConsumerHeadInput{ - WorkflowId: consumer.GetWorkflowId(), - OwnerRunId: consumer.GetRunId(), - StreamId: streamID, - HeadOffset: head, - }, - }) - var gone *serviceerror.NotFound - out := response.GetFrontendResponse() - switch { - case errors.As(err, &gone), err == nil && out.GetConsumerClosed(): - // The run that subscribed is over and nothing carries the - // subscription on. Its replay floor is holding storage for a - // recovery that can no longer be asked for, and this probe is the - // one place that finds out. - notifyErrs = append(notifyErrs, h.forget(ctx, ref, consumerID)) - case err != nil: - h.logger.Error("failed to tell a stream consumer that the frontier moved", - tag.NewStringTag("stream-id", streamID), - tag.NewStringTag("consumer-workflow-id", consumer.GetWorkflowId()), - tag.Error(err)) - notifyErrs = append(notifyErrs, err) - case out.GetSuccessorRunId() != "": - // A continue-as-new moved the subscription to a new run. The pin - // follows it, with the floor the successor's cursor began at; the - // predecessor's ranges are in a closed history that never replays. - notifyErrs = append(notifyErrs, h.rekey(ctx, ref, consumer, out)) - default: - // Told, and still there. Nothing to clean up. - } - } - return errors.Join(notifyErrs...) -} - -func (h *notifyConsumersTaskHandler) forget( - ctx context.Context, - ref chasm.ComponentRef, - consumerID string, -) error { - _, _, err := chasm.UpdateComponent(ctx, ref, - func(s *stream.Stream, mctx chasm.MutableContext, id string) (struct{}, error) { - s.ForgetConsumer(mctx, id) - return struct{}{}, nil - }, consumerID) - return err -} - -func (h *notifyConsumersTaskHandler) rekey( - ctx context.Context, - ref chasm.ComponentRef, - consumer *streampb.ConsumerCursor, - out *streampb.AdvanceConsumerHeadOutput, -) error { - registration := stream.ConsumerRegistration{ - ConsumerID: externalConsumerID(consumer.GetWorkflowId(), out.GetSuccessorRunId()), - WorkflowID: consumer.GetWorkflowId(), - RunID: out.GetSuccessorRunId(), - Offset: out.GetSuccessorStartOffset(), - External: true, - } - _, _, err := chasm.UpdateComponent(ctx, ref, - func(s *stream.Stream, mctx chasm.MutableContext, reg stream.ConsumerRegistration) (struct{}, error) { - // Registering the successor drops the predecessor's entry, since - // they share a workflow id and differ in run. - _, err := s.RegisterConsumer(mctx, reg) - return struct{}{}, err - }, registration) - return err + return h.notifier.notify(ctx, ref, state, false) } func (h *notifyConsumersTaskHandler) Discard( diff --git a/tests/stream_replay_test.go b/tests/stream_replay_test.go index e22d9a45bf6..ba14e219629 100644 --- a/tests/stream_replay_test.go +++ b/tests/stream_replay_test.go @@ -191,7 +191,9 @@ func workflowTaskFailedWith( func deleteStreamAndWait(t *testing.T, s *streamTestEnv, streamID string) { t.Helper() _, err := s.client.DeleteStream(s.ctx(), &streamlib.DeleteStreamRequest{ - FrontendRequest: &streamlib.DeleteStreamInput{Namespace: s.ns, StreamId: streamID}, + FrontendRequest: &streamlib.DeleteStreamInput{ + Namespace: s.ns, StreamId: streamID, Force: true, + }, }) require.NoError(t, err) await.RequireTrue(t, func() bool { diff --git a/tests/stream_retention_test.go b/tests/stream_retention_test.go index d9d91ae604d..c25a92f4da7 100644 --- a/tests/stream_retention_test.go +++ b/tests/stream_retention_test.go @@ -11,8 +11,12 @@ import ( enumspb "go.temporal.io/api/enums/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" "go.temporal.io/api/workflowservice/v1" + chasmstream "go.temporal.io/server/chasm/lib/stream" streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/testing/await" "go.temporal.io/server/tests/testcore" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/durationpb" ) @@ -74,17 +78,23 @@ func TestTruncationRefusesToDropWhatAConsumerNeedsToReplay(t *testing.T) { }) require.NoError(t, err) - // Delivered and recorded, which is what makes these three messages part of - // the workflow's recovery rather than spare capacity. + // The floor is where the subscription started. It holds from the moment of + // subscribing, before anything has been delivered, because the first range + // the workflow will record begins there. + truncate := func() error { + _, err := s.client.TruncateStream(s.ctx(), &streamlib.TruncateStreamRequest{ + FrontendRequest: &streamlib.TruncateStreamInput{ + Namespace: s.ns, StreamId: streamID, NewBaseOffset: 2, + }, + }) + return err + } + require.ErrorContains(t, truncate(), "still depends on offset 0") + + // Delivering and recording the range does not move the floor either. _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - - _, err = s.client.TruncateStream(s.ctx(), &streamlib.TruncateStreamRequest{ - FrontendRequest: &streamlib.TruncateStreamInput{ - Namespace: s.ns, StreamId: streamID, NewBaseOffset: 2, - }, - }) - require.ErrorContains(t, err, "still depends on offset 0") + require.ErrorContains(t, truncate(), "still depends on offset 0") desc, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, @@ -131,3 +141,121 @@ func TestTruncationStillWorksWithNoConsumer(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(2), desc.GetFrontendResponse().GetState().GetBaseOffset()) } + +// A closed stream past its retention is still held by a consumer that is +// running: its History depends on the ranges it consumed. Deletion waits and +// asks again, and goes through once the consumer is gone. +func TestRetentionWaitsForAnActiveConsumer(t *testing.T) { + env := testcore.NewEnv(t, + testcore.WithDynamicConfig(chasmstream.RetentionRecheckIntervalSetting, time.Second), + ) + s := newStreamTestEnvFrom(t, env) + + streamID := "retained-until-read-" + uuid.NewString() + _, err := s.client.CreateStream(s.ctx(), &streamlib.CreateStreamRequest{ + FrontendRequest: &streamlib.CreateStreamInput{ + Namespace: s.ns, StreamId: streamID, + Lifecycle: &streamlib.StreamLifecycle{Retention: durationpb.New(time.Second)}, + }, + }) + require.NoError(t, err) + + execution, tq := startConsumer(t, s, "stream-wf-retained-") + task := 0 + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + task++ + if task == 2 { + return completeWorkflowCommand(), nil + } + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + + _, err = s.client.CloseStream(s.ctx(), &streamlib.CloseStreamRequest{ + FrontendRequest: &streamlib.CloseStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.NoError(t, err) + + // Well past retention, and the stream is still there because the consumer + // is still running. + time.Sleep(3 * time.Second) + state := describeStream(t, s, streamID) + require.True(t, state.GetClosed()) + require.Len(t, state.GetConsumers(), 1) + + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + _, err = poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + + // The next recheck finds the consumer gone and lets retention through. + await.RequireTrue(t, func() bool { + _, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + return err != nil + }, 20*time.Second, 200*time.Millisecond) +} + +// Deleting a stream a running workflow consumes succeeds now and fails that +// workflow at its next replay, so the caller has to say it means it. +func TestDeleteStreamRefusesWhileAConsumerIsActive(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "guarded-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + execution, tq := startConsumer(t, s, "stream-wf-guarded-") + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + return nil, nil + }, + Logger: env.Logger, + T: t, + } + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + + _, err = s.client.DeleteStream(s.ctx(), &streamlib.DeleteStreamRequest{ + FrontendRequest: &streamlib.DeleteStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + require.Equal(t, codes.FailedPrecondition, status.Code(err)) + require.ErrorContains(t, err, execution.GetWorkflowId(), "the refusal names the consumer") + + _, err = s.client.DeleteStream(s.ctx(), &streamlib.DeleteStreamRequest{ + FrontendRequest: &streamlib.DeleteStreamInput{Namespace: s.ns, StreamId: streamID, Force: true}, + }) + require.NoError(t, err) + await.RequireTrue(t, func() bool { + _, err := s.client.DescribeStream(s.ctx(), &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: streamID}, + }) + return err != nil + }, 20*time.Second, 100*time.Millisecond) +} From 98320c4bd5c601b81002b13f18d79e3eb31038d8 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:38:12 -0700 Subject: [PATCH 34/47] Stopped a filtered stream read from skipping unexamined offsets. The page-level fallback moved the reader to the end of a window whose batches did not reach it. The message loop already advances past every filtered message it examined, which is all the advance a filtered page is owed. --- chasm/lib/stream/service/handler.go | 18 +++----- chasm/lib/stream/service/handler_test.go | 54 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 chasm/lib/stream/service/handler_test.go diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index 833906ac787..c9cf91fce29 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -586,12 +586,14 @@ func (h *handler) PollWorkflowMessages( return &streampb.PollWorkflowMessagesResponse{FrontendResponse: out}, nil } -// readWindow serves a reader's window out of a frontier the caller resolved. -// Standalone and attached streams differ only in where that frontier comes -// from, so nothing past it is aware of the difference. // formatWindow turns a component read into the wire response. The read happens // in the component, so the frontier and the bytes it was served with cannot // disagree. +// +// The reader is advanced only over offsets that were examined. CollectMessages +// steps past every message it filtered out, so a filtered page that matched +// nothing still moves the reader; a window whose batches stop short of its end +// must not be reported as read to the end. func formatWindow(w stream.Window, req stream.WindowRequest) (*streampb.PollMessagesOutput, error) { out := &streampb.PollMessagesOutput{ NextOffset: req.From, @@ -604,17 +606,11 @@ func formatWindow(w stream.Window, req stream.WindowRequest) (*streampb.PollMess return out, nil } - messages, next, err := stream.CollectMessages(w.Blobs, w.Starts, req.From, w.To, w.Limit, req.Topics) + messages, next, err := stream.CollectMessages( + w.Blobs, w.Starts, req.From, w.To, w.Limit, req.Topics) if err != nil { return nil, err } - if next < w.To && len(messages) == 0 && len(req.Topics) > 0 { - // A page that filtered everything out still has to advance, or the - // caller loops forever on the same offsets. Limited to a filtered read - // on purpose: for any other reason a page comes back short, moving the - // reader past offsets it was never given would hide the short read. - next = w.To - } out.Messages = messages out.NextOffset = next return out, nil diff --git a/chasm/lib/stream/service/handler_test.go b/chasm/lib/stream/service/handler_test.go new file mode 100644 index 00000000000..2d5789004cd --- /dev/null +++ b/chasm/lib/stream/service/handler_test.go @@ -0,0 +1,54 @@ +package service + +import ( + "testing" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + "go.temporal.io/server/chasm/lib/stream" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "google.golang.org/protobuf/proto" +) + +func batchBlob(t *testing.T, topic string, bodies ...string) *commonpb.DataBlob { + t.Helper() + messages := make([]*streampb.StreamMessage, len(bodies)) + for i, b := range bodies { + messages[i] = &streampb.StreamMessage{ + Body: &commonpb.Payload{Data: []byte(b)}, + Topic: topic, + Kind: streampb.STREAM_MESSAGE_KIND_DATA, + } + } + data, err := proto.Marshal(&streampb.StreamMessageBatch{Messages: messages}) + require.NoError(t, err) + return &commonpb.DataBlob{EncodingType: enumspb.ENCODING_TYPE_PROTO3, Data: data} +} + +// A filtered page that matched nothing still advances the reader, but only +// over the offsets that were actually examined. A window whose batches stop +// short of its end must not report the end as the next offset, or the reader +// steps over messages it was never shown. +func TestFormatWindowAdvancesOnlyOverExaminedOffsets(t *testing.T) { + w := stream.Window{ + State: &streampb.StreamState{HeadOffset: 10}, + Blobs: []*commonpb.DataBlob{batchBlob(t, "a", "m0", "m1", "m2")}, + Starts: []int64{0}, + To: 5, + Limit: 5, + } + out, err := formatWindow(w, stream.WindowRequest{From: 0, Topics: []string{"nothing"}}) + require.NoError(t, err) + require.Empty(t, out.GetMessages()) + require.Equal(t, int64(3), out.GetNextOffset(), + "offsets 3 and 4 were never read, so the reader must not be moved past them") + + // A page that filtered everything out but covered its whole window does + // advance to the end, so the reader does not loop on the same offsets. + w.Blobs = []*commonpb.DataBlob{batchBlob(t, "a", "m0", "m1", "m2", "m3", "m4")} + out, err = formatWindow(w, stream.WindowRequest{From: 0, Topics: []string{"nothing"}}) + require.NoError(t, err) + require.Empty(t, out.GetMessages()) + require.Equal(t, int64(5), out.GetNextOffset()) +} From b6c8b0c1af3cab6661a28a87305fe0d7d06263d9 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:40:01 -0700 Subject: [PATCH 35/47] Validated stream ids, names, offsets and page sizes on the frontend. A stream id becomes an execution's business id and a stream name a key in mutable state, so both are bounded like workflow ids before the request is routed. --- chasm/lib/stream/config.go | 4 + chasm/lib/stream/service/frontend.go | 154 ++++++++++++++++++---- chasm/lib/stream/service/frontend_test.go | 95 +++++++++++++ 3 files changed, 227 insertions(+), 26 deletions(-) create mode 100644 chasm/lib/stream/service/frontend_test.go diff --git a/chasm/lib/stream/config.go b/chasm/lib/stream/config.go index f3f598a9dc7..823a70568a9 100644 --- a/chasm/lib/stream/config.go +++ b/chasm/lib/stream/config.go @@ -154,6 +154,9 @@ consumers holding it are still running.`, // Config holds the settings as live property functions. type Config struct { + // The id length limit shared with workflow ids. A stream id becomes an + // execution's business id, and a stream name a key in mutable state. + MaxIDLength dynamicconfig.IntPropertyFn RetentionRecheckInterval dynamicconfig.DurationPropertyFn MaxConsumeItemsPerTask dynamicconfig.IntPropertyFnWithNamespaceFilter MaxConsumeBytesPerTask dynamicconfig.IntPropertyFnWithNamespaceFilter @@ -168,6 +171,7 @@ type Config struct { func NewConfig(dc *dynamicconfig.Collection) *Config { return &Config{ + MaxIDLength: dynamicconfig.MaxIDLengthLimit.Get(dc), RetentionRecheckInterval: RetentionRecheckIntervalSetting.Get(dc), MaxConsumeItemsPerTask: MaxConsumeItemsPerTaskSetting.Get(dc), MaxConsumeBytesPerTask: MaxConsumeBytesPerTaskSetting.Get(dc), diff --git a/chasm/lib/stream/service/frontend.go b/chasm/lib/stream/service/frontend.go index 34d6e18e644..b7c929bd102 100644 --- a/chasm/lib/stream/service/frontend.go +++ b/chasm/lib/stream/service/frontend.go @@ -13,25 +13,35 @@ import ( ) // FrontendHandler serves StreamService on the frontend. It resolves the -// namespace name to an ID and forwards to the history shard that owns the -// stream; the layered client does the routing from the business ID. +// namespace name to an ID, checks what can be checked without the stream, and +// forwards to the history shard that owns the stream; the layered client does +// the routing from the business ID. +// +// The checks are the ones the workflow handler makes for its own ids. A stream +// id becomes an execution's business id and a stream name a key in mutable +// state, so neither may be longer than an id is allowed to be, and an offset +// or page size that history would only clamp or refuse later is refused here +// before the request is routed. type FrontendHandler struct { streampb.UnimplementedStreamServiceServer client streampb.StreamServiceClient namespaceRegistry namespace.Registry logger log.Logger + config *stream.Config } func NewFrontendHandler( client streampb.StreamServiceClient, namespaceRegistry namespace.Registry, logger log.Logger, + config *stream.Config, ) *FrontendHandler { return &FrontendHandler{ client: client, namespaceRegistry: namespaceRegistry, logger: logger, + config: config, } } @@ -46,147 +56,239 @@ func (h *FrontendHandler) namespaceID(name string) (string, error) { return id.String(), nil } +// checkID refuses an id or name longer than the namespace's id limit. Empty is +// allowed here: some fields mean the default when empty and the ones that do +// not are checked by their handler. +func (h *FrontendHandler) checkID(field, value string) error { + if len(value) > h.config.MaxIDLength() { + return serviceerror.NewInvalidArgumentf( + "%s is %d characters, over the %d limit", field, len(value), h.config.MaxIDLength()) + } + return nil +} + +func checkOffset(field string, value int64) error { + if value < 0 { + return serviceerror.NewInvalidArgumentf("%s cannot be negative, got %d", field, value) + } + return nil +} + +// clampMaxMessages bounds a page the way the history side does, so a caller +// asking for more sees the same page size it would have been given anyway. +func clampMaxMessages(requested int32) int32 { + if requested <= 0 || requested > stream.DefaultMaxMessagesPerPoll { + return stream.DefaultMaxMessagesPerPoll + } + return requested +} + func (h *FrontendHandler) CreateStream( ctx context.Context, req *streampb.CreateStreamRequest, ) (*streampb.CreateStreamResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if in.GetStreamId() == "" { + return nil, serviceerror.NewInvalidArgument("stream id is required") + } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } return h.client.CreateStream(ctx, &streampb.CreateStreamRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) AddMessages( ctx context.Context, req *streampb.AddMessagesRequest, ) (*streampb.AddMessagesResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } return h.client.AddMessages(ctx, &streampb.AddMessagesRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) FinishWriting( ctx context.Context, req *streampb.FinishWritingRequest, ) (*streampb.FinishWritingResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } return h.client.FinishWriting(ctx, &streampb.FinishWritingRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) SubscribeWorkflow( ctx context.Context, req *streampb.SubscribeWorkflowRequest, ) (*streampb.SubscribeWorkflowResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } + if err := h.checkID("stream name", in.GetStreamName()); err != nil { + return nil, err + } return h.client.SubscribeWorkflow(ctx, &streampb.SubscribeWorkflowRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) PollMessages( ctx context.Context, req *streampb.PollMessagesRequest, ) (*streampb.PollMessagesResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } + if err := checkOffset("from offset", in.GetFromOffset()); err != nil { + return nil, err + } + in.MaxMessages = clampMaxMessages(in.GetMaxMessages()) return h.client.PollMessages(ctx, &streampb.PollMessagesRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) PollWorkflowMessages( ctx context.Context, req *streampb.PollWorkflowMessagesRequest, ) (*streampb.PollWorkflowMessagesResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream name", in.GetStreamName()); err != nil { + return nil, err + } + if err := checkOffset("from offset", in.GetFromOffset()); err != nil { + return nil, err + } + in.MaxMessages = clampMaxMessages(in.GetMaxMessages()) return h.client.PollWorkflowMessages(ctx, &streampb.PollWorkflowMessagesRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) DescribeWorkflowStream( ctx context.Context, req *streampb.DescribeWorkflowStreamRequest, ) (*streampb.DescribeWorkflowStreamResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream name", in.GetStreamName()); err != nil { + return nil, err + } return h.client.DescribeWorkflowStream(ctx, &streampb.DescribeWorkflowStreamRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) AddWorkflowMessages( ctx context.Context, req *streampb.AddWorkflowMessagesRequest, ) (*streampb.AddWorkflowMessagesResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream name", in.GetStreamName()); err != nil { + return nil, err + } return h.client.AddWorkflowMessages(ctx, &streampb.AddWorkflowMessagesRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) DescribeStream( ctx context.Context, req *streampb.DescribeStreamRequest, ) (*streampb.DescribeStreamResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } return h.client.DescribeStream(ctx, &streampb.DescribeStreamRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) CloseStream( ctx context.Context, req *streampb.CloseStreamRequest, ) (*streampb.CloseStreamResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } return h.client.CloseStream(ctx, &streampb.CloseStreamRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) TruncateStream( ctx context.Context, req *streampb.TruncateStreamRequest, ) (*streampb.TruncateStreamResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } + if err := checkOffset("new base offset", in.GetNewBaseOffset()); err != nil { + return nil, err + } return h.client.TruncateStream(ctx, &streampb.TruncateStreamRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } func (h *FrontendHandler) DeleteStream( ctx context.Context, req *streampb.DeleteStreamRequest, ) (*streampb.DeleteStreamResponse, error) { - id, err := h.namespaceID(req.GetFrontendRequest().GetNamespace()) + in := req.GetFrontendRequest() + id, err := h.namespaceID(in.GetNamespace()) if err != nil { return nil, err } + if err := h.checkID("stream id", in.GetStreamId()); err != nil { + return nil, err + } return h.client.DeleteStream(ctx, &streampb.DeleteStreamRequest{ - NamespaceId: id, FrontendRequest: req.GetFrontendRequest(), + NamespaceId: id, FrontendRequest: in, }) } diff --git a/chasm/lib/stream/service/frontend_test.go b/chasm/lib/stream/service/frontend_test.go new file mode 100644 index 00000000000..b861176fc80 --- /dev/null +++ b/chasm/lib/stream/service/frontend_test.go @@ -0,0 +1,95 @@ +package service + +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "go.temporal.io/api/serviceerror" + "go.temporal.io/server/chasm/lib/stream" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/dynamicconfig" + "go.temporal.io/server/common/log" + "go.temporal.io/server/common/namespace" + "go.uber.org/mock/gomock" + "google.golang.org/grpc" +) + +// recordingClient captures what the frontend forwards, so a test can check +// what history would have been asked without a history service. +type recordingClient struct { + streampb.StreamServiceClient + polls []*streampb.PollMessagesRequest + truncates []*streampb.TruncateStreamRequest +} + +func (c *recordingClient) PollMessages( + _ context.Context, req *streampb.PollMessagesRequest, _ ...grpc.CallOption, +) (*streampb.PollMessagesResponse, error) { + c.polls = append(c.polls, req) + return &streampb.PollMessagesResponse{}, nil +} + +func (c *recordingClient) TruncateStream( + _ context.Context, req *streampb.TruncateStreamRequest, _ ...grpc.CallOption, +) (*streampb.TruncateStreamResponse, error) { + c.truncates = append(c.truncates, req) + return &streampb.TruncateStreamResponse{}, nil +} + +func newTestFrontend(t *testing.T, maxIDLength int) (*FrontendHandler, *recordingClient) { + t.Helper() + registry := namespace.NewMockRegistry(gomock.NewController(t)) + registry.EXPECT().GetNamespaceID(namespace.Name("ns")).Return(namespace.ID("ns-id"), nil).AnyTimes() + client := &recordingClient{} + config := &stream.Config{MaxIDLength: dynamicconfig.GetIntPropertyFn(maxIDLength)} + return NewFrontendHandler(client, registry, log.NewNoopLogger(), config), client +} + +// The checks the workflow handler makes for its own ids, made here for stream +// ids and names before a request is routed. +func TestFrontendRefusesWhatHistoryWouldOnlyDiscoverLater(t *testing.T) { + h, client := newTestFrontend(t, 16) + ctx := context.Background() + var invalid *serviceerror.InvalidArgument + + _, err := h.CreateStream(ctx, &streampb.CreateStreamRequest{ + FrontendRequest: &streampb.CreateStreamInput{Namespace: "ns", StreamId: strings.Repeat("x", 17)}, + }) + require.ErrorAs(t, err, &invalid, "a stream id becomes a business id and is bounded like one") + + _, err = h.PollWorkflowMessages(ctx, &streampb.PollWorkflowMessagesRequest{ + FrontendRequest: &streampb.PollWorkflowMessagesInput{ + Namespace: "ns", WorkflowId: "wf", StreamName: strings.Repeat("n", 17), + }, + }) + require.ErrorAs(t, err, &invalid, "a stream name becomes a state key and is bounded the same way") + + _, err = h.PollMessages(ctx, &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{Namespace: "ns", StreamId: "s", FromOffset: -1}, + }) + require.ErrorAs(t, err, &invalid) + + _, err = h.TruncateStream(ctx, &streampb.TruncateStreamRequest{ + FrontendRequest: &streampb.TruncateStreamInput{Namespace: "ns", StreamId: "s", NewBaseOffset: -5}, + }) + require.ErrorAs(t, err, &invalid) + require.Empty(t, client.polls) + require.Empty(t, client.truncates, "a refused request is never routed") +} + +// A page larger than the server serves is clamped before routing, so the +// caller sees the same page it would have been given anyway. +func TestFrontendClampsThePageSize(t *testing.T) { + h, client := newTestFrontend(t, 1000) + _, err := h.PollMessages(context.Background(), &streampb.PollMessagesRequest{ + FrontendRequest: &streampb.PollMessagesInput{ + Namespace: "ns", StreamId: "s", MaxMessages: stream.DefaultMaxMessagesPerPoll * 4, + }, + }) + require.NoError(t, err) + require.Len(t, client.polls, 1) + require.Equal(t, "ns-id", client.polls[0].GetNamespaceId()) + require.EqualValues(t, stream.DefaultMaxMessagesPerPoll, client.polls[0].GetFrontendRequest().GetMaxMessages()) +} From fb3567dc180553a8fd58e070e157cb4c4c6be946 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:45:54 -0700 Subject: [PATCH 36/47] Wrapped long lines, tagged every handler and rewrote stale comments. Hand-written lines now fit in 100 characters, the stream handlers all tag their context with the caller, the comments describe the current behaviour without history, and the SDK validation host is opt-in like the other tooling. --- chasm/lib/stream/messages.go | 6 +- chasm/lib/stream/proto/v1/service.proto | 6 +- chasm/lib/stream/service/frontend.go | 15 +- chasm/lib/stream/service/handler.go | 33 +- chasm/lib/stream/service/library.go | 6 +- chasm/lib/stream/service/tasks.go | 4 +- chasm/lib/stream/stream.go | 10 +- chasm/lib/stream/stream_test.go | 6 +- chasm/lib/workflow/stream_commands.go | 32 +- .../stream_routing_test.go | 67 +++- .../stream_slices.go | 3 +- service/history/handler.go | 2 +- service/matching/wire_compat_test.go | 13 +- tests/api_fork_test.go | 6 +- tests/sdk_server_host_test.go | 27 +- tests/stream_consume_test.go | 331 +++++++++++------- tests/stream_consumer_lifecycle_test.go | 36 +- tests/stream_publish_cost_test.go | 84 +++-- tests/stream_rejection_test.go | 12 +- tests/stream_replay_test.go | 51 ++- tests/stream_retention_test.go | 35 +- tests/stream_routing_cross_host_test.go | 51 +-- tests/stream_test.go | 13 +- tests/stream_workflow_test.go | 133 +++---- tests/streaming_baseline_test.go | 30 +- tests/streaming_native_test.go | 3 +- 26 files changed, 634 insertions(+), 381 deletions(-) diff --git a/chasm/lib/stream/messages.go b/chasm/lib/stream/messages.go index efb29e1aab8..30ea7b49e5a 100644 --- a/chasm/lib/stream/messages.go +++ b/chasm/lib/stream/messages.go @@ -78,9 +78,9 @@ func ToAPIMessages(in []*streamlib.StreamMessage) []*streampb.StreamMessage { // the offset just past the last one kept. // // It always keeps the first message, however large. Dropping it would leave the -// cursor unable to advance, and since an unconsumed range now schedules a -// workflow task, a stream holding one oversized message would wake the workflow -// forever without ever delivering anything. +// cursor unable to advance, and since an unconsumed range schedules a workflow +// task, a stream holding one oversized message would wake the workflow forever +// without ever delivering anything. func CapByBytes( messages []*streamlib.StreamMessage, from int64, diff --git a/chasm/lib/stream/proto/v1/service.proto b/chasm/lib/stream/proto/v1/service.proto index bdd0a918551..1d358a53caa 100644 --- a/chasm/lib/stream/proto/v1/service.proto +++ b/chasm/lib/stream/proto/v1/service.proto @@ -45,7 +45,8 @@ service StreamService { option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_LONG_POLL; } - rpc DescribeWorkflowStream(DescribeWorkflowStreamRequest) returns (DescribeWorkflowStreamResponse) { + rpc DescribeWorkflowStream(DescribeWorkflowStreamRequest) + returns (DescribeWorkflowStreamResponse) { option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.workflow_id"; option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; } @@ -58,7 +59,8 @@ service StreamService { // Internal. History calls these on itself to reach a shard it does not own, // which is the only way a step that spans two executions can work on a // cluster with more than one history host. - rpc RegisterStreamConsumer(RegisterStreamConsumerRequest) returns (RegisterStreamConsumerResponse) { + rpc RegisterStreamConsumer(RegisterStreamConsumerRequest) + returns (RegisterStreamConsumerResponse) { option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; } diff --git a/chasm/lib/stream/service/frontend.go b/chasm/lib/stream/service/frontend.go index b7c929bd102..843f1e8bf4d 100644 --- a/chasm/lib/stream/service/frontend.go +++ b/chasm/lib/stream/service/frontend.go @@ -307,12 +307,15 @@ func (h *FrontendHandler) ListStreams( pageSize = stream.MaxListPageSize } - resp, err := chasm.ListExecutions[*stream.Stream, *emptypb.Empty](ctx, &chasm.ListExecutionsRequest{ - NamespaceName: in.GetNamespace(), - PageSize: pageSize, - NextPageToken: in.GetNextPageToken(), - Query: in.GetQuery(), - }) + resp, err := chasm.ListExecutions[*stream.Stream, *emptypb.Empty]( + ctx, + &chasm.ListExecutionsRequest{ + NamespaceName: in.GetNamespace(), + PageSize: pageSize, + NextPageToken: in.GetNextPageToken(), + Query: in.GetQuery(), + }, + ) if err != nil { return nil, err } diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index c9cf91fce29..a788e608bf9 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -120,6 +120,7 @@ func (h *handler) CreateStream( req *streampb.CreateStreamRequest, ) (*streampb.CreateStreamResponse, error) { in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) if in.GetStreamId() == "" { return nil, serviceerror.NewInvalidArgument("stream id is required") } @@ -237,6 +238,7 @@ func (h *handler) FinishWriting( req *streampb.FinishWritingRequest, ) (*streampb.FinishWritingResponse, error) { in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) _, _, err := chasm.UpdateComponent( ctx, refFor(req.GetNamespaceId(), in.GetStreamId()), @@ -488,7 +490,8 @@ func (h *handler) AdvanceConsumerHead( out.ConsumerClosed = true return &streampb.AdvanceConsumerHeadResponse{FrontendResponse: out}, nil } - if err := h.pushHead(ctx, namespaceID, workflowID, pinned.runID, streamID, in.GetHeadOffset()); err != nil { + err := h.pushHead(ctx, namespaceID, workflowID, pinned.runID, streamID, in.GetHeadOffset()) + if err != nil { return nil, err } return &streampb.AdvanceConsumerHeadResponse{FrontendResponse: out}, nil @@ -504,7 +507,8 @@ func (h *handler) AdvanceConsumerHead( out.ConsumerClosed = true return &streampb.AdvanceConsumerHeadResponse{FrontendResponse: out}, nil } - if err := h.pushHead(ctx, namespaceID, workflowID, current.runID, streamID, in.GetHeadOffset()); err != nil { + err = h.pushHead(ctx, namespaceID, workflowID, current.runID, streamID, in.GetHeadOffset()) + if err != nil { return nil, err } out.SuccessorRunId = current.runID @@ -575,7 +579,8 @@ func (h *handler) PollWorkflowMessages( } wreq := stream.WindowRequest{From: from, MaxMessages: in.GetMaxMessages(), Topics: in.GetTopics()} - w, err := chasm.ReadComponent(ctx, ref, readOwnedWindow, ownedWindowRequest{Name: name, Window: wreq}) + w, err := chasm.ReadComponent(ctx, ref, readOwnedWindow, + ownedWindowRequest{Name: name, Window: wreq}) if err != nil { return nil, err } @@ -636,7 +641,10 @@ func readOwnedWindow( if s == nil { // Nothing published yet, which reads as an empty stream so a reader can // attach before the first append. - return stream.Window{State: &streampb.StreamState{Closed: !cctx.ExecutionInfo().CloseTime.IsZero()}, To: req.Window.From}, nil + return stream.Window{ + State: &streampb.StreamState{Closed: !cctx.ExecutionInfo().CloseTime.IsZero()}, + To: req.Window.From, + }, nil } w, err := s.ReadWindow(cctx, req.Window) if err != nil { @@ -697,7 +705,8 @@ func (h *handler) waitForMessages( from int64, current *streampb.StreamState, ) (*streampb.StreamState, error) { - pollCtx, cancel := contextutil.WithDeadlineBuffer(ctx, stream.LongPollTimeout, stream.LongPollBuffer) + pollCtx, cancel := contextutil.WithDeadlineBuffer( + ctx, stream.LongPollTimeout, stream.LongPollBuffer) defer cancel() state, _, err := chasm.PollComponent(pollCtx, ref, @@ -722,11 +731,14 @@ func (h *handler) waitForOwnedMessages( from int64, current *streampb.StreamState, ) (*streampb.StreamState, error) { - pollCtx, cancel := contextutil.WithDeadlineBuffer(ctx, stream.LongPollTimeout, stream.LongPollBuffer) + pollCtx, cancel := contextutil.WithDeadlineBuffer( + ctx, stream.LongPollTimeout, stream.LongPollBuffer) defer cancel() state, _, err := chasm.PollComponent(pollCtx, ref, - func(wf *chasmworkflow.Workflow, cctx chasm.Context, offset int64) (*streampb.StreamState, bool, error) { + func( + wf *chasmworkflow.Workflow, cctx chasm.Context, offset int64, + ) (*streampb.StreamState, bool, error) { owned, err := readOwnedStream(wf, cctx, name) if err != nil { return nil, false, err @@ -774,6 +786,7 @@ func (h *handler) DescribeStream( req *streampb.DescribeStreamRequest, ) (*streampb.DescribeStreamResponse, error) { in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) state, err := chasm.ReadComponent(ctx, refFor(req.GetNamespaceId(), in.GetStreamId()), (*stream.Stream).Snapshot, struct{}{}) if err != nil { @@ -810,6 +823,7 @@ func (h *handler) CloseStream( req *streampb.CloseStreamRequest, ) (*streampb.CloseStreamResponse, error) { in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) _, _, err := chasm.UpdateComponent( ctx, refFor(req.GetNamespaceId(), in.GetStreamId()), @@ -829,6 +843,7 @@ func (h *handler) TruncateStream( req *streampb.TruncateStreamRequest, ) (*streampb.TruncateStreamResponse, error) { in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) if _, _, err := chasm.UpdateComponent( ctx, @@ -851,6 +866,7 @@ func (h *handler) DeleteStream( req *streampb.DeleteStreamRequest, ) (*streampb.DeleteStreamResponse, error) { in := req.GetFrontendRequest() + ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) key := chasm.ExecutionKey{NamespaceID: req.GetNamespaceId(), BusinessID: in.GetStreamId()} // A workflow consuming the stream recorded ranges its replay will ask for. @@ -874,7 +890,8 @@ func (h *handler) DeleteStream( } // The payload is component state, so deleting the execution takes it too. - if err := chasm.DeleteExecution[*stream.Stream](ctx, key, chasm.DeleteExecutionRequest{}); err != nil { + err := chasm.DeleteExecution[*stream.Stream](ctx, key, chasm.DeleteExecutionRequest{}) + if err != nil { return nil, err } return &streampb.DeleteStreamResponse{FrontendResponse: &streampb.DeleteStreamOutput{}}, nil diff --git a/chasm/lib/stream/service/library.go b/chasm/lib/stream/service/library.go index ffc6bb9a220..8abf549cbbe 100644 --- a/chasm/lib/stream/service/library.go +++ b/chasm/lib/stream/service/library.go @@ -24,7 +24,11 @@ type library struct { notifyConsumers *notifyConsumersTaskHandler } -func newLibrary(h *handler, retention *retentionTaskHandler, notifyConsumers *notifyConsumersTaskHandler) *library { +func newLibrary( + h *handler, + retention *retentionTaskHandler, + notifyConsumers *notifyConsumersTaskHandler, +) *library { return &library{handler: h, retention: retention, notifyConsumers: notifyConsumers} } diff --git a/chasm/lib/stream/service/tasks.go b/chasm/lib/stream/service/tasks.go index 4b63bba9321..3471da034b2 100644 --- a/chasm/lib/stream/service/tasks.go +++ b/chasm/lib/stream/service/tasks.go @@ -107,7 +107,9 @@ func rekeyConsumer( External: true, } _, _, err := chasm.UpdateComponent(ctx, ref, - func(s *stream.Stream, mctx chasm.MutableContext, reg stream.ConsumerRegistration) (struct{}, error) { + func( + s *stream.Stream, mctx chasm.MutableContext, reg stream.ConsumerRegistration, + ) (struct{}, error) { // Registering the successor drops the predecessor's entry, since // they share a workflow id and differ in run. _, err := s.RegisterConsumer(mctx, reg) diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index 30f98ecfcc1..fd48247c81a 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -251,7 +251,8 @@ func (s *Stream) notifyConsumers(mctx chasm.MutableContext) { for _, consumer := range s.State.Consumers { if consumer.GetExternal() && consumer.GetActive() && consumer.GetOffset() < s.State.HeadOffset { s.State.NotifyPending = true - mctx.AddTask(s, chasm.TaskAttributes{ScheduledTime: mctx.Now(s)}, &streampb.StreamNotifyConsumersTask{}) + mctx.AddTask(s, chasm.TaskAttributes{ScheduledTime: mctx.Now(s)}, + &streampb.StreamNotifyConsumersTask{}) return } } @@ -261,7 +262,9 @@ func (s *Stream) notifyConsumers(mctx chasm.MutableContext) { // pending flag in the same transition that reads the head is what makes the // coalescing safe: any append that commits after this one sees the flag down // and schedules its own task. -func (s *Stream) TakeNotifySnapshot(_ chasm.MutableContext, _ struct{}) (*streampb.StreamState, error) { +func (s *Stream) TakeNotifySnapshot( + _ chasm.MutableContext, _ struct{}, +) (*streampb.StreamState, error) { s.State.NotifyPending = false return common.CloneProto(s.State), nil } @@ -747,7 +750,8 @@ func marshalBatch(messages []*streampb.StreamMessage) (*commonpb.DataBlob, error // protobuf map iteration order is not stable. A record carrying payload or // message metadata would otherwise hash differently on a retry and be // refused as a conflicting duplicate of itself. - data, err := (proto.MarshalOptions{Deterministic: true}).Marshal(&streampb.StreamMessageBatch{Messages: messages}) + data, err := (proto.MarshalOptions{Deterministic: true}).Marshal( + &streampb.StreamMessageBatch{Messages: messages}) if err != nil { return nil, err } diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go index 1fa4ae35dc4..180ecd87565 100644 --- a/chasm/lib/stream/stream_test.go +++ b/chasm/lib/stream/stream_test.go @@ -616,7 +616,8 @@ func TestRegisterConsumerReplacesAnEntryFromAnotherRun(t *testing.T) { // A different workflow id is not the same consumer and keeps its pin. _, err = s.RegisterConsumer(nil, ConsumerRegistration{ - ConsumerID: "workflow:other/run-9", WorkflowID: "other", RunID: "run-9", Offset: 3, External: true, + ConsumerID: "workflow:other/run-9", WorkflowID: "other", RunID: "run-9", + Offset: 3, External: true, }) require.NoError(t, err) require.Len(t, s.State.Consumers, 2) @@ -645,7 +646,8 @@ func TestNotifyCoalescesIntoOneOutstandingTask(t *testing.T) { // The task's read lowers the flag, so the next append owes a new task. state, err := s.TakeNotifySnapshot(mctx, struct{}{}) require.NoError(t, err) - require.Equal(t, int64(2), state.GetHeadOffset(), "the task sees every append that landed before it") + require.Equal(t, int64(2), state.GetHeadOffset(), + "the task sees every append that landed before it") _, err = s.AddMessages(mctx, AddMessagesRequest{Messages: msgs("c")}) require.NoError(t, err) require.Len(t, mctx.Tasks, 2) diff --git a/chasm/lib/workflow/stream_commands.go b/chasm/lib/workflow/stream_commands.go index c06784bb0bf..a6194a96dac 100644 --- a/chasm/lib/workflow/stream_commands.go +++ b/chasm/lib/workflow/stream_commands.go @@ -198,12 +198,14 @@ func (w *Workflow) ReserveStreamSubscribedEvent( streamID string, workflowTaskCompletedEventID int64, ) *historypb.HistoryEvent { - return w.AddHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, func(e *historypb.HistoryEvent) { + eventType := enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED + return w.AddHistoryEvent(eventType, func(e *historypb.HistoryEvent) { + attrs := &historypb.WorkflowStreamSubscribedEventAttributes{ + WorkflowTaskCompletedEventId: workflowTaskCompletedEventID, + StreamId: streamID, + } e.Attributes = &historypb.HistoryEvent_WorkflowStreamSubscribedEventAttributes{ - WorkflowStreamSubscribedEventAttributes: &historypb.WorkflowStreamSubscribedEventAttributes{ - WorkflowTaskCompletedEventId: workflowTaskCompletedEventID, - StreamId: streamID, - }, + WorkflowStreamSubscribedEventAttributes: attrs, } }) } @@ -234,7 +236,9 @@ func (streamMessagesAddedEvent) IsWorkflowTaskTrigger() bool { return false } // The frontier it describes is CHASM state, persisted and rebuilt with the // execution, so there is nothing here to reconstruct. -func (streamMessagesAddedEvent) Apply(chasm.MutableContext, *Workflow, *historypb.HistoryEvent) error { +func (streamMessagesAddedEvent) Apply( + chasm.MutableContext, *Workflow, *historypb.HistoryEvent, +) error { return nil } @@ -256,14 +260,16 @@ func (w *Workflow) RecordStreamMessagesAdded( count int64, workflowTaskCompletedEventID int64, ) { - w.AddHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED, func(e *historypb.HistoryEvent) { + eventType := enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED + w.AddHistoryEvent(eventType, func(e *historypb.HistoryEvent) { + attrs := &historypb.WorkflowStreamMessagesAddedEventAttributes{ + WorkflowTaskCompletedEventId: workflowTaskCompletedEventID, + StreamId: streamID, + FirstOffset: firstOffset, + MessageCount: count, + } e.Attributes = &historypb.HistoryEvent_WorkflowStreamMessagesAddedEventAttributes{ - WorkflowStreamMessagesAddedEventAttributes: &historypb.WorkflowStreamMessagesAddedEventAttributes{ - WorkflowTaskCompletedEventId: workflowTaskCompletedEventID, - StreamId: streamID, - FirstOffset: firstOffset, - MessageCount: count, - }, + WorkflowStreamMessagesAddedEventAttributes: attrs, } }) } diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing_test.go b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go index a4150151c25..d9111b65cdb 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_routing_test.go +++ b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go @@ -32,7 +32,9 @@ func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { for _, replay := range []bool{false, true} { t.Run(map[bool]string{false: "live", true: "replay"}[replay], func(t *testing.T) { calls := 0 - client := &routedStreamClient{poll: func(ctx context.Context, req *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + client := &routedStreamClient{poll: func( + ctx context.Context, req *streamlib.PollMessagesRequest, + ) (*streamlib.PollMessagesResponse, error) { calls++ require.Equal(t, "namespace-id", req.GetNamespaceId()) require.Equal(t, "remote-source", req.GetFrontendRequest().GetStreamId()) @@ -43,13 +45,21 @@ func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { return &streamlib.PollMessagesResponse{FrontendResponse: &streamlib.PollMessagesOutput{ NextOffset: 6, HeadOffset: 9, Messages: []*streamlib.StreamMessage{ - {Offset: 4, Kind: streamlib.STREAM_MESSAGE_KIND_DATA, Body: &commonpb.Payload{Data: []byte("a")}}, - {Offset: 5, Kind: streamlib.STREAM_MESSAGE_KIND_DATA, Body: &commonpb.Payload{Data: []byte("b")}}, + { + Offset: 4, + Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + Body: &commonpb.Payload{Data: []byte("a")}, + }, + { + Offset: 5, + Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + Body: &commonpb.Payload{Data: []byte("b")}, + }, }, }}, nil }} - // No local engine is installed. Reverting to the old local-controller - // path cannot accidentally satisfy this test's remote source. + // No local engine is installed, so a read that resolved the source + // through the local controller instead of the routed client fails. ctx := WithStreamClient(context.Background(), client) var window stream.Window var err error @@ -62,7 +72,8 @@ func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { } require.NoError(t, err) require.Equal(t, 1, calls) - messages, next, err := stream.CollectMessages(window.Blobs, window.Starts, 4, window.To, window.Limit, nil) + messages, next, err := stream.CollectMessages( + window.Blobs, window.Starts, 4, window.To, window.Limit, nil) require.NoError(t, err) require.EqualValues(t, 6, next) require.Len(t, messages, 2) @@ -74,18 +85,33 @@ func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { func TestExternalStreamRoutedReadRejectsCorruptOrExpandedRange(t *testing.T) { cases := map[string]*streamlib.PollMessagesOutput{ - "missing-response": nil, - "skipped-offset": {NextOffset: 6, HeadOffset: 9, Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 6}}}, - "short-count": {NextOffset: 6, HeadOffset: 9, Messages: []*streamlib.StreamMessage{{Offset: 4}}}, - "past-requested-frontier": {NextOffset: 7, HeadOffset: 9, Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}, {Offset: 6}}}, - "past-committed-head": {NextOffset: 6, HeadOffset: 5, Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}}}, + "missing-response": nil, + "skipped-offset": { + NextOffset: 6, HeadOffset: 9, + Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 6}}, + }, + "short-count": { + NextOffset: 6, HeadOffset: 9, + Messages: []*streamlib.StreamMessage{{Offset: 4}}, + }, + "past-requested-frontier": { + NextOffset: 7, HeadOffset: 9, + Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}, {Offset: 6}}, + }, + "past-committed-head": { + NextOffset: 6, HeadOffset: 5, + Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}}, + }, } for name, response := range cases { t.Run(name, func(t *testing.T) { - client := &routedStreamClient{poll: func(context.Context, *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + client := &routedStreamClient{poll: func( + context.Context, *streamlib.PollMessagesRequest, + ) (*streamlib.PollMessagesResponse, error) { return &streamlib.PollMessagesResponse{FrontendResponse: response}, nil }} - _, err := readExternalWindow(WithStreamClient(context.Background(), client), "ns", "source", 4, 6) + ctx := WithStreamClient(context.Background(), client) + _, err := readExternalWindow(ctx, "ns", "source", 4, 6) var integrity *serviceerror.DataLoss require.ErrorAs(t, err, &integrity) }) @@ -94,7 +120,9 @@ func TestExternalStreamRoutedReadRejectsCorruptOrExpandedRange(t *testing.T) { func TestExternalStreamRoutedReadPropagatesSourceFailure(t *testing.T) { want := errors.New("source shard unavailable") - client := &routedStreamClient{poll: func(context.Context, *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + client := &routedStreamClient{poll: func( + context.Context, *streamlib.PollMessagesRequest, + ) (*streamlib.PollMessagesResponse, error) { return nil, want }} _, err := readExternalWindow(WithStreamClient(context.Background(), client), "ns", "source", 4, 6) @@ -102,7 +130,9 @@ func TestExternalStreamRoutedReadPropagatesSourceFailure(t *testing.T) { } func TestOwnedLiveStreamNeverReentersThroughRoutedClient(t *testing.T) { - client := &routedStreamClient{poll: func(context.Context, *streamlib.PollMessagesRequest) (*streamlib.PollMessagesResponse, error) { + client := &routedStreamClient{poll: func( + context.Context, *streamlib.PollMessagesRequest, + ) (*streamlib.PollMessagesResponse, error) { t.Fatal("owned stream read re-entered the consumer through an RPC") return nil, nil }} @@ -115,7 +145,12 @@ func TestOwnedLiveStreamNeverReentersThroughRoutedClient(t *testing.T) { func TestOwnedReplayPinsConsumerRun(t *testing.T) { engine := chasm.NewMockEngine(gomock.NewController(t)) engine.EXPECT().ReadComponent(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(_ context.Context, ref chasm.ComponentRef, _ func(chasm.Context, chasm.Component) error, _ ...chasm.TransitionOption) error { + func( + _ context.Context, + ref chasm.ComponentRef, + _ func(chasm.Context, chasm.Component) error, + _ ...chasm.TransitionOption, + ) error { require.Equal(t, "consumer-run", ref.RunID) require.Equal(t, "consumer", ref.BusinessID) return nil diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go index 44afaf12383..544461e51dc 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_slices.go +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -603,7 +603,8 @@ func eventsOfResponse( raw := resp.GetRawHistoryBytes() if len(raw) == 0 { - raw = resp.GetRawHistory() //nolint:staticcheck // SA1019: still populated while the newer field rolls out. + //nolint:staticcheck // SA1019: still populated while the newer field rolls out. + raw = resp.GetRawHistory() } blobs := make([]*commonpb.DataBlob, 0, len(raw)) for _, batch := range raw { diff --git a/service/history/handler.go b/service/history/handler.go index d5012a959e5..c4aa4ea9f3b 100644 --- a/service/history/handler.go +++ b/service/history/handler.go @@ -2616,7 +2616,7 @@ func (h *Handler) StartNexusOperation( payload.Metadata = make(map[string][]byte, 1) } - // For now, we require all responess from the System Nexus Endpoint be protobufs. + // Responses from the System Nexus Endpoint have to be protobufs. encoding := string(payload.Metadata["encoding"]) if encoding != "binary/protobuf" { return nil, serviceerror.NewFailedPreconditionf("system payload must be encoded as binary/protobuf but got %s", encoding) diff --git a/service/matching/wire_compat_test.go b/service/matching/wire_compat_test.go index 56fcfa06524..f55255343e4 100644 --- a/service/matching/wire_compat_test.go +++ b/service/matching/wire_compat_test.go @@ -99,7 +99,8 @@ func wireCompatDiff( if aField.MapKey().Kind() != bField.MapKey().Kind() { report("field %d (%s) has a different map key type on each side", number, aField.Name()) } - if problem := valueTypeDiff(aField.MapValue(), bField.MapValue(), number, aField.Name()); problem != "" { + problem := valueTypeDiff(aField.MapValue(), bField.MapValue(), number, aField.Name()) + if problem != "" { report("%s", problem) } continue @@ -117,7 +118,8 @@ func wireCompatDiff( for _, number := range sortedFieldNumbers(bFields) { if _, ok := aFields[number]; !ok { - report("field %d (%s) is on %s but missing from %s", number, bFields[number].Name(), bName, aName) + report("field %d (%s) is on %s but missing from %s", + number, bFields[number].Name(), bName, aName) } } @@ -131,7 +133,8 @@ func valueTypeDiff( name protoreflect.Name, ) string { if a.Kind() != b.Kind() { - return fmt.Sprintf("field %d (%s) is %s on one side and %s on the other", number, name, a.Kind(), b.Kind()) + return fmt.Sprintf("field %d (%s) is %s on one side and %s on the other", + number, name, a.Kind(), b.Kind()) } switch a.Kind() { @@ -163,7 +166,9 @@ func fieldsByNumber(m proto.Message) map[protoreflect.FieldNumber]protoreflect.F // Field order drives the order of reported problems, which keeps a failure // message stable between runs. -func sortedFieldNumbers(fields map[protoreflect.FieldNumber]protoreflect.FieldDescriptor) []protoreflect.FieldNumber { +func sortedFieldNumbers( + fields map[protoreflect.FieldNumber]protoreflect.FieldDescriptor, +) []protoreflect.FieldNumber { numbers := make([]protoreflect.FieldNumber, 0, len(fields)) for number := range fields { numbers = append(numbers, number) diff --git a/tests/api_fork_test.go b/tests/api_fork_test.go index 6428272bf1b..35ac851eb63 100644 --- a/tests/api_fork_test.go +++ b/tests/api_fork_test.go @@ -32,7 +32,8 @@ func TestApiForkCarriesStreamShapes(t *testing.T) { var back commandpb.Command require.NoError(t, proto.Unmarshal(b, &back)) require.Equal(t, "s1", back.GetAddStreamMessagesCommandAttributes().GetStreamId()) - require.Equal(t, "tokens", back.GetAddStreamMessagesCommandAttributes().GetMessages()[0].GetTopic()) + require.Equal(t, "tokens", + back.GetAddStreamMessagesCommandAttributes().GetMessages()[0].GetTopic()) resp := &workflowservice.PollWorkflowTaskQueueResponse{ StreamSlices: []*streampb.StreamSlice{{StreamId: "s1", FromOffset: 4, ToOffset: 7}}, @@ -53,5 +54,6 @@ func TestApiForkCarriesStreamShapes(t *testing.T) { // An empty range has to survive the round trip: it is the fact that a // subscription observed nothing, which replay must reproduce. require.Len(t, aback.GetStreamCursors(), 1) - require.Equal(t, aback.GetStreamCursors()[0].GetFromOffset(), aback.GetStreamCursors()[0].GetToOffset()) + require.Equal(t, + aback.GetStreamCursors()[0].GetFromOffset(), aback.GetStreamCursors()[0].GetToOffset()) } diff --git a/tests/sdk_server_host_test.go b/tests/sdk_server_host_test.go index 1bfb2291c05..2278f350960 100644 --- a/tests/sdk_server_host_test.go +++ b/tests/sdk_server_host_test.go @@ -14,7 +14,15 @@ import ( "go.temporal.io/server/tests/testcore" ) +// TestSDKValidationServerHost is not a test. It keeps a dedicated cluster up so +// an SDK's own test suite can run against a server built from this tree, and +// tears it down when the caller writes the shutdown file. Opt-in like the +// other measurement tooling; nothing in a normal run should start a cluster +// and hold it for hours. func TestSDKValidationServerHost(t *testing.T) { + if os.Getenv("TEMPORAL_STREAM_BENCH") != "1" { + t.Skip("set TEMPORAL_STREAM_BENCH=1 to run") + } readyPath := os.Getenv("AI198_SDK_SERVER_READY_FILE") stopPath := os.Getenv("AI198_SDK_SERVER_STOP_FILE") if readyPath == "" || stopPath == "" { @@ -25,15 +33,21 @@ func TestSDKValidationServerHost(t *testing.T) { env := testcore.NewEnv(t, testcore.WithDedicatedCluster()) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - _, err = env.RegisterNamespace(ctx, namespace.Name("default"), 1, enumspb.ARCHIVAL_STATE_DISABLED, "", "") + _, err = env.RegisterNamespace( + ctx, namespace.Name("default"), 1, enumspb.ARCHIVAL_STATE_DISABLED, "", "") require.NoError(t, err) config := env.GetTestClusterConfig() metadata := map[string]any{ "status": "running", "pid": os.Getpid(), "target": env.FrontendGRPCAddress(), - "namespace": "default", "persistence": "SQLite", "history_hosts": config.HistoryConfig.NumHistoryHosts, - "history_shards": config.HistoryConfig.NumHistoryShards, "started_utc": time.Now().UTC().Format(time.RFC3339), - "shutdown_file": stopPath, "source_provenance": "../server-source-provenance.json", - "configuration": "tests/testcore dedicated test-cluster defaults; namespace default added for SDK clients", + "namespace": "default", + "persistence": "SQLite", + "history_hosts": config.HistoryConfig.NumHistoryHosts, + "history_shards": config.HistoryConfig.NumHistoryShards, + "started_utc": time.Now().UTC().Format(time.RFC3339), + "shutdown_file": stopPath, + "source_provenance": "../server-source-provenance.json", + "configuration": "tests/testcore dedicated test-cluster defaults; " + + "namespace default added for SDK clients", } writeMetadata := func() { data, marshalErr := json.MarshalIndent(metadata, "", " ") @@ -41,7 +55,8 @@ func TestSDKValidationServerHost(t *testing.T) { require.NoError(t, os.WriteFile(readyPath, append(data, '\n'), 0o644)) } writeMetadata() - t.Logf("SDK validation server ready at %s in namespace default; shutdown file %s", env.FrontendGRPCAddress(), stopPath) + t.Logf("SDK validation server ready at %s in namespace default; shutdown file %s", + env.FrontendGRPCAddress(), stopPath) ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() deadline := time.NewTimer(2 * time.Hour) diff --git a/tests/stream_consume_test.go b/tests/stream_consume_test.go index 4a479bc5a26..72714ca8edd 100644 --- a/tests/stream_consume_test.go +++ b/tests/stream_consume_test.go @@ -31,16 +31,19 @@ func TestStreamWorkflowConsumesWithoutHistoryPayloads(t *testing.T) { id := "stream-wf-consume-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + we, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) // What each task was handed, in the order the tasks ran. @@ -53,7 +56,9 @@ func TestStreamWorkflowConsumesWithoutHistoryPayloads(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) task++ if task > 1 { @@ -162,13 +167,16 @@ func recordedCursors(events []*historypb.HistoryEvent) []*streampb.StreamCursor func signalWorkflow(t *testing.T, s *streamTestEnv, workflowID, runID string) { t.Helper() - _, err := s.env.FrontendClient().SignalWorkflowExecution(s.ctx(), &workflowservice.SignalWorkflowExecutionRequest{ - Namespace: s.ns, - WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, - SignalName: "wake", - Identity: "tester", - RequestId: uuid.NewString(), - }) + _, err := s.env.FrontendClient().SignalWorkflowExecution( + s.ctx(), + &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: s.ns, + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID}, + SignalName: "wake", + Identity: "tester", + RequestId: uuid.NewString(), + }, + ) require.NoError(t, err) } @@ -182,16 +190,19 @@ func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { id := "stream-wf-wake-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) publish := func(bodies ...string) []*commandpb.Command { @@ -202,7 +213,9 @@ func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { return []*commandpb.Command{{ CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{Messages: messages}, + AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + Messages: messages, + }, }, }} } @@ -216,7 +229,9 @@ func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) task++ switch task { @@ -250,7 +265,8 @@ func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { signalWorkflow(t, s, id, "") _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - require.Equal(t, int64(1), currentSlice(t, delivered[1]).GetToOffset(), "nothing new at the tail yet") + require.Equal(t, int64(1), currentSlice(t, delivered[1]).GetToOffset(), + "nothing new at the tail yet") // No signal this time. The subscription owes two offsets, so completing the // previous task has to have scheduled this one. @@ -267,11 +283,14 @@ func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { // workflow on empty tasks forever. pollCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - idle, err := env.FrontendClient().PollWorkflowTaskQueue(pollCtx, &workflowservice.PollWorkflowTaskQueueRequest{ - Namespace: s.ns, - TaskQueue: tq, - Identity: "tester", - }) + idle, err := env.FrontendClient().PollWorkflowTaskQueue( + pollCtx, + &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + }, + ) if err == nil { require.Empty(t, idle.GetTaskToken(), "a caught-up subscription must not keep scheduling tasks") @@ -287,16 +306,19 @@ func TestSubscribingToABacklogSchedulesAWorkflowTask(t *testing.T) { id := "stream-wf-backlog-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) var delivered [][]*streampb.StreamSlice @@ -308,7 +330,9 @@ func TestSubscribingToABacklogSchedulesAWorkflowTask(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) task++ if task > 1 { @@ -364,16 +388,19 @@ func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { id := "stream-wf-replay-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + we, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) var delivered [][]*streampb.StreamSlice @@ -385,7 +412,9 @@ func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) task++ if task > 1 { @@ -435,7 +464,8 @@ func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { require.NoError(t, err) replayed := sliceForEvent(delivered[2], consumedAt) - require.NotNil(t, replayed, "the replayed task must carry the range recorded at event %d", consumedAt) + require.NotNil(t, replayed, + "the replayed task must carry the range recorded at event %d", consumedAt) require.Equal(t, int64(0), replayed.GetFromOffset()) require.Equal(t, int64(2), replayed.GetToOffset()) require.Len(t, replayed.GetMessages(), 2, "the payloads have to come back from the stream") @@ -500,16 +530,19 @@ func TestWorkflowConsumesAStreamItDoesNotOwn(t *testing.T) { id := "stream-wf-external-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + we, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) var delivered [][]*streampb.StreamSlice @@ -520,7 +553,9 @@ func TestWorkflowConsumesAStreamItDoesNotOwn(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) return nil, nil }, @@ -557,8 +592,14 @@ func TestWorkflowConsumesAStreamItDoesNotOwn(t *testing.T) { FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, Messages: []*streamlib.StreamMessage{ - {Body: &commonpb.Payload{Data: []byte("from-outside-1")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, - {Body: &commonpb.Payload{Data: []byte("from-outside-2")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + { + Body: &commonpb.Payload{Data: []byte("from-outside-1")}, + Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + }, + { + Body: &commonpb.Payload{Data: []byte("from-outside-2")}, + Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + }, }, }, }) @@ -602,16 +643,19 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { id := "stream-wf-can-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) var delivered [][]*streampb.StreamSlice @@ -623,7 +667,9 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) task++ if task == 2 { @@ -631,7 +677,8 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { return []*commandpb.Command{{ CommandType: enumspb.COMMAND_TYPE_CONTINUE_AS_NEW_WORKFLOW_EXECUTION, Attributes: &commandpb.Command_ContinueAsNewWorkflowExecutionCommandAttributes{ - ContinueAsNewWorkflowExecutionCommandAttributes: &commandpb.ContinueAsNewWorkflowExecutionCommandAttributes{ + ContinueAsNewWorkflowExecutionCommandAttributes: &commandpb. + ContinueAsNewWorkflowExecutionCommandAttributes{ WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, TaskQueue: tq, WorkflowRunTimeout: durationpb.New(100 * time.Second), @@ -694,7 +741,8 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { require.NoError(t, err) afterCAN := currentSlice(t, delivered[len(delivered)-1]) - require.Equal(t, int64(1), afterCAN.GetFromOffset(), "the successor resumes where its predecessor stopped") + require.Equal(t, int64(1), afterCAN.GetFromOffset(), + "the successor resumes where its predecessor stopped") require.Equal(t, int64(2), afterCAN.GetToOffset()) require.Len(t, afterCAN.GetMessages(), 1) require.Equal(t, "after-can", string(afterCAN.GetMessages()[0].GetBody().GetData())) @@ -732,16 +780,19 @@ func TestWorkflowSubscribesToAStreamItself(t *testing.T) { id := "stream-wf-selfsub-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) var delivered [][]*streampb.StreamSlice @@ -753,7 +804,9 @@ func TestWorkflowSubscribesToAStreamItself(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) task++ if task > 1 { @@ -820,7 +873,9 @@ func TestWorkflowSubscribesToAStreamItself(t *testing.T) { require.Equal(t, "self-1", string(got.GetMessages()[0].GetBody().GetData())) } -func subscribedEvents(events []*historypb.HistoryEvent) []*historypb.WorkflowStreamSubscribedEventAttributes { +func subscribedEvents( + events []*historypb.HistoryEvent, +) []*historypb.WorkflowStreamSubscribedEventAttributes { var out []*historypb.WorkflowStreamSubscribedEventAttributes for _, e := range events { if attrs := e.GetWorkflowStreamSubscribedEventAttributes(); attrs != nil { @@ -844,16 +899,19 @@ func TestResubscribingStillWritesItsEvent(t *testing.T) { id := "stream-wf-resub-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + we, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) subscribe := []*commandpb.Command{{ @@ -872,7 +930,9 @@ func TestResubscribingStillWritesItsEvent(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { task++ if task > 2 { return nil, nil @@ -886,13 +946,16 @@ func TestResubscribingStillWritesItsEvent(t *testing.T) { // Two tasks, each issuing the same subscribe. The second registers nothing. _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - _, err = env.FrontendClient().SignalWorkflowExecution(s.ctx(), &workflowservice.SignalWorkflowExecutionRequest{ - Namespace: s.ns, - WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: id}, - SignalName: "wake", - Identity: "tester", - RequestId: uuid.NewString(), - }) + _, err = env.FrontendClient().SignalWorkflowExecution( + s.ctx(), + &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: s.ns, + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: id}, + SignalName: "wake", + Identity: "tester", + RequestId: uuid.NewString(), + }, + ) require.NoError(t, err) _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) @@ -913,11 +976,8 @@ func TestResubscribingStillWritesItsEvent(t *testing.T) { // A subscribe followed by another command in the same Workflow Task. Every SDK // matches issued commands against the events they produced by position, so the -// subscription's event has to sit where its command did. -// -// It used to be written in the flush, which runs after every command, so it -// landed behind the events of commands issued later and the first replay of -// any workflow that subscribed before doing anything else would fail. +// subscription's event has to sit where its command did, even though its start +// offset is only resolved after every command has run. func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { env := testcore.NewEnv(t) s := newStreamTestEnvFrom(t, env) @@ -928,16 +988,19 @@ func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { id := "stream-order-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-order"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-order"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) //nolint:staticcheck // SA1019: deprecated poller is the only one that can emit the command. @@ -946,7 +1009,9 @@ func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { // Subscribe first, then publish. The publish event must come second. return []*commandpb.Command{ { @@ -1009,7 +1074,9 @@ func TestMessagesAppendedWithoutAKindReachTheWorkflow(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) return nil, nil }, diff --git a/tests/stream_consumer_lifecycle_test.go b/tests/stream_consumer_lifecycle_test.go index 15254c82849..dd3e33dccdb 100644 --- a/tests/stream_consumer_lifecycle_test.go +++ b/tests/stream_consumer_lifecycle_test.go @@ -18,10 +18,11 @@ import ( ) func completeWorkflowCommand() []*commandpb.Command { + attrs := &commandpb.CompleteWorkflowExecutionCommandAttributes{} return []*commandpb.Command{{ CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ - CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, + CompleteWorkflowExecutionCommandAttributes: attrs, }, }} } @@ -49,7 +50,9 @@ func subscribeConsumeAndComplete( Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { task++ if task == 3 { return completeWorkflowCommand(), nil @@ -103,7 +106,8 @@ func TestACompletedConsumerReleasesItsFloor(t *testing.T) { Namespace: s.ns, StreamId: streamID, NewBaseOffset: 2, }, }) - require.ErrorContains(t, err, "still depends on offset 0", "the pin holds until the stream finds out") + require.ErrorContains(t, err, "still depends on offset 0", + "the pin holds until the stream finds out") // The append is what makes the stream go and ask. _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ @@ -136,16 +140,19 @@ func TestANewRunSubscribesFreshAfterThePreviousCompleted(t *testing.T) { first := subscribeConsumeAndComplete(t, s, streamID, "stream-wf-rerun-") // The same workflow id, started again after the first run completed. - second, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: first.GetWorkflowId(), - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: &taskqueuepb.TaskQueue{Name: first.GetWorkflowId() + "-tq2"}, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + second, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: first.GetWorkflowId(), + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: first.GetWorkflowId() + "-tq2"}, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) require.NotEqual(t, first.GetRunId(), second.GetRunId()) @@ -159,7 +166,8 @@ func TestANewRunSubscribesFreshAfterThePreviousCompleted(t *testing.T) { "the new run starts where it asked, not where the old run did") state := describeStream(t, s, streamID) - require.Len(t, state.GetConsumers(), 1, "the old run's pin is replaced, not kept beside the new one") + require.Len(t, state.GetConsumers(), 1, + "the old run's pin is replaced, not kept beside the new one") for _, consumer := range state.GetConsumers() { require.Equal(t, second.GetRunId(), consumer.GetRunId()) require.Equal(t, int64(2), consumer.GetReplayFloor()) diff --git a/tests/stream_publish_cost_test.go b/tests/stream_publish_cost_test.go index 4b3b416faf3..7c778c8ee57 100644 --- a/tests/stream_publish_cost_test.go +++ b/tests/stream_publish_cost_test.go @@ -19,14 +19,12 @@ import ( "google.golang.org/protobuf/types/known/durationpb" ) -// What a history event per publish would cost. +// What the history event per publish costs. // -// Publishing from workflow code is the one stream command with no history -// event, which is why no SDK can reach it: sdk-core matches commands to events -// positionally, so a command that produces none desynchronises replay. Giving -// it an event fixes that, and the objection is that unlike subscribing, which -// happens once, publishing happens per batch. This measures the per-batch -// price so the trade is decided on a number. +// Every SDK matches commands to events positionally, so the publish command +// has to produce one. The objection is that unlike subscribing, which happens +// once, publishing happens per batch. This measures the per-batch price so the +// trade is decided on a number. // // The method is a marginal one. Each arm runs a workflow whose single workflow // task carries N publish commands and then completes, so history holds the @@ -93,7 +91,10 @@ func TestStreamPublishHistoryCost(t *testing.T) { // What the same traffic costs through Signals today. {name: "signal-b100-s20", batches: 100, messagesPerBatch: 1, messageSize: 20, viaSignal: true}, - {name: "signal-b100-s2000", batches: 100, messagesPerBatch: 1, messageSize: 2000, viaSignal: true}, + { + name: "signal-b100-s2000", batches: 100, messagesPerBatch: 1, messageSize: 2000, + viaSignal: true, + }, } results := make([]publishCostResult, 0, len(arms)) @@ -156,16 +157,19 @@ func runPublishCostArm(t *testing.T, arm publishCostArm) publishCostResult { tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} ctx := testcore.NewContext() - _, err := env.FrontendClient().StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: env.Namespace().String(), - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "publish-cost"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(60 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + ctx, + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: env.Namespace().String(), + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "publish-cost"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(60 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) body := []byte(strings.Repeat("x", arm.messageSize)) @@ -178,14 +182,17 @@ func runPublishCostArm(t *testing.T, arm publishCostArm) publishCostResult { for range arm.messagesPerBatch { payloads = append(payloads, &commonpb.Payload{Data: body}) } - _, err := env.FrontendClient().SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{ - Namespace: env.Namespace().String(), - WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: id}, - SignalName: "stream-item", - Input: &commonpb.Payloads{Payloads: payloads}, - Identity: "tester", - RequestId: uuid.NewString(), - }) + _, err := env.FrontendClient().SignalWorkflowExecution( + ctx, + &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: id}, + SignalName: "stream-item", + Input: &commonpb.Payloads{Payloads: payloads}, + Identity: "tester", + RequestId: uuid.NewString(), + }, + ) require.NoError(t, err) } } @@ -196,7 +203,9 @@ func runPublishCostArm(t *testing.T, arm publishCostArm) publishCostResult { Namespace: env.Namespace().String(), TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { commands := make([]*commandpb.Command, 0, arm.batches+1) if !arm.viaSignal { for range arm.batches { @@ -217,12 +226,7 @@ func runPublishCostArm(t *testing.T, arm publishCostArm) publishCostResult { }) } } - return append(commands, &commandpb.Command{ - CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, - Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ - CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, - }, - }), nil + return append(commands, completeWorkflowCommand()...), nil }, Logger: env.Logger, T: t, @@ -231,10 +235,13 @@ func runPublishCostArm(t *testing.T, arm publishCostArm) publishCostResult { _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - desc, err := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ - Namespace: env.Namespace().String(), - Execution: &commonpb.WorkflowExecution{WorkflowId: id}, - }) + desc, err := env.FrontendClient().DescribeWorkflowExecution( + ctx, + &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: &commonpb.WorkflowExecution{WorkflowId: id}, + }, + ) require.NoError(t, err) return publishCostResult{ @@ -255,7 +262,8 @@ func reportPublishCost(t *testing.T, results []publishCostResult) { t.Log("Cost of one history event per publish, measured by differencing against an empty run.") t.Logf("Control: %d events, %d bytes.", control.historyEvents, control.historyBytes) t.Log("") - t.Log("| arm | batches | msgs | msg size | events | bytes | events/batch | bytes/batch | bytes/msg |") + t.Log("| arm | batches | msgs | msg size | events | bytes " + + "| events/batch | bytes/batch | bytes/msg |") t.Log("|---|---|---|---|---|---|---|---|---|") for _, r := range results { if r.arm.batches == 0 { diff --git a/tests/stream_rejection_test.go b/tests/stream_rejection_test.go index 508a3f6c93d..7e5fc7f87b8 100644 --- a/tests/stream_rejection_test.go +++ b/tests/stream_rejection_test.go @@ -35,7 +35,9 @@ func TestOverLimitPublishFailsTheWorkflowTask(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { task++ if task == 1 { return publishCommand(tooMany...), nil @@ -95,7 +97,9 @@ func TestSubscribeToAMissingStreamFailsTheWorkflowTask(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) if len(delivered) == 1 { return subscribeCommand(missing), nil @@ -145,7 +149,9 @@ func TestPublishOnAFailedTaskLeavesNoTrace(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { task++ if task == 1 { return append(publishCommand("lost-with-the-task"), invalidTrailer), nil diff --git a/tests/stream_replay_test.go b/tests/stream_replay_test.go index ba14e219629..3ae064750cb 100644 --- a/tests/stream_replay_test.go +++ b/tests/stream_replay_test.go @@ -30,16 +30,19 @@ func startConsumer( t.Helper() id := prefix + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - we, err := s.env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + we, err := s.env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) return &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}, tq } @@ -98,7 +101,9 @@ func TestReplayFollowsHistoryPastTheFirstPage(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) if len(delivered) == 1 { return publishCommand("page-1", "page-2"), nil @@ -132,7 +137,8 @@ func TestReplayFollowsHistoryPastTheFirstPage(t *testing.T) { recordedAt := completedEventsWithCursors(events) require.Len(t, recordedAt, 4, "every task since the subscription recorded a range") consumedAt := completedEventWithCursors(t, events) - require.Greater(t, len(events), 8, "the history has to span more than one page for this to mean anything") + require.Greater(t, len(events), 8, + "the history has to span more than one page for this to mean anything") env.CloseShard(env.NamespaceID().String(), execution.GetWorkflowId()) signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) @@ -154,7 +160,9 @@ func TestReplayFollowsHistoryPastTheFirstPage(t *testing.T) { // pollInBackground keeps a poller on the queue until the returned stop // function is called. Used where a task is expected never to reach the worker, // because matching only asks history to start a task while someone polls. -func pollInBackground(t *testing.T, env *testcore.TestEnv, ns string, tq *taskqueuepb.TaskQueue) func() { +func pollInBackground( + t *testing.T, env *testcore.TestEnv, ns string, tq *taskqueuepb.TaskQueue, +) func() { t.Helper() ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) @@ -162,9 +170,12 @@ func pollInBackground(t *testing.T, env *testcore.TestEnv, ns string, tq *taskqu defer close(done) for ctx.Err() == nil { pollCtx, pollCancel := context.WithTimeout(ctx, 5*time.Second) - _, _ = env.FrontendClient().PollWorkflowTaskQueue(pollCtx, &workflowservice.PollWorkflowTaskQueueRequest{ - Namespace: ns, TaskQueue: tq, Identity: "tester", - }) + _, _ = env.FrontendClient().PollWorkflowTaskQueue( + pollCtx, + &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: ns, TaskQueue: tq, Identity: "tester", + }, + ) pollCancel() } }() @@ -221,7 +232,9 @@ func TestConsumerOfADeletedStreamFailsItsTaskLoudly(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { return nil, nil }, Logger: env.Logger, @@ -287,7 +300,9 @@ func TestReplayOfADeletedStreamFailsTheTaskLoudly(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(resp *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) return nil, nil }, diff --git a/tests/stream_retention_test.go b/tests/stream_retention_test.go index c25a92f4da7..18278ee9a85 100644 --- a/tests/stream_retention_test.go +++ b/tests/stream_retention_test.go @@ -44,16 +44,19 @@ func TestTruncationRefusesToDropWhatAConsumerNeedsToReplay(t *testing.T) { id := "stream-retention-wf-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err = env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err = env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) //nolint:staticcheck // SA1019: consistent with the other stream tests. @@ -62,7 +65,9 @@ func TestTruncationRefusesToDropWhatAConsumerNeedsToReplay(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(_ *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + _ *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { return nil, nil }, Logger: env.Logger, @@ -168,7 +173,9 @@ func TestRetentionWaitsForAnActiveConsumer(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { task++ if task == 2 { return completeWorkflowCommand(), nil @@ -227,7 +234,9 @@ func TestDeleteStreamRefusesWhileAConsumerIsActive(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { return nil, nil }, Logger: env.Logger, diff --git a/tests/stream_routing_cross_host_test.go b/tests/stream_routing_cross_host_test.go index a209d6007db..325f14d3e1d 100644 --- a/tests/stream_routing_cross_host_test.go +++ b/tests/stream_routing_cross_host_test.go @@ -94,15 +94,18 @@ func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { require.NoError(t, err) tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - started, err := frontend.StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: topo.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "cross-host-consumer"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(2 * time.Minute), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - }) + started, err := frontend.StartWorkflowExecution( + ctx, + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: topo.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "cross-host-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(2 * time.Minute), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + }, + ) require.NoError(t, err) execution := &commonpb.WorkflowExecution{WorkflowId: id, RunId: started.GetRunId()} @@ -189,15 +192,18 @@ func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { RequestId: uuid.NewString(), }) require.NoError(t, err) - response, err := owner.RecordWorkflowTaskStarted(ctx, &historyservice.RecordWorkflowTaskStartedRequest{ - NamespaceId: topo.nsID, - WorkflowExecution: execution, - ScheduledEventId: latestScheduled(), - RequestId: uuid.NewString(), - PollRequest: &workflowservice.PollWorkflowTaskQueueRequest{ - Namespace: topo.ns, TaskQueue: tq, Identity: "cross-host-validation", + response, err := owner.RecordWorkflowTaskStarted( + ctx, + &historyservice.RecordWorkflowTaskStartedRequest{ + NamespaceId: topo.nsID, + WorkflowExecution: execution, + ScheduledEventId: latestScheduled(), + RequestId: uuid.NewString(), + PollRequest: &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: topo.ns, TaskQueue: tq, Identity: "cross-host-validation", + }, }, - }) + ) require.NoError(t, err) artifacts.writeProto(tc.name+"-cold-response", response) @@ -313,10 +319,13 @@ func (topo *crossHostTopology) requireOwner( ctx context.Context, t *testing.T, businessID string, host string, ) { t.Helper() - desc, err := topo.cluster.AdminClient().DescribeHistoryHost(ctx, &adminservice.DescribeHistoryHostRequest{ - Namespace: topo.ns, - WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: businessID}, - }) + desc, err := topo.cluster.AdminClient().DescribeHistoryHost( + ctx, + &adminservice.DescribeHistoryHostRequest{ + Namespace: topo.ns, + WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: businessID}, + }, + ) require.NoError(t, err) require.Equal(t, host, desc.GetAddress()) } diff --git a/tests/stream_test.go b/tests/stream_test.go index 2d8b50c6c3a..c2586ef9a6b 100644 --- a/tests/stream_test.go +++ b/tests/stream_test.go @@ -43,7 +43,9 @@ func newStreamTestEnvFrom(t *testing.T, env *testcore.TestEnv) *streamTestEnv { require.NoError(t, err) t.Cleanup(func() { _ = conn.Close() }) - env2 := &streamTestEnv{env: env, client: streampb.NewStreamServiceClient(conn), ns: env.Namespace().String()} + env2 := &streamTestEnv{ + env: env, client: streampb.NewStreamServiceClient(conn), ns: env.Namespace().String(), + } t.Cleanup(func() { for _, c := range env2.cleanup { c() @@ -125,7 +127,8 @@ func TestStreamAppendAndRead(t *testing.T) { const id = "stream-append-read" s.create(ctx, t, id) - first, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) + first, err := s.add(ctx, t, id, + &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) require.NoError(t, err) require.Equal(t, int64(0), first.GetFirstOffset()) require.Equal(t, int64(3), first.GetNextOffset()) @@ -413,7 +416,8 @@ func TestStreamLongPollReturnsEmptyOnTimeout(t *testing.T) { require.Empty(t, out.GetMessages()) require.Equal(t, int64(0), out.GetNextOffset()) require.False(t, out.GetClosed()) - require.Greater(t, time.Since(start), 5*time.Second, "the poll should have parked, not returned immediately") + require.Greater(t, time.Since(start), 5*time.Second, + "the poll should have parked, not returned immediately") } func TestStreamLongPollReturnsImmediatelyWhenBehind(t *testing.T) { @@ -606,7 +610,8 @@ func TestStreamFilteredReadReportsRealOffsets(t *testing.T) { } func (s *streamTestEnv) pollMaxTopics( - ctx context.Context, t *testing.T, streamID string, from int64, maxMessages int32, topics ...string, + ctx context.Context, t *testing.T, streamID string, from int64, maxMessages int32, + topics ...string, ) *streampb.PollMessagesOutput { t.Helper() resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ diff --git a/tests/stream_workflow_test.go b/tests/stream_workflow_test.go index 08c1b877857..1d0dee7606d 100644 --- a/tests/stream_workflow_test.go +++ b/tests/stream_workflow_test.go @@ -38,16 +38,19 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { id := "stream-wf-publish-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - we, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + we, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) published := false @@ -59,14 +62,11 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { if published { - return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, - Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ - CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, - }, - }}, nil + return completeWorkflowCommand(), nil } published = true return []*commandpb.Command{{ @@ -120,7 +120,8 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { }, }) require.NoError(t, err) - require.Equal(t, []string{"planning", "calling tool"}, bodies(poll.GetFrontendResponse().GetMessages())) + require.Equal(t, []string{"planning", "calling tool"}, + bodies(poll.GetFrontendResponse().GetMessages())) require.Equal(t, []int64{0, 1}, offsets(poll.GetFrontendResponse().GetMessages()), "each message carries where it sits in the log, so a reader can resume between them") require.Equal(t, int64(2), poll.GetFrontendResponse().GetNextOffset()) @@ -182,16 +183,19 @@ func TestStreamWorkflowLongPollWakesOnPublish(t *testing.T) { id := "stream-wf-longpoll-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) type result struct { @@ -214,7 +218,9 @@ func TestStreamWorkflowLongPollWakesOnPublish(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { return []*commandpb.Command{{ CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ @@ -252,16 +258,19 @@ func TestStreamWorkflowTakesAppendsFromOutsideToo(t *testing.T) { id := "stream-wf-mixed-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) appendOutside := func(body string) *streamlib.AddMessagesOutput { @@ -289,7 +298,9 @@ func TestStreamWorkflowTakesAppendsFromOutsideToo(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { return []*commandpb.Command{{ CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ @@ -344,16 +355,19 @@ func TestStreamWorkflowStreamClosesWithItsWorkflow(t *testing.T) { id := "stream-wf-close-" + uuid.NewString() tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} - _, err := env.FrontendClient().StartWorkflowExecution(s.ctx(), &workflowservice.StartWorkflowExecutionRequest{ - RequestId: uuid.NewString(), - Namespace: s.ns, - WorkflowId: id, - WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, - TaskQueue: tq, - WorkflowRunTimeout: durationpb.New(100 * time.Second), - WorkflowTaskTimeout: durationpb.New(10 * time.Second), - Identity: "tester", - }) + _, err := env.FrontendClient().StartWorkflowExecution( + s.ctx(), + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: s.ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-publisher"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(100 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }, + ) require.NoError(t, err) describe := func() *streamlib.StreamState { @@ -373,7 +387,9 @@ func TestStreamWorkflowStreamClosesWithItsWorkflow(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { return []*commandpb.Command{ { CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, @@ -385,12 +401,7 @@ func TestStreamWorkflowStreamClosesWithItsWorkflow(t *testing.T) { }, }, }, - { - CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, - Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ - CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, - }, - }, + completeWorkflowCommand()[0], }, nil }, Logger: env.Logger, @@ -431,7 +442,9 @@ func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { return publishCommand("from-workflow"), nil }, Logger: env.Logger, @@ -500,7 +513,9 @@ func TestOwnedStreamRefusesAppendsPastItsBudget(t *testing.T) { Namespace: s.ns, TaskQueue: tq, Identity: "tester", - WorkflowTaskHandler: func(*workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) { + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { task++ if task == 1 { return publishCommand("one", "two"), nil diff --git a/tests/streaming_baseline_test.go b/tests/streaming_baseline_test.go index a97817e01b2..cfc36864d8b 100644 --- a/tests/streaming_baseline_test.go +++ b/tests/streaming_baseline_test.go @@ -137,8 +137,14 @@ func TestStreamingBaseline(t *testing.T) { func shortStreamBaselineMatrix() []streamBaselineParams { return []streamBaselineParams{ - {name: "flush2s_sub1", flushInterval: 2 * time.Second, subscribers: 1, messageRate: 40, duration: 6 * time.Second}, - {name: "flush100ms_sub1", flushInterval: 100 * time.Millisecond, subscribers: 1, messageRate: 40, duration: 6 * time.Second}, + { + name: "flush2s_sub1", flushInterval: 2 * time.Second, subscribers: 1, + messageRate: 40, duration: 6 * time.Second, + }, + { + name: "flush100ms_sub1", flushInterval: 100 * time.Millisecond, subscribers: 1, + messageRate: 40, duration: 6 * time.Second, + }, } } @@ -201,7 +207,8 @@ func runStreamBaseline(t *testing.T, p streamBaselineParams) streamBaselineResul consumers.Add(1) go func(idx int) { defer consumers.Done() - lat, n := runStreamConsumer(consumerCtx, env, wfID, run.GetRunID(), sentAt, &receivedTotal, &pollRejections) + lat, n := runStreamConsumer( + consumerCtx, env, wfID, run.GetRunID(), sentAt, &receivedTotal, &pollRejections) consumerLatencies[idx] = lat consumerCounts[idx] = n }(i) @@ -223,7 +230,8 @@ func runStreamBaseline(t *testing.T, p streamBaselineParams) streamBaselineResul } stopConsumers() consumers.Wait() - require.NoError(t, env.SdkClient().SignalWorkflow(ctx, wfID, run.GetRunID(), streamDoneSignal, nil)) + require.NoError(t, + env.SdkClient().SignalWorkflow(ctx, wfID, run.GetRunID(), streamDoneSignal, nil)) if err := run.Get(ctx, nil); err != nil { t.Logf("workflow did not complete cleanly: %v", err) } @@ -237,10 +245,13 @@ func runStreamBaseline(t *testing.T, p streamBaselineParams) streamBaselineResul res.latencyP50 = percentile(all, 0.50) res.latencyP99 = percentile(all, 0.99) - desc, err := env.FrontendClient().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{ - Namespace: env.Namespace().String(), - Execution: env.Tv().WithWorkflowID(wfID).WorkflowExecution(), - }) + desc, err := env.FrontendClient().DescribeWorkflowExecution( + ctx, + &workflowservice.DescribeWorkflowExecutionRequest{ + Namespace: env.Namespace().String(), + Execution: env.Tv().WithWorkflowID(wfID).WorkflowExecution(), + }, + ) if err == nil { res.historyBytes = desc.GetWorkflowExecutionInfo().GetHistorySizeBytes() res.historyEvents = desc.GetWorkflowExecutionInfo().GetHistoryLength() @@ -423,7 +434,8 @@ func reportStreamBaseline(t *testing.T, results []streamBaselineResult) { // without being retyped, which is how transcription errors get in. t.Log("Workflow Streams baseline: Signals in, polling Update out") t.Log("") - t.Log("| scenario | msgs | delivered | rejected polls | hist events/msg | hist bytes/msg | persist ops/msg | p50 | p99 |") + t.Log("| scenario | msgs | delivered | rejected polls | hist events/msg | hist bytes/msg " + + "| persist ops/msg | p50 | p99 |") t.Log("|---|---|---|---|---|---|---|---|---|") for _, r := range results { perMsg := func(v int64) string { diff --git a/tests/streaming_native_test.go b/tests/streaming_native_test.go index a7e400caee8..935dcc10b00 100644 --- a/tests/streaming_native_test.go +++ b/tests/streaming_native_test.go @@ -230,7 +230,8 @@ func TestStreamingComparison(t *testing.T) { t.Log("Workflow Streams (Signals in, polling Update out) versus native streams") t.Log("") - t.Log("| scenario | design | msgs | delivered | rejected | wf hist bytes/msg | persist ops/msg | p50 | p99 |") + t.Log("| scenario | design | msgs | delivered | rejected | wf hist bytes/msg " + + "| persist ops/msg | p50 | p99 |") t.Log("|---|---|---|---|---|---|---|---|---|") for i := range baseline { logComparisonRow(t, "signals+update", baseline[i]) From edfba28fa45f8545c9ca1edf66ce9ab5d8c7cbc6 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Fri, 18 Sep 2026 17:50:38 -0700 Subject: [PATCH 37/47] Cleared the linter findings in the stream code. --- chasm/lib/stream/stream.go | 8 ++++---- tests/stream_retention_test.go | 5 +++-- tests/stream_workflow_test.go | 6 ++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index fd48247c81a..552832085fb 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -307,17 +307,17 @@ func (s *Stream) checkBudget(count int64, size int64) error { if budget == nil { return nil } - if max := budget.GetMaxItems(); max > 0 && s.State.HeadOffset+count > max { + if limit := budget.GetMaxItems(); limit > 0 && s.State.HeadOffset+count > limit { return serviceerror.NewResourceExhaustedf( enumspb.RESOURCE_EXHAUSTED_CAUSE_PERSISTENCE_STORAGE_LIMIT, "stream holds %d of its budget of %d messages; the append of %d does not fit", - s.State.HeadOffset, max, count) + s.State.HeadOffset, limit, count) } - if max := budget.GetMaxBytes(); max > 0 && s.State.AppendedBytes+size > max { + if limit := budget.GetMaxBytes(); limit > 0 && s.State.AppendedBytes+size > limit { return serviceerror.NewResourceExhaustedf( enumspb.RESOURCE_EXHAUSTED_CAUSE_PERSISTENCE_STORAGE_LIMIT, "stream holds %d of its budget of %d bytes; the append of %d does not fit", - s.State.AppendedBytes, max, size) + s.State.AppendedBytes, limit, size) } return nil } diff --git a/tests/stream_retention_test.go b/tests/stream_retention_test.go index 18278ee9a85..3e2ffcacdc1 100644 --- a/tests/stream_retention_test.go +++ b/tests/stream_retention_test.go @@ -200,8 +200,9 @@ func TestRetentionWaitsForAnActiveConsumer(t *testing.T) { require.NoError(t, err) // Well past retention, and the stream is still there because the consumer - // is still running. - time.Sleep(3 * time.Second) + // is still running. The assertion is that nothing happens, which only the + // deadline passing can show. + time.Sleep(3 * time.Second) //nolint:forbidigo // waiting for retention to elapse is the test. state := describeStream(t, s, streamID) require.True(t, state.GetClosed()) require.Len(t, state.GetConsumers(), 1) diff --git a/tests/stream_workflow_test.go b/tests/stream_workflow_test.go index 1d0dee7606d..118c2bf494f 100644 --- a/tests/stream_workflow_test.go +++ b/tests/stream_workflow_test.go @@ -454,9 +454,7 @@ func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { errs := make(chan error, producers*perProducer) var wg sync.WaitGroup for p := range producers { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { for i := range perProducer { _, err := s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ FrontendRequest: &streamlib.AddWorkflowMessagesInput{ @@ -468,7 +466,7 @@ func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { }) errs <- err } - }() + }) } for range workflowTasks { _, err := poller.PollAndProcessWorkflowTask() From bedc962ebd7f8fa1d9e606410b45c80a987980d0 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 01:38:41 -0700 Subject: [PATCH 38/47] Carried the record vocabulary and producer identity through the server. The api names an entry a record and carries its kind, producer, attempt and sequence on the wire, so the store keeps those fields and hands them back on the Workflow Task instead of dropping everything but the body. The workflow's own publish clears the producer id, which is how a reader tells its records from an outside producer's. --- chasm/lib/stream/config.go | 16 +- .../gen/streampb/v1/message.go-helpers.pb.go | 61 ++--- .../lib/stream/gen/streampb/v1/message.pb.go | 210 +++++++----------- .../gen/streampb/v1/request_response.pb.go | 50 ++--- chasm/lib/stream/messages.go | 67 +++--- chasm/lib/stream/messages_test.go | 52 ++++- chasm/lib/stream/proto/v1/message.proto | 39 ++-- .../stream/proto/v1/request_response.proto | 8 +- chasm/lib/stream/service/events.go | 16 +- chasm/lib/stream/service/handler.go | 20 +- chasm/lib/stream/service/handler_test.go | 13 +- chasm/lib/stream/stream.go | 63 +++--- chasm/lib/stream/stream_test.go | 109 ++++----- chasm/lib/workflow/stream_commands.go | 93 ++++---- chasm/lib/workflow/stream_commands_test.go | 41 ++++ chasm/lib/workflow/stream_cursor_test.go | 18 +- chasm/lib/workflow/workflow.go | 6 +- go.mod | 2 +- go.sum | 4 +- service/history/api/command_attr_validator.go | 2 +- .../stream_routing.go | 8 +- .../stream_routing_test.go | 17 +- .../stream_slices.go | 50 ++--- .../history/historybuilder/event_factory.go | 4 +- service/history/historybuilder/event_store.go | 2 +- .../history/historybuilder/history_builder.go | 2 +- .../historybuilder/history_builder_test.go | 2 +- .../history/workflow/mutable_state_impl.go | 2 +- tests/api_fork_test.go | 20 +- tests/stream_consume_test.go | 114 +++++----- tests/stream_consumer_lifecycle_test.go | 4 +- tests/stream_publish_cost_test.go | 12 +- tests/stream_rejection_test.go | 16 +- tests/stream_replay_test.go | 26 +-- tests/stream_retention_test.go | 15 +- tests/stream_routing_cross_host_test.go | 6 +- tests/stream_test.go | 105 ++++----- tests/stream_workflow_test.go | 72 +++--- tests/streaming_native_test.go | 11 +- 39 files changed, 714 insertions(+), 664 deletions(-) create mode 100644 chasm/lib/workflow/stream_commands_test.go diff --git a/chasm/lib/stream/config.go b/chasm/lib/stream/config.go index 823a70568a9..cb7ac2cd6d6 100644 --- a/chasm/lib/stream/config.go +++ b/chasm/lib/stream/config.go @@ -9,12 +9,12 @@ import ( // DefaultMaxMessagesPerPoll bounds a read page when the caller does not. const DefaultMaxMessagesPerPoll = 1000 -// MaxMessagesPerBatch bounds one append. It is not only an admission limit: a +// MaxRecordsPerBatch bounds one append. It is not only an admission limit: a // batch is keyed by its first offset, so to serve a read starting inside a // batch the reader has to find the batch that contains it. Bounding the batch // bounds how far back it has to look, which turns an unbounded scan into a // fixed overread. -const MaxMessagesPerBatch = 1000 +const MaxRecordsPerBatch = 1000 // LongPollTimeout matches the convention used by the history long polls: on // expiry the caller gets an empty response and polls again, rather than an @@ -44,14 +44,14 @@ const MaxStreamNameLength = 255 // back on. const ( // MaxConsumeItemsPerTask bounds one Workflow Task's slice. A byte cap alone - // is not enough: a burst of tiny messages stays under it while still making + // is not enough: a burst of tiny records stays under it while still making // one task's drain arbitrarily long. Whichever bound binds first, the rest // is delivered on the following task. MaxConsumeItemsPerTask = 1000 // MaxConsumeBytesPerTask bounds one Workflow Task's slice by size. Paired // with MaxConsumeItemsPerTask because neither bound alone is enough: a - // burst of tiny messages slips under the byte budget, and a few large ones + // burst of tiny records slips under the byte budget, and a few large ones // slip under the item count. MaxConsumeBytesPerTask = 2 << 20 @@ -101,12 +101,12 @@ var ( MaxConsumeItemsPerTaskSetting = dynamicconfig.NewNamespaceIntSetting( "stream.maxConsumeItemsPerTask", MaxConsumeItemsPerTask, - `Most stream messages one workflow task carries per subscription.`, + `Most stream records one workflow task carries per subscription.`, ) MaxConsumeBytesPerTaskSetting = dynamicconfig.NewNamespaceIntSetting( "stream.maxConsumeBytesPerTask", MaxConsumeBytesPerTask, - `Most stream message bytes one workflow task carries per subscription.`, + `Most stream record bytes one workflow task carries per subscription.`, ) MaxProducersPerStreamSetting = dynamicconfig.NewNamespaceIntSetting( "stream.maxProducersPerStream", @@ -121,12 +121,12 @@ var ( MaxMessageBytesSetting = dynamicconfig.NewNamespaceIntSetting( "stream.maxMessageBytes", MaxMessageBytes, - `Largest single stream message accepted.`, + `Largest single stream record accepted.`, ) MaxBatchBytesSetting = dynamicconfig.NewNamespaceIntSetting( "stream.maxBatchBytes", MaxBatchBytes, - `Largest stream append accepted, summed over its messages.`, + `Largest stream append accepted, summed over its records.`, ) MaxOwnedStreamsPerWorkflowSetting = dynamicconfig.NewNamespaceIntSetting( "stream.maxOwnedStreamsPerWorkflow", diff --git a/chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go b/chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go index 28778a8fce9..ef697410810 100644 --- a/chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/message.go-helpers.pb.go @@ -2,40 +2,38 @@ package streampb import ( - "fmt" - "google.golang.org/protobuf/proto" ) -// Marshal an object of type StreamMessage to the protobuf v3 wire format -func (val *StreamMessage) Marshal() ([]byte, error) { +// Marshal an object of type StreamRecord to the protobuf v3 wire format +func (val *StreamRecord) Marshal() ([]byte, error) { return proto.Marshal(val) } -// Unmarshal an object of type StreamMessage from the protobuf v3 wire format -func (val *StreamMessage) Unmarshal(buf []byte) error { +// Unmarshal an object of type StreamRecord from the protobuf v3 wire format +func (val *StreamRecord) Unmarshal(buf []byte) error { return proto.Unmarshal(buf, val) } // Size returns the size of the object, in bytes, once serialized -func (val *StreamMessage) Size() int { +func (val *StreamRecord) Size() int { return proto.Size(val) } -// Equal returns whether two StreamMessage values are equivalent by recursively +// Equal returns whether two StreamRecord 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 *StreamMessage) Equal(that interface{}) bool { +func (this *StreamRecord) Equal(that interface{}) bool { if that == nil { return this == nil } - var that1 *StreamMessage + var that1 *StreamRecord switch t := that.(type) { - case *StreamMessage: + case *StreamRecord: that1 = t - case StreamMessage: + case StreamRecord: that1 = &t default: return false @@ -44,35 +42,35 @@ func (this *StreamMessage) Equal(that interface{}) bool { return proto.Equal(this, that1) } -// Marshal an object of type StreamMessageBatch to the protobuf v3 wire format -func (val *StreamMessageBatch) Marshal() ([]byte, error) { +// Marshal an object of type StreamRecordBatch to the protobuf v3 wire format +func (val *StreamRecordBatch) Marshal() ([]byte, error) { return proto.Marshal(val) } -// Unmarshal an object of type StreamMessageBatch from the protobuf v3 wire format -func (val *StreamMessageBatch) Unmarshal(buf []byte) error { +// Unmarshal an object of type StreamRecordBatch from the protobuf v3 wire format +func (val *StreamRecordBatch) Unmarshal(buf []byte) error { return proto.Unmarshal(buf, val) } // Size returns the size of the object, in bytes, once serialized -func (val *StreamMessageBatch) Size() int { +func (val *StreamRecordBatch) Size() int { return proto.Size(val) } -// Equal returns whether two StreamMessageBatch values are equivalent by recursively +// Equal returns whether two StreamRecordBatch 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 *StreamMessageBatch) Equal(that interface{}) bool { +func (this *StreamRecordBatch) Equal(that interface{}) bool { if that == nil { return this == nil } - var that1 *StreamMessageBatch + var that1 *StreamRecordBatch switch t := that.(type) { - case *StreamMessageBatch: + case *StreamRecordBatch: that1 = t - case StreamMessageBatch: + case StreamRecordBatch: that1 = &t default: return false @@ -80,22 +78,3 @@ func (this *StreamMessageBatch) Equal(that interface{}) bool { return proto.Equal(this, that1) } - -var ( - StreamMessageKind_shorthandValue = map[string]int32{ - "Unspecified": 0, - "Data": 1, - "Flush": 2, - } -) - -// StreamMessageKindFromString parses a StreamMessageKind value from either the protojson -// canonical SCREAMING_CASE enum or the traditional temporal PascalCase enum to StreamMessageKind -func StreamMessageKindFromString(s string) (StreamMessageKind, error) { - if v, ok := StreamMessageKind_value[s]; ok { - return StreamMessageKind(v), nil - } else if v, ok := StreamMessageKind_shorthandValue[s]; ok { - return StreamMessageKind(v), nil - } - return StreamMessageKind(0), fmt.Errorf("%s is not a valid StreamMessageKind", s) -} diff --git a/chasm/lib/stream/gen/streampb/v1/message.pb.go b/chasm/lib/stream/gen/streampb/v1/message.pb.go index 9e1470f4d3a..400dc3c16b4 100644 --- a/chasm/lib/stream/gen/streampb/v1/message.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/message.pb.go @@ -8,11 +8,11 @@ package streampb import ( reflect "reflect" - "strconv" sync "sync" unsafe "unsafe" v1 "go.temporal.io/api/common/v1" + v11 "go.temporal.io/api/stream/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) @@ -24,100 +24,49 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type StreamMessageKind int32 - -const ( - STREAM_MESSAGE_KIND_UNSPECIFIED StreamMessageKind = 0 - STREAM_MESSAGE_KIND_DATA StreamMessageKind = 1 - // Producer signalling a delivery boundary. Carries no body and consumes an - // offset like any other message. A consumer uses it to end a turn without - // waiting out an idle timeout. - STREAM_MESSAGE_KIND_FLUSH StreamMessageKind = 2 -) - -// Enum value maps for StreamMessageKind. -var ( - StreamMessageKind_name = map[int32]string{ - 0: "STREAM_MESSAGE_KIND_UNSPECIFIED", - 1: "STREAM_MESSAGE_KIND_DATA", - 2: "STREAM_MESSAGE_KIND_FLUSH", - } - StreamMessageKind_value = map[string]int32{ - "STREAM_MESSAGE_KIND_UNSPECIFIED": 0, - "STREAM_MESSAGE_KIND_DATA": 1, - "STREAM_MESSAGE_KIND_FLUSH": 2, - } -) - -func (x StreamMessageKind) Enum() *StreamMessageKind { - p := new(StreamMessageKind) - *p = x - return p -} - -func (x StreamMessageKind) String() string { - switch x { - case STREAM_MESSAGE_KIND_UNSPECIFIED: - return "Unspecified" - case STREAM_MESSAGE_KIND_DATA: - return "Data" - case STREAM_MESSAGE_KIND_FLUSH: - return "Flush" - default: - return strconv.Itoa(int(x)) - } - -} - -func (StreamMessageKind) Descriptor() protoreflect.EnumDescriptor { - return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_enumTypes[0].Descriptor() -} - -func (StreamMessageKind) Type() protoreflect.EnumType { - return &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_enumTypes[0] -} - -func (x StreamMessageKind) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use StreamMessageKind.Descriptor instead. -func (StreamMessageKind) EnumDescriptor() ([]byte, []int) { - return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDescGZIP(), []int{0} -} - -type StreamMessage struct { +// The stored shape of temporal.api.stream.v1.StreamRecord plus the offset a +// read assigns. Field for field the public record, so one crosses the frontend +// in either direction without translation. +type StreamRecord struct { state protoimpl.MessageState `protogen:"open.v1"` Body *v1.Payload `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` - // Producer-supplied provenance. The server does not populate this today. + // Producer-supplied provenance, stored as sent. Metadata map[string]*v1.Payload `protobuf:"bytes,2,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Topic string `protobuf:"bytes,3,opt,name=topic,proto3" json:"topic,omitempty"` - // Position within this topic. The global offset orders the whole stream; - // this lets a consumer reason about one topic without decoding the rest. - TopicSequence int64 `protobuf:"varint,4,opt,name=topic_sequence,json=topicSequence,proto3" json:"topic_sequence,omitempty"` - Kind StreamMessageKind `protobuf:"varint,5,opt,name=kind,proto3,enum=temporal.server.chasm.lib.stream.proto.v1.StreamMessageKind" json:"kind,omitempty"` + // The producer's position within its attempt, or -1 when unnumbered. Stored + // as sent; the global offset is what orders the stream. + Sequence int64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` + // Settled to DATA on append when left unspecified, so a retry hashes the + // same bytes and a reader never sees the zero value. + Kind v11.StreamRecordKind `protobuf:"varint,5,opt,name=kind,proto3,enum=temporal.api.stream.v1.StreamRecordKind" json:"kind,omitempty"` // Position in the whole stream, set on read and never stored. A consumer - // that resumes at message granularity needs it, and a topic-filtered read + // that resumes at record granularity needs it, and a topic-filtered read // leaves gaps that make it underivable from the response alone. - Offset int64 `protobuf:"varint,6,opt,name=offset,proto3" json:"offset,omitempty"` + Offset int64 `protobuf:"varint,6,opt,name=offset,proto3" json:"offset,omitempty"` + // Who wrote the record. Empty when the owning Workflow did: its publish + // command clears the field, whatever the worker sent. + ProducerId string `protobuf:"bytes,7,opt,name=producer_id,json=producerId,proto3" json:"producer_id,omitempty"` + // The producer's attempt, stored as sent. Readers treat a later attempt by + // the same producer as superseding what the earlier one wrote. + Attempt int64 `protobuf:"varint,8,opt,name=attempt,proto3" json:"attempt,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *StreamMessage) Reset() { - *x = StreamMessage{} +func (x *StreamRecord) Reset() { + *x = StreamRecord{} mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *StreamMessage) String() string { +func (x *StreamRecord) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StreamMessage) ProtoMessage() {} +func (*StreamRecord) ProtoMessage() {} -func (x *StreamMessage) ProtoReflect() protoreflect.Message { +func (x *StreamRecord) ProtoReflect() protoreflect.Message { mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -129,77 +78,91 @@ func (x *StreamMessage) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StreamMessage.ProtoReflect.Descriptor instead. -func (*StreamMessage) Descriptor() ([]byte, []int) { +// Deprecated: Use StreamRecord.ProtoReflect.Descriptor instead. +func (*StreamRecord) Descriptor() ([]byte, []int) { return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDescGZIP(), []int{0} } -func (x *StreamMessage) GetBody() *v1.Payload { +func (x *StreamRecord) GetBody() *v1.Payload { if x != nil { return x.Body } return nil } -func (x *StreamMessage) GetMetadata() map[string]*v1.Payload { +func (x *StreamRecord) GetMetadata() map[string]*v1.Payload { if x != nil { return x.Metadata } return nil } -func (x *StreamMessage) GetTopic() string { +func (x *StreamRecord) GetTopic() string { if x != nil { return x.Topic } return "" } -func (x *StreamMessage) GetTopicSequence() int64 { +func (x *StreamRecord) GetSequence() int64 { if x != nil { - return x.TopicSequence + return x.Sequence } return 0 } -func (x *StreamMessage) GetKind() StreamMessageKind { +func (x *StreamRecord) GetKind() v11.StreamRecordKind { if x != nil { return x.Kind } - return STREAM_MESSAGE_KIND_UNSPECIFIED + return v11.StreamRecordKind(0) } -func (x *StreamMessage) GetOffset() int64 { +func (x *StreamRecord) GetOffset() int64 { if x != nil { return x.Offset } return 0 } +func (x *StreamRecord) GetProducerId() string { + if x != nil { + return x.ProducerId + } + return "" +} + +func (x *StreamRecord) GetAttempt() int64 { + if x != nil { + return x.Attempt + } + return 0 +} + // One append is one batch, and one batch is one data node. The server stores // this serialized and opaque; it decodes only to trim a partial first page or // to apply a topic filter. -type StreamMessageBatch struct { +type StreamRecordBatch struct { state protoimpl.MessageState `protogen:"open.v1"` - Messages []*StreamMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + Records []*StreamRecord `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *StreamMessageBatch) Reset() { - *x = StreamMessageBatch{} +func (x *StreamRecordBatch) Reset() { + *x = StreamRecordBatch{} mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *StreamMessageBatch) String() string { +func (x *StreamRecordBatch) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StreamMessageBatch) ProtoMessage() {} +func (*StreamRecordBatch) ProtoMessage() {} -func (x *StreamMessageBatch) ProtoReflect() protoreflect.Message { +func (x *StreamRecordBatch) ProtoReflect() protoreflect.Message { mi := &file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -211,14 +174,14 @@ func (x *StreamMessageBatch) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StreamMessageBatch.ProtoReflect.Descriptor instead. -func (*StreamMessageBatch) Descriptor() ([]byte, []int) { +// Deprecated: Use StreamRecordBatch.ProtoReflect.Descriptor instead. +func (*StreamRecordBatch) Descriptor() ([]byte, []int) { return file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDescGZIP(), []int{1} } -func (x *StreamMessageBatch) GetMessages() []*StreamMessage { +func (x *StreamRecordBatch) GetRecords() []*StreamRecord { if x != nil { - return x.Messages + return x.Records } return nil } @@ -227,23 +190,22 @@ var File_temporal_server_chasm_lib_stream_proto_v1_message_proto protoreflect.Fi const file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDesc = "" + "\n" + - "7temporal/server/chasm/lib/stream/proto/v1/message.proto\x12)temporal.server.chasm.lib.stream.proto.v1\x1a$temporal/api/common/v1/message.proto\"\xad\x03\n" + - "\rStreamMessage\x123\n" + - "\x04body\x18\x01 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\x04body\x12b\n" + - "\bmetadata\x18\x02 \x03(\v2F.temporal.server.chasm.lib.stream.proto.v1.StreamMessage.MetadataEntryR\bmetadata\x12\x14\n" + - "\x05topic\x18\x03 \x01(\tR\x05topic\x12%\n" + - "\x0etopic_sequence\x18\x04 \x01(\x03R\rtopicSequence\x12P\n" + - "\x04kind\x18\x05 \x01(\x0e2<.temporal.server.chasm.lib.stream.proto.v1.StreamMessageKindR\x04kind\x12\x16\n" + - "\x06offset\x18\x06 \x01(\x03R\x06offset\x1a\\\n" + + "7temporal/server/chasm/lib/stream/proto/v1/message.proto\x12)temporal.server.chasm.lib.stream.proto.v1\x1a$temporal/api/common/v1/message.proto\x1a$temporal/api/stream/v1/message.proto\"\xc7\x03\n" + + "\fStreamRecord\x123\n" + + "\x04body\x18\x01 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\x04body\x12a\n" + + "\bmetadata\x18\x02 \x03(\v2E.temporal.server.chasm.lib.stream.proto.v1.StreamRecord.MetadataEntryR\bmetadata\x12\x14\n" + + "\x05topic\x18\x03 \x01(\tR\x05topic\x12\x1a\n" + + "\bsequence\x18\x04 \x01(\x03R\bsequence\x12<\n" + + "\x04kind\x18\x05 \x01(\x0e2(.temporal.api.stream.v1.StreamRecordKindR\x04kind\x12\x16\n" + + "\x06offset\x18\x06 \x01(\x03R\x06offset\x12\x1f\n" + + "\vproducer_id\x18\a \x01(\tR\n" + + "producerId\x12\x18\n" + + "\aattempt\x18\b \x01(\x03R\aattempt\x1a\\\n" + "\rMetadataEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x125\n" + - "\x05value\x18\x02 \x01(\v2\x1f.temporal.api.common.v1.PayloadR\x05value:\x028\x01\"j\n" + - "\x12StreamMessageBatch\x12T\n" + - "\bmessages\x18\x01 \x03(\v28.temporal.server.chasm.lib.stream.proto.v1.StreamMessageR\bmessages*u\n" + - "\x11StreamMessageKind\x12#\n" + - "\x1fSTREAM_MESSAGE_KIND_UNSPECIFIED\x10\x00\x12\x1c\n" + - "\x18STREAM_MESSAGE_KIND_DATA\x10\x01\x12\x1d\n" + - "\x19STREAM_MESSAGE_KIND_FLUSH\x10\x02B>ZZ temporal.api.common.v1.Payload - 3, // 1: temporal.server.chasm.lib.stream.proto.v1.StreamMessage.metadata:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage.MetadataEntry - 0, // 2: temporal.server.chasm.lib.stream.proto.v1.StreamMessage.kind:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessageKind - 1, // 3: temporal.server.chasm.lib.stream.proto.v1.StreamMessageBatch.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage - 4, // 4: temporal.server.chasm.lib.stream.proto.v1.StreamMessage.MetadataEntry.value:type_name -> temporal.api.common.v1.Payload + 3, // 0: temporal.server.chasm.lib.stream.proto.v1.StreamRecord.body:type_name -> temporal.api.common.v1.Payload + 2, // 1: temporal.server.chasm.lib.stream.proto.v1.StreamRecord.metadata:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamRecord.MetadataEntry + 4, // 2: temporal.server.chasm.lib.stream.proto.v1.StreamRecord.kind:type_name -> temporal.api.stream.v1.StreamRecordKind + 0, // 3: temporal.server.chasm.lib.stream.proto.v1.StreamRecordBatch.records:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamRecord + 3, // 4: temporal.server.chasm.lib.stream.proto.v1.StreamRecord.MetadataEntry.value:type_name -> temporal.api.common.v1.Payload 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name @@ -289,14 +250,13 @@ func file_temporal_server_chasm_lib_stream_proto_v1_message_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDesc), len(file_temporal_server_chasm_lib_stream_proto_v1_message_proto_rawDesc)), - NumEnums: 1, + NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_goTypes, DependencyIndexes: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_depIdxs, - EnumInfos: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_enumTypes, MessageInfos: file_temporal_server_chasm_lib_stream_proto_v1_message_proto_msgTypes, }.Build() File_temporal_server_chasm_lib_stream_proto_v1_message_proto = out.File diff --git a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go index 3f20fe64c7b..ab1d3863ef8 100644 --- a/chasm/lib/stream/gen/streampb/v1/request_response.pb.go +++ b/chasm/lib/stream/gen/streampb/v1/request_response.pb.go @@ -133,8 +133,8 @@ type AddMessagesInput struct { StreamId string `protobuf:"bytes,2,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` // Optional. Supplying it skips resolving the stream's current run, which is // a persistence lookup on every call. CreateStream returns it. - RunId string `protobuf:"bytes,9,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` - Messages []*StreamMessage `protobuf:"bytes,3,rep,name=messages,proto3" json:"messages,omitempty"` + RunId string `protobuf:"bytes,9,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + Records []*StreamRecord `protobuf:"bytes,3,rep,name=records,proto3" json:"records,omitempty"` // Idempotency, all optional. Supply a producer identity and sequence, or an // expected offset, or neither and accept at-least-once. ProducerId string `protobuf:"bytes,4,opt,name=producer_id,json=producerId,proto3" json:"producer_id,omitempty"` @@ -198,9 +198,9 @@ func (x *AddMessagesInput) GetRunId() string { return "" } -func (x *AddMessagesInput) GetMessages() []*StreamMessage { +func (x *AddMessagesInput) GetRecords() []*StreamRecord { if x != nil { - return x.Messages + return x.Records } return nil } @@ -546,7 +546,7 @@ type PollMessagesInput struct { FromOffset int64 `protobuf:"varint,3,opt,name=from_offset,json=fromOffset,proto3" json:"from_offset,omitempty"` MaxMessages int32 `protobuf:"varint,4,opt,name=max_messages,json=maxMessages,proto3" json:"max_messages,omitempty"` // Filters by exact topic. Offsets are assigned over the unfiltered stream, so - // next_offset advances past filtered-out messages too. + // next_offset advances past filtered-out records too. Topics []string `protobuf:"bytes,5,rep,name=topics,proto3" json:"topics,omitempty"` // When set and the reader is caught up, block until something arrives, the // stream closes, or the server's long-poll timeout elapses. A timeout returns @@ -637,7 +637,7 @@ func (x *PollMessagesInput) GetWaitNewMessages() bool { type PollMessagesOutput struct { state protoimpl.MessageState `protogen:"open.v1"` - Messages []*StreamMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + Records []*StreamRecord `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` NextOffset int64 `protobuf:"varint,2,opt,name=next_offset,json=nextOffset,proto3" json:"next_offset,omitempty"` HeadOffset int64 `protobuf:"varint,3,opt,name=head_offset,json=headOffset,proto3" json:"head_offset,omitempty"` Closed bool `protobuf:"varint,4,opt,name=closed,proto3" json:"closed,omitempty"` @@ -679,9 +679,9 @@ func (*PollMessagesOutput) Descriptor() ([]byte, []int) { return file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawDescGZIP(), []int{9} } -func (x *PollMessagesOutput) GetMessages() []*StreamMessage { +func (x *PollMessagesOutput) GetRecords() []*StreamRecord { if x != nil { - return x.Messages + return x.Records } return nil } @@ -963,8 +963,8 @@ type AddWorkflowMessagesInput struct { // offset zero. Empty means whichever run is current. OwnerRunId string `protobuf:"bytes,7,opt,name=owner_run_id,json=ownerRunId,proto3" json:"owner_run_id,omitempty"` // Empty means the workflow's default output stream. - StreamName string `protobuf:"bytes,3,opt,name=stream_name,json=streamName,proto3" json:"stream_name,omitempty"` - Messages []*StreamMessage `protobuf:"bytes,4,rep,name=messages,proto3" json:"messages,omitempty"` + StreamName string `protobuf:"bytes,3,opt,name=stream_name,json=streamName,proto3" json:"stream_name,omitempty"` + Records []*StreamRecord `protobuf:"bytes,4,rep,name=records,proto3" json:"records,omitempty"` // Optional idempotency, as on AddMessagesInput. ProducerId string `protobuf:"bytes,5,opt,name=producer_id,json=producerId,proto3" json:"producer_id,omitempty"` Sequence int64 `protobuf:"varint,6,opt,name=sequence,proto3" json:"sequence,omitempty"` @@ -1030,9 +1030,9 @@ func (x *AddWorkflowMessagesInput) GetStreamName() string { return "" } -func (x *AddWorkflowMessagesInput) GetMessages() []*StreamMessage { +func (x *AddWorkflowMessagesInput) GetRecords() []*StreamRecord { if x != nil { - return x.Messages + return x.Records } return nil } @@ -3292,12 +3292,12 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\tstream_id\x18\x02 \x01(\tR\bstreamId\x12X\n" + "\tlifecycle\x18\x03 \x01(\v2:.temporal.server.chasm.lib.stream.proto.v1.StreamLifecycleR\tlifecycle\"+\n" + "\x12CreateStreamOutput\x12\x15\n" + - "\x06run_id\x18\x01 \x01(\tR\x05runId\"\xd6\x02\n" + + "\x06run_id\x18\x01 \x01(\tR\x05runId\"\xd3\x02\n" + "\x10AddMessagesInput\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1b\n" + "\tstream_id\x18\x02 \x01(\tR\bstreamId\x12\x15\n" + - "\x06run_id\x18\t \x01(\tR\x05runId\x12T\n" + - "\bmessages\x18\x03 \x03(\v28.temporal.server.chasm.lib.stream.proto.v1.StreamMessageR\bmessages\x12\x1f\n" + + "\x06run_id\x18\t \x01(\tR\x05runId\x12Q\n" + + "\arecords\x18\x03 \x03(\v27.temporal.server.chasm.lib.stream.proto.v1.StreamRecordR\arecords\x12\x1f\n" + "\vproducer_id\x18\x04 \x01(\tR\n" + "producerId\x12\x1a\n" + "\bsequence\x18\x05 \x01(\x03R\bsequence\x12'\n" + @@ -3335,9 +3335,9 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "fromOffset\x12!\n" + "\fmax_messages\x18\x04 \x01(\x05R\vmaxMessages\x12\x16\n" + "\x06topics\x18\x05 \x03(\tR\x06topics\x12*\n" + - "\x11wait_new_messages\x18\x06 \x01(\bR\x0fwaitNewMessages\"\x9f\x02\n" + - "\x12PollMessagesOutput\x12T\n" + - "\bmessages\x18\x01 \x03(\v28.temporal.server.chasm.lib.stream.proto.v1.StreamMessageR\bmessages\x12\x1f\n" + + "\x11wait_new_messages\x18\x06 \x01(\bR\x0fwaitNewMessages\"\x9c\x02\n" + + "\x12PollMessagesOutput\x12Q\n" + + "\arecords\x18\x01 \x03(\v27.temporal.server.chasm.lib.stream.proto.v1.StreamRecordR\arecords\x12\x1f\n" + "\vnext_offset\x18\x02 \x01(\x03R\n" + "nextOffset\x12\x1f\n" + "\vhead_offset\x18\x03 \x01(\x03R\n" + @@ -3368,7 +3368,7 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\fowner_run_id\x18\x04 \x01(\tR\n" + "ownerRunId\x12\x1f\n" + "\vstream_name\x18\x03 \x01(\tR\n" + - "streamName\"\xaf\x02\n" + + "streamName\"\xac\x02\n" + "\x18AddWorkflowMessagesInput\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x1f\n" + "\vworkflow_id\x18\x02 \x01(\tR\n" + @@ -3376,8 +3376,8 @@ const file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_rawD "\fowner_run_id\x18\a \x01(\tR\n" + "ownerRunId\x12\x1f\n" + "\vstream_name\x18\x03 \x01(\tR\n" + - "streamName\x12T\n" + - "\bmessages\x18\x04 \x03(\v28.temporal.server.chasm.lib.stream.proto.v1.StreamMessageR\bmessages\x12\x1f\n" + + "streamName\x12Q\n" + + "\arecords\x18\x04 \x03(\v27.temporal.server.chasm.lib.stream.proto.v1.StreamRecordR\arecords\x12\x1f\n" + "\vproducer_id\x18\x05 \x01(\tR\n" + "producerId\x12\x1a\n" + "\bsequence\x18\x06 \x01(\x03R\bsequence\"d\n" + @@ -3581,16 +3581,16 @@ var file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_goType (*DeleteStreamRequest)(nil), // 56: temporal.server.chasm.lib.stream.proto.v1.DeleteStreamRequest (*DeleteStreamResponse)(nil), // 57: temporal.server.chasm.lib.stream.proto.v1.DeleteStreamResponse (*StreamLifecycle)(nil), // 58: temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle - (*StreamMessage)(nil), // 59: temporal.server.chasm.lib.stream.proto.v1.StreamMessage + (*StreamRecord)(nil), // 59: temporal.server.chasm.lib.stream.proto.v1.StreamRecord (*v1.Payload)(nil), // 60: temporal.api.common.v1.Payload (*StreamState)(nil), // 61: temporal.server.chasm.lib.stream.proto.v1.StreamState } var file_temporal_server_chasm_lib_stream_proto_v1_request_response_proto_depIdxs = []int32{ 58, // 0: temporal.server.chasm.lib.stream.proto.v1.CreateStreamInput.lifecycle:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamLifecycle - 59, // 1: temporal.server.chasm.lib.stream.proto.v1.AddMessagesInput.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage - 59, // 2: temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutput.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage + 59, // 1: temporal.server.chasm.lib.stream.proto.v1.AddMessagesInput.records:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamRecord + 59, // 2: temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutput.records:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamRecord 60, // 3: temporal.server.chasm.lib.stream.proto.v1.PollMessagesOutput.close_reason:type_name -> temporal.api.common.v1.Payload - 59, // 4: temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesInput.messages:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamMessage + 59, // 4: temporal.server.chasm.lib.stream.proto.v1.AddWorkflowMessagesInput.records:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamRecord 61, // 5: temporal.server.chasm.lib.stream.proto.v1.DescribeStreamOutput.state:type_name -> temporal.server.chasm.lib.stream.proto.v1.StreamState 60, // 6: temporal.server.chasm.lib.stream.proto.v1.CloseStreamInput.reason:type_name -> temporal.api.common.v1.Payload 0, // 7: temporal.server.chasm.lib.stream.proto.v1.CreateStreamRequest.frontend_request:type_name -> temporal.server.chasm.lib.stream.proto.v1.CreateStreamInput diff --git a/chasm/lib/stream/messages.go b/chasm/lib/stream/messages.go index 30ea7b49e5a..2a4417214da 100644 --- a/chasm/lib/stream/messages.go +++ b/chasm/lib/stream/messages.go @@ -8,31 +8,31 @@ import ( "google.golang.org/protobuf/proto" ) -// CollectMessages decodes the batches covering a range and trims to the +// CollectRecords decodes the batches covering a range and trims to the // requested window. Decoding happens only here and only on the batches a read // actually touches; the store never interprets them, and user payloads stay // opaque because the codec runs in the SDK. -func CollectMessages( +func CollectRecords( blobs []*commonpb.DataBlob, startOffsets []int64, from int64, head int64, maxMessages int, topics []string, -) ([]*streamlib.StreamMessage, int64, error) { +) ([]*streamlib.StreamRecord, int64, error) { wanted := make(map[string]struct{}, len(topics)) for _, t := range topics { wanted[t] = struct{}{} } - var out []*streamlib.StreamMessage + var out []*streamlib.StreamRecord next := from for i, blob := range blobs { - var batch streamlib.StreamMessageBatch + var batch streamlib.StreamRecordBatch if err := proto.Unmarshal(blob.GetData(), &batch); err != nil { return nil, 0, err } - for j, msg := range batch.GetMessages() { + for j, msg := range batch.GetRecords() { offset := startOffsets[i] + int64(j) if offset < from || offset >= head { continue @@ -55,65 +55,66 @@ func CollectMessages( return out, next, nil } -// ToAPIMessages converts stored messages to the shape carried on a Workflow -// Task. Control messages are dropped: they steer the log itself and mean -// nothing to a consumer. -func ToAPIMessages(in []*streamlib.StreamMessage) []*streampb.StreamMessage { - out := make([]*streampb.StreamMessage, 0, len(in)) +// ToAPIRecords converts stored records to the shape carried on a Workflow +// Task. Every stored field crosses over except the offset, which the slice +// carries as a range, so a reader in any language sees the record the producer +// wrote, kind and identity included. +func ToAPIRecords(in []*streamlib.StreamRecord) []*streampb.StreamRecord { + out := make([]*streampb.StreamRecord, 0, len(in)) for _, m := range in { - if m.GetKind() != streamlib.STREAM_MESSAGE_KIND_DATA { - continue - } - out = append(out, &streampb.StreamMessage{ - Body: m.GetBody(), - Metadata: m.GetMetadata(), - Topic: m.GetTopic(), - TopicSequence: m.GetTopicSequence(), + out = append(out, &streampb.StreamRecord{ + Body: m.GetBody(), + Metadata: m.GetMetadata(), + Topic: m.GetTopic(), + Kind: m.GetKind(), + ProducerId: m.GetProducerId(), + Attempt: m.GetAttempt(), + Sequence: m.GetSequence(), }) } return out } -// CapByBytes trims a contiguous run of messages to a byte budget and returns +// CapByBytes trims a contiguous run of records to a byte budget and returns // the offset just past the last one kept. // -// It always keeps the first message, however large. Dropping it would leave the +// It always keeps the first record, however large. Dropping it would leave the // cursor unable to advance, and since an unconsumed range schedules a workflow -// task, a stream holding one oversized message would wake the workflow forever +// task, a stream holding one oversized record would wake the workflow forever // without ever delivering anything. func CapByBytes( - messages []*streamlib.StreamMessage, + records []*streamlib.StreamRecord, from int64, maxBytes int, -) ([]*streamlib.StreamMessage, int64) { - if len(messages) == 0 { - return messages, from +) ([]*streamlib.StreamRecord, int64) { + if len(records) == 0 { + return records, from } total := 0 - for i, m := range messages { + for i, m := range records { total += proto.Size(m) if total > maxBytes && i > 0 { - return messages[:i], from + int64(i) + return records[:i], from + int64(i) } } - return messages, from + int64(len(messages)) + return records, from + int64(len(records)) } // checkBatchBytes rejects an append that is too large to store or too large to // ever hand back. // -// The per-message bound matters on its own: a message is never split, so one +// The per-record bound matters on its own: a record is never split, so one // that exceeds a consumer's byte budget can never be delivered, and CapByBytes // would hand it over alone forever rather than reject it. The batch bound is // the storage side, since a batch is written as a single node. -func checkBatchBytes(messages []*streamlib.StreamMessage, limits Limits) error { +func checkBatchBytes(records []*streamlib.StreamRecord, limits Limits) error { total := 0 - for i, m := range messages { + for i, m := range records { size := proto.Size(m) if size > limits.MaxMessageBytes { return serviceerror.NewInvalidArgumentf( - "message %d is %d bytes, over the %d byte limit", i, size, limits.MaxMessageBytes) + "record %d is %d bytes, over the %d byte limit", i, size, limits.MaxMessageBytes) } total += size } diff --git a/chasm/lib/stream/messages_test.go b/chasm/lib/stream/messages_test.go index 41a23f4620f..86aca440b3a 100644 --- a/chasm/lib/stream/messages_test.go +++ b/chasm/lib/stream/messages_test.go @@ -5,15 +5,16 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" + apistreampb "go.temporal.io/api/stream/v1" streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" ) -func sized(n int, bytes int) []*streampb.StreamMessage { - out := make([]*streampb.StreamMessage, n) +func sized(n int, bytes int) []*streampb.StreamRecord { + out := make([]*streampb.StreamRecord, n) for i := range out { - out[i] = &streampb.StreamMessage{ + out[i] = &streampb.StreamRecord{ Body: &commonpb.Payload{Data: make([]byte, bytes)}, - Kind: streampb.STREAM_MESSAGE_KIND_DATA, + Kind: apistreampb.STREAM_RECORD_KIND_DATA, } } return out @@ -45,3 +46,46 @@ func TestCapByBytesOnAnEmptyRun(t *testing.T) { require.Empty(t, messages) require.Equal(t, int64(9), next) } + +// A reader in any language decodes the record the producer wrote, so every +// field the store holds has to come back on the Workflow Task, the kind and the +// producer identity included. A FINISH record is a record like any other to the +// consumer that reads it. +func TestToAPIRecordsCarriesTheRecordAsWritten(t *testing.T) { + stored := []*streampb.StreamRecord{ + { + Body: &commonpb.Payload{Data: []byte("token")}, + Metadata: map[string]*commonpb.Payload{"model": {Data: []byte("m1")}}, + Topic: "tokens", + Kind: apistreampb.STREAM_RECORD_KIND_DATA, + ProducerId: "model-call", + Attempt: 2, + Sequence: 7, + Offset: 41, + }, + { + Topic: "tokens", + Kind: apistreampb.STREAM_RECORD_KIND_FINISH, + ProducerId: "model-call", + Attempt: 2, + Sequence: -1, + Offset: 42, + }, + } + + got := ToAPIRecords(stored) + require.Len(t, got, 2) + + require.Equal(t, "token", string(got[0].GetBody().GetData())) + require.Equal(t, "m1", string(got[0].GetMetadata()["model"].GetData())) + require.Equal(t, "tokens", got[0].GetTopic()) + require.Equal(t, apistreampb.STREAM_RECORD_KIND_DATA, got[0].GetKind()) + require.Equal(t, "model-call", got[0].GetProducerId()) + require.Equal(t, int64(2), got[0].GetAttempt()) + require.Equal(t, int64(7), got[0].GetSequence()) + + require.Equal(t, apistreampb.STREAM_RECORD_KIND_FINISH, got[1].GetKind()) + require.Nil(t, got[1].GetBody()) + require.Equal(t, "model-call", got[1].GetProducerId()) + require.Equal(t, int64(-1), got[1].GetSequence()) +} diff --git a/chasm/lib/stream/proto/v1/message.proto b/chasm/lib/stream/proto/v1/message.proto index ee63cdc7168..d940f769789 100644 --- a/chasm/lib/stream/proto/v1/message.proto +++ b/chasm/lib/stream/proto/v1/message.proto @@ -3,37 +3,40 @@ syntax = "proto3"; package temporal.server.chasm.lib.stream.proto.v1; import "temporal/api/common/v1/message.proto"; +import "temporal/api/stream/v1/message.proto"; option go_package = "go.temporal.io/server/chasm/lib/stream/gen/streampb;streampb"; -enum StreamMessageKind { - STREAM_MESSAGE_KIND_UNSPECIFIED = 0; - STREAM_MESSAGE_KIND_DATA = 1; - // Producer signalling a delivery boundary. Carries no body and consumes an - // offset like any other message. A consumer uses it to end a turn without - // waiting out an idle timeout. - STREAM_MESSAGE_KIND_FLUSH = 2; -} - -message StreamMessage { +// The stored shape of temporal.api.stream.v1.StreamRecord plus the offset a +// read assigns. Field for field the public record, so one crosses the frontend +// in either direction without translation. +message StreamRecord { temporal.api.common.v1.Payload body = 1; - // Producer-supplied provenance. The server does not populate this today. + // Producer-supplied provenance, stored as sent. map metadata = 2; string topic = 3; - // Position within this topic. The global offset orders the whole stream; - // this lets a consumer reason about one topic without decoding the rest. - int64 topic_sequence = 4; - StreamMessageKind kind = 5; + // The producer's position within its attempt, or -1 when unnumbered. Stored + // as sent; the global offset is what orders the stream. + int64 sequence = 4; + // Settled to DATA on append when left unspecified, so a retry hashes the + // same bytes and a reader never sees the zero value. + temporal.api.stream.v1.StreamRecordKind kind = 5; // Position in the whole stream, set on read and never stored. A consumer - // that resumes at message granularity needs it, and a topic-filtered read + // that resumes at record granularity needs it, and a topic-filtered read // leaves gaps that make it underivable from the response alone. int64 offset = 6; + // Who wrote the record. Empty when the owning Workflow did: its publish + // command clears the field, whatever the worker sent. + string producer_id = 7; + // The producer's attempt, stored as sent. Readers treat a later attempt by + // the same producer as superseding what the earlier one wrote. + int64 attempt = 8; } // One append is one batch, and one batch is one data node. The server stores // this serialized and opaque; it decodes only to trim a partial first page or // to apply a topic filter. -message StreamMessageBatch { - repeated StreamMessage messages = 1; +message StreamRecordBatch { + repeated StreamRecord records = 1; } diff --git a/chasm/lib/stream/proto/v1/request_response.proto b/chasm/lib/stream/proto/v1/request_response.proto index 03bfead3b87..66e5dadb63f 100644 --- a/chasm/lib/stream/proto/v1/request_response.proto +++ b/chasm/lib/stream/proto/v1/request_response.proto @@ -29,7 +29,7 @@ message AddMessagesInput { // Optional. Supplying it skips resolving the stream's current run, which is // a persistence lookup on every call. CreateStream returns it. string run_id = 9; - repeated StreamMessage messages = 3; + repeated StreamRecord records = 3; // Idempotency, all optional. Supply a producer identity and sequence, or an // expected offset, or neither and accept at-least-once. @@ -92,7 +92,7 @@ message PollMessagesInput { int64 from_offset = 3; int32 max_messages = 4; // Filters by exact topic. Offsets are assigned over the unfiltered stream, so - // next_offset advances past filtered-out messages too. + // next_offset advances past filtered-out records too. repeated string topics = 5; // When set and the reader is caught up, block until something arrives, the @@ -102,7 +102,7 @@ message PollMessagesInput { } message PollMessagesOutput { - repeated StreamMessage messages = 1; + repeated StreamRecord records = 1; int64 next_offset = 2; int64 head_offset = 3; bool closed = 4; @@ -160,7 +160,7 @@ message AddWorkflowMessagesInput { // Empty means the workflow's default output stream. string stream_name = 3; - repeated StreamMessage messages = 4; + repeated StreamRecord records = 4; // Optional idempotency, as on AddMessagesInput. string producer_id = 5; int64 sequence = 6; diff --git a/chasm/lib/stream/service/events.go b/chasm/lib/stream/service/events.go index 69f0b104a51..d496181ee3a 100644 --- a/chasm/lib/stream/service/events.go +++ b/chasm/lib/stream/service/events.go @@ -38,29 +38,29 @@ func (streamSubscribedEventDefinition) CherryPick( return hsm.ErrNotCherryPickable } -// streamMessagesAddedEventDefinition tells the history service how to treat +// streamRecordsAppendedEventDefinition tells the history service how to treat // the event a publish command writes. // // Like the subscription event it applies nothing: the stream's frontier is // CHASM state committed with the workflow task, and the bodies are in the // stream component. What the event carries is the offset range, which is what // lets anyone reading History find the batch without History having held it. -type streamMessagesAddedEventDefinition struct{} +type streamRecordsAppendedEventDefinition struct{} -func (streamMessagesAddedEventDefinition) Type() enumspb.EventType { - return enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED +func (streamRecordsAppendedEventDefinition) Type() enumspb.EventType { + return enumspb.EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED } // A workflow publishing to its own stream has nothing to be woken about. -func (streamMessagesAddedEventDefinition) IsWorkflowTaskTrigger() bool { return false } +func (streamRecordsAppendedEventDefinition) IsWorkflowTaskTrigger() bool { return false } -func (streamMessagesAddedEventDefinition) Apply(*hsm.Node, *historypb.HistoryEvent) error { +func (streamRecordsAppendedEventDefinition) Apply(*hsm.Node, *historypb.HistoryEvent) error { return nil } // A command event, so never reapplied onto another branch. Reapplying would // claim offsets in a log the new branch never wrote to. -func (streamMessagesAddedEventDefinition) CherryPick( +func (streamRecordsAppendedEventDefinition) CherryPick( *hsm.Node, *historypb.HistoryEvent, map[enumspb.ResetReapplyExcludeType]struct{}, @@ -74,5 +74,5 @@ func RegisterEventDefinitions(reg *hsm.Registry) error { if err := reg.RegisterEventDefinition(streamSubscribedEventDefinition{}); err != nil { return err } - return reg.RegisterEventDefinition(streamMessagesAddedEventDefinition{}) + return reg.RegisterEventDefinition(streamRecordsAppendedEventDefinition{}) } diff --git a/chasm/lib/stream/service/handler.go b/chasm/lib/stream/service/handler.go index a788e608bf9..2d716ce671c 100644 --- a/chasm/lib/stream/service/handler.go +++ b/chasm/lib/stream/service/handler.go @@ -146,8 +146,8 @@ func (h *handler) AddMessages( req *streampb.AddMessagesRequest, ) (*streampb.AddMessagesResponse, error) { in := req.GetFrontendRequest() - if len(in.GetMessages()) == 0 { - return nil, serviceerror.NewInvalidArgument("no messages to append") + if len(in.GetRecords()) == 0 { + return nil, serviceerror.NewInvalidArgument("no records to append") } ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) @@ -156,7 +156,7 @@ func (h *handler) AddMessages( // whatever offset it lands at. A retried sequence is answered by the // producer table, not by a pin on the head. addReq := stream.AddMessagesRequest{ - Messages: in.GetMessages(), + Records: in.GetRecords(), ProducerID: in.GetProducerId(), Sequence: in.GetSequence(), Limits: h.limitsFor(req.GetNamespaceId()), @@ -200,14 +200,16 @@ func (h *handler) AddWorkflowMessages( req *streampb.AddWorkflowMessagesRequest, ) (*streampb.AddWorkflowMessagesResponse, error) { in := req.GetFrontendRequest() - if len(in.GetMessages()) == 0 { - return nil, serviceerror.NewInvalidArgument("no messages to append") + if len(in.GetRecords()) == 0 { + return nil, serviceerror.NewInvalidArgument("no records to append") } ctx = h.withCallerInfo(ctx, req.GetNamespaceId()) + // The records go in as sent, producer identity included. Who is writing is + // the caller's claim to make; the store only answers whether it fits. name := ownedStreamName(in.GetStreamName()) addReq := stream.AddMessagesRequest{ - Messages: in.GetMessages(), + Records: in.GetRecords(), ProducerID: in.GetProducerId(), Sequence: in.GetSequence(), Limits: h.limitsFor(req.GetNamespaceId()), @@ -595,7 +597,7 @@ func (h *handler) PollWorkflowMessages( // in the component, so the frontier and the bytes it was served with cannot // disagree. // -// The reader is advanced only over offsets that were examined. CollectMessages +// The reader is advanced only over offsets that were examined. CollectRecords // steps past every message it filtered out, so a filtered page that matched // nothing still moves the reader; a window whose batches stop short of its end // must not be reported as read to the end. @@ -611,12 +613,12 @@ func formatWindow(w stream.Window, req stream.WindowRequest) (*streampb.PollMess return out, nil } - messages, next, err := stream.CollectMessages( + records, next, err := stream.CollectRecords( w.Blobs, w.Starts, req.From, w.To, w.Limit, req.Topics) if err != nil { return nil, err } - out.Messages = messages + out.Records = records out.NextOffset = next return out, nil } diff --git a/chasm/lib/stream/service/handler_test.go b/chasm/lib/stream/service/handler_test.go index 2d5789004cd..b87ef91c25e 100644 --- a/chasm/lib/stream/service/handler_test.go +++ b/chasm/lib/stream/service/handler_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" + apistreampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm/lib/stream" streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "google.golang.org/protobuf/proto" @@ -13,15 +14,15 @@ import ( func batchBlob(t *testing.T, topic string, bodies ...string) *commonpb.DataBlob { t.Helper() - messages := make([]*streampb.StreamMessage, len(bodies)) + messages := make([]*streampb.StreamRecord, len(bodies)) for i, b := range bodies { - messages[i] = &streampb.StreamMessage{ + messages[i] = &streampb.StreamRecord{ Body: &commonpb.Payload{Data: []byte(b)}, Topic: topic, - Kind: streampb.STREAM_MESSAGE_KIND_DATA, + Kind: apistreampb.STREAM_RECORD_KIND_DATA, } } - data, err := proto.Marshal(&streampb.StreamMessageBatch{Messages: messages}) + data, err := proto.Marshal(&streampb.StreamRecordBatch{Records: messages}) require.NoError(t, err) return &commonpb.DataBlob{EncodingType: enumspb.ENCODING_TYPE_PROTO3, Data: data} } @@ -40,7 +41,7 @@ func TestFormatWindowAdvancesOnlyOverExaminedOffsets(t *testing.T) { } out, err := formatWindow(w, stream.WindowRequest{From: 0, Topics: []string{"nothing"}}) require.NoError(t, err) - require.Empty(t, out.GetMessages()) + require.Empty(t, out.GetRecords()) require.Equal(t, int64(3), out.GetNextOffset(), "offsets 3 and 4 were never read, so the reader must not be moved past them") @@ -49,6 +50,6 @@ func TestFormatWindowAdvancesOnlyOverExaminedOffsets(t *testing.T) { w.Blobs = []*commonpb.DataBlob{batchBlob(t, "a", "m0", "m1", "m2", "m3", "m4")} out, err = formatWindow(w, stream.WindowRequest{From: 0, Topics: []string{"nothing"}}) require.NoError(t, err) - require.Empty(t, out.GetMessages()) + require.Empty(t, out.GetRecords()) require.Equal(t, int64(5), out.GetNextOffset()) } diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index 552832085fb..6887b06c86f 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -10,6 +10,7 @@ import ( commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" "go.temporal.io/api/serviceerror" + apistreampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm" streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "go.temporal.io/server/common" @@ -58,7 +59,7 @@ type NewStreamRequest struct { } type AddMessagesRequest struct { - Messages []*streampb.StreamMessage + Records []*streampb.StreamRecord // Optional idempotency. A producer supplies either an identity and // sequence, or an expected offset, or neither and accepts at-least-once. @@ -142,28 +143,28 @@ func (s *Stream) AddMessages( if s.State.Closed { return AddMessagesResult{}, serviceerror.NewFailedPrecondition("stream is closed") } - if len(req.Messages) == 0 { - return AddMessagesResult{}, serviceerror.NewInvalidArgument("no messages to append") + if len(req.Records) == 0 { + return AddMessagesResult{}, serviceerror.NewInvalidArgument("no records to append") } - if len(req.Messages) > MaxMessagesPerBatch { + if len(req.Records) > MaxRecordsPerBatch { return AddMessagesResult{}, serviceerror.NewInvalidArgumentf( - "batch of %d exceeds the limit of %d messages", len(req.Messages), MaxMessagesPerBatch) + "batch of %d exceeds the limit of %d records", len(req.Records), MaxRecordsPerBatch) } limits := req.Limits.withDefaults() - if err := checkBatchBytes(req.Messages, limits); err != nil { + if err := checkBatchBytes(req.Records, limits); err != nil { return AddMessagesResult{}, err } - // A message with no kind is data. Delivery to a workflow drops anything - // that is not, so a producer leaving the field at its zero value would get - // an offset for a message no subscriber ever sees. Settled before the batch - // is marshalled, so a retry hashes the same bytes. - for _, m := range req.Messages { - if m.GetKind() == streampb.STREAM_MESSAGE_KIND_UNSPECIFIED { - m.Kind = streampb.STREAM_MESSAGE_KIND_DATA + // A record with no kind is data. A producer that never heard of kinds + // leaves the field at its zero value, and every reader would otherwise have + // to agree on what that means. Settled before the batch is marshalled, so a + // retry hashes the same bytes. + for _, m := range req.Records { + if m.GetKind() == apistreampb.STREAM_RECORD_KIND_UNSPECIFIED { + m.Kind = apistreampb.STREAM_RECORD_KIND_DATA } } - blob, err := marshalBatch(req.Messages) + blob, err := marshalBatch(req.Records) if err != nil { return AddMessagesResult{}, err } @@ -180,7 +181,7 @@ func (s *Stream) AddMessages( if err := s.checkProducerRoom(req.ProducerID, limits.MaxProducersPerStream); err != nil { return AddMessagesResult{}, err } - if err := s.checkBudget(int64(len(req.Messages)), int64(len(blob.Data))); err != nil { + if err := s.checkBudget(int64(len(req.Records)), int64(len(blob.Data))); err != nil { return AddMessagesResult{}, err } @@ -189,7 +190,7 @@ func (s *Stream) AddMessages( // History still refers to. Refusing the write is the honest half of that // choice: capacity may constrain what is admitted, and may not quietly take // back a workflow's ability to replay a decision it already made. - if err := s.checkCapRoom(int64(len(req.Messages))); err != nil { + if err := s.checkCapRoom(int64(len(req.Records))); err != nil { return AddMessagesResult{}, err } @@ -199,7 +200,7 @@ func (s *Stream) AddMessages( } first := s.State.HeadOffset - count := int64(len(req.Messages)) + count := int64(len(req.Records)) if s.Batches == nil { s.Batches = make(chasm.Map[int64, *commonpb.DataBlob]) @@ -310,7 +311,7 @@ func (s *Stream) checkBudget(count int64, size int64) error { if limit := budget.GetMaxItems(); limit > 0 && s.State.HeadOffset+count > limit { return serviceerror.NewResourceExhaustedf( enumspb.RESOURCE_EXHAUSTED_CAUSE_PERSISTENCE_STORAGE_LIMIT, - "stream holds %d of its budget of %d messages; the append of %d does not fit", + "stream holds %d of its budget of %d records; the append of %d does not fit", s.State.HeadOffset, limit, count) } if limit := budget.GetMaxBytes(); limit > 0 && s.State.AppendedBytes+size > limit { @@ -433,7 +434,7 @@ func (s *Stream) Truncate(_ chasm.MutableContext, newBase int64) error { if floor, holder, pinned := s.replayFloor(); pinned && newBase > floor { return serviceerror.NewFailedPreconditionf( "cannot truncate to %d: consumer %q still depends on offset %d and above "+ - "to replay; deregister it first if those messages are no longer needed", + "to replay; deregister it first if those records are no longer needed", newBase, holder, floor) } s.State.BaseOffset = newBase @@ -532,8 +533,8 @@ func (s *Stream) ReadWindow(ctx chasm.Context, req WindowRequest) (Window, error return w, nil } - // Clip to what the caller can actually be given. One offset is one message, - // so the bound is exact. Without it a poll for a single message off a long + // Clip to what the caller can actually be given. One offset is one record, + // so the bound is exact. Without it a poll for a single record off a long // stream materialises every batch to the head before trimming. w.To = min(s.State.HeadOffset, req.From+int64(limit)) blobs, starts, err := s.ReadBatches(ctx, req.From, w.To, 0) @@ -580,7 +581,7 @@ func (s *Stream) ReadBatches( return blobs, starts, nil } -// applyCap advances the readable floor when the stream is over its message cap. +// applyCap advances the readable floor when the stream is over its record cap. // Evaluated at the end of a successful append rather than by a sweeper: the // append transition is already writing, so folding the check into it costs // nothing and keeps the cap tight instead of eventually true. @@ -597,7 +598,7 @@ func (s *Stream) applyCap() { // Clamped rather than refused, because refusing belongs to admission and // has already happened: checkCapRoom turned away the append that would have // needed this. Reaching the clamp means a consumer registered after the - // messages were written, and keeping its bytes is still the right answer. + // records were written, and keeping its bytes is still the right answer. if floor, _, pinned := s.replayFloor(); pinned && newBase > floor { newBase = floor } @@ -611,9 +612,9 @@ func (s *Stream) applyCap() { // checkCapRoom refuses an append the cap could only absorb by dropping bytes an // active consumer still needs. // -// A capped stream with no consumer behaves as before: the oldest messages go. +// A capped stream with no consumer behaves as before: the oldest records go. // The refusal only arrives when honouring the cap and honouring a recorded -// consumption are the same messages, and it names the consumer so the operator +// consumption are the same records, and it names the consumer so the operator // knows what to do about it. func (s *Stream) checkCapRoom(count int64) error { maxItems := s.State.GetLifecycle().GetMaxItems() @@ -630,9 +631,9 @@ func (s *Stream) checkCapRoom(count int64) error { } return serviceerror.NewResourceExhaustedf( enumspb.RESOURCE_EXHAUSTED_CAUSE_UNSPECIFIED, - "stream is at its cap of %d messages and consumer %q still depends on "+ + "stream is at its cap of %d records and consumer %q still depends on "+ "offset %d and above to replay; the append would have to delete those "+ - "messages to make room", + "records to make room", maxItems, holder, floor) } @@ -729,7 +730,7 @@ func (s *Stream) AdvanceConsumer(_ chasm.MutableContext, consumerID string, offs } // DeregisterConsumer releases the floor a consumer was holding, so retention -// and the message cap can reach its messages again. The entry stays, inactive, +// and the record cap can reach its records again. The entry stays, inactive, // so the same consumer coming back is refused if the stream has moved past // what its History refers to. func (s *Stream) DeregisterConsumer(_ chasm.MutableContext, consumerID string) { @@ -745,13 +746,13 @@ func (s *Stream) ForgetConsumer(_ chasm.MutableContext, consumerID string) { delete(s.State.Consumers, consumerID) } -func marshalBatch(messages []*streampb.StreamMessage) (*commonpb.DataBlob, error) { +func marshalBatch(records []*streampb.StreamRecord) (*commonpb.DataBlob, error) { // The serialized batch is also the producer's deduplication fingerprint, and // protobuf map iteration order is not stable. A record carrying payload or - // message metadata would otherwise hash differently on a retry and be + // record metadata would otherwise hash differently on a retry and be // refused as a conflicting duplicate of itself. data, err := (proto.MarshalOptions{Deterministic: true}).Marshal( - &streampb.StreamMessageBatch{Messages: messages}) + &streampb.StreamRecordBatch{Records: records}) if err != nil { return nil, err } diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go index 180ecd87565..256e0f0a8cf 100644 --- a/chasm/lib/stream/stream_test.go +++ b/chasm/lib/stream/stream_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" "go.temporal.io/api/serviceerror" + apistreampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm" streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "google.golang.org/protobuf/types/known/durationpb" @@ -27,12 +28,12 @@ func newTestStream(t *testing.T) *Stream { } } -func msgs(bodies ...string) []*streampb.StreamMessage { - out := make([]*streampb.StreamMessage, len(bodies)) +func msgs(bodies ...string) []*streampb.StreamRecord { + out := make([]*streampb.StreamRecord, len(bodies)) for i, b := range bodies { - out[i] = &streampb.StreamMessage{ + out[i] = &streampb.StreamRecord{ Body: &commonpb.Payload{Data: []byte(b)}, - Kind: streampb.STREAM_MESSAGE_KIND_DATA, + Kind: apistreampb.STREAM_RECORD_KIND_DATA, } } return out @@ -41,13 +42,13 @@ func msgs(bodies ...string) []*streampb.StreamMessage { func TestAddMessagesAssignsContiguousOffsets(t *testing.T) { s := newTestStream(t) - first, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) + first, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c")}) require.NoError(t, err) require.Equal(t, int64(0), first.FirstOffset) require.Equal(t, int64(3), first.Count) require.Equal(t, int64(3), first.NextOffset) - second, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) + second, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("d", "e")}) require.NoError(t, err) require.Equal(t, int64(3), second.FirstOffset) require.Equal(t, int64(5), second.NextOffset) @@ -57,7 +58,7 @@ func TestAddMessagesAssignsContiguousOffsets(t *testing.T) { func TestAddMessagesWritesTheBatchIntoTheComponent(t *testing.T) { s := newTestStream(t) - res, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + res, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) require.NotEmpty(t, res.Blob.Data) @@ -71,7 +72,7 @@ func TestAddMessagesWritesTheBatchIntoTheComponent(t *testing.T) { func TestDedupReturnsOriginalOffsets(t *testing.T) { s := newTestStream(t) - req := AddMessagesRequest{Messages: msgs("a", "b"), ProducerID: "p1", Sequence: 1} + req := AddMessagesRequest{Records: msgs("a", "b"), ProducerID: "p1", Sequence: 1} first, err := s.AddMessages(nil, req) require.NoError(t, err) @@ -98,8 +99,8 @@ func TestDedupIgnoresPayloadMetadataMapOrder(t *testing.T) { }, } result, err := s.AddMessages(nil, AddMessagesRequest{ - Messages: []*streampb.StreamMessage{{ - Kind: streampb.STREAM_MESSAGE_KIND_DATA, + Records: []*streampb.StreamRecord{{ + Kind: apistreampb.STREAM_RECORD_KIND_DATA, Body: body, Metadata: map[string]*commonpb.Payload{"attempt": body, "checkpoint": body}, }}, @@ -115,14 +116,14 @@ func TestDedupIgnoresPayloadMetadataMapOrder(t *testing.T) { func TestDedupRejectsDifferentContent(t *testing.T) { s := newTestStream(t) _, err := s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("a"), ProducerID: "p1", Sequence: 1, + Records: msgs("a"), ProducerID: "p1", Sequence: 1, }) require.NoError(t, err) // Returning the recorded offsets here would report success while dropping // the caller's data, which is worse than failing. _, err = s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("different"), ProducerID: "p1", Sequence: 1, + Records: msgs("different"), ProducerID: "p1", Sequence: 1, }) require.Error(t, err) require.Contains(t, err.Error(), "different content") @@ -130,12 +131,12 @@ func TestDedupRejectsDifferentContent(t *testing.T) { func TestExpectedOffsetMismatchReportsHead(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a")}) require.NoError(t, err) stale := int64(0) _, err = s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("b"), ExpectedOffset: &stale, + Records: msgs("b"), ExpectedOffset: &stale, }) require.Error(t, err) require.Contains(t, err.Error(), "stream head is 1") @@ -146,13 +147,13 @@ func TestFinishWritingFencesOneProducerOnly(t *testing.T) { require.NoError(t, s.FinishWriting(nil, "p1")) _, err := s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("a"), ProducerID: "p1", Sequence: 1, + Records: msgs("a"), ProducerID: "p1", Sequence: 1, }) require.Error(t, err) // Another producer is unaffected: finishing is per-producer, not a close. _, err = s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("a"), ProducerID: "p2", Sequence: 1, + Records: msgs("a"), ProducerID: "p2", Sequence: 1, }) require.NoError(t, err) require.False(t, s.State.Closed) @@ -162,7 +163,7 @@ func TestCloseRejectsFurtherAppends(t *testing.T) { s := newTestStream(t) s.Close(time.Now(), nil) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a")}) require.Error(t, err) var precondition *serviceerror.FailedPrecondition require.ErrorAs(t, err, &precondition) @@ -170,9 +171,9 @@ func TestCloseRejectsFurtherAppends(t *testing.T) { func TestReadSpansBatchesAndStartsAtTheBatchHoldingTheOffset(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c")}) require.NoError(t, err) - _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) + _, err = s.AddMessages(nil, AddMessagesRequest{Records: msgs("d", "e")}) require.NoError(t, err) require.Len(t, s.Batches, 2) @@ -193,9 +194,9 @@ func TestReadSpansBatchesAndStartsAtTheBatchHoldingTheOffset(t *testing.T) { func TestReclaimDropsOnlyBatchesFullyBelowTheFloor(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c")}) require.NoError(t, err) - _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("d", "e")}) + _, err = s.AddMessages(nil, AddMessagesRequest{Records: msgs("d", "e")}) require.NoError(t, err) // The floor lands mid-batch, so that batch stays: offsets above the floor @@ -212,7 +213,7 @@ func TestReclaimDropsOnlyBatchesFullyBelowTheFloor(t *testing.T) { func TestTruncateStopsAtAnActiveConsumersReplayFloor(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ @@ -233,7 +234,7 @@ func TestTruncateStopsAtAnActiveConsumersReplayFloor(t *testing.T) { func TestTruncateBounds(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) err = s.Truncate(nil, 1) @@ -249,7 +250,7 @@ func TestCapTruncatesInline(t *testing.T) { s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 4} for range 4 { - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) } @@ -267,14 +268,14 @@ func TestCapRefusesAnAppendItCouldOnlyAbsorbByDroppingReadRecords(t *testing.T) }) require.NoError(t, err) - _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err = s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.ErrorContains(t, err, "still depends on offset 0") require.Equal(t, int64(0), s.State.HeadOffset, "a refused append writes nothing") // Nothing is stuck. The consumer going away is what makes room, and it is // something someone does rather than something that happens quietly. s.DeregisterConsumer(nil, "wf-1") - _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err = s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) require.Equal(t, int64(2), s.State.BaseOffset, "the cap applies once nobody needs the bytes") } @@ -299,7 +300,7 @@ func TestCloseSchedulesRetentionOnlyWhenConfigured(t *testing.T) { func TestRegisterConsumerPinsFromWhereItSubscribed(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) // Subscribing at 2 says nothing about offsets 0 and 1, so those stay @@ -316,7 +317,7 @@ func TestRegisterConsumerPinsFromWhereItSubscribed(t *testing.T) { func TestAdvanceConsumerTracksWhereAConsumerHasReached(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, @@ -333,7 +334,7 @@ func TestAdvanceConsumerTracksWhereAConsumerHasReached(t *testing.T) { // recorded range has to stay re-readable. func TestAdvanceConsumerNeverRewinds(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, @@ -348,7 +349,7 @@ func TestAdvanceConsumerNeverRewinds(t *testing.T) { func TestRegisterConsumerRejectsAnOffsetBelowTheFloor(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) err = s.Truncate(nil, 2) require.NoError(t, err) @@ -363,7 +364,7 @@ func TestRegisterConsumerRejectsAnOffsetBelowTheFloor(t *testing.T) { // consumer cannot rewind its own floor by subscribing again. func TestRegisterConsumerTwiceKeepsThePin(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, @@ -381,7 +382,7 @@ func TestRegisterConsumerTwiceKeepsThePin(t *testing.T) { func TestDeregisterConsumerReleasesThePin(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 1, @@ -399,7 +400,7 @@ func TestMessageCapStillAppliesWithNoConsumerToProtect(t *testing.T) { s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} for range 3 { - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) } @@ -416,7 +417,7 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { s := newTestStream(t) s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, @@ -440,7 +441,7 @@ func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { s.DeregisterConsumer(nil, "workflow:output") for range 3 { - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) } require.Equal(t, int64(4), s.State.BaseOffset) @@ -451,7 +452,7 @@ func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { // depends on those offsets again. func TestReregisteringBelowTheFloorIsRefused(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, @@ -476,7 +477,7 @@ func TestStreamProducerTableIsBounded(t *testing.T) { for i := range MaxProducersPerStream { _, err := s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("m"), + Records: msgs("m"), ProducerID: fmt.Sprintf("p%d", i), Sequence: 1, }) @@ -484,7 +485,7 @@ func TestStreamProducerTableIsBounded(t *testing.T) { } _, err := s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("one too many"), + Records: msgs("one too many"), ProducerID: "p-over", Sequence: 1, }) @@ -494,7 +495,7 @@ func TestStreamProducerTableIsBounded(t *testing.T) { // A producer already tracked keeps working, so the cap cannot wedge the // producers that filled it. _, err = s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("still fine"), + Records: msgs("still fine"), ProducerID: "p0", Sequence: 2, }) @@ -502,7 +503,7 @@ func TestStreamProducerTableIsBounded(t *testing.T) { // An anonymous append is never blocked by the table. _, err = s.AddMessages(nil, AddMessagesRequest{ - Messages: msgs("anon"), + Records: msgs("anon"), }) require.NoError(t, err) } @@ -537,24 +538,24 @@ func TestStreamConsumerTableIsBounded(t *testing.T) { // offset and never be seen by a subscriber. func TestAddMessagesTreatsAnUnsetKindAsData(t *testing.T) { s := newTestStream(t) - unset := []*streampb.StreamMessage{ + unset := []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("a")}}, {Body: &commonpb.Payload{Data: []byte("b")}}, } _, err := s.AddMessages(nil, AddMessagesRequest{ - Messages: unset, ProducerID: "p", Sequence: 1, + Records: unset, ProducerID: "p", Sequence: 1, }) require.NoError(t, err) blobs, starts, err := s.ReadBatches(nil, 0, 2, 0) require.NoError(t, err) - collected, _, err := CollectMessages(blobs, starts, 0, 2, 10, nil) + collected, _, err := CollectRecords(blobs, starts, 0, 2, 10, nil) require.NoError(t, err) - require.Len(t, ToAPIMessages(collected), 2, "both messages must reach a subscriber") + require.Len(t, ToAPIRecords(collected), 2, "both messages must reach a subscriber") // The retry carries the same unset kind and has to hash the same. retry, err := s.AddMessages(nil, AddMessagesRequest{ - Messages: []*streampb.StreamMessage{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("a")}}, {Body: &commonpb.Payload{Data: []byte("b")}}, }, @@ -571,23 +572,23 @@ func TestBudgetRefusesAnAppendThatDoesNotFit(t *testing.T) { s := newTestStream(t) s.State.Budget = &streampb.StreamBudget{MaxItems: 3} - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) - _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("c", "d")}) + _, err = s.AddMessages(nil, AddMessagesRequest{Records: msgs("c", "d")}) var exhausted *serviceerror.ResourceExhausted require.ErrorAs(t, err, &exhausted) require.Equal(t, int64(2), s.State.HeadOffset, "a refused append writes nothing") // The last slot is still there for an append that fits. - _, err = s.AddMessages(nil, AddMessagesRequest{Messages: msgs("c")}) + _, err = s.AddMessages(nil, AddMessagesRequest{Records: msgs("c")}) require.NoError(t, err) bytesOnly := newTestStream(t) bytesOnly.State.Budget = &streampb.StreamBudget{MaxBytes: 16} - _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Messages: msgs("small")}) + _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Records: msgs("small")}) require.NoError(t, err) - _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Messages: msgs("another one")}) + _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Records: msgs("another one")}) require.ErrorAs(t, err, &exhausted) require.Equal(t, int64(1), bytesOnly.State.HeadOffset) } @@ -598,7 +599,7 @@ func TestBudgetRefusesAnAppendThatDoesNotFit(t *testing.T) { // a floor that may already be below the stream's base. func TestRegisterConsumerReplacesAnEntryFromAnotherRun(t *testing.T) { s := newTestStream(t) - _, err := s.AddMessages(nil, AddMessagesRequest{Messages: msgs("a", "b", "c", "d")}) + _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b", "c", "d")}) require.NoError(t, err) _, err = s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:wf/run-1", WorkflowID: "wf", RunID: "run-1", Offset: 0, External: true, @@ -637,9 +638,9 @@ func TestNotifyCoalescesIntoOneOutstandingTask(t *testing.T) { }) require.NoError(t, err) - _, err = s.AddMessages(mctx, AddMessagesRequest{Messages: msgs("a")}) + _, err = s.AddMessages(mctx, AddMessagesRequest{Records: msgs("a")}) require.NoError(t, err) - _, err = s.AddMessages(mctx, AddMessagesRequest{Messages: msgs("b")}) + _, err = s.AddMessages(mctx, AddMessagesRequest{Records: msgs("b")}) require.NoError(t, err) require.Len(t, mctx.Tasks, 1, "the second append rides the task the first scheduled") @@ -648,7 +649,7 @@ func TestNotifyCoalescesIntoOneOutstandingTask(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(2), state.GetHeadOffset(), "the task sees every append that landed before it") - _, err = s.AddMessages(mctx, AddMessagesRequest{Messages: msgs("c")}) + _, err = s.AddMessages(mctx, AddMessagesRequest{Records: msgs("c")}) require.NoError(t, err) require.Len(t, mctx.Tasks, 2) } diff --git a/chasm/lib/workflow/stream_commands.go b/chasm/lib/workflow/stream_commands.go index a6194a96dac..22400d2f7d8 100644 --- a/chasm/lib/workflow/stream_commands.go +++ b/chasm/lib/workflow/stream_commands.go @@ -36,7 +36,7 @@ func StreamAdmissionFailure(cause enumspb.WorkflowTaskFailedCause, err error) er return err } -// handleAddStreamMessagesCommand appends to a stream the workflow owns. +// handleAppendStreamRecordsCommand appends to a stream the workflow owns. // // The stream is a co-located subcomponent, so the batch and the frontier land // in the workflow task's own commit: no extra transition, no cross-execution @@ -44,7 +44,7 @@ func StreamAdmissionFailure(cause enumspb.WorkflowTaskFailedCause, err error) er // // The offsets are known here, unlike a subscription's, so the event is written // here too rather than reserved and filled in later. -func handleAddStreamMessagesCommand( +func handleAppendStreamRecordsCommand( chasmCtx chasm.MutableContext, wf *Workflow, validator Validator, @@ -52,16 +52,16 @@ func handleAddStreamMessagesCommand( opts CommandHandlerOptions, limits stream.Limits, ) error { - badAttributes := enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES - attrs := command.GetAddStreamMessagesCommandAttributes() + badAttributes := enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES + attrs := command.GetAppendStreamRecordsCommandAttributes() if attrs == nil { return FailWorkflowTaskError{ - Cause: badAttributes, Message: "AddStreamMessagesCommandAttributes is not set", + Cause: badAttributes, Message: "AppendStreamRecordsCommandAttributes is not set", } } - if len(attrs.GetMessages()) == 0 { + if len(attrs.GetRecords()) == 0 { return FailWorkflowTaskError{ - Cause: badAttributes, Message: "AddStreamMessages command carries no messages", + Cause: badAttributes, Message: "AppendStreamRecords command carries no records", } } @@ -70,13 +70,13 @@ func handleAddStreamMessagesCommand( // persistence error out of a task the worker will replay and re-issue // forever, with nothing naming the batch as the cause. size := 0 - for _, m := range attrs.GetMessages() { + for _, m := range attrs.GetRecords() { size += m.Size() } if !validator.IsValidPayloadSize(size) { return FailWorkflowTaskError{ Cause: enumspb.WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE, - Message: "AddStreamMessagesCommandAttributes.Messages exceeds size limit", + Message: "AppendStreamRecordsCommandAttributes.Records exceeds size limit", TerminateWorkflow: true, } } @@ -92,8 +92,8 @@ func handleAddStreamMessagesCommand( } result, err := s.AddMessages(chasmCtx, stream.AddMessagesRequest{ - Messages: toLibraryMessages(attrs.GetMessages()), - Limits: limits, + Records: toLibraryRecords(attrs.GetRecords()), + Limits: limits, }) if err != nil { return StreamAdmissionFailure(badAttributes, err) @@ -102,7 +102,7 @@ func handleAddStreamMessagesCommand( // the command was still issued and the event is what the replaying worker // matches it against. It names the original offsets, which is what a // deduplicated append resolves to. - wf.RecordStreamMessagesAdded( + wf.RecordStreamRecordsAppended( name, result.FirstOffset, result.Count, opts.WorkflowTaskCompletedEventID) return nil } @@ -155,7 +155,7 @@ func handleSubscribeStreamCommand( // streamSubscribedEvent is the event a subscription writes. // -// It is recorded once per subscription, not per message: the offsets a task +// It is recorded once per subscription, not per record: the offsets a task // consumed ride WorkflowTaskCompleted, and payloads never enter History. The // event exists because a command that produces none desynchronises the // command-to-event matching every SDK's replay depends on, and because without @@ -217,26 +217,26 @@ func RecordStreamSubscribedOffset(event *historypb.HistoryEvent, startOffset int event.GetWorkflowStreamSubscribedEventAttributes().StartOffset = startOffset } -// streamMessagesAddedEvent is the event a publish writes. +// streamRecordsAppendedEvent is the event a publish writes. // // One per batch, holding the offset range and nothing else. That is what makes -// it a fixed cost: a batch of one 20-byte message and a batch of a thousand -// 2KB messages write the same event, because the bodies stay in the stream +// it a fixed cost: a batch of one 20-byte record and a batch of a thousand +// 2KB records write the same event, because the bodies stay in the stream // component. It exists for the same reason the subscription event does, that a // command producing no event desynchronises the command-to-event matching // every SDK's replay depends on, and it doubles as the only record in History // that the workflow published at all. -type streamMessagesAddedEvent struct{} +type streamRecordsAppendedEvent struct{} -func (streamMessagesAddedEvent) Type() enumspb.EventType { - return enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED +func (streamRecordsAppendedEvent) Type() enumspb.EventType { + return enumspb.EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED } -func (streamMessagesAddedEvent) IsWorkflowTaskTrigger() bool { return false } +func (streamRecordsAppendedEvent) IsWorkflowTaskTrigger() bool { return false } // The frontier it describes is CHASM state, persisted and rebuilt with the // execution, so there is nothing here to reconstruct. -func (streamMessagesAddedEvent) Apply( +func (streamRecordsAppendedEvent) Apply( chasm.MutableContext, *Workflow, *historypb.HistoryEvent, ) error { return nil @@ -244,7 +244,7 @@ func (streamMessagesAddedEvent) Apply( // A command event, so it is never cherry-picked: the offsets belong to a log // the new branch did not write. -func (streamMessagesAddedEvent) CherryPick( +func (streamRecordsAppendedEvent) CherryPick( chasm.MutableContext, *Workflow, *historypb.HistoryEvent, @@ -253,23 +253,23 @@ func (streamMessagesAddedEvent) CherryPick( return ErrEventNotCherryPickable } -// RecordStreamMessagesAdded writes the event for one published batch. -func (w *Workflow) RecordStreamMessagesAdded( +// RecordStreamRecordsAppended writes the event for one published batch. +func (w *Workflow) RecordStreamRecordsAppended( streamID string, firstOffset int64, count int64, workflowTaskCompletedEventID int64, ) { - eventType := enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED + eventType := enumspb.EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED w.AddHistoryEvent(eventType, func(e *historypb.HistoryEvent) { - attrs := &historypb.WorkflowStreamMessagesAddedEventAttributes{ + attrs := &historypb.WorkflowStreamRecordsAppendedEventAttributes{ WorkflowTaskCompletedEventId: workflowTaskCompletedEventID, StreamId: streamID, FirstOffset: firstOffset, - MessageCount: count, + RecordCount: count, } - e.Attributes = &historypb.HistoryEvent_WorkflowStreamMessagesAddedEventAttributes{ - WorkflowStreamMessagesAddedEventAttributes: attrs, + e.Attributes = &historypb.HistoryEvent_WorkflowStreamRecordsAppendedEventAttributes{ + WorkflowStreamRecordsAppendedEventAttributes: attrs, } }) } @@ -319,15 +319,28 @@ func (w *Workflow) streamNamed( return created, nil } -func toLibraryMessages(in []*streampb.StreamMessage) []*streamlib.StreamMessage { - out := make([]*streamlib.StreamMessage, len(in)) +// toLibraryRecords shapes a command's records for the store. +// +// The producer id is cleared rather than copied: an empty id is how a reader +// tells the owning workflow's records from an outside producer's, and only this +// path writes on the workflow's behalf. The kind is settled here as well as in +// the store, because the store settles it in place and the command's records +// belong to the worker's request. +func toLibraryRecords(in []*streampb.StreamRecord) []*streamlib.StreamRecord { + out := make([]*streamlib.StreamRecord, len(in)) for i, m := range in { - out[i] = &streamlib.StreamMessage{ - Body: m.GetBody(), - Metadata: m.GetMetadata(), - Topic: m.GetTopic(), - TopicSequence: m.GetTopicSequence(), - Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + kind := m.GetKind() + if kind == streampb.STREAM_RECORD_KIND_UNSPECIFIED { + kind = streampb.STREAM_RECORD_KIND_DATA + } + out[i] = &streamlib.StreamRecord{ + Body: m.GetBody(), + Metadata: m.GetMetadata(), + Topic: m.GetTopic(), + Kind: kind, + ProducerId: "", + Attempt: m.GetAttempt(), + Sequence: m.GetSequence(), } } return out @@ -344,7 +357,7 @@ func newStreamLibrary(config *stream.Config) *streamLibrary { func (l *streamLibrary) CommandHandlers() map[enumspb.CommandType]CommandHandler { return map[enumspb.CommandType]CommandHandler{ - enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES: func( + enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS: func( chasmCtx chasm.MutableContext, wf *Workflow, validator Validator, @@ -352,12 +365,12 @@ func (l *streamLibrary) CommandHandlers() map[enumspb.CommandType]CommandHandler opts CommandHandlerOptions, ) error { limits := l.config.LimitsFor(chasmCtx.NamespaceEntry().Name().String()) - return handleAddStreamMessagesCommand(chasmCtx, wf, validator, command, opts, limits) + return handleAppendStreamRecordsCommand(chasmCtx, wf, validator, command, opts, limits) }, enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM: handleSubscribeStreamCommand, } } func (l *streamLibrary) EventDefinitions() []EventDefinition { - return []EventDefinition{streamSubscribedEvent{}, streamMessagesAddedEvent{}} + return []EventDefinition{streamSubscribedEvent{}, streamRecordsAppendedEvent{}} } diff --git a/chasm/lib/workflow/stream_commands_test.go b/chasm/lib/workflow/stream_commands_test.go new file mode 100644 index 00000000000..994e5c92bd2 --- /dev/null +++ b/chasm/lib/workflow/stream_commands_test.go @@ -0,0 +1,41 @@ +package workflow + +import ( + "testing" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + streampb "go.temporal.io/api/stream/v1" +) + +// An empty producer id is how a reader tells the owning workflow's records from +// an outside producer's. The command path is the only writer on the workflow's +// behalf, so it decides the field rather than trusting what the worker sent, +// while the attempt and sequence stay the worker's to number. +func TestPublishedRecordsBelongToTheWorkflow(t *testing.T) { + got := toLibraryRecords([]*streampb.StreamRecord{ + { + Body: &commonpb.Payload{Data: []byte("token")}, + Metadata: map[string]*commonpb.Payload{"k": {Data: []byte("v")}}, + Topic: "tokens", + Kind: streampb.STREAM_RECORD_KIND_FINISH, + ProducerId: "impostor", + Attempt: 3, + Sequence: 9, + }, + {Body: &commonpb.Payload{Data: []byte("kindless")}}, + }) + require.Len(t, got, 2) + + require.Equal(t, "token", string(got[0].GetBody().GetData())) + require.Equal(t, "v", string(got[0].GetMetadata()["k"].GetData())) + require.Equal(t, "tokens", got[0].GetTopic()) + require.Equal(t, streampb.STREAM_RECORD_KIND_FINISH, got[0].GetKind()) + require.Empty(t, got[0].GetProducerId(), "the workflow is the producer here") + require.Equal(t, int64(3), got[0].GetAttempt()) + require.Equal(t, int64(9), got[0].GetSequence()) + + require.Equal(t, streampb.STREAM_RECORD_KIND_DATA, got[1].GetKind(), + "a record with no kind is data") + require.Empty(t, got[1].GetProducerId()) +} diff --git a/chasm/lib/workflow/stream_cursor_test.go b/chasm/lib/workflow/stream_cursor_test.go index 9b5fc3ad1fa..b0cbba876da 100644 --- a/chasm/lib/workflow/stream_cursor_test.go +++ b/chasm/lib/workflow/stream_cursor_test.go @@ -40,11 +40,11 @@ func newAttachedStream(t *testing.T, ctx chasm.MutableContext, count int) *strea }, } - messages := make([]*streamlib.StreamMessage, count) + messages := make([]*streamlib.StreamRecord, count) for i := range messages { - messages[i] = &streamlib.StreamMessage{Kind: streamlib.STREAM_MESSAGE_KIND_DATA} + messages[i] = &streamlib.StreamRecord{Kind: streampb.STREAM_RECORD_KIND_DATA} } - _, err := s.AddMessages(ctx, stream.AddMessagesRequest{Messages: messages}) + _, err := s.AddMessages(ctx, stream.AddMessagesRequest{Records: messages}) require.NoError(t, err) return s @@ -170,10 +170,10 @@ func TestPublishStagesEachBatchAtItsOwnOffset(t *testing.T) { opts := CommandHandlerOptions{WorkflowTaskCompletedEventID: 10} publish := &commandpb.Command{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("x")}}, {Body: &commonpb.Payload{Data: []byte("y")}}, }, @@ -182,8 +182,8 @@ func TestPublishStagesEachBatchAtItsOwnOffset(t *testing.T) { } limits := stream.DefaultLimits() - require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts, limits)) - require.NoError(t, handleAddStreamMessagesCommand(ctx, w, allowAnySize{}, publish, opts, limits)) + require.NoError(t, handleAppendStreamRecordsCommand(ctx, w, allowAnySize{}, publish, opts, limits)) + require.NoError(t, handleAppendStreamRecordsCommand(ctx, w, allowAnySize{}, publish, opts, limits)) // Both publishes committed with the workflow task, so the batches are on // the component keyed by the offsets they start at. diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index aae62f51248..dd922e2b269 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -285,7 +285,7 @@ func (w *Workflow) StreamCursorsBehind(ctx chasm.Context) bool { // A cursor with nothing staged is skipped, but a cursor staged with an empty // range is not: replay has to see that the subscription was live and observed // nothing. -func (w *Workflow) CommitStreamCursors(mctx chasm.MutableContext) []*streampb.StreamCursor { +func (w *Workflow) CommitStreamCursors(mctx chasm.MutableContext) []*streampb.StreamRange { if w.StreamCursors == nil { return nil } @@ -298,7 +298,7 @@ func (w *Workflow) CommitStreamCursors(mctx chasm.MutableContext) []*streampb.St // event than the one the original execution wrote. slices.Sort(names) - var recorded []*streampb.StreamCursor + var recorded []*streampb.StreamRange for _, name := range names { cursor := w.StreamCursors[name].Get(mctx) from, to, ok := cursor.Commit(mctx) @@ -312,7 +312,7 @@ func (w *Workflow) CommitStreamCursors(mctx chasm.MutableContext) []*streampb.St field.Get(mctx).AdvanceConsumer(mctx, streamConsumerID(name), to) } - recorded = append(recorded, &streampb.StreamCursor{ + recorded = append(recorded, &streampb.StreamRange{ StreamId: cursor.StreamID(), FromOffset: from, ToOffset: to, diff --git a/go.mod b/go.mod index e3cde8649d9..2be924061ca 100644 --- a/go.mod +++ b/go.mod @@ -240,4 +240,4 @@ require ( tool golang.org/x/perf/cmd/benchstat -replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260918232949-bc9a4a97be0b +replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260921082525-9a6aa5f95be8 diff --git a/go.sum b/go.sum index 90ce90a71ea..13b45018247 100644 --- a/go.sum +++ b/go.sum @@ -321,8 +321,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moedash/api-go v1.63.6-0.20260918232949-bc9a4a97be0b h1:2N29jq0QI1cPq2OsAlcXvQIUTclVaaZ1AjUgeWhpnUQ= -github.com/moedash/api-go v1.63.6-0.20260918232949-bc9a4a97be0b/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= +github.com/moedash/api-go v1.63.6-0.20260921082525-9a6aa5f95be8 h1:WdG6n/8Da2RC5y3t3/vdpq0P4wOBbOQX+C+C9TiChew= +github.com/moedash/api-go v1.63.6-0.20260921082525-9a6aa5f95be8/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= diff --git a/service/history/api/command_attr_validator.go b/service/history/api/command_attr_validator.go index 5118a1d4ea8..257135405d9 100644 --- a/service/history/api/command_attr_validator.go +++ b/service/history/api/command_attr_validator.go @@ -664,7 +664,7 @@ func (v *CommandAttrValidator) ValidateCommandSequence( enumspb.COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION, // Publishing to a stream the workflow owns. Not a close command: // it appends and returns, scheduling nothing further. - enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, + enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, // Subscribing to a stream. Also not closing: it records a cursor // and the workflow carries on. enumspb.COMMAND_TYPE_SUBSCRIBE_STREAM: diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing.go b/service/history/api/recordworkflowtaskstarted/stream_routing.go index 1071fd29af6..546160a3476 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_routing.go +++ b/service/history/api/recordworkflowtaskstarted/stream_routing.go @@ -69,11 +69,11 @@ func readExternalWindow( out := response.GetFrontendResponse() if out == nil || out.GetNextOffset() < from || out.GetNextOffset() > to || out.GetNextOffset() > out.GetHeadOffset() || - int64(len(out.GetMessages())) != out.GetNextOffset()-from { + int64(len(out.GetRecords())) != out.GetNextOffset()-from { return stream.Window{}, serviceerror.NewDataLoss( "stream read returned an invalid contiguous range") } - for index, message := range out.GetMessages() { + for index, message := range out.GetRecords() { if message == nil || message.GetOffset() != from+int64(index) { return stream.Window{}, serviceerror.NewDataLoss( "stream read returned a missing or reordered offset") @@ -89,10 +89,10 @@ func readExternalWindow( Limit: int(limit), RunID: out.GetRunId(), } - if len(out.GetMessages()) == 0 { + if len(out.GetRecords()) == 0 { return w, nil } - data, err := proto.Marshal(&streamlib.StreamMessageBatch{Messages: out.GetMessages()}) + data, err := proto.Marshal(&streamlib.StreamRecordBatch{Records: out.GetRecords()}) if err != nil { return stream.Window{}, err } diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing_test.go b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go index d9111b65cdb..28589f344a5 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_routing_test.go +++ b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm" "go.temporal.io/server/chasm/lib/stream" streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" @@ -44,15 +45,15 @@ func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { require.Empty(t, req.GetFrontendRequest().GetTopics()) return &streamlib.PollMessagesResponse{FrontendResponse: &streamlib.PollMessagesOutput{ NextOffset: 6, HeadOffset: 9, - Messages: []*streamlib.StreamMessage{ + Records: []*streamlib.StreamRecord{ { Offset: 4, - Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, Body: &commonpb.Payload{Data: []byte("a")}, }, { Offset: 5, - Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, Body: &commonpb.Payload{Data: []byte("b")}, }, }, @@ -72,7 +73,7 @@ func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { } require.NoError(t, err) require.Equal(t, 1, calls) - messages, next, err := stream.CollectMessages( + messages, next, err := stream.CollectRecords( window.Blobs, window.Starts, 4, window.To, window.Limit, nil) require.NoError(t, err) require.EqualValues(t, 6, next) @@ -88,19 +89,19 @@ func TestExternalStreamRoutedReadRejectsCorruptOrExpandedRange(t *testing.T) { "missing-response": nil, "skipped-offset": { NextOffset: 6, HeadOffset: 9, - Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 6}}, + Records: []*streamlib.StreamRecord{{Offset: 4}, {Offset: 6}}, }, "short-count": { NextOffset: 6, HeadOffset: 9, - Messages: []*streamlib.StreamMessage{{Offset: 4}}, + Records: []*streamlib.StreamRecord{{Offset: 4}}, }, "past-requested-frontier": { NextOffset: 7, HeadOffset: 9, - Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}, {Offset: 6}}, + Records: []*streamlib.StreamRecord{{Offset: 4}, {Offset: 5}, {Offset: 6}}, }, "past-committed-head": { NextOffset: 6, HeadOffset: 5, - Messages: []*streamlib.StreamMessage{{Offset: 4}, {Offset: 5}}, + Records: []*streamlib.StreamRecord{{Offset: 4}, {Offset: 5}}, }, } for name, response := range cases { diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go index 544461e51dc..dec0ee5a586 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_slices.go +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -142,7 +142,7 @@ func readDeliverable( cursor *stream.Cursor, from, to int64, limits stream.Limits, -) ([]*streampb.StreamMessage, int64, string, error) { +) ([]*streampb.StreamRecord, int64, string, error) { if to <= from { return nil, from, "", nil } @@ -155,7 +155,7 @@ func readDeliverable( } // The collected run is contiguous from `from`, so the byte cap recomputes // the same end offset the read would have reported. - collected, _, err := stream.CollectMessages( + collected, _, err := stream.CollectRecords( w.Blobs, w.Starts, from, w.To, limits.MaxConsumeItemsPerTask, nil) if err != nil { return nil, 0, "", err @@ -163,7 +163,7 @@ func readDeliverable( // Cap before converting: the byte budget applies to the run as stored, and // trimming decides how far the recorded range reaches. collected, readTo := stream.CapByBytes(collected, from, limits.MaxConsumeBytesPerTask) - return stream.ToAPIMessages(collected), readTo, w.RunID, nil + return stream.ToAPIRecords(collected), readTo, w.RunID, nil } // readWindowFor reads a range from whichever component holds it. @@ -273,7 +273,7 @@ func deliverStreamSlices( to = min(from+int64(maxItems), head) } - messages, next, ownerRunID, err := readDeliverable( + records, next, ownerRunID, err := readDeliverable( ctx, chasmCtx, wf, consumer.NamespaceID, name, cursor, from, to, limits) if err != nil { return nil, nil, err @@ -303,7 +303,7 @@ func deliverStreamSlices( RunId: ownerRunID, FromOffset: from, ToOffset: next, - Messages: messages, + Records: records, }) addresses[cursor.StreamID()] = streamOrigin{ external: cursor.IsExternal(), @@ -321,8 +321,8 @@ func deliverStreamSlices( // task being started. A consumer past these bounds cannot be replayed by this // path; its task is failed with a cause that says so rather than retried. const ( - maxReplayMessages = 100_000 - maxReplayBytes = 64 << 20 + maxReplayRecords = 100_000 + maxReplayBytes = 64 << 20 // Pages of history walked to find the recorded ranges. Empty ranges cost // nothing to attach but each page is a store read under a request deadline. maxReplayPages = 256 @@ -374,8 +374,8 @@ type replaySupply struct { consumer definition.WorkflowKey addresses map[string]streamOrigin - messages int - bytes int + records int + bytes int // How far the recorded ranges reach, per stream, so the coverage check // can tell a complete re-supply from a short one. reached map[string]int64 @@ -414,7 +414,7 @@ func attachReplaySlices( // A sticky task means the worker still holds the execution, so it has // nothing to replay. Its history begins at the previous task's completion, // and that event carries the range that task already consumed, so - // re-supplying it here would hand the workflow the same messages twice. + // re-supplying it here would hand the workflow the same records twice. if resp.GetStickyExecutionEnabled() { return nil } @@ -486,7 +486,7 @@ func attachReplaySlices( // collect re-reads every range the events on one page recorded. func (s *replaySupply) collect(events []*historypb.HistoryEvent) error { for _, event := range events { - for _, recorded := range event.GetWorkflowTaskCompletedEventAttributes().GetStreamCursors() { + for _, recorded := range event.GetWorkflowTaskCompletedEventAttributes().GetConsumedStreamRanges() { address, ok := s.addresses[recorded.GetStreamId()] if !ok { // A subscription the workflow has since dropped. The range is @@ -494,7 +494,7 @@ func (s *replaySupply) collect(events []*historypb.HistoryEvent) error { continue } - messages, ownerRunID, err := s.messagesFor(address, recorded) + records, ownerRunID, err := s.recordsFor(address, recorded) if err != nil { return err } @@ -510,7 +510,7 @@ func (s *replaySupply) collect(events []*historypb.HistoryEvent) error { RunId: ownerRunID, FromOffset: recorded.GetFromOffset(), ToOffset: recorded.GetToOffset(), - Messages: messages, + Records: records, WorkflowTaskCompletedEventId: event.GetEventId(), }) } @@ -518,13 +518,13 @@ func (s *replaySupply) collect(events []*historypb.HistoryEvent) error { return nil } -// messagesFor re-reads one recorded range, or returns nothing for a range that +// recordsFor re-reads one recorded range, or returns nothing for a range that // recorded an empty observation. The run id is the execution holding the // stream, which for an owned stream is the consumer itself. -func (s *replaySupply) messagesFor( +func (s *replaySupply) recordsFor( address streamOrigin, - recorded *streampb.StreamCursor, -) ([]*streampb.StreamMessage, string, error) { + recorded *streampb.StreamRange, +) ([]*streampb.StreamRecord, string, error) { ownerRunID := s.consumer.RunID if recorded.GetToOffset() <= recorded.GetFromOffset() { if address.external { @@ -548,29 +548,29 @@ func (s *replaySupply) messagesFor( if address.external { ownerRunID = w.RunID } - collected, _, err := stream.CollectMessages( + collected, _, err := stream.CollectRecords( w.Blobs, w.Starts, recorded.GetFromOffset(), recorded.GetToOffset(), int(recorded.GetToOffset()-recorded.GetFromOffset()), nil) if err != nil { return nil, "", err } - messages := stream.ToAPIMessages(collected) + records := stream.ToAPIRecords(collected) - s.messages += len(messages) - for _, m := range messages { + s.records += len(records) + for _, m := range records { s.bytes += proto.Size(m) } // Bounded because every prior task's range is re-read into one response, so // a long-lived consumer's cold replay grows with its whole history. Refused // rather than trimmed: a short re-supply is what replay cannot survive. - if s.messages > maxReplayMessages || s.bytes > maxReplayBytes { + if s.records > maxReplayRecords || s.bytes > maxReplayBytes { return nil, "", unavailablef( - "replaying workflow %q needs more than %d messages or %d bytes of stream history "+ + "replaying workflow %q needs more than %d records or %d bytes of stream history "+ "to re-supply; the consumed ranges cannot be re-delivered in one response", - s.consumer.GetWorkflowID(), maxReplayMessages, maxReplayBytes) + s.consumer.GetWorkflowID(), maxReplayRecords, maxReplayBytes) } - return messages, ownerRunID, nil + return records, ownerRunID, nil } // checkCoverage refuses a re-supply that stops short of what the cursor says diff --git a/service/history/historybuilder/event_factory.go b/service/history/historybuilder/event_factory.go index 4f2c2f059af..a40bac21f26 100644 --- a/service/history/historybuilder/event_factory.go +++ b/service/history/historybuilder/event_factory.go @@ -163,7 +163,7 @@ func (b *EventFactory) CreateWorkflowTaskCompletedEvent( deploymentName string, deployment *deploymentpb.Deployment, behavior enumspb.VersioningBehavior, - streamCursors []*streampb.StreamCursor, + streamCursors []*streampb.StreamRange, ) *historypb.HistoryEvent { event := b.createHistoryEvent(enumspb.EVENT_TYPE_WORKFLOW_TASK_COMPLETED, b.timeSource.Now()) event.Attributes = &historypb.HistoryEvent_WorkflowTaskCompletedEventAttributes{ @@ -180,7 +180,7 @@ func (b *EventFactory) CreateWorkflowTaskCompletedEvent( VersioningBehavior: behavior, // Offsets only. The payloads the task consumed rode the task // response, so History grows with tasks rather than with messages. - StreamCursors: streamCursors, + ConsumedStreamRanges: streamCursors, }, } diff --git a/service/history/historybuilder/event_store.go b/service/history/historybuilder/event_store.go index ceb5d7b1d96..540b471ac91 100644 --- a/service/history/historybuilder/event_store.go +++ b/service/history/historybuilder/event_store.go @@ -333,7 +333,7 @@ func (b *EventStore) bufferEvent( enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, - enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: + enumspb.EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: // do not buffer event if event is directly generated from a corresponding command return false diff --git a/service/history/historybuilder/history_builder.go b/service/history/historybuilder/history_builder.go index 85934808916..c24e00f3c20 100644 --- a/service/history/historybuilder/history_builder.go +++ b/service/history/historybuilder/history_builder.go @@ -237,7 +237,7 @@ func (b *HistoryBuilder) AddWorkflowTaskCompletedEvent( deploymentName string, deployment *deploymentpb.Deployment, behavior enumspb.VersioningBehavior, - streamCursors []*streampb.StreamCursor, + streamCursors []*streampb.StreamRange, ) *historypb.HistoryEvent { event := b.CreateWorkflowTaskCompletedEvent( scheduledEventID, diff --git a/service/history/historybuilder/history_builder_test.go b/service/history/historybuilder/history_builder_test.go index a8b02c4eaf8..d9f47866976 100644 --- a/service/history/historybuilder/history_builder_test.go +++ b/service/history/historybuilder/history_builder_test.go @@ -2274,7 +2274,7 @@ func (s *historyBuilderSuite) TestBufferEvent() { enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: true, enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: true, enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: true, - enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: true, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: true, } // events corresponding to message from client will be assigned an event ID immediately diff --git a/service/history/workflow/mutable_state_impl.go b/service/history/workflow/mutable_state_impl.go index c1c0d277c6c..e297187ad89 100644 --- a/service/history/workflow/mutable_state_impl.go +++ b/service/history/workflow/mutable_state_impl.go @@ -678,7 +678,7 @@ func (ms *MutableStateImpl) mustInitHSM() { // the event carrying the range are in one transaction: split apart, a crash // between them would either redeliver a range or skip it with nothing in // History to say so. -func (ms *MutableStateImpl) commitStreamCursors() ([]*streampb.StreamCursor, error) { +func (ms *MutableStateImpl) commitStreamCursors() ([]*streampb.StreamRange, error) { if !ms.HasChasmWorkflowComponent() { return nil, nil } diff --git a/tests/api_fork_test.go b/tests/api_fork_test.go index 35ac851eb63..8e39bb7109b 100644 --- a/tests/api_fork_test.go +++ b/tests/api_fork_test.go @@ -16,14 +16,14 @@ import ( // compile. A field added without its descriptor would compile and silently // drop on marshal. func TestApiForkCarriesStreamShapes(t *testing.T) { - require.Equal(t, enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, enumspb.CommandType(19)) + require.Equal(t, enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, enumspb.CommandType(19)) cmd := &commandpb.Command{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ StreamId: "s1", - Messages: []*streampb.StreamMessage{{Topic: "tokens"}}, + Records: []*streampb.StreamRecord{{Topic: "tokens"}}, }, }, } @@ -31,9 +31,9 @@ func TestApiForkCarriesStreamShapes(t *testing.T) { require.NoError(t, err) var back commandpb.Command require.NoError(t, proto.Unmarshal(b, &back)) - require.Equal(t, "s1", back.GetAddStreamMessagesCommandAttributes().GetStreamId()) + require.Equal(t, "s1", back.GetAppendStreamRecordsCommandAttributes().GetStreamId()) require.Equal(t, "tokens", - back.GetAddStreamMessagesCommandAttributes().GetMessages()[0].GetTopic()) + back.GetAppendStreamRecordsCommandAttributes().GetRecords()[0].GetTopic()) resp := &workflowservice.PollWorkflowTaskQueueResponse{ StreamSlices: []*streampb.StreamSlice{{StreamId: "s1", FromOffset: 4, ToOffset: 7}}, @@ -45,7 +45,7 @@ func TestApiForkCarriesStreamShapes(t *testing.T) { require.Equal(t, int64(7), rback.GetStreamSlices()[0].GetToOffset()) attrs := &historypb.WorkflowTaskCompletedEventAttributes{ - StreamCursors: []*streampb.StreamCursor{{StreamId: "s1", FromOffset: 4, ToOffset: 4}}, + ConsumedStreamRanges: []*streampb.StreamRange{{StreamId: "s1", FromOffset: 4, ToOffset: 4}}, } ab, err := proto.Marshal(attrs) require.NoError(t, err) @@ -53,7 +53,7 @@ func TestApiForkCarriesStreamShapes(t *testing.T) { require.NoError(t, proto.Unmarshal(ab, &aback)) // An empty range has to survive the round trip: it is the fact that a // subscription observed nothing, which replay must reproduce. - require.Len(t, aback.GetStreamCursors(), 1) + require.Len(t, aback.GetConsumedStreamRanges(), 1) require.Equal(t, - aback.GetStreamCursors()[0].GetFromOffset(), aback.GetStreamCursors()[0].GetToOffset()) + aback.GetConsumedStreamRanges()[0].GetFromOffset(), aback.GetConsumedStreamRanges()[0].GetToOffset()) } diff --git a/tests/stream_consume_test.go b/tests/stream_consume_test.go index 72714ca8edd..4b372a0bbc4 100644 --- a/tests/stream_consume_test.go +++ b/tests/stream_consume_test.go @@ -67,10 +67,10 @@ func TestStreamWorkflowConsumesWithoutHistoryPayloads(t *testing.T) { // First task publishes; nothing is subscribed yet, so it consumes // nothing. return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("first-token")}, Topic: "tokens"}, {Body: &commonpb.Payload{Data: []byte("second-token")}, Topic: "tokens"}, {Body: &commonpb.Payload{Data: []byte("third-token")}, Topic: "tokens"}, @@ -119,9 +119,9 @@ func TestStreamWorkflowConsumesWithoutHistoryPayloads(t *testing.T) { slice := currentSlice(t, delivered[1]) require.Equal(t, int64(0), slice.GetFromOffset()) require.Equal(t, int64(3), slice.GetToOffset()) - require.Len(t, slice.GetMessages(), 3) - require.Equal(t, "first-token", string(slice.GetMessages()[0].GetBody().GetData())) - require.Equal(t, "third-token", string(slice.GetMessages()[2].GetBody().GetData())) + require.Len(t, slice.GetRecords(), 3) + require.Equal(t, "first-token", string(slice.GetRecords()[0].GetBody().GetData())) + require.Equal(t, "third-token", string(slice.GetRecords()[2].GetBody().GetData())) // Third task: the cursor is caught up, so the range is empty. An empty // range is still delivered and still recorded, because a task that saw @@ -133,7 +133,7 @@ func TestStreamWorkflowConsumesWithoutHistoryPayloads(t *testing.T) { idleSlice := currentSlice(t, delivered[2]) require.Equal(t, int64(3), idleSlice.GetFromOffset(), "a caught-up subscription is still attached") require.Equal(t, int64(3), idleSlice.GetToOffset()) - require.Empty(t, idleSlice.GetMessages()) + require.Empty(t, idleSlice.GetRecords()) events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}) recorded := recordedCursors(events) @@ -156,11 +156,11 @@ func TestStreamWorkflowConsumesWithoutHistoryPayloads(t *testing.T) { } } -func recordedCursors(events []*historypb.HistoryEvent) []*streampb.StreamCursor { - var out []*streampb.StreamCursor +func recordedCursors(events []*historypb.HistoryEvent) []*streampb.StreamRange { + var out []*streampb.StreamRange for _, e := range events { attrs := e.GetWorkflowTaskCompletedEventAttributes() - out = append(out, attrs.GetStreamCursors()...) + out = append(out, attrs.GetConsumedStreamRanges()...) } return out } @@ -206,15 +206,15 @@ func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { require.NoError(t, err) publish := func(bodies ...string) []*commandpb.Command { - messages := make([]*streampb.StreamMessage, len(bodies)) + messages := make([]*streampb.StreamRecord, len(bodies)) for i, b := range bodies { - messages[i] = &streampb.StreamMessage{Body: &commonpb.Payload{Data: []byte(b)}, Topic: "tokens"} + messages[i] = &streampb.StreamRecord{Body: &commonpb.Payload{Data: []byte(b)}, Topic: "tokens"} } return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: messages, + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: messages, }, }, }} @@ -276,8 +276,8 @@ func TestStreamSubscriptionSchedulesItsOwnWorkflowTask(t *testing.T) { woken := currentSlice(t, delivered[2]) require.Equal(t, int64(1), woken.GetFromOffset()) require.Equal(t, int64(3), woken.GetToOffset()) - require.Len(t, woken.GetMessages(), 2) - require.Equal(t, "alpha", string(woken.GetMessages()[0].GetBody().GetData())) + require.Len(t, woken.GetRecords(), 2) + require.Equal(t, "alpha", string(woken.GetRecords()[0].GetBody().GetData())) // And it has to stop. A wake condition that stayed true would spin the // workflow on empty tasks forever. @@ -339,10 +339,10 @@ func TestSubscribingToABacklogSchedulesAWorkflowTask(t *testing.T) { return nil, nil } return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("backlog-1")}, Topic: "tokens"}, {Body: &commonpb.Payload{Data: []byte("backlog-2")}, Topic: "tokens"}, }, @@ -421,10 +421,10 @@ func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { return nil, nil } return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("replay-me-1")}, Topic: "tokens"}, {Body: &commonpb.Payload{Data: []byte("replay-me-2")}, Topic: "tokens"}, }, @@ -450,7 +450,7 @@ func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { // Consume the range. This is the task replay will have to reproduce. _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - require.Len(t, currentSlice(t, delivered[1]).GetMessages(), 2) + require.Len(t, currentSlice(t, delivered[1]).GetRecords(), 2) consumedAt := completedEventWithCursors(t, env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()})) @@ -468,9 +468,9 @@ func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { "the replayed task must carry the range recorded at event %d", consumedAt) require.Equal(t, int64(0), replayed.GetFromOffset()) require.Equal(t, int64(2), replayed.GetToOffset()) - require.Len(t, replayed.GetMessages(), 2, "the payloads have to come back from the stream") - require.Equal(t, "replay-me-1", string(replayed.GetMessages()[0].GetBody().GetData())) - require.Equal(t, "replay-me-2", string(replayed.GetMessages()[1].GetBody().GetData())) + require.Len(t, replayed.GetRecords(), 2, "the payloads have to come back from the stream") + require.Equal(t, "replay-me-1", string(replayed.GetRecords()[0].GetBody().GetData())) + require.Equal(t, "replay-me-2", string(replayed.GetRecords()[1].GetBody().GetData())) } // completedEventWithCursors returns the id of the first WorkflowTaskCompleted @@ -478,7 +478,7 @@ func TestReplayGetsTheConsumedRangesBackFromTheStream(t *testing.T) { func completedEventWithCursors(t *testing.T, events []*historypb.HistoryEvent) int64 { t.Helper() for _, e := range events { - for _, c := range e.GetWorkflowTaskCompletedEventAttributes().GetStreamCursors() { + for _, c := range e.GetWorkflowTaskCompletedEventAttributes().GetConsumedStreamRanges() { if c.GetToOffset() > c.GetFromOffset() { return e.GetEventId() } @@ -591,14 +591,14 @@ func TestWorkflowConsumesAStreamItDoesNotOwn(t *testing.T) { _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, - Messages: []*streamlib.StreamMessage{ + Records: []*streamlib.StreamRecord{ { Body: &commonpb.Payload{Data: []byte("from-outside-1")}, - Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, }, { Body: &commonpb.Payload{Data: []byte("from-outside-2")}, - Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, }, }, }, @@ -625,8 +625,8 @@ func TestWorkflowConsumesAStreamItDoesNotOwn(t *testing.T) { require.Equal(t, streamID, got.GetStreamId()) require.Equal(t, int64(0), got.GetFromOffset()) require.Equal(t, int64(2), got.GetToOffset()) - require.Len(t, got.GetMessages(), 2) - require.Equal(t, "from-outside-1", string(got.GetMessages()[0].GetBody().GetData())) + require.Len(t, got.GetRecords(), 2) + require.Equal(t, "from-outside-1", string(got.GetRecords()[0].GetBody().GetData())) } // A cursor is workflow state, so a continue-as-new has to carry it. Without @@ -707,8 +707,8 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, - Messages: []*streamlib.StreamMessage{ - {Body: &commonpb.Payload{Data: []byte("before-can")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + Records: []*streamlib.StreamRecord{ + {Body: &commonpb.Payload{Data: []byte("before-can")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, }, }, }) @@ -719,7 +719,7 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { require.NoError(t, err) consumed := currentSlice(t, delivered[1]) require.Equal(t, int64(1), consumed.GetToOffset()) - require.Len(t, consumed.GetMessages(), 1) + require.Len(t, consumed.GetRecords(), 1) // Drain the successor's first task, which carries nothing new. _, err = poller.PollAndProcessWorkflowTask() @@ -730,8 +730,8 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, - Messages: []*streamlib.StreamMessage{ - {Body: &commonpb.Payload{Data: []byte("after-can")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + Records: []*streamlib.StreamRecord{ + {Body: &commonpb.Payload{Data: []byte("after-can")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, }, }, }) @@ -744,8 +744,8 @@ func TestSubscriptionSurvivesContinueAsNew(t *testing.T) { require.Equal(t, int64(1), afterCAN.GetFromOffset(), "the successor resumes where its predecessor stopped") require.Equal(t, int64(2), afterCAN.GetToOffset()) - require.Len(t, afterCAN.GetMessages(), 1) - require.Equal(t, "after-can", string(afterCAN.GetMessages()[0].GetBody().GetData())) + require.Len(t, afterCAN.GetRecords(), 1) + require.Equal(t, "after-can", string(afterCAN.GetRecords()[0].GetBody().GetData())) // The pin followed the subscription to the new run, with the floor where // that run's cursor began. The predecessor's ranges are in a closed history @@ -854,9 +854,9 @@ func TestWorkflowSubscribesToAStreamItself(t *testing.T) { _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, - Messages: []*streamlib.StreamMessage{ - {Body: &commonpb.Payload{Data: []byte("self-1")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, - {Body: &commonpb.Payload{Data: []byte("self-2")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + Records: []*streamlib.StreamRecord{ + {Body: &commonpb.Payload{Data: []byte("self-1")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("self-2")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, }, }, }) @@ -869,8 +869,8 @@ func TestWorkflowSubscribesToAStreamItself(t *testing.T) { require.Equal(t, streamID, got.GetStreamId()) require.Equal(t, int64(0), got.GetFromOffset()) require.Equal(t, int64(2), got.GetToOffset()) - require.Len(t, got.GetMessages(), 2) - require.Equal(t, "self-1", string(got.GetMessages()[0].GetBody().GetData())) + require.Len(t, got.GetRecords(), 2) + require.Equal(t, "self-1", string(got.GetRecords()[0].GetBody().GetData())) } func subscribedEvents( @@ -1023,10 +1023,10 @@ func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { }, }, { - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("after subscribe")}}, }, }, @@ -1044,7 +1044,7 @@ func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { for _, e := range env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id}) { switch e.GetEventType() { case enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, - enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: + enumspb.EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: order = append(order, e.GetEventType()) default: // Every other event is noise for this assertion. @@ -1052,7 +1052,7 @@ func TestStreamSubscribeEventKeepsCommandOrder(t *testing.T) { } require.Equal(t, []enumspb.EventType{ enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, - enumspb.EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED, + enumspb.EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED, }, order, "the events must be in the order their commands were issued") } @@ -1096,7 +1096,7 @@ func TestMessagesAppendedWithoutAKindReachTheWorkflow(t *testing.T) { _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, - Messages: []*streamlib.StreamMessage{ + Records: []*streamlib.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("no-kind-1")}}, {Body: &commonpb.Payload{Data: []byte("no-kind-2")}}, }, @@ -1108,6 +1108,6 @@ func TestMessagesAppendedWithoutAKindReachTheWorkflow(t *testing.T) { require.NoError(t, err) got := currentSlice(t, delivered[1]) require.Equal(t, int64(2), got.GetToOffset()) - require.Equal(t, []string{"no-kind-1", "no-kind-2"}, apiBodies(got.GetMessages()), + require.Equal(t, []string{"no-kind-1", "no-kind-2"}, apiBodies(got.GetRecords()), "the slice must carry the messages, not just advance past them") } diff --git a/tests/stream_consumer_lifecycle_test.go b/tests/stream_consumer_lifecycle_test.go index dd3e33dccdb..aa7a27704e0 100644 --- a/tests/stream_consumer_lifecycle_test.go +++ b/tests/stream_consumer_lifecycle_test.go @@ -73,7 +73,7 @@ func subscribeConsumeAndComplete( require.NoError(t, err) _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ - Namespace: s.ns, StreamId: streamID, Messages: streamMsgs("tokens", "one", "two"), + Namespace: s.ns, StreamId: streamID, Records: streamMsgs("tokens", "one", "two"), }, }) require.NoError(t, err) @@ -112,7 +112,7 @@ func TestACompletedConsumerReleasesItsFloor(t *testing.T) { // The append is what makes the stream go and ask. _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ - Namespace: s.ns, StreamId: streamID, Messages: streamMsgs("tokens", "three"), + Namespace: s.ns, StreamId: streamID, Records: streamMsgs("tokens", "three"), }, }) require.NoError(t, err) diff --git a/tests/stream_publish_cost_test.go b/tests/stream_publish_cost_test.go index 7c778c8ee57..7195c575fbf 100644 --- a/tests/stream_publish_cost_test.go +++ b/tests/stream_publish_cost_test.go @@ -209,18 +209,18 @@ func runPublishCostArm(t *testing.T, arm publishCostArm) publishCostResult { commands := make([]*commandpb.Command, 0, arm.batches+1) if !arm.viaSignal { for range arm.batches { - messages := make([]*streampb.StreamMessage, 0, arm.messagesPerBatch) + messages := make([]*streampb.StreamRecord, 0, arm.messagesPerBatch) for range arm.messagesPerBatch { - messages = append(messages, &streampb.StreamMessage{ + messages = append(messages, &streampb.StreamRecord{ Body: &commonpb.Payload{Data: body}, Topic: "progress", }) } commands = append(commands, &commandpb.Command{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: messages, + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: messages, }, }, }) diff --git a/tests/stream_rejection_test.go b/tests/stream_rejection_test.go index 7e5fc7f87b8..248172b7a6f 100644 --- a/tests/stream_rejection_test.go +++ b/tests/stream_rejection_test.go @@ -24,7 +24,7 @@ func TestOverLimitPublishFailsTheWorkflowTask(t *testing.T) { s := newStreamTestEnvFrom(t, env) execution, tq := startConsumer(t, s, "stream-wf-reject-publish-") - tooMany := make([]string, chasmstream.MaxMessagesPerBatch+1) + tooMany := make([]string, chasmstream.MaxRecordsPerBatch+1) for i := range tooMany { tooMany[i] = "x" } @@ -54,7 +54,7 @@ func TestOverLimitPublishFailsTheWorkflowTask(t *testing.T) { events := env.GetHistory(s.ns, execution) failed := workflowTaskFailedWith(events, - enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES) + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES) require.NotNil(t, failed, "the refusal is a workflow task failure with its own cause") require.ErrorContains(t, err, failed.GetFailure().GetMessage()) @@ -77,8 +77,8 @@ func TestOverLimitPublishFailsTheWorkflowTask(t *testing.T) { }, }) require.NoError(t, err) - require.Equal(t, []string{"fits"}, bodies(poll.GetFrontendResponse().GetMessages())) - require.Equal(t, []int64{0}, offsets(poll.GetFrontendResponse().GetMessages())) + require.Equal(t, []string{"fits"}, bodies(poll.GetFrontendResponse().GetRecords())) + require.Equal(t, []int64{0}, offsets(poll.GetFrontendResponse().GetRecords())) } // A subscribe naming a stream that does not exist is refused where the stream @@ -169,7 +169,7 @@ func TestPublishOnAFailedTaskLeavesNoTrace(t *testing.T) { require.NotNil(t, workflowTaskFailedWith(events, enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_ACTIVITY_ATTRIBUTES)) for _, e := range events { - require.Nil(t, e.GetWorkflowStreamMessagesAddedEventAttributes(), + require.Nil(t, e.GetWorkflowStreamRecordsAppendedEventAttributes(), "a publish on a failed task leaves no event") } desc, err := s.client.DescribeWorkflowStream(s.ctx(), &streamlib.DescribeWorkflowStreamRequest{ @@ -185,7 +185,7 @@ func TestPublishOnAFailedTaskLeavesNoTrace(t *testing.T) { }, }) require.NoError(t, err) - require.Empty(t, poll.GetFrontendResponse().GetMessages()) + require.Empty(t, poll.GetFrontendResponse().GetRecords()) // The retry's publish is the first thing the stream ever holds. _, err = poller.PollAndProcessWorkflowTask(testcore.WithExpectedAttemptCount(2)) @@ -196,6 +196,6 @@ func TestPublishOnAFailedTaskLeavesNoTrace(t *testing.T) { }, }) require.NoError(t, err) - require.Equal(t, []string{"first-to-land"}, bodies(poll.GetFrontendResponse().GetMessages())) - require.Equal(t, []int64{0}, offsets(poll.GetFrontendResponse().GetMessages())) + require.Equal(t, []string{"first-to-land"}, bodies(poll.GetFrontendResponse().GetRecords())) + require.Equal(t, []int64{0}, offsets(poll.GetFrontendResponse().GetRecords())) } diff --git a/tests/stream_replay_test.go b/tests/stream_replay_test.go index 3ae064750cb..fbd8952e0a8 100644 --- a/tests/stream_replay_test.go +++ b/tests/stream_replay_test.go @@ -48,21 +48,21 @@ func startConsumer( } func publishCommand(bodies ...string) []*commandpb.Command { - messages := make([]*streampb.StreamMessage, len(bodies)) + messages := make([]*streampb.StreamRecord, len(bodies)) for i, b := range bodies { - messages[i] = &streampb.StreamMessage{Body: &commonpb.Payload{Data: []byte(b)}, Topic: "tokens"} + messages[i] = &streampb.StreamRecord{Body: &commonpb.Payload{Data: []byte(b)}, Topic: "tokens"} } return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: messages, + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: messages, }, }, }} } -func apiBodies(msgs []*streampb.StreamMessage) []string { +func apiBodies(msgs []*streampb.StreamRecord) []string { out := make([]string, len(msgs)) for i, m := range msgs { out[i] = string(m.GetBody().GetData()) @@ -75,7 +75,7 @@ func apiBodies(msgs []*streampb.StreamMessage) []string { func completedEventsWithCursors(events []*historypb.HistoryEvent) []int64 { var out []int64 for _, e := range events { - if len(e.GetWorkflowTaskCompletedEventAttributes().GetStreamCursors()) > 0 { + if len(e.GetWorkflowTaskCompletedEventAttributes().GetConsumedStreamRanges()) > 0 { out = append(out, e.GetEventId()) } } @@ -131,7 +131,7 @@ func TestReplayFollowsHistoryPastTheFirstPage(t *testing.T) { _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) } - require.Len(t, currentSlice(t, delivered[1]).GetMessages(), 2) + require.Len(t, currentSlice(t, delivered[1]).GetRecords(), 2) events := env.GetHistory(s.ns, execution) recordedAt := completedEventsWithCursors(events) @@ -148,7 +148,7 @@ func TestReplayFollowsHistoryPastTheFirstPage(t *testing.T) { replay := delivered[len(delivered)-1] replayed := sliceForEvent(replay, consumedAt) require.NotNil(t, replayed, "the range recorded on a later page must be re-supplied") - require.Equal(t, []string{"page-1", "page-2"}, apiBodies(replayed.GetMessages())) + require.Equal(t, []string{"page-1", "page-2"}, apiBodies(replayed.GetRecords())) require.Equal(t, execution.GetRunId(), replayed.GetRunId(), "an owned stream's slice names the run that holds it") for _, eventID := range recordedAt { @@ -253,7 +253,7 @@ func TestConsumerOfADeletedStreamFailsItsTaskLoudly(t *testing.T) { // The append schedules the task that is owed the range. _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ - Namespace: s.ns, StreamId: streamID, Messages: streamMsgs("tokens", "never-delivered"), + Namespace: s.ns, StreamId: streamID, Records: streamMsgs("tokens", "never-delivered"), }, }) require.NoError(t, err) @@ -320,7 +320,7 @@ func TestReplayOfADeletedStreamFailsTheTaskLoudly(t *testing.T) { require.NoError(t, err) _, err = s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ - Namespace: s.ns, StreamId: streamID, Messages: streamMsgs("tokens", "consumed-once"), + Namespace: s.ns, StreamId: streamID, Records: streamMsgs("tokens", "consumed-once"), }, }) require.NoError(t, err) @@ -328,7 +328,7 @@ func TestReplayOfADeletedStreamFailsTheTaskLoudly(t *testing.T) { // Consumed and recorded, which is what a replay will have to reproduce. _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - require.Len(t, currentSlice(t, delivered[1]).GetMessages(), 1) + require.Len(t, currentSlice(t, delivered[1]).GetRecords(), 1) deleteStreamAndWait(t, s, streamID) diff --git a/tests/stream_retention_test.go b/tests/stream_retention_test.go index 3e2ffcacdc1..1d4bb3d9c5e 100644 --- a/tests/stream_retention_test.go +++ b/tests/stream_retention_test.go @@ -9,6 +9,7 @@ import ( commandpb "go.temporal.io/api/command/v1" commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" + streampb "go.temporal.io/api/stream/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" "go.temporal.io/api/workflowservice/v1" chasmstream "go.temporal.io/server/chasm/lib/stream" @@ -33,10 +34,10 @@ func TestTruncationRefusesToDropWhatAConsumerNeedsToReplay(t *testing.T) { _, err := s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, - Messages: []*streamlib.StreamMessage{ - {Body: &commonpb.Payload{Data: []byte("one")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, - {Body: &commonpb.Payload{Data: []byte("two")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, - {Body: &commonpb.Payload{Data: []byte("three")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + Records: []*streamlib.StreamRecord{ + {Body: &commonpb.Payload{Data: []byte("one")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("two")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("three")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, }, }, }) @@ -125,9 +126,9 @@ func TestTruncationStillWorksWithNoConsumer(t *testing.T) { _, err := s.client.AddMessages(s.ctx(), &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: s.ns, StreamId: streamID, - Messages: []*streamlib.StreamMessage{ - {Body: &commonpb.Payload{Data: []byte("one")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, - {Body: &commonpb.Payload{Data: []byte("two")}, Kind: streamlib.STREAM_MESSAGE_KIND_DATA}, + Records: []*streamlib.StreamRecord{ + {Body: &commonpb.Payload{Data: []byte("one")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, + {Body: &commonpb.Payload{Data: []byte("two")}, Kind: streampb.STREAM_RECORD_KIND_DATA}, }, }, }) diff --git a/tests/stream_routing_cross_host_test.go b/tests/stream_routing_cross_host_test.go index 325f14d3e1d..f2bf31a3ee4 100644 --- a/tests/stream_routing_cross_host_test.go +++ b/tests/stream_routing_cross_host_test.go @@ -152,7 +152,7 @@ func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { _, err = streams.AddMessages(ctx, &streamlib.AddMessagesRequest{ FrontendRequest: &streamlib.AddMessagesInput{ Namespace: topo.ns, StreamId: source, - Messages: streamMsgs("tokens", "retained-input"), + Records: streamMsgs("tokens", "retained-input"), }, }) require.NoError(t, err) @@ -176,7 +176,7 @@ func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) live := currentSlice(t, delivered[1]) - require.Equal(t, "retained-input", string(live.GetMessages()[0].GetBody().GetData())) + require.Equal(t, "retained-input", string(live.GetRecords()[0].GetBody().GetData())) artifacts.writeProto(tc.name+"-live-slice", live) artifacts.writeProto(tc.name+"-committed-history", &historypb.History{Events: history()}) consumedAt := completedEventWithCursors(t, history()) @@ -209,7 +209,7 @@ func TestStreamRoutedCrossHostDeliveryAndReplay(t *testing.T) { replayed := sliceForEvent(response.GetStreamSlices(), consumedAt) require.NotNil(t, replayed, "cold replay must re-supply the committed input range") - require.Equal(t, "retained-input", string(replayed.GetMessages()[0].GetBody().GetData())) + require.Equal(t, "retained-input", string(replayed.GetRecords()[0].GetBody().GetData())) }) } } diff --git a/tests/stream_test.go b/tests/stream_test.go index c2586ef9a6b..2fbe4542550 100644 --- a/tests/stream_test.go +++ b/tests/stream_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" + apistreampb "go.temporal.io/api/stream/v1" chasmstream "go.temporal.io/server/chasm/lib/stream" streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "go.temporal.io/server/common/testing/await" @@ -21,7 +22,7 @@ import ( // path the benchmark will eventually compare against the Signal-and-Update // baseline in streaming_baseline_test.go. -const streamMaxBatch = chasmstream.MaxMessagesPerBatch +const streamMaxBatch = chasmstream.MaxRecordsPerBatch type streamTestEnv struct { env *testcore.TestEnv @@ -95,19 +96,19 @@ func (s *streamTestEnv) poll( return resp.GetFrontendResponse() } -func streamMsgs(topic string, bodies ...string) []*streampb.StreamMessage { - out := make([]*streampb.StreamMessage, len(bodies)) +func streamMsgs(topic string, bodies ...string) []*streampb.StreamRecord { + out := make([]*streampb.StreamRecord, len(bodies)) for i, b := range bodies { - out[i] = &streampb.StreamMessage{ + out[i] = &streampb.StreamRecord{ Body: &commonpb.Payload{Data: []byte(b)}, Topic: topic, - Kind: streampb.STREAM_MESSAGE_KIND_DATA, + Kind: apistreampb.STREAM_RECORD_KIND_DATA, } } return out } -func bodies(msgs []*streampb.StreamMessage) []string { +func bodies(msgs []*streampb.StreamRecord) []string { out := make([]string, len(msgs)) for i, m := range msgs { out[i] = string(m.GetBody().GetData()) @@ -128,28 +129,28 @@ func TestStreamAppendAndRead(t *testing.T) { s.create(ctx, t, id) first, err := s.add(ctx, t, id, - &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) + &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) require.NoError(t, err) require.Equal(t, int64(0), first.GetFirstOffset()) require.Equal(t, int64(3), first.GetNextOffset()) - second, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "d")}) + second, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "d")}) require.NoError(t, err) require.Equal(t, int64(3), second.GetFirstOffset()) all := s.poll(ctx, t, id, 0) - require.Equal(t, []string{"a", "b", "c", "d"}, bodies(all.GetMessages())) + require.Equal(t, []string{"a", "b", "c", "d"}, bodies(all.GetRecords())) require.Equal(t, int64(4), all.GetNextOffset()) require.Equal(t, int64(4), all.GetHeadOffset()) require.False(t, all.GetClosed()) // A reader owns its cursor, so resuming mid-stream is just another read. tail := s.poll(ctx, t, id, 2) - require.Equal(t, []string{"c", "d"}, bodies(tail.GetMessages())) + require.Equal(t, []string{"c", "d"}, bodies(tail.GetRecords())) // Caught up returns empty rather than erroring. caughtUp := s.poll(ctx, t, id, 4) - require.Empty(t, caughtUp.GetMessages()) + require.Empty(t, caughtUp.GetRecords()) require.Equal(t, int64(4), caughtUp.GetNextOffset()) } @@ -159,7 +160,7 @@ func TestStreamManyReadersAreIndependent(t *testing.T) { const id = "stream-many-readers" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b")}) require.NoError(t, err) // No durable per-subscriber state, so reader count is not a state-machine @@ -167,7 +168,7 @@ func TestStreamManyReadersAreIndependent(t *testing.T) { // Signal-and-Update pattern hits. for range 25 { got := s.poll(ctx, t, id, 0) - require.Equal(t, []string{"a", "b"}, bodies(got.GetMessages())) + require.Equal(t, []string{"a", "b"}, bodies(got.GetRecords())) } } @@ -178,14 +179,14 @@ func TestStreamProducerDedup(t *testing.T) { s.create(ctx, t, id) in := &streampb.AddMessagesInput{ - Messages: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, + Records: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, } first, err := s.add(ctx, t, id, in) require.NoError(t, err) require.False(t, first.GetDeduplicated()) retry, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ - Messages: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, + Records: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, }) require.NoError(t, err) require.True(t, retry.GetDeduplicated()) @@ -193,12 +194,12 @@ func TestStreamProducerDedup(t *testing.T) { // The retry must not have appended a second copy. got := s.poll(ctx, t, id, 0) - require.Equal(t, []string{"a", "b"}, bodies(got.GetMessages())) + require.Equal(t, []string{"a", "b"}, bodies(got.GetRecords())) // Same sequence with different content is a client bug. Returning the // recorded offsets would report success while dropping the data. _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ - Messages: streamMsgs("", "different"), ProducerId: "p1", Sequence: 1, + Records: streamMsgs("", "different"), ProducerId: "p1", Sequence: 1, }) require.ErrorContains(t, err, "different content") } @@ -209,15 +210,15 @@ func TestStreamTopicFilter(t *testing.T) { const id = "stream-topics" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("tokens", "t1")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("tokens", "t1")}) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("tools", "x1")}) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("tools", "x1")}) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("tokens", "t2")}) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("tokens", "t2")}) require.NoError(t, err) got := s.poll(ctx, t, id, 0, "tokens") - require.Equal(t, []string{"t1", "t2"}, bodies(got.GetMessages())) + require.Equal(t, []string{"t1", "t2"}, bodies(got.GetRecords())) // Offsets are global, so a filtered read still advances past what it skipped. require.Equal(t, int64(3), got.GetNextOffset()) } @@ -236,13 +237,13 @@ func TestStreamFinishWritingIsPerProducer(t *testing.T) { require.NoError(t, err) _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ - Messages: streamMsgs("", "a"), ProducerId: "p1", Sequence: 1, + Records: streamMsgs("", "a"), ProducerId: "p1", Sequence: 1, }) require.Error(t, err) // Finishing is per-producer, not a close, so others carry on. _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ - Messages: streamMsgs("", "b"), ProducerId: "p2", Sequence: 1, + Records: streamMsgs("", "b"), ProducerId: "p2", Sequence: 1, }) require.NoError(t, err) } @@ -253,7 +254,7 @@ func TestStreamCloseAndTruncate(t *testing.T) { const id = "stream-lifecycle" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) require.NoError(t, err) _, err = s.client.TruncateStream(ctx, &streampb.TruncateStreamRequest{ @@ -275,14 +276,14 @@ func TestStreamCloseAndTruncate(t *testing.T) { }) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "d")}) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "d")}) require.Error(t, err) // Closed is a state a reader observes, not an error, and the data stays // readable rather than requiring a shutdown handshake with the producer. got := s.poll(ctx, t, id, 1) require.True(t, got.GetClosed()) - require.Equal(t, []string{"b", "c"}, bodies(got.GetMessages())) + require.Equal(t, []string{"b", "c"}, bodies(got.GetRecords())) } func TestStreamReadStartingInsideABatch(t *testing.T) { @@ -295,9 +296,9 @@ func TestStreamReadStartingInsideABatch(t *testing.T) { // first offset of its batch, so reading from 2 has to find the node that // contains it rather than the node whose ID equals it. Getting that wrong // silently drops the messages before the boundary. - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b", "c")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "d")}) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "d")}) require.NoError(t, err) for from, want := range map[int64][]string{ @@ -307,7 +308,7 @@ func TestStreamReadStartingInsideABatch(t *testing.T) { 3: {"d"}, } { got := s.poll(ctx, t, id, from) - require.Equal(t, want, bodies(got.GetMessages()), "reading from offset %d", from) + require.Equal(t, want, bodies(got.GetRecords()), "reading from offset %d", from) require.Equal(t, int64(4), got.GetNextOffset()) } } @@ -324,7 +325,7 @@ func TestStreamBatchSizeIsBounded(t *testing.T) { for i := range tooMany { tooMany[i] = "x" } - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", tooMany...)}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", tooMany...)}) require.ErrorContains(t, err, "exceeds the limit") } @@ -359,13 +360,13 @@ func TestStreamLongPollWakesOnAppend(t *testing.T) { }() // The poll is parked on an empty stream; the append is what releases it. - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a")}) require.NoError(t, err) select { case r := <-done: require.NoError(t, r.err) - require.Equal(t, []string{"a"}, bodies(r.out.GetMessages())) + require.Equal(t, []string{"a"}, bodies(r.out.GetRecords())) require.Equal(t, int64(1), r.out.GetNextOffset()) case <-time.After(25 * time.Second): t.Fatal("long poll did not wake on append") @@ -396,7 +397,7 @@ func TestStreamLongPollWakesOnClose(t *testing.T) { select { case out := <-done: require.True(t, out.GetClosed()) - require.Empty(t, out.GetMessages()) + require.Empty(t, out.GetRecords()) case <-time.After(25 * time.Second): t.Fatal("long poll did not wake on close") } @@ -413,7 +414,7 @@ func TestStreamLongPollReturnsEmptyOnTimeout(t *testing.T) { start := time.Now() out, err := s.pollWait(ctx, id, 0) require.NoError(t, err) - require.Empty(t, out.GetMessages()) + require.Empty(t, out.GetRecords()) require.Equal(t, int64(0), out.GetNextOffset()) require.False(t, out.GetClosed()) require.Greater(t, time.Since(start), 5*time.Second, @@ -426,7 +427,7 @@ func TestStreamLongPollReturnsImmediatelyWhenBehind(t *testing.T) { const id = "stream-longpoll-behind" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b")}) require.NoError(t, err) // Waiting is only for a reader that is caught up. One that is behind must @@ -434,7 +435,7 @@ func TestStreamLongPollReturnsImmediatelyWhenBehind(t *testing.T) { start := time.Now() out, err := s.pollWait(ctx, id, 0) require.NoError(t, err) - require.Equal(t, []string{"a", "b"}, bodies(out.GetMessages())) + require.Equal(t, []string{"a", "b"}, bodies(out.GetRecords())) require.Less(t, time.Since(start), 5*time.Second) } @@ -452,7 +453,7 @@ func TestStreamCapTruncatesAndReclaims(t *testing.T) { require.NoError(t, err) for _, batch := range [][]string{{"a", "b"}, {"c", "d"}, {"e", "f"}} { - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", batch...)}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", batch...)}) require.NoError(t, err) } @@ -460,7 +461,7 @@ func TestStreamCapTruncatesAndReclaims(t *testing.T) { // asking. Reading from the floor still works and returns exactly what the // cap retained. got := s.poll(ctx, t, id, 2) - require.Equal(t, []string{"c", "d", "e", "f"}, bodies(got.GetMessages())) + require.Equal(t, []string{"c", "d", "e", "f"}, bodies(got.GetRecords())) // Below the floor is a distinguishable error, not silence. _, err = s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ @@ -475,7 +476,7 @@ func TestStreamClosedStaysReadable(t *testing.T) { const id = "stream-closed-readable" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "a", "b")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b")}) require.NoError(t, err) _, err = s.client.CloseStream(ctx, &streampb.CloseStreamRequest{ FrontendRequest: &streampb.CloseStreamInput{Namespace: s.ns, StreamId: id}, @@ -486,7 +487,7 @@ func TestStreamClosedStaysReadable(t *testing.T) { // coordinating a shutdown with the producer, which is the handshake the // signal-based implementation forces today. got := s.poll(ctx, t, id, 0) - require.Equal(t, []string{"a", "b"}, bodies(got.GetMessages())) + require.Equal(t, []string{"a", "b"}, bodies(got.GetRecords())) require.True(t, got.GetClosed()) desc, err := s.client.DescribeStream(ctx, &streampb.DescribeStreamRequest{ @@ -538,26 +539,26 @@ func TestStreamPollReadsOnlyWhatItReturns(t *testing.T) { for i := range 40 { _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ - Messages: streamMsgs("", fmt.Sprintf("m%d", i)), + Records: streamMsgs("", fmt.Sprintf("m%d", i)), }) require.NoError(t, err) } got := s.pollMax(ctx, t, id, 0, 1) - require.Equal(t, []string{"m0"}, bodies(got.GetMessages())) + require.Equal(t, []string{"m0"}, bodies(got.GetRecords())) require.Equal(t, int64(1), got.GetNextOffset(), "a capped read advances only over what it gave") require.Equal(t, int64(40), got.GetHeadOffset(), "the frontier is still reported in full") // Paging from there covers the rest, so the cap trims the read and not the stream. got = s.pollMax(ctx, t, id, got.GetNextOffset(), 10) require.Equal(t, []string{"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", "m10"}, - bodies(got.GetMessages())) + bodies(got.GetRecords())) require.Equal(t, int64(11), got.GetNextOffset()) // A filter finding nothing in the page still has to leave the reader able to // go on, and it must stop at the page rather than scanning to the head. got = s.pollMaxTopics(ctx, t, id, 0, 5, "nothing-matches-this") - require.Empty(t, got.GetMessages()) + require.Empty(t, got.GetRecords()) require.Equal(t, int64(5), got.GetNextOffset(), "a filtered page advanced past its own bound, so the read ran to the head") } @@ -570,10 +571,10 @@ func TestStreamPollAfterIdIsReusedServesTheNewStream(t *testing.T) { const id = "stream-reused-id" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "old")}) + _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "old")}) require.NoError(t, err) // Read it back so the bytes are certain to be cached before the id is reused. - require.Equal(t, []string{"old"}, bodies(s.poll(ctx, t, id, 0).GetMessages())) + require.Equal(t, []string{"old"}, bodies(s.poll(ctx, t, id, 0).GetRecords())) _, err = s.client.DeleteStream(ctx, &streampb.DeleteStreamRequest{ FrontendRequest: &streampb.DeleteStreamInput{Namespace: s.ns, StreamId: id}, @@ -581,11 +582,11 @@ func TestStreamPollAfterIdIsReusedServesTheNewStream(t *testing.T) { require.NoError(t, err) s.create(ctx, t, id) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Messages: streamMsgs("", "new")}) + _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "new")}) require.NoError(t, err) got := s.poll(ctx, t, id, 0) - require.Equal(t, []string{"new"}, bodies(got.GetMessages()), + require.Equal(t, []string{"new"}, bodies(got.GetRecords()), "a reused id served bytes from the deleted stream") } @@ -599,14 +600,14 @@ func TestStreamFilteredReadReportsRealOffsets(t *testing.T) { for i, topic := range []string{"a", "b", "a", "b", "a"} { _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ - Messages: streamMsgs(topic, fmt.Sprintf("m%d", i)), + Records: streamMsgs(topic, fmt.Sprintf("m%d", i)), }) require.NoError(t, err) } got := s.pollMaxTopics(ctx, t, id, 0, 0, "a") - require.Equal(t, []string{"m0", "m2", "m4"}, bodies(got.GetMessages())) - require.Equal(t, []int64{0, 2, 4}, offsets(got.GetMessages())) + require.Equal(t, []string{"m0", "m2", "m4"}, bodies(got.GetRecords())) + require.Equal(t, []int64{0, 2, 4}, offsets(got.GetRecords())) } func (s *streamTestEnv) pollMaxTopics( @@ -637,7 +638,7 @@ func (s *streamTestEnv) pollMax( return resp.GetFrontendResponse() } -func offsets(msgs []*streampb.StreamMessage) []int64 { +func offsets(msgs []*streampb.StreamRecord) []int64 { out := make([]int64, len(msgs)) for i, m := range msgs { out[i] = m.GetOffset() diff --git a/tests/stream_workflow_test.go b/tests/stream_workflow_test.go index 118c2bf494f..c62899e539b 100644 --- a/tests/stream_workflow_test.go +++ b/tests/stream_workflow_test.go @@ -70,10 +70,10 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { } published = true return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("planning")}, Topic: "progress"}, {Body: &commonpb.Payload{Data: []byte("calling tool")}, Topic: "progress"}, }, @@ -91,15 +91,15 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { // One event for the batch, holding the range and none of the payload. Two // messages were published, so it has to name both of them and stop there. events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}) - var added []*historypb.WorkflowStreamMessagesAddedEventAttributes + var added []*historypb.WorkflowStreamRecordsAppendedEventAttributes for _, e := range events { - if a := e.GetWorkflowStreamMessagesAddedEventAttributes(); a != nil { + if a := e.GetWorkflowStreamRecordsAppendedEventAttributes(); a != nil { added = append(added, a) } } require.Len(t, added, 1, "one publish command writes one event") require.Equal(t, int64(0), added[0].GetFirstOffset()) - require.Equal(t, int64(2), added[0].GetMessageCount()) + require.Equal(t, int64(2), added[0].GetRecordCount()) require.Equal(t, chasmworkflow.DefaultStreamName, added[0].GetStreamId(), "an unnamed stream resolves to the default before it is recorded") @@ -121,8 +121,8 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { }) require.NoError(t, err) require.Equal(t, []string{"planning", "calling tool"}, - bodies(poll.GetFrontendResponse().GetMessages())) - require.Equal(t, []int64{0, 1}, offsets(poll.GetFrontendResponse().GetMessages()), + bodies(poll.GetFrontendResponse().GetRecords())) + require.Equal(t, []int64{0, 1}, offsets(poll.GetFrontendResponse().GetRecords()), "each message carries where it sits in the log, so a reader can resume between them") require.Equal(t, int64(2), poll.GetFrontendResponse().GetNextOffset()) require.Equal(t, int64(2), poll.GetFrontendResponse().GetHeadOffset()) @@ -135,7 +135,7 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { }, }) require.NoError(t, err) - require.Empty(t, filtered.GetFrontendResponse().GetMessages()) + require.Empty(t, filtered.GetFrontendResponse().GetRecords()) require.Equal(t, int64(2), filtered.GetFrontendResponse().GetNextOffset(), "a filtered page still advances the reader") @@ -147,7 +147,7 @@ func TestStreamWorkflowPublishesWithARangeEvent(t *testing.T) { }, }) require.NoError(t, err) - require.Empty(t, unwritten.GetFrontendResponse().GetMessages()) + require.Empty(t, unwritten.GetFrontendResponse().GetRecords()) require.Equal(t, int64(0), unwritten.GetFrontendResponse().GetHeadOffset()) require.False(t, unwritten.GetFrontendResponse().GetClosed()) @@ -222,10 +222,10 @@ func TestStreamWorkflowLongPollWakesOnPublish(t *testing.T) { *workflowservice.PollWorkflowTaskQueueResponse, ) ([]*commandpb.Command, error) { return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("first token")}}, }, }, @@ -241,7 +241,7 @@ func TestStreamWorkflowLongPollWakesOnPublish(t *testing.T) { select { case r := <-done: require.NoError(t, r.err) - require.Equal(t, []string{"first token"}, bodies(r.out.GetMessages())) + require.Equal(t, []string{"first token"}, bodies(r.out.GetRecords())) require.Equal(t, int64(1), r.out.GetNextOffset()) case <-time.After(25 * time.Second): t.Fatal("the parked reader did not wake when the workflow published") @@ -278,9 +278,9 @@ func TestStreamWorkflowTakesAppendsFromOutsideToo(t *testing.T) { resp, err := s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ FrontendRequest: &streamlib.AddWorkflowMessagesInput{ Namespace: s.ns, WorkflowId: id, - Messages: []*streamlib.StreamMessage{{ + Records: []*streamlib.StreamRecord{{ Body: &commonpb.Payload{Data: []byte(body)}, - Kind: streamlib.STREAM_MESSAGE_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, }}, }, }) @@ -302,10 +302,10 @@ func TestStreamWorkflowTakesAppendsFromOutsideToo(t *testing.T) { *workflowservice.PollWorkflowTaskQueueResponse, ) ([]*commandpb.Command, error) { return []*commandpb.Command{{ - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("turn ended")}}, }, }, @@ -329,15 +329,15 @@ func TestStreamWorkflowTakesAppendsFromOutsideToo(t *testing.T) { }) require.NoError(t, err) require.Equal(t, []string{"token one", "turn ended", "token two"}, - bodies(poll.GetFrontendResponse().GetMessages()), + bodies(poll.GetFrontendResponse().GetRecords()), "both producers write one ordered log") // The event names the offset the workflow's own publish landed at, which // only holds if the workflow saw the outside writer's message first. events := env.GetHistory(s.ns, &commonpb.WorkflowExecution{WorkflowId: id}) - var added []*historypb.WorkflowStreamMessagesAddedEventAttributes + var added []*historypb.WorkflowStreamRecordsAppendedEventAttributes for _, e := range events { - if a := e.GetWorkflowStreamMessagesAddedEventAttributes(); a != nil { + if a := e.GetWorkflowStreamRecordsAppendedEventAttributes(); a != nil { added = append(added, a) } } @@ -392,10 +392,10 @@ func TestStreamWorkflowStreamClosesWithItsWorkflow(t *testing.T) { ) ([]*commandpb.Command, error) { return []*commandpb.Command{ { - CommandType: enumspb.COMMAND_TYPE_ADD_STREAM_MESSAGES, - Attributes: &commandpb.Command_AddStreamMessagesCommandAttributes{ - AddStreamMessagesCommandAttributes: &commandpb.AddStreamMessagesCommandAttributes{ - Messages: []*streampb.StreamMessage{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: []*streampb.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("last word")}}, }, }, @@ -420,7 +420,7 @@ func TestStreamWorkflowStreamClosesWithItsWorkflow(t *testing.T) { }, }) require.NoError(t, err) - require.Equal(t, []string{"last word"}, bodies(poll.GetFrontendResponse().GetMessages())) + require.Equal(t, []string{"last word"}, bodies(poll.GetFrontendResponse().GetRecords())) require.True(t, poll.GetFrontendResponse().GetClosed()) } @@ -459,7 +459,7 @@ func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { _, err := s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ FrontendRequest: &streamlib.AddWorkflowMessagesInput{ Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), - Messages: []*streamlib.StreamMessage{{ + Records: []*streamlib.StreamRecord{{ Body: &commonpb.Payload{Data: []byte(fmt.Sprintf("outside-%d-%d", p, i))}, }}, }, @@ -488,7 +488,7 @@ func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(total), poll.GetFrontendResponse().GetHeadOffset()) seen := make(map[string]int, total) - for _, body := range bodies(poll.GetFrontendResponse().GetMessages()) { + for _, body := range bodies(poll.GetFrontendResponse().GetRecords()) { seen[body]++ } require.Len(t, seen, producers*perProducer+1, "every outside message appears exactly once") @@ -530,18 +530,18 @@ func TestOwnedStreamRefusesAppendsPastItsBudget(t *testing.T) { _, err = poller.PollAndProcessWorkflowTask() require.Error(t, err, "two more do not fit in a budget of three") failed := workflowTaskFailedWith(env.GetHistory(s.ns, execution), - enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES) + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES) require.NotNil(t, failed) require.Contains(t, failed.GetFailure().GetMessage(), "budget") appendOutside := func(bodies ...string) error { - messages := make([]*streamlib.StreamMessage, len(bodies)) + messages := make([]*streamlib.StreamRecord, len(bodies)) for i, b := range bodies { - messages[i] = &streamlib.StreamMessage{Body: &commonpb.Payload{Data: []byte(b)}} + messages[i] = &streamlib.StreamRecord{Body: &commonpb.Payload{Data: []byte(b)}} } _, err := s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ FrontendRequest: &streamlib.AddWorkflowMessagesInput{ - Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), Messages: messages, + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), Records: messages, }, }) return err diff --git a/tests/streaming_native_test.go b/tests/streaming_native_test.go index 935dcc10b00..b61a8b9085f 100644 --- a/tests/streaming_native_test.go +++ b/tests/streaming_native_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" + apistreampb "go.temporal.io/api/stream/v1" streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "go.temporal.io/server/common/metrics" "go.temporal.io/server/tests/testcore" @@ -122,7 +123,7 @@ func runNativeProducer( deadline := time.Now().Add(p.duration) seq := 0 - var pending []*streampb.StreamMessage + var pending []*streampb.StreamRecord sequence := int64(0) flush := func() bool { @@ -134,7 +135,7 @@ func runNativeProducer( sequence++ _, err := client.AddMessages(ctx, &streampb.AddMessagesRequest{ FrontendRequest: &streampb.AddMessagesInput{ - Namespace: ns, StreamId: streamID, RunId: runID, Messages: batch, + Namespace: ns, StreamId: streamID, RunId: runID, Records: batch, ProducerId: "bench", Sequence: sequence, }, }) @@ -152,9 +153,9 @@ func runNativeProducer( return seq case <-genTicker.C: sentAt.Store(seq, time.Now()) - pending = append(pending, &streampb.StreamMessage{ + pending = append(pending, &streampb.StreamRecord{ Body: &commonpb.Payload{Data: payload}, - Kind: streampb.STREAM_MESSAGE_KIND_DATA, + Kind: apistreampb.STREAM_RECORD_KIND_DATA, }) seq++ case <-flushTicker.C: @@ -196,7 +197,7 @@ func runNativeConsumer( } received := time.Now() fr := resp.GetFrontendResponse() - for range fr.GetMessages() { + for range fr.GetRecords() { if v, ok := sentAt.Load(int(lastSeen)); ok { out = append(out, received.Sub(v.(time.Time))) } From 3f3f26904d330db32b3d455610d3db4f146b2ef1 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 01:42:42 -0700 Subject: [PATCH 39/47] Created the owned stream when a workflow subscribes to it by name. A workflow reads a topic before anything has been written to it, and the name it subscribes with is the stream an outside producer later appends to. A subscribe that names neither an owned stream nor a standalone one now creates the owned stream instead of failing the task. --- chasm/lib/workflow/stream_cursor_test.go | 16 ++++- chasm/lib/workflow/workflow.go | 12 ++-- .../stream_appends.go | 20 +++++- tests/stream_rejection_test.go | 61 +++++++++++++++---- 4 files changed, 86 insertions(+), 23 deletions(-) diff --git a/chasm/lib/workflow/stream_cursor_test.go b/chasm/lib/workflow/stream_cursor_test.go index b0cbba876da..db65d0cc2fc 100644 --- a/chasm/lib/workflow/stream_cursor_test.go +++ b/chasm/lib/workflow/stream_cursor_test.go @@ -91,12 +91,22 @@ func TestSubscribeFromTheTailResolvesToHead(t *testing.T) { require.Equal(t, int64(4), start, "a negative offset means from wherever the stream is now") } -func TestSubscribeRejectsAStreamTheWorkflowDoesNotOwn(t *testing.T) { +// A workflow reads a topic by name before anything has been written to it, so +// subscribing has to bring the stream into being the way a first write does. +func TestSubscribeCreatesTheStreamItNames(t *testing.T) { ctx := newStreamCursorTestContext() w := &Workflow{} - _, err := w.SubscribeToOwnedStream(ctx, "absent", 0, stream.DefaultLimits()) - require.ErrorContains(t, err, "does not own a stream") + start, err := w.SubscribeToOwnedStream(ctx, "inputs", 0, stream.DefaultLimits()) + require.NoError(t, err) + require.Equal(t, int64(0), start) + + field, ok := w.Streams["inputs"] + require.True(t, ok, "the subscribed name is now an owned stream") + state, err := field.Get(ctx).Snapshot(ctx, struct{}{}) + require.NoError(t, err) + require.Equal(t, int64(0), state.GetHeadOffset()) + require.Len(t, state.GetConsumers(), 1, "the subscription pinned the new stream") } // Committing a delivered range moves the consumer's cursor on the stream, so diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index dd922e2b269..1c32b7df32f 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -106,11 +106,13 @@ func (w *Workflow) SubscribeToOwnedStream( startOffset int64, limits stream.Limits, ) (int64, error) { - field, ok := w.Streams[name] - if !ok { - return 0, serviceerror.NewNotFoundf("workflow does not own a stream named %q", name) + // Created here as well as on first write, so a workflow can start reading a + // topic before anything has been published to it. An outside producer that + // arrives later appends to the same stream. + owned, err := w.streamNamed(mctx, name, limits) + if err != nil { + return 0, err } - owned := field.Get(mctx) if w.StreamCursors == nil { w.StreamCursors = make(chasm.Map[string, *stream.Cursor]) @@ -125,7 +127,7 @@ func (w *Workflow) SubscribeToOwnedStream( // separately it could be lost while the cursor survived, and truncation // would then be free to take a range the cursor still points at. key := mctx.ExecutionKey() - startOffset, err := owned.RegisterConsumer(mctx, stream.ConsumerRegistration{ + startOffset, err = owned.RegisterConsumer(mctx, stream.ConsumerRegistration{ ConsumerID: streamConsumerID(name), WorkflowID: key.BusinessID, RunID: key.RunID, diff --git a/service/history/api/respondworkflowtaskcompleted/stream_appends.go b/service/history/api/respondworkflowtaskcompleted/stream_appends.go index e0a6499d10a..fa73426b14c 100644 --- a/service/history/api/respondworkflowtaskcompleted/stream_appends.go +++ b/service/history/api/respondworkflowtaskcompleted/stream_appends.go @@ -2,6 +2,7 @@ package respondworkflowtaskcompleted import ( "context" + "errors" enumspb "go.temporal.io/api/enums/v1" "go.temporal.io/api/serviceerror" @@ -82,8 +83,23 @@ func resolveStagedStreamSubscriptions( pin, err := registerExternalConsumer(ctx, ms, namespaceID, pending) if err != nil { - return chasmworkflow.StreamAdmissionFailure( - enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES, err) + var notFound *serviceerror.NotFound + if !errors.As(err, ¬Found) { + return chasmworkflow.StreamAdmissionFailure( + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES, err) + } + // No execution holds a stream by this id, so the name is one of this + // workflow's own that nothing has written to yet. A reader has to be + // able to subscribe before the first record arrives, so the stream + // is created here and the subscription lands on it. + startOffset, err := wf.SubscribeToOwnedStream( + chasmCtx, pending.StreamID, pending.StartOffset, limits) + if err != nil { + return chasmworkflow.StreamAdmissionFailure( + enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES, err) + } + chasmworkflow.RecordStreamSubscribedOffset(pending.Event, startOffset) + continue } if _, err := wf.SubscribeToExternalStream(chasmCtx, chasmworkflow.ExternalStreamSubscription{ diff --git a/tests/stream_rejection_test.go b/tests/stream_rejection_test.go index 248172b7a6f..be463629351 100644 --- a/tests/stream_rejection_test.go +++ b/tests/stream_rejection_test.go @@ -81,15 +81,16 @@ func TestOverLimitPublishFailsTheWorkflowTask(t *testing.T) { require.Equal(t, []int64{0}, offsets(poll.GetFrontendResponse().GetRecords())) } -// A subscribe naming a stream that does not exist is refused where the stream -// is looked up, which is after the commands and before the commit. It still has -// to surface as a failed task with a cause, and leave no half-made subscription. -func TestSubscribeToAMissingStreamFailsTheWorkflowTask(t *testing.T) { +// A workflow reads a topic by name before anything has been written to it, so +// the subscribe has to bring the owned stream into being rather than fail the +// task. An outside producer that arrives later appends to that same stream, +// and its records reach the workflow carrying the identity it wrote. +func TestSubscribeToAnUnwrittenNameCreatesTheOwnedStream(t *testing.T) { env := testcore.NewEnv(t) s := newStreamTestEnvFrom(t, env) - execution, tq := startConsumer(t, s, "stream-wf-reject-subscribe-") + execution, tq := startConsumer(t, s, "stream-wf-subscribe-first-") - missing := "no-such-stream-" + uuid.NewString() + name := "inputs-" + uuid.NewString() var delivered [][]*streampb.StreamSlice //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. poller := &testcore.TaskPoller{ @@ -102,7 +103,7 @@ func TestSubscribeToAMissingStreamFailsTheWorkflowTask(t *testing.T) { ) ([]*commandpb.Command, error) { delivered = append(delivered, resp.GetStreamSlices()) if len(delivered) == 1 { - return subscribeCommand(missing), nil + return subscribeCommand(name), nil } return nil, nil }, @@ -111,17 +112,51 @@ func TestSubscribeToAMissingStreamFailsTheWorkflowTask(t *testing.T) { } _, err := poller.PollAndProcessWorkflowTask() - require.Error(t, err) + require.NoError(t, err) events := env.GetHistory(s.ns, execution) - require.NotNil(t, workflowTaskFailedWith(events, + require.Nil(t, workflowTaskFailedWith(events, enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES)) - require.Empty(t, subscribedEvents(events), "a refused subscribe writes no event") + subscribed := subscribedEvents(events) + require.Len(t, subscribed, 1) + require.Equal(t, name, subscribed[0].GetStreamId()) + require.Equal(t, int64(0), subscribed[0].GetStartOffset(), "the new stream starts empty") + + _, err = s.client.AddWorkflowMessages(s.ctx(), &streamlib.AddWorkflowMessagesRequest{ + FrontendRequest: &streamlib.AddWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), StreamName: name, + Records: []*streamlib.StreamRecord{ + { + Body: &commonpb.Payload{Data: []byte("hello")}, Topic: name, + ProducerId: "model", Attempt: 2, Sequence: 0, + }, + { + Kind: streampb.STREAM_RECORD_KIND_FINISH, Topic: name, + ProducerId: "model", Attempt: 2, Sequence: -1, + }, + }, + }, + }) + require.NoError(t, err) - // The retry carries no slice, because no cursor was made. - _, err = poller.PollAndProcessWorkflowTask(testcore.WithExpectedAttemptCount(2)) + _, err = poller.PollAndProcessWorkflowTask() require.NoError(t, err) - require.Empty(t, delivered[1]) + got := currentSlice(t, delivered[1]) + require.Equal(t, name, got.GetStreamId()) + require.Equal(t, int64(2), got.GetToOffset()) + + records := got.GetRecords() + require.Len(t, records, 2) + require.Equal(t, "hello", string(records[0].GetBody().GetData())) + require.Equal(t, streampb.STREAM_RECORD_KIND_DATA, records[0].GetKind(), + "a record with no kind is data") + require.Equal(t, "model", records[0].GetProducerId()) + require.Equal(t, int64(2), records[0].GetAttempt()) + require.Equal(t, int64(0), records[0].GetSequence()) + require.Equal(t, streampb.STREAM_RECORD_KIND_FINISH, records[1].GetKind(), + "a finish record is delivered like any other") + require.Equal(t, "model", records[1].GetProducerId()) + require.Equal(t, int64(-1), records[1].GetSequence()) } // The guarantee the design is sold on: a publish commits with the workflow From cf095d2e9fecbbe400b4e29e140d739d61cdbe5f Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 04:23:28 -0700 Subject: [PATCH 40/47] Applied the repo formatter to the stream service proto and race test. --- chasm/lib/stream/proto/v1/service.proto | 6 ++---- tests/stream_workflow_test.go | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/chasm/lib/stream/proto/v1/service.proto b/chasm/lib/stream/proto/v1/service.proto index 1d358a53caa..bdd0a918551 100644 --- a/chasm/lib/stream/proto/v1/service.proto +++ b/chasm/lib/stream/proto/v1/service.proto @@ -45,8 +45,7 @@ service StreamService { option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_LONG_POLL; } - rpc DescribeWorkflowStream(DescribeWorkflowStreamRequest) - returns (DescribeWorkflowStreamResponse) { + rpc DescribeWorkflowStream(DescribeWorkflowStreamRequest) returns (DescribeWorkflowStreamResponse) { option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.workflow_id"; option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; } @@ -59,8 +58,7 @@ service StreamService { // Internal. History calls these on itself to reach a shard it does not own, // which is the only way a step that spans two executions can work on a // cluster with more than one history host. - rpc RegisterStreamConsumer(RegisterStreamConsumerRequest) - returns (RegisterStreamConsumerResponse) { + rpc RegisterStreamConsumer(RegisterStreamConsumerRequest) returns (RegisterStreamConsumerResponse) { option (temporal.server.api.routing.v1.routing).business_id = "frontend_request.stream_id"; option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; } diff --git a/tests/stream_workflow_test.go b/tests/stream_workflow_test.go index c62899e539b..72242761f22 100644 --- a/tests/stream_workflow_test.go +++ b/tests/stream_workflow_test.go @@ -460,7 +460,7 @@ func TestOutsideAppendsRaceTheWorkflowPublishWithoutFailing(t *testing.T) { FrontendRequest: &streamlib.AddWorkflowMessagesInput{ Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), Records: []*streamlib.StreamRecord{{ - Body: &commonpb.Payload{Data: []byte(fmt.Sprintf("outside-%d-%d", p, i))}, + Body: &commonpb.Payload{Data: fmt.Appendf(nil, "outside-%d-%d", p, i)}, }}, }, }) From ed16b239f47b0dbf663effa7089015fc82889d3a Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 04:23:58 -0700 Subject: [PATCH 41/47] Imported the api stream package as streampb wherever both stream packages meet. The importas rule reserves the pb suffix for go.temporal.io/api packages, so the internal chasm package takes the streamlib alias the rest of the tree already uses for it. --- chasm/lib/stream/messages_test.go | 22 ++-- chasm/lib/stream/service/handler_test.go | 14 +-- chasm/lib/stream/stream.go | 48 +++---- chasm/lib/stream/stream_test.go | 44 +++---- tests/stream_test.go | 152 +++++++++++------------ tests/streaming_native_test.go | 28 ++--- 6 files changed, 154 insertions(+), 154 deletions(-) diff --git a/chasm/lib/stream/messages_test.go b/chasm/lib/stream/messages_test.go index 86aca440b3a..ee7fd463a8c 100644 --- a/chasm/lib/stream/messages_test.go +++ b/chasm/lib/stream/messages_test.go @@ -5,16 +5,16 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" - apistreampb "go.temporal.io/api/stream/v1" - streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + streampb "go.temporal.io/api/stream/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" ) -func sized(n int, bytes int) []*streampb.StreamRecord { - out := make([]*streampb.StreamRecord, n) +func sized(n int, bytes int) []*streamlib.StreamRecord { + out := make([]*streamlib.StreamRecord, n) for i := range out { - out[i] = &streampb.StreamRecord{ + out[i] = &streamlib.StreamRecord{ Body: &commonpb.Payload{Data: make([]byte, bytes)}, - Kind: apistreampb.STREAM_RECORD_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, } } return out @@ -52,12 +52,12 @@ func TestCapByBytesOnAnEmptyRun(t *testing.T) { // producer identity included. A FINISH record is a record like any other to the // consumer that reads it. func TestToAPIRecordsCarriesTheRecordAsWritten(t *testing.T) { - stored := []*streampb.StreamRecord{ + stored := []*streamlib.StreamRecord{ { Body: &commonpb.Payload{Data: []byte("token")}, Metadata: map[string]*commonpb.Payload{"model": {Data: []byte("m1")}}, Topic: "tokens", - Kind: apistreampb.STREAM_RECORD_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, ProducerId: "model-call", Attempt: 2, Sequence: 7, @@ -65,7 +65,7 @@ func TestToAPIRecordsCarriesTheRecordAsWritten(t *testing.T) { }, { Topic: "tokens", - Kind: apistreampb.STREAM_RECORD_KIND_FINISH, + Kind: streampb.STREAM_RECORD_KIND_FINISH, ProducerId: "model-call", Attempt: 2, Sequence: -1, @@ -79,12 +79,12 @@ func TestToAPIRecordsCarriesTheRecordAsWritten(t *testing.T) { require.Equal(t, "token", string(got[0].GetBody().GetData())) require.Equal(t, "m1", string(got[0].GetMetadata()["model"].GetData())) require.Equal(t, "tokens", got[0].GetTopic()) - require.Equal(t, apistreampb.STREAM_RECORD_KIND_DATA, got[0].GetKind()) + require.Equal(t, streampb.STREAM_RECORD_KIND_DATA, got[0].GetKind()) require.Equal(t, "model-call", got[0].GetProducerId()) require.Equal(t, int64(2), got[0].GetAttempt()) require.Equal(t, int64(7), got[0].GetSequence()) - require.Equal(t, apistreampb.STREAM_RECORD_KIND_FINISH, got[1].GetKind()) + require.Equal(t, streampb.STREAM_RECORD_KIND_FINISH, got[1].GetKind()) require.Nil(t, got[1].GetBody()) require.Equal(t, "model-call", got[1].GetProducerId()) require.Equal(t, int64(-1), got[1].GetSequence()) diff --git a/chasm/lib/stream/service/handler_test.go b/chasm/lib/stream/service/handler_test.go index b87ef91c25e..68d4259859c 100644 --- a/chasm/lib/stream/service/handler_test.go +++ b/chasm/lib/stream/service/handler_test.go @@ -6,23 +6,23 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" - apistreampb "go.temporal.io/api/stream/v1" + streampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm/lib/stream" - streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "google.golang.org/protobuf/proto" ) func batchBlob(t *testing.T, topic string, bodies ...string) *commonpb.DataBlob { t.Helper() - messages := make([]*streampb.StreamRecord, len(bodies)) + messages := make([]*streamlib.StreamRecord, len(bodies)) for i, b := range bodies { - messages[i] = &streampb.StreamRecord{ + messages[i] = &streamlib.StreamRecord{ Body: &commonpb.Payload{Data: []byte(b)}, Topic: topic, - Kind: apistreampb.STREAM_RECORD_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, } } - data, err := proto.Marshal(&streampb.StreamRecordBatch{Records: messages}) + data, err := proto.Marshal(&streamlib.StreamRecordBatch{Records: messages}) require.NoError(t, err) return &commonpb.DataBlob{EncodingType: enumspb.ENCODING_TYPE_PROTO3, Data: data} } @@ -33,7 +33,7 @@ func batchBlob(t *testing.T, topic string, bodies ...string) *commonpb.DataBlob // steps over messages it was never shown. func TestFormatWindowAdvancesOnlyOverExaminedOffsets(t *testing.T) { w := stream.Window{ - State: &streampb.StreamState{HeadOffset: 10}, + State: &streamlib.StreamState{HeadOffset: 10}, Blobs: []*commonpb.DataBlob{batchBlob(t, "a", "m0", "m1", "m2")}, Starts: []int64{0}, To: 5, diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index 6887b06c86f..f54a41f5fcd 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -10,9 +10,9 @@ import ( commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" "go.temporal.io/api/serviceerror" - apistreampb "go.temporal.io/api/stream/v1" + streampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm" - streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "go.temporal.io/server/common" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" @@ -28,7 +28,7 @@ import ( type Stream struct { chasm.UnimplementedComponent - State *streampb.StreamState + State *streamlib.StreamState // Batches holds the payload, each keyed by the offset it starts at. They are // data nodes, so they replicate with the component and are reclaimed with @@ -45,11 +45,11 @@ type Stream struct { } type NewStreamRequest struct { - Lifecycle *streampb.StreamLifecycle + Lifecycle *streamlib.StreamLifecycle // Budget bounds what the stream may hold. Set for a stream a workflow // owns, whose batches are the owning execution's mutable state. - Budget *streampb.StreamBudget + Budget *streamlib.StreamBudget // Attached means the stream is a subcomponent of another execution rather // than a root. CHASM requires a visibility component to be an immediate @@ -59,7 +59,7 @@ type NewStreamRequest struct { } type AddMessagesRequest struct { - Records []*streampb.StreamRecord + Records []*streamlib.StreamRecord // Optional idempotency. A producer supplies either an identity and // sequence, or an expected offset, or neither and accepts at-least-once. @@ -94,11 +94,11 @@ func NewStream(ctx chasm.MutableContext, req NewStreamRequest) (*Stream, error) return &Stream{ Visibility: visibility, Batches: make(chasm.Map[int64, *commonpb.DataBlob]), - State: &streampb.StreamState{ + State: &streamlib.StreamState{ Lifecycle: req.Lifecycle, Budget: req.Budget, - Producers: make(map[string]*streampb.ProducerCursor), - Consumers: make(map[string]*streampb.ConsumerCursor), + Producers: make(map[string]*streamlib.ProducerCursor), + Consumers: make(map[string]*streamlib.ConsumerCursor), }, }, nil } @@ -122,7 +122,7 @@ func (s *Stream) Terminate( // Snapshot returns a copy of the frontier for read paths. It is a copy because // the caller reads it outside the transition that produced it. -func (s *Stream) Snapshot(_ chasm.Context, _ struct{}) (*streampb.StreamState, error) { +func (s *Stream) Snapshot(_ chasm.Context, _ struct{}) (*streamlib.StreamState, error) { return common.CloneProto(s.State), nil } @@ -159,8 +159,8 @@ func (s *Stream) AddMessages( // to agree on what that means. Settled before the batch is marshalled, so a // retry hashes the same bytes. for _, m := range req.Records { - if m.GetKind() == apistreampb.STREAM_RECORD_KIND_UNSPECIFIED { - m.Kind = apistreampb.STREAM_RECORD_KIND_DATA + if m.GetKind() == streampb.STREAM_RECORD_KIND_UNSPECIFIED { + m.Kind = streampb.STREAM_RECORD_KIND_DATA } } @@ -211,9 +211,9 @@ func (s *Stream) AddMessages( s.State.AppendedBytes += int64(len(blob.Data)) if req.ProducerID != "" { if s.State.Producers == nil { - s.State.Producers = make(map[string]*streampb.ProducerCursor) + s.State.Producers = make(map[string]*streamlib.ProducerCursor) } - s.State.Producers[req.ProducerID] = &streampb.ProducerCursor{ + s.State.Producers[req.ProducerID] = &streamlib.ProducerCursor{ Seq: req.Sequence, FirstOffset: first, Count: count, @@ -253,7 +253,7 @@ func (s *Stream) notifyConsumers(mctx chasm.MutableContext) { if consumer.GetExternal() && consumer.GetActive() && consumer.GetOffset() < s.State.HeadOffset { s.State.NotifyPending = true mctx.AddTask(s, chasm.TaskAttributes{ScheduledTime: mctx.Now(s)}, - &streampb.StreamNotifyConsumersTask{}) + &streamlib.StreamNotifyConsumersTask{}) return } } @@ -265,7 +265,7 @@ func (s *Stream) notifyConsumers(mctx chasm.MutableContext) { // and schedules its own task. func (s *Stream) TakeNotifySnapshot( _ chasm.MutableContext, _ struct{}, -) (*streampb.StreamState, error) { +) (*streamlib.StreamState, error) { s.State.NotifyPending = false return common.CloneProto(s.State), nil } @@ -366,11 +366,11 @@ func (s *Stream) FinishWriting(_ chasm.MutableContext, producerID string) error return serviceerror.NewInvalidArgument("producer id is required") } if s.State.Producers == nil { - s.State.Producers = make(map[string]*streampb.ProducerCursor) + s.State.Producers = make(map[string]*streamlib.ProducerCursor) } cursor := s.State.Producers[producerID] if cursor == nil { - cursor = &streampb.ProducerCursor{Seq: -1} + cursor = &streamlib.ProducerCursor{Seq: -1} s.State.Producers[producerID] = cursor } cursor.Fenced = true @@ -402,7 +402,7 @@ func (s *Stream) Close(now time.Time, reason *commonpb.Payload) time.Time { // CloseAndSchedule closes the stream and arms retention if it asked for it. func (s *Stream) CloseAndSchedule(mctx chasm.MutableContext, reason *commonpb.Payload) error { if at := s.Close(mctx.Now(s), reason); !at.IsZero() { - mctx.AddTask(s, chasm.TaskAttributes{ScheduledTime: at}, &streampb.StreamRetentionTask{}) + mctx.AddTask(s, chasm.TaskAttributes{ScheduledTime: at}, &streamlib.StreamRetentionTask{}) } return nil } @@ -494,7 +494,7 @@ type WindowRequest struct { // Window is one read's worth: the frontier it was served against, the batches // covering the range, and the range itself. type Window struct { - State *streampb.StreamState + State *streamlib.StreamState Blobs []*commonpb.DataBlob Starts []int64 To int64 @@ -684,7 +684,7 @@ func (s *Stream) RegisterConsumer(_ chasm.MutableContext, reg ConsumerRegistrati "stream already has %d consumers, which is the limit", maxConsumers) } if s.State.Consumers == nil { - s.State.Consumers = make(map[string]*streampb.ConsumerCursor) + s.State.Consumers = make(map[string]*streamlib.ConsumerCursor) } // One workflow id has one open run, so an entry for another run of this // workflow belongs to a closed run. Its floor would otherwise hold storage @@ -707,7 +707,7 @@ func (s *Stream) RegisterConsumer(_ chasm.MutableContext, reg ConsumerRegistrati existing.Active = true return existing.GetOffset(), nil } - s.State.Consumers[reg.ConsumerID] = &streampb.ConsumerCursor{ + s.State.Consumers[reg.ConsumerID] = &streamlib.ConsumerCursor{ WorkflowId: reg.WorkflowID, RunId: reg.RunID, Offset: offset, @@ -746,13 +746,13 @@ func (s *Stream) ForgetConsumer(_ chasm.MutableContext, consumerID string) { delete(s.State.Consumers, consumerID) } -func marshalBatch(records []*streampb.StreamRecord) (*commonpb.DataBlob, error) { +func marshalBatch(records []*streamlib.StreamRecord) (*commonpb.DataBlob, error) { // The serialized batch is also the producer's deduplication fingerprint, and // protobuf map iteration order is not stable. A record carrying payload or // record metadata would otherwise hash differently on a retry and be // refused as a conflicting duplicate of itself. data, err := (proto.MarshalOptions{Deterministic: true}).Marshal( - &streampb.StreamRecordBatch{Records: records}) + &streamlib.StreamRecordBatch{Records: records}) if err != nil { return nil, err } diff --git a/chasm/lib/stream/stream_test.go b/chasm/lib/stream/stream_test.go index 256e0f0a8cf..9438b8bc435 100644 --- a/chasm/lib/stream/stream_test.go +++ b/chasm/lib/stream/stream_test.go @@ -8,9 +8,9 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" "go.temporal.io/api/serviceerror" - apistreampb "go.temporal.io/api/stream/v1" + streampb "go.temporal.io/api/stream/v1" "go.temporal.io/server/chasm" - streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "google.golang.org/protobuf/types/known/durationpb" ) @@ -21,19 +21,19 @@ import ( func newTestStream(t *testing.T) *Stream { t.Helper() return &Stream{ - State: &streampb.StreamState{ - Producers: make(map[string]*streampb.ProducerCursor), - Consumers: make(map[string]*streampb.ConsumerCursor), + State: &streamlib.StreamState{ + Producers: make(map[string]*streamlib.ProducerCursor), + Consumers: make(map[string]*streamlib.ConsumerCursor), }, } } -func msgs(bodies ...string) []*streampb.StreamRecord { - out := make([]*streampb.StreamRecord, len(bodies)) +func msgs(bodies ...string) []*streamlib.StreamRecord { + out := make([]*streamlib.StreamRecord, len(bodies)) for i, b := range bodies { - out[i] = &streampb.StreamRecord{ + out[i] = &streamlib.StreamRecord{ Body: &commonpb.Payload{Data: []byte(b)}, - Kind: apistreampb.STREAM_RECORD_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, } } return out @@ -99,8 +99,8 @@ func TestDedupIgnoresPayloadMetadataMapOrder(t *testing.T) { }, } result, err := s.AddMessages(nil, AddMessagesRequest{ - Records: []*streampb.StreamRecord{{ - Kind: apistreampb.STREAM_RECORD_KIND_DATA, + Records: []*streamlib.StreamRecord{{ + Kind: streampb.STREAM_RECORD_KIND_DATA, Body: body, Metadata: map[string]*commonpb.Payload{"attempt": body, "checkpoint": body}, }}, @@ -247,7 +247,7 @@ func TestTruncateBounds(t *testing.T) { func TestCapTruncatesInline(t *testing.T) { s := newTestStream(t) - s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 4} + s.State.Lifecycle = &streamlib.StreamLifecycle{MaxItems: 4} for range 4 { _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) @@ -262,7 +262,7 @@ func TestCapTruncatesInline(t *testing.T) { func TestCapRefusesAnAppendItCouldOnlyAbsorbByDroppingReadRecords(t *testing.T) { s := newTestStream(t) - s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + s.State.Lifecycle = &streamlib.StreamLifecycle{MaxItems: 2} _, err := s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "wf-1", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, }) @@ -287,7 +287,7 @@ func TestCloseSchedulesRetentionOnlyWhenConfigured(t *testing.T) { require.True(t, plain.Close(now, nil).IsZero(), "no retention configured, nothing to schedule") withRetention := newTestStream(t) - withRetention.State.Lifecycle = &streampb.StreamLifecycle{ + withRetention.State.Lifecycle = &streamlib.StreamLifecycle{ Retention: durationpb.New(time.Hour), } at := withRetention.Close(now, nil) @@ -397,7 +397,7 @@ func TestDeregisterConsumerReleasesThePin(t *testing.T) { func TestMessageCapStillAppliesWithNoConsumerToProtect(t *testing.T) { s := newTestStream(t) - s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + s.State.Lifecycle = &streamlib.StreamLifecycle{MaxItems: 2} for range 3 { _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) @@ -415,7 +415,7 @@ func TestMessageCapStillAppliesWithNoConsumerToProtect(t *testing.T) { // its cap until it goes away. func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { s := newTestStream(t) - s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + s.State.Lifecycle = &streamlib.StreamLifecycle{MaxItems: 2} _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) @@ -423,7 +423,7 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, }) require.NoError(t, err) - s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 1} + s.State.Lifecycle = &streamlib.StreamLifecycle{MaxItems: 1} s.applyCap() require.Equal(t, int64(0), s.State.BaseOffset, "the clamp keeps what the consumer needs") @@ -433,7 +433,7 @@ func TestCapClampsToAConsumerThatRegisteredLate(t *testing.T) { // a consumer that finished has to stop holding storage. func TestAConsumerThatDeregisteredHoldsNothing(t *testing.T) { s := newTestStream(t) - s.State.Lifecycle = &streampb.StreamLifecycle{MaxItems: 2} + s.State.Lifecycle = &streamlib.StreamLifecycle{MaxItems: 2} _, err := s.RegisterConsumer(nil, ConsumerRegistration{ ConsumerID: "workflow:output", WorkflowID: "wf-1", RunID: "run-1", Offset: 0, }) @@ -538,7 +538,7 @@ func TestStreamConsumerTableIsBounded(t *testing.T) { // offset and never be seen by a subscriber. func TestAddMessagesTreatsAnUnsetKindAsData(t *testing.T) { s := newTestStream(t) - unset := []*streampb.StreamRecord{ + unset := []*streamlib.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("a")}}, {Body: &commonpb.Payload{Data: []byte("b")}}, } @@ -555,7 +555,7 @@ func TestAddMessagesTreatsAnUnsetKindAsData(t *testing.T) { // The retry carries the same unset kind and has to hash the same. retry, err := s.AddMessages(nil, AddMessagesRequest{ - Records: []*streampb.StreamRecord{ + Records: []*streamlib.StreamRecord{ {Body: &commonpb.Payload{Data: []byte("a")}}, {Body: &commonpb.Payload{Data: []byte("b")}}, }, @@ -570,7 +570,7 @@ func TestAddMessagesTreatsAnUnsetKindAsData(t *testing.T) { // to refusing is the execution size limit terminating that workflow later. func TestBudgetRefusesAnAppendThatDoesNotFit(t *testing.T) { s := newTestStream(t) - s.State.Budget = &streampb.StreamBudget{MaxItems: 3} + s.State.Budget = &streamlib.StreamBudget{MaxItems: 3} _, err := s.AddMessages(nil, AddMessagesRequest{Records: msgs("a", "b")}) require.NoError(t, err) @@ -585,7 +585,7 @@ func TestBudgetRefusesAnAppendThatDoesNotFit(t *testing.T) { require.NoError(t, err) bytesOnly := newTestStream(t) - bytesOnly.State.Budget = &streampb.StreamBudget{MaxBytes: 16} + bytesOnly.State.Budget = &streamlib.StreamBudget{MaxBytes: 16} _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Records: msgs("small")}) require.NoError(t, err) _, err = bytesOnly.AddMessages(nil, AddMessagesRequest{Records: msgs("another one")}) diff --git a/tests/stream_test.go b/tests/stream_test.go index 2fbe4542550..9f20800244f 100644 --- a/tests/stream_test.go +++ b/tests/stream_test.go @@ -8,9 +8,9 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" - apistreampb "go.temporal.io/api/stream/v1" + streampb "go.temporal.io/api/stream/v1" chasmstream "go.temporal.io/server/chasm/lib/stream" - streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "go.temporal.io/server/common/testing/await" "go.temporal.io/server/tests/testcore" "google.golang.org/grpc" @@ -26,7 +26,7 @@ const streamMaxBatch = chasmstream.MaxRecordsPerBatch type streamTestEnv struct { env *testcore.TestEnv - client streampb.StreamServiceClient + client streamlib.StreamServiceClient ns string cleanup []func() } @@ -45,7 +45,7 @@ func newStreamTestEnvFrom(t *testing.T, env *testcore.TestEnv) *streamTestEnv { t.Cleanup(func() { _ = conn.Close() }) env2 := &streamTestEnv{ - env: env, client: streampb.NewStreamServiceClient(conn), ns: env.Namespace().String(), + env: env, client: streamlib.NewStreamServiceClient(conn), ns: env.Namespace().String(), } t.Cleanup(func() { for _, c := range env2.cleanup { @@ -64,19 +64,19 @@ func (s *streamTestEnv) ctx() context.Context { func (s *streamTestEnv) create(ctx context.Context, t *testing.T, streamID string) { t.Helper() - _, err := s.client.CreateStream(ctx, &streampb.CreateStreamRequest{ - FrontendRequest: &streampb.CreateStreamInput{Namespace: s.ns, StreamId: streamID}, + _, err := s.client.CreateStream(ctx, &streamlib.CreateStreamRequest{ + FrontendRequest: &streamlib.CreateStreamInput{Namespace: s.ns, StreamId: streamID}, }) require.NoError(t, err) } func (s *streamTestEnv) add( - ctx context.Context, t *testing.T, streamID string, in *streampb.AddMessagesInput, -) (*streampb.AddMessagesOutput, error) { + ctx context.Context, t *testing.T, streamID string, in *streamlib.AddMessagesInput, +) (*streamlib.AddMessagesOutput, error) { t.Helper() in.Namespace = s.ns in.StreamId = streamID - resp, err := s.client.AddMessages(ctx, &streampb.AddMessagesRequest{FrontendRequest: in}) + resp, err := s.client.AddMessages(ctx, &streamlib.AddMessagesRequest{FrontendRequest: in}) if err != nil { return nil, err } @@ -85,10 +85,10 @@ func (s *streamTestEnv) add( func (s *streamTestEnv) poll( ctx context.Context, t *testing.T, streamID string, from int64, topics ...string, -) *streampb.PollMessagesOutput { +) *streamlib.PollMessagesOutput { t.Helper() - resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ - FrontendRequest: &streampb.PollMessagesInput{ + resp, err := s.client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{ Namespace: s.ns, StreamId: streamID, FromOffset: from, Topics: topics, }, }) @@ -96,19 +96,19 @@ func (s *streamTestEnv) poll( return resp.GetFrontendResponse() } -func streamMsgs(topic string, bodies ...string) []*streampb.StreamRecord { - out := make([]*streampb.StreamRecord, len(bodies)) +func streamMsgs(topic string, bodies ...string) []*streamlib.StreamRecord { + out := make([]*streamlib.StreamRecord, len(bodies)) for i, b := range bodies { - out[i] = &streampb.StreamRecord{ + out[i] = &streamlib.StreamRecord{ Body: &commonpb.Payload{Data: []byte(b)}, Topic: topic, - Kind: apistreampb.STREAM_RECORD_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, } } return out } -func bodies(msgs []*streampb.StreamRecord) []string { +func bodies(msgs []*streamlib.StreamRecord) []string { out := make([]string, len(msgs)) for i, m := range msgs { out[i] = string(m.GetBody().GetData()) @@ -129,12 +129,12 @@ func TestStreamAppendAndRead(t *testing.T) { s.create(ctx, t, id) first, err := s.add(ctx, t, id, - &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) + &streamlib.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) require.NoError(t, err) require.Equal(t, int64(0), first.GetFirstOffset()) require.Equal(t, int64(3), first.GetNextOffset()) - second, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "d")}) + second, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "d")}) require.NoError(t, err) require.Equal(t, int64(3), second.GetFirstOffset()) @@ -160,7 +160,7 @@ func TestStreamManyReadersAreIndependent(t *testing.T) { const id = "stream-many-readers" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "a", "b")}) require.NoError(t, err) // No durable per-subscriber state, so reader count is not a state-machine @@ -178,14 +178,14 @@ func TestStreamProducerDedup(t *testing.T) { const id = "stream-dedup" s.create(ctx, t, id) - in := &streampb.AddMessagesInput{ + in := &streamlib.AddMessagesInput{ Records: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, } first, err := s.add(ctx, t, id, in) require.NoError(t, err) require.False(t, first.GetDeduplicated()) - retry, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ + retry, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{ Records: streamMsgs("", "a", "b"), ProducerId: "p1", Sequence: 1, }) require.NoError(t, err) @@ -198,7 +198,7 @@ func TestStreamProducerDedup(t *testing.T) { // Same sequence with different content is a client bug. Returning the // recorded offsets would report success while dropping the data. - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{ Records: streamMsgs("", "different"), ProducerId: "p1", Sequence: 1, }) require.ErrorContains(t, err, "different content") @@ -210,11 +210,11 @@ func TestStreamTopicFilter(t *testing.T) { const id = "stream-topics" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("tokens", "t1")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("tokens", "t1")}) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("tools", "x1")}) + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("tools", "x1")}) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("tokens", "t2")}) + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("tokens", "t2")}) require.NoError(t, err) got := s.poll(ctx, t, id, 0, "tokens") @@ -229,20 +229,20 @@ func TestStreamFinishWritingIsPerProducer(t *testing.T) { const id = "stream-finish" s.create(ctx, t, id) - _, err := s.client.FinishWriting(ctx, &streampb.FinishWritingRequest{ - FrontendRequest: &streampb.FinishWritingInput{ + _, err := s.client.FinishWriting(ctx, &streamlib.FinishWritingRequest{ + FrontendRequest: &streamlib.FinishWritingInput{ Namespace: s.ns, StreamId: id, ProducerId: "p1", }, }) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{ Records: streamMsgs("", "a"), ProducerId: "p1", Sequence: 1, }) require.Error(t, err) // Finishing is per-producer, not a close, so others carry on. - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{ + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{ Records: streamMsgs("", "b"), ProducerId: "p2", Sequence: 1, }) require.NoError(t, err) @@ -254,11 +254,11 @@ func TestStreamCloseAndTruncate(t *testing.T) { const id = "stream-lifecycle" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) require.NoError(t, err) - _, err = s.client.TruncateStream(ctx, &streampb.TruncateStreamRequest{ - FrontendRequest: &streampb.TruncateStreamInput{ + _, err = s.client.TruncateStream(ctx, &streamlib.TruncateStreamRequest{ + FrontendRequest: &streamlib.TruncateStreamInput{ Namespace: s.ns, StreamId: id, NewBaseOffset: 1, }, }) @@ -266,17 +266,17 @@ func TestStreamCloseAndTruncate(t *testing.T) { // A reader below the floor gets a distinguishable error carrying the floor, // so it can jump forward rather than fail. - _, err = s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ - FrontendRequest: &streampb.PollMessagesInput{Namespace: s.ns, StreamId: id, FromOffset: 0}, + _, err = s.client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{Namespace: s.ns, StreamId: id, FromOffset: 0}, }) require.ErrorContains(t, err, "truncated") - _, err = s.client.CloseStream(ctx, &streampb.CloseStreamRequest{ - FrontendRequest: &streampb.CloseStreamInput{Namespace: s.ns, StreamId: id}, + _, err = s.client.CloseStream(ctx, &streamlib.CloseStreamRequest{ + FrontendRequest: &streamlib.CloseStreamInput{Namespace: s.ns, StreamId: id}, }) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "d")}) + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "d")}) require.Error(t, err) // Closed is a state a reader observes, not an error, and the data stays @@ -296,9 +296,9 @@ func TestStreamReadStartingInsideABatch(t *testing.T) { // first offset of its batch, so reading from 2 has to find the node that // contains it rather than the node whose ID equals it. Getting that wrong // silently drops the messages before the boundary. - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "a", "b", "c")}) require.NoError(t, err) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "d")}) + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "d")}) require.NoError(t, err) for from, want := range map[int64][]string{ @@ -325,15 +325,15 @@ func TestStreamBatchSizeIsBounded(t *testing.T) { for i := range tooMany { tooMany[i] = "x" } - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", tooMany...)}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", tooMany...)}) require.ErrorContains(t, err, "exceeds the limit") } func (s *streamTestEnv) pollWait( ctx context.Context, streamID string, from int64, -) (*streampb.PollMessagesOutput, error) { - resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ - FrontendRequest: &streampb.PollMessagesInput{ +) (*streamlib.PollMessagesOutput, error) { + resp, err := s.client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{ Namespace: s.ns, StreamId: streamID, FromOffset: from, WaitNewMessages: true, }, }) @@ -350,7 +350,7 @@ func TestStreamLongPollWakesOnAppend(t *testing.T) { s.create(ctx, t, id) type result struct { - out *streampb.PollMessagesOutput + out *streamlib.PollMessagesOutput err error } done := make(chan result, 1) @@ -360,7 +360,7 @@ func TestStreamLongPollWakesOnAppend(t *testing.T) { }() // The poll is parked on an empty stream; the append is what releases it. - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "a")}) require.NoError(t, err) select { @@ -379,7 +379,7 @@ func TestStreamLongPollWakesOnClose(t *testing.T) { const id = "stream-longpoll-close" s.create(ctx, t, id) - done := make(chan *streampb.PollMessagesOutput, 1) + done := make(chan *streamlib.PollMessagesOutput, 1) go func() { out, err := s.pollWait(ctx, id, 0) if err == nil { @@ -387,8 +387,8 @@ func TestStreamLongPollWakesOnClose(t *testing.T) { } }() - _, err := s.client.CloseStream(ctx, &streampb.CloseStreamRequest{ - FrontendRequest: &streampb.CloseStreamInput{Namespace: s.ns, StreamId: id}, + _, err := s.client.CloseStream(ctx, &streamlib.CloseStreamRequest{ + FrontendRequest: &streamlib.CloseStreamInput{Namespace: s.ns, StreamId: id}, }) require.NoError(t, err) @@ -427,7 +427,7 @@ func TestStreamLongPollReturnsImmediatelyWhenBehind(t *testing.T) { const id = "stream-longpoll-behind" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "a", "b")}) require.NoError(t, err) // Waiting is only for a reader that is caught up. One that is behind must @@ -444,16 +444,16 @@ func TestStreamCapTruncatesAndReclaims(t *testing.T) { ctx := streamCtx(t) const id = "stream-cap" - _, err := s.client.CreateStream(ctx, &streampb.CreateStreamRequest{ - FrontendRequest: &streampb.CreateStreamInput{ + _, err := s.client.CreateStream(ctx, &streamlib.CreateStreamRequest{ + FrontendRequest: &streamlib.CreateStreamInput{ Namespace: s.ns, StreamId: id, - Lifecycle: &streampb.StreamLifecycle{MaxItems: 4}, + Lifecycle: &streamlib.StreamLifecycle{MaxItems: 4}, }, }) require.NoError(t, err) for _, batch := range [][]string{{"a", "b"}, {"c", "d"}, {"e", "f"}} { - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", batch...)}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", batch...)}) require.NoError(t, err) } @@ -464,8 +464,8 @@ func TestStreamCapTruncatesAndReclaims(t *testing.T) { require.Equal(t, []string{"c", "d", "e", "f"}, bodies(got.GetRecords())) // Below the floor is a distinguishable error, not silence. - _, err = s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ - FrontendRequest: &streampb.PollMessagesInput{Namespace: s.ns, StreamId: id, FromOffset: 0}, + _, err = s.client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{Namespace: s.ns, StreamId: id, FromOffset: 0}, }) require.ErrorContains(t, err, "truncated") } @@ -476,10 +476,10 @@ func TestStreamClosedStaysReadable(t *testing.T) { const id = "stream-closed-readable" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "a", "b")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "a", "b")}) require.NoError(t, err) - _, err = s.client.CloseStream(ctx, &streampb.CloseStreamRequest{ - FrontendRequest: &streampb.CloseStreamInput{Namespace: s.ns, StreamId: id}, + _, err = s.client.CloseStream(ctx, &streamlib.CloseStreamRequest{ + FrontendRequest: &streamlib.CloseStreamInput{Namespace: s.ns, StreamId: id}, }) require.NoError(t, err) @@ -490,8 +490,8 @@ func TestStreamClosedStaysReadable(t *testing.T) { require.Equal(t, []string{"a", "b"}, bodies(got.GetRecords())) require.True(t, got.GetClosed()) - desc, err := s.client.DescribeStream(ctx, &streampb.DescribeStreamRequest{ - FrontendRequest: &streampb.DescribeStreamInput{Namespace: s.ns, StreamId: id}, + desc, err := s.client.DescribeStream(ctx, &streamlib.DescribeStreamRequest{ + FrontendRequest: &streamlib.DescribeStreamInput{Namespace: s.ns, StreamId: id}, }) require.NoError(t, err) require.NotNil(t, desc.GetFrontendResponse().GetState().GetCloseTime()) @@ -509,8 +509,8 @@ func TestStreamListStreams(t *testing.T) { // Visibility is written by a task after the create commits, so this is // eventually consistent by design rather than by accident. await.RequireTrue(t, func() bool { - resp, err := s.client.ListStreams(ctx, &streampb.ListStreamsRequest{ - FrontendRequest: &streampb.ListStreamsInput{Namespace: s.ns}, + resp, err := s.client.ListStreams(ctx, &streamlib.ListStreamsRequest{ + FrontendRequest: &streamlib.ListStreamsInput{Namespace: s.ns}, }) if err != nil { return false @@ -538,7 +538,7 @@ func TestStreamPollReadsOnlyWhatItReturns(t *testing.T) { s.create(ctx, t, id) for i := range 40 { - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{ Records: streamMsgs("", fmt.Sprintf("m%d", i)), }) require.NoError(t, err) @@ -571,18 +571,18 @@ func TestStreamPollAfterIdIsReusedServesTheNewStream(t *testing.T) { const id = "stream-reused-id" s.create(ctx, t, id) - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "old")}) + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "old")}) require.NoError(t, err) // Read it back so the bytes are certain to be cached before the id is reused. require.Equal(t, []string{"old"}, bodies(s.poll(ctx, t, id, 0).GetRecords())) - _, err = s.client.DeleteStream(ctx, &streampb.DeleteStreamRequest{ - FrontendRequest: &streampb.DeleteStreamInput{Namespace: s.ns, StreamId: id}, + _, err = s.client.DeleteStream(ctx, &streamlib.DeleteStreamRequest{ + FrontendRequest: &streamlib.DeleteStreamInput{Namespace: s.ns, StreamId: id}, }) require.NoError(t, err) s.create(ctx, t, id) - _, err = s.add(ctx, t, id, &streampb.AddMessagesInput{Records: streamMsgs("", "new")}) + _, err = s.add(ctx, t, id, &streamlib.AddMessagesInput{Records: streamMsgs("", "new")}) require.NoError(t, err) got := s.poll(ctx, t, id, 0) @@ -599,7 +599,7 @@ func TestStreamFilteredReadReportsRealOffsets(t *testing.T) { s.create(ctx, t, id) for i, topic := range []string{"a", "b", "a", "b", "a"} { - _, err := s.add(ctx, t, id, &streampb.AddMessagesInput{ + _, err := s.add(ctx, t, id, &streamlib.AddMessagesInput{ Records: streamMsgs(topic, fmt.Sprintf("m%d", i)), }) require.NoError(t, err) @@ -613,10 +613,10 @@ func TestStreamFilteredReadReportsRealOffsets(t *testing.T) { func (s *streamTestEnv) pollMaxTopics( ctx context.Context, t *testing.T, streamID string, from int64, maxMessages int32, topics ...string, -) *streampb.PollMessagesOutput { +) *streamlib.PollMessagesOutput { t.Helper() - resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ - FrontendRequest: &streampb.PollMessagesInput{ + resp, err := s.client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{ Namespace: s.ns, StreamId: streamID, FromOffset: from, MaxMessages: maxMessages, Topics: topics, }, @@ -627,10 +627,10 @@ func (s *streamTestEnv) pollMaxTopics( func (s *streamTestEnv) pollMax( ctx context.Context, t *testing.T, streamID string, from int64, maxMessages int32, -) *streampb.PollMessagesOutput { +) *streamlib.PollMessagesOutput { t.Helper() - resp, err := s.client.PollMessages(ctx, &streampb.PollMessagesRequest{ - FrontendRequest: &streampb.PollMessagesInput{ + resp, err := s.client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{ Namespace: s.ns, StreamId: streamID, FromOffset: from, MaxMessages: maxMessages, }, }) @@ -638,7 +638,7 @@ func (s *streamTestEnv) pollMax( return resp.GetFrontendResponse() } -func offsets(msgs []*streampb.StreamRecord) []int64 { +func offsets(msgs []*streamlib.StreamRecord) []int64 { out := make([]int64, len(msgs)) for i, m := range msgs { out[i] = m.GetOffset() diff --git a/tests/streaming_native_test.go b/tests/streaming_native_test.go index b61a8b9085f..556f9ed6e0f 100644 --- a/tests/streaming_native_test.go +++ b/tests/streaming_native_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/require" commonpb "go.temporal.io/api/common/v1" - apistreampb "go.temporal.io/api/stream/v1" - streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + streampb "go.temporal.io/api/stream/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "go.temporal.io/server/common/metrics" "go.temporal.io/server/tests/testcore" "google.golang.org/grpc" @@ -32,15 +32,15 @@ func runNativeStream(t *testing.T, p streamBaselineParams) streamBaselineResult grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) defer func() { _ = conn.Close() }() - client := streampb.NewStreamServiceClient(conn) + client := streamlib.NewStreamServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), p.duration+2*time.Minute) defer cancel() ns := env.Namespace().String() streamID := fmt.Sprintf("bench-%s", p.name) - created, err := client.CreateStream(ctx, &streampb.CreateStreamRequest{ - FrontendRequest: &streampb.CreateStreamInput{Namespace: ns, StreamId: streamID}, + created, err := client.CreateStream(ctx, &streamlib.CreateStreamRequest{ + FrontendRequest: &streamlib.CreateStreamInput{Namespace: ns, StreamId: streamID}, }) require.NoError(t, err) runID := created.GetFrontendResponse().GetRunId() @@ -105,7 +105,7 @@ func runNativeStream(t *testing.T, p streamBaselineParams) streamBaselineResult func runNativeProducer( ctx context.Context, t *testing.T, - client streampb.StreamServiceClient, + client streamlib.StreamServiceClient, ns, streamID, runID string, p streamBaselineParams, sentAt *sync.Map, @@ -123,7 +123,7 @@ func runNativeProducer( deadline := time.Now().Add(p.duration) seq := 0 - var pending []*streampb.StreamRecord + var pending []*streamlib.StreamRecord sequence := int64(0) flush := func() bool { @@ -133,8 +133,8 @@ func runNativeProducer( batch := pending pending = nil sequence++ - _, err := client.AddMessages(ctx, &streampb.AddMessagesRequest{ - FrontendRequest: &streampb.AddMessagesInput{ + _, err := client.AddMessages(ctx, &streamlib.AddMessagesRequest{ + FrontendRequest: &streamlib.AddMessagesInput{ Namespace: ns, StreamId: streamID, RunId: runID, Records: batch, ProducerId: "bench", Sequence: sequence, }, @@ -153,9 +153,9 @@ func runNativeProducer( return seq case <-genTicker.C: sentAt.Store(seq, time.Now()) - pending = append(pending, &streampb.StreamRecord{ + pending = append(pending, &streamlib.StreamRecord{ Body: &commonpb.Payload{Data: payload}, - Kind: apistreampb.STREAM_RECORD_KIND_DATA, + Kind: streampb.STREAM_RECORD_KIND_DATA, }) seq++ case <-flushTicker.C: @@ -170,7 +170,7 @@ func runNativeProducer( func runNativeConsumer( ctx context.Context, - client streampb.StreamServiceClient, + client streamlib.StreamServiceClient, ns, streamID, runID string, sentAt *sync.Map, receivedTotal *atomic.Int64, @@ -180,8 +180,8 @@ func runNativeConsumer( lastSeen := int64(0) for ctx.Err() == nil { - resp, err := client.PollMessages(ctx, &streampb.PollMessagesRequest{ - FrontendRequest: &streampb.PollMessagesInput{ + resp, err := client.PollMessages(ctx, &streamlib.PollMessagesRequest{ + FrontendRequest: &streamlib.PollMessagesInput{ Namespace: ns, StreamId: streamID, RunId: runID, FromOffset: lastSeen, WaitNewMessages: true, }, From 032c3827f14790446d836660df6269e8301631ff Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 04:23:59 -0700 Subject: [PATCH 42/47] Guarded the stream test env's cancel list against concurrent producers. The race test asks the env for a context from eight goroutines at once, and the unguarded append was a data race. Under the race detector that fails the test, and every parallel test running at that moment fails with it, which is how the standalone Nexus and activity parity tests went red on the same shard. --- tests/stream_test.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/stream_test.go b/tests/stream_test.go index 9f20800244f..9eae35308c3 100644 --- a/tests/stream_test.go +++ b/tests/stream_test.go @@ -3,6 +3,7 @@ package tests import ( "context" "fmt" + "sync" "testing" "time" @@ -25,9 +26,13 @@ import ( const streamMaxBatch = chasmstream.MaxRecordsPerBatch type streamTestEnv struct { - env *testcore.TestEnv - client streamlib.StreamServiceClient - ns string + env *testcore.TestEnv + client streamlib.StreamServiceClient + ns string + + // Guards cleanup: a test that races outside producers against the + // workflow asks for contexts from several goroutines at once. + mu sync.Mutex cleanup []func() } @@ -48,6 +53,8 @@ func newStreamTestEnvFrom(t *testing.T, env *testcore.TestEnv) *streamTestEnv { env: env, client: streamlib.NewStreamServiceClient(conn), ns: env.Namespace().String(), } t.Cleanup(func() { + env2.mu.Lock() + defer env2.mu.Unlock() for _, c := range env2.cleanup { c() } @@ -58,7 +65,9 @@ func newStreamTestEnvFrom(t *testing.T, env *testcore.TestEnv) *streamTestEnv { func (s *streamTestEnv) ctx() context.Context { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + s.mu.Lock() s.cleanup = append(s.cleanup, cancel) + s.mu.Unlock() return ctx } From c0b383d0fdd1ecfcef62373bb513cfc7fb3fbae8 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 14:10:34 -0700 Subject: [PATCH 43/47] Declared authorization scope, quotas and redirection for the stream service. Without an entry in the method table the default authorizer denied every stream RPC and the no-op one admitted any namespace caller. Each method now declares namespace scope with read, write or admin access, has a rate-limit priority, and is forwarded to the namespace's active cell like a workflow RPC. --- chasm/lib/stream/service/frontend.go | 53 ++++++++++++++ common/api/metadata.go | 30 +++++++- common/api/metadata_stream_test.go | 72 +++++++++++++++++++ common/api/metadata_test.go | 9 +++ common/rpc/interceptor/redirection_test.go | 80 ++++++++++++++++++++++ service/frontend/configs/quotas.go | 31 +++++++++ service/frontend/configs/quotas_test.go | 26 +++++++ service/frontend/fx.go | 2 +- 8 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 common/api/metadata_stream_test.go diff --git a/chasm/lib/stream/service/frontend.go b/chasm/lib/stream/service/frontend.go index 843f1e8bf4d..431ae37ec06 100644 --- a/chasm/lib/stream/service/frontend.go +++ b/chasm/lib/stream/service/frontend.go @@ -45,6 +45,59 @@ func NewFrontendHandler( } } +// RedirectableMethods lists the stream RPCs a cell forwards to the namespace's +// active cell when it is not that cell itself, each with the response it +// answers with, for the redirection interceptor. +// +// The stream lives in the active cell's mutable state, so a call served where +// it happens to land would write to a copy nothing reads or read one that +// stops at the last replication. The two calls History makes on itself are not +// listed: they never reach a frontend legitimately, and this handler answers +// them with Unimplemented wherever they land. +func RedirectableMethods() map[string]func() any { + return map[string]func() any{ + streampb.StreamService_CreateStream_FullMethodName: func() any { + return &streampb.CreateStreamResponse{} + }, + streampb.StreamService_AddMessages_FullMethodName: func() any { + return &streampb.AddMessagesResponse{} + }, + streampb.StreamService_FinishWriting_FullMethodName: func() any { + return &streampb.FinishWritingResponse{} + }, + streampb.StreamService_SubscribeWorkflow_FullMethodName: func() any { + return &streampb.SubscribeWorkflowResponse{} + }, + streampb.StreamService_PollMessages_FullMethodName: func() any { + return &streampb.PollMessagesResponse{} + }, + streampb.StreamService_DescribeStream_FullMethodName: func() any { + return &streampb.DescribeStreamResponse{} + }, + streampb.StreamService_PollWorkflowMessages_FullMethodName: func() any { + return &streampb.PollWorkflowMessagesResponse{} + }, + streampb.StreamService_DescribeWorkflowStream_FullMethodName: func() any { + return &streampb.DescribeWorkflowStreamResponse{} + }, + streampb.StreamService_AddWorkflowMessages_FullMethodName: func() any { + return &streampb.AddWorkflowMessagesResponse{} + }, + streampb.StreamService_CloseStream_FullMethodName: func() any { + return &streampb.CloseStreamResponse{} + }, + streampb.StreamService_TruncateStream_FullMethodName: func() any { + return &streampb.TruncateStreamResponse{} + }, + streampb.StreamService_ListStreams_FullMethodName: func() any { + return &streampb.ListStreamsResponse{} + }, + streampb.StreamService_DeleteStream_FullMethodName: func() any { + return &streampb.DeleteStreamResponse{} + }, + } +} + func (h *FrontendHandler) namespaceID(name string) (string, error) { if name == "" { return "", serviceerror.NewInvalidArgument("namespace is required") diff --git a/common/api/metadata.go b/common/api/metadata.go index 41843262284..8abfee41c66 100644 --- a/common/api/metadata.go +++ b/common/api/metadata.go @@ -64,6 +64,9 @@ const ( MatchingServicePrefix = "/temporal.server.api.matchingservice.v1.MatchingService/" // Technically not a gRPC service, but still using this format for metadata. NexusServicePrefix = "/temporal.api.nexusservice.v1.NexusService/" + // The stream service is served on the frontend next to the workflow + // service, so its methods need the same authorization metadata. + StreamServicePrefix = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/" ) var ( @@ -213,10 +216,33 @@ var ( "CompleteNexusOperation": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, "CompleteNexusOperationChasm": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, } + // Every stream request carries its namespace inside `frontend_request`, + // exposed through a hand-written `GetNamespace()`, which is what namespace + // scope needs. Closing, truncating and deleting a stream are ordinary + // writes here; whether they should need an operator is a policy call this + // table does not make. The two internal calls History makes on itself are + // admin so that no namespace-level role can reach them through a frontend. + streamServiceMetadata = map[string]MethodMetadata{ + "CreateStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "AddMessages": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "FinishWriting": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "SubscribeWorkflow": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "PollMessages": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingCapable}, + "DescribeStream": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingNone}, + "PollWorkflowMessages": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingCapable}, + "DescribeWorkflowStream": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingNone}, + "AddWorkflowMessages": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "RegisterStreamConsumer": {Scope: ScopeNamespace, Access: AccessAdmin, Polling: PollingNone}, + "AdvanceConsumerHead": {Scope: ScopeNamespace, Access: AccessAdmin, Polling: PollingNone}, + "CloseStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "TruncateStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "ListStreams": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingNone}, + "DeleteStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + } ) // GetMethodMetadata gets metadata for a given API method in one of the services exported by -// frontend (WorkflowService, OperatorService, AdminService). +// frontend (WorkflowService, OperatorService, AdminService, StreamService). func GetMethodMetadata(fullApiName string) MethodMetadata { switch { case strings.HasPrefix(fullApiName, WorkflowServicePrefix): @@ -225,6 +251,8 @@ func GetMethodMetadata(fullApiName string) MethodMetadata { return operatorServiceMetadata[MethodName(fullApiName)] case strings.HasPrefix(fullApiName, NexusServicePrefix): return nexusServiceMetadata[MethodName(fullApiName)] + case strings.HasPrefix(fullApiName, StreamServicePrefix): + return streamServiceMetadata[MethodName(fullApiName)] case strings.HasPrefix(fullApiName, AdminServicePrefix): return MethodMetadata{Scope: ScopeCluster, Access: AccessAdmin} default: diff --git a/common/api/metadata_stream_test.go b/common/api/metadata_stream_test.go new file mode 100644 index 00000000000..a914bdea5c4 --- /dev/null +++ b/common/api/metadata_stream_test.go @@ -0,0 +1,72 @@ +package api_test + +// An external test package, because the generated stream package depends on +// this one and an internal test importing it would be a cycle. + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/api" + "go.temporal.io/server/common/testing/temporalapi" +) + +// The access each stream method declares. Kept as a table so a new method +// fails here until someone decides what it needs, and so a change of mind +// shows up in the diff. +var expectedStreamAccess = map[string]api.Access{ + "CreateStream": api.AccessWrite, + "AddMessages": api.AccessWrite, + "FinishWriting": api.AccessWrite, + "SubscribeWorkflow": api.AccessWrite, + "PollMessages": api.AccessReadOnly, + "DescribeStream": api.AccessReadOnly, + "PollWorkflowMessages": api.AccessReadOnly, + "DescribeWorkflowStream": api.AccessReadOnly, + "AddWorkflowMessages": api.AccessWrite, + "RegisterStreamConsumer": api.AccessAdmin, + "AdvanceConsumerHead": api.AccessAdmin, + "CloseStream": api.AccessWrite, + "TruncateStream": api.AccessWrite, + "ListStreams": api.AccessReadOnly, + "DeleteStream": api.AccessWrite, +} + +func TestStreamServiceMetadata(t *testing.T) { + var server streampb.StreamServiceServer + seen := make(map[string]struct{}) + temporalapi.WalkExportedMethods(&server, func(method reflect.Method) { + seen[method.Name] = struct{}{} + md := api.GetMethodMetadata(api.StreamServicePrefix + method.Name) + + expected, ok := expectedStreamAccess[method.Name] + require.Truef(t, ok, + "%s has no entry in expectedStreamAccess: decide its access and add it", method.Name) + require.Equalf(t, expected, md.Access, "%s access", method.Name) + require.Equalf(t, api.ScopeNamespace, md.Scope, "%s scope", method.Name) + + // Namespace scope is only enforceable if the interceptors can find the + // namespace on the request, and these requests carry it through a method + // rather than a top-level field. + requestType := method.Type.In(1) + getter, ok := requestType.MethodByName("GetNamespace") + require.Truef(t, ok, "%s request has no GetNamespace()", method.Name) + require.Equal(t, reflect.TypeFor[string](), getter.Type.Out(0)) + }) + + for name := range expectedStreamAccess { + _, ok := seen[name] + require.Truef(t, ok, "%s is in expectedStreamAccess but not on the service", name) + } +} + +func TestStreamPollsAreMarkedAsPolling(t *testing.T) { + for _, name := range []string{"PollMessages", "PollWorkflowMessages"} { + md := api.GetMethodMetadata(api.StreamServicePrefix + name) + require.Equalf(t, api.PollingCapable, md.Polling, "%s blocks only when asked to", name) + } + md := api.GetMethodMetadata(api.StreamServicePrefix + "DescribeStream") + require.Equal(t, api.PollingNone, md.Polling) +} diff --git a/common/api/metadata_test.go b/common/api/metadata_test.go index 6b7791a6cd5..7921c7d92e9 100644 --- a/common/api/metadata_test.go +++ b/common/api/metadata_test.go @@ -84,6 +84,15 @@ func TestGetMethodMetadata(t *testing.T) { assert.Equal(t, ScopeCluster, md.Scope) assert.Equal(t, AccessAdmin, md.Access) + md = GetMethodMetadata(StreamServicePrefix + "AddWorkflowMessages") + require.Equal(t, ScopeNamespace, md.Scope) + require.Equal(t, AccessWrite, md.Access) + + md = GetMethodMetadata(StreamServicePrefix + "PollWorkflowMessages") + require.Equal(t, ScopeNamespace, md.Scope) + require.Equal(t, AccessReadOnly, md.Access) + require.Equal(t, PollingCapable, md.Polling) + md = GetMethodMetadata("/OtherService/Method1") assert.Equal(t, ScopeUnknown, md.Scope) assert.Equal(t, AccessUnknown, md.Access) diff --git a/common/rpc/interceptor/redirection_test.go b/common/rpc/interceptor/redirection_test.go index 11e6df3a681..66bf8f1c548 100644 --- a/common/rpc/interceptor/redirection_test.go +++ b/common/rpc/interceptor/redirection_test.go @@ -11,6 +11,7 @@ import ( "go.temporal.io/api/serviceerror" "go.temporal.io/api/workflowservice/v1" persistencespb "go.temporal.io/server/api/persistence/v1" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" "go.temporal.io/server/client" "go.temporal.io/server/common/clock" "go.temporal.io/server/common/cluster" @@ -698,3 +699,82 @@ func (s *redirectionInterceptorSuite) TestHandleGlobalAPIInvocation_RemoteRoutin s.Len(failureMetrics, 1) s.Equal(int64(1), failureMetrics[0].Value) } + +// streamNamespaceActiveElsewhere registers a global namespace whose active +// cluster is the alternative one, so a call for it has to be forwarded. +func (s *redirectionInterceptorSuite) streamNamespaceActiveElsewhere() namespace.Name { + namespaceName := namespace.Name("stream-namespace-active-elsewhere") + namespaceEntry := namespace.NewGlobalNamespaceForTest( + &persistencespb.NamespaceInfo{Id: uuid.NewString(), Name: namespaceName.String()}, + &persistencespb.NamespaceConfig{Retention: timestamp.DurationFromDays(1)}, + &persistencespb.NamespaceReplicationConfig{ + ActiveClusterName: cluster.TestAlternativeClusterName, + Clusters: []string{ + cluster.TestCurrentClusterName, + cluster.TestAlternativeClusterName, + }, + }, + 1, + ) + s.namespaceCache.EXPECT().GetNamespace(namespaceName).Return(namespaceEntry, nil).AnyTimes() + return namespaceName +} + +// A stream call registered as redirectable is forwarded to the namespace's +// active cluster, resolved from the namespace inside `frontend_request`. +func (s *redirectionInterceptorSuite) TestStreamAPI_ForwardedWhenRegistered() { + namespaceName := s.streamNamespaceActiveElsewhere() + info := &grpc.UnaryServerInfo{ + FullMethod: streampb.StreamService_AddWorkflowMessages_FullMethodName, + } + req := &streampb.AddWorkflowMessagesRequest{ + FrontendRequest: &streampb.AddWorkflowMessagesInput{ + Namespace: namespaceName.String(), + WorkflowId: "wf", + }, + } + redirector := s.redirector.WithRedirectResponses(map[string]func() any{ + info.FullMethod: func() any { return &streampb.AddWorkflowMessagesResponse{} }, + }) + + grpcConn := &mockClientConnInterface{ + Suite: &s.Suite, + targetMethod: info.FullMethod, + targetResponse: &streampb.AddWorkflowMessagesResponse{}, + } + s.clientBean.EXPECT().GetRemoteFrontendClient(cluster.TestAlternativeClusterName). + Return(grpcConn, nil, nil).Times(1) + + resp, err := redirector.Intercept(context.Background(), req, info, + func(context.Context, any) (any, error) { + s.Fail("a call for a namespace active elsewhere must not be served locally") + return nil, nil + }) + s.NoError(err) + s.IsType(&streampb.AddWorkflowMessagesResponse{}, resp) +} + +// Without registration a stream call is served wherever it lands, which is +// what the bare interceptor does for any service other than WorkflowService. +func (s *redirectionInterceptorSuite) TestStreamAPI_ServedLocallyWhenNotRegistered() { + namespaceName := s.streamNamespaceActiveElsewhere() + info := &grpc.UnaryServerInfo{ + FullMethod: streampb.StreamService_AddWorkflowMessages_FullMethodName, + } + req := &streampb.AddWorkflowMessagesRequest{ + FrontendRequest: &streampb.AddWorkflowMessagesInput{ + Namespace: namespaceName.String(), + WorkflowId: "wf", + }, + } + + served := false + resp, err := s.redirector.Intercept(context.Background(), req, info, + func(context.Context, any) (any, error) { + served = true + return &streampb.AddWorkflowMessagesResponse{}, nil + }) + s.NoError(err) + s.True(served) + s.IsType(&streampb.AddWorkflowMessagesResponse{}, resp) +} diff --git a/service/frontend/configs/quotas.go b/service/frontend/configs/quotas.go index beaf12baa2a..b93c0fa9731 100644 --- a/service/frontend/configs/quotas.go +++ b/service/frontend/configs/quotas.go @@ -22,6 +22,10 @@ const ( PollWorkflowHistoryAPIName = "/temporal.api.workflowservice.v1.WorkflowService/PollWorkflowExecutionHistory" // PollActivityExecutionAPIName is used instead of DescribeActivityExecution if LongPollToken is set in request. PollActivityExecutionAPIName = "/temporal.api.workflowservice.v1.WorkflowService/PollActivityExecutionDescription" + + // The stream service is served on the same frontend server as the workflow + // service and shares its rate limiters. + streamServicePrefix = "/temporal.server.chasm.lib.stream.proto.v1.StreamService/" ) var ( @@ -61,6 +65,10 @@ var ( // Dispatching a Nexus task is a potentially long running RPC, it's classified in the same bucket as QueryWorkflow. DispatchNexusTaskByNamespaceAndTaskQueueAPIName: 1, DispatchNexusTaskByEndpointAPIName: 1, + + // Stream reads block only when asked to wait for new records. + streamServicePrefix + "PollMessages": 1, + streamServicePrefix + "PollWorkflowMessages": 1, } // PollTaskAPISet is the set of API methods for which NamespaceRateLimitInterceptor will @@ -208,6 +216,26 @@ var ( // Informational API that aren't required for the temporal service to function OpenAPIV3APIName: 5, OpenAPIV2APIName: 5, + + // Stream service. Appends and subscriptions are external events like a + // signal; closing, truncating and deleting change state; describes are + // status reads; the two polls sit with the other polls. + streamServicePrefix + "CreateStream": 1, + streamServicePrefix + "AddMessages": 1, + streamServicePrefix + "FinishWriting": 1, + streamServicePrefix + "SubscribeWorkflow": 1, + streamServicePrefix + "AddWorkflowMessages": 1, + streamServicePrefix + "CloseStream": 2, + streamServicePrefix + "TruncateStream": 2, + streamServicePrefix + "DeleteStream": 2, + streamServicePrefix + "DescribeStream": 3, + streamServicePrefix + "DescribeWorkflowStream": 3, + streamServicePrefix + "PollMessages": 4, + streamServicePrefix + "PollWorkflowMessages": 4, + // History calls these on itself; the frontend answers them with + // Unimplemented, so they only ever cost a refusal. + streamServicePrefix + "RegisterStreamConsumer": 5, + streamServicePrefix + "AdvanceConsumerHead": 5, } ExecutionAPIPrioritiesOrdered = []int{0, 1, 2, 3, 4, 5} @@ -233,6 +261,9 @@ var ( "/temporal.api.workflowservice.v1.WorkflowService/ListDeployments": 1, "/temporal.api.workflowservice.v1.WorkflowService/GetDeploymentReachability": 1, "/temporal.api.workflowservice.v1.WorkflowService/ListWorkerDeployments": 1, + + // Answered from visibility on the frontend, like the other lists. + streamServicePrefix + "ListStreams": 1, } VisibilityAPIPrioritiesOrdered = []int{0, 1} diff --git a/service/frontend/configs/quotas_test.go b/service/frontend/configs/quotas_test.go index efb5c49291d..5e3d028df76 100644 --- a/service/frontend/configs/quotas_test.go +++ b/service/frontend/configs/quotas_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.temporal.io/api/workflowservice/v1" + streampb "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + "go.temporal.io/server/common/api" "go.temporal.io/server/common/headers" "go.temporal.io/server/common/log" "go.temporal.io/server/common/quotas" @@ -121,6 +123,9 @@ func (s *quotasSuite) TestVisibilityAPIs() { apiToPriority[apiName] = VisibilityAPIToPriority[apiName] } } + // The one stream method answered from visibility. + listStreams := streamServicePrefix + "ListStreams" + apiToPriority[listStreams] = VisibilityAPIToPriority[listStreams] s.Equal(apiToPriority, VisibilityAPIToPriority) } @@ -171,6 +176,27 @@ func (s *quotasSuite) TestAllAPIs() { s.Truef(ok, "missing priority for API: %q", CompleteNexusOperation) } +// Every stream method has a priority, the two polls are counted as +// long-running, and the one list is a visibility read. +func (s *quotasSuite) TestStreamServiceAPIs() { + var service streampb.StreamServiceServer + temporalapi.WalkExportedMethods(&service, func(m reflect.Method) { + apiName := streamServicePrefix + m.Name + _, inExecution := APIToPriority[apiName] + _, inVisibility := VisibilityAPIToPriority[apiName] + s.Truef(inExecution || inVisibility, "missing priority for API: %v", m.Name) + s.Falsef(inExecution && inVisibility, "API in two limiters: %v", m.Name) + }) + + for _, poll := range []string{"PollMessages", "PollWorkflowMessages"} { + _, ok := ExecutionAPICountLimitOverride[streamServicePrefix+poll] + s.Truef(ok, "%s can block, so it counts against concurrent long polls", poll) + } + s.Equal(1, VisibilityAPIToPriority[streamServicePrefix+"ListStreams"]) + s.Equal(streamServicePrefix, api.StreamServicePrefix, + "the quota table and the authorization table have to agree on the service name") +} + func (s *quotasSuite) TestOperatorPriority_Execution() { limiter := NewExecutionPriorityRateLimiter(testRateBurstFn, testOperatorRPSRatioFn) s.testOperatorPrioritized(limiter, "DescribeWorkflowExecution") diff --git a/service/frontend/fx.go b/service/frontend/fx.go index 1546d14ad8d..470506c4ce3 100644 --- a/service/frontend/fx.go +++ b/service/frontend/fx.go @@ -416,7 +416,7 @@ func RedirectionInterceptorProvider( metricsHandler, timeSource, clusterMetadata, - ) + ).WithRedirectResponses(chasmstream.RedirectableMethods()) } func BusinessIDInterceptorProvider( From 16af6ea769051f7f329f4bd97f2b0bac92a0cccb Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 14:22:37 -0700 Subject: [PATCH 44/47] Carried the recorded stream ranges on non-sticky query tasks. A query dispatched straight through matching is built without RecordWorkflowTaskStarted, so nothing re-supplied the ranges its history recorded and a cold worker could not replay a consuming workflow to answer. Matching now asks History for them through a new internal RPC after it has fetched the history. --- .../v1/request_response.go-helpers.pb.go | 74 + api/historyservice/v1/request_response.pb.go | 2528 +++++++++-------- api/historyservice/v1/service.pb.go | 625 ++-- api/historyservice/v1/service_grpc.pb.go | 43 + .../v1/service_grpc.pb.mock.go | 35 + client/history/client_gen.go | 20 + client/history/metric_client_gen.go | 14 + client/history/retryable_client_gen.go | 15 + .../logtags/history_service_server_gen.go | 7 + .../historyservice/v1/request_response.proto | 16 + .../api/historyservice/v1/service.proto | 7 + .../stream_slices.go | 126 + service/history/handler.go | 27 + service/history/history_engine.go | 38 + service/history/interfaces/engine.go | 1 + service/history/interfaces/engine_mock.go | 15 + service/matching/matching_engine.go | 39 + service/matching/matching_engine_test.go | 67 + tests/stream_query_test.go | 137 + 19 files changed, 2315 insertions(+), 1519 deletions(-) create mode 100644 tests/stream_query_test.go diff --git a/api/historyservice/v1/request_response.go-helpers.pb.go b/api/historyservice/v1/request_response.go-helpers.pb.go index 68cf73e2c50..33050f93afe 100644 --- a/api/historyservice/v1/request_response.go-helpers.pb.go +++ b/api/historyservice/v1/request_response.go-helpers.pb.go @@ -116,6 +116,80 @@ func (this *StartWorkflowExecutionResponse) Equal(that interface{}) bool { return proto.Equal(this, that1) } +// Marshal an object of type GetStreamReplaySlicesRequest to the protobuf v3 wire format +func (val *GetStreamReplaySlicesRequest) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type GetStreamReplaySlicesRequest from the protobuf v3 wire format +func (val *GetStreamReplaySlicesRequest) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *GetStreamReplaySlicesRequest) Size() int { + return proto.Size(val) +} + +// Equal returns whether two GetStreamReplaySlicesRequest 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 *GetStreamReplaySlicesRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *GetStreamReplaySlicesRequest + switch t := that.(type) { + case *GetStreamReplaySlicesRequest: + that1 = t + case GetStreamReplaySlicesRequest: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + +// Marshal an object of type GetStreamReplaySlicesResponse to the protobuf v3 wire format +func (val *GetStreamReplaySlicesResponse) Marshal() ([]byte, error) { + return proto.Marshal(val) +} + +// Unmarshal an object of type GetStreamReplaySlicesResponse from the protobuf v3 wire format +func (val *GetStreamReplaySlicesResponse) Unmarshal(buf []byte) error { + return proto.Unmarshal(buf, val) +} + +// Size returns the size of the object, in bytes, once serialized +func (val *GetStreamReplaySlicesResponse) Size() int { + return proto.Size(val) +} + +// Equal returns whether two GetStreamReplaySlicesResponse 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 *GetStreamReplaySlicesResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + var that1 *GetStreamReplaySlicesResponse + switch t := that.(type) { + case *GetStreamReplaySlicesResponse: + that1 = t + case GetStreamReplaySlicesResponse: + that1 = &t + default: + return false + } + + return proto.Equal(this, that1) +} + // Marshal an object of type GetMutableStateRequest to the protobuf v3 wire format func (val *GetMutableStateRequest) Marshal() ([]byte, error) { return proto.Marshal(val) diff --git a/api/historyservice/v1/request_response.pb.go b/api/historyservice/v1/request_response.pb.go index 3b92b759aa9..d911cf62654 100644 --- a/api/historyservice/v1/request_response.pb.go +++ b/api/historyservice/v1/request_response.pb.go @@ -18,22 +18,22 @@ import ( v13 "go.temporal.io/api/failure/v1" v17 "go.temporal.io/api/history/v1" v122 "go.temporal.io/api/nexus/v1" - v115 "go.temporal.io/api/protocol/v1" - v114 "go.temporal.io/api/query/v1" - v116 "go.temporal.io/api/stream/v1" - v111 "go.temporal.io/api/taskqueue/v1" + v116 "go.temporal.io/api/protocol/v1" + v115 "go.temporal.io/api/query/v1" + v19 "go.temporal.io/api/stream/v1" + v112 "go.temporal.io/api/taskqueue/v1" v15 "go.temporal.io/api/workflow/v1" v1 "go.temporal.io/api/workflowservice/v1" v119 "go.temporal.io/server/api/adminservice/v1" v18 "go.temporal.io/server/api/clock/v1" v120 "go.temporal.io/server/api/common/v1" - v112 "go.temporal.io/server/api/enums/v1" + v113 "go.temporal.io/server/api/enums/v1" v123 "go.temporal.io/server/api/health/v1" - v19 "go.temporal.io/server/api/history/v1" + v110 "go.temporal.io/server/api/history/v1" v117 "go.temporal.io/server/api/namespace/v1" - v110 "go.temporal.io/server/api/persistence/v1" + v111 "go.temporal.io/server/api/persistence/v1" v118 "go.temporal.io/server/api/replication/v1" - v113 "go.temporal.io/server/api/taskqueue/v1" + v114 "go.temporal.io/server/api/taskqueue/v1" v121 "go.temporal.io/server/api/token/v1" v11 "go.temporal.io/server/api/workflow/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -459,21 +459,122 @@ func (x *StartWorkflowExecutionResponse) GetFirstExecutionRunId() string { return "" } +// Asks for the stream ranges a workflow's completed tasks recorded, re-read from +// their streams, for a task that is built without RecordWorkflowTaskStarted. +// A query dispatched straight through matching is the one such task. +type GetStreamReplaySlicesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + Execution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=execution,proto3" json:"execution,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetStreamReplaySlicesRequest) Reset() { + *x = GetStreamReplaySlicesRequest{} + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetStreamReplaySlicesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStreamReplaySlicesRequest) ProtoMessage() {} + +func (x *GetStreamReplaySlicesRequest) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStreamReplaySlicesRequest.ProtoReflect.Descriptor instead. +func (*GetStreamReplaySlicesRequest) Descriptor() ([]byte, []int) { + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{3} +} + +func (x *GetStreamReplaySlicesRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + +func (x *GetStreamReplaySlicesRequest) GetExecution() *v14.WorkflowExecution { + if x != nil { + return x.Execution + } + return nil +} + +type GetStreamReplaySlicesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Every slice is tagged with the completed event that recorded its range. + // Empty when the workflow consumes no stream. + StreamSlices []*v19.StreamSlice `protobuf:"bytes,1,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetStreamReplaySlicesResponse) Reset() { + *x = GetStreamReplaySlicesResponse{} + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetStreamReplaySlicesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStreamReplaySlicesResponse) ProtoMessage() {} + +func (x *GetStreamReplaySlicesResponse) ProtoReflect() protoreflect.Message { + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStreamReplaySlicesResponse.ProtoReflect.Descriptor instead. +func (*GetStreamReplaySlicesResponse) Descriptor() ([]byte, []int) { + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{4} +} + +func (x *GetStreamReplaySlicesResponse) GetStreamSlices() []*v19.StreamSlice { + if x != nil { + return x.StreamSlices + } + return nil +} + type GetMutableStateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` Execution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=execution,proto3" json:"execution,omitempty"` ExpectedNextEventId int64 `protobuf:"varint,3,opt,name=expected_next_event_id,json=expectedNextEventId,proto3" json:"expected_next_event_id,omitempty"` CurrentBranchToken []byte `protobuf:"bytes,4,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` - VersionHistoryItem *v19.VersionHistoryItem `protobuf:"bytes,5,opt,name=version_history_item,json=versionHistoryItem,proto3" json:"version_history_item,omitempty"` - VersionedTransition *v110.VersionedTransition `protobuf:"bytes,6,opt,name=versioned_transition,json=versionedTransition,proto3" json:"versioned_transition,omitempty"` + VersionHistoryItem *v110.VersionHistoryItem `protobuf:"bytes,5,opt,name=version_history_item,json=versionHistoryItem,proto3" json:"version_history_item,omitempty"` + VersionedTransition *v111.VersionedTransition `protobuf:"bytes,6,opt,name=versioned_transition,json=versionedTransition,proto3" json:"versioned_transition,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetMutableStateRequest) Reset() { *x = GetMutableStateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[3] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -485,7 +586,7 @@ func (x *GetMutableStateRequest) String() string { func (*GetMutableStateRequest) ProtoMessage() {} func (x *GetMutableStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[3] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -498,7 +599,7 @@ func (x *GetMutableStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMutableStateRequest.ProtoReflect.Descriptor instead. func (*GetMutableStateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{3} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{5} } func (x *GetMutableStateRequest) GetNamespaceId() string { @@ -529,14 +630,14 @@ func (x *GetMutableStateRequest) GetCurrentBranchToken() []byte { return nil } -func (x *GetMutableStateRequest) GetVersionHistoryItem() *v19.VersionHistoryItem { +func (x *GetMutableStateRequest) GetVersionHistoryItem() *v110.VersionHistoryItem { if x != nil { return x.VersionHistoryItem } return nil } -func (x *GetMutableStateRequest) GetVersionedTransition() *v110.VersionedTransition { +func (x *GetMutableStateRequest) GetVersionedTransition() *v111.VersionedTransition { if x != nil { return x.VersionedTransition } @@ -550,16 +651,16 @@ type GetMutableStateResponse struct { NextEventId int64 `protobuf:"varint,3,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` PreviousStartedEventId int64 `protobuf:"varint,4,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` LastFirstEventId int64 `protobuf:"varint,5,opt,name=last_first_event_id,json=lastFirstEventId,proto3" json:"last_first_event_id,omitempty"` - TaskQueue *v111.TaskQueue `protobuf:"bytes,6,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - StickyTaskQueue *v111.TaskQueue `protobuf:"bytes,7,opt,name=sticky_task_queue,json=stickyTaskQueue,proto3" json:"sticky_task_queue,omitempty"` + TaskQueue *v112.TaskQueue `protobuf:"bytes,6,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` + StickyTaskQueue *v112.TaskQueue `protobuf:"bytes,7,opt,name=sticky_task_queue,json=stickyTaskQueue,proto3" json:"sticky_task_queue,omitempty"` // (-- api-linter: core::0140::prepositions=disabled // // aip.dev/not-precedent: "to" is used to indicate interval. --) StickyTaskQueueScheduleToStartTimeout *durationpb.Duration `protobuf:"bytes,11,opt,name=sticky_task_queue_schedule_to_start_timeout,json=stickyTaskQueueScheduleToStartTimeout,proto3" json:"sticky_task_queue_schedule_to_start_timeout,omitempty"` CurrentBranchToken []byte `protobuf:"bytes,13,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` - WorkflowState v112.WorkflowExecutionState `protobuf:"varint,15,opt,name=workflow_state,json=workflowState,proto3,enum=temporal.server.api.enums.v1.WorkflowExecutionState" json:"workflow_state,omitempty"` + WorkflowState v113.WorkflowExecutionState `protobuf:"varint,15,opt,name=workflow_state,json=workflowState,proto3,enum=temporal.server.api.enums.v1.WorkflowExecutionState" json:"workflow_state,omitempty"` WorkflowStatus v12.WorkflowExecutionStatus `protobuf:"varint,16,opt,name=workflow_status,json=workflowStatus,proto3,enum=temporal.api.enums.v1.WorkflowExecutionStatus" json:"workflow_status,omitempty"` - VersionHistories *v19.VersionHistories `protobuf:"bytes,17,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` + VersionHistories *v110.VersionHistories `protobuf:"bytes,17,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` IsStickyTaskQueueEnabled bool `protobuf:"varint,18,opt,name=is_sticky_task_queue_enabled,json=isStickyTaskQueueEnabled,proto3" json:"is_sticky_task_queue_enabled,omitempty"` LastFirstEventTxnId int64 `protobuf:"varint,19,opt,name=last_first_event_txn_id,json=lastFirstEventTxnId,proto3" json:"last_first_event_txn_id,omitempty"` FirstExecutionRunId string `protobuf:"bytes,20,opt,name=first_execution_run_id,json=firstExecutionRunId,proto3" json:"first_execution_run_id,omitempty"` @@ -570,18 +671,18 @@ type GetMutableStateResponse struct { // for this execution. AssignedBuildId string `protobuf:"bytes,22,opt,name=assigned_build_id,json=assignedBuildId,proto3" json:"assigned_build_id,omitempty"` InheritedBuildId string `protobuf:"bytes,23,opt,name=inherited_build_id,json=inheritedBuildId,proto3" json:"inherited_build_id,omitempty"` - TransitionHistory []*v110.VersionedTransition `protobuf:"bytes,24,rep,name=transition_history,json=transitionHistory,proto3" json:"transition_history,omitempty"` + TransitionHistory []*v111.VersionedTransition `protobuf:"bytes,24,rep,name=transition_history,json=transitionHistory,proto3" json:"transition_history,omitempty"` VersioningInfo *v15.WorkflowExecutionVersioningInfo `protobuf:"bytes,25,opt,name=versioning_info,json=versioningInfo,proto3" json:"versioning_info,omitempty"` // Transient or speculative workflow task events which are not yet persisted in the history. // These events should be appended to the history when it is returned to the worker. - TransientOrSpeculativeTasks *v19.TransientWorkflowTaskInfo `protobuf:"bytes,26,opt,name=transient_or_speculative_tasks,json=transientOrSpeculativeTasks,proto3" json:"transient_or_speculative_tasks,omitempty"` + TransientOrSpeculativeTasks *v110.TransientWorkflowTaskInfo `protobuf:"bytes,26,opt,name=transient_or_speculative_tasks,json=transientOrSpeculativeTasks,proto3" json:"transient_or_speculative_tasks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetMutableStateResponse) Reset() { *x = GetMutableStateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[4] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -593,7 +694,7 @@ func (x *GetMutableStateResponse) String() string { func (*GetMutableStateResponse) ProtoMessage() {} func (x *GetMutableStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[4] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -606,7 +707,7 @@ func (x *GetMutableStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMutableStateResponse.ProtoReflect.Descriptor instead. func (*GetMutableStateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{4} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{6} } func (x *GetMutableStateResponse) GetExecution() *v14.WorkflowExecution { @@ -644,14 +745,14 @@ func (x *GetMutableStateResponse) GetLastFirstEventId() int64 { return 0 } -func (x *GetMutableStateResponse) GetTaskQueue() *v111.TaskQueue { +func (x *GetMutableStateResponse) GetTaskQueue() *v112.TaskQueue { if x != nil { return x.TaskQueue } return nil } -func (x *GetMutableStateResponse) GetStickyTaskQueue() *v111.TaskQueue { +func (x *GetMutableStateResponse) GetStickyTaskQueue() *v112.TaskQueue { if x != nil { return x.StickyTaskQueue } @@ -672,11 +773,11 @@ func (x *GetMutableStateResponse) GetCurrentBranchToken() []byte { return nil } -func (x *GetMutableStateResponse) GetWorkflowState() v112.WorkflowExecutionState { +func (x *GetMutableStateResponse) GetWorkflowState() v113.WorkflowExecutionState { if x != nil { return x.WorkflowState } - return v112.WorkflowExecutionState(0) + return v113.WorkflowExecutionState(0) } func (x *GetMutableStateResponse) GetWorkflowStatus() v12.WorkflowExecutionStatus { @@ -686,7 +787,7 @@ func (x *GetMutableStateResponse) GetWorkflowStatus() v12.WorkflowExecutionStatu return v12.WorkflowExecutionStatus(0) } -func (x *GetMutableStateResponse) GetVersionHistories() *v19.VersionHistories { +func (x *GetMutableStateResponse) GetVersionHistories() *v110.VersionHistories { if x != nil { return x.VersionHistories } @@ -735,7 +836,7 @@ func (x *GetMutableStateResponse) GetInheritedBuildId() string { return "" } -func (x *GetMutableStateResponse) GetTransitionHistory() []*v110.VersionedTransition { +func (x *GetMutableStateResponse) GetTransitionHistory() []*v111.VersionedTransition { if x != nil { return x.TransitionHistory } @@ -749,7 +850,7 @@ func (x *GetMutableStateResponse) GetVersioningInfo() *v15.WorkflowExecutionVers return nil } -func (x *GetMutableStateResponse) GetTransientOrSpeculativeTasks() *v19.TransientWorkflowTaskInfo { +func (x *GetMutableStateResponse) GetTransientOrSpeculativeTasks() *v110.TransientWorkflowTaskInfo { if x != nil { return x.TransientOrSpeculativeTasks } @@ -757,19 +858,19 @@ func (x *GetMutableStateResponse) GetTransientOrSpeculativeTasks() *v19.Transien } type PollMutableStateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - Execution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=execution,proto3" json:"execution,omitempty"` - ExpectedNextEventId int64 `protobuf:"varint,3,opt,name=expected_next_event_id,json=expectedNextEventId,proto3" json:"expected_next_event_id,omitempty"` - CurrentBranchToken []byte `protobuf:"bytes,4,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` - VersionHistoryItem *v19.VersionHistoryItem `protobuf:"bytes,5,opt,name=version_history_item,json=versionHistoryItem,proto3" json:"version_history_item,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + Execution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=execution,proto3" json:"execution,omitempty"` + ExpectedNextEventId int64 `protobuf:"varint,3,opt,name=expected_next_event_id,json=expectedNextEventId,proto3" json:"expected_next_event_id,omitempty"` + CurrentBranchToken []byte `protobuf:"bytes,4,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` + VersionHistoryItem *v110.VersionHistoryItem `protobuf:"bytes,5,opt,name=version_history_item,json=versionHistoryItem,proto3" json:"version_history_item,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PollMutableStateRequest) Reset() { *x = PollMutableStateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[5] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -781,7 +882,7 @@ func (x *PollMutableStateRequest) String() string { func (*PollMutableStateRequest) ProtoMessage() {} func (x *PollMutableStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[5] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -794,7 +895,7 @@ func (x *PollMutableStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PollMutableStateRequest.ProtoReflect.Descriptor instead. func (*PollMutableStateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{5} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{7} } func (x *PollMutableStateRequest) GetNamespaceId() string { @@ -825,7 +926,7 @@ func (x *PollMutableStateRequest) GetCurrentBranchToken() []byte { return nil } -func (x *PollMutableStateRequest) GetVersionHistoryItem() *v19.VersionHistoryItem { +func (x *PollMutableStateRequest) GetVersionHistoryItem() *v110.VersionHistoryItem { if x != nil { return x.VersionHistoryItem } @@ -839,15 +940,15 @@ type PollMutableStateResponse struct { NextEventId int64 `protobuf:"varint,3,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` PreviousStartedEventId int64 `protobuf:"varint,4,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` LastFirstEventId int64 `protobuf:"varint,5,opt,name=last_first_event_id,json=lastFirstEventId,proto3" json:"last_first_event_id,omitempty"` - TaskQueue *v111.TaskQueue `protobuf:"bytes,6,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` - StickyTaskQueue *v111.TaskQueue `protobuf:"bytes,7,opt,name=sticky_task_queue,json=stickyTaskQueue,proto3" json:"sticky_task_queue,omitempty"` + TaskQueue *v112.TaskQueue `protobuf:"bytes,6,opt,name=task_queue,json=taskQueue,proto3" json:"task_queue,omitempty"` + StickyTaskQueue *v112.TaskQueue `protobuf:"bytes,7,opt,name=sticky_task_queue,json=stickyTaskQueue,proto3" json:"sticky_task_queue,omitempty"` // (-- api-linter: core::0140::prepositions=disabled // // aip.dev/not-precedent: "to" is used to indicate interval. --) StickyTaskQueueScheduleToStartTimeout *durationpb.Duration `protobuf:"bytes,11,opt,name=sticky_task_queue_schedule_to_start_timeout,json=stickyTaskQueueScheduleToStartTimeout,proto3" json:"sticky_task_queue_schedule_to_start_timeout,omitempty"` CurrentBranchToken []byte `protobuf:"bytes,12,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` - VersionHistories *v19.VersionHistories `protobuf:"bytes,14,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` - WorkflowState v112.WorkflowExecutionState `protobuf:"varint,15,opt,name=workflow_state,json=workflowState,proto3,enum=temporal.server.api.enums.v1.WorkflowExecutionState" json:"workflow_state,omitempty"` + VersionHistories *v110.VersionHistories `protobuf:"bytes,14,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` + WorkflowState v113.WorkflowExecutionState `protobuf:"varint,15,opt,name=workflow_state,json=workflowState,proto3,enum=temporal.server.api.enums.v1.WorkflowExecutionState" json:"workflow_state,omitempty"` WorkflowStatus v12.WorkflowExecutionStatus `protobuf:"varint,16,opt,name=workflow_status,json=workflowStatus,proto3,enum=temporal.api.enums.v1.WorkflowExecutionStatus" json:"workflow_status,omitempty"` LastFirstEventTxnId int64 `protobuf:"varint,17,opt,name=last_first_event_txn_id,json=lastFirstEventTxnId,proto3" json:"last_first_event_txn_id,omitempty"` FirstExecutionRunId string `protobuf:"bytes,18,opt,name=first_execution_run_id,json=firstExecutionRunId,proto3" json:"first_execution_run_id,omitempty"` @@ -857,7 +958,7 @@ type PollMutableStateResponse struct { func (x *PollMutableStateResponse) Reset() { *x = PollMutableStateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[6] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -869,7 +970,7 @@ func (x *PollMutableStateResponse) String() string { func (*PollMutableStateResponse) ProtoMessage() {} func (x *PollMutableStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[6] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -882,7 +983,7 @@ func (x *PollMutableStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PollMutableStateResponse.ProtoReflect.Descriptor instead. func (*PollMutableStateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{6} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{8} } func (x *PollMutableStateResponse) GetExecution() *v14.WorkflowExecution { @@ -920,14 +1021,14 @@ func (x *PollMutableStateResponse) GetLastFirstEventId() int64 { return 0 } -func (x *PollMutableStateResponse) GetTaskQueue() *v111.TaskQueue { +func (x *PollMutableStateResponse) GetTaskQueue() *v112.TaskQueue { if x != nil { return x.TaskQueue } return nil } -func (x *PollMutableStateResponse) GetStickyTaskQueue() *v111.TaskQueue { +func (x *PollMutableStateResponse) GetStickyTaskQueue() *v112.TaskQueue { if x != nil { return x.StickyTaskQueue } @@ -948,18 +1049,18 @@ func (x *PollMutableStateResponse) GetCurrentBranchToken() []byte { return nil } -func (x *PollMutableStateResponse) GetVersionHistories() *v19.VersionHistories { +func (x *PollMutableStateResponse) GetVersionHistories() *v110.VersionHistories { if x != nil { return x.VersionHistories } return nil } -func (x *PollMutableStateResponse) GetWorkflowState() v112.WorkflowExecutionState { +func (x *PollMutableStateResponse) GetWorkflowState() v113.WorkflowExecutionState { if x != nil { return x.WorkflowState } - return v112.WorkflowExecutionState(0) + return v113.WorkflowExecutionState(0) } func (x *PollMutableStateResponse) GetWorkflowStatus() v12.WorkflowExecutionStatus { @@ -993,7 +1094,7 @@ type ResetStickyTaskQueueRequest struct { func (x *ResetStickyTaskQueueRequest) Reset() { *x = ResetStickyTaskQueueRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[7] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1005,7 +1106,7 @@ func (x *ResetStickyTaskQueueRequest) String() string { func (*ResetStickyTaskQueueRequest) ProtoMessage() {} func (x *ResetStickyTaskQueueRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[7] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1018,7 +1119,7 @@ func (x *ResetStickyTaskQueueRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetStickyTaskQueueRequest.ProtoReflect.Descriptor instead. func (*ResetStickyTaskQueueRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{7} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{9} } func (x *ResetStickyTaskQueueRequest) GetNamespaceId() string { @@ -1043,7 +1144,7 @@ type ResetStickyTaskQueueResponse struct { func (x *ResetStickyTaskQueueResponse) Reset() { *x = ResetStickyTaskQueueResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[8] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1055,7 +1156,7 @@ func (x *ResetStickyTaskQueueResponse) String() string { func (*ResetStickyTaskQueueResponse) ProtoMessage() {} func (x *ResetStickyTaskQueueResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[8] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1068,7 +1169,7 @@ func (x *ResetStickyTaskQueueResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetStickyTaskQueueResponse.ProtoReflect.Descriptor instead. func (*ResetStickyTaskQueueResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{8} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{10} } type ExecuteMultiOperationRequest struct { @@ -1082,7 +1183,7 @@ type ExecuteMultiOperationRequest struct { func (x *ExecuteMultiOperationRequest) Reset() { *x = ExecuteMultiOperationRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[9] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1094,7 +1195,7 @@ func (x *ExecuteMultiOperationRequest) String() string { func (*ExecuteMultiOperationRequest) ProtoMessage() {} func (x *ExecuteMultiOperationRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[9] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1107,7 +1208,7 @@ func (x *ExecuteMultiOperationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecuteMultiOperationRequest.ProtoReflect.Descriptor instead. func (*ExecuteMultiOperationRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{9} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{11} } func (x *ExecuteMultiOperationRequest) GetNamespaceId() string { @@ -1140,7 +1241,7 @@ type ExecuteMultiOperationResponse struct { func (x *ExecuteMultiOperationResponse) Reset() { *x = ExecuteMultiOperationResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[10] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1152,7 +1253,7 @@ func (x *ExecuteMultiOperationResponse) String() string { func (*ExecuteMultiOperationResponse) ProtoMessage() {} func (x *ExecuteMultiOperationResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[10] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1165,7 +1266,7 @@ func (x *ExecuteMultiOperationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecuteMultiOperationResponse.ProtoReflect.Descriptor instead. func (*ExecuteMultiOperationResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{10} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{12} } func (x *ExecuteMultiOperationResponse) GetResponses() []*ExecuteMultiOperationResponse_Response { @@ -1184,12 +1285,12 @@ type RecordWorkflowTaskStartedRequest struct { RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` PollRequest *v1.PollWorkflowTaskQueueRequest `protobuf:"bytes,6,opt,name=poll_request,json=pollRequest,proto3" json:"poll_request,omitempty"` Clock *v18.VectorClock `protobuf:"bytes,7,opt,name=clock,proto3" json:"clock,omitempty"` - BuildIdRedirectInfo *v113.BuildIdRedirectInfo `protobuf:"bytes,8,opt,name=build_id_redirect_info,json=buildIdRedirectInfo,proto3" json:"build_id_redirect_info,omitempty"` + BuildIdRedirectInfo *v114.BuildIdRedirectInfo `protobuf:"bytes,8,opt,name=build_id_redirect_info,json=buildIdRedirectInfo,proto3" json:"build_id_redirect_info,omitempty"` // The deployment passed by History when the task was scheduled. // Deprecated. use `version_directive.deployment`. ScheduledDeployment *v16.Deployment `protobuf:"bytes,9,opt,name=scheduled_deployment,json=scheduledDeployment,proto3" json:"scheduled_deployment,omitempty"` // Versioning directive that was sent by history when scheduling the task. - VersionDirective *v113.TaskVersionDirective `protobuf:"bytes,10,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` + VersionDirective *v114.TaskVersionDirective `protobuf:"bytes,10,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` // Stamp value from when the workflow task was scheduled. Used to validate the task is still relevant. Stamp int32 `protobuf:"varint,11,opt,name=stamp,proto3" json:"stamp,omitempty"` // Revision number that was sent by matching when the task was dispatched. Used to resolve eventual consistency issues @@ -1205,7 +1306,7 @@ type RecordWorkflowTaskStartedRequest struct { func (x *RecordWorkflowTaskStartedRequest) Reset() { *x = RecordWorkflowTaskStartedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[11] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1217,7 +1318,7 @@ func (x *RecordWorkflowTaskStartedRequest) String() string { func (*RecordWorkflowTaskStartedRequest) ProtoMessage() {} func (x *RecordWorkflowTaskStartedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[11] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1230,7 +1331,7 @@ func (x *RecordWorkflowTaskStartedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RecordWorkflowTaskStartedRequest.ProtoReflect.Descriptor instead. func (*RecordWorkflowTaskStartedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{11} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{13} } func (x *RecordWorkflowTaskStartedRequest) GetNamespaceId() string { @@ -1275,7 +1376,7 @@ func (x *RecordWorkflowTaskStartedRequest) GetClock() *v18.VectorClock { return nil } -func (x *RecordWorkflowTaskStartedRequest) GetBuildIdRedirectInfo() *v113.BuildIdRedirectInfo { +func (x *RecordWorkflowTaskStartedRequest) GetBuildIdRedirectInfo() *v114.BuildIdRedirectInfo { if x != nil { return x.BuildIdRedirectInfo } @@ -1289,7 +1390,7 @@ func (x *RecordWorkflowTaskStartedRequest) GetScheduledDeployment() *v16.Deploym return nil } -func (x *RecordWorkflowTaskStartedRequest) GetVersionDirective() *v113.TaskVersionDirective { +func (x *RecordWorkflowTaskStartedRequest) GetVersionDirective() *v114.TaskVersionDirective { if x != nil { return x.VersionDirective } @@ -1318,25 +1419,25 @@ func (x *RecordWorkflowTaskStartedRequest) GetTargetDeploymentVersion() *v16.Wor } type RecordWorkflowTaskStartedResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - WorkflowType *v14.WorkflowType `protobuf:"bytes,1,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` - PreviousStartedEventId int64 `protobuf:"varint,2,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` - ScheduledEventId int64 `protobuf:"varint,3,opt,name=scheduled_event_id,json=scheduledEventId,proto3" json:"scheduled_event_id,omitempty"` - StartedEventId int64 `protobuf:"varint,4,opt,name=started_event_id,json=startedEventId,proto3" json:"started_event_id,omitempty"` - NextEventId int64 `protobuf:"varint,5,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` - Attempt int32 `protobuf:"varint,6,opt,name=attempt,proto3" json:"attempt,omitempty"` - StickyExecutionEnabled bool `protobuf:"varint,7,opt,name=sticky_execution_enabled,json=stickyExecutionEnabled,proto3" json:"sticky_execution_enabled,omitempty"` - TransientWorkflowTask *v19.TransientWorkflowTaskInfo `protobuf:"bytes,8,opt,name=transient_workflow_task,json=transientWorkflowTask,proto3" json:"transient_workflow_task,omitempty"` - WorkflowExecutionTaskQueue *v111.TaskQueue `protobuf:"bytes,9,opt,name=workflow_execution_task_queue,json=workflowExecutionTaskQueue,proto3" json:"workflow_execution_task_queue,omitempty"` - BranchToken []byte `protobuf:"bytes,11,opt,name=branch_token,json=branchToken,proto3" json:"branch_token,omitempty"` - ScheduledTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` - StartedTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` - Queries map[string]*v114.WorkflowQuery `protobuf:"bytes,14,rep,name=queries,proto3" json:"queries,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - Clock *v18.VectorClock `protobuf:"bytes,15,opt,name=clock,proto3" json:"clock,omitempty"` - Messages []*v115.Message `protobuf:"bytes,16,rep,name=messages,proto3" json:"messages,omitempty"` - Version int64 `protobuf:"varint,17,opt,name=version,proto3" json:"version,omitempty"` - History *v17.History `protobuf:"bytes,18,opt,name=history,proto3" json:"history,omitempty"` - NextPageToken []byte `protobuf:"bytes,19,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + WorkflowType *v14.WorkflowType `protobuf:"bytes,1,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` + PreviousStartedEventId int64 `protobuf:"varint,2,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` + ScheduledEventId int64 `protobuf:"varint,3,opt,name=scheduled_event_id,json=scheduledEventId,proto3" json:"scheduled_event_id,omitempty"` + StartedEventId int64 `protobuf:"varint,4,opt,name=started_event_id,json=startedEventId,proto3" json:"started_event_id,omitempty"` + NextEventId int64 `protobuf:"varint,5,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` + Attempt int32 `protobuf:"varint,6,opt,name=attempt,proto3" json:"attempt,omitempty"` + StickyExecutionEnabled bool `protobuf:"varint,7,opt,name=sticky_execution_enabled,json=stickyExecutionEnabled,proto3" json:"sticky_execution_enabled,omitempty"` + TransientWorkflowTask *v110.TransientWorkflowTaskInfo `protobuf:"bytes,8,opt,name=transient_workflow_task,json=transientWorkflowTask,proto3" json:"transient_workflow_task,omitempty"` + WorkflowExecutionTaskQueue *v112.TaskQueue `protobuf:"bytes,9,opt,name=workflow_execution_task_queue,json=workflowExecutionTaskQueue,proto3" json:"workflow_execution_task_queue,omitempty"` + BranchToken []byte `protobuf:"bytes,11,opt,name=branch_token,json=branchToken,proto3" json:"branch_token,omitempty"` + ScheduledTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` + StartedTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` + Queries map[string]*v115.WorkflowQuery `protobuf:"bytes,14,rep,name=queries,proto3" json:"queries,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Clock *v18.VectorClock `protobuf:"bytes,15,opt,name=clock,proto3" json:"clock,omitempty"` + Messages []*v116.Message `protobuf:"bytes,16,rep,name=messages,proto3" json:"messages,omitempty"` + Version int64 `protobuf:"varint,17,opt,name=version,proto3" json:"version,omitempty"` + History *v17.History `protobuf:"bytes,18,opt,name=history,proto3" json:"history,omitempty"` + NextPageToken []byte `protobuf:"bytes,19,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` // Deprecated: This field is being replaced by raw_history_bytes which sends raw bytes // instead of a proto-decoded History. This avoids matching service having to decode history. // TODO: PRATHYUSH @@ -1357,14 +1458,14 @@ type RecordWorkflowTaskStartedResponse struct { // Slices of the streams this Workflow is consuming, each covering the offset // range the task may deliver. Payloads ride here and never enter History; // only the consumed range is recorded, on WorkflowTaskCompleted. - StreamSlices []*v116.StreamSlice `protobuf:"bytes,22,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` + StreamSlices []*v19.StreamSlice `protobuf:"bytes,22,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RecordWorkflowTaskStartedResponse) Reset() { *x = RecordWorkflowTaskStartedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[12] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1376,7 +1477,7 @@ func (x *RecordWorkflowTaskStartedResponse) String() string { func (*RecordWorkflowTaskStartedResponse) ProtoMessage() {} func (x *RecordWorkflowTaskStartedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[12] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1389,7 +1490,7 @@ func (x *RecordWorkflowTaskStartedResponse) ProtoReflect() protoreflect.Message // Deprecated: Use RecordWorkflowTaskStartedResponse.ProtoReflect.Descriptor instead. func (*RecordWorkflowTaskStartedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{12} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{14} } func (x *RecordWorkflowTaskStartedResponse) GetWorkflowType() *v14.WorkflowType { @@ -1441,14 +1542,14 @@ func (x *RecordWorkflowTaskStartedResponse) GetStickyExecutionEnabled() bool { return false } -func (x *RecordWorkflowTaskStartedResponse) GetTransientWorkflowTask() *v19.TransientWorkflowTaskInfo { +func (x *RecordWorkflowTaskStartedResponse) GetTransientWorkflowTask() *v110.TransientWorkflowTaskInfo { if x != nil { return x.TransientWorkflowTask } return nil } -func (x *RecordWorkflowTaskStartedResponse) GetWorkflowExecutionTaskQueue() *v111.TaskQueue { +func (x *RecordWorkflowTaskStartedResponse) GetWorkflowExecutionTaskQueue() *v112.TaskQueue { if x != nil { return x.WorkflowExecutionTaskQueue } @@ -1476,7 +1577,7 @@ func (x *RecordWorkflowTaskStartedResponse) GetStartedTime() *timestamppb.Timest return nil } -func (x *RecordWorkflowTaskStartedResponse) GetQueries() map[string]*v114.WorkflowQuery { +func (x *RecordWorkflowTaskStartedResponse) GetQueries() map[string]*v115.WorkflowQuery { if x != nil { return x.Queries } @@ -1490,7 +1591,7 @@ func (x *RecordWorkflowTaskStartedResponse) GetClock() *v18.VectorClock { return nil } -func (x *RecordWorkflowTaskStartedResponse) GetMessages() []*v115.Message { +func (x *RecordWorkflowTaskStartedResponse) GetMessages() []*v116.Message { if x != nil { return x.Messages } @@ -1533,7 +1634,7 @@ func (x *RecordWorkflowTaskStartedResponse) GetRawHistoryBytes() [][]byte { return nil } -func (x *RecordWorkflowTaskStartedResponse) GetStreamSlices() []*v116.StreamSlice { +func (x *RecordWorkflowTaskStartedResponse) GetStreamSlices() []*v19.StreamSlice { if x != nil { return x.StreamSlices } @@ -1559,25 +1660,25 @@ func (x *RecordWorkflowTaskStartedResponse) GetStreamSlices() []*v116.StreamSlic // IMPORTANT: Field numbers and all other fields must remain identical between these two messages. // Any change to RecordWorkflowTaskStartedResponse must be mirrored here. type RecordWorkflowTaskStartedResponseWithRawHistory struct { - state protoimpl.MessageState `protogen:"open.v1"` - WorkflowType *v14.WorkflowType `protobuf:"bytes,1,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` - PreviousStartedEventId int64 `protobuf:"varint,2,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` - ScheduledEventId int64 `protobuf:"varint,3,opt,name=scheduled_event_id,json=scheduledEventId,proto3" json:"scheduled_event_id,omitempty"` - StartedEventId int64 `protobuf:"varint,4,opt,name=started_event_id,json=startedEventId,proto3" json:"started_event_id,omitempty"` - NextEventId int64 `protobuf:"varint,5,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` - Attempt int32 `protobuf:"varint,6,opt,name=attempt,proto3" json:"attempt,omitempty"` - StickyExecutionEnabled bool `protobuf:"varint,7,opt,name=sticky_execution_enabled,json=stickyExecutionEnabled,proto3" json:"sticky_execution_enabled,omitempty"` - TransientWorkflowTask *v19.TransientWorkflowTaskInfo `protobuf:"bytes,8,opt,name=transient_workflow_task,json=transientWorkflowTask,proto3" json:"transient_workflow_task,omitempty"` - WorkflowExecutionTaskQueue *v111.TaskQueue `protobuf:"bytes,9,opt,name=workflow_execution_task_queue,json=workflowExecutionTaskQueue,proto3" json:"workflow_execution_task_queue,omitempty"` - BranchToken []byte `protobuf:"bytes,11,opt,name=branch_token,json=branchToken,proto3" json:"branch_token,omitempty"` - ScheduledTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` - StartedTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` - Queries map[string]*v114.WorkflowQuery `protobuf:"bytes,14,rep,name=queries,proto3" json:"queries,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - Clock *v18.VectorClock `protobuf:"bytes,15,opt,name=clock,proto3" json:"clock,omitempty"` - Messages []*v115.Message `protobuf:"bytes,16,rep,name=messages,proto3" json:"messages,omitempty"` - Version int64 `protobuf:"varint,17,opt,name=version,proto3" json:"version,omitempty"` - History *v17.History `protobuf:"bytes,18,opt,name=history,proto3" json:"history,omitempty"` - NextPageToken []byte `protobuf:"bytes,19,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + WorkflowType *v14.WorkflowType `protobuf:"bytes,1,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` + PreviousStartedEventId int64 `protobuf:"varint,2,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` + ScheduledEventId int64 `protobuf:"varint,3,opt,name=scheduled_event_id,json=scheduledEventId,proto3" json:"scheduled_event_id,omitempty"` + StartedEventId int64 `protobuf:"varint,4,opt,name=started_event_id,json=startedEventId,proto3" json:"started_event_id,omitempty"` + NextEventId int64 `protobuf:"varint,5,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` + Attempt int32 `protobuf:"varint,6,opt,name=attempt,proto3" json:"attempt,omitempty"` + StickyExecutionEnabled bool `protobuf:"varint,7,opt,name=sticky_execution_enabled,json=stickyExecutionEnabled,proto3" json:"sticky_execution_enabled,omitempty"` + TransientWorkflowTask *v110.TransientWorkflowTaskInfo `protobuf:"bytes,8,opt,name=transient_workflow_task,json=transientWorkflowTask,proto3" json:"transient_workflow_task,omitempty"` + WorkflowExecutionTaskQueue *v112.TaskQueue `protobuf:"bytes,9,opt,name=workflow_execution_task_queue,json=workflowExecutionTaskQueue,proto3" json:"workflow_execution_task_queue,omitempty"` + BranchToken []byte `protobuf:"bytes,11,opt,name=branch_token,json=branchToken,proto3" json:"branch_token,omitempty"` + ScheduledTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` + StartedTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` + Queries map[string]*v115.WorkflowQuery `protobuf:"bytes,14,rep,name=queries,proto3" json:"queries,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Clock *v18.VectorClock `protobuf:"bytes,15,opt,name=clock,proto3" json:"clock,omitempty"` + Messages []*v116.Message `protobuf:"bytes,16,rep,name=messages,proto3" json:"messages,omitempty"` + Version int64 `protobuf:"varint,17,opt,name=version,proto3" json:"version,omitempty"` + History *v17.History `protobuf:"bytes,18,opt,name=history,proto3" json:"history,omitempty"` + NextPageToken []byte `protobuf:"bytes,19,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` // Deprecated: This field is being replaced by raw_history_bytes which sends raw bytes // instead of a proto-decoded History. This avoids matching service having to decode history. // @@ -1587,14 +1688,14 @@ type RecordWorkflowTaskStartedResponseWithRawHistory struct { // Slices of the streams this Workflow is consuming, each covering the offset // range the task may deliver. Payloads ride here and never enter History; // only the consumed range is recorded, on WorkflowTaskCompleted. - StreamSlices []*v116.StreamSlice `protobuf:"bytes,22,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` + StreamSlices []*v19.StreamSlice `protobuf:"bytes,22,rep,name=stream_slices,json=streamSlices,proto3" json:"stream_slices,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RecordWorkflowTaskStartedResponseWithRawHistory) Reset() { *x = RecordWorkflowTaskStartedResponseWithRawHistory{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[13] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1606,7 +1707,7 @@ func (x *RecordWorkflowTaskStartedResponseWithRawHistory) String() string { func (*RecordWorkflowTaskStartedResponseWithRawHistory) ProtoMessage() {} func (x *RecordWorkflowTaskStartedResponseWithRawHistory) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[13] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1619,7 +1720,7 @@ func (x *RecordWorkflowTaskStartedResponseWithRawHistory) ProtoReflect() protore // Deprecated: Use RecordWorkflowTaskStartedResponseWithRawHistory.ProtoReflect.Descriptor instead. func (*RecordWorkflowTaskStartedResponseWithRawHistory) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{13} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{15} } func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetWorkflowType() *v14.WorkflowType { @@ -1671,14 +1772,14 @@ func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetStickyExecutionEnab return false } -func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetTransientWorkflowTask() *v19.TransientWorkflowTaskInfo { +func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetTransientWorkflowTask() *v110.TransientWorkflowTaskInfo { if x != nil { return x.TransientWorkflowTask } return nil } -func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetWorkflowExecutionTaskQueue() *v111.TaskQueue { +func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetWorkflowExecutionTaskQueue() *v112.TaskQueue { if x != nil { return x.WorkflowExecutionTaskQueue } @@ -1706,7 +1807,7 @@ func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetStartedTime() *time return nil } -func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetQueries() map[string]*v114.WorkflowQuery { +func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetQueries() map[string]*v115.WorkflowQuery { if x != nil { return x.Queries } @@ -1720,7 +1821,7 @@ func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetClock() *v18.Vector return nil } -func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetMessages() []*v115.Message { +func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetMessages() []*v116.Message { if x != nil { return x.Messages } @@ -1763,7 +1864,7 @@ func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetRawHistoryBytes() [ return nil } -func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetStreamSlices() []*v116.StreamSlice { +func (x *RecordWorkflowTaskStartedResponseWithRawHistory) GetStreamSlices() []*v19.StreamSlice { if x != nil { return x.StreamSlices } @@ -1779,14 +1880,14 @@ type RecordActivityTaskStartedRequest struct { RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` PollRequest *v1.PollActivityTaskQueueRequest `protobuf:"bytes,6,opt,name=poll_request,json=pollRequest,proto3" json:"poll_request,omitempty"` Clock *v18.VectorClock `protobuf:"bytes,7,opt,name=clock,proto3" json:"clock,omitempty"` - BuildIdRedirectInfo *v113.BuildIdRedirectInfo `protobuf:"bytes,8,opt,name=build_id_redirect_info,json=buildIdRedirectInfo,proto3" json:"build_id_redirect_info,omitempty"` + BuildIdRedirectInfo *v114.BuildIdRedirectInfo `protobuf:"bytes,8,opt,name=build_id_redirect_info,json=buildIdRedirectInfo,proto3" json:"build_id_redirect_info,omitempty"` // Stamp represents the internal “version” of the activity options and can/will be changed with Activity API. Stamp int32 `protobuf:"varint,9,opt,name=stamp,proto3" json:"stamp,omitempty"` // The deployment passed by History when the task was scheduled. // Deprecated. use `version_directive.deployment`. ScheduledDeployment *v16.Deployment `protobuf:"bytes,10,opt,name=scheduled_deployment,json=scheduledDeployment,proto3" json:"scheduled_deployment,omitempty"` // Versioning directive that was sent by history when scheduling the task. - VersionDirective *v113.TaskVersionDirective `protobuf:"bytes,12,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` + VersionDirective *v114.TaskVersionDirective `protobuf:"bytes,12,opt,name=version_directive,json=versionDirective,proto3" json:"version_directive,omitempty"` // Revision number that was sent by matching when the task was dispatched. Used to resolve eventual consistency issues // that may arise due to stale routing configs in task queue partitions. TaskDispatchRevisionNumber int64 `protobuf:"varint,13,opt,name=task_dispatch_revision_number,json=taskDispatchRevisionNumber,proto3" json:"task_dispatch_revision_number,omitempty"` @@ -1800,7 +1901,7 @@ type RecordActivityTaskStartedRequest struct { func (x *RecordActivityTaskStartedRequest) Reset() { *x = RecordActivityTaskStartedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[14] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1812,7 +1913,7 @@ func (x *RecordActivityTaskStartedRequest) String() string { func (*RecordActivityTaskStartedRequest) ProtoMessage() {} func (x *RecordActivityTaskStartedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[14] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1825,7 +1926,7 @@ func (x *RecordActivityTaskStartedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RecordActivityTaskStartedRequest.ProtoReflect.Descriptor instead. func (*RecordActivityTaskStartedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{14} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{16} } func (x *RecordActivityTaskStartedRequest) GetNamespaceId() string { @@ -1870,7 +1971,7 @@ func (x *RecordActivityTaskStartedRequest) GetClock() *v18.VectorClock { return nil } -func (x *RecordActivityTaskStartedRequest) GetBuildIdRedirectInfo() *v113.BuildIdRedirectInfo { +func (x *RecordActivityTaskStartedRequest) GetBuildIdRedirectInfo() *v114.BuildIdRedirectInfo { if x != nil { return x.BuildIdRedirectInfo } @@ -1891,7 +1992,7 @@ func (x *RecordActivityTaskStartedRequest) GetScheduledDeployment() *v16.Deploym return nil } -func (x *RecordActivityTaskStartedRequest) GetVersionDirective() *v113.TaskVersionDirective { +func (x *RecordActivityTaskStartedRequest) GetVersionDirective() *v114.TaskVersionDirective { if x != nil { return x.VersionDirective } @@ -1934,7 +2035,7 @@ type RecordActivityTaskStartedResponse struct { func (x *RecordActivityTaskStartedResponse) Reset() { *x = RecordActivityTaskStartedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[15] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1946,7 +2047,7 @@ func (x *RecordActivityTaskStartedResponse) String() string { func (*RecordActivityTaskStartedResponse) ProtoMessage() {} func (x *RecordActivityTaskStartedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[15] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1959,7 +2060,7 @@ func (x *RecordActivityTaskStartedResponse) ProtoReflect() protoreflect.Message // Deprecated: Use RecordActivityTaskStartedResponse.ProtoReflect.Descriptor instead. func (*RecordActivityTaskStartedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{15} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{17} } func (x *RecordActivityTaskStartedResponse) GetScheduledEvent() *v17.HistoryEvent { @@ -2063,7 +2164,7 @@ type RespondWorkflowTaskCompletedRequest struct { func (x *RespondWorkflowTaskCompletedRequest) Reset() { *x = RespondWorkflowTaskCompletedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[16] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2075,7 +2176,7 @@ func (x *RespondWorkflowTaskCompletedRequest) String() string { func (*RespondWorkflowTaskCompletedRequest) ProtoMessage() {} func (x *RespondWorkflowTaskCompletedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[16] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2088,7 +2189,7 @@ func (x *RespondWorkflowTaskCompletedRequest) ProtoReflect() protoreflect.Messag // Deprecated: Use RespondWorkflowTaskCompletedRequest.ProtoReflect.Descriptor instead. func (*RespondWorkflowTaskCompletedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{16} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{18} } func (x *RespondWorkflowTaskCompletedRequest) GetNamespaceId() string { @@ -2118,7 +2219,7 @@ type RespondWorkflowTaskCompletedResponse struct { func (x *RespondWorkflowTaskCompletedResponse) Reset() { *x = RespondWorkflowTaskCompletedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[17] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2130,7 +2231,7 @@ func (x *RespondWorkflowTaskCompletedResponse) String() string { func (*RespondWorkflowTaskCompletedResponse) ProtoMessage() {} func (x *RespondWorkflowTaskCompletedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[17] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2143,7 +2244,7 @@ func (x *RespondWorkflowTaskCompletedResponse) ProtoReflect() protoreflect.Messa // Deprecated: Use RespondWorkflowTaskCompletedResponse.ProtoReflect.Descriptor instead. func (*RespondWorkflowTaskCompletedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{17} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{19} } // Deprecated: Marked as deprecated in temporal/server/api/historyservice/v1/request_response.proto. @@ -2185,7 +2286,7 @@ type RespondWorkflowTaskFailedRequest struct { func (x *RespondWorkflowTaskFailedRequest) Reset() { *x = RespondWorkflowTaskFailedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[18] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2197,7 +2298,7 @@ func (x *RespondWorkflowTaskFailedRequest) String() string { func (*RespondWorkflowTaskFailedRequest) ProtoMessage() {} func (x *RespondWorkflowTaskFailedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[18] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2210,7 +2311,7 @@ func (x *RespondWorkflowTaskFailedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RespondWorkflowTaskFailedRequest.ProtoReflect.Descriptor instead. func (*RespondWorkflowTaskFailedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{18} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{20} } func (x *RespondWorkflowTaskFailedRequest) GetNamespaceId() string { @@ -2235,7 +2336,7 @@ type RespondWorkflowTaskFailedResponse struct { func (x *RespondWorkflowTaskFailedResponse) Reset() { *x = RespondWorkflowTaskFailedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[19] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2247,7 +2348,7 @@ func (x *RespondWorkflowTaskFailedResponse) String() string { func (*RespondWorkflowTaskFailedResponse) ProtoMessage() {} func (x *RespondWorkflowTaskFailedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[19] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2260,7 +2361,7 @@ func (x *RespondWorkflowTaskFailedResponse) ProtoReflect() protoreflect.Message // Deprecated: Use RespondWorkflowTaskFailedResponse.ProtoReflect.Descriptor instead. func (*RespondWorkflowTaskFailedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{19} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{21} } type IsWorkflowTaskValidRequest struct { @@ -2276,7 +2377,7 @@ type IsWorkflowTaskValidRequest struct { func (x *IsWorkflowTaskValidRequest) Reset() { *x = IsWorkflowTaskValidRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[20] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2288,7 +2389,7 @@ func (x *IsWorkflowTaskValidRequest) String() string { func (*IsWorkflowTaskValidRequest) ProtoMessage() {} func (x *IsWorkflowTaskValidRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[20] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2301,7 +2402,7 @@ func (x *IsWorkflowTaskValidRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use IsWorkflowTaskValidRequest.ProtoReflect.Descriptor instead. func (*IsWorkflowTaskValidRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{20} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{22} } func (x *IsWorkflowTaskValidRequest) GetNamespaceId() string { @@ -2349,7 +2450,7 @@ type IsWorkflowTaskValidResponse struct { func (x *IsWorkflowTaskValidResponse) Reset() { *x = IsWorkflowTaskValidResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[21] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2361,7 +2462,7 @@ func (x *IsWorkflowTaskValidResponse) String() string { func (*IsWorkflowTaskValidResponse) ProtoMessage() {} func (x *IsWorkflowTaskValidResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[21] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2374,7 +2475,7 @@ func (x *IsWorkflowTaskValidResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use IsWorkflowTaskValidResponse.ProtoReflect.Descriptor instead. func (*IsWorkflowTaskValidResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{21} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{23} } func (x *IsWorkflowTaskValidResponse) GetIsValid() bool { @@ -2394,7 +2495,7 @@ type RecordActivityTaskHeartbeatRequest struct { func (x *RecordActivityTaskHeartbeatRequest) Reset() { *x = RecordActivityTaskHeartbeatRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[22] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2406,7 +2507,7 @@ func (x *RecordActivityTaskHeartbeatRequest) String() string { func (*RecordActivityTaskHeartbeatRequest) ProtoMessage() {} func (x *RecordActivityTaskHeartbeatRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[22] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2419,7 +2520,7 @@ func (x *RecordActivityTaskHeartbeatRequest) ProtoReflect() protoreflect.Message // Deprecated: Use RecordActivityTaskHeartbeatRequest.ProtoReflect.Descriptor instead. func (*RecordActivityTaskHeartbeatRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{22} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{24} } func (x *RecordActivityTaskHeartbeatRequest) GetNamespaceId() string { @@ -2447,7 +2548,7 @@ type RecordActivityTaskHeartbeatResponse struct { func (x *RecordActivityTaskHeartbeatResponse) Reset() { *x = RecordActivityTaskHeartbeatResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[23] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2459,7 +2560,7 @@ func (x *RecordActivityTaskHeartbeatResponse) String() string { func (*RecordActivityTaskHeartbeatResponse) ProtoMessage() {} func (x *RecordActivityTaskHeartbeatResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[23] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2472,7 +2573,7 @@ func (x *RecordActivityTaskHeartbeatResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use RecordActivityTaskHeartbeatResponse.ProtoReflect.Descriptor instead. func (*RecordActivityTaskHeartbeatResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{23} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{25} } func (x *RecordActivityTaskHeartbeatResponse) GetCancelRequested() bool { @@ -2506,7 +2607,7 @@ type RespondActivityTaskCompletedRequest struct { func (x *RespondActivityTaskCompletedRequest) Reset() { *x = RespondActivityTaskCompletedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[24] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2518,7 +2619,7 @@ func (x *RespondActivityTaskCompletedRequest) String() string { func (*RespondActivityTaskCompletedRequest) ProtoMessage() {} func (x *RespondActivityTaskCompletedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[24] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2531,7 +2632,7 @@ func (x *RespondActivityTaskCompletedRequest) ProtoReflect() protoreflect.Messag // Deprecated: Use RespondActivityTaskCompletedRequest.ProtoReflect.Descriptor instead. func (*RespondActivityTaskCompletedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{24} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{26} } func (x *RespondActivityTaskCompletedRequest) GetNamespaceId() string { @@ -2556,7 +2657,7 @@ type RespondActivityTaskCompletedResponse struct { func (x *RespondActivityTaskCompletedResponse) Reset() { *x = RespondActivityTaskCompletedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[25] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2568,7 +2669,7 @@ func (x *RespondActivityTaskCompletedResponse) String() string { func (*RespondActivityTaskCompletedResponse) ProtoMessage() {} func (x *RespondActivityTaskCompletedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[25] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2581,7 +2682,7 @@ func (x *RespondActivityTaskCompletedResponse) ProtoReflect() protoreflect.Messa // Deprecated: Use RespondActivityTaskCompletedResponse.ProtoReflect.Descriptor instead. func (*RespondActivityTaskCompletedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{25} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{27} } type RespondActivityTaskFailedRequest struct { @@ -2594,7 +2695,7 @@ type RespondActivityTaskFailedRequest struct { func (x *RespondActivityTaskFailedRequest) Reset() { *x = RespondActivityTaskFailedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[26] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2606,7 +2707,7 @@ func (x *RespondActivityTaskFailedRequest) String() string { func (*RespondActivityTaskFailedRequest) ProtoMessage() {} func (x *RespondActivityTaskFailedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[26] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2619,7 +2720,7 @@ func (x *RespondActivityTaskFailedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RespondActivityTaskFailedRequest.ProtoReflect.Descriptor instead. func (*RespondActivityTaskFailedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{26} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{28} } func (x *RespondActivityTaskFailedRequest) GetNamespaceId() string { @@ -2644,7 +2745,7 @@ type RespondActivityTaskFailedResponse struct { func (x *RespondActivityTaskFailedResponse) Reset() { *x = RespondActivityTaskFailedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[27] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2656,7 +2757,7 @@ func (x *RespondActivityTaskFailedResponse) String() string { func (*RespondActivityTaskFailedResponse) ProtoMessage() {} func (x *RespondActivityTaskFailedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[27] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2669,7 +2770,7 @@ func (x *RespondActivityTaskFailedResponse) ProtoReflect() protoreflect.Message // Deprecated: Use RespondActivityTaskFailedResponse.ProtoReflect.Descriptor instead. func (*RespondActivityTaskFailedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{27} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{29} } type RespondActivityTaskCanceledRequest struct { @@ -2682,7 +2783,7 @@ type RespondActivityTaskCanceledRequest struct { func (x *RespondActivityTaskCanceledRequest) Reset() { *x = RespondActivityTaskCanceledRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[28] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2694,7 +2795,7 @@ func (x *RespondActivityTaskCanceledRequest) String() string { func (*RespondActivityTaskCanceledRequest) ProtoMessage() {} func (x *RespondActivityTaskCanceledRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[28] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2707,7 +2808,7 @@ func (x *RespondActivityTaskCanceledRequest) ProtoReflect() protoreflect.Message // Deprecated: Use RespondActivityTaskCanceledRequest.ProtoReflect.Descriptor instead. func (*RespondActivityTaskCanceledRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{28} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{30} } func (x *RespondActivityTaskCanceledRequest) GetNamespaceId() string { @@ -2732,7 +2833,7 @@ type RespondActivityTaskCanceledResponse struct { func (x *RespondActivityTaskCanceledResponse) Reset() { *x = RespondActivityTaskCanceledResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[29] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2744,7 +2845,7 @@ func (x *RespondActivityTaskCanceledResponse) String() string { func (*RespondActivityTaskCanceledResponse) ProtoMessage() {} func (x *RespondActivityTaskCanceledResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[29] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2757,7 +2858,7 @@ func (x *RespondActivityTaskCanceledResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use RespondActivityTaskCanceledResponse.ProtoReflect.Descriptor instead. func (*RespondActivityTaskCanceledResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{29} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{31} } type IsActivityTaskValidRequest struct { @@ -2774,7 +2875,7 @@ type IsActivityTaskValidRequest struct { func (x *IsActivityTaskValidRequest) Reset() { *x = IsActivityTaskValidRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[30] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2786,7 +2887,7 @@ func (x *IsActivityTaskValidRequest) String() string { func (*IsActivityTaskValidRequest) ProtoMessage() {} func (x *IsActivityTaskValidRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[30] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2799,7 +2900,7 @@ func (x *IsActivityTaskValidRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use IsActivityTaskValidRequest.ProtoReflect.Descriptor instead. func (*IsActivityTaskValidRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{30} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{32} } func (x *IsActivityTaskValidRequest) GetNamespaceId() string { @@ -2847,7 +2948,7 @@ type IsActivityTaskValidResponse struct { func (x *IsActivityTaskValidResponse) Reset() { *x = IsActivityTaskValidResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[31] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2859,7 +2960,7 @@ func (x *IsActivityTaskValidResponse) String() string { func (*IsActivityTaskValidResponse) ProtoMessage() {} func (x *IsActivityTaskValidResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[31] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2872,7 +2973,7 @@ func (x *IsActivityTaskValidResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use IsActivityTaskValidResponse.ProtoReflect.Descriptor instead. func (*IsActivityTaskValidResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{31} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{33} } func (x *IsActivityTaskValidResponse) GetIsValid() bool { @@ -2894,7 +2995,7 @@ type SignalWorkflowExecutionRequest struct { func (x *SignalWorkflowExecutionRequest) Reset() { *x = SignalWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[32] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2906,7 +3007,7 @@ func (x *SignalWorkflowExecutionRequest) String() string { func (*SignalWorkflowExecutionRequest) ProtoMessage() {} func (x *SignalWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[32] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2919,7 +3020,7 @@ func (x *SignalWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SignalWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*SignalWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{32} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{34} } func (x *SignalWorkflowExecutionRequest) GetNamespaceId() string { @@ -2959,7 +3060,7 @@ type SignalWorkflowExecutionResponse struct { func (x *SignalWorkflowExecutionResponse) Reset() { *x = SignalWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[33] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2971,7 +3072,7 @@ func (x *SignalWorkflowExecutionResponse) String() string { func (*SignalWorkflowExecutionResponse) ProtoMessage() {} func (x *SignalWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[33] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2984,7 +3085,7 @@ func (x *SignalWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SignalWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*SignalWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{33} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{35} } func (x *SignalWorkflowExecutionResponse) GetLink() *v14.Link { @@ -3007,7 +3108,7 @@ type SignalWithStartWorkflowExecutionRequest struct { func (x *SignalWithStartWorkflowExecutionRequest) Reset() { *x = SignalWithStartWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[34] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3019,7 +3120,7 @@ func (x *SignalWithStartWorkflowExecutionRequest) String() string { func (*SignalWithStartWorkflowExecutionRequest) ProtoMessage() {} func (x *SignalWithStartWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[34] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3032,7 +3133,7 @@ func (x *SignalWithStartWorkflowExecutionRequest) ProtoReflect() protoreflect.Me // Deprecated: Use SignalWithStartWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*SignalWithStartWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{34} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{36} } func (x *SignalWithStartWorkflowExecutionRequest) GetNamespaceId() string { @@ -3064,7 +3165,7 @@ type SignalWithStartWorkflowExecutionResponse struct { func (x *SignalWithStartWorkflowExecutionResponse) Reset() { *x = SignalWithStartWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[35] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3076,7 +3177,7 @@ func (x *SignalWithStartWorkflowExecutionResponse) String() string { func (*SignalWithStartWorkflowExecutionResponse) ProtoMessage() {} func (x *SignalWithStartWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[35] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3089,7 +3190,7 @@ func (x *SignalWithStartWorkflowExecutionResponse) ProtoReflect() protoreflect.M // Deprecated: Use SignalWithStartWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*SignalWithStartWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{35} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{37} } func (x *SignalWithStartWorkflowExecutionResponse) GetRunId() string { @@ -3131,7 +3232,7 @@ type RemoveSignalMutableStateRequest struct { func (x *RemoveSignalMutableStateRequest) Reset() { *x = RemoveSignalMutableStateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[36] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3143,7 +3244,7 @@ func (x *RemoveSignalMutableStateRequest) String() string { func (*RemoveSignalMutableStateRequest) ProtoMessage() {} func (x *RemoveSignalMutableStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[36] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3156,7 +3257,7 @@ func (x *RemoveSignalMutableStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveSignalMutableStateRequest.ProtoReflect.Descriptor instead. func (*RemoveSignalMutableStateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{36} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{38} } func (x *RemoveSignalMutableStateRequest) GetNamespaceId() string { @@ -3188,7 +3289,7 @@ type RemoveSignalMutableStateResponse struct { func (x *RemoveSignalMutableStateResponse) Reset() { *x = RemoveSignalMutableStateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[37] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3200,7 +3301,7 @@ func (x *RemoveSignalMutableStateResponse) String() string { func (*RemoveSignalMutableStateResponse) ProtoMessage() {} func (x *RemoveSignalMutableStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[37] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3213,7 +3314,7 @@ func (x *RemoveSignalMutableStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveSignalMutableStateResponse.ProtoReflect.Descriptor instead. func (*RemoveSignalMutableStateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{37} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{39} } type TerminateWorkflowExecutionRequest struct { @@ -3228,7 +3329,7 @@ type TerminateWorkflowExecutionRequest struct { func (x *TerminateWorkflowExecutionRequest) Reset() { *x = TerminateWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[38] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3240,7 +3341,7 @@ func (x *TerminateWorkflowExecutionRequest) String() string { func (*TerminateWorkflowExecutionRequest) ProtoMessage() {} func (x *TerminateWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[38] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3253,7 +3354,7 @@ func (x *TerminateWorkflowExecutionRequest) ProtoReflect() protoreflect.Message // Deprecated: Use TerminateWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*TerminateWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{38} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{40} } func (x *TerminateWorkflowExecutionRequest) GetNamespaceId() string { @@ -3292,7 +3393,7 @@ type TerminateWorkflowExecutionResponse struct { func (x *TerminateWorkflowExecutionResponse) Reset() { *x = TerminateWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[39] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3304,7 +3405,7 @@ func (x *TerminateWorkflowExecutionResponse) String() string { func (*TerminateWorkflowExecutionResponse) ProtoMessage() {} func (x *TerminateWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[39] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3317,7 +3418,7 @@ func (x *TerminateWorkflowExecutionResponse) ProtoReflect() protoreflect.Message // Deprecated: Use TerminateWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*TerminateWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{39} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{41} } type DeleteWorkflowExecutionRequest struct { @@ -3331,7 +3432,7 @@ type DeleteWorkflowExecutionRequest struct { func (x *DeleteWorkflowExecutionRequest) Reset() { *x = DeleteWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[40] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3343,7 +3444,7 @@ func (x *DeleteWorkflowExecutionRequest) String() string { func (*DeleteWorkflowExecutionRequest) ProtoMessage() {} func (x *DeleteWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[40] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3356,7 +3457,7 @@ func (x *DeleteWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{40} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{42} } func (x *DeleteWorkflowExecutionRequest) GetNamespaceId() string { @@ -3388,7 +3489,7 @@ type DeleteWorkflowExecutionResponse struct { func (x *DeleteWorkflowExecutionResponse) Reset() { *x = DeleteWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[41] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3400,7 +3501,7 @@ func (x *DeleteWorkflowExecutionResponse) String() string { func (*DeleteWorkflowExecutionResponse) ProtoMessage() {} func (x *DeleteWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[41] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3413,7 +3514,7 @@ func (x *DeleteWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*DeleteWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{41} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{43} } type ResetWorkflowExecutionRequest struct { @@ -3426,7 +3527,7 @@ type ResetWorkflowExecutionRequest struct { func (x *ResetWorkflowExecutionRequest) Reset() { *x = ResetWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[42] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3438,7 +3539,7 @@ func (x *ResetWorkflowExecutionRequest) String() string { func (*ResetWorkflowExecutionRequest) ProtoMessage() {} func (x *ResetWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[42] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3451,7 +3552,7 @@ func (x *ResetWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*ResetWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{42} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{44} } func (x *ResetWorkflowExecutionRequest) GetNamespaceId() string { @@ -3477,7 +3578,7 @@ type ResetWorkflowExecutionResponse struct { func (x *ResetWorkflowExecutionResponse) Reset() { *x = ResetWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[43] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3489,7 +3590,7 @@ func (x *ResetWorkflowExecutionResponse) String() string { func (*ResetWorkflowExecutionResponse) ProtoMessage() {} func (x *ResetWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[43] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3502,7 +3603,7 @@ func (x *ResetWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*ResetWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{43} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{45} } func (x *ResetWorkflowExecutionResponse) GetRunId() string { @@ -3525,7 +3626,7 @@ type RequestCancelWorkflowExecutionRequest struct { func (x *RequestCancelWorkflowExecutionRequest) Reset() { *x = RequestCancelWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[44] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3537,7 +3638,7 @@ func (x *RequestCancelWorkflowExecutionRequest) String() string { func (*RequestCancelWorkflowExecutionRequest) ProtoMessage() {} func (x *RequestCancelWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[44] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3550,7 +3651,7 @@ func (x *RequestCancelWorkflowExecutionRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use RequestCancelWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*RequestCancelWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{44} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{46} } func (x *RequestCancelWorkflowExecutionRequest) GetNamespaceId() string { @@ -3596,7 +3697,7 @@ type RequestCancelWorkflowExecutionResponse struct { func (x *RequestCancelWorkflowExecutionResponse) Reset() { *x = RequestCancelWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[45] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3608,7 +3709,7 @@ func (x *RequestCancelWorkflowExecutionResponse) String() string { func (*RequestCancelWorkflowExecutionResponse) ProtoMessage() {} func (x *RequestCancelWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[45] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3621,7 +3722,7 @@ func (x *RequestCancelWorkflowExecutionResponse) ProtoReflect() protoreflect.Mes // Deprecated: Use RequestCancelWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*RequestCancelWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{45} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{47} } type ScheduleWorkflowTaskRequest struct { @@ -3637,7 +3738,7 @@ type ScheduleWorkflowTaskRequest struct { func (x *ScheduleWorkflowTaskRequest) Reset() { *x = ScheduleWorkflowTaskRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[46] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3649,7 +3750,7 @@ func (x *ScheduleWorkflowTaskRequest) String() string { func (*ScheduleWorkflowTaskRequest) ProtoMessage() {} func (x *ScheduleWorkflowTaskRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[46] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3662,7 +3763,7 @@ func (x *ScheduleWorkflowTaskRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ScheduleWorkflowTaskRequest.ProtoReflect.Descriptor instead. func (*ScheduleWorkflowTaskRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{46} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{48} } func (x *ScheduleWorkflowTaskRequest) GetNamespaceId() string { @@ -3708,7 +3809,7 @@ type ScheduleWorkflowTaskResponse struct { func (x *ScheduleWorkflowTaskResponse) Reset() { *x = ScheduleWorkflowTaskResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[47] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3720,7 +3821,7 @@ func (x *ScheduleWorkflowTaskResponse) String() string { func (*ScheduleWorkflowTaskResponse) ProtoMessage() {} func (x *ScheduleWorkflowTaskResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[47] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3733,7 +3834,7 @@ func (x *ScheduleWorkflowTaskResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ScheduleWorkflowTaskResponse.ProtoReflect.Descriptor instead. func (*ScheduleWorkflowTaskResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{47} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{49} } type VerifyFirstWorkflowTaskScheduledRequest struct { @@ -3748,7 +3849,7 @@ type VerifyFirstWorkflowTaskScheduledRequest struct { func (x *VerifyFirstWorkflowTaskScheduledRequest) Reset() { *x = VerifyFirstWorkflowTaskScheduledRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[48] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3760,7 +3861,7 @@ func (x *VerifyFirstWorkflowTaskScheduledRequest) String() string { func (*VerifyFirstWorkflowTaskScheduledRequest) ProtoMessage() {} func (x *VerifyFirstWorkflowTaskScheduledRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[48] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3773,7 +3874,7 @@ func (x *VerifyFirstWorkflowTaskScheduledRequest) ProtoReflect() protoreflect.Me // Deprecated: Use VerifyFirstWorkflowTaskScheduledRequest.ProtoReflect.Descriptor instead. func (*VerifyFirstWorkflowTaskScheduledRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{48} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{50} } func (x *VerifyFirstWorkflowTaskScheduledRequest) GetNamespaceId() string { @@ -3812,7 +3913,7 @@ type VerifyFirstWorkflowTaskScheduledResponse struct { func (x *VerifyFirstWorkflowTaskScheduledResponse) Reset() { *x = VerifyFirstWorkflowTaskScheduledResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[49] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3824,7 +3925,7 @@ func (x *VerifyFirstWorkflowTaskScheduledResponse) String() string { func (*VerifyFirstWorkflowTaskScheduledResponse) ProtoMessage() {} func (x *VerifyFirstWorkflowTaskScheduledResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[49] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3837,7 +3938,7 @@ func (x *VerifyFirstWorkflowTaskScheduledResponse) ProtoReflect() protoreflect.M // Deprecated: Use VerifyFirstWorkflowTaskScheduledResponse.ProtoReflect.Descriptor instead. func (*VerifyFirstWorkflowTaskScheduledResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{49} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{51} } // * @@ -3862,7 +3963,7 @@ type RecordChildExecutionCompletedRequest struct { func (x *RecordChildExecutionCompletedRequest) Reset() { *x = RecordChildExecutionCompletedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[50] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3874,7 +3975,7 @@ func (x *RecordChildExecutionCompletedRequest) String() string { func (*RecordChildExecutionCompletedRequest) ProtoMessage() {} func (x *RecordChildExecutionCompletedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[50] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3887,7 +3988,7 @@ func (x *RecordChildExecutionCompletedRequest) ProtoReflect() protoreflect.Messa // Deprecated: Use RecordChildExecutionCompletedRequest.ProtoReflect.Descriptor instead. func (*RecordChildExecutionCompletedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{50} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{52} } func (x *RecordChildExecutionCompletedRequest) GetNamespaceId() string { @@ -3954,7 +4055,7 @@ type RecordChildExecutionCompletedResponse struct { func (x *RecordChildExecutionCompletedResponse) Reset() { *x = RecordChildExecutionCompletedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[51] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3966,7 +4067,7 @@ func (x *RecordChildExecutionCompletedResponse) String() string { func (*RecordChildExecutionCompletedResponse) ProtoMessage() {} func (x *RecordChildExecutionCompletedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[51] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3979,7 +4080,7 @@ func (x *RecordChildExecutionCompletedResponse) ProtoReflect() protoreflect.Mess // Deprecated: Use RecordChildExecutionCompletedResponse.ProtoReflect.Descriptor instead. func (*RecordChildExecutionCompletedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{51} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{53} } type VerifyChildExecutionCompletionRecordedRequest struct { @@ -3997,7 +4098,7 @@ type VerifyChildExecutionCompletionRecordedRequest struct { func (x *VerifyChildExecutionCompletionRecordedRequest) Reset() { *x = VerifyChildExecutionCompletionRecordedRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[52] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4009,7 +4110,7 @@ func (x *VerifyChildExecutionCompletionRecordedRequest) String() string { func (*VerifyChildExecutionCompletionRecordedRequest) ProtoMessage() {} func (x *VerifyChildExecutionCompletionRecordedRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[52] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4022,7 +4123,7 @@ func (x *VerifyChildExecutionCompletionRecordedRequest) ProtoReflect() protorefl // Deprecated: Use VerifyChildExecutionCompletionRecordedRequest.ProtoReflect.Descriptor instead. func (*VerifyChildExecutionCompletionRecordedRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{52} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{54} } func (x *VerifyChildExecutionCompletionRecordedRequest) GetNamespaceId() string { @@ -4082,7 +4183,7 @@ type VerifyChildExecutionCompletionRecordedResponse struct { func (x *VerifyChildExecutionCompletionRecordedResponse) Reset() { *x = VerifyChildExecutionCompletionRecordedResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[53] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4094,7 +4195,7 @@ func (x *VerifyChildExecutionCompletionRecordedResponse) String() string { func (*VerifyChildExecutionCompletionRecordedResponse) ProtoMessage() {} func (x *VerifyChildExecutionCompletionRecordedResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[53] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4107,7 +4208,7 @@ func (x *VerifyChildExecutionCompletionRecordedResponse) ProtoReflect() protoref // Deprecated: Use VerifyChildExecutionCompletionRecordedResponse.ProtoReflect.Descriptor instead. func (*VerifyChildExecutionCompletionRecordedResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{53} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{55} } type DescribeWorkflowExecutionRequest struct { @@ -4120,7 +4221,7 @@ type DescribeWorkflowExecutionRequest struct { func (x *DescribeWorkflowExecutionRequest) Reset() { *x = DescribeWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[54] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4132,7 +4233,7 @@ func (x *DescribeWorkflowExecutionRequest) String() string { func (*DescribeWorkflowExecutionRequest) ProtoMessage() {} func (x *DescribeWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[54] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4145,7 +4246,7 @@ func (x *DescribeWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DescribeWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*DescribeWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{54} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{56} } func (x *DescribeWorkflowExecutionRequest) GetNamespaceId() string { @@ -4178,7 +4279,7 @@ type DescribeWorkflowExecutionResponse struct { func (x *DescribeWorkflowExecutionResponse) Reset() { *x = DescribeWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[55] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4190,7 +4291,7 @@ func (x *DescribeWorkflowExecutionResponse) String() string { func (*DescribeWorkflowExecutionResponse) ProtoMessage() {} func (x *DescribeWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[55] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4203,7 +4304,7 @@ func (x *DescribeWorkflowExecutionResponse) ProtoReflect() protoreflect.Message // Deprecated: Use DescribeWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*DescribeWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{55} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{57} } func (x *DescribeWorkflowExecutionResponse) GetExecutionConfig() *v15.WorkflowExecutionConfig { @@ -4263,11 +4364,11 @@ func (x *DescribeWorkflowExecutionResponse) GetWorkflowExtendedInfo() *v15.Workf } type ReplicateEventsV2Request struct { - state protoimpl.MessageState `protogen:"open.v1"` - NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - WorkflowExecution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` - VersionHistoryItems []*v19.VersionHistoryItem `protobuf:"bytes,3,rep,name=version_history_items,json=versionHistoryItems,proto3" json:"version_history_items,omitempty"` - Events *v14.DataBlob `protobuf:"bytes,4,opt,name=events,proto3" json:"events,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + WorkflowExecution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` + VersionHistoryItems []*v110.VersionHistoryItem `protobuf:"bytes,3,rep,name=version_history_items,json=versionHistoryItems,proto3" json:"version_history_items,omitempty"` + Events *v14.DataBlob `protobuf:"bytes,4,opt,name=events,proto3" json:"events,omitempty"` // New run events does not need version history since there is no prior events. NewRunEvents *v14.DataBlob `protobuf:"bytes,5,opt,name=new_run_events,json=newRunEvents,proto3" json:"new_run_events,omitempty"` BaseExecutionInfo *v11.BaseExecutionInfo `protobuf:"bytes,6,opt,name=base_execution_info,json=baseExecutionInfo,proto3" json:"base_execution_info,omitempty"` @@ -4278,7 +4379,7 @@ type ReplicateEventsV2Request struct { func (x *ReplicateEventsV2Request) Reset() { *x = ReplicateEventsV2Request{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[56] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4290,7 +4391,7 @@ func (x *ReplicateEventsV2Request) String() string { func (*ReplicateEventsV2Request) ProtoMessage() {} func (x *ReplicateEventsV2Request) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[56] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4303,7 +4404,7 @@ func (x *ReplicateEventsV2Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ReplicateEventsV2Request.ProtoReflect.Descriptor instead. func (*ReplicateEventsV2Request) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{56} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{58} } func (x *ReplicateEventsV2Request) GetNamespaceId() string { @@ -4320,7 +4421,7 @@ func (x *ReplicateEventsV2Request) GetWorkflowExecution() *v14.WorkflowExecution return nil } -func (x *ReplicateEventsV2Request) GetVersionHistoryItems() []*v19.VersionHistoryItem { +func (x *ReplicateEventsV2Request) GetVersionHistoryItems() []*v110.VersionHistoryItem { if x != nil { return x.VersionHistoryItems } @@ -4363,7 +4464,7 @@ type ReplicateEventsV2Response struct { func (x *ReplicateEventsV2Response) Reset() { *x = ReplicateEventsV2Response{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[57] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4375,7 +4476,7 @@ func (x *ReplicateEventsV2Response) String() string { func (*ReplicateEventsV2Response) ProtoMessage() {} func (x *ReplicateEventsV2Response) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[57] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4388,12 +4489,12 @@ func (x *ReplicateEventsV2Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ReplicateEventsV2Response.ProtoReflect.Descriptor instead. func (*ReplicateEventsV2Response) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{57} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{59} } type ReplicateWorkflowStateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - WorkflowState *v110.WorkflowMutableState `protobuf:"bytes,1,opt,name=workflow_state,json=workflowState,proto3" json:"workflow_state,omitempty"` + WorkflowState *v111.WorkflowMutableState `protobuf:"bytes,1,opt,name=workflow_state,json=workflowState,proto3" json:"workflow_state,omitempty"` RemoteCluster string `protobuf:"bytes,2,opt,name=remote_cluster,json=remoteCluster,proto3" json:"remote_cluster,omitempty"` NamespaceId string `protobuf:"bytes,3,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` IsForceReplication bool `protobuf:"varint,4,opt,name=is_force_replication,json=isForceReplication,proto3" json:"is_force_replication,omitempty"` @@ -4404,7 +4505,7 @@ type ReplicateWorkflowStateRequest struct { func (x *ReplicateWorkflowStateRequest) Reset() { *x = ReplicateWorkflowStateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[58] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4416,7 +4517,7 @@ func (x *ReplicateWorkflowStateRequest) String() string { func (*ReplicateWorkflowStateRequest) ProtoMessage() {} func (x *ReplicateWorkflowStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[58] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4429,10 +4530,10 @@ func (x *ReplicateWorkflowStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReplicateWorkflowStateRequest.ProtoReflect.Descriptor instead. func (*ReplicateWorkflowStateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{58} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{60} } -func (x *ReplicateWorkflowStateRequest) GetWorkflowState() *v110.WorkflowMutableState { +func (x *ReplicateWorkflowStateRequest) GetWorkflowState() *v111.WorkflowMutableState { if x != nil { return x.WorkflowState } @@ -4475,7 +4576,7 @@ type ReplicateWorkflowStateResponse struct { func (x *ReplicateWorkflowStateResponse) Reset() { *x = ReplicateWorkflowStateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[59] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4487,7 +4588,7 @@ func (x *ReplicateWorkflowStateResponse) String() string { func (*ReplicateWorkflowStateResponse) ProtoMessage() {} func (x *ReplicateWorkflowStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[59] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4500,7 +4601,7 @@ func (x *ReplicateWorkflowStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReplicateWorkflowStateResponse.ProtoReflect.Descriptor instead. func (*ReplicateWorkflowStateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{59} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{61} } type SyncShardStatusRequest struct { @@ -4514,7 +4615,7 @@ type SyncShardStatusRequest struct { func (x *SyncShardStatusRequest) Reset() { *x = SyncShardStatusRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[60] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4526,7 +4627,7 @@ func (x *SyncShardStatusRequest) String() string { func (*SyncShardStatusRequest) ProtoMessage() {} func (x *SyncShardStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[60] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4539,7 +4640,7 @@ func (x *SyncShardStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncShardStatusRequest.ProtoReflect.Descriptor instead. func (*SyncShardStatusRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{60} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{62} } func (x *SyncShardStatusRequest) GetSourceCluster() string { @@ -4571,7 +4672,7 @@ type SyncShardStatusResponse struct { func (x *SyncShardStatusResponse) Reset() { *x = SyncShardStatusResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[61] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4583,7 +4684,7 @@ func (x *SyncShardStatusResponse) String() string { func (*SyncShardStatusResponse) ProtoMessage() {} func (x *SyncShardStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[61] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4596,7 +4697,7 @@ func (x *SyncShardStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncShardStatusResponse.ProtoReflect.Descriptor instead. func (*SyncShardStatusResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{61} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{63} } type SyncActivityRequest struct { @@ -4614,7 +4715,7 @@ type SyncActivityRequest struct { Attempt int32 `protobuf:"varint,11,opt,name=attempt,proto3" json:"attempt,omitempty"` LastFailure *v13.Failure `protobuf:"bytes,12,opt,name=last_failure,json=lastFailure,proto3" json:"last_failure,omitempty"` LastWorkerIdentity string `protobuf:"bytes,13,opt,name=last_worker_identity,json=lastWorkerIdentity,proto3" json:"last_worker_identity,omitempty"` - VersionHistory *v19.VersionHistory `protobuf:"bytes,14,opt,name=version_history,json=versionHistory,proto3" json:"version_history,omitempty"` + VersionHistory *v110.VersionHistory `protobuf:"bytes,14,opt,name=version_history,json=versionHistory,proto3" json:"version_history,omitempty"` BaseExecutionInfo *v11.BaseExecutionInfo `protobuf:"bytes,15,opt,name=base_execution_info,json=baseExecutionInfo,proto3" json:"base_execution_info,omitempty"` // build ID of the worker who received this activity last time LastStartedBuildId string `protobuf:"bytes,16,opt,name=last_started_build_id,json=lastStartedBuildId,proto3" json:"last_started_build_id,omitempty"` @@ -4640,7 +4741,7 @@ type SyncActivityRequest struct { func (x *SyncActivityRequest) Reset() { *x = SyncActivityRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[62] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4652,7 +4753,7 @@ func (x *SyncActivityRequest) String() string { func (*SyncActivityRequest) ProtoMessage() {} func (x *SyncActivityRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[62] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4665,7 +4766,7 @@ func (x *SyncActivityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncActivityRequest.ProtoReflect.Descriptor instead. func (*SyncActivityRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{62} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{64} } func (x *SyncActivityRequest) GetNamespaceId() string { @@ -4759,7 +4860,7 @@ func (x *SyncActivityRequest) GetLastWorkerIdentity() string { return "" } -func (x *SyncActivityRequest) GetVersionHistory() *v19.VersionHistory { +func (x *SyncActivityRequest) GetVersionHistory() *v110.VersionHistory { if x != nil { return x.VersionHistory } @@ -4862,7 +4963,7 @@ type SyncActivitiesRequest struct { func (x *SyncActivitiesRequest) Reset() { *x = SyncActivitiesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[63] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4874,7 +4975,7 @@ func (x *SyncActivitiesRequest) String() string { func (*SyncActivitiesRequest) ProtoMessage() {} func (x *SyncActivitiesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[63] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4887,7 +4988,7 @@ func (x *SyncActivitiesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncActivitiesRequest.ProtoReflect.Descriptor instead. func (*SyncActivitiesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{63} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{65} } func (x *SyncActivitiesRequest) GetNamespaceId() string { @@ -4930,7 +5031,7 @@ type ActivitySyncInfo struct { Attempt int32 `protobuf:"varint,8,opt,name=attempt,proto3" json:"attempt,omitempty"` LastFailure *v13.Failure `protobuf:"bytes,9,opt,name=last_failure,json=lastFailure,proto3" json:"last_failure,omitempty"` LastWorkerIdentity string `protobuf:"bytes,10,opt,name=last_worker_identity,json=lastWorkerIdentity,proto3" json:"last_worker_identity,omitempty"` - VersionHistory *v19.VersionHistory `protobuf:"bytes,11,opt,name=version_history,json=versionHistory,proto3" json:"version_history,omitempty"` + VersionHistory *v110.VersionHistory `protobuf:"bytes,11,opt,name=version_history,json=versionHistory,proto3" json:"version_history,omitempty"` // build ID of the worker who received this activity last time LastStartedBuildId string `protobuf:"bytes,12,opt,name=last_started_build_id,json=lastStartedBuildId,proto3" json:"last_started_build_id,omitempty"` // workflows redirect_counter value when this activity started last time @@ -4955,7 +5056,7 @@ type ActivitySyncInfo struct { func (x *ActivitySyncInfo) Reset() { *x = ActivitySyncInfo{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[64] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4967,7 +5068,7 @@ func (x *ActivitySyncInfo) String() string { func (*ActivitySyncInfo) ProtoMessage() {} func (x *ActivitySyncInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[64] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4980,7 +5081,7 @@ func (x *ActivitySyncInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ActivitySyncInfo.ProtoReflect.Descriptor instead. func (*ActivitySyncInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{64} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{66} } func (x *ActivitySyncInfo) GetVersion() int64 { @@ -5053,7 +5154,7 @@ func (x *ActivitySyncInfo) GetLastWorkerIdentity() string { return "" } -func (x *ActivitySyncInfo) GetVersionHistory() *v19.VersionHistory { +func (x *ActivitySyncInfo) GetVersionHistory() *v110.VersionHistory { if x != nil { return x.VersionHistory } @@ -5145,7 +5246,7 @@ type SyncActivityResponse struct { func (x *SyncActivityResponse) Reset() { *x = SyncActivityResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[65] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5157,7 +5258,7 @@ func (x *SyncActivityResponse) String() string { func (*SyncActivityResponse) ProtoMessage() {} func (x *SyncActivityResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[65] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5170,7 +5271,7 @@ func (x *SyncActivityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncActivityResponse.ProtoReflect.Descriptor instead. func (*SyncActivityResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{65} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{67} } type DescribeMutableStateRequest struct { @@ -5186,7 +5287,7 @@ type DescribeMutableStateRequest struct { func (x *DescribeMutableStateRequest) Reset() { *x = DescribeMutableStateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[66] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5198,7 +5299,7 @@ func (x *DescribeMutableStateRequest) String() string { func (*DescribeMutableStateRequest) ProtoMessage() {} func (x *DescribeMutableStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[66] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5211,7 +5312,7 @@ func (x *DescribeMutableStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DescribeMutableStateRequest.ProtoReflect.Descriptor instead. func (*DescribeMutableStateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{66} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{68} } func (x *DescribeMutableStateRequest) GetNamespaceId() string { @@ -5245,17 +5346,17 @@ func (x *DescribeMutableStateRequest) GetArchetypeId() uint32 { type DescribeMutableStateResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // CacheMutableState is only available when mutable state is in cache. - CacheMutableState *v110.WorkflowMutableState `protobuf:"bytes,1,opt,name=cache_mutable_state,json=cacheMutableState,proto3" json:"cache_mutable_state,omitempty"` + CacheMutableState *v111.WorkflowMutableState `protobuf:"bytes,1,opt,name=cache_mutable_state,json=cacheMutableState,proto3" json:"cache_mutable_state,omitempty"` // DatabaseMutableState is always available, // but only loaded from database when mutable state is NOT in cache or skip_force_reload is false. - DatabaseMutableState *v110.WorkflowMutableState `protobuf:"bytes,2,opt,name=database_mutable_state,json=databaseMutableState,proto3" json:"database_mutable_state,omitempty"` + DatabaseMutableState *v111.WorkflowMutableState `protobuf:"bytes,2,opt,name=database_mutable_state,json=databaseMutableState,proto3" json:"database_mutable_state,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DescribeMutableStateResponse) Reset() { *x = DescribeMutableStateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[67] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5267,7 +5368,7 @@ func (x *DescribeMutableStateResponse) String() string { func (*DescribeMutableStateResponse) ProtoMessage() {} func (x *DescribeMutableStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[67] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5280,17 +5381,17 @@ func (x *DescribeMutableStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DescribeMutableStateResponse.ProtoReflect.Descriptor instead. func (*DescribeMutableStateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{67} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{69} } -func (x *DescribeMutableStateResponse) GetCacheMutableState() *v110.WorkflowMutableState { +func (x *DescribeMutableStateResponse) GetCacheMutableState() *v111.WorkflowMutableState { if x != nil { return x.CacheMutableState } return nil } -func (x *DescribeMutableStateResponse) GetDatabaseMutableState() *v110.WorkflowMutableState { +func (x *DescribeMutableStateResponse) GetDatabaseMutableState() *v111.WorkflowMutableState { if x != nil { return x.DatabaseMutableState } @@ -5311,7 +5412,7 @@ type DescribeHistoryHostRequest struct { func (x *DescribeHistoryHostRequest) Reset() { *x = DescribeHistoryHostRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[68] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5323,7 +5424,7 @@ func (x *DescribeHistoryHostRequest) String() string { func (*DescribeHistoryHostRequest) ProtoMessage() {} func (x *DescribeHistoryHostRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[68] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5336,7 +5437,7 @@ func (x *DescribeHistoryHostRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DescribeHistoryHostRequest.ProtoReflect.Descriptor instead. func (*DescribeHistoryHostRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{68} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{70} } func (x *DescribeHistoryHostRequest) GetHostAddress() string { @@ -5379,7 +5480,7 @@ type DescribeHistoryHostResponse struct { func (x *DescribeHistoryHostResponse) Reset() { *x = DescribeHistoryHostResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[69] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5391,7 +5492,7 @@ func (x *DescribeHistoryHostResponse) String() string { func (*DescribeHistoryHostResponse) ProtoMessage() {} func (x *DescribeHistoryHostResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[69] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5404,7 +5505,7 @@ func (x *DescribeHistoryHostResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DescribeHistoryHostResponse.ProtoReflect.Descriptor instead. func (*DescribeHistoryHostResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{69} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{71} } func (x *DescribeHistoryHostResponse) GetShardsNumber() int32 { @@ -5444,7 +5545,7 @@ type CloseShardRequest struct { func (x *CloseShardRequest) Reset() { *x = CloseShardRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[70] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5456,7 +5557,7 @@ func (x *CloseShardRequest) String() string { func (*CloseShardRequest) ProtoMessage() {} func (x *CloseShardRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[70] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5469,7 +5570,7 @@ func (x *CloseShardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseShardRequest.ProtoReflect.Descriptor instead. func (*CloseShardRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{70} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{72} } func (x *CloseShardRequest) GetShardId() int32 { @@ -5487,7 +5588,7 @@ type CloseShardResponse struct { func (x *CloseShardResponse) Reset() { *x = CloseShardResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[71] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5499,7 +5600,7 @@ func (x *CloseShardResponse) String() string { func (*CloseShardResponse) ProtoMessage() {} func (x *CloseShardResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[71] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5512,7 +5613,7 @@ func (x *CloseShardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseShardResponse.ProtoReflect.Descriptor instead. func (*CloseShardResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{71} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{73} } type GetShardRequest struct { @@ -5524,7 +5625,7 @@ type GetShardRequest struct { func (x *GetShardRequest) Reset() { *x = GetShardRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[72] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5536,7 +5637,7 @@ func (x *GetShardRequest) String() string { func (*GetShardRequest) ProtoMessage() {} func (x *GetShardRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[72] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5549,7 +5650,7 @@ func (x *GetShardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetShardRequest.ProtoReflect.Descriptor instead. func (*GetShardRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{72} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{74} } func (x *GetShardRequest) GetShardId() int32 { @@ -5561,14 +5662,14 @@ func (x *GetShardRequest) GetShardId() int32 { type GetShardResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - ShardInfo *v110.ShardInfo `protobuf:"bytes,1,opt,name=shard_info,json=shardInfo,proto3" json:"shard_info,omitempty"` + ShardInfo *v111.ShardInfo `protobuf:"bytes,1,opt,name=shard_info,json=shardInfo,proto3" json:"shard_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetShardResponse) Reset() { *x = GetShardResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[73] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5580,7 +5681,7 @@ func (x *GetShardResponse) String() string { func (*GetShardResponse) ProtoMessage() {} func (x *GetShardResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[73] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5593,10 +5694,10 @@ func (x *GetShardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetShardResponse.ProtoReflect.Descriptor instead. func (*GetShardResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{73} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{75} } -func (x *GetShardResponse) GetShardInfo() *v110.ShardInfo { +func (x *GetShardResponse) GetShardInfo() *v111.ShardInfo { if x != nil { return x.ShardInfo } @@ -5616,7 +5717,7 @@ type RemoveTaskRequest struct { func (x *RemoveTaskRequest) Reset() { *x = RemoveTaskRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[74] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5628,7 +5729,7 @@ func (x *RemoveTaskRequest) String() string { func (*RemoveTaskRequest) ProtoMessage() {} func (x *RemoveTaskRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[74] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5641,7 +5742,7 @@ func (x *RemoveTaskRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveTaskRequest.ProtoReflect.Descriptor instead. func (*RemoveTaskRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{74} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{76} } func (x *RemoveTaskRequest) GetShardId() int32 { @@ -5680,7 +5781,7 @@ type RemoveTaskResponse struct { func (x *RemoveTaskResponse) Reset() { *x = RemoveTaskResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[75] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5692,7 +5793,7 @@ func (x *RemoveTaskResponse) String() string { func (*RemoveTaskResponse) ProtoMessage() {} func (x *RemoveTaskResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[75] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5705,7 +5806,7 @@ func (x *RemoveTaskResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveTaskResponse.ProtoReflect.Descriptor instead. func (*RemoveTaskResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{75} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{77} } type GetReplicationMessagesRequest struct { @@ -5718,7 +5819,7 @@ type GetReplicationMessagesRequest struct { func (x *GetReplicationMessagesRequest) Reset() { *x = GetReplicationMessagesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[76] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5730,7 +5831,7 @@ func (x *GetReplicationMessagesRequest) String() string { func (*GetReplicationMessagesRequest) ProtoMessage() {} func (x *GetReplicationMessagesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[76] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5743,7 +5844,7 @@ func (x *GetReplicationMessagesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReplicationMessagesRequest.ProtoReflect.Descriptor instead. func (*GetReplicationMessagesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{76} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{78} } func (x *GetReplicationMessagesRequest) GetTokens() []*v118.ReplicationToken { @@ -5769,7 +5870,7 @@ type GetReplicationMessagesResponse struct { func (x *GetReplicationMessagesResponse) Reset() { *x = GetReplicationMessagesResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[77] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5781,7 +5882,7 @@ func (x *GetReplicationMessagesResponse) String() string { func (*GetReplicationMessagesResponse) ProtoMessage() {} func (x *GetReplicationMessagesResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[77] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5794,7 +5895,7 @@ func (x *GetReplicationMessagesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReplicationMessagesResponse.ProtoReflect.Descriptor instead. func (*GetReplicationMessagesResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{77} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{79} } func (x *GetReplicationMessagesResponse) GetShardMessages() map[int32]*v118.ReplicationMessages { @@ -5813,7 +5914,7 @@ type GetDLQReplicationMessagesRequest struct { func (x *GetDLQReplicationMessagesRequest) Reset() { *x = GetDLQReplicationMessagesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[78] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5825,7 +5926,7 @@ func (x *GetDLQReplicationMessagesRequest) String() string { func (*GetDLQReplicationMessagesRequest) ProtoMessage() {} func (x *GetDLQReplicationMessagesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[78] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5838,7 +5939,7 @@ func (x *GetDLQReplicationMessagesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDLQReplicationMessagesRequest.ProtoReflect.Descriptor instead. func (*GetDLQReplicationMessagesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{78} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{80} } func (x *GetDLQReplicationMessagesRequest) GetTaskInfos() []*v118.ReplicationTaskInfo { @@ -5857,7 +5958,7 @@ type GetDLQReplicationMessagesResponse struct { func (x *GetDLQReplicationMessagesResponse) Reset() { *x = GetDLQReplicationMessagesResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[79] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5869,7 +5970,7 @@ func (x *GetDLQReplicationMessagesResponse) String() string { func (*GetDLQReplicationMessagesResponse) ProtoMessage() {} func (x *GetDLQReplicationMessagesResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[79] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5882,7 +5983,7 @@ func (x *GetDLQReplicationMessagesResponse) ProtoReflect() protoreflect.Message // Deprecated: Use GetDLQReplicationMessagesResponse.ProtoReflect.Descriptor instead. func (*GetDLQReplicationMessagesResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{79} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{81} } func (x *GetDLQReplicationMessagesResponse) GetReplicationTasks() []*v118.ReplicationTask { @@ -5902,7 +6003,7 @@ type QueryWorkflowRequest struct { func (x *QueryWorkflowRequest) Reset() { *x = QueryWorkflowRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[80] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5914,7 +6015,7 @@ func (x *QueryWorkflowRequest) String() string { func (*QueryWorkflowRequest) ProtoMessage() {} func (x *QueryWorkflowRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[80] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5927,7 +6028,7 @@ func (x *QueryWorkflowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryWorkflowRequest.ProtoReflect.Descriptor instead. func (*QueryWorkflowRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{80} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{82} } func (x *QueryWorkflowRequest) GetNamespaceId() string { @@ -5953,7 +6054,7 @@ type QueryWorkflowResponse struct { func (x *QueryWorkflowResponse) Reset() { *x = QueryWorkflowResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[81] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5965,7 +6066,7 @@ func (x *QueryWorkflowResponse) String() string { func (*QueryWorkflowResponse) ProtoMessage() {} func (x *QueryWorkflowResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[81] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5978,7 +6079,7 @@ func (x *QueryWorkflowResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryWorkflowResponse.ProtoReflect.Descriptor instead. func (*QueryWorkflowResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{81} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{83} } func (x *QueryWorkflowResponse) GetResponse() *v1.QueryWorkflowResponse { @@ -5998,7 +6099,7 @@ type ReapplyEventsRequest struct { func (x *ReapplyEventsRequest) Reset() { *x = ReapplyEventsRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[82] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6010,7 +6111,7 @@ func (x *ReapplyEventsRequest) String() string { func (*ReapplyEventsRequest) ProtoMessage() {} func (x *ReapplyEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[82] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6023,7 +6124,7 @@ func (x *ReapplyEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReapplyEventsRequest.ProtoReflect.Descriptor instead. func (*ReapplyEventsRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{82} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{84} } func (x *ReapplyEventsRequest) GetNamespaceId() string { @@ -6048,7 +6149,7 @@ type ReapplyEventsResponse struct { func (x *ReapplyEventsResponse) Reset() { *x = ReapplyEventsResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[83] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6060,7 +6161,7 @@ func (x *ReapplyEventsResponse) String() string { func (*ReapplyEventsResponse) ProtoMessage() {} func (x *ReapplyEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[83] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6073,12 +6174,12 @@ func (x *ReapplyEventsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReapplyEventsResponse.ProtoReflect.Descriptor instead. func (*ReapplyEventsResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{83} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{85} } type GetDLQMessagesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Type v112.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` + Type v113.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` SourceCluster string `protobuf:"bytes,3,opt,name=source_cluster,json=sourceCluster,proto3" json:"source_cluster,omitempty"` InclusiveEndMessageId int64 `protobuf:"varint,4,opt,name=inclusive_end_message_id,json=inclusiveEndMessageId,proto3" json:"inclusive_end_message_id,omitempty"` @@ -6090,7 +6191,7 @@ type GetDLQMessagesRequest struct { func (x *GetDLQMessagesRequest) Reset() { *x = GetDLQMessagesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[84] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6102,7 +6203,7 @@ func (x *GetDLQMessagesRequest) String() string { func (*GetDLQMessagesRequest) ProtoMessage() {} func (x *GetDLQMessagesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[84] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6115,14 +6216,14 @@ func (x *GetDLQMessagesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDLQMessagesRequest.ProtoReflect.Descriptor instead. func (*GetDLQMessagesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{84} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{86} } -func (x *GetDLQMessagesRequest) GetType() v112.DeadLetterQueueType { +func (x *GetDLQMessagesRequest) GetType() v113.DeadLetterQueueType { if x != nil { return x.Type } - return v112.DeadLetterQueueType(0) + return v113.DeadLetterQueueType(0) } func (x *GetDLQMessagesRequest) GetShardId() int32 { @@ -6162,7 +6263,7 @@ func (x *GetDLQMessagesRequest) GetNextPageToken() []byte { type GetDLQMessagesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Type v112.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` + Type v113.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` ReplicationTasks []*v118.ReplicationTask `protobuf:"bytes,2,rep,name=replication_tasks,json=replicationTasks,proto3" json:"replication_tasks,omitempty"` NextPageToken []byte `protobuf:"bytes,3,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` ReplicationTasksInfo []*v118.ReplicationTaskInfo `protobuf:"bytes,4,rep,name=replication_tasks_info,json=replicationTasksInfo,proto3" json:"replication_tasks_info,omitempty"` @@ -6172,7 +6273,7 @@ type GetDLQMessagesResponse struct { func (x *GetDLQMessagesResponse) Reset() { *x = GetDLQMessagesResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[85] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6184,7 +6285,7 @@ func (x *GetDLQMessagesResponse) String() string { func (*GetDLQMessagesResponse) ProtoMessage() {} func (x *GetDLQMessagesResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[85] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6197,14 +6298,14 @@ func (x *GetDLQMessagesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDLQMessagesResponse.ProtoReflect.Descriptor instead. func (*GetDLQMessagesResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{85} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{87} } -func (x *GetDLQMessagesResponse) GetType() v112.DeadLetterQueueType { +func (x *GetDLQMessagesResponse) GetType() v113.DeadLetterQueueType { if x != nil { return x.Type } - return v112.DeadLetterQueueType(0) + return v113.DeadLetterQueueType(0) } func (x *GetDLQMessagesResponse) GetReplicationTasks() []*v118.ReplicationTask { @@ -6230,7 +6331,7 @@ func (x *GetDLQMessagesResponse) GetReplicationTasksInfo() []*v118.ReplicationTa type PurgeDLQMessagesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Type v112.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` + Type v113.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` SourceCluster string `protobuf:"bytes,3,opt,name=source_cluster,json=sourceCluster,proto3" json:"source_cluster,omitempty"` InclusiveEndMessageId int64 `protobuf:"varint,4,opt,name=inclusive_end_message_id,json=inclusiveEndMessageId,proto3" json:"inclusive_end_message_id,omitempty"` @@ -6240,7 +6341,7 @@ type PurgeDLQMessagesRequest struct { func (x *PurgeDLQMessagesRequest) Reset() { *x = PurgeDLQMessagesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[86] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6252,7 +6353,7 @@ func (x *PurgeDLQMessagesRequest) String() string { func (*PurgeDLQMessagesRequest) ProtoMessage() {} func (x *PurgeDLQMessagesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[86] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6265,14 +6366,14 @@ func (x *PurgeDLQMessagesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PurgeDLQMessagesRequest.ProtoReflect.Descriptor instead. func (*PurgeDLQMessagesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{86} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{88} } -func (x *PurgeDLQMessagesRequest) GetType() v112.DeadLetterQueueType { +func (x *PurgeDLQMessagesRequest) GetType() v113.DeadLetterQueueType { if x != nil { return x.Type } - return v112.DeadLetterQueueType(0) + return v113.DeadLetterQueueType(0) } func (x *PurgeDLQMessagesRequest) GetShardId() int32 { @@ -6304,7 +6405,7 @@ type PurgeDLQMessagesResponse struct { func (x *PurgeDLQMessagesResponse) Reset() { *x = PurgeDLQMessagesResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[87] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6316,7 +6417,7 @@ func (x *PurgeDLQMessagesResponse) String() string { func (*PurgeDLQMessagesResponse) ProtoMessage() {} func (x *PurgeDLQMessagesResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[87] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6329,12 +6430,12 @@ func (x *PurgeDLQMessagesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PurgeDLQMessagesResponse.ProtoReflect.Descriptor instead. func (*PurgeDLQMessagesResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{87} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{89} } type MergeDLQMessagesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Type v112.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` + Type v113.DeadLetterQueueType `protobuf:"varint,1,opt,name=type,proto3,enum=temporal.server.api.enums.v1.DeadLetterQueueType" json:"type,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` SourceCluster string `protobuf:"bytes,3,opt,name=source_cluster,json=sourceCluster,proto3" json:"source_cluster,omitempty"` InclusiveEndMessageId int64 `protobuf:"varint,4,opt,name=inclusive_end_message_id,json=inclusiveEndMessageId,proto3" json:"inclusive_end_message_id,omitempty"` @@ -6346,7 +6447,7 @@ type MergeDLQMessagesRequest struct { func (x *MergeDLQMessagesRequest) Reset() { *x = MergeDLQMessagesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[88] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6358,7 +6459,7 @@ func (x *MergeDLQMessagesRequest) String() string { func (*MergeDLQMessagesRequest) ProtoMessage() {} func (x *MergeDLQMessagesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[88] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6371,14 +6472,14 @@ func (x *MergeDLQMessagesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MergeDLQMessagesRequest.ProtoReflect.Descriptor instead. func (*MergeDLQMessagesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{88} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{90} } -func (x *MergeDLQMessagesRequest) GetType() v112.DeadLetterQueueType { +func (x *MergeDLQMessagesRequest) GetType() v113.DeadLetterQueueType { if x != nil { return x.Type } - return v112.DeadLetterQueueType(0) + return v113.DeadLetterQueueType(0) } func (x *MergeDLQMessagesRequest) GetShardId() int32 { @@ -6425,7 +6526,7 @@ type MergeDLQMessagesResponse struct { func (x *MergeDLQMessagesResponse) Reset() { *x = MergeDLQMessagesResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[89] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6437,7 +6538,7 @@ func (x *MergeDLQMessagesResponse) String() string { func (*MergeDLQMessagesResponse) ProtoMessage() {} func (x *MergeDLQMessagesResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[89] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6450,7 +6551,7 @@ func (x *MergeDLQMessagesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MergeDLQMessagesResponse.ProtoReflect.Descriptor instead. func (*MergeDLQMessagesResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{89} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{91} } func (x *MergeDLQMessagesResponse) GetNextPageToken() []byte { @@ -6472,7 +6573,7 @@ type RefreshWorkflowTasksRequest struct { func (x *RefreshWorkflowTasksRequest) Reset() { *x = RefreshWorkflowTasksRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[90] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6484,7 +6585,7 @@ func (x *RefreshWorkflowTasksRequest) String() string { func (*RefreshWorkflowTasksRequest) ProtoMessage() {} func (x *RefreshWorkflowTasksRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[90] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[92] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6497,7 +6598,7 @@ func (x *RefreshWorkflowTasksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshWorkflowTasksRequest.ProtoReflect.Descriptor instead. func (*RefreshWorkflowTasksRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{90} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{92} } func (x *RefreshWorkflowTasksRequest) GetNamespaceId() string { @@ -6529,7 +6630,7 @@ type RefreshWorkflowTasksResponse struct { func (x *RefreshWorkflowTasksResponse) Reset() { *x = RefreshWorkflowTasksResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[91] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6541,7 +6642,7 @@ func (x *RefreshWorkflowTasksResponse) String() string { func (*RefreshWorkflowTasksResponse) ProtoMessage() {} func (x *RefreshWorkflowTasksResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[91] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[93] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6554,7 +6655,7 @@ func (x *RefreshWorkflowTasksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshWorkflowTasksResponse.ProtoReflect.Descriptor instead. func (*RefreshWorkflowTasksResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{91} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{93} } type GenerateLastHistoryReplicationTasksRequest struct { @@ -6570,7 +6671,7 @@ type GenerateLastHistoryReplicationTasksRequest struct { func (x *GenerateLastHistoryReplicationTasksRequest) Reset() { *x = GenerateLastHistoryReplicationTasksRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[92] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6582,7 +6683,7 @@ func (x *GenerateLastHistoryReplicationTasksRequest) String() string { func (*GenerateLastHistoryReplicationTasksRequest) ProtoMessage() {} func (x *GenerateLastHistoryReplicationTasksRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[92] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[94] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6595,7 +6696,7 @@ func (x *GenerateLastHistoryReplicationTasksRequest) ProtoReflect() protoreflect // Deprecated: Use GenerateLastHistoryReplicationTasksRequest.ProtoReflect.Descriptor instead. func (*GenerateLastHistoryReplicationTasksRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{92} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{94} } func (x *GenerateLastHistoryReplicationTasksRequest) GetNamespaceId() string { @@ -6636,7 +6737,7 @@ type GenerateLastHistoryReplicationTasksResponse struct { func (x *GenerateLastHistoryReplicationTasksResponse) Reset() { *x = GenerateLastHistoryReplicationTasksResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[93] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6648,7 +6749,7 @@ func (x *GenerateLastHistoryReplicationTasksResponse) String() string { func (*GenerateLastHistoryReplicationTasksResponse) ProtoMessage() {} func (x *GenerateLastHistoryReplicationTasksResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[93] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6661,7 +6762,7 @@ func (x *GenerateLastHistoryReplicationTasksResponse) ProtoReflect() protoreflec // Deprecated: Use GenerateLastHistoryReplicationTasksResponse.ProtoReflect.Descriptor instead. func (*GenerateLastHistoryReplicationTasksResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{93} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{95} } func (x *GenerateLastHistoryReplicationTasksResponse) GetStateTransitionCount() int64 { @@ -6688,7 +6789,7 @@ type GetReplicationStatusRequest struct { func (x *GetReplicationStatusRequest) Reset() { *x = GetReplicationStatusRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[94] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6700,7 +6801,7 @@ func (x *GetReplicationStatusRequest) String() string { func (*GetReplicationStatusRequest) ProtoMessage() {} func (x *GetReplicationStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[94] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6713,7 +6814,7 @@ func (x *GetReplicationStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReplicationStatusRequest.ProtoReflect.Descriptor instead. func (*GetReplicationStatusRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{94} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{96} } func (x *GetReplicationStatusRequest) GetRemoteClusters() []string { @@ -6732,7 +6833,7 @@ type GetReplicationStatusResponse struct { func (x *GetReplicationStatusResponse) Reset() { *x = GetReplicationStatusResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[95] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6744,7 +6845,7 @@ func (x *GetReplicationStatusResponse) String() string { func (*GetReplicationStatusResponse) ProtoMessage() {} func (x *GetReplicationStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[95] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6757,7 +6858,7 @@ func (x *GetReplicationStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReplicationStatusResponse.ProtoReflect.Descriptor instead. func (*GetReplicationStatusResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{95} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{97} } func (x *GetReplicationStatusResponse) GetShards() []*ShardReplicationStatus { @@ -6783,7 +6884,7 @@ type ShardReplicationStatus struct { func (x *ShardReplicationStatus) Reset() { *x = ShardReplicationStatus{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[96] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6795,7 +6896,7 @@ func (x *ShardReplicationStatus) String() string { func (*ShardReplicationStatus) ProtoMessage() {} func (x *ShardReplicationStatus) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[96] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6808,7 +6909,7 @@ func (x *ShardReplicationStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use ShardReplicationStatus.ProtoReflect.Descriptor instead. func (*ShardReplicationStatus) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{96} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{98} } func (x *ShardReplicationStatus) GetShardId() int32 { @@ -6863,7 +6964,7 @@ type HandoverNamespaceInfo struct { func (x *HandoverNamespaceInfo) Reset() { *x = HandoverNamespaceInfo{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[97] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6875,7 +6976,7 @@ func (x *HandoverNamespaceInfo) String() string { func (*HandoverNamespaceInfo) ProtoMessage() {} func (x *HandoverNamespaceInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[97] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6888,7 +6989,7 @@ func (x *HandoverNamespaceInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use HandoverNamespaceInfo.ProtoReflect.Descriptor instead. func (*HandoverNamespaceInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{97} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{99} } func (x *HandoverNamespaceInfo) GetHandoverReplicationTaskId() int64 { @@ -6910,7 +7011,7 @@ type ShardReplicationStatusPerCluster struct { func (x *ShardReplicationStatusPerCluster) Reset() { *x = ShardReplicationStatusPerCluster{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[98] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6922,7 +7023,7 @@ func (x *ShardReplicationStatusPerCluster) String() string { func (*ShardReplicationStatusPerCluster) ProtoMessage() {} func (x *ShardReplicationStatusPerCluster) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[98] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6935,7 +7036,7 @@ func (x *ShardReplicationStatusPerCluster) ProtoReflect() protoreflect.Message { // Deprecated: Use ShardReplicationStatusPerCluster.ProtoReflect.Descriptor instead. func (*ShardReplicationStatusPerCluster) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{98} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{100} } func (x *ShardReplicationStatusPerCluster) GetAckedTaskId() int64 { @@ -6962,7 +7063,7 @@ type RebuildMutableStateRequest struct { func (x *RebuildMutableStateRequest) Reset() { *x = RebuildMutableStateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[99] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6974,7 +7075,7 @@ func (x *RebuildMutableStateRequest) String() string { func (*RebuildMutableStateRequest) ProtoMessage() {} func (x *RebuildMutableStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[99] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6987,7 +7088,7 @@ func (x *RebuildMutableStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RebuildMutableStateRequest.ProtoReflect.Descriptor instead. func (*RebuildMutableStateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{99} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{101} } func (x *RebuildMutableStateRequest) GetNamespaceId() string { @@ -7012,7 +7113,7 @@ type RebuildMutableStateResponse struct { func (x *RebuildMutableStateResponse) Reset() { *x = RebuildMutableStateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[100] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7024,7 +7125,7 @@ func (x *RebuildMutableStateResponse) String() string { func (*RebuildMutableStateResponse) ProtoMessage() {} func (x *RebuildMutableStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[100] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7037,7 +7138,7 @@ func (x *RebuildMutableStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RebuildMutableStateResponse.ProtoReflect.Descriptor instead. func (*RebuildMutableStateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{100} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{102} } type ImportWorkflowExecutionRequest struct { @@ -7045,7 +7146,7 @@ type ImportWorkflowExecutionRequest struct { NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` Execution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=execution,proto3" json:"execution,omitempty"` HistoryBatches []*v14.DataBlob `protobuf:"bytes,3,rep,name=history_batches,json=historyBatches,proto3" json:"history_batches,omitempty"` - VersionHistory *v19.VersionHistory `protobuf:"bytes,4,opt,name=version_history,json=versionHistory,proto3" json:"version_history,omitempty"` + VersionHistory *v110.VersionHistory `protobuf:"bytes,4,opt,name=version_history,json=versionHistory,proto3" json:"version_history,omitempty"` Token []byte `protobuf:"bytes,5,opt,name=token,proto3" json:"token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -7053,7 +7154,7 @@ type ImportWorkflowExecutionRequest struct { func (x *ImportWorkflowExecutionRequest) Reset() { *x = ImportWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[101] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7065,7 +7166,7 @@ func (x *ImportWorkflowExecutionRequest) String() string { func (*ImportWorkflowExecutionRequest) ProtoMessage() {} func (x *ImportWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[101] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7078,7 +7179,7 @@ func (x *ImportWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ImportWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*ImportWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{101} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{103} } func (x *ImportWorkflowExecutionRequest) GetNamespaceId() string { @@ -7102,7 +7203,7 @@ func (x *ImportWorkflowExecutionRequest) GetHistoryBatches() []*v14.DataBlob { return nil } -func (x *ImportWorkflowExecutionRequest) GetVersionHistory() *v19.VersionHistory { +func (x *ImportWorkflowExecutionRequest) GetVersionHistory() *v110.VersionHistory { if x != nil { return x.VersionHistory } @@ -7126,7 +7227,7 @@ type ImportWorkflowExecutionResponse struct { func (x *ImportWorkflowExecutionResponse) Reset() { *x = ImportWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[102] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7138,7 +7239,7 @@ func (x *ImportWorkflowExecutionResponse) String() string { func (*ImportWorkflowExecutionResponse) ProtoMessage() {} func (x *ImportWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[102] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7151,7 +7252,7 @@ func (x *ImportWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ImportWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*ImportWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{102} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{104} } func (x *ImportWorkflowExecutionResponse) GetToken() []byte { @@ -7180,7 +7281,7 @@ type DeleteWorkflowVisibilityRecordRequest struct { func (x *DeleteWorkflowVisibilityRecordRequest) Reset() { *x = DeleteWorkflowVisibilityRecordRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[103] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7192,7 +7293,7 @@ func (x *DeleteWorkflowVisibilityRecordRequest) String() string { func (*DeleteWorkflowVisibilityRecordRequest) ProtoMessage() {} func (x *DeleteWorkflowVisibilityRecordRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[103] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7205,7 +7306,7 @@ func (x *DeleteWorkflowVisibilityRecordRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use DeleteWorkflowVisibilityRecordRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkflowVisibilityRecordRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{103} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{105} } func (x *DeleteWorkflowVisibilityRecordRequest) GetNamespaceId() string { @@ -7244,7 +7345,7 @@ type DeleteWorkflowVisibilityRecordResponse struct { func (x *DeleteWorkflowVisibilityRecordResponse) Reset() { *x = DeleteWorkflowVisibilityRecordResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[104] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7256,7 +7357,7 @@ func (x *DeleteWorkflowVisibilityRecordResponse) String() string { func (*DeleteWorkflowVisibilityRecordResponse) ProtoMessage() {} func (x *DeleteWorkflowVisibilityRecordResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[104] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7269,7 +7370,7 @@ func (x *DeleteWorkflowVisibilityRecordResponse) ProtoReflect() protoreflect.Mes // Deprecated: Use DeleteWorkflowVisibilityRecordResponse.ProtoReflect.Descriptor instead. func (*DeleteWorkflowVisibilityRecordResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{104} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{106} } // (-- api-linter: core::0134=disabled @@ -7285,7 +7386,7 @@ type UpdateWorkflowExecutionRequest struct { func (x *UpdateWorkflowExecutionRequest) Reset() { *x = UpdateWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[105] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7297,7 +7398,7 @@ func (x *UpdateWorkflowExecutionRequest) String() string { func (*UpdateWorkflowExecutionRequest) ProtoMessage() {} func (x *UpdateWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[105] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7310,7 +7411,7 @@ func (x *UpdateWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*UpdateWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{105} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{107} } func (x *UpdateWorkflowExecutionRequest) GetNamespaceId() string { @@ -7336,7 +7437,7 @@ type UpdateWorkflowExecutionResponse struct { func (x *UpdateWorkflowExecutionResponse) Reset() { *x = UpdateWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[106] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7348,7 +7449,7 @@ func (x *UpdateWorkflowExecutionResponse) String() string { func (*UpdateWorkflowExecutionResponse) ProtoMessage() {} func (x *UpdateWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[106] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7361,7 +7462,7 @@ func (x *UpdateWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*UpdateWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{106} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{108} } func (x *UpdateWorkflowExecutionResponse) GetResponse() *v1.UpdateWorkflowExecutionResponse { @@ -7383,7 +7484,7 @@ type StreamWorkflowReplicationMessagesRequest struct { func (x *StreamWorkflowReplicationMessagesRequest) Reset() { *x = StreamWorkflowReplicationMessagesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[107] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7395,7 +7496,7 @@ func (x *StreamWorkflowReplicationMessagesRequest) String() string { func (*StreamWorkflowReplicationMessagesRequest) ProtoMessage() {} func (x *StreamWorkflowReplicationMessagesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[107] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7408,7 +7509,7 @@ func (x *StreamWorkflowReplicationMessagesRequest) ProtoReflect() protoreflect.M // Deprecated: Use StreamWorkflowReplicationMessagesRequest.ProtoReflect.Descriptor instead. func (*StreamWorkflowReplicationMessagesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{107} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{109} } func (x *StreamWorkflowReplicationMessagesRequest) GetAttributes() isStreamWorkflowReplicationMessagesRequest_Attributes { @@ -7450,7 +7551,7 @@ type StreamWorkflowReplicationMessagesResponse struct { func (x *StreamWorkflowReplicationMessagesResponse) Reset() { *x = StreamWorkflowReplicationMessagesResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[108] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7462,7 +7563,7 @@ func (x *StreamWorkflowReplicationMessagesResponse) String() string { func (*StreamWorkflowReplicationMessagesResponse) ProtoMessage() {} func (x *StreamWorkflowReplicationMessagesResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[108] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7475,7 +7576,7 @@ func (x *StreamWorkflowReplicationMessagesResponse) ProtoReflect() protoreflect. // Deprecated: Use StreamWorkflowReplicationMessagesResponse.ProtoReflect.Descriptor instead. func (*StreamWorkflowReplicationMessagesResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{108} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{110} } func (x *StreamWorkflowReplicationMessagesResponse) GetAttributes() isStreamWorkflowReplicationMessagesResponse_Attributes { @@ -7515,7 +7616,7 @@ type PollWorkflowExecutionUpdateRequest struct { func (x *PollWorkflowExecutionUpdateRequest) Reset() { *x = PollWorkflowExecutionUpdateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[109] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7527,7 +7628,7 @@ func (x *PollWorkflowExecutionUpdateRequest) String() string { func (*PollWorkflowExecutionUpdateRequest) ProtoMessage() {} func (x *PollWorkflowExecutionUpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[109] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7540,7 +7641,7 @@ func (x *PollWorkflowExecutionUpdateRequest) ProtoReflect() protoreflect.Message // Deprecated: Use PollWorkflowExecutionUpdateRequest.ProtoReflect.Descriptor instead. func (*PollWorkflowExecutionUpdateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{109} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{111} } func (x *PollWorkflowExecutionUpdateRequest) GetNamespaceId() string { @@ -7566,7 +7667,7 @@ type PollWorkflowExecutionUpdateResponse struct { func (x *PollWorkflowExecutionUpdateResponse) Reset() { *x = PollWorkflowExecutionUpdateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[110] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7578,7 +7679,7 @@ func (x *PollWorkflowExecutionUpdateResponse) String() string { func (*PollWorkflowExecutionUpdateResponse) ProtoMessage() {} func (x *PollWorkflowExecutionUpdateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[110] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7591,7 +7692,7 @@ func (x *PollWorkflowExecutionUpdateResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use PollWorkflowExecutionUpdateResponse.ProtoReflect.Descriptor instead. func (*PollWorkflowExecutionUpdateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{110} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{112} } func (x *PollWorkflowExecutionUpdateResponse) GetResponse() *v1.PollWorkflowExecutionUpdateResponse { @@ -7611,7 +7712,7 @@ type GetWorkflowExecutionHistoryRequest struct { func (x *GetWorkflowExecutionHistoryRequest) Reset() { *x = GetWorkflowExecutionHistoryRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[111] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7623,7 +7724,7 @@ func (x *GetWorkflowExecutionHistoryRequest) String() string { func (*GetWorkflowExecutionHistoryRequest) ProtoMessage() {} func (x *GetWorkflowExecutionHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[111] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7636,7 +7737,7 @@ func (x *GetWorkflowExecutionHistoryRequest) ProtoReflect() protoreflect.Message // Deprecated: Use GetWorkflowExecutionHistoryRequest.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionHistoryRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{111} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{113} } func (x *GetWorkflowExecutionHistoryRequest) GetNamespaceId() string { @@ -7663,7 +7764,7 @@ type GetWorkflowExecutionHistoryResponse struct { func (x *GetWorkflowExecutionHistoryResponse) Reset() { *x = GetWorkflowExecutionHistoryResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[112] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7675,7 +7776,7 @@ func (x *GetWorkflowExecutionHistoryResponse) String() string { func (*GetWorkflowExecutionHistoryResponse) ProtoMessage() {} func (x *GetWorkflowExecutionHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[112] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7688,7 +7789,7 @@ func (x *GetWorkflowExecutionHistoryResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use GetWorkflowExecutionHistoryResponse.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionHistoryResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{112} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{114} } func (x *GetWorkflowExecutionHistoryResponse) GetResponse() *v1.GetWorkflowExecutionHistoryResponse { @@ -7716,7 +7817,7 @@ type GetWorkflowExecutionHistoryResponseWithRaw struct { func (x *GetWorkflowExecutionHistoryResponseWithRaw) Reset() { *x = GetWorkflowExecutionHistoryResponseWithRaw{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[113] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7728,7 +7829,7 @@ func (x *GetWorkflowExecutionHistoryResponseWithRaw) String() string { func (*GetWorkflowExecutionHistoryResponseWithRaw) ProtoMessage() {} func (x *GetWorkflowExecutionHistoryResponseWithRaw) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[113] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7741,7 +7842,7 @@ func (x *GetWorkflowExecutionHistoryResponseWithRaw) ProtoReflect() protoreflect // Deprecated: Use GetWorkflowExecutionHistoryResponseWithRaw.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionHistoryResponseWithRaw) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{113} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{115} } func (x *GetWorkflowExecutionHistoryResponseWithRaw) GetResponse() *v1.GetWorkflowExecutionHistoryResponse { @@ -7768,7 +7869,7 @@ type GetWorkflowExecutionHistoryReverseRequest struct { func (x *GetWorkflowExecutionHistoryReverseRequest) Reset() { *x = GetWorkflowExecutionHistoryReverseRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[114] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7780,7 +7881,7 @@ func (x *GetWorkflowExecutionHistoryReverseRequest) String() string { func (*GetWorkflowExecutionHistoryReverseRequest) ProtoMessage() {} func (x *GetWorkflowExecutionHistoryReverseRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[114] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7793,7 +7894,7 @@ func (x *GetWorkflowExecutionHistoryReverseRequest) ProtoReflect() protoreflect. // Deprecated: Use GetWorkflowExecutionHistoryReverseRequest.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionHistoryReverseRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{114} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{116} } func (x *GetWorkflowExecutionHistoryReverseRequest) GetNamespaceId() string { @@ -7819,7 +7920,7 @@ type GetWorkflowExecutionHistoryReverseResponse struct { func (x *GetWorkflowExecutionHistoryReverseResponse) Reset() { *x = GetWorkflowExecutionHistoryReverseResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[115] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7831,7 +7932,7 @@ func (x *GetWorkflowExecutionHistoryReverseResponse) String() string { func (*GetWorkflowExecutionHistoryReverseResponse) ProtoMessage() {} func (x *GetWorkflowExecutionHistoryReverseResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[115] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7844,7 +7945,7 @@ func (x *GetWorkflowExecutionHistoryReverseResponse) ProtoReflect() protoreflect // Deprecated: Use GetWorkflowExecutionHistoryReverseResponse.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionHistoryReverseResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{115} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{117} } func (x *GetWorkflowExecutionHistoryReverseResponse) GetResponse() *v1.GetWorkflowExecutionHistoryReverseResponse { @@ -7867,7 +7968,7 @@ type GetWorkflowExecutionRawHistoryV2Request struct { func (x *GetWorkflowExecutionRawHistoryV2Request) Reset() { *x = GetWorkflowExecutionRawHistoryV2Request{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[116] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7879,7 +7980,7 @@ func (x *GetWorkflowExecutionRawHistoryV2Request) String() string { func (*GetWorkflowExecutionRawHistoryV2Request) ProtoMessage() {} func (x *GetWorkflowExecutionRawHistoryV2Request) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[116] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7892,7 +7993,7 @@ func (x *GetWorkflowExecutionRawHistoryV2Request) ProtoReflect() protoreflect.Me // Deprecated: Use GetWorkflowExecutionRawHistoryV2Request.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionRawHistoryV2Request) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{116} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{118} } func (x *GetWorkflowExecutionRawHistoryV2Request) GetNamespaceId() string { @@ -7918,7 +8019,7 @@ type GetWorkflowExecutionRawHistoryV2Response struct { func (x *GetWorkflowExecutionRawHistoryV2Response) Reset() { *x = GetWorkflowExecutionRawHistoryV2Response{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[117] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7930,7 +8031,7 @@ func (x *GetWorkflowExecutionRawHistoryV2Response) String() string { func (*GetWorkflowExecutionRawHistoryV2Response) ProtoMessage() {} func (x *GetWorkflowExecutionRawHistoryV2Response) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[117] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7943,7 +8044,7 @@ func (x *GetWorkflowExecutionRawHistoryV2Response) ProtoReflect() protoreflect.M // Deprecated: Use GetWorkflowExecutionRawHistoryV2Response.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionRawHistoryV2Response) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{117} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{119} } func (x *GetWorkflowExecutionRawHistoryV2Response) GetResponse() *v119.GetWorkflowExecutionRawHistoryV2Response { @@ -7963,7 +8064,7 @@ type GetWorkflowExecutionRawHistoryRequest struct { func (x *GetWorkflowExecutionRawHistoryRequest) Reset() { *x = GetWorkflowExecutionRawHistoryRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[118] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7975,7 +8076,7 @@ func (x *GetWorkflowExecutionRawHistoryRequest) String() string { func (*GetWorkflowExecutionRawHistoryRequest) ProtoMessage() {} func (x *GetWorkflowExecutionRawHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[118] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7988,7 +8089,7 @@ func (x *GetWorkflowExecutionRawHistoryRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use GetWorkflowExecutionRawHistoryRequest.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionRawHistoryRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{118} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{120} } func (x *GetWorkflowExecutionRawHistoryRequest) GetNamespaceId() string { @@ -8014,7 +8115,7 @@ type GetWorkflowExecutionRawHistoryResponse struct { func (x *GetWorkflowExecutionRawHistoryResponse) Reset() { *x = GetWorkflowExecutionRawHistoryResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[119] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8026,7 +8127,7 @@ func (x *GetWorkflowExecutionRawHistoryResponse) String() string { func (*GetWorkflowExecutionRawHistoryResponse) ProtoMessage() {} func (x *GetWorkflowExecutionRawHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[119] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8039,7 +8140,7 @@ func (x *GetWorkflowExecutionRawHistoryResponse) ProtoReflect() protoreflect.Mes // Deprecated: Use GetWorkflowExecutionRawHistoryResponse.ProtoReflect.Descriptor instead. func (*GetWorkflowExecutionRawHistoryResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{119} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{121} } func (x *GetWorkflowExecutionRawHistoryResponse) GetResponse() *v119.GetWorkflowExecutionRawHistoryResponse { @@ -8061,7 +8162,7 @@ type ForceDeleteWorkflowExecutionRequest struct { func (x *ForceDeleteWorkflowExecutionRequest) Reset() { *x = ForceDeleteWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[120] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8073,7 +8174,7 @@ func (x *ForceDeleteWorkflowExecutionRequest) String() string { func (*ForceDeleteWorkflowExecutionRequest) ProtoMessage() {} func (x *ForceDeleteWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[120] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8086,7 +8187,7 @@ func (x *ForceDeleteWorkflowExecutionRequest) ProtoReflect() protoreflect.Messag // Deprecated: Use ForceDeleteWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*ForceDeleteWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{120} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{122} } func (x *ForceDeleteWorkflowExecutionRequest) GetNamespaceId() string { @@ -8119,7 +8220,7 @@ type ForceDeleteWorkflowExecutionResponse struct { func (x *ForceDeleteWorkflowExecutionResponse) Reset() { *x = ForceDeleteWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[121] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8131,7 +8232,7 @@ func (x *ForceDeleteWorkflowExecutionResponse) String() string { func (*ForceDeleteWorkflowExecutionResponse) ProtoMessage() {} func (x *ForceDeleteWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[121] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8144,7 +8245,7 @@ func (x *ForceDeleteWorkflowExecutionResponse) ProtoReflect() protoreflect.Messa // Deprecated: Use ForceDeleteWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*ForceDeleteWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{121} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{123} } func (x *ForceDeleteWorkflowExecutionResponse) GetResponse() *v119.DeleteWorkflowExecutionResponse { @@ -8168,7 +8269,7 @@ type DeleteExecutionRequest struct { func (x *DeleteExecutionRequest) Reset() { *x = DeleteExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[122] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8180,7 +8281,7 @@ func (x *DeleteExecutionRequest) String() string { func (*DeleteExecutionRequest) ProtoMessage() {} func (x *DeleteExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[122] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8193,7 +8294,7 @@ func (x *DeleteExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteExecutionRequest.ProtoReflect.Descriptor instead. func (*DeleteExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{122} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{124} } func (x *DeleteExecutionRequest) GetNamespaceId() string { @@ -8239,7 +8340,7 @@ type DeleteExecutionResponse struct { func (x *DeleteExecutionResponse) Reset() { *x = DeleteExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[123] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8251,7 +8352,7 @@ func (x *DeleteExecutionResponse) String() string { func (*DeleteExecutionResponse) ProtoMessage() {} func (x *DeleteExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[123] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8264,7 +8365,7 @@ func (x *DeleteExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteExecutionResponse.ProtoReflect.Descriptor instead. func (*DeleteExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{123} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{125} } type GetDLQTasksRequest struct { @@ -8279,7 +8380,7 @@ type GetDLQTasksRequest struct { func (x *GetDLQTasksRequest) Reset() { *x = GetDLQTasksRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[124] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8291,7 +8392,7 @@ func (x *GetDLQTasksRequest) String() string { func (*GetDLQTasksRequest) ProtoMessage() {} func (x *GetDLQTasksRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[124] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8304,7 +8405,7 @@ func (x *GetDLQTasksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDLQTasksRequest.ProtoReflect.Descriptor instead. func (*GetDLQTasksRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{124} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{126} } func (x *GetDLQTasksRequest) GetDlqKey() *v120.HistoryDLQKey { @@ -8341,7 +8442,7 @@ type GetDLQTasksResponse struct { func (x *GetDLQTasksResponse) Reset() { *x = GetDLQTasksResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[125] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8353,7 +8454,7 @@ func (x *GetDLQTasksResponse) String() string { func (*GetDLQTasksResponse) ProtoMessage() {} func (x *GetDLQTasksResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[125] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8366,7 +8467,7 @@ func (x *GetDLQTasksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDLQTasksResponse.ProtoReflect.Descriptor instead. func (*GetDLQTasksResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{125} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{127} } func (x *GetDLQTasksResponse) GetDlqTasks() []*v120.HistoryDLQTask { @@ -8393,7 +8494,7 @@ type DeleteDLQTasksRequest struct { func (x *DeleteDLQTasksRequest) Reset() { *x = DeleteDLQTasksRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[126] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8405,7 +8506,7 @@ func (x *DeleteDLQTasksRequest) String() string { func (*DeleteDLQTasksRequest) ProtoMessage() {} func (x *DeleteDLQTasksRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[126] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8418,7 +8519,7 @@ func (x *DeleteDLQTasksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteDLQTasksRequest.ProtoReflect.Descriptor instead. func (*DeleteDLQTasksRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{126} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{128} } func (x *DeleteDLQTasksRequest) GetDlqKey() *v120.HistoryDLQKey { @@ -8445,7 +8546,7 @@ type DeleteDLQTasksResponse struct { func (x *DeleteDLQTasksResponse) Reset() { *x = DeleteDLQTasksResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[127] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8457,7 +8558,7 @@ func (x *DeleteDLQTasksResponse) String() string { func (*DeleteDLQTasksResponse) ProtoMessage() {} func (x *DeleteDLQTasksResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[127] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8470,7 +8571,7 @@ func (x *DeleteDLQTasksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteDLQTasksResponse.ProtoReflect.Descriptor instead. func (*DeleteDLQTasksResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{127} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{129} } func (x *DeleteDLQTasksResponse) GetMessagesDeleted() int64 { @@ -8491,7 +8592,7 @@ type ListQueuesRequest struct { func (x *ListQueuesRequest) Reset() { *x = ListQueuesRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[128] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8503,7 +8604,7 @@ func (x *ListQueuesRequest) String() string { func (*ListQueuesRequest) ProtoMessage() {} func (x *ListQueuesRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[128] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8516,7 +8617,7 @@ func (x *ListQueuesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListQueuesRequest.ProtoReflect.Descriptor instead. func (*ListQueuesRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{128} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{130} } func (x *ListQueuesRequest) GetQueueType() int32 { @@ -8550,7 +8651,7 @@ type ListQueuesResponse struct { func (x *ListQueuesResponse) Reset() { *x = ListQueuesResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[129] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8562,7 +8663,7 @@ func (x *ListQueuesResponse) String() string { func (*ListQueuesResponse) ProtoMessage() {} func (x *ListQueuesResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[129] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8575,7 +8676,7 @@ func (x *ListQueuesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListQueuesResponse.ProtoReflect.Descriptor instead. func (*ListQueuesResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{129} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{131} } func (x *ListQueuesResponse) GetQueues() []*ListQueuesResponse_QueueInfo { @@ -8606,7 +8707,7 @@ type AddTasksRequest struct { func (x *AddTasksRequest) Reset() { *x = AddTasksRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[130] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8618,7 +8719,7 @@ func (x *AddTasksRequest) String() string { func (*AddTasksRequest) ProtoMessage() {} func (x *AddTasksRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[130] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8631,7 +8732,7 @@ func (x *AddTasksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddTasksRequest.ProtoReflect.Descriptor instead. func (*AddTasksRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{130} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{132} } func (x *AddTasksRequest) GetShardId() int32 { @@ -8656,7 +8757,7 @@ type AddTasksResponse struct { func (x *AddTasksResponse) Reset() { *x = AddTasksResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[131] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8668,7 +8769,7 @@ func (x *AddTasksResponse) String() string { func (*AddTasksResponse) ProtoMessage() {} func (x *AddTasksResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[131] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8681,7 +8782,7 @@ func (x *AddTasksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddTasksResponse.ProtoReflect.Descriptor instead. func (*AddTasksResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{131} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{133} } type ListTasksRequest struct { @@ -8693,7 +8794,7 @@ type ListTasksRequest struct { func (x *ListTasksRequest) Reset() { *x = ListTasksRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[132] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8705,7 +8806,7 @@ func (x *ListTasksRequest) String() string { func (*ListTasksRequest) ProtoMessage() {} func (x *ListTasksRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[132] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8718,7 +8819,7 @@ func (x *ListTasksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTasksRequest.ProtoReflect.Descriptor instead. func (*ListTasksRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{132} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{134} } func (x *ListTasksRequest) GetRequest() *v119.ListHistoryTasksRequest { @@ -8737,7 +8838,7 @@ type ListTasksResponse struct { func (x *ListTasksResponse) Reset() { *x = ListTasksResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[133] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8749,7 +8850,7 @@ func (x *ListTasksResponse) String() string { func (*ListTasksResponse) ProtoMessage() {} func (x *ListTasksResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[133] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8762,7 +8863,7 @@ func (x *ListTasksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTasksResponse.ProtoReflect.Descriptor instead. func (*ListTasksResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{133} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{135} } func (x *ListTasksResponse) GetResponse() *v119.ListHistoryTasksResponse { @@ -8796,7 +8897,7 @@ type CompleteNexusOperationChasmRequest struct { func (x *CompleteNexusOperationChasmRequest) Reset() { *x = CompleteNexusOperationChasmRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[134] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8808,7 +8909,7 @@ func (x *CompleteNexusOperationChasmRequest) String() string { func (*CompleteNexusOperationChasmRequest) ProtoMessage() {} func (x *CompleteNexusOperationChasmRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[134] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8821,7 +8922,7 @@ func (x *CompleteNexusOperationChasmRequest) ProtoReflect() protoreflect.Message // Deprecated: Use CompleteNexusOperationChasmRequest.ProtoReflect.Descriptor instead. func (*CompleteNexusOperationChasmRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{134} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{136} } func (x *CompleteNexusOperationChasmRequest) GetCompletion() *v121.NexusOperationCompletion { @@ -8910,7 +9011,7 @@ type CompleteNexusOperationChasmResponse struct { func (x *CompleteNexusOperationChasmResponse) Reset() { *x = CompleteNexusOperationChasmResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[135] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8922,7 +9023,7 @@ func (x *CompleteNexusOperationChasmResponse) String() string { func (*CompleteNexusOperationChasmResponse) ProtoMessage() {} func (x *CompleteNexusOperationChasmResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[135] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8935,7 +9036,7 @@ func (x *CompleteNexusOperationChasmResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use CompleteNexusOperationChasmResponse.ProtoReflect.Descriptor instead. func (*CompleteNexusOperationChasmResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{135} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{137} } type CompleteNexusOperationRequest struct { @@ -8961,7 +9062,7 @@ type CompleteNexusOperationRequest struct { func (x *CompleteNexusOperationRequest) Reset() { *x = CompleteNexusOperationRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[136] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8973,7 +9074,7 @@ func (x *CompleteNexusOperationRequest) String() string { func (*CompleteNexusOperationRequest) ProtoMessage() {} func (x *CompleteNexusOperationRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[136] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8986,7 +9087,7 @@ func (x *CompleteNexusOperationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CompleteNexusOperationRequest.ProtoReflect.Descriptor instead. func (*CompleteNexusOperationRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{136} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{138} } func (x *CompleteNexusOperationRequest) GetCompletion() *v121.NexusOperationCompletion { @@ -9075,7 +9176,7 @@ type CompleteNexusOperationResponse struct { func (x *CompleteNexusOperationResponse) Reset() { *x = CompleteNexusOperationResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[137] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9087,7 +9188,7 @@ func (x *CompleteNexusOperationResponse) String() string { func (*CompleteNexusOperationResponse) ProtoMessage() {} func (x *CompleteNexusOperationResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[137] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9100,7 +9201,7 @@ func (x *CompleteNexusOperationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CompleteNexusOperationResponse.ProtoReflect.Descriptor instead. func (*CompleteNexusOperationResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{137} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{139} } type InvokeStateMachineMethodRequest struct { @@ -9115,7 +9216,7 @@ type InvokeStateMachineMethodRequest struct { RunId string `protobuf:"bytes,3,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` // Reference including the path to the backing Operation state machine and a version + transition count for // staleness checks. - Ref *v110.StateMachineRef `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"` + Ref *v111.StateMachineRef `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"` // The method name to invoke. Methods must be explicitly registered for the target state machine in the state // machine registry, and accept an argument type of HistoryEvent that is the completion event of the completed // workflow. @@ -9128,7 +9229,7 @@ type InvokeStateMachineMethodRequest struct { func (x *InvokeStateMachineMethodRequest) Reset() { *x = InvokeStateMachineMethodRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[138] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9140,7 +9241,7 @@ func (x *InvokeStateMachineMethodRequest) String() string { func (*InvokeStateMachineMethodRequest) ProtoMessage() {} func (x *InvokeStateMachineMethodRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[138] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9153,7 +9254,7 @@ func (x *InvokeStateMachineMethodRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use InvokeStateMachineMethodRequest.ProtoReflect.Descriptor instead. func (*InvokeStateMachineMethodRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{138} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{140} } func (x *InvokeStateMachineMethodRequest) GetNamespaceId() string { @@ -9177,7 +9278,7 @@ func (x *InvokeStateMachineMethodRequest) GetRunId() string { return "" } -func (x *InvokeStateMachineMethodRequest) GetRef() *v110.StateMachineRef { +func (x *InvokeStateMachineMethodRequest) GetRef() *v111.StateMachineRef { if x != nil { return x.Ref } @@ -9208,7 +9309,7 @@ type InvokeStateMachineMethodResponse struct { func (x *InvokeStateMachineMethodResponse) Reset() { *x = InvokeStateMachineMethodResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[139] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9220,7 +9321,7 @@ func (x *InvokeStateMachineMethodResponse) String() string { func (*InvokeStateMachineMethodResponse) ProtoMessage() {} func (x *InvokeStateMachineMethodResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[139] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[141] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9233,7 +9334,7 @@ func (x *InvokeStateMachineMethodResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use InvokeStateMachineMethodResponse.ProtoReflect.Descriptor instead. func (*InvokeStateMachineMethodResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{139} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{141} } func (x *InvokeStateMachineMethodResponse) GetOutput() []byte { @@ -9252,7 +9353,7 @@ type DeepHealthCheckRequest struct { func (x *DeepHealthCheckRequest) Reset() { *x = DeepHealthCheckRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[140] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9264,7 +9365,7 @@ func (x *DeepHealthCheckRequest) String() string { func (*DeepHealthCheckRequest) ProtoMessage() {} func (x *DeepHealthCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[140] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[142] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9277,7 +9378,7 @@ func (x *DeepHealthCheckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeepHealthCheckRequest.ProtoReflect.Descriptor instead. func (*DeepHealthCheckRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{140} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{142} } func (x *DeepHealthCheckRequest) GetHostAddress() string { @@ -9289,7 +9390,7 @@ func (x *DeepHealthCheckRequest) GetHostAddress() string { type DeepHealthCheckResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - State v112.HealthState `protobuf:"varint,1,opt,name=state,proto3,enum=temporal.server.api.enums.v1.HealthState" json:"state,omitempty"` + State v113.HealthState `protobuf:"varint,1,opt,name=state,proto3,enum=temporal.server.api.enums.v1.HealthState" json:"state,omitempty"` // Per-check diagnostic results. Populated for all checks regardless of state. Checks []*v123.HealthCheck `protobuf:"bytes,2,rep,name=checks,proto3" json:"checks,omitempty"` unknownFields protoimpl.UnknownFields @@ -9298,7 +9399,7 @@ type DeepHealthCheckResponse struct { func (x *DeepHealthCheckResponse) Reset() { *x = DeepHealthCheckResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[141] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9310,7 +9411,7 @@ func (x *DeepHealthCheckResponse) String() string { func (*DeepHealthCheckResponse) ProtoMessage() {} func (x *DeepHealthCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[141] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[143] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9323,14 +9424,14 @@ func (x *DeepHealthCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeepHealthCheckResponse.ProtoReflect.Descriptor instead. func (*DeepHealthCheckResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{141} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{143} } -func (x *DeepHealthCheckResponse) GetState() v112.HealthState { +func (x *DeepHealthCheckResponse) GetState() v113.HealthState { if x != nil { return x.State } - return v112.HealthState(0) + return v113.HealthState(0) } func (x *DeepHealthCheckResponse) GetChecks() []*v123.HealthCheck { @@ -9344,8 +9445,8 @@ type SyncWorkflowStateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` Execution *v14.WorkflowExecution `protobuf:"bytes,2,opt,name=execution,proto3" json:"execution,omitempty"` - VersionedTransition *v110.VersionedTransition `protobuf:"bytes,3,opt,name=versioned_transition,json=versionedTransition,proto3" json:"versioned_transition,omitempty"` - VersionHistories *v19.VersionHistories `protobuf:"bytes,4,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` + VersionedTransition *v111.VersionedTransition `protobuf:"bytes,3,opt,name=versioned_transition,json=versionedTransition,proto3" json:"versioned_transition,omitempty"` + VersionHistories *v110.VersionHistories `protobuf:"bytes,4,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` TargetClusterId int32 `protobuf:"varint,5,opt,name=target_cluster_id,json=targetClusterId,proto3" json:"target_cluster_id,omitempty"` // (-- api-linter: core::0141::forbidden-types=disabled --) ArchetypeId uint32 `protobuf:"varint,6,opt,name=archetype_id,json=archetypeId,proto3" json:"archetype_id,omitempty"` @@ -9355,7 +9456,7 @@ type SyncWorkflowStateRequest struct { func (x *SyncWorkflowStateRequest) Reset() { *x = SyncWorkflowStateRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[142] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9367,7 +9468,7 @@ func (x *SyncWorkflowStateRequest) String() string { func (*SyncWorkflowStateRequest) ProtoMessage() {} func (x *SyncWorkflowStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[142] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[144] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9380,7 +9481,7 @@ func (x *SyncWorkflowStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncWorkflowStateRequest.ProtoReflect.Descriptor instead. func (*SyncWorkflowStateRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{142} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{144} } func (x *SyncWorkflowStateRequest) GetNamespaceId() string { @@ -9397,14 +9498,14 @@ func (x *SyncWorkflowStateRequest) GetExecution() *v14.WorkflowExecution { return nil } -func (x *SyncWorkflowStateRequest) GetVersionedTransition() *v110.VersionedTransition { +func (x *SyncWorkflowStateRequest) GetVersionedTransition() *v111.VersionedTransition { if x != nil { return x.VersionedTransition } return nil } -func (x *SyncWorkflowStateRequest) GetVersionHistories() *v19.VersionHistories { +func (x *SyncWorkflowStateRequest) GetVersionHistories() *v110.VersionHistories { if x != nil { return x.VersionHistories } @@ -9434,7 +9535,7 @@ type SyncWorkflowStateResponse struct { func (x *SyncWorkflowStateResponse) Reset() { *x = SyncWorkflowStateResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[143] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9446,7 +9547,7 @@ func (x *SyncWorkflowStateResponse) String() string { func (*SyncWorkflowStateResponse) ProtoMessage() {} func (x *SyncWorkflowStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[143] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[145] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9459,7 +9560,7 @@ func (x *SyncWorkflowStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncWorkflowStateResponse.ProtoReflect.Descriptor instead. func (*SyncWorkflowStateResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{143} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{145} } func (x *SyncWorkflowStateResponse) GetVersionedTransitionArtifact() *v118.VersionedTransitionArtifact { @@ -9482,7 +9583,7 @@ type UpdateActivityOptionsRequest struct { func (x *UpdateActivityOptionsRequest) Reset() { *x = UpdateActivityOptionsRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[144] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9494,7 +9595,7 @@ func (x *UpdateActivityOptionsRequest) String() string { func (*UpdateActivityOptionsRequest) ProtoMessage() {} func (x *UpdateActivityOptionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[144] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[146] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9507,7 +9608,7 @@ func (x *UpdateActivityOptionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateActivityOptionsRequest.ProtoReflect.Descriptor instead. func (*UpdateActivityOptionsRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{144} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{146} } func (x *UpdateActivityOptionsRequest) GetNamespaceId() string { @@ -9534,7 +9635,7 @@ type UpdateActivityOptionsResponse struct { func (x *UpdateActivityOptionsResponse) Reset() { *x = UpdateActivityOptionsResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[145] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9546,7 +9647,7 @@ func (x *UpdateActivityOptionsResponse) String() string { func (*UpdateActivityOptionsResponse) ProtoMessage() {} func (x *UpdateActivityOptionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[145] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[147] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9559,7 +9660,7 @@ func (x *UpdateActivityOptionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateActivityOptionsResponse.ProtoReflect.Descriptor instead. func (*UpdateActivityOptionsResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{145} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{147} } func (x *UpdateActivityOptionsResponse) GetActivityOptions() *v124.ActivityOptions { @@ -9580,7 +9681,7 @@ type PauseActivityRequest struct { func (x *PauseActivityRequest) Reset() { *x = PauseActivityRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[146] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9592,7 +9693,7 @@ func (x *PauseActivityRequest) String() string { func (*PauseActivityRequest) ProtoMessage() {} func (x *PauseActivityRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[146] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[148] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9605,7 +9706,7 @@ func (x *PauseActivityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PauseActivityRequest.ProtoReflect.Descriptor instead. func (*PauseActivityRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{146} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{148} } func (x *PauseActivityRequest) GetNamespaceId() string { @@ -9630,7 +9731,7 @@ type PauseActivityResponse struct { func (x *PauseActivityResponse) Reset() { *x = PauseActivityResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[147] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9642,7 +9743,7 @@ func (x *PauseActivityResponse) String() string { func (*PauseActivityResponse) ProtoMessage() {} func (x *PauseActivityResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[147] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[149] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9655,7 +9756,7 @@ func (x *PauseActivityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PauseActivityResponse.ProtoReflect.Descriptor instead. func (*PauseActivityResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{147} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{149} } type UnpauseActivityRequest struct { @@ -9669,7 +9770,7 @@ type UnpauseActivityRequest struct { func (x *UnpauseActivityRequest) Reset() { *x = UnpauseActivityRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[148] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9681,7 +9782,7 @@ func (x *UnpauseActivityRequest) String() string { func (*UnpauseActivityRequest) ProtoMessage() {} func (x *UnpauseActivityRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[148] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[150] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9694,7 +9795,7 @@ func (x *UnpauseActivityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UnpauseActivityRequest.ProtoReflect.Descriptor instead. func (*UnpauseActivityRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{148} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{150} } func (x *UnpauseActivityRequest) GetNamespaceId() string { @@ -9719,7 +9820,7 @@ type UnpauseActivityResponse struct { func (x *UnpauseActivityResponse) Reset() { *x = UnpauseActivityResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[149] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9731,7 +9832,7 @@ func (x *UnpauseActivityResponse) String() string { func (*UnpauseActivityResponse) ProtoMessage() {} func (x *UnpauseActivityResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[149] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[151] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9744,7 +9845,7 @@ func (x *UnpauseActivityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UnpauseActivityResponse.ProtoReflect.Descriptor instead. func (*UnpauseActivityResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{149} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{151} } type ResetActivityRequest struct { @@ -9758,7 +9859,7 @@ type ResetActivityRequest struct { func (x *ResetActivityRequest) Reset() { *x = ResetActivityRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[150] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9770,7 +9871,7 @@ func (x *ResetActivityRequest) String() string { func (*ResetActivityRequest) ProtoMessage() {} func (x *ResetActivityRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[150] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9783,7 +9884,7 @@ func (x *ResetActivityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetActivityRequest.ProtoReflect.Descriptor instead. func (*ResetActivityRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{150} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{152} } func (x *ResetActivityRequest) GetNamespaceId() string { @@ -9808,7 +9909,7 @@ type ResetActivityResponse struct { func (x *ResetActivityResponse) Reset() { *x = ResetActivityResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[151] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9820,7 +9921,7 @@ func (x *ResetActivityResponse) String() string { func (*ResetActivityResponse) ProtoMessage() {} func (x *ResetActivityResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[151] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9833,7 +9934,7 @@ func (x *ResetActivityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetActivityResponse.ProtoReflect.Descriptor instead. func (*ResetActivityResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{151} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{153} } // (-- api-linter: core::0134::request-mask-required=disabled @@ -9848,7 +9949,7 @@ type UpdateWorkflowExecutionOptionsRequest struct { func (x *UpdateWorkflowExecutionOptionsRequest) Reset() { *x = UpdateWorkflowExecutionOptionsRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[152] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9860,7 +9961,7 @@ func (x *UpdateWorkflowExecutionOptionsRequest) String() string { func (*UpdateWorkflowExecutionOptionsRequest) ProtoMessage() {} func (x *UpdateWorkflowExecutionOptionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[152] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9873,7 +9974,7 @@ func (x *UpdateWorkflowExecutionOptionsRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use UpdateWorkflowExecutionOptionsRequest.ProtoReflect.Descriptor instead. func (*UpdateWorkflowExecutionOptionsRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{152} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{154} } func (x *UpdateWorkflowExecutionOptionsRequest) GetNamespaceId() string { @@ -9903,7 +10004,7 @@ type UpdateWorkflowExecutionOptionsResponse struct { func (x *UpdateWorkflowExecutionOptionsResponse) Reset() { *x = UpdateWorkflowExecutionOptionsResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[153] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9915,7 +10016,7 @@ func (x *UpdateWorkflowExecutionOptionsResponse) String() string { func (*UpdateWorkflowExecutionOptionsResponse) ProtoMessage() {} func (x *UpdateWorkflowExecutionOptionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[153] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9928,7 +10029,7 @@ func (x *UpdateWorkflowExecutionOptionsResponse) ProtoReflect() protoreflect.Mes // Deprecated: Use UpdateWorkflowExecutionOptionsResponse.ProtoReflect.Descriptor instead. func (*UpdateWorkflowExecutionOptionsResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{153} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{155} } func (x *UpdateWorkflowExecutionOptionsResponse) GetWorkflowExecutionOptions() *v15.WorkflowExecutionOptions { @@ -9956,7 +10057,7 @@ type PauseWorkflowExecutionRequest struct { func (x *PauseWorkflowExecutionRequest) Reset() { *x = PauseWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[154] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9968,7 +10069,7 @@ func (x *PauseWorkflowExecutionRequest) String() string { func (*PauseWorkflowExecutionRequest) ProtoMessage() {} func (x *PauseWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[154] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9981,7 +10082,7 @@ func (x *PauseWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PauseWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*PauseWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{154} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{156} } func (x *PauseWorkflowExecutionRequest) GetNamespaceId() string { @@ -10006,7 +10107,7 @@ type PauseWorkflowExecutionResponse struct { func (x *PauseWorkflowExecutionResponse) Reset() { *x = PauseWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[155] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10018,7 +10119,7 @@ func (x *PauseWorkflowExecutionResponse) String() string { func (*PauseWorkflowExecutionResponse) ProtoMessage() {} func (x *PauseWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[155] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10031,7 +10132,7 @@ func (x *PauseWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PauseWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*PauseWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{155} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{157} } type UnpauseWorkflowExecutionRequest struct { @@ -10045,7 +10146,7 @@ type UnpauseWorkflowExecutionRequest struct { func (x *UnpauseWorkflowExecutionRequest) Reset() { *x = UnpauseWorkflowExecutionRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[156] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10057,7 +10158,7 @@ func (x *UnpauseWorkflowExecutionRequest) String() string { func (*UnpauseWorkflowExecutionRequest) ProtoMessage() {} func (x *UnpauseWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[156] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10070,7 +10171,7 @@ func (x *UnpauseWorkflowExecutionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UnpauseWorkflowExecutionRequest.ProtoReflect.Descriptor instead. func (*UnpauseWorkflowExecutionRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{156} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{158} } func (x *UnpauseWorkflowExecutionRequest) GetNamespaceId() string { @@ -10095,7 +10196,7 @@ type UnpauseWorkflowExecutionResponse struct { func (x *UnpauseWorkflowExecutionResponse) Reset() { *x = UnpauseWorkflowExecutionResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[157] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10107,7 +10208,7 @@ func (x *UnpauseWorkflowExecutionResponse) String() string { func (*UnpauseWorkflowExecutionResponse) ProtoMessage() {} func (x *UnpauseWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[157] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10120,7 +10221,7 @@ func (x *UnpauseWorkflowExecutionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UnpauseWorkflowExecutionResponse.ProtoReflect.Descriptor instead. func (*UnpauseWorkflowExecutionResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{157} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{159} } type StartNexusOperationRequest struct { @@ -10134,7 +10235,7 @@ type StartNexusOperationRequest struct { func (x *StartNexusOperationRequest) Reset() { *x = StartNexusOperationRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[158] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10146,7 +10247,7 @@ func (x *StartNexusOperationRequest) String() string { func (*StartNexusOperationRequest) ProtoMessage() {} func (x *StartNexusOperationRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[158] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10159,7 +10260,7 @@ func (x *StartNexusOperationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StartNexusOperationRequest.ProtoReflect.Descriptor instead. func (*StartNexusOperationRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{158} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{160} } func (x *StartNexusOperationRequest) GetNamespaceId() string { @@ -10192,7 +10293,7 @@ type StartNexusOperationResponse struct { func (x *StartNexusOperationResponse) Reset() { *x = StartNexusOperationResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[159] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10204,7 +10305,7 @@ func (x *StartNexusOperationResponse) String() string { func (*StartNexusOperationResponse) ProtoMessage() {} func (x *StartNexusOperationResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[159] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10217,7 +10318,7 @@ func (x *StartNexusOperationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StartNexusOperationResponse.ProtoReflect.Descriptor instead. func (*StartNexusOperationResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{159} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{161} } func (x *StartNexusOperationResponse) GetResponse() *v122.StartOperationResponse { @@ -10238,7 +10339,7 @@ type CancelNexusOperationRequest struct { func (x *CancelNexusOperationRequest) Reset() { *x = CancelNexusOperationRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[160] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10250,7 +10351,7 @@ func (x *CancelNexusOperationRequest) String() string { func (*CancelNexusOperationRequest) ProtoMessage() {} func (x *CancelNexusOperationRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[160] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10263,7 +10364,7 @@ func (x *CancelNexusOperationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CancelNexusOperationRequest.ProtoReflect.Descriptor instead. func (*CancelNexusOperationRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{160} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{162} } func (x *CancelNexusOperationRequest) GetNamespaceId() string { @@ -10296,7 +10397,7 @@ type CancelNexusOperationResponse struct { func (x *CancelNexusOperationResponse) Reset() { *x = CancelNexusOperationResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[161] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10308,7 +10409,7 @@ func (x *CancelNexusOperationResponse) String() string { func (*CancelNexusOperationResponse) ProtoMessage() {} func (x *CancelNexusOperationResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[161] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[163] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10321,7 +10422,7 @@ func (x *CancelNexusOperationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CancelNexusOperationResponse.ProtoReflect.Descriptor instead. func (*CancelNexusOperationResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{161} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{163} } func (x *CancelNexusOperationResponse) GetResponse() *v122.CancelOperationResponse { @@ -10341,7 +10442,7 @@ type PollWorkflowExecutionTimeSkippingRequest struct { func (x *PollWorkflowExecutionTimeSkippingRequest) Reset() { *x = PollWorkflowExecutionTimeSkippingRequest{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[162] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10353,7 +10454,7 @@ func (x *PollWorkflowExecutionTimeSkippingRequest) String() string { func (*PollWorkflowExecutionTimeSkippingRequest) ProtoMessage() {} func (x *PollWorkflowExecutionTimeSkippingRequest) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[162] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[164] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10366,7 +10467,7 @@ func (x *PollWorkflowExecutionTimeSkippingRequest) ProtoReflect() protoreflect.M // Deprecated: Use PollWorkflowExecutionTimeSkippingRequest.ProtoReflect.Descriptor instead. func (*PollWorkflowExecutionTimeSkippingRequest) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{162} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{164} } func (x *PollWorkflowExecutionTimeSkippingRequest) GetNamespaceId() string { @@ -10392,7 +10493,7 @@ type PollWorkflowExecutionTimeSkippingResponse struct { func (x *PollWorkflowExecutionTimeSkippingResponse) Reset() { *x = PollWorkflowExecutionTimeSkippingResponse{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[163] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10404,7 +10505,7 @@ func (x *PollWorkflowExecutionTimeSkippingResponse) String() string { func (*PollWorkflowExecutionTimeSkippingResponse) ProtoMessage() {} func (x *PollWorkflowExecutionTimeSkippingResponse) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[163] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10417,7 +10518,7 @@ func (x *PollWorkflowExecutionTimeSkippingResponse) ProtoReflect() protoreflect. // Deprecated: Use PollWorkflowExecutionTimeSkippingResponse.ProtoReflect.Descriptor instead. func (*PollWorkflowExecutionTimeSkippingResponse) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{163} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{165} } func (x *PollWorkflowExecutionTimeSkippingResponse) GetResponse() *v1.PollWorkflowExecutionTimeSkippingResponse { @@ -10440,7 +10541,7 @@ type ExecuteMultiOperationRequest_Operation struct { func (x *ExecuteMultiOperationRequest_Operation) Reset() { *x = ExecuteMultiOperationRequest_Operation{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[164] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10452,7 +10553,7 @@ func (x *ExecuteMultiOperationRequest_Operation) String() string { func (*ExecuteMultiOperationRequest_Operation) ProtoMessage() {} func (x *ExecuteMultiOperationRequest_Operation) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[164] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10465,7 +10566,7 @@ func (x *ExecuteMultiOperationRequest_Operation) ProtoReflect() protoreflect.Mes // Deprecated: Use ExecuteMultiOperationRequest_Operation.ProtoReflect.Descriptor instead. func (*ExecuteMultiOperationRequest_Operation) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{9, 0} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{11, 0} } func (x *ExecuteMultiOperationRequest_Operation) GetOperation() isExecuteMultiOperationRequest_Operation_Operation { @@ -10524,7 +10625,7 @@ type ExecuteMultiOperationResponse_Response struct { func (x *ExecuteMultiOperationResponse_Response) Reset() { *x = ExecuteMultiOperationResponse_Response{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[165] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10536,7 +10637,7 @@ func (x *ExecuteMultiOperationResponse_Response) String() string { func (*ExecuteMultiOperationResponse_Response) ProtoMessage() {} func (x *ExecuteMultiOperationResponse_Response) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[165] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10549,7 +10650,7 @@ func (x *ExecuteMultiOperationResponse_Response) ProtoReflect() protoreflect.Mes // Deprecated: Use ExecuteMultiOperationResponse_Response.ProtoReflect.Descriptor instead. func (*ExecuteMultiOperationResponse_Response) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{10, 0} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{12, 0} } func (x *ExecuteMultiOperationResponse_Response) GetResponse() isExecuteMultiOperationResponse_Response_Response { @@ -10606,7 +10707,7 @@ type ListQueuesResponse_QueueInfo struct { func (x *ListQueuesResponse_QueueInfo) Reset() { *x = ListQueuesResponse_QueueInfo{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[171] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10618,7 +10719,7 @@ func (x *ListQueuesResponse_QueueInfo) String() string { func (*ListQueuesResponse_QueueInfo) ProtoMessage() {} func (x *ListQueuesResponse_QueueInfo) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[171] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10631,7 +10732,7 @@ func (x *ListQueuesResponse_QueueInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ListQueuesResponse_QueueInfo.ProtoReflect.Descriptor instead. func (*ListQueuesResponse_QueueInfo) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{129, 0} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{131, 0} } func (x *ListQueuesResponse_QueueInfo) GetQueueName() string { @@ -10669,7 +10770,7 @@ type AddTasksRequest_Task struct { func (x *AddTasksRequest_Task) Reset() { *x = AddTasksRequest_Task{} - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[172] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10681,7 +10782,7 @@ func (x *AddTasksRequest_Task) String() string { func (*AddTasksRequest_Task) ProtoMessage() {} func (x *AddTasksRequest_Task) ProtoReflect() protoreflect.Message { - mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[172] + mi := &file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10694,7 +10795,7 @@ func (x *AddTasksRequest_Task) ProtoReflect() protoreflect.Message { // Deprecated: Use AddTasksRequest_Task.ProtoReflect.Descriptor instead. func (*AddTasksRequest_Task) Descriptor() ([]byte, []int) { - return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{130, 0} + return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZIP(), []int{132, 0} } func (x *AddTasksRequest_Task) GetCategoryId() int32 { @@ -10772,7 +10873,12 @@ const file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc "\astarted\x18\x04 \x01(\bR\astarted\x12F\n" + "\x06status\x18\x05 \x01(\x0e2..temporal.api.enums.v1.WorkflowExecutionStatusR\x06status\x120\n" + "\x04link\x18\x06 \x01(\v2\x1c.temporal.api.common.v1.LinkR\x04link\x123\n" + - "\x16first_execution_run_id\x18\a \x01(\tR\x13firstExecutionRunId\"\xda\x03\n" + + "\x16first_execution_run_id\x18\a \x01(\tR\x13firstExecutionRunId\"\xa7\x01\n" + + "\x1cGetStreamReplaySlicesRequest\x12!\n" + + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12G\n" + + "\texecution\x18\x02 \x01(\v2).temporal.api.common.v1.WorkflowExecutionR\texecution:\x1b\x92\xc4\x03\x17*\x15execution.workflow_id\"i\n" + + "\x1dGetStreamReplaySlicesResponse\x12H\n" + + "\rstream_slices\x18\x01 \x03(\v2#.temporal.api.stream.v1.StreamSliceR\fstreamSlices\"\xda\x03\n" + "\x16GetMutableStateRequest\x12!\n" + "\fnamespace_id\x18\x01 \x01(\tR\vnamespaceId\x12G\n" + "\texecution\x18\x02 \x01(\v2).temporal.api.common.v1.WorkflowExecutionR\texecution\x123\n" + @@ -11523,578 +11629,582 @@ func file_temporal_server_api_historyservice_v1_request_response_proto_rawDescGZ return file_temporal_server_api_historyservice_v1_request_response_proto_rawDescData } -var file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes = make([]protoimpl.MessageInfo, 173) +var file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes = make([]protoimpl.MessageInfo, 175) var file_temporal_server_api_historyservice_v1_request_response_proto_goTypes = []any{ (*RoutingOptions)(nil), // 0: temporal.server.api.historyservice.v1.RoutingOptions (*StartWorkflowExecutionRequest)(nil), // 1: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest (*StartWorkflowExecutionResponse)(nil), // 2: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse - (*GetMutableStateRequest)(nil), // 3: temporal.server.api.historyservice.v1.GetMutableStateRequest - (*GetMutableStateResponse)(nil), // 4: temporal.server.api.historyservice.v1.GetMutableStateResponse - (*PollMutableStateRequest)(nil), // 5: temporal.server.api.historyservice.v1.PollMutableStateRequest - (*PollMutableStateResponse)(nil), // 6: temporal.server.api.historyservice.v1.PollMutableStateResponse - (*ResetStickyTaskQueueRequest)(nil), // 7: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest - (*ResetStickyTaskQueueResponse)(nil), // 8: temporal.server.api.historyservice.v1.ResetStickyTaskQueueResponse - (*ExecuteMultiOperationRequest)(nil), // 9: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest - (*ExecuteMultiOperationResponse)(nil), // 10: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse - (*RecordWorkflowTaskStartedRequest)(nil), // 11: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest - (*RecordWorkflowTaskStartedResponse)(nil), // 12: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse - (*RecordWorkflowTaskStartedResponseWithRawHistory)(nil), // 13: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory - (*RecordActivityTaskStartedRequest)(nil), // 14: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest - (*RecordActivityTaskStartedResponse)(nil), // 15: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse - (*RespondWorkflowTaskCompletedRequest)(nil), // 16: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest - (*RespondWorkflowTaskCompletedResponse)(nil), // 17: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse - (*RespondWorkflowTaskFailedRequest)(nil), // 18: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest - (*RespondWorkflowTaskFailedResponse)(nil), // 19: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedResponse - (*IsWorkflowTaskValidRequest)(nil), // 20: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest - (*IsWorkflowTaskValidResponse)(nil), // 21: temporal.server.api.historyservice.v1.IsWorkflowTaskValidResponse - (*RecordActivityTaskHeartbeatRequest)(nil), // 22: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest - (*RecordActivityTaskHeartbeatResponse)(nil), // 23: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatResponse - (*RespondActivityTaskCompletedRequest)(nil), // 24: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest - (*RespondActivityTaskCompletedResponse)(nil), // 25: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedResponse - (*RespondActivityTaskFailedRequest)(nil), // 26: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest - (*RespondActivityTaskFailedResponse)(nil), // 27: temporal.server.api.historyservice.v1.RespondActivityTaskFailedResponse - (*RespondActivityTaskCanceledRequest)(nil), // 28: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest - (*RespondActivityTaskCanceledResponse)(nil), // 29: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledResponse - (*IsActivityTaskValidRequest)(nil), // 30: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest - (*IsActivityTaskValidResponse)(nil), // 31: temporal.server.api.historyservice.v1.IsActivityTaskValidResponse - (*SignalWorkflowExecutionRequest)(nil), // 32: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest - (*SignalWorkflowExecutionResponse)(nil), // 33: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse - (*SignalWithStartWorkflowExecutionRequest)(nil), // 34: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest - (*SignalWithStartWorkflowExecutionResponse)(nil), // 35: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse - (*RemoveSignalMutableStateRequest)(nil), // 36: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest - (*RemoveSignalMutableStateResponse)(nil), // 37: temporal.server.api.historyservice.v1.RemoveSignalMutableStateResponse - (*TerminateWorkflowExecutionRequest)(nil), // 38: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest - (*TerminateWorkflowExecutionResponse)(nil), // 39: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionResponse - (*DeleteWorkflowExecutionRequest)(nil), // 40: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest - (*DeleteWorkflowExecutionResponse)(nil), // 41: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionResponse - (*ResetWorkflowExecutionRequest)(nil), // 42: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest - (*ResetWorkflowExecutionResponse)(nil), // 43: temporal.server.api.historyservice.v1.ResetWorkflowExecutionResponse - (*RequestCancelWorkflowExecutionRequest)(nil), // 44: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest - (*RequestCancelWorkflowExecutionResponse)(nil), // 45: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionResponse - (*ScheduleWorkflowTaskRequest)(nil), // 46: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest - (*ScheduleWorkflowTaskResponse)(nil), // 47: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskResponse - (*VerifyFirstWorkflowTaskScheduledRequest)(nil), // 48: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest - (*VerifyFirstWorkflowTaskScheduledResponse)(nil), // 49: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledResponse - (*RecordChildExecutionCompletedRequest)(nil), // 50: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest - (*RecordChildExecutionCompletedResponse)(nil), // 51: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedResponse - (*VerifyChildExecutionCompletionRecordedRequest)(nil), // 52: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest - (*VerifyChildExecutionCompletionRecordedResponse)(nil), // 53: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedResponse - (*DescribeWorkflowExecutionRequest)(nil), // 54: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest - (*DescribeWorkflowExecutionResponse)(nil), // 55: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse - (*ReplicateEventsV2Request)(nil), // 56: temporal.server.api.historyservice.v1.ReplicateEventsV2Request - (*ReplicateEventsV2Response)(nil), // 57: temporal.server.api.historyservice.v1.ReplicateEventsV2Response - (*ReplicateWorkflowStateRequest)(nil), // 58: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest - (*ReplicateWorkflowStateResponse)(nil), // 59: temporal.server.api.historyservice.v1.ReplicateWorkflowStateResponse - (*SyncShardStatusRequest)(nil), // 60: temporal.server.api.historyservice.v1.SyncShardStatusRequest - (*SyncShardStatusResponse)(nil), // 61: temporal.server.api.historyservice.v1.SyncShardStatusResponse - (*SyncActivityRequest)(nil), // 62: temporal.server.api.historyservice.v1.SyncActivityRequest - (*SyncActivitiesRequest)(nil), // 63: temporal.server.api.historyservice.v1.SyncActivitiesRequest - (*ActivitySyncInfo)(nil), // 64: temporal.server.api.historyservice.v1.ActivitySyncInfo - (*SyncActivityResponse)(nil), // 65: temporal.server.api.historyservice.v1.SyncActivityResponse - (*DescribeMutableStateRequest)(nil), // 66: temporal.server.api.historyservice.v1.DescribeMutableStateRequest - (*DescribeMutableStateResponse)(nil), // 67: temporal.server.api.historyservice.v1.DescribeMutableStateResponse - (*DescribeHistoryHostRequest)(nil), // 68: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest - (*DescribeHistoryHostResponse)(nil), // 69: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse - (*CloseShardRequest)(nil), // 70: temporal.server.api.historyservice.v1.CloseShardRequest - (*CloseShardResponse)(nil), // 71: temporal.server.api.historyservice.v1.CloseShardResponse - (*GetShardRequest)(nil), // 72: temporal.server.api.historyservice.v1.GetShardRequest - (*GetShardResponse)(nil), // 73: temporal.server.api.historyservice.v1.GetShardResponse - (*RemoveTaskRequest)(nil), // 74: temporal.server.api.historyservice.v1.RemoveTaskRequest - (*RemoveTaskResponse)(nil), // 75: temporal.server.api.historyservice.v1.RemoveTaskResponse - (*GetReplicationMessagesRequest)(nil), // 76: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest - (*GetReplicationMessagesResponse)(nil), // 77: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse - (*GetDLQReplicationMessagesRequest)(nil), // 78: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest - (*GetDLQReplicationMessagesResponse)(nil), // 79: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse - (*QueryWorkflowRequest)(nil), // 80: temporal.server.api.historyservice.v1.QueryWorkflowRequest - (*QueryWorkflowResponse)(nil), // 81: temporal.server.api.historyservice.v1.QueryWorkflowResponse - (*ReapplyEventsRequest)(nil), // 82: temporal.server.api.historyservice.v1.ReapplyEventsRequest - (*ReapplyEventsResponse)(nil), // 83: temporal.server.api.historyservice.v1.ReapplyEventsResponse - (*GetDLQMessagesRequest)(nil), // 84: temporal.server.api.historyservice.v1.GetDLQMessagesRequest - (*GetDLQMessagesResponse)(nil), // 85: temporal.server.api.historyservice.v1.GetDLQMessagesResponse - (*PurgeDLQMessagesRequest)(nil), // 86: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest - (*PurgeDLQMessagesResponse)(nil), // 87: temporal.server.api.historyservice.v1.PurgeDLQMessagesResponse - (*MergeDLQMessagesRequest)(nil), // 88: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest - (*MergeDLQMessagesResponse)(nil), // 89: temporal.server.api.historyservice.v1.MergeDLQMessagesResponse - (*RefreshWorkflowTasksRequest)(nil), // 90: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest - (*RefreshWorkflowTasksResponse)(nil), // 91: temporal.server.api.historyservice.v1.RefreshWorkflowTasksResponse - (*GenerateLastHistoryReplicationTasksRequest)(nil), // 92: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest - (*GenerateLastHistoryReplicationTasksResponse)(nil), // 93: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksResponse - (*GetReplicationStatusRequest)(nil), // 94: temporal.server.api.historyservice.v1.GetReplicationStatusRequest - (*GetReplicationStatusResponse)(nil), // 95: temporal.server.api.historyservice.v1.GetReplicationStatusResponse - (*ShardReplicationStatus)(nil), // 96: temporal.server.api.historyservice.v1.ShardReplicationStatus - (*HandoverNamespaceInfo)(nil), // 97: temporal.server.api.historyservice.v1.HandoverNamespaceInfo - (*ShardReplicationStatusPerCluster)(nil), // 98: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster - (*RebuildMutableStateRequest)(nil), // 99: temporal.server.api.historyservice.v1.RebuildMutableStateRequest - (*RebuildMutableStateResponse)(nil), // 100: temporal.server.api.historyservice.v1.RebuildMutableStateResponse - (*ImportWorkflowExecutionRequest)(nil), // 101: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest - (*ImportWorkflowExecutionResponse)(nil), // 102: temporal.server.api.historyservice.v1.ImportWorkflowExecutionResponse - (*DeleteWorkflowVisibilityRecordRequest)(nil), // 103: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest - (*DeleteWorkflowVisibilityRecordResponse)(nil), // 104: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordResponse - (*UpdateWorkflowExecutionRequest)(nil), // 105: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest - (*UpdateWorkflowExecutionResponse)(nil), // 106: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse - (*StreamWorkflowReplicationMessagesRequest)(nil), // 107: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest - (*StreamWorkflowReplicationMessagesResponse)(nil), // 108: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse - (*PollWorkflowExecutionUpdateRequest)(nil), // 109: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest - (*PollWorkflowExecutionUpdateResponse)(nil), // 110: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse - (*GetWorkflowExecutionHistoryRequest)(nil), // 111: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest - (*GetWorkflowExecutionHistoryResponse)(nil), // 112: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse - (*GetWorkflowExecutionHistoryResponseWithRaw)(nil), // 113: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw - (*GetWorkflowExecutionHistoryReverseRequest)(nil), // 114: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest - (*GetWorkflowExecutionHistoryReverseResponse)(nil), // 115: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse - (*GetWorkflowExecutionRawHistoryV2Request)(nil), // 116: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request - (*GetWorkflowExecutionRawHistoryV2Response)(nil), // 117: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response - (*GetWorkflowExecutionRawHistoryRequest)(nil), // 118: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest - (*GetWorkflowExecutionRawHistoryResponse)(nil), // 119: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse - (*ForceDeleteWorkflowExecutionRequest)(nil), // 120: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest - (*ForceDeleteWorkflowExecutionResponse)(nil), // 121: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse - (*DeleteExecutionRequest)(nil), // 122: temporal.server.api.historyservice.v1.DeleteExecutionRequest - (*DeleteExecutionResponse)(nil), // 123: temporal.server.api.historyservice.v1.DeleteExecutionResponse - (*GetDLQTasksRequest)(nil), // 124: temporal.server.api.historyservice.v1.GetDLQTasksRequest - (*GetDLQTasksResponse)(nil), // 125: temporal.server.api.historyservice.v1.GetDLQTasksResponse - (*DeleteDLQTasksRequest)(nil), // 126: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest - (*DeleteDLQTasksResponse)(nil), // 127: temporal.server.api.historyservice.v1.DeleteDLQTasksResponse - (*ListQueuesRequest)(nil), // 128: temporal.server.api.historyservice.v1.ListQueuesRequest - (*ListQueuesResponse)(nil), // 129: temporal.server.api.historyservice.v1.ListQueuesResponse - (*AddTasksRequest)(nil), // 130: temporal.server.api.historyservice.v1.AddTasksRequest - (*AddTasksResponse)(nil), // 131: temporal.server.api.historyservice.v1.AddTasksResponse - (*ListTasksRequest)(nil), // 132: temporal.server.api.historyservice.v1.ListTasksRequest - (*ListTasksResponse)(nil), // 133: temporal.server.api.historyservice.v1.ListTasksResponse - (*CompleteNexusOperationChasmRequest)(nil), // 134: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest - (*CompleteNexusOperationChasmResponse)(nil), // 135: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmResponse - (*CompleteNexusOperationRequest)(nil), // 136: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest - (*CompleteNexusOperationResponse)(nil), // 137: temporal.server.api.historyservice.v1.CompleteNexusOperationResponse - (*InvokeStateMachineMethodRequest)(nil), // 138: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest - (*InvokeStateMachineMethodResponse)(nil), // 139: temporal.server.api.historyservice.v1.InvokeStateMachineMethodResponse - (*DeepHealthCheckRequest)(nil), // 140: temporal.server.api.historyservice.v1.DeepHealthCheckRequest - (*DeepHealthCheckResponse)(nil), // 141: temporal.server.api.historyservice.v1.DeepHealthCheckResponse - (*SyncWorkflowStateRequest)(nil), // 142: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest - (*SyncWorkflowStateResponse)(nil), // 143: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse - (*UpdateActivityOptionsRequest)(nil), // 144: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest - (*UpdateActivityOptionsResponse)(nil), // 145: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse - (*PauseActivityRequest)(nil), // 146: temporal.server.api.historyservice.v1.PauseActivityRequest - (*PauseActivityResponse)(nil), // 147: temporal.server.api.historyservice.v1.PauseActivityResponse - (*UnpauseActivityRequest)(nil), // 148: temporal.server.api.historyservice.v1.UnpauseActivityRequest - (*UnpauseActivityResponse)(nil), // 149: temporal.server.api.historyservice.v1.UnpauseActivityResponse - (*ResetActivityRequest)(nil), // 150: temporal.server.api.historyservice.v1.ResetActivityRequest - (*ResetActivityResponse)(nil), // 151: temporal.server.api.historyservice.v1.ResetActivityResponse - (*UpdateWorkflowExecutionOptionsRequest)(nil), // 152: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest - (*UpdateWorkflowExecutionOptionsResponse)(nil), // 153: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse - (*PauseWorkflowExecutionRequest)(nil), // 154: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest - (*PauseWorkflowExecutionResponse)(nil), // 155: temporal.server.api.historyservice.v1.PauseWorkflowExecutionResponse - (*UnpauseWorkflowExecutionRequest)(nil), // 156: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest - (*UnpauseWorkflowExecutionResponse)(nil), // 157: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionResponse - (*StartNexusOperationRequest)(nil), // 158: temporal.server.api.historyservice.v1.StartNexusOperationRequest - (*StartNexusOperationResponse)(nil), // 159: temporal.server.api.historyservice.v1.StartNexusOperationResponse - (*CancelNexusOperationRequest)(nil), // 160: temporal.server.api.historyservice.v1.CancelNexusOperationRequest - (*CancelNexusOperationResponse)(nil), // 161: temporal.server.api.historyservice.v1.CancelNexusOperationResponse - (*PollWorkflowExecutionTimeSkippingRequest)(nil), // 162: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest - (*PollWorkflowExecutionTimeSkippingResponse)(nil), // 163: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse - (*ExecuteMultiOperationRequest_Operation)(nil), // 164: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation - (*ExecuteMultiOperationResponse_Response)(nil), // 165: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response - nil, // 166: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry - nil, // 167: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry - nil, // 168: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry - nil, // 169: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry - nil, // 170: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry - (*ListQueuesResponse_QueueInfo)(nil), // 171: temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo - (*AddTasksRequest_Task)(nil), // 172: temporal.server.api.historyservice.v1.AddTasksRequest.Task - (*v1.StartWorkflowExecutionRequest)(nil), // 173: temporal.api.workflowservice.v1.StartWorkflowExecutionRequest - (*v11.ParentExecutionInfo)(nil), // 174: temporal.server.api.workflow.v1.ParentExecutionInfo - (*timestamppb.Timestamp)(nil), // 175: google.protobuf.Timestamp - (v12.ContinueAsNewInitiator)(0), // 176: temporal.api.enums.v1.ContinueAsNewInitiator - (*v13.Failure)(nil), // 177: temporal.api.failure.v1.Failure - (*v14.Payloads)(nil), // 178: temporal.api.common.v1.Payloads - (*durationpb.Duration)(nil), // 179: google.protobuf.Duration - (*v14.WorkerVersionStamp)(nil), // 180: temporal.api.common.v1.WorkerVersionStamp - (*v11.RootExecutionInfo)(nil), // 181: temporal.server.api.workflow.v1.RootExecutionInfo - (*v15.VersioningOverride)(nil), // 182: temporal.api.workflow.v1.VersioningOverride - (*v16.WorkerDeploymentVersion)(nil), // 183: temporal.api.deployment.v1.WorkerDeploymentVersion - (*v16.InheritedAutoUpgradeInfo)(nil), // 184: temporal.api.deployment.v1.InheritedAutoUpgradeInfo - (*v17.DeclinedTargetVersionUpgrade)(nil), // 185: temporal.api.history.v1.DeclinedTargetVersionUpgrade - (*v14.TimeSkippingStatePropagation)(nil), // 186: temporal.api.common.v1.TimeSkippingStatePropagation - (*v18.VectorClock)(nil), // 187: temporal.server.api.clock.v1.VectorClock - (*v1.PollWorkflowTaskQueueResponse)(nil), // 188: temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse - (v12.WorkflowExecutionStatus)(0), // 189: temporal.api.enums.v1.WorkflowExecutionStatus - (*v14.Link)(nil), // 190: temporal.api.common.v1.Link - (*v14.WorkflowExecution)(nil), // 191: temporal.api.common.v1.WorkflowExecution - (*v19.VersionHistoryItem)(nil), // 192: temporal.server.api.history.v1.VersionHistoryItem - (*v110.VersionedTransition)(nil), // 193: temporal.server.api.persistence.v1.VersionedTransition - (*v14.WorkflowType)(nil), // 194: temporal.api.common.v1.WorkflowType - (*v111.TaskQueue)(nil), // 195: temporal.api.taskqueue.v1.TaskQueue - (v112.WorkflowExecutionState)(0), // 196: temporal.server.api.enums.v1.WorkflowExecutionState - (*v19.VersionHistories)(nil), // 197: temporal.server.api.history.v1.VersionHistories - (*v15.WorkflowExecutionVersioningInfo)(nil), // 198: temporal.api.workflow.v1.WorkflowExecutionVersioningInfo - (*v19.TransientWorkflowTaskInfo)(nil), // 199: temporal.server.api.history.v1.TransientWorkflowTaskInfo - (*v1.PollWorkflowTaskQueueRequest)(nil), // 200: temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest - (*v113.BuildIdRedirectInfo)(nil), // 201: temporal.server.api.taskqueue.v1.BuildIdRedirectInfo - (*v16.Deployment)(nil), // 202: temporal.api.deployment.v1.Deployment - (*v113.TaskVersionDirective)(nil), // 203: temporal.server.api.taskqueue.v1.TaskVersionDirective - (*v115.Message)(nil), // 204: temporal.api.protocol.v1.Message - (*v17.History)(nil), // 205: temporal.api.history.v1.History - (*v116.StreamSlice)(nil), // 206: temporal.api.stream.v1.StreamSlice - (*v1.PollActivityTaskQueueRequest)(nil), // 207: temporal.api.workflowservice.v1.PollActivityTaskQueueRequest - (*v17.HistoryEvent)(nil), // 208: temporal.api.history.v1.HistoryEvent - (*v14.Priority)(nil), // 209: temporal.api.common.v1.Priority - (*v14.RetryPolicy)(nil), // 210: temporal.api.common.v1.RetryPolicy - (*v1.RespondWorkflowTaskCompletedRequest)(nil), // 211: temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest - (*v1.PollActivityTaskQueueResponse)(nil), // 212: temporal.api.workflowservice.v1.PollActivityTaskQueueResponse - (*v1.RespondWorkflowTaskFailedRequest)(nil), // 213: temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest - (*v1.RecordActivityTaskHeartbeatRequest)(nil), // 214: temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest - (*v1.RespondActivityTaskCompletedRequest)(nil), // 215: temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest - (*v1.RespondActivityTaskFailedRequest)(nil), // 216: temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest - (*v1.RespondActivityTaskCanceledRequest)(nil), // 217: temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest - (*v1.SignalWorkflowExecutionRequest)(nil), // 218: temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest - (*v1.SignalWithStartWorkflowExecutionRequest)(nil), // 219: temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest - (*v1.TerminateWorkflowExecutionRequest)(nil), // 220: temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest - (*v1.ResetWorkflowExecutionRequest)(nil), // 221: temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest - (*v1.RequestCancelWorkflowExecutionRequest)(nil), // 222: temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest - (*v1.DescribeWorkflowExecutionRequest)(nil), // 223: temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest - (*v15.WorkflowExecutionConfig)(nil), // 224: temporal.api.workflow.v1.WorkflowExecutionConfig - (*v15.WorkflowExecutionInfo)(nil), // 225: temporal.api.workflow.v1.WorkflowExecutionInfo - (*v15.PendingActivityInfo)(nil), // 226: temporal.api.workflow.v1.PendingActivityInfo - (*v15.PendingChildExecutionInfo)(nil), // 227: temporal.api.workflow.v1.PendingChildExecutionInfo - (*v15.PendingWorkflowTaskInfo)(nil), // 228: temporal.api.workflow.v1.PendingWorkflowTaskInfo - (*v15.CallbackInfo)(nil), // 229: temporal.api.workflow.v1.CallbackInfo - (*v15.PendingNexusOperationInfo)(nil), // 230: temporal.api.workflow.v1.PendingNexusOperationInfo - (*v15.WorkflowExecutionExtendedInfo)(nil), // 231: temporal.api.workflow.v1.WorkflowExecutionExtendedInfo - (*v14.DataBlob)(nil), // 232: temporal.api.common.v1.DataBlob - (*v11.BaseExecutionInfo)(nil), // 233: temporal.server.api.workflow.v1.BaseExecutionInfo - (*v110.WorkflowMutableState)(nil), // 234: temporal.server.api.persistence.v1.WorkflowMutableState - (*v19.VersionHistory)(nil), // 235: temporal.server.api.history.v1.VersionHistory - (*v117.NamespaceCacheInfo)(nil), // 236: temporal.server.api.namespace.v1.NamespaceCacheInfo - (*v110.ShardInfo)(nil), // 237: temporal.server.api.persistence.v1.ShardInfo - (*v118.ReplicationToken)(nil), // 238: temporal.server.api.replication.v1.ReplicationToken - (*v118.ReplicationTaskInfo)(nil), // 239: temporal.server.api.replication.v1.ReplicationTaskInfo - (*v118.ReplicationTask)(nil), // 240: temporal.server.api.replication.v1.ReplicationTask - (*v1.QueryWorkflowRequest)(nil), // 241: temporal.api.workflowservice.v1.QueryWorkflowRequest - (*v1.QueryWorkflowResponse)(nil), // 242: temporal.api.workflowservice.v1.QueryWorkflowResponse - (*v119.ReapplyEventsRequest)(nil), // 243: temporal.server.api.adminservice.v1.ReapplyEventsRequest - (v112.DeadLetterQueueType)(0), // 244: temporal.server.api.enums.v1.DeadLetterQueueType - (*v119.RefreshWorkflowTasksRequest)(nil), // 245: temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest - (*v1.UpdateWorkflowExecutionRequest)(nil), // 246: temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest - (*v1.UpdateWorkflowExecutionResponse)(nil), // 247: temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse - (*v118.SyncReplicationState)(nil), // 248: temporal.server.api.replication.v1.SyncReplicationState - (*v118.WorkflowReplicationMessages)(nil), // 249: temporal.server.api.replication.v1.WorkflowReplicationMessages - (*v1.PollWorkflowExecutionUpdateRequest)(nil), // 250: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest - (*v1.PollWorkflowExecutionUpdateResponse)(nil), // 251: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse - (*v1.GetWorkflowExecutionHistoryRequest)(nil), // 252: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest - (*v1.GetWorkflowExecutionHistoryResponse)(nil), // 253: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - (*v1.GetWorkflowExecutionHistoryReverseRequest)(nil), // 254: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest - (*v1.GetWorkflowExecutionHistoryReverseResponse)(nil), // 255: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse - (*v119.GetWorkflowExecutionRawHistoryV2Request)(nil), // 256: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request - (*v119.GetWorkflowExecutionRawHistoryV2Response)(nil), // 257: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response - (*v119.GetWorkflowExecutionRawHistoryRequest)(nil), // 258: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest - (*v119.GetWorkflowExecutionRawHistoryResponse)(nil), // 259: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse - (*v119.DeleteWorkflowExecutionRequest)(nil), // 260: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest - (*v119.DeleteWorkflowExecutionResponse)(nil), // 261: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse - (*v120.HistoryDLQKey)(nil), // 262: temporal.server.api.common.v1.HistoryDLQKey - (*v120.HistoryDLQTask)(nil), // 263: temporal.server.api.common.v1.HistoryDLQTask - (*v120.HistoryDLQTaskMetadata)(nil), // 264: temporal.server.api.common.v1.HistoryDLQTaskMetadata - (*v119.ListHistoryTasksRequest)(nil), // 265: temporal.server.api.adminservice.v1.ListHistoryTasksRequest - (*v119.ListHistoryTasksResponse)(nil), // 266: temporal.server.api.adminservice.v1.ListHistoryTasksResponse - (*v121.NexusOperationCompletion)(nil), // 267: temporal.server.api.token.v1.NexusOperationCompletion - (*v14.Payload)(nil), // 268: temporal.api.common.v1.Payload - (*v122.Failure)(nil), // 269: temporal.api.nexus.v1.Failure - (*v110.StateMachineRef)(nil), // 270: temporal.server.api.persistence.v1.StateMachineRef - (v112.HealthState)(0), // 271: temporal.server.api.enums.v1.HealthState - (*v123.HealthCheck)(nil), // 272: temporal.server.api.health.v1.HealthCheck - (*v118.VersionedTransitionArtifact)(nil), // 273: temporal.server.api.replication.v1.VersionedTransitionArtifact - (*v1.UpdateActivityOptionsRequest)(nil), // 274: temporal.api.workflowservice.v1.UpdateActivityOptionsRequest - (*v124.ActivityOptions)(nil), // 275: temporal.api.activity.v1.ActivityOptions - (*v1.PauseActivityRequest)(nil), // 276: temporal.api.workflowservice.v1.PauseActivityRequest - (*v1.UnpauseActivityRequest)(nil), // 277: temporal.api.workflowservice.v1.UnpauseActivityRequest - (*v1.ResetActivityRequest)(nil), // 278: temporal.api.workflowservice.v1.ResetActivityRequest - (*v1.UpdateWorkflowExecutionOptionsRequest)(nil), // 279: temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest - (*v15.WorkflowExecutionOptions)(nil), // 280: temporal.api.workflow.v1.WorkflowExecutionOptions - (*v1.PauseWorkflowExecutionRequest)(nil), // 281: temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest - (*v1.UnpauseWorkflowExecutionRequest)(nil), // 282: temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest - (*v122.StartOperationRequest)(nil), // 283: temporal.api.nexus.v1.StartOperationRequest - (*v122.StartOperationResponse)(nil), // 284: temporal.api.nexus.v1.StartOperationResponse - (*v122.CancelOperationRequest)(nil), // 285: temporal.api.nexus.v1.CancelOperationRequest - (*v122.CancelOperationResponse)(nil), // 286: temporal.api.nexus.v1.CancelOperationResponse - (*v1.PollWorkflowExecutionTimeSkippingRequest)(nil), // 287: temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest - (*v1.PollWorkflowExecutionTimeSkippingResponse)(nil), // 288: temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse - (*v114.WorkflowQuery)(nil), // 289: temporal.api.query.v1.WorkflowQuery - (*v118.ReplicationMessages)(nil), // 290: temporal.server.api.replication.v1.ReplicationMessages - (*descriptorpb.MessageOptions)(nil), // 291: google.protobuf.MessageOptions + (*GetStreamReplaySlicesRequest)(nil), // 3: temporal.server.api.historyservice.v1.GetStreamReplaySlicesRequest + (*GetStreamReplaySlicesResponse)(nil), // 4: temporal.server.api.historyservice.v1.GetStreamReplaySlicesResponse + (*GetMutableStateRequest)(nil), // 5: temporal.server.api.historyservice.v1.GetMutableStateRequest + (*GetMutableStateResponse)(nil), // 6: temporal.server.api.historyservice.v1.GetMutableStateResponse + (*PollMutableStateRequest)(nil), // 7: temporal.server.api.historyservice.v1.PollMutableStateRequest + (*PollMutableStateResponse)(nil), // 8: temporal.server.api.historyservice.v1.PollMutableStateResponse + (*ResetStickyTaskQueueRequest)(nil), // 9: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest + (*ResetStickyTaskQueueResponse)(nil), // 10: temporal.server.api.historyservice.v1.ResetStickyTaskQueueResponse + (*ExecuteMultiOperationRequest)(nil), // 11: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest + (*ExecuteMultiOperationResponse)(nil), // 12: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse + (*RecordWorkflowTaskStartedRequest)(nil), // 13: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest + (*RecordWorkflowTaskStartedResponse)(nil), // 14: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse + (*RecordWorkflowTaskStartedResponseWithRawHistory)(nil), // 15: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory + (*RecordActivityTaskStartedRequest)(nil), // 16: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest + (*RecordActivityTaskStartedResponse)(nil), // 17: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse + (*RespondWorkflowTaskCompletedRequest)(nil), // 18: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest + (*RespondWorkflowTaskCompletedResponse)(nil), // 19: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse + (*RespondWorkflowTaskFailedRequest)(nil), // 20: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest + (*RespondWorkflowTaskFailedResponse)(nil), // 21: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedResponse + (*IsWorkflowTaskValidRequest)(nil), // 22: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest + (*IsWorkflowTaskValidResponse)(nil), // 23: temporal.server.api.historyservice.v1.IsWorkflowTaskValidResponse + (*RecordActivityTaskHeartbeatRequest)(nil), // 24: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest + (*RecordActivityTaskHeartbeatResponse)(nil), // 25: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatResponse + (*RespondActivityTaskCompletedRequest)(nil), // 26: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest + (*RespondActivityTaskCompletedResponse)(nil), // 27: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedResponse + (*RespondActivityTaskFailedRequest)(nil), // 28: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest + (*RespondActivityTaskFailedResponse)(nil), // 29: temporal.server.api.historyservice.v1.RespondActivityTaskFailedResponse + (*RespondActivityTaskCanceledRequest)(nil), // 30: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest + (*RespondActivityTaskCanceledResponse)(nil), // 31: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledResponse + (*IsActivityTaskValidRequest)(nil), // 32: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest + (*IsActivityTaskValidResponse)(nil), // 33: temporal.server.api.historyservice.v1.IsActivityTaskValidResponse + (*SignalWorkflowExecutionRequest)(nil), // 34: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest + (*SignalWorkflowExecutionResponse)(nil), // 35: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse + (*SignalWithStartWorkflowExecutionRequest)(nil), // 36: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest + (*SignalWithStartWorkflowExecutionResponse)(nil), // 37: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse + (*RemoveSignalMutableStateRequest)(nil), // 38: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest + (*RemoveSignalMutableStateResponse)(nil), // 39: temporal.server.api.historyservice.v1.RemoveSignalMutableStateResponse + (*TerminateWorkflowExecutionRequest)(nil), // 40: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest + (*TerminateWorkflowExecutionResponse)(nil), // 41: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionResponse + (*DeleteWorkflowExecutionRequest)(nil), // 42: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest + (*DeleteWorkflowExecutionResponse)(nil), // 43: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionResponse + (*ResetWorkflowExecutionRequest)(nil), // 44: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest + (*ResetWorkflowExecutionResponse)(nil), // 45: temporal.server.api.historyservice.v1.ResetWorkflowExecutionResponse + (*RequestCancelWorkflowExecutionRequest)(nil), // 46: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest + (*RequestCancelWorkflowExecutionResponse)(nil), // 47: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionResponse + (*ScheduleWorkflowTaskRequest)(nil), // 48: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest + (*ScheduleWorkflowTaskResponse)(nil), // 49: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskResponse + (*VerifyFirstWorkflowTaskScheduledRequest)(nil), // 50: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest + (*VerifyFirstWorkflowTaskScheduledResponse)(nil), // 51: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledResponse + (*RecordChildExecutionCompletedRequest)(nil), // 52: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest + (*RecordChildExecutionCompletedResponse)(nil), // 53: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedResponse + (*VerifyChildExecutionCompletionRecordedRequest)(nil), // 54: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest + (*VerifyChildExecutionCompletionRecordedResponse)(nil), // 55: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedResponse + (*DescribeWorkflowExecutionRequest)(nil), // 56: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest + (*DescribeWorkflowExecutionResponse)(nil), // 57: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse + (*ReplicateEventsV2Request)(nil), // 58: temporal.server.api.historyservice.v1.ReplicateEventsV2Request + (*ReplicateEventsV2Response)(nil), // 59: temporal.server.api.historyservice.v1.ReplicateEventsV2Response + (*ReplicateWorkflowStateRequest)(nil), // 60: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest + (*ReplicateWorkflowStateResponse)(nil), // 61: temporal.server.api.historyservice.v1.ReplicateWorkflowStateResponse + (*SyncShardStatusRequest)(nil), // 62: temporal.server.api.historyservice.v1.SyncShardStatusRequest + (*SyncShardStatusResponse)(nil), // 63: temporal.server.api.historyservice.v1.SyncShardStatusResponse + (*SyncActivityRequest)(nil), // 64: temporal.server.api.historyservice.v1.SyncActivityRequest + (*SyncActivitiesRequest)(nil), // 65: temporal.server.api.historyservice.v1.SyncActivitiesRequest + (*ActivitySyncInfo)(nil), // 66: temporal.server.api.historyservice.v1.ActivitySyncInfo + (*SyncActivityResponse)(nil), // 67: temporal.server.api.historyservice.v1.SyncActivityResponse + (*DescribeMutableStateRequest)(nil), // 68: temporal.server.api.historyservice.v1.DescribeMutableStateRequest + (*DescribeMutableStateResponse)(nil), // 69: temporal.server.api.historyservice.v1.DescribeMutableStateResponse + (*DescribeHistoryHostRequest)(nil), // 70: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest + (*DescribeHistoryHostResponse)(nil), // 71: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse + (*CloseShardRequest)(nil), // 72: temporal.server.api.historyservice.v1.CloseShardRequest + (*CloseShardResponse)(nil), // 73: temporal.server.api.historyservice.v1.CloseShardResponse + (*GetShardRequest)(nil), // 74: temporal.server.api.historyservice.v1.GetShardRequest + (*GetShardResponse)(nil), // 75: temporal.server.api.historyservice.v1.GetShardResponse + (*RemoveTaskRequest)(nil), // 76: temporal.server.api.historyservice.v1.RemoveTaskRequest + (*RemoveTaskResponse)(nil), // 77: temporal.server.api.historyservice.v1.RemoveTaskResponse + (*GetReplicationMessagesRequest)(nil), // 78: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest + (*GetReplicationMessagesResponse)(nil), // 79: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse + (*GetDLQReplicationMessagesRequest)(nil), // 80: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest + (*GetDLQReplicationMessagesResponse)(nil), // 81: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse + (*QueryWorkflowRequest)(nil), // 82: temporal.server.api.historyservice.v1.QueryWorkflowRequest + (*QueryWorkflowResponse)(nil), // 83: temporal.server.api.historyservice.v1.QueryWorkflowResponse + (*ReapplyEventsRequest)(nil), // 84: temporal.server.api.historyservice.v1.ReapplyEventsRequest + (*ReapplyEventsResponse)(nil), // 85: temporal.server.api.historyservice.v1.ReapplyEventsResponse + (*GetDLQMessagesRequest)(nil), // 86: temporal.server.api.historyservice.v1.GetDLQMessagesRequest + (*GetDLQMessagesResponse)(nil), // 87: temporal.server.api.historyservice.v1.GetDLQMessagesResponse + (*PurgeDLQMessagesRequest)(nil), // 88: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest + (*PurgeDLQMessagesResponse)(nil), // 89: temporal.server.api.historyservice.v1.PurgeDLQMessagesResponse + (*MergeDLQMessagesRequest)(nil), // 90: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest + (*MergeDLQMessagesResponse)(nil), // 91: temporal.server.api.historyservice.v1.MergeDLQMessagesResponse + (*RefreshWorkflowTasksRequest)(nil), // 92: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest + (*RefreshWorkflowTasksResponse)(nil), // 93: temporal.server.api.historyservice.v1.RefreshWorkflowTasksResponse + (*GenerateLastHistoryReplicationTasksRequest)(nil), // 94: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest + (*GenerateLastHistoryReplicationTasksResponse)(nil), // 95: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksResponse + (*GetReplicationStatusRequest)(nil), // 96: temporal.server.api.historyservice.v1.GetReplicationStatusRequest + (*GetReplicationStatusResponse)(nil), // 97: temporal.server.api.historyservice.v1.GetReplicationStatusResponse + (*ShardReplicationStatus)(nil), // 98: temporal.server.api.historyservice.v1.ShardReplicationStatus + (*HandoverNamespaceInfo)(nil), // 99: temporal.server.api.historyservice.v1.HandoverNamespaceInfo + (*ShardReplicationStatusPerCluster)(nil), // 100: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster + (*RebuildMutableStateRequest)(nil), // 101: temporal.server.api.historyservice.v1.RebuildMutableStateRequest + (*RebuildMutableStateResponse)(nil), // 102: temporal.server.api.historyservice.v1.RebuildMutableStateResponse + (*ImportWorkflowExecutionRequest)(nil), // 103: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest + (*ImportWorkflowExecutionResponse)(nil), // 104: temporal.server.api.historyservice.v1.ImportWorkflowExecutionResponse + (*DeleteWorkflowVisibilityRecordRequest)(nil), // 105: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest + (*DeleteWorkflowVisibilityRecordResponse)(nil), // 106: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordResponse + (*UpdateWorkflowExecutionRequest)(nil), // 107: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest + (*UpdateWorkflowExecutionResponse)(nil), // 108: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse + (*StreamWorkflowReplicationMessagesRequest)(nil), // 109: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest + (*StreamWorkflowReplicationMessagesResponse)(nil), // 110: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse + (*PollWorkflowExecutionUpdateRequest)(nil), // 111: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest + (*PollWorkflowExecutionUpdateResponse)(nil), // 112: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse + (*GetWorkflowExecutionHistoryRequest)(nil), // 113: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest + (*GetWorkflowExecutionHistoryResponse)(nil), // 114: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse + (*GetWorkflowExecutionHistoryResponseWithRaw)(nil), // 115: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw + (*GetWorkflowExecutionHistoryReverseRequest)(nil), // 116: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest + (*GetWorkflowExecutionHistoryReverseResponse)(nil), // 117: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse + (*GetWorkflowExecutionRawHistoryV2Request)(nil), // 118: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request + (*GetWorkflowExecutionRawHistoryV2Response)(nil), // 119: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response + (*GetWorkflowExecutionRawHistoryRequest)(nil), // 120: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest + (*GetWorkflowExecutionRawHistoryResponse)(nil), // 121: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse + (*ForceDeleteWorkflowExecutionRequest)(nil), // 122: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest + (*ForceDeleteWorkflowExecutionResponse)(nil), // 123: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse + (*DeleteExecutionRequest)(nil), // 124: temporal.server.api.historyservice.v1.DeleteExecutionRequest + (*DeleteExecutionResponse)(nil), // 125: temporal.server.api.historyservice.v1.DeleteExecutionResponse + (*GetDLQTasksRequest)(nil), // 126: temporal.server.api.historyservice.v1.GetDLQTasksRequest + (*GetDLQTasksResponse)(nil), // 127: temporal.server.api.historyservice.v1.GetDLQTasksResponse + (*DeleteDLQTasksRequest)(nil), // 128: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest + (*DeleteDLQTasksResponse)(nil), // 129: temporal.server.api.historyservice.v1.DeleteDLQTasksResponse + (*ListQueuesRequest)(nil), // 130: temporal.server.api.historyservice.v1.ListQueuesRequest + (*ListQueuesResponse)(nil), // 131: temporal.server.api.historyservice.v1.ListQueuesResponse + (*AddTasksRequest)(nil), // 132: temporal.server.api.historyservice.v1.AddTasksRequest + (*AddTasksResponse)(nil), // 133: temporal.server.api.historyservice.v1.AddTasksResponse + (*ListTasksRequest)(nil), // 134: temporal.server.api.historyservice.v1.ListTasksRequest + (*ListTasksResponse)(nil), // 135: temporal.server.api.historyservice.v1.ListTasksResponse + (*CompleteNexusOperationChasmRequest)(nil), // 136: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest + (*CompleteNexusOperationChasmResponse)(nil), // 137: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmResponse + (*CompleteNexusOperationRequest)(nil), // 138: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest + (*CompleteNexusOperationResponse)(nil), // 139: temporal.server.api.historyservice.v1.CompleteNexusOperationResponse + (*InvokeStateMachineMethodRequest)(nil), // 140: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest + (*InvokeStateMachineMethodResponse)(nil), // 141: temporal.server.api.historyservice.v1.InvokeStateMachineMethodResponse + (*DeepHealthCheckRequest)(nil), // 142: temporal.server.api.historyservice.v1.DeepHealthCheckRequest + (*DeepHealthCheckResponse)(nil), // 143: temporal.server.api.historyservice.v1.DeepHealthCheckResponse + (*SyncWorkflowStateRequest)(nil), // 144: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest + (*SyncWorkflowStateResponse)(nil), // 145: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse + (*UpdateActivityOptionsRequest)(nil), // 146: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest + (*UpdateActivityOptionsResponse)(nil), // 147: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse + (*PauseActivityRequest)(nil), // 148: temporal.server.api.historyservice.v1.PauseActivityRequest + (*PauseActivityResponse)(nil), // 149: temporal.server.api.historyservice.v1.PauseActivityResponse + (*UnpauseActivityRequest)(nil), // 150: temporal.server.api.historyservice.v1.UnpauseActivityRequest + (*UnpauseActivityResponse)(nil), // 151: temporal.server.api.historyservice.v1.UnpauseActivityResponse + (*ResetActivityRequest)(nil), // 152: temporal.server.api.historyservice.v1.ResetActivityRequest + (*ResetActivityResponse)(nil), // 153: temporal.server.api.historyservice.v1.ResetActivityResponse + (*UpdateWorkflowExecutionOptionsRequest)(nil), // 154: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest + (*UpdateWorkflowExecutionOptionsResponse)(nil), // 155: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse + (*PauseWorkflowExecutionRequest)(nil), // 156: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest + (*PauseWorkflowExecutionResponse)(nil), // 157: temporal.server.api.historyservice.v1.PauseWorkflowExecutionResponse + (*UnpauseWorkflowExecutionRequest)(nil), // 158: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest + (*UnpauseWorkflowExecutionResponse)(nil), // 159: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionResponse + (*StartNexusOperationRequest)(nil), // 160: temporal.server.api.historyservice.v1.StartNexusOperationRequest + (*StartNexusOperationResponse)(nil), // 161: temporal.server.api.historyservice.v1.StartNexusOperationResponse + (*CancelNexusOperationRequest)(nil), // 162: temporal.server.api.historyservice.v1.CancelNexusOperationRequest + (*CancelNexusOperationResponse)(nil), // 163: temporal.server.api.historyservice.v1.CancelNexusOperationResponse + (*PollWorkflowExecutionTimeSkippingRequest)(nil), // 164: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest + (*PollWorkflowExecutionTimeSkippingResponse)(nil), // 165: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse + (*ExecuteMultiOperationRequest_Operation)(nil), // 166: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation + (*ExecuteMultiOperationResponse_Response)(nil), // 167: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response + nil, // 168: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry + nil, // 169: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry + nil, // 170: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry + nil, // 171: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry + nil, // 172: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry + (*ListQueuesResponse_QueueInfo)(nil), // 173: temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo + (*AddTasksRequest_Task)(nil), // 174: temporal.server.api.historyservice.v1.AddTasksRequest.Task + (*v1.StartWorkflowExecutionRequest)(nil), // 175: temporal.api.workflowservice.v1.StartWorkflowExecutionRequest + (*v11.ParentExecutionInfo)(nil), // 176: temporal.server.api.workflow.v1.ParentExecutionInfo + (*timestamppb.Timestamp)(nil), // 177: google.protobuf.Timestamp + (v12.ContinueAsNewInitiator)(0), // 178: temporal.api.enums.v1.ContinueAsNewInitiator + (*v13.Failure)(nil), // 179: temporal.api.failure.v1.Failure + (*v14.Payloads)(nil), // 180: temporal.api.common.v1.Payloads + (*durationpb.Duration)(nil), // 181: google.protobuf.Duration + (*v14.WorkerVersionStamp)(nil), // 182: temporal.api.common.v1.WorkerVersionStamp + (*v11.RootExecutionInfo)(nil), // 183: temporal.server.api.workflow.v1.RootExecutionInfo + (*v15.VersioningOverride)(nil), // 184: temporal.api.workflow.v1.VersioningOverride + (*v16.WorkerDeploymentVersion)(nil), // 185: temporal.api.deployment.v1.WorkerDeploymentVersion + (*v16.InheritedAutoUpgradeInfo)(nil), // 186: temporal.api.deployment.v1.InheritedAutoUpgradeInfo + (*v17.DeclinedTargetVersionUpgrade)(nil), // 187: temporal.api.history.v1.DeclinedTargetVersionUpgrade + (*v14.TimeSkippingStatePropagation)(nil), // 188: temporal.api.common.v1.TimeSkippingStatePropagation + (*v18.VectorClock)(nil), // 189: temporal.server.api.clock.v1.VectorClock + (*v1.PollWorkflowTaskQueueResponse)(nil), // 190: temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse + (v12.WorkflowExecutionStatus)(0), // 191: temporal.api.enums.v1.WorkflowExecutionStatus + (*v14.Link)(nil), // 192: temporal.api.common.v1.Link + (*v14.WorkflowExecution)(nil), // 193: temporal.api.common.v1.WorkflowExecution + (*v19.StreamSlice)(nil), // 194: temporal.api.stream.v1.StreamSlice + (*v110.VersionHistoryItem)(nil), // 195: temporal.server.api.history.v1.VersionHistoryItem + (*v111.VersionedTransition)(nil), // 196: temporal.server.api.persistence.v1.VersionedTransition + (*v14.WorkflowType)(nil), // 197: temporal.api.common.v1.WorkflowType + (*v112.TaskQueue)(nil), // 198: temporal.api.taskqueue.v1.TaskQueue + (v113.WorkflowExecutionState)(0), // 199: temporal.server.api.enums.v1.WorkflowExecutionState + (*v110.VersionHistories)(nil), // 200: temporal.server.api.history.v1.VersionHistories + (*v15.WorkflowExecutionVersioningInfo)(nil), // 201: temporal.api.workflow.v1.WorkflowExecutionVersioningInfo + (*v110.TransientWorkflowTaskInfo)(nil), // 202: temporal.server.api.history.v1.TransientWorkflowTaskInfo + (*v1.PollWorkflowTaskQueueRequest)(nil), // 203: temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest + (*v114.BuildIdRedirectInfo)(nil), // 204: temporal.server.api.taskqueue.v1.BuildIdRedirectInfo + (*v16.Deployment)(nil), // 205: temporal.api.deployment.v1.Deployment + (*v114.TaskVersionDirective)(nil), // 206: temporal.server.api.taskqueue.v1.TaskVersionDirective + (*v116.Message)(nil), // 207: temporal.api.protocol.v1.Message + (*v17.History)(nil), // 208: temporal.api.history.v1.History + (*v1.PollActivityTaskQueueRequest)(nil), // 209: temporal.api.workflowservice.v1.PollActivityTaskQueueRequest + (*v17.HistoryEvent)(nil), // 210: temporal.api.history.v1.HistoryEvent + (*v14.Priority)(nil), // 211: temporal.api.common.v1.Priority + (*v14.RetryPolicy)(nil), // 212: temporal.api.common.v1.RetryPolicy + (*v1.RespondWorkflowTaskCompletedRequest)(nil), // 213: temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest + (*v1.PollActivityTaskQueueResponse)(nil), // 214: temporal.api.workflowservice.v1.PollActivityTaskQueueResponse + (*v1.RespondWorkflowTaskFailedRequest)(nil), // 215: temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest + (*v1.RecordActivityTaskHeartbeatRequest)(nil), // 216: temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest + (*v1.RespondActivityTaskCompletedRequest)(nil), // 217: temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest + (*v1.RespondActivityTaskFailedRequest)(nil), // 218: temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest + (*v1.RespondActivityTaskCanceledRequest)(nil), // 219: temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest + (*v1.SignalWorkflowExecutionRequest)(nil), // 220: temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest + (*v1.SignalWithStartWorkflowExecutionRequest)(nil), // 221: temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest + (*v1.TerminateWorkflowExecutionRequest)(nil), // 222: temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest + (*v1.ResetWorkflowExecutionRequest)(nil), // 223: temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest + (*v1.RequestCancelWorkflowExecutionRequest)(nil), // 224: temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest + (*v1.DescribeWorkflowExecutionRequest)(nil), // 225: temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest + (*v15.WorkflowExecutionConfig)(nil), // 226: temporal.api.workflow.v1.WorkflowExecutionConfig + (*v15.WorkflowExecutionInfo)(nil), // 227: temporal.api.workflow.v1.WorkflowExecutionInfo + (*v15.PendingActivityInfo)(nil), // 228: temporal.api.workflow.v1.PendingActivityInfo + (*v15.PendingChildExecutionInfo)(nil), // 229: temporal.api.workflow.v1.PendingChildExecutionInfo + (*v15.PendingWorkflowTaskInfo)(nil), // 230: temporal.api.workflow.v1.PendingWorkflowTaskInfo + (*v15.CallbackInfo)(nil), // 231: temporal.api.workflow.v1.CallbackInfo + (*v15.PendingNexusOperationInfo)(nil), // 232: temporal.api.workflow.v1.PendingNexusOperationInfo + (*v15.WorkflowExecutionExtendedInfo)(nil), // 233: temporal.api.workflow.v1.WorkflowExecutionExtendedInfo + (*v14.DataBlob)(nil), // 234: temporal.api.common.v1.DataBlob + (*v11.BaseExecutionInfo)(nil), // 235: temporal.server.api.workflow.v1.BaseExecutionInfo + (*v111.WorkflowMutableState)(nil), // 236: temporal.server.api.persistence.v1.WorkflowMutableState + (*v110.VersionHistory)(nil), // 237: temporal.server.api.history.v1.VersionHistory + (*v117.NamespaceCacheInfo)(nil), // 238: temporal.server.api.namespace.v1.NamespaceCacheInfo + (*v111.ShardInfo)(nil), // 239: temporal.server.api.persistence.v1.ShardInfo + (*v118.ReplicationToken)(nil), // 240: temporal.server.api.replication.v1.ReplicationToken + (*v118.ReplicationTaskInfo)(nil), // 241: temporal.server.api.replication.v1.ReplicationTaskInfo + (*v118.ReplicationTask)(nil), // 242: temporal.server.api.replication.v1.ReplicationTask + (*v1.QueryWorkflowRequest)(nil), // 243: temporal.api.workflowservice.v1.QueryWorkflowRequest + (*v1.QueryWorkflowResponse)(nil), // 244: temporal.api.workflowservice.v1.QueryWorkflowResponse + (*v119.ReapplyEventsRequest)(nil), // 245: temporal.server.api.adminservice.v1.ReapplyEventsRequest + (v113.DeadLetterQueueType)(0), // 246: temporal.server.api.enums.v1.DeadLetterQueueType + (*v119.RefreshWorkflowTasksRequest)(nil), // 247: temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest + (*v1.UpdateWorkflowExecutionRequest)(nil), // 248: temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest + (*v1.UpdateWorkflowExecutionResponse)(nil), // 249: temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse + (*v118.SyncReplicationState)(nil), // 250: temporal.server.api.replication.v1.SyncReplicationState + (*v118.WorkflowReplicationMessages)(nil), // 251: temporal.server.api.replication.v1.WorkflowReplicationMessages + (*v1.PollWorkflowExecutionUpdateRequest)(nil), // 252: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest + (*v1.PollWorkflowExecutionUpdateResponse)(nil), // 253: temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse + (*v1.GetWorkflowExecutionHistoryRequest)(nil), // 254: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest + (*v1.GetWorkflowExecutionHistoryResponse)(nil), // 255: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + (*v1.GetWorkflowExecutionHistoryReverseRequest)(nil), // 256: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest + (*v1.GetWorkflowExecutionHistoryReverseResponse)(nil), // 257: temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse + (*v119.GetWorkflowExecutionRawHistoryV2Request)(nil), // 258: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request + (*v119.GetWorkflowExecutionRawHistoryV2Response)(nil), // 259: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response + (*v119.GetWorkflowExecutionRawHistoryRequest)(nil), // 260: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest + (*v119.GetWorkflowExecutionRawHistoryResponse)(nil), // 261: temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse + (*v119.DeleteWorkflowExecutionRequest)(nil), // 262: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest + (*v119.DeleteWorkflowExecutionResponse)(nil), // 263: temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse + (*v120.HistoryDLQKey)(nil), // 264: temporal.server.api.common.v1.HistoryDLQKey + (*v120.HistoryDLQTask)(nil), // 265: temporal.server.api.common.v1.HistoryDLQTask + (*v120.HistoryDLQTaskMetadata)(nil), // 266: temporal.server.api.common.v1.HistoryDLQTaskMetadata + (*v119.ListHistoryTasksRequest)(nil), // 267: temporal.server.api.adminservice.v1.ListHistoryTasksRequest + (*v119.ListHistoryTasksResponse)(nil), // 268: temporal.server.api.adminservice.v1.ListHistoryTasksResponse + (*v121.NexusOperationCompletion)(nil), // 269: temporal.server.api.token.v1.NexusOperationCompletion + (*v14.Payload)(nil), // 270: temporal.api.common.v1.Payload + (*v122.Failure)(nil), // 271: temporal.api.nexus.v1.Failure + (*v111.StateMachineRef)(nil), // 272: temporal.server.api.persistence.v1.StateMachineRef + (v113.HealthState)(0), // 273: temporal.server.api.enums.v1.HealthState + (*v123.HealthCheck)(nil), // 274: temporal.server.api.health.v1.HealthCheck + (*v118.VersionedTransitionArtifact)(nil), // 275: temporal.server.api.replication.v1.VersionedTransitionArtifact + (*v1.UpdateActivityOptionsRequest)(nil), // 276: temporal.api.workflowservice.v1.UpdateActivityOptionsRequest + (*v124.ActivityOptions)(nil), // 277: temporal.api.activity.v1.ActivityOptions + (*v1.PauseActivityRequest)(nil), // 278: temporal.api.workflowservice.v1.PauseActivityRequest + (*v1.UnpauseActivityRequest)(nil), // 279: temporal.api.workflowservice.v1.UnpauseActivityRequest + (*v1.ResetActivityRequest)(nil), // 280: temporal.api.workflowservice.v1.ResetActivityRequest + (*v1.UpdateWorkflowExecutionOptionsRequest)(nil), // 281: temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest + (*v15.WorkflowExecutionOptions)(nil), // 282: temporal.api.workflow.v1.WorkflowExecutionOptions + (*v1.PauseWorkflowExecutionRequest)(nil), // 283: temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest + (*v1.UnpauseWorkflowExecutionRequest)(nil), // 284: temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest + (*v122.StartOperationRequest)(nil), // 285: temporal.api.nexus.v1.StartOperationRequest + (*v122.StartOperationResponse)(nil), // 286: temporal.api.nexus.v1.StartOperationResponse + (*v122.CancelOperationRequest)(nil), // 287: temporal.api.nexus.v1.CancelOperationRequest + (*v122.CancelOperationResponse)(nil), // 288: temporal.api.nexus.v1.CancelOperationResponse + (*v1.PollWorkflowExecutionTimeSkippingRequest)(nil), // 289: temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest + (*v1.PollWorkflowExecutionTimeSkippingResponse)(nil), // 290: temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse + (*v115.WorkflowQuery)(nil), // 291: temporal.api.query.v1.WorkflowQuery + (*v118.ReplicationMessages)(nil), // 292: temporal.server.api.replication.v1.ReplicationMessages + (*descriptorpb.MessageOptions)(nil), // 293: google.protobuf.MessageOptions } var file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs = []int32{ - 173, // 0: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.start_request:type_name -> temporal.api.workflowservice.v1.StartWorkflowExecutionRequest - 174, // 1: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.parent_execution_info:type_name -> temporal.server.api.workflow.v1.ParentExecutionInfo - 175, // 2: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.workflow_execution_expiration_time:type_name -> google.protobuf.Timestamp - 176, // 3: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.continue_as_new_initiator:type_name -> temporal.api.enums.v1.ContinueAsNewInitiator - 177, // 4: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.continued_failure:type_name -> temporal.api.failure.v1.Failure - 178, // 5: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.last_completion_result:type_name -> temporal.api.common.v1.Payloads - 179, // 6: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.first_workflow_task_backoff:type_name -> google.protobuf.Duration - 180, // 7: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.source_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp - 181, // 8: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.root_execution_info:type_name -> temporal.server.api.workflow.v1.RootExecutionInfo - 182, // 9: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.versioning_override:type_name -> temporal.api.workflow.v1.VersioningOverride - 183, // 10: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.inherited_pinned_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion - 184, // 11: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.inherited_auto_upgrade_info:type_name -> temporal.api.deployment.v1.InheritedAutoUpgradeInfo - 185, // 12: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.declined_target_version_upgrade:type_name -> temporal.api.history.v1.DeclinedTargetVersionUpgrade - 186, // 13: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.time_skipping_state_propagation:type_name -> temporal.api.common.v1.TimeSkippingStatePropagation - 187, // 14: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 188, // 15: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.eager_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse - 189, // 16: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus - 190, // 17: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link - 191, // 18: temporal.server.api.historyservice.v1.GetMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 192, // 19: temporal.server.api.historyservice.v1.GetMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 193, // 20: temporal.server.api.historyservice.v1.GetMutableStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 191, // 21: temporal.server.api.historyservice.v1.GetMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 194, // 22: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 195, // 23: temporal.server.api.historyservice.v1.GetMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 195, // 24: temporal.server.api.historyservice.v1.GetMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 179, // 25: temporal.server.api.historyservice.v1.GetMutableStateResponse.sticky_task_queue_schedule_to_start_timeout:type_name -> google.protobuf.Duration - 196, // 26: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState - 189, // 27: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus - 197, // 28: temporal.server.api.historyservice.v1.GetMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 180, // 29: temporal.server.api.historyservice.v1.GetMutableStateResponse.most_recent_worker_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp - 193, // 30: temporal.server.api.historyservice.v1.GetMutableStateResponse.transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 198, // 31: temporal.server.api.historyservice.v1.GetMutableStateResponse.versioning_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionVersioningInfo - 199, // 32: temporal.server.api.historyservice.v1.GetMutableStateResponse.transient_or_speculative_tasks:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 191, // 33: temporal.server.api.historyservice.v1.PollMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 192, // 34: temporal.server.api.historyservice.v1.PollMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 191, // 35: temporal.server.api.historyservice.v1.PollMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 194, // 36: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 195, // 37: temporal.server.api.historyservice.v1.PollMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 195, // 38: temporal.server.api.historyservice.v1.PollMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 179, // 39: temporal.server.api.historyservice.v1.PollMutableStateResponse.sticky_task_queue_schedule_to_start_timeout:type_name -> google.protobuf.Duration - 197, // 40: temporal.server.api.historyservice.v1.PollMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 196, // 41: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState - 189, // 42: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus - 191, // 43: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 164, // 44: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.operations:type_name -> temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation - 165, // 45: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.responses:type_name -> temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response - 191, // 46: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 200, // 47: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest - 187, // 48: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 201, // 49: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo - 202, // 50: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment - 203, // 51: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective - 183, // 52: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.target_deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion - 194, // 53: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 199, // 54: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 195, // 55: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 175, // 56: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 57: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp - 166, // 58: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry - 187, // 59: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 204, // 60: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.messages:type_name -> temporal.api.protocol.v1.Message - 205, // 61: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.history:type_name -> temporal.api.history.v1.History - 205, // 62: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.raw_history:type_name -> temporal.api.history.v1.History - 206, // 63: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice - 194, // 64: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 199, // 65: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo - 195, // 66: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue - 175, // 67: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 68: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.started_time:type_name -> google.protobuf.Timestamp - 167, // 69: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry - 187, // 70: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 204, // 71: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message - 205, // 72: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History - 206, // 73: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice - 191, // 74: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 207, // 75: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest - 187, // 76: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 201, // 77: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo - 202, // 78: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment - 203, // 79: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective - 208, // 80: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.scheduled_event:type_name -> temporal.api.history.v1.HistoryEvent - 175, // 81: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp - 175, // 82: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.current_attempt_scheduled_time:type_name -> google.protobuf.Timestamp - 178, // 83: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.heartbeat_details:type_name -> temporal.api.common.v1.Payloads - 194, // 84: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType - 187, // 85: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 209, // 86: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.priority:type_name -> temporal.api.common.v1.Priority - 210, // 87: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy - 211, // 88: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest - 12, // 89: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.started_response:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse - 212, // 90: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.activity_tasks:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueResponse - 188, // 91: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.new_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse - 213, // 92: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest - 191, // 93: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 94: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 214, // 95: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest.heartbeat_request:type_name -> temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest - 215, // 96: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest - 216, // 97: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest - 217, // 98: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest - 191, // 99: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 100: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 218, // 101: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.signal_request:type_name -> temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest - 191, // 102: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 190, // 103: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link - 219, // 104: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest - 190, // 105: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse.signal_link:type_name -> temporal.api.common.v1.Link - 191, // 106: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 220, // 107: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest - 191, // 108: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 109: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 221, // 110: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest - 222, // 111: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest - 191, // 112: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 113: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 114: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 187, // 115: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock - 191, // 116: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 117: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 191, // 118: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 119: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 208, // 120: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent - 187, // 121: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 191, // 122: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 123: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 187, // 124: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock - 223, // 125: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest - 224, // 126: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig - 225, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo - 226, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo - 227, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo - 228, // 130: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo - 229, // 131: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo - 230, // 132: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo - 231, // 133: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo - 191, // 134: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 192, // 135: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem - 232, // 136: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob - 232, // 137: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob - 233, // 138: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 234, // 139: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 175, // 140: temporal.server.api.historyservice.v1.SyncShardStatusRequest.status_time:type_name -> google.protobuf.Timestamp - 175, // 141: temporal.server.api.historyservice.v1.SyncActivityRequest.scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 142: temporal.server.api.historyservice.v1.SyncActivityRequest.started_time:type_name -> google.protobuf.Timestamp - 175, // 143: temporal.server.api.historyservice.v1.SyncActivityRequest.last_heartbeat_time:type_name -> google.protobuf.Timestamp - 178, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.details:type_name -> temporal.api.common.v1.Payloads - 177, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.last_failure:type_name -> temporal.api.failure.v1.Failure - 235, // 146: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 233, // 147: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo - 175, // 148: temporal.server.api.historyservice.v1.SyncActivityRequest.first_scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 149: temporal.server.api.historyservice.v1.SyncActivityRequest.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 179, // 150: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_initial_interval:type_name -> google.protobuf.Duration - 179, // 151: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_maximum_interval:type_name -> google.protobuf.Duration - 64, // 152: temporal.server.api.historyservice.v1.SyncActivitiesRequest.activities_info:type_name -> temporal.server.api.historyservice.v1.ActivitySyncInfo - 175, // 153: temporal.server.api.historyservice.v1.ActivitySyncInfo.scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 154: temporal.server.api.historyservice.v1.ActivitySyncInfo.started_time:type_name -> google.protobuf.Timestamp - 175, // 155: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_heartbeat_time:type_name -> google.protobuf.Timestamp - 178, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.details:type_name -> temporal.api.common.v1.Payloads - 177, // 157: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_failure:type_name -> temporal.api.failure.v1.Failure - 235, // 158: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 175, // 159: temporal.server.api.historyservice.v1.ActivitySyncInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp - 175, // 160: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp - 179, // 161: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_initial_interval:type_name -> google.protobuf.Duration - 179, // 162: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_maximum_interval:type_name -> google.protobuf.Duration - 191, // 163: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 234, // 164: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 234, // 165: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState - 191, // 166: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution - 236, // 167: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo - 237, // 168: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo - 175, // 169: temporal.server.api.historyservice.v1.RemoveTaskRequest.visibility_time:type_name -> google.protobuf.Timestamp - 238, // 170: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken - 168, // 171: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.shard_messages:type_name -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry - 239, // 172: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 240, // 173: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 241, // 174: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest - 242, // 175: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse - 243, // 176: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest - 244, // 177: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 244, // 178: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 240, // 179: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask - 239, // 180: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo - 244, // 181: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 244, // 182: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType - 245, // 183: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest - 191, // 184: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 96, // 185: temporal.server.api.historyservice.v1.GetReplicationStatusResponse.shards:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus - 175, // 186: temporal.server.api.historyservice.v1.ShardReplicationStatus.shard_local_time:type_name -> google.protobuf.Timestamp - 169, // 187: temporal.server.api.historyservice.v1.ShardReplicationStatus.remote_clusters:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry - 170, // 188: temporal.server.api.historyservice.v1.ShardReplicationStatus.handover_namespaces:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry - 175, // 189: temporal.server.api.historyservice.v1.ShardReplicationStatus.max_replication_task_visibility_time:type_name -> google.protobuf.Timestamp - 175, // 190: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster.acked_task_visibility_time:type_name -> google.protobuf.Timestamp - 191, // 191: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 191, // 192: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 232, // 193: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob - 235, // 194: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory - 191, // 195: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 175, // 196: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_start_time:type_name -> google.protobuf.Timestamp - 175, // 197: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_close_time:type_name -> google.protobuf.Timestamp - 246, // 198: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest - 247, // 199: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse - 248, // 200: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState - 249, // 201: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages - 250, // 202: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest - 251, // 203: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse - 252, // 204: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest - 253, // 205: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 205, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History - 253, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse - 254, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest - 255, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse - 256, // 210: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request - 257, // 211: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response - 258, // 212: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest - 259, // 213: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse - 260, // 214: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest - 261, // 215: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse - 191, // 216: temporal.server.api.historyservice.v1.DeleteExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 262, // 217: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 263, // 218: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask - 262, // 219: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey - 264, // 220: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata - 171, // 221: temporal.server.api.historyservice.v1.ListQueuesResponse.queues:type_name -> temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo - 172, // 222: temporal.server.api.historyservice.v1.AddTasksRequest.tasks:type_name -> temporal.server.api.historyservice.v1.AddTasksRequest.Task - 265, // 223: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest - 266, // 224: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse - 267, // 225: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 268, // 226: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload - 177, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.failure:type_name -> temporal.api.failure.v1.Failure - 175, // 228: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.close_time:type_name -> google.protobuf.Timestamp - 190, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link - 175, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.start_time:type_name -> google.protobuf.Timestamp - 267, // 231: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion - 268, // 232: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload - 269, // 233: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure - 175, // 234: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.start_time:type_name -> google.protobuf.Timestamp - 190, // 235: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link - 270, // 236: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef - 271, // 237: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState - 272, // 238: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck - 191, // 239: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution - 193, // 240: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition - 197, // 241: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories - 273, // 242: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact - 274, // 243: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest - 275, // 244: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions - 276, // 245: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest - 277, // 246: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest - 278, // 247: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest - 279, // 248: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest - 280, // 249: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions - 175, // 250: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.update_time:type_name -> google.protobuf.Timestamp - 281, // 251: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest - 282, // 252: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest - 283, // 253: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest - 284, // 254: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse - 285, // 255: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest - 286, // 256: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse - 287, // 257: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest - 288, // 258: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse - 1, // 259: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest - 105, // 260: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest - 2, // 261: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse - 106, // 262: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse - 289, // 263: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 289, // 264: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery - 290, // 265: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages - 98, // 266: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster - 97, // 267: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo - 232, // 268: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob - 291, // 269: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions - 0, // 270: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions - 271, // [271:271] is the sub-list for method output_type - 271, // [271:271] is the sub-list for method input_type - 270, // [270:271] is the sub-list for extension type_name - 269, // [269:270] is the sub-list for extension extendee - 0, // [0:269] is the sub-list for field type_name + 175, // 0: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.start_request:type_name -> temporal.api.workflowservice.v1.StartWorkflowExecutionRequest + 176, // 1: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.parent_execution_info:type_name -> temporal.server.api.workflow.v1.ParentExecutionInfo + 177, // 2: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.workflow_execution_expiration_time:type_name -> google.protobuf.Timestamp + 178, // 3: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.continue_as_new_initiator:type_name -> temporal.api.enums.v1.ContinueAsNewInitiator + 179, // 4: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.continued_failure:type_name -> temporal.api.failure.v1.Failure + 180, // 5: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.last_completion_result:type_name -> temporal.api.common.v1.Payloads + 181, // 6: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.first_workflow_task_backoff:type_name -> google.protobuf.Duration + 182, // 7: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.source_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp + 183, // 8: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.root_execution_info:type_name -> temporal.server.api.workflow.v1.RootExecutionInfo + 184, // 9: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.versioning_override:type_name -> temporal.api.workflow.v1.VersioningOverride + 185, // 10: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.inherited_pinned_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion + 186, // 11: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.inherited_auto_upgrade_info:type_name -> temporal.api.deployment.v1.InheritedAutoUpgradeInfo + 187, // 12: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.declined_target_version_upgrade:type_name -> temporal.api.history.v1.DeclinedTargetVersionUpgrade + 188, // 13: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest.time_skipping_state_propagation:type_name -> temporal.api.common.v1.TimeSkippingStatePropagation + 189, // 14: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 190, // 15: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.eager_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse + 191, // 16: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus + 192, // 17: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link + 193, // 18: temporal.server.api.historyservice.v1.GetStreamReplaySlicesRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 194, // 19: temporal.server.api.historyservice.v1.GetStreamReplaySlicesResponse.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice + 193, // 20: temporal.server.api.historyservice.v1.GetMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 195, // 21: temporal.server.api.historyservice.v1.GetMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 196, // 22: temporal.server.api.historyservice.v1.GetMutableStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 193, // 23: temporal.server.api.historyservice.v1.GetMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 197, // 24: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 198, // 25: temporal.server.api.historyservice.v1.GetMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 198, // 26: temporal.server.api.historyservice.v1.GetMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 181, // 27: temporal.server.api.historyservice.v1.GetMutableStateResponse.sticky_task_queue_schedule_to_start_timeout:type_name -> google.protobuf.Duration + 199, // 28: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState + 191, // 29: temporal.server.api.historyservice.v1.GetMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus + 200, // 30: temporal.server.api.historyservice.v1.GetMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 182, // 31: temporal.server.api.historyservice.v1.GetMutableStateResponse.most_recent_worker_version_stamp:type_name -> temporal.api.common.v1.WorkerVersionStamp + 196, // 32: temporal.server.api.historyservice.v1.GetMutableStateResponse.transition_history:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 201, // 33: temporal.server.api.historyservice.v1.GetMutableStateResponse.versioning_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionVersioningInfo + 202, // 34: temporal.server.api.historyservice.v1.GetMutableStateResponse.transient_or_speculative_tasks:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 193, // 35: temporal.server.api.historyservice.v1.PollMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 195, // 36: temporal.server.api.historyservice.v1.PollMutableStateRequest.version_history_item:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 193, // 37: temporal.server.api.historyservice.v1.PollMutableStateResponse.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 197, // 38: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 198, // 39: temporal.server.api.historyservice.v1.PollMutableStateResponse.task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 198, // 40: temporal.server.api.historyservice.v1.PollMutableStateResponse.sticky_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 181, // 41: temporal.server.api.historyservice.v1.PollMutableStateResponse.sticky_task_queue_schedule_to_start_timeout:type_name -> google.protobuf.Duration + 200, // 42: temporal.server.api.historyservice.v1.PollMutableStateResponse.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 199, // 43: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_state:type_name -> temporal.server.api.enums.v1.WorkflowExecutionState + 191, // 44: temporal.server.api.historyservice.v1.PollMutableStateResponse.workflow_status:type_name -> temporal.api.enums.v1.WorkflowExecutionStatus + 193, // 45: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 166, // 46: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.operations:type_name -> temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation + 167, // 47: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.responses:type_name -> temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response + 193, // 48: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 203, // 49: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest + 189, // 50: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 204, // 51: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo + 205, // 52: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment + 206, // 53: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 185, // 54: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest.target_deployment_version:type_name -> temporal.api.deployment.v1.WorkerDeploymentVersion + 197, // 55: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 202, // 56: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 198, // 57: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 177, // 58: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.scheduled_time:type_name -> google.protobuf.Timestamp + 177, // 59: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp + 168, // 60: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry + 189, // 61: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 207, // 62: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.messages:type_name -> temporal.api.protocol.v1.Message + 208, // 63: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.history:type_name -> temporal.api.history.v1.History + 208, // 64: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.raw_history:type_name -> temporal.api.history.v1.History + 194, // 65: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice + 197, // 66: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 202, // 67: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.transient_workflow_task:type_name -> temporal.server.api.history.v1.TransientWorkflowTaskInfo + 198, // 68: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.workflow_execution_task_queue:type_name -> temporal.api.taskqueue.v1.TaskQueue + 177, // 69: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.scheduled_time:type_name -> google.protobuf.Timestamp + 177, // 70: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.started_time:type_name -> google.protobuf.Timestamp + 169, // 71: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.queries:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry + 189, // 72: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 207, // 73: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.messages:type_name -> temporal.api.protocol.v1.Message + 208, // 74: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.history:type_name -> temporal.api.history.v1.History + 194, // 75: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.stream_slices:type_name -> temporal.api.stream.v1.StreamSlice + 193, // 76: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 209, // 77: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.poll_request:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueRequest + 189, // 78: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 204, // 79: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.build_id_redirect_info:type_name -> temporal.server.api.taskqueue.v1.BuildIdRedirectInfo + 205, // 80: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.scheduled_deployment:type_name -> temporal.api.deployment.v1.Deployment + 206, // 81: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest.version_directive:type_name -> temporal.server.api.taskqueue.v1.TaskVersionDirective + 210, // 82: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.scheduled_event:type_name -> temporal.api.history.v1.HistoryEvent + 177, // 83: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.started_time:type_name -> google.protobuf.Timestamp + 177, // 84: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.current_attempt_scheduled_time:type_name -> google.protobuf.Timestamp + 180, // 85: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.heartbeat_details:type_name -> temporal.api.common.v1.Payloads + 197, // 86: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.workflow_type:type_name -> temporal.api.common.v1.WorkflowType + 189, // 87: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 211, // 88: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.priority:type_name -> temporal.api.common.v1.Priority + 212, // 89: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse.retry_policy:type_name -> temporal.api.common.v1.RetryPolicy + 213, // 90: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest + 14, // 91: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.started_response:type_name -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse + 214, // 92: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.activity_tasks:type_name -> temporal.api.workflowservice.v1.PollActivityTaskQueueResponse + 190, // 93: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse.new_workflow_task:type_name -> temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse + 215, // 94: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest + 193, // 95: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 96: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 216, // 97: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest.heartbeat_request:type_name -> temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest + 217, // 98: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest.complete_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest + 218, // 99: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest.failed_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest + 219, // 100: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RespondActivityTaskCanceledRequest + 193, // 101: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 102: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 220, // 103: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.signal_request:type_name -> temporal.api.workflowservice.v1.SignalWorkflowExecutionRequest + 193, // 104: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 192, // 105: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse.link:type_name -> temporal.api.common.v1.Link + 221, // 106: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest.signal_with_start_request:type_name -> temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest + 192, // 107: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse.signal_link:type_name -> temporal.api.common.v1.Link + 193, // 108: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 222, // 109: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.terminate_request:type_name -> temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest + 193, // 110: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 193, // 111: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 223, // 112: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest.reset_request:type_name -> temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest + 224, // 113: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.cancel_request:type_name -> temporal.api.workflowservice.v1.RequestCancelWorkflowExecutionRequest + 193, // 114: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest.external_workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 193, // 115: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 116: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.child_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 189, // 117: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest.parent_clock:type_name -> temporal.server.api.clock.v1.VectorClock + 193, // 118: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 119: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 193, // 120: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 193, // 121: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 210, // 122: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.completion_event:type_name -> temporal.api.history.v1.HistoryEvent + 189, // 123: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 193, // 124: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.parent_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 193, // 125: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.child_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 189, // 126: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest.clock:type_name -> temporal.server.api.clock.v1.VectorClock + 225, // 127: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest + 226, // 128: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.execution_config:type_name -> temporal.api.workflow.v1.WorkflowExecutionConfig + 227, // 129: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_execution_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionInfo + 228, // 130: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_activities:type_name -> temporal.api.workflow.v1.PendingActivityInfo + 229, // 131: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_children:type_name -> temporal.api.workflow.v1.PendingChildExecutionInfo + 230, // 132: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_workflow_task:type_name -> temporal.api.workflow.v1.PendingWorkflowTaskInfo + 231, // 133: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.callbacks:type_name -> temporal.api.workflow.v1.CallbackInfo + 232, // 134: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.pending_nexus_operations:type_name -> temporal.api.workflow.v1.PendingNexusOperationInfo + 233, // 135: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse.workflow_extended_info:type_name -> temporal.api.workflow.v1.WorkflowExecutionExtendedInfo + 193, // 136: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 195, // 137: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.version_history_items:type_name -> temporal.server.api.history.v1.VersionHistoryItem + 234, // 138: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.events:type_name -> temporal.api.common.v1.DataBlob + 234, // 139: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.new_run_events:type_name -> temporal.api.common.v1.DataBlob + 235, // 140: temporal.server.api.historyservice.v1.ReplicateEventsV2Request.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 236, // 141: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest.workflow_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 177, // 142: temporal.server.api.historyservice.v1.SyncShardStatusRequest.status_time:type_name -> google.protobuf.Timestamp + 177, // 143: temporal.server.api.historyservice.v1.SyncActivityRequest.scheduled_time:type_name -> google.protobuf.Timestamp + 177, // 144: temporal.server.api.historyservice.v1.SyncActivityRequest.started_time:type_name -> google.protobuf.Timestamp + 177, // 145: temporal.server.api.historyservice.v1.SyncActivityRequest.last_heartbeat_time:type_name -> google.protobuf.Timestamp + 180, // 146: temporal.server.api.historyservice.v1.SyncActivityRequest.details:type_name -> temporal.api.common.v1.Payloads + 179, // 147: temporal.server.api.historyservice.v1.SyncActivityRequest.last_failure:type_name -> temporal.api.failure.v1.Failure + 237, // 148: temporal.server.api.historyservice.v1.SyncActivityRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 235, // 149: temporal.server.api.historyservice.v1.SyncActivityRequest.base_execution_info:type_name -> temporal.server.api.workflow.v1.BaseExecutionInfo + 177, // 150: temporal.server.api.historyservice.v1.SyncActivityRequest.first_scheduled_time:type_name -> google.protobuf.Timestamp + 177, // 151: temporal.server.api.historyservice.v1.SyncActivityRequest.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 181, // 152: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_initial_interval:type_name -> google.protobuf.Duration + 181, // 153: temporal.server.api.historyservice.v1.SyncActivityRequest.retry_maximum_interval:type_name -> google.protobuf.Duration + 66, // 154: temporal.server.api.historyservice.v1.SyncActivitiesRequest.activities_info:type_name -> temporal.server.api.historyservice.v1.ActivitySyncInfo + 177, // 155: temporal.server.api.historyservice.v1.ActivitySyncInfo.scheduled_time:type_name -> google.protobuf.Timestamp + 177, // 156: temporal.server.api.historyservice.v1.ActivitySyncInfo.started_time:type_name -> google.protobuf.Timestamp + 177, // 157: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_heartbeat_time:type_name -> google.protobuf.Timestamp + 180, // 158: temporal.server.api.historyservice.v1.ActivitySyncInfo.details:type_name -> temporal.api.common.v1.Payloads + 179, // 159: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_failure:type_name -> temporal.api.failure.v1.Failure + 237, // 160: temporal.server.api.historyservice.v1.ActivitySyncInfo.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 177, // 161: temporal.server.api.historyservice.v1.ActivitySyncInfo.first_scheduled_time:type_name -> google.protobuf.Timestamp + 177, // 162: temporal.server.api.historyservice.v1.ActivitySyncInfo.last_attempt_complete_time:type_name -> google.protobuf.Timestamp + 181, // 163: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_initial_interval:type_name -> google.protobuf.Duration + 181, // 164: temporal.server.api.historyservice.v1.ActivitySyncInfo.retry_maximum_interval:type_name -> google.protobuf.Duration + 193, // 165: temporal.server.api.historyservice.v1.DescribeMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 236, // 166: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.cache_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 236, // 167: temporal.server.api.historyservice.v1.DescribeMutableStateResponse.database_mutable_state:type_name -> temporal.server.api.persistence.v1.WorkflowMutableState + 193, // 168: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest.workflow_execution:type_name -> temporal.api.common.v1.WorkflowExecution + 238, // 169: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse.namespace_cache:type_name -> temporal.server.api.namespace.v1.NamespaceCacheInfo + 239, // 170: temporal.server.api.historyservice.v1.GetShardResponse.shard_info:type_name -> temporal.server.api.persistence.v1.ShardInfo + 177, // 171: temporal.server.api.historyservice.v1.RemoveTaskRequest.visibility_time:type_name -> google.protobuf.Timestamp + 240, // 172: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest.tokens:type_name -> temporal.server.api.replication.v1.ReplicationToken + 170, // 173: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.shard_messages:type_name -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry + 241, // 174: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest.task_infos:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 242, // 175: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 243, // 176: temporal.server.api.historyservice.v1.QueryWorkflowRequest.request:type_name -> temporal.api.workflowservice.v1.QueryWorkflowRequest + 244, // 177: temporal.server.api.historyservice.v1.QueryWorkflowResponse.response:type_name -> temporal.api.workflowservice.v1.QueryWorkflowResponse + 245, // 178: temporal.server.api.historyservice.v1.ReapplyEventsRequest.request:type_name -> temporal.server.api.adminservice.v1.ReapplyEventsRequest + 246, // 179: temporal.server.api.historyservice.v1.GetDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 246, // 180: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 242, // 181: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks:type_name -> temporal.server.api.replication.v1.ReplicationTask + 241, // 182: temporal.server.api.historyservice.v1.GetDLQMessagesResponse.replication_tasks_info:type_name -> temporal.server.api.replication.v1.ReplicationTaskInfo + 246, // 183: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 246, // 184: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest.type:type_name -> temporal.server.api.enums.v1.DeadLetterQueueType + 247, // 185: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.RefreshWorkflowTasksRequest + 193, // 186: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 98, // 187: temporal.server.api.historyservice.v1.GetReplicationStatusResponse.shards:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus + 177, // 188: temporal.server.api.historyservice.v1.ShardReplicationStatus.shard_local_time:type_name -> google.protobuf.Timestamp + 171, // 189: temporal.server.api.historyservice.v1.ShardReplicationStatus.remote_clusters:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry + 172, // 190: temporal.server.api.historyservice.v1.ShardReplicationStatus.handover_namespaces:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry + 177, // 191: temporal.server.api.historyservice.v1.ShardReplicationStatus.max_replication_task_visibility_time:type_name -> google.protobuf.Timestamp + 177, // 192: temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster.acked_task_visibility_time:type_name -> google.protobuf.Timestamp + 193, // 193: temporal.server.api.historyservice.v1.RebuildMutableStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 193, // 194: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 234, // 195: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.history_batches:type_name -> temporal.api.common.v1.DataBlob + 237, // 196: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest.version_history:type_name -> temporal.server.api.history.v1.VersionHistory + 193, // 197: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 177, // 198: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_start_time:type_name -> google.protobuf.Timestamp + 177, // 199: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest.workflow_close_time:type_name -> google.protobuf.Timestamp + 248, // 200: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest.request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequest + 249, // 201: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse.response:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse + 250, // 202: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest.sync_replication_state:type_name -> temporal.server.api.replication.v1.SyncReplicationState + 251, // 203: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse.messages:type_name -> temporal.server.api.replication.v1.WorkflowReplicationMessages + 252, // 204: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest + 253, // 205: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionUpdateResponse + 254, // 206: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest + 255, // 207: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 208, // 208: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse.history:type_name -> temporal.api.history.v1.History + 255, // 209: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponseWithRaw.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse + 256, // 210: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest.request:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest + 257, // 211: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse.response:type_name -> temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse + 258, // 212: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Request + 259, // 213: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryV2Response + 260, // 214: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest.request:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryRequest + 261, // 215: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse.response:type_name -> temporal.server.api.adminservice.v1.GetWorkflowExecutionRawHistoryResponse + 262, // 216: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest.request:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionRequest + 263, // 217: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse.response:type_name -> temporal.server.api.adminservice.v1.DeleteWorkflowExecutionResponse + 193, // 218: temporal.server.api.historyservice.v1.DeleteExecutionRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 264, // 219: temporal.server.api.historyservice.v1.GetDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 265, // 220: temporal.server.api.historyservice.v1.GetDLQTasksResponse.dlq_tasks:type_name -> temporal.server.api.common.v1.HistoryDLQTask + 264, // 221: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.dlq_key:type_name -> temporal.server.api.common.v1.HistoryDLQKey + 266, // 222: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest.inclusive_max_task_metadata:type_name -> temporal.server.api.common.v1.HistoryDLQTaskMetadata + 173, // 223: temporal.server.api.historyservice.v1.ListQueuesResponse.queues:type_name -> temporal.server.api.historyservice.v1.ListQueuesResponse.QueueInfo + 174, // 224: temporal.server.api.historyservice.v1.AddTasksRequest.tasks:type_name -> temporal.server.api.historyservice.v1.AddTasksRequest.Task + 267, // 225: temporal.server.api.historyservice.v1.ListTasksRequest.request:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksRequest + 268, // 226: temporal.server.api.historyservice.v1.ListTasksResponse.response:type_name -> temporal.server.api.adminservice.v1.ListHistoryTasksResponse + 269, // 227: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 270, // 228: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.success:type_name -> temporal.api.common.v1.Payload + 179, // 229: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.failure:type_name -> temporal.api.failure.v1.Failure + 177, // 230: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.close_time:type_name -> google.protobuf.Timestamp + 192, // 231: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.links:type_name -> temporal.api.common.v1.Link + 177, // 232: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest.start_time:type_name -> google.protobuf.Timestamp + 269, // 233: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.completion:type_name -> temporal.server.api.token.v1.NexusOperationCompletion + 270, // 234: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.success:type_name -> temporal.api.common.v1.Payload + 271, // 235: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.failure:type_name -> temporal.api.nexus.v1.Failure + 177, // 236: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.start_time:type_name -> google.protobuf.Timestamp + 192, // 237: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest.links:type_name -> temporal.api.common.v1.Link + 272, // 238: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest.ref:type_name -> temporal.server.api.persistence.v1.StateMachineRef + 273, // 239: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.state:type_name -> temporal.server.api.enums.v1.HealthState + 274, // 240: temporal.server.api.historyservice.v1.DeepHealthCheckResponse.checks:type_name -> temporal.server.api.health.v1.HealthCheck + 193, // 241: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.execution:type_name -> temporal.api.common.v1.WorkflowExecution + 196, // 242: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.versioned_transition:type_name -> temporal.server.api.persistence.v1.VersionedTransition + 200, // 243: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest.version_histories:type_name -> temporal.server.api.history.v1.VersionHistories + 275, // 244: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse.versioned_transition_artifact:type_name -> temporal.server.api.replication.v1.VersionedTransitionArtifact + 276, // 245: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateActivityOptionsRequest + 277, // 246: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse.activity_options:type_name -> temporal.api.activity.v1.ActivityOptions + 278, // 247: temporal.server.api.historyservice.v1.PauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.PauseActivityRequest + 279, // 248: temporal.server.api.historyservice.v1.UnpauseActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.UnpauseActivityRequest + 280, // 249: temporal.server.api.historyservice.v1.ResetActivityRequest.frontend_request:type_name -> temporal.api.workflowservice.v1.ResetActivityRequest + 281, // 250: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest.update_request:type_name -> temporal.api.workflowservice.v1.UpdateWorkflowExecutionOptionsRequest + 282, // 251: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.workflow_execution_options:type_name -> temporal.api.workflow.v1.WorkflowExecutionOptions + 177, // 252: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse.update_time:type_name -> google.protobuf.Timestamp + 283, // 253: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest.pause_request:type_name -> temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest + 284, // 254: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest.unpause_request:type_name -> temporal.api.workflowservice.v1.UnpauseWorkflowExecutionRequest + 285, // 255: temporal.server.api.historyservice.v1.StartNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.StartOperationRequest + 286, // 256: temporal.server.api.historyservice.v1.StartNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.StartOperationResponse + 287, // 257: temporal.server.api.historyservice.v1.CancelNexusOperationRequest.request:type_name -> temporal.api.nexus.v1.CancelOperationRequest + 288, // 258: temporal.server.api.historyservice.v1.CancelNexusOperationResponse.response:type_name -> temporal.api.nexus.v1.CancelOperationResponse + 289, // 259: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest.request:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest + 290, // 260: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse.response:type_name -> temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse + 1, // 261: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest + 107, // 262: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest.Operation.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest + 2, // 263: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.start_workflow:type_name -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse + 108, // 264: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse.Response.update_workflow:type_name -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse + 291, // 265: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 291, // 266: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponseWithRawHistory.QueriesEntry.value:type_name -> temporal.api.query.v1.WorkflowQuery + 292, // 267: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse.ShardMessagesEntry.value:type_name -> temporal.server.api.replication.v1.ReplicationMessages + 100, // 268: temporal.server.api.historyservice.v1.ShardReplicationStatus.RemoteClustersEntry.value:type_name -> temporal.server.api.historyservice.v1.ShardReplicationStatusPerCluster + 99, // 269: temporal.server.api.historyservice.v1.ShardReplicationStatus.HandoverNamespacesEntry.value:type_name -> temporal.server.api.historyservice.v1.HandoverNamespaceInfo + 234, // 270: temporal.server.api.historyservice.v1.AddTasksRequest.Task.blob:type_name -> temporal.api.common.v1.DataBlob + 293, // 271: temporal.server.api.historyservice.v1.routing:extendee -> google.protobuf.MessageOptions + 0, // 272: temporal.server.api.historyservice.v1.routing:type_name -> temporal.server.api.historyservice.v1.RoutingOptions + 273, // [273:273] is the sub-list for method output_type + 273, // [273:273] is the sub-list for method input_type + 272, // [272:273] is the sub-list for extension type_name + 271, // [271:272] is the sub-list for extension extendee + 0, // [0:271] is the sub-list for field type_name } func init() { file_temporal_server_api_historyservice_v1_request_response_proto_init() } @@ -12102,25 +12212,25 @@ func file_temporal_server_api_historyservice_v1_request_response_proto_init() { if File_temporal_server_api_historyservice_v1_request_response_proto != nil { return } - file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[107].OneofWrappers = []any{ + file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[109].OneofWrappers = []any{ (*StreamWorkflowReplicationMessagesRequest_SyncReplicationState)(nil), } - file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[108].OneofWrappers = []any{ + file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[110].OneofWrappers = []any{ (*StreamWorkflowReplicationMessagesResponse_Messages)(nil), } - file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[134].OneofWrappers = []any{ + file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[136].OneofWrappers = []any{ (*CompleteNexusOperationChasmRequest_Success)(nil), (*CompleteNexusOperationChasmRequest_Failure)(nil), } - file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[136].OneofWrappers = []any{ + file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[138].OneofWrappers = []any{ (*CompleteNexusOperationRequest_Success)(nil), (*CompleteNexusOperationRequest_Failure)(nil), } - file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[164].OneofWrappers = []any{ + file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[166].OneofWrappers = []any{ (*ExecuteMultiOperationRequest_Operation_StartWorkflow)(nil), (*ExecuteMultiOperationRequest_Operation_UpdateWorkflow)(nil), } - file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[165].OneofWrappers = []any{ + file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[167].OneofWrappers = []any{ (*ExecuteMultiOperationResponse_Response_StartWorkflow)(nil), (*ExecuteMultiOperationResponse_Response_UpdateWorkflow)(nil), } @@ -12130,7 +12240,7 @@ func file_temporal_server_api_historyservice_v1_request_response_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc), len(file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc)), NumEnums: 0, - NumMessages: 173, + NumMessages: 175, NumExtensions: 1, NumServices: 0, }, diff --git a/api/historyservice/v1/service.pb.go b/api/historyservice/v1/service.pb.go index 640fef3b4d4..bee88cf6888 100644 --- a/api/historyservice/v1/service.pb.go +++ b/api/historyservice/v1/service.pb.go @@ -26,11 +26,12 @@ var File_temporal_server_api_historyservice_v1_service_proto protoreflect.FileDe const file_temporal_server_api_historyservice_v1_service_proto_rawDesc = "" + "\n" + - "3temporal/server/api/historyservice/v1/service.proto\x12%temporal.server.api.historyservice.v1\x1a0temporal/server/api/common/v1/api_category.proto\x1a.temporal.server.api.historyservice.v1.GetMutableStateResponse\"\x06\x8a\xb5\x18\x02\b\x01\x12\x9b\x01\n" + - "\x10PollMutableState\x12>.temporal.server.api.historyservice.v1.PollMutableStateRequest\x1a?.temporal.server.api.historyservice.v1.PollMutableStateResponse\"\x06\x8a\xb5\x18\x02\b\x02\x12\xa7\x01\n" + + "\x10PollMutableState\x12>.temporal.server.api.historyservice.v1.PollMutableStateRequest\x1a?.temporal.server.api.historyservice.v1.PollMutableStateResponse\"\x06\x8a\xb5\x18\x02\b\x02\x12\xaa\x01\n" + + "\x15GetStreamReplaySlices\x12C.temporal.server.api.historyservice.v1.GetStreamReplaySlicesRequest\x1aD.temporal.server.api.historyservice.v1.GetStreamReplaySlicesResponse\"\x06\x8a\xb5\x18\x02\b\x01\x12\xa7\x01\n" + "\x14ResetStickyTaskQueue\x12B.temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest\x1aC.temporal.server.api.historyservice.v1.ResetStickyTaskQueueResponse\"\x06\x8a\xb5\x18\x02\b\x01\x12\xb6\x01\n" + "\x19RecordWorkflowTaskStarted\x12G.temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest\x1aH.temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse\"\x06\x8a\xb5\x18\x02\b\x01\x12\xb6\x01\n" + "\x19RecordActivityTaskStarted\x12G.temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest\x1aH.temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse\"\x06\x8a\xb5\x18\x02\b\x01\x12\xbf\x01\n" + @@ -114,319 +115,323 @@ var file_temporal_server_api_historyservice_v1_service_proto_goTypes = []any{ (*StartWorkflowExecutionRequest)(nil), // 0: temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest (*GetMutableStateRequest)(nil), // 1: temporal.server.api.historyservice.v1.GetMutableStateRequest (*PollMutableStateRequest)(nil), // 2: temporal.server.api.historyservice.v1.PollMutableStateRequest - (*ResetStickyTaskQueueRequest)(nil), // 3: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest - (*RecordWorkflowTaskStartedRequest)(nil), // 4: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest - (*RecordActivityTaskStartedRequest)(nil), // 5: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest - (*RespondWorkflowTaskCompletedRequest)(nil), // 6: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest - (*RespondWorkflowTaskFailedRequest)(nil), // 7: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest - (*IsWorkflowTaskValidRequest)(nil), // 8: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest - (*RecordActivityTaskHeartbeatRequest)(nil), // 9: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest - (*RespondActivityTaskCompletedRequest)(nil), // 10: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest - (*RespondActivityTaskFailedRequest)(nil), // 11: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest - (*RespondActivityTaskCanceledRequest)(nil), // 12: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest - (*IsActivityTaskValidRequest)(nil), // 13: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest - (*SignalWorkflowExecutionRequest)(nil), // 14: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest - (*SignalWithStartWorkflowExecutionRequest)(nil), // 15: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest - (*ExecuteMultiOperationRequest)(nil), // 16: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest - (*RemoveSignalMutableStateRequest)(nil), // 17: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest - (*TerminateWorkflowExecutionRequest)(nil), // 18: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest - (*DeleteWorkflowExecutionRequest)(nil), // 19: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest - (*ResetWorkflowExecutionRequest)(nil), // 20: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest - (*UpdateWorkflowExecutionOptionsRequest)(nil), // 21: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest - (*RequestCancelWorkflowExecutionRequest)(nil), // 22: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest - (*ScheduleWorkflowTaskRequest)(nil), // 23: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest - (*VerifyFirstWorkflowTaskScheduledRequest)(nil), // 24: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest - (*RecordChildExecutionCompletedRequest)(nil), // 25: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest - (*VerifyChildExecutionCompletionRecordedRequest)(nil), // 26: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest - (*DescribeWorkflowExecutionRequest)(nil), // 27: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest - (*ReplicateEventsV2Request)(nil), // 28: temporal.server.api.historyservice.v1.ReplicateEventsV2Request - (*ReplicateWorkflowStateRequest)(nil), // 29: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest - (*SyncShardStatusRequest)(nil), // 30: temporal.server.api.historyservice.v1.SyncShardStatusRequest - (*SyncActivityRequest)(nil), // 31: temporal.server.api.historyservice.v1.SyncActivityRequest - (*DescribeMutableStateRequest)(nil), // 32: temporal.server.api.historyservice.v1.DescribeMutableStateRequest - (*DescribeHistoryHostRequest)(nil), // 33: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest - (*CloseShardRequest)(nil), // 34: temporal.server.api.historyservice.v1.CloseShardRequest - (*GetShardRequest)(nil), // 35: temporal.server.api.historyservice.v1.GetShardRequest - (*RemoveTaskRequest)(nil), // 36: temporal.server.api.historyservice.v1.RemoveTaskRequest - (*GetReplicationMessagesRequest)(nil), // 37: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest - (*GetDLQReplicationMessagesRequest)(nil), // 38: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest - (*QueryWorkflowRequest)(nil), // 39: temporal.server.api.historyservice.v1.QueryWorkflowRequest - (*ReapplyEventsRequest)(nil), // 40: temporal.server.api.historyservice.v1.ReapplyEventsRequest - (*GetDLQMessagesRequest)(nil), // 41: temporal.server.api.historyservice.v1.GetDLQMessagesRequest - (*PurgeDLQMessagesRequest)(nil), // 42: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest - (*MergeDLQMessagesRequest)(nil), // 43: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest - (*RefreshWorkflowTasksRequest)(nil), // 44: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest - (*GenerateLastHistoryReplicationTasksRequest)(nil), // 45: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest - (*GetReplicationStatusRequest)(nil), // 46: temporal.server.api.historyservice.v1.GetReplicationStatusRequest - (*RebuildMutableStateRequest)(nil), // 47: temporal.server.api.historyservice.v1.RebuildMutableStateRequest - (*ImportWorkflowExecutionRequest)(nil), // 48: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest - (*DeleteWorkflowVisibilityRecordRequest)(nil), // 49: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest - (*UpdateWorkflowExecutionRequest)(nil), // 50: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest - (*PollWorkflowExecutionUpdateRequest)(nil), // 51: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest - (*StreamWorkflowReplicationMessagesRequest)(nil), // 52: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest - (*GetWorkflowExecutionHistoryRequest)(nil), // 53: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest - (*GetWorkflowExecutionHistoryReverseRequest)(nil), // 54: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest - (*GetWorkflowExecutionRawHistoryV2Request)(nil), // 55: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request - (*GetWorkflowExecutionRawHistoryRequest)(nil), // 56: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest - (*ForceDeleteWorkflowExecutionRequest)(nil), // 57: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest - (*DeleteExecutionRequest)(nil), // 58: temporal.server.api.historyservice.v1.DeleteExecutionRequest - (*GetDLQTasksRequest)(nil), // 59: temporal.server.api.historyservice.v1.GetDLQTasksRequest - (*DeleteDLQTasksRequest)(nil), // 60: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest - (*ListQueuesRequest)(nil), // 61: temporal.server.api.historyservice.v1.ListQueuesRequest - (*AddTasksRequest)(nil), // 62: temporal.server.api.historyservice.v1.AddTasksRequest - (*ListTasksRequest)(nil), // 63: temporal.server.api.historyservice.v1.ListTasksRequest - (*CompleteNexusOperationRequest)(nil), // 64: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest - (*CompleteNexusOperationChasmRequest)(nil), // 65: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest - (*InvokeStateMachineMethodRequest)(nil), // 66: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest - (*DeepHealthCheckRequest)(nil), // 67: temporal.server.api.historyservice.v1.DeepHealthCheckRequest - (*SyncWorkflowStateRequest)(nil), // 68: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest - (*UpdateActivityOptionsRequest)(nil), // 69: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest - (*PauseActivityRequest)(nil), // 70: temporal.server.api.historyservice.v1.PauseActivityRequest - (*UnpauseActivityRequest)(nil), // 71: temporal.server.api.historyservice.v1.UnpauseActivityRequest - (*ResetActivityRequest)(nil), // 72: temporal.server.api.historyservice.v1.ResetActivityRequest - (*PauseWorkflowExecutionRequest)(nil), // 73: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest - (*UnpauseWorkflowExecutionRequest)(nil), // 74: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest - (*StartNexusOperationRequest)(nil), // 75: temporal.server.api.historyservice.v1.StartNexusOperationRequest - (*CancelNexusOperationRequest)(nil), // 76: temporal.server.api.historyservice.v1.CancelNexusOperationRequest - (*PollWorkflowExecutionTimeSkippingRequest)(nil), // 77: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest - (*StartWorkflowExecutionResponse)(nil), // 78: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse - (*GetMutableStateResponse)(nil), // 79: temporal.server.api.historyservice.v1.GetMutableStateResponse - (*PollMutableStateResponse)(nil), // 80: temporal.server.api.historyservice.v1.PollMutableStateResponse - (*ResetStickyTaskQueueResponse)(nil), // 81: temporal.server.api.historyservice.v1.ResetStickyTaskQueueResponse - (*RecordWorkflowTaskStartedResponse)(nil), // 82: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse - (*RecordActivityTaskStartedResponse)(nil), // 83: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse - (*RespondWorkflowTaskCompletedResponse)(nil), // 84: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse - (*RespondWorkflowTaskFailedResponse)(nil), // 85: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedResponse - (*IsWorkflowTaskValidResponse)(nil), // 86: temporal.server.api.historyservice.v1.IsWorkflowTaskValidResponse - (*RecordActivityTaskHeartbeatResponse)(nil), // 87: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatResponse - (*RespondActivityTaskCompletedResponse)(nil), // 88: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedResponse - (*RespondActivityTaskFailedResponse)(nil), // 89: temporal.server.api.historyservice.v1.RespondActivityTaskFailedResponse - (*RespondActivityTaskCanceledResponse)(nil), // 90: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledResponse - (*IsActivityTaskValidResponse)(nil), // 91: temporal.server.api.historyservice.v1.IsActivityTaskValidResponse - (*SignalWorkflowExecutionResponse)(nil), // 92: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse - (*SignalWithStartWorkflowExecutionResponse)(nil), // 93: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse - (*ExecuteMultiOperationResponse)(nil), // 94: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse - (*RemoveSignalMutableStateResponse)(nil), // 95: temporal.server.api.historyservice.v1.RemoveSignalMutableStateResponse - (*TerminateWorkflowExecutionResponse)(nil), // 96: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionResponse - (*DeleteWorkflowExecutionResponse)(nil), // 97: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionResponse - (*ResetWorkflowExecutionResponse)(nil), // 98: temporal.server.api.historyservice.v1.ResetWorkflowExecutionResponse - (*UpdateWorkflowExecutionOptionsResponse)(nil), // 99: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse - (*RequestCancelWorkflowExecutionResponse)(nil), // 100: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionResponse - (*ScheduleWorkflowTaskResponse)(nil), // 101: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskResponse - (*VerifyFirstWorkflowTaskScheduledResponse)(nil), // 102: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledResponse - (*RecordChildExecutionCompletedResponse)(nil), // 103: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedResponse - (*VerifyChildExecutionCompletionRecordedResponse)(nil), // 104: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedResponse - (*DescribeWorkflowExecutionResponse)(nil), // 105: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse - (*ReplicateEventsV2Response)(nil), // 106: temporal.server.api.historyservice.v1.ReplicateEventsV2Response - (*ReplicateWorkflowStateResponse)(nil), // 107: temporal.server.api.historyservice.v1.ReplicateWorkflowStateResponse - (*SyncShardStatusResponse)(nil), // 108: temporal.server.api.historyservice.v1.SyncShardStatusResponse - (*SyncActivityResponse)(nil), // 109: temporal.server.api.historyservice.v1.SyncActivityResponse - (*DescribeMutableStateResponse)(nil), // 110: temporal.server.api.historyservice.v1.DescribeMutableStateResponse - (*DescribeHistoryHostResponse)(nil), // 111: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse - (*CloseShardResponse)(nil), // 112: temporal.server.api.historyservice.v1.CloseShardResponse - (*GetShardResponse)(nil), // 113: temporal.server.api.historyservice.v1.GetShardResponse - (*RemoveTaskResponse)(nil), // 114: temporal.server.api.historyservice.v1.RemoveTaskResponse - (*GetReplicationMessagesResponse)(nil), // 115: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse - (*GetDLQReplicationMessagesResponse)(nil), // 116: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse - (*QueryWorkflowResponse)(nil), // 117: temporal.server.api.historyservice.v1.QueryWorkflowResponse - (*ReapplyEventsResponse)(nil), // 118: temporal.server.api.historyservice.v1.ReapplyEventsResponse - (*GetDLQMessagesResponse)(nil), // 119: temporal.server.api.historyservice.v1.GetDLQMessagesResponse - (*PurgeDLQMessagesResponse)(nil), // 120: temporal.server.api.historyservice.v1.PurgeDLQMessagesResponse - (*MergeDLQMessagesResponse)(nil), // 121: temporal.server.api.historyservice.v1.MergeDLQMessagesResponse - (*RefreshWorkflowTasksResponse)(nil), // 122: temporal.server.api.historyservice.v1.RefreshWorkflowTasksResponse - (*GenerateLastHistoryReplicationTasksResponse)(nil), // 123: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksResponse - (*GetReplicationStatusResponse)(nil), // 124: temporal.server.api.historyservice.v1.GetReplicationStatusResponse - (*RebuildMutableStateResponse)(nil), // 125: temporal.server.api.historyservice.v1.RebuildMutableStateResponse - (*ImportWorkflowExecutionResponse)(nil), // 126: temporal.server.api.historyservice.v1.ImportWorkflowExecutionResponse - (*DeleteWorkflowVisibilityRecordResponse)(nil), // 127: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordResponse - (*UpdateWorkflowExecutionResponse)(nil), // 128: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse - (*PollWorkflowExecutionUpdateResponse)(nil), // 129: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse - (*StreamWorkflowReplicationMessagesResponse)(nil), // 130: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse - (*GetWorkflowExecutionHistoryResponse)(nil), // 131: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse - (*GetWorkflowExecutionHistoryReverseResponse)(nil), // 132: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse - (*GetWorkflowExecutionRawHistoryV2Response)(nil), // 133: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response - (*GetWorkflowExecutionRawHistoryResponse)(nil), // 134: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse - (*ForceDeleteWorkflowExecutionResponse)(nil), // 135: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse - (*DeleteExecutionResponse)(nil), // 136: temporal.server.api.historyservice.v1.DeleteExecutionResponse - (*GetDLQTasksResponse)(nil), // 137: temporal.server.api.historyservice.v1.GetDLQTasksResponse - (*DeleteDLQTasksResponse)(nil), // 138: temporal.server.api.historyservice.v1.DeleteDLQTasksResponse - (*ListQueuesResponse)(nil), // 139: temporal.server.api.historyservice.v1.ListQueuesResponse - (*AddTasksResponse)(nil), // 140: temporal.server.api.historyservice.v1.AddTasksResponse - (*ListTasksResponse)(nil), // 141: temporal.server.api.historyservice.v1.ListTasksResponse - (*CompleteNexusOperationResponse)(nil), // 142: temporal.server.api.historyservice.v1.CompleteNexusOperationResponse - (*CompleteNexusOperationChasmResponse)(nil), // 143: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmResponse - (*InvokeStateMachineMethodResponse)(nil), // 144: temporal.server.api.historyservice.v1.InvokeStateMachineMethodResponse - (*DeepHealthCheckResponse)(nil), // 145: temporal.server.api.historyservice.v1.DeepHealthCheckResponse - (*SyncWorkflowStateResponse)(nil), // 146: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse - (*UpdateActivityOptionsResponse)(nil), // 147: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse - (*PauseActivityResponse)(nil), // 148: temporal.server.api.historyservice.v1.PauseActivityResponse - (*UnpauseActivityResponse)(nil), // 149: temporal.server.api.historyservice.v1.UnpauseActivityResponse - (*ResetActivityResponse)(nil), // 150: temporal.server.api.historyservice.v1.ResetActivityResponse - (*PauseWorkflowExecutionResponse)(nil), // 151: temporal.server.api.historyservice.v1.PauseWorkflowExecutionResponse - (*UnpauseWorkflowExecutionResponse)(nil), // 152: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionResponse - (*StartNexusOperationResponse)(nil), // 153: temporal.server.api.historyservice.v1.StartNexusOperationResponse - (*CancelNexusOperationResponse)(nil), // 154: temporal.server.api.historyservice.v1.CancelNexusOperationResponse - (*PollWorkflowExecutionTimeSkippingResponse)(nil), // 155: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse + (*GetStreamReplaySlicesRequest)(nil), // 3: temporal.server.api.historyservice.v1.GetStreamReplaySlicesRequest + (*ResetStickyTaskQueueRequest)(nil), // 4: temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest + (*RecordWorkflowTaskStartedRequest)(nil), // 5: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest + (*RecordActivityTaskStartedRequest)(nil), // 6: temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest + (*RespondWorkflowTaskCompletedRequest)(nil), // 7: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest + (*RespondWorkflowTaskFailedRequest)(nil), // 8: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest + (*IsWorkflowTaskValidRequest)(nil), // 9: temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest + (*RecordActivityTaskHeartbeatRequest)(nil), // 10: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest + (*RespondActivityTaskCompletedRequest)(nil), // 11: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest + (*RespondActivityTaskFailedRequest)(nil), // 12: temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest + (*RespondActivityTaskCanceledRequest)(nil), // 13: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest + (*IsActivityTaskValidRequest)(nil), // 14: temporal.server.api.historyservice.v1.IsActivityTaskValidRequest + (*SignalWorkflowExecutionRequest)(nil), // 15: temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest + (*SignalWithStartWorkflowExecutionRequest)(nil), // 16: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest + (*ExecuteMultiOperationRequest)(nil), // 17: temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest + (*RemoveSignalMutableStateRequest)(nil), // 18: temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest + (*TerminateWorkflowExecutionRequest)(nil), // 19: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest + (*DeleteWorkflowExecutionRequest)(nil), // 20: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest + (*ResetWorkflowExecutionRequest)(nil), // 21: temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest + (*UpdateWorkflowExecutionOptionsRequest)(nil), // 22: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest + (*RequestCancelWorkflowExecutionRequest)(nil), // 23: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest + (*ScheduleWorkflowTaskRequest)(nil), // 24: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest + (*VerifyFirstWorkflowTaskScheduledRequest)(nil), // 25: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest + (*RecordChildExecutionCompletedRequest)(nil), // 26: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest + (*VerifyChildExecutionCompletionRecordedRequest)(nil), // 27: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest + (*DescribeWorkflowExecutionRequest)(nil), // 28: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest + (*ReplicateEventsV2Request)(nil), // 29: temporal.server.api.historyservice.v1.ReplicateEventsV2Request + (*ReplicateWorkflowStateRequest)(nil), // 30: temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest + (*SyncShardStatusRequest)(nil), // 31: temporal.server.api.historyservice.v1.SyncShardStatusRequest + (*SyncActivityRequest)(nil), // 32: temporal.server.api.historyservice.v1.SyncActivityRequest + (*DescribeMutableStateRequest)(nil), // 33: temporal.server.api.historyservice.v1.DescribeMutableStateRequest + (*DescribeHistoryHostRequest)(nil), // 34: temporal.server.api.historyservice.v1.DescribeHistoryHostRequest + (*CloseShardRequest)(nil), // 35: temporal.server.api.historyservice.v1.CloseShardRequest + (*GetShardRequest)(nil), // 36: temporal.server.api.historyservice.v1.GetShardRequest + (*RemoveTaskRequest)(nil), // 37: temporal.server.api.historyservice.v1.RemoveTaskRequest + (*GetReplicationMessagesRequest)(nil), // 38: temporal.server.api.historyservice.v1.GetReplicationMessagesRequest + (*GetDLQReplicationMessagesRequest)(nil), // 39: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest + (*QueryWorkflowRequest)(nil), // 40: temporal.server.api.historyservice.v1.QueryWorkflowRequest + (*ReapplyEventsRequest)(nil), // 41: temporal.server.api.historyservice.v1.ReapplyEventsRequest + (*GetDLQMessagesRequest)(nil), // 42: temporal.server.api.historyservice.v1.GetDLQMessagesRequest + (*PurgeDLQMessagesRequest)(nil), // 43: temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest + (*MergeDLQMessagesRequest)(nil), // 44: temporal.server.api.historyservice.v1.MergeDLQMessagesRequest + (*RefreshWorkflowTasksRequest)(nil), // 45: temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest + (*GenerateLastHistoryReplicationTasksRequest)(nil), // 46: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest + (*GetReplicationStatusRequest)(nil), // 47: temporal.server.api.historyservice.v1.GetReplicationStatusRequest + (*RebuildMutableStateRequest)(nil), // 48: temporal.server.api.historyservice.v1.RebuildMutableStateRequest + (*ImportWorkflowExecutionRequest)(nil), // 49: temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest + (*DeleteWorkflowVisibilityRecordRequest)(nil), // 50: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest + (*UpdateWorkflowExecutionRequest)(nil), // 51: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest + (*PollWorkflowExecutionUpdateRequest)(nil), // 52: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest + (*StreamWorkflowReplicationMessagesRequest)(nil), // 53: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest + (*GetWorkflowExecutionHistoryRequest)(nil), // 54: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest + (*GetWorkflowExecutionHistoryReverseRequest)(nil), // 55: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest + (*GetWorkflowExecutionRawHistoryV2Request)(nil), // 56: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request + (*GetWorkflowExecutionRawHistoryRequest)(nil), // 57: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest + (*ForceDeleteWorkflowExecutionRequest)(nil), // 58: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest + (*DeleteExecutionRequest)(nil), // 59: temporal.server.api.historyservice.v1.DeleteExecutionRequest + (*GetDLQTasksRequest)(nil), // 60: temporal.server.api.historyservice.v1.GetDLQTasksRequest + (*DeleteDLQTasksRequest)(nil), // 61: temporal.server.api.historyservice.v1.DeleteDLQTasksRequest + (*ListQueuesRequest)(nil), // 62: temporal.server.api.historyservice.v1.ListQueuesRequest + (*AddTasksRequest)(nil), // 63: temporal.server.api.historyservice.v1.AddTasksRequest + (*ListTasksRequest)(nil), // 64: temporal.server.api.historyservice.v1.ListTasksRequest + (*CompleteNexusOperationRequest)(nil), // 65: temporal.server.api.historyservice.v1.CompleteNexusOperationRequest + (*CompleteNexusOperationChasmRequest)(nil), // 66: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest + (*InvokeStateMachineMethodRequest)(nil), // 67: temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest + (*DeepHealthCheckRequest)(nil), // 68: temporal.server.api.historyservice.v1.DeepHealthCheckRequest + (*SyncWorkflowStateRequest)(nil), // 69: temporal.server.api.historyservice.v1.SyncWorkflowStateRequest + (*UpdateActivityOptionsRequest)(nil), // 70: temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest + (*PauseActivityRequest)(nil), // 71: temporal.server.api.historyservice.v1.PauseActivityRequest + (*UnpauseActivityRequest)(nil), // 72: temporal.server.api.historyservice.v1.UnpauseActivityRequest + (*ResetActivityRequest)(nil), // 73: temporal.server.api.historyservice.v1.ResetActivityRequest + (*PauseWorkflowExecutionRequest)(nil), // 74: temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest + (*UnpauseWorkflowExecutionRequest)(nil), // 75: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest + (*StartNexusOperationRequest)(nil), // 76: temporal.server.api.historyservice.v1.StartNexusOperationRequest + (*CancelNexusOperationRequest)(nil), // 77: temporal.server.api.historyservice.v1.CancelNexusOperationRequest + (*PollWorkflowExecutionTimeSkippingRequest)(nil), // 78: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest + (*StartWorkflowExecutionResponse)(nil), // 79: temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse + (*GetMutableStateResponse)(nil), // 80: temporal.server.api.historyservice.v1.GetMutableStateResponse + (*PollMutableStateResponse)(nil), // 81: temporal.server.api.historyservice.v1.PollMutableStateResponse + (*GetStreamReplaySlicesResponse)(nil), // 82: temporal.server.api.historyservice.v1.GetStreamReplaySlicesResponse + (*ResetStickyTaskQueueResponse)(nil), // 83: temporal.server.api.historyservice.v1.ResetStickyTaskQueueResponse + (*RecordWorkflowTaskStartedResponse)(nil), // 84: temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse + (*RecordActivityTaskStartedResponse)(nil), // 85: temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse + (*RespondWorkflowTaskCompletedResponse)(nil), // 86: temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse + (*RespondWorkflowTaskFailedResponse)(nil), // 87: temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedResponse + (*IsWorkflowTaskValidResponse)(nil), // 88: temporal.server.api.historyservice.v1.IsWorkflowTaskValidResponse + (*RecordActivityTaskHeartbeatResponse)(nil), // 89: temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatResponse + (*RespondActivityTaskCompletedResponse)(nil), // 90: temporal.server.api.historyservice.v1.RespondActivityTaskCompletedResponse + (*RespondActivityTaskFailedResponse)(nil), // 91: temporal.server.api.historyservice.v1.RespondActivityTaskFailedResponse + (*RespondActivityTaskCanceledResponse)(nil), // 92: temporal.server.api.historyservice.v1.RespondActivityTaskCanceledResponse + (*IsActivityTaskValidResponse)(nil), // 93: temporal.server.api.historyservice.v1.IsActivityTaskValidResponse + (*SignalWorkflowExecutionResponse)(nil), // 94: temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse + (*SignalWithStartWorkflowExecutionResponse)(nil), // 95: temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse + (*ExecuteMultiOperationResponse)(nil), // 96: temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse + (*RemoveSignalMutableStateResponse)(nil), // 97: temporal.server.api.historyservice.v1.RemoveSignalMutableStateResponse + (*TerminateWorkflowExecutionResponse)(nil), // 98: temporal.server.api.historyservice.v1.TerminateWorkflowExecutionResponse + (*DeleteWorkflowExecutionResponse)(nil), // 99: temporal.server.api.historyservice.v1.DeleteWorkflowExecutionResponse + (*ResetWorkflowExecutionResponse)(nil), // 100: temporal.server.api.historyservice.v1.ResetWorkflowExecutionResponse + (*UpdateWorkflowExecutionOptionsResponse)(nil), // 101: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse + (*RequestCancelWorkflowExecutionResponse)(nil), // 102: temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionResponse + (*ScheduleWorkflowTaskResponse)(nil), // 103: temporal.server.api.historyservice.v1.ScheduleWorkflowTaskResponse + (*VerifyFirstWorkflowTaskScheduledResponse)(nil), // 104: temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledResponse + (*RecordChildExecutionCompletedResponse)(nil), // 105: temporal.server.api.historyservice.v1.RecordChildExecutionCompletedResponse + (*VerifyChildExecutionCompletionRecordedResponse)(nil), // 106: temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedResponse + (*DescribeWorkflowExecutionResponse)(nil), // 107: temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse + (*ReplicateEventsV2Response)(nil), // 108: temporal.server.api.historyservice.v1.ReplicateEventsV2Response + (*ReplicateWorkflowStateResponse)(nil), // 109: temporal.server.api.historyservice.v1.ReplicateWorkflowStateResponse + (*SyncShardStatusResponse)(nil), // 110: temporal.server.api.historyservice.v1.SyncShardStatusResponse + (*SyncActivityResponse)(nil), // 111: temporal.server.api.historyservice.v1.SyncActivityResponse + (*DescribeMutableStateResponse)(nil), // 112: temporal.server.api.historyservice.v1.DescribeMutableStateResponse + (*DescribeHistoryHostResponse)(nil), // 113: temporal.server.api.historyservice.v1.DescribeHistoryHostResponse + (*CloseShardResponse)(nil), // 114: temporal.server.api.historyservice.v1.CloseShardResponse + (*GetShardResponse)(nil), // 115: temporal.server.api.historyservice.v1.GetShardResponse + (*RemoveTaskResponse)(nil), // 116: temporal.server.api.historyservice.v1.RemoveTaskResponse + (*GetReplicationMessagesResponse)(nil), // 117: temporal.server.api.historyservice.v1.GetReplicationMessagesResponse + (*GetDLQReplicationMessagesResponse)(nil), // 118: temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse + (*QueryWorkflowResponse)(nil), // 119: temporal.server.api.historyservice.v1.QueryWorkflowResponse + (*ReapplyEventsResponse)(nil), // 120: temporal.server.api.historyservice.v1.ReapplyEventsResponse + (*GetDLQMessagesResponse)(nil), // 121: temporal.server.api.historyservice.v1.GetDLQMessagesResponse + (*PurgeDLQMessagesResponse)(nil), // 122: temporal.server.api.historyservice.v1.PurgeDLQMessagesResponse + (*MergeDLQMessagesResponse)(nil), // 123: temporal.server.api.historyservice.v1.MergeDLQMessagesResponse + (*RefreshWorkflowTasksResponse)(nil), // 124: temporal.server.api.historyservice.v1.RefreshWorkflowTasksResponse + (*GenerateLastHistoryReplicationTasksResponse)(nil), // 125: temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksResponse + (*GetReplicationStatusResponse)(nil), // 126: temporal.server.api.historyservice.v1.GetReplicationStatusResponse + (*RebuildMutableStateResponse)(nil), // 127: temporal.server.api.historyservice.v1.RebuildMutableStateResponse + (*ImportWorkflowExecutionResponse)(nil), // 128: temporal.server.api.historyservice.v1.ImportWorkflowExecutionResponse + (*DeleteWorkflowVisibilityRecordResponse)(nil), // 129: temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordResponse + (*UpdateWorkflowExecutionResponse)(nil), // 130: temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse + (*PollWorkflowExecutionUpdateResponse)(nil), // 131: temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse + (*StreamWorkflowReplicationMessagesResponse)(nil), // 132: temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse + (*GetWorkflowExecutionHistoryResponse)(nil), // 133: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse + (*GetWorkflowExecutionHistoryReverseResponse)(nil), // 134: temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse + (*GetWorkflowExecutionRawHistoryV2Response)(nil), // 135: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response + (*GetWorkflowExecutionRawHistoryResponse)(nil), // 136: temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse + (*ForceDeleteWorkflowExecutionResponse)(nil), // 137: temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse + (*DeleteExecutionResponse)(nil), // 138: temporal.server.api.historyservice.v1.DeleteExecutionResponse + (*GetDLQTasksResponse)(nil), // 139: temporal.server.api.historyservice.v1.GetDLQTasksResponse + (*DeleteDLQTasksResponse)(nil), // 140: temporal.server.api.historyservice.v1.DeleteDLQTasksResponse + (*ListQueuesResponse)(nil), // 141: temporal.server.api.historyservice.v1.ListQueuesResponse + (*AddTasksResponse)(nil), // 142: temporal.server.api.historyservice.v1.AddTasksResponse + (*ListTasksResponse)(nil), // 143: temporal.server.api.historyservice.v1.ListTasksResponse + (*CompleteNexusOperationResponse)(nil), // 144: temporal.server.api.historyservice.v1.CompleteNexusOperationResponse + (*CompleteNexusOperationChasmResponse)(nil), // 145: temporal.server.api.historyservice.v1.CompleteNexusOperationChasmResponse + (*InvokeStateMachineMethodResponse)(nil), // 146: temporal.server.api.historyservice.v1.InvokeStateMachineMethodResponse + (*DeepHealthCheckResponse)(nil), // 147: temporal.server.api.historyservice.v1.DeepHealthCheckResponse + (*SyncWorkflowStateResponse)(nil), // 148: temporal.server.api.historyservice.v1.SyncWorkflowStateResponse + (*UpdateActivityOptionsResponse)(nil), // 149: temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse + (*PauseActivityResponse)(nil), // 150: temporal.server.api.historyservice.v1.PauseActivityResponse + (*UnpauseActivityResponse)(nil), // 151: temporal.server.api.historyservice.v1.UnpauseActivityResponse + (*ResetActivityResponse)(nil), // 152: temporal.server.api.historyservice.v1.ResetActivityResponse + (*PauseWorkflowExecutionResponse)(nil), // 153: temporal.server.api.historyservice.v1.PauseWorkflowExecutionResponse + (*UnpauseWorkflowExecutionResponse)(nil), // 154: temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionResponse + (*StartNexusOperationResponse)(nil), // 155: temporal.server.api.historyservice.v1.StartNexusOperationResponse + (*CancelNexusOperationResponse)(nil), // 156: temporal.server.api.historyservice.v1.CancelNexusOperationResponse + (*PollWorkflowExecutionTimeSkippingResponse)(nil), // 157: temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse } var file_temporal_server_api_historyservice_v1_service_proto_depIdxs = []int32{ 0, // 0: temporal.server.api.historyservice.v1.HistoryService.StartWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.StartWorkflowExecutionRequest 1, // 1: temporal.server.api.historyservice.v1.HistoryService.GetMutableState:input_type -> temporal.server.api.historyservice.v1.GetMutableStateRequest 2, // 2: temporal.server.api.historyservice.v1.HistoryService.PollMutableState:input_type -> temporal.server.api.historyservice.v1.PollMutableStateRequest - 3, // 3: temporal.server.api.historyservice.v1.HistoryService.ResetStickyTaskQueue:input_type -> temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest - 4, // 4: temporal.server.api.historyservice.v1.HistoryService.RecordWorkflowTaskStarted:input_type -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest - 5, // 5: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskStarted:input_type -> temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest - 6, // 6: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskCompleted:input_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest - 7, // 7: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskFailed:input_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest - 8, // 8: temporal.server.api.historyservice.v1.HistoryService.IsWorkflowTaskValid:input_type -> temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest - 9, // 9: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskHeartbeat:input_type -> temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest - 10, // 10: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCompleted:input_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest - 11, // 11: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskFailed:input_type -> temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest - 12, // 12: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCanceled:input_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest - 13, // 13: temporal.server.api.historyservice.v1.HistoryService.IsActivityTaskValid:input_type -> temporal.server.api.historyservice.v1.IsActivityTaskValidRequest - 14, // 14: temporal.server.api.historyservice.v1.HistoryService.SignalWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest - 15, // 15: temporal.server.api.historyservice.v1.HistoryService.SignalWithStartWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest - 16, // 16: temporal.server.api.historyservice.v1.HistoryService.ExecuteMultiOperation:input_type -> temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest - 17, // 17: temporal.server.api.historyservice.v1.HistoryService.RemoveSignalMutableState:input_type -> temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest - 18, // 18: temporal.server.api.historyservice.v1.HistoryService.TerminateWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest - 19, // 19: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest - 20, // 20: temporal.server.api.historyservice.v1.HistoryService.ResetWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest - 21, // 21: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecutionOptions:input_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest - 22, // 22: temporal.server.api.historyservice.v1.HistoryService.RequestCancelWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest - 23, // 23: temporal.server.api.historyservice.v1.HistoryService.ScheduleWorkflowTask:input_type -> temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest - 24, // 24: temporal.server.api.historyservice.v1.HistoryService.VerifyFirstWorkflowTaskScheduled:input_type -> temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest - 25, // 25: temporal.server.api.historyservice.v1.HistoryService.RecordChildExecutionCompleted:input_type -> temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest - 26, // 26: temporal.server.api.historyservice.v1.HistoryService.VerifyChildExecutionCompletionRecorded:input_type -> temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest - 27, // 27: temporal.server.api.historyservice.v1.HistoryService.DescribeWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest - 28, // 28: temporal.server.api.historyservice.v1.HistoryService.ReplicateEventsV2:input_type -> temporal.server.api.historyservice.v1.ReplicateEventsV2Request - 29, // 29: temporal.server.api.historyservice.v1.HistoryService.ReplicateWorkflowState:input_type -> temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest - 30, // 30: temporal.server.api.historyservice.v1.HistoryService.SyncShardStatus:input_type -> temporal.server.api.historyservice.v1.SyncShardStatusRequest - 31, // 31: temporal.server.api.historyservice.v1.HistoryService.SyncActivity:input_type -> temporal.server.api.historyservice.v1.SyncActivityRequest - 32, // 32: temporal.server.api.historyservice.v1.HistoryService.DescribeMutableState:input_type -> temporal.server.api.historyservice.v1.DescribeMutableStateRequest - 33, // 33: temporal.server.api.historyservice.v1.HistoryService.DescribeHistoryHost:input_type -> temporal.server.api.historyservice.v1.DescribeHistoryHostRequest - 34, // 34: temporal.server.api.historyservice.v1.HistoryService.CloseShard:input_type -> temporal.server.api.historyservice.v1.CloseShardRequest - 35, // 35: temporal.server.api.historyservice.v1.HistoryService.GetShard:input_type -> temporal.server.api.historyservice.v1.GetShardRequest - 36, // 36: temporal.server.api.historyservice.v1.HistoryService.RemoveTask:input_type -> temporal.server.api.historyservice.v1.RemoveTaskRequest - 37, // 37: temporal.server.api.historyservice.v1.HistoryService.GetReplicationMessages:input_type -> temporal.server.api.historyservice.v1.GetReplicationMessagesRequest - 38, // 38: temporal.server.api.historyservice.v1.HistoryService.GetDLQReplicationMessages:input_type -> temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest - 39, // 39: temporal.server.api.historyservice.v1.HistoryService.QueryWorkflow:input_type -> temporal.server.api.historyservice.v1.QueryWorkflowRequest - 40, // 40: temporal.server.api.historyservice.v1.HistoryService.ReapplyEvents:input_type -> temporal.server.api.historyservice.v1.ReapplyEventsRequest - 41, // 41: temporal.server.api.historyservice.v1.HistoryService.GetDLQMessages:input_type -> temporal.server.api.historyservice.v1.GetDLQMessagesRequest - 42, // 42: temporal.server.api.historyservice.v1.HistoryService.PurgeDLQMessages:input_type -> temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest - 43, // 43: temporal.server.api.historyservice.v1.HistoryService.MergeDLQMessages:input_type -> temporal.server.api.historyservice.v1.MergeDLQMessagesRequest - 44, // 44: temporal.server.api.historyservice.v1.HistoryService.RefreshWorkflowTasks:input_type -> temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest - 45, // 45: temporal.server.api.historyservice.v1.HistoryService.GenerateLastHistoryReplicationTasks:input_type -> temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest - 46, // 46: temporal.server.api.historyservice.v1.HistoryService.GetReplicationStatus:input_type -> temporal.server.api.historyservice.v1.GetReplicationStatusRequest - 47, // 47: temporal.server.api.historyservice.v1.HistoryService.RebuildMutableState:input_type -> temporal.server.api.historyservice.v1.RebuildMutableStateRequest - 48, // 48: temporal.server.api.historyservice.v1.HistoryService.ImportWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest - 49, // 49: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowVisibilityRecord:input_type -> temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest - 50, // 50: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest - 51, // 51: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionUpdate:input_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest - 52, // 52: temporal.server.api.historyservice.v1.HistoryService.StreamWorkflowReplicationMessages:input_type -> temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest - 53, // 53: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistory:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest - 54, // 54: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistoryReverse:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest - 55, // 55: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistoryV2:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request - 56, // 56: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistory:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest - 57, // 57: temporal.server.api.historyservice.v1.HistoryService.ForceDeleteWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest - 58, // 58: temporal.server.api.historyservice.v1.HistoryService.DeleteExecution:input_type -> temporal.server.api.historyservice.v1.DeleteExecutionRequest - 59, // 59: temporal.server.api.historyservice.v1.HistoryService.GetDLQTasks:input_type -> temporal.server.api.historyservice.v1.GetDLQTasksRequest - 60, // 60: temporal.server.api.historyservice.v1.HistoryService.DeleteDLQTasks:input_type -> temporal.server.api.historyservice.v1.DeleteDLQTasksRequest - 61, // 61: temporal.server.api.historyservice.v1.HistoryService.ListQueues:input_type -> temporal.server.api.historyservice.v1.ListQueuesRequest - 62, // 62: temporal.server.api.historyservice.v1.HistoryService.AddTasks:input_type -> temporal.server.api.historyservice.v1.AddTasksRequest - 63, // 63: temporal.server.api.historyservice.v1.HistoryService.ListTasks:input_type -> temporal.server.api.historyservice.v1.ListTasksRequest - 64, // 64: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperation:input_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationRequest - 65, // 65: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperationChasm:input_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest - 66, // 66: temporal.server.api.historyservice.v1.HistoryService.InvokeStateMachineMethod:input_type -> temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest - 67, // 67: temporal.server.api.historyservice.v1.HistoryService.DeepHealthCheck:input_type -> temporal.server.api.historyservice.v1.DeepHealthCheckRequest - 68, // 68: temporal.server.api.historyservice.v1.HistoryService.SyncWorkflowState:input_type -> temporal.server.api.historyservice.v1.SyncWorkflowStateRequest - 69, // 69: temporal.server.api.historyservice.v1.HistoryService.UpdateActivityOptions:input_type -> temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest - 70, // 70: temporal.server.api.historyservice.v1.HistoryService.PauseActivity:input_type -> temporal.server.api.historyservice.v1.PauseActivityRequest - 71, // 71: temporal.server.api.historyservice.v1.HistoryService.UnpauseActivity:input_type -> temporal.server.api.historyservice.v1.UnpauseActivityRequest - 72, // 72: temporal.server.api.historyservice.v1.HistoryService.ResetActivity:input_type -> temporal.server.api.historyservice.v1.ResetActivityRequest - 73, // 73: temporal.server.api.historyservice.v1.HistoryService.PauseWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest - 74, // 74: temporal.server.api.historyservice.v1.HistoryService.UnpauseWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest - 75, // 75: temporal.server.api.historyservice.v1.HistoryService.StartNexusOperation:input_type -> temporal.server.api.historyservice.v1.StartNexusOperationRequest - 76, // 76: temporal.server.api.historyservice.v1.HistoryService.CancelNexusOperation:input_type -> temporal.server.api.historyservice.v1.CancelNexusOperationRequest - 77, // 77: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionTimeSkipping:input_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest - 78, // 78: temporal.server.api.historyservice.v1.HistoryService.StartWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse - 79, // 79: temporal.server.api.historyservice.v1.HistoryService.GetMutableState:output_type -> temporal.server.api.historyservice.v1.GetMutableStateResponse - 80, // 80: temporal.server.api.historyservice.v1.HistoryService.PollMutableState:output_type -> temporal.server.api.historyservice.v1.PollMutableStateResponse - 81, // 81: temporal.server.api.historyservice.v1.HistoryService.ResetStickyTaskQueue:output_type -> temporal.server.api.historyservice.v1.ResetStickyTaskQueueResponse - 82, // 82: temporal.server.api.historyservice.v1.HistoryService.RecordWorkflowTaskStarted:output_type -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse - 83, // 83: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskStarted:output_type -> temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse - 84, // 84: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskCompleted:output_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse - 85, // 85: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskFailed:output_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedResponse - 86, // 86: temporal.server.api.historyservice.v1.HistoryService.IsWorkflowTaskValid:output_type -> temporal.server.api.historyservice.v1.IsWorkflowTaskValidResponse - 87, // 87: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskHeartbeat:output_type -> temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatResponse - 88, // 88: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCompleted:output_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCompletedResponse - 89, // 89: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskFailed:output_type -> temporal.server.api.historyservice.v1.RespondActivityTaskFailedResponse - 90, // 90: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCanceled:output_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCanceledResponse - 91, // 91: temporal.server.api.historyservice.v1.HistoryService.IsActivityTaskValid:output_type -> temporal.server.api.historyservice.v1.IsActivityTaskValidResponse - 92, // 92: temporal.server.api.historyservice.v1.HistoryService.SignalWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse - 93, // 93: temporal.server.api.historyservice.v1.HistoryService.SignalWithStartWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse - 94, // 94: temporal.server.api.historyservice.v1.HistoryService.ExecuteMultiOperation:output_type -> temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse - 95, // 95: temporal.server.api.historyservice.v1.HistoryService.RemoveSignalMutableState:output_type -> temporal.server.api.historyservice.v1.RemoveSignalMutableStateResponse - 96, // 96: temporal.server.api.historyservice.v1.HistoryService.TerminateWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.TerminateWorkflowExecutionResponse - 97, // 97: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.DeleteWorkflowExecutionResponse - 98, // 98: temporal.server.api.historyservice.v1.HistoryService.ResetWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.ResetWorkflowExecutionResponse - 99, // 99: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecutionOptions:output_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse - 100, // 100: temporal.server.api.historyservice.v1.HistoryService.RequestCancelWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionResponse - 101, // 101: temporal.server.api.historyservice.v1.HistoryService.ScheduleWorkflowTask:output_type -> temporal.server.api.historyservice.v1.ScheduleWorkflowTaskResponse - 102, // 102: temporal.server.api.historyservice.v1.HistoryService.VerifyFirstWorkflowTaskScheduled:output_type -> temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledResponse - 103, // 103: temporal.server.api.historyservice.v1.HistoryService.RecordChildExecutionCompleted:output_type -> temporal.server.api.historyservice.v1.RecordChildExecutionCompletedResponse - 104, // 104: temporal.server.api.historyservice.v1.HistoryService.VerifyChildExecutionCompletionRecorded:output_type -> temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedResponse - 105, // 105: temporal.server.api.historyservice.v1.HistoryService.DescribeWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse - 106, // 106: temporal.server.api.historyservice.v1.HistoryService.ReplicateEventsV2:output_type -> temporal.server.api.historyservice.v1.ReplicateEventsV2Response - 107, // 107: temporal.server.api.historyservice.v1.HistoryService.ReplicateWorkflowState:output_type -> temporal.server.api.historyservice.v1.ReplicateWorkflowStateResponse - 108, // 108: temporal.server.api.historyservice.v1.HistoryService.SyncShardStatus:output_type -> temporal.server.api.historyservice.v1.SyncShardStatusResponse - 109, // 109: temporal.server.api.historyservice.v1.HistoryService.SyncActivity:output_type -> temporal.server.api.historyservice.v1.SyncActivityResponse - 110, // 110: temporal.server.api.historyservice.v1.HistoryService.DescribeMutableState:output_type -> temporal.server.api.historyservice.v1.DescribeMutableStateResponse - 111, // 111: temporal.server.api.historyservice.v1.HistoryService.DescribeHistoryHost:output_type -> temporal.server.api.historyservice.v1.DescribeHistoryHostResponse - 112, // 112: temporal.server.api.historyservice.v1.HistoryService.CloseShard:output_type -> temporal.server.api.historyservice.v1.CloseShardResponse - 113, // 113: temporal.server.api.historyservice.v1.HistoryService.GetShard:output_type -> temporal.server.api.historyservice.v1.GetShardResponse - 114, // 114: temporal.server.api.historyservice.v1.HistoryService.RemoveTask:output_type -> temporal.server.api.historyservice.v1.RemoveTaskResponse - 115, // 115: temporal.server.api.historyservice.v1.HistoryService.GetReplicationMessages:output_type -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse - 116, // 116: temporal.server.api.historyservice.v1.HistoryService.GetDLQReplicationMessages:output_type -> temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse - 117, // 117: temporal.server.api.historyservice.v1.HistoryService.QueryWorkflow:output_type -> temporal.server.api.historyservice.v1.QueryWorkflowResponse - 118, // 118: temporal.server.api.historyservice.v1.HistoryService.ReapplyEvents:output_type -> temporal.server.api.historyservice.v1.ReapplyEventsResponse - 119, // 119: temporal.server.api.historyservice.v1.HistoryService.GetDLQMessages:output_type -> temporal.server.api.historyservice.v1.GetDLQMessagesResponse - 120, // 120: temporal.server.api.historyservice.v1.HistoryService.PurgeDLQMessages:output_type -> temporal.server.api.historyservice.v1.PurgeDLQMessagesResponse - 121, // 121: temporal.server.api.historyservice.v1.HistoryService.MergeDLQMessages:output_type -> temporal.server.api.historyservice.v1.MergeDLQMessagesResponse - 122, // 122: temporal.server.api.historyservice.v1.HistoryService.RefreshWorkflowTasks:output_type -> temporal.server.api.historyservice.v1.RefreshWorkflowTasksResponse - 123, // 123: temporal.server.api.historyservice.v1.HistoryService.GenerateLastHistoryReplicationTasks:output_type -> temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksResponse - 124, // 124: temporal.server.api.historyservice.v1.HistoryService.GetReplicationStatus:output_type -> temporal.server.api.historyservice.v1.GetReplicationStatusResponse - 125, // 125: temporal.server.api.historyservice.v1.HistoryService.RebuildMutableState:output_type -> temporal.server.api.historyservice.v1.RebuildMutableStateResponse - 126, // 126: temporal.server.api.historyservice.v1.HistoryService.ImportWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.ImportWorkflowExecutionResponse - 127, // 127: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowVisibilityRecord:output_type -> temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordResponse - 128, // 128: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse - 129, // 129: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionUpdate:output_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse - 130, // 130: temporal.server.api.historyservice.v1.HistoryService.StreamWorkflowReplicationMessages:output_type -> temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse - 131, // 131: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistory:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse - 132, // 132: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistoryReverse:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse - 133, // 133: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistoryV2:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response - 134, // 134: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistory:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse - 135, // 135: temporal.server.api.historyservice.v1.HistoryService.ForceDeleteWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse - 136, // 136: temporal.server.api.historyservice.v1.HistoryService.DeleteExecution:output_type -> temporal.server.api.historyservice.v1.DeleteExecutionResponse - 137, // 137: temporal.server.api.historyservice.v1.HistoryService.GetDLQTasks:output_type -> temporal.server.api.historyservice.v1.GetDLQTasksResponse - 138, // 138: temporal.server.api.historyservice.v1.HistoryService.DeleteDLQTasks:output_type -> temporal.server.api.historyservice.v1.DeleteDLQTasksResponse - 139, // 139: temporal.server.api.historyservice.v1.HistoryService.ListQueues:output_type -> temporal.server.api.historyservice.v1.ListQueuesResponse - 140, // 140: temporal.server.api.historyservice.v1.HistoryService.AddTasks:output_type -> temporal.server.api.historyservice.v1.AddTasksResponse - 141, // 141: temporal.server.api.historyservice.v1.HistoryService.ListTasks:output_type -> temporal.server.api.historyservice.v1.ListTasksResponse - 142, // 142: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperation:output_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationResponse - 143, // 143: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperationChasm:output_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationChasmResponse - 144, // 144: temporal.server.api.historyservice.v1.HistoryService.InvokeStateMachineMethod:output_type -> temporal.server.api.historyservice.v1.InvokeStateMachineMethodResponse - 145, // 145: temporal.server.api.historyservice.v1.HistoryService.DeepHealthCheck:output_type -> temporal.server.api.historyservice.v1.DeepHealthCheckResponse - 146, // 146: temporal.server.api.historyservice.v1.HistoryService.SyncWorkflowState:output_type -> temporal.server.api.historyservice.v1.SyncWorkflowStateResponse - 147, // 147: temporal.server.api.historyservice.v1.HistoryService.UpdateActivityOptions:output_type -> temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse - 148, // 148: temporal.server.api.historyservice.v1.HistoryService.PauseActivity:output_type -> temporal.server.api.historyservice.v1.PauseActivityResponse - 149, // 149: temporal.server.api.historyservice.v1.HistoryService.UnpauseActivity:output_type -> temporal.server.api.historyservice.v1.UnpauseActivityResponse - 150, // 150: temporal.server.api.historyservice.v1.HistoryService.ResetActivity:output_type -> temporal.server.api.historyservice.v1.ResetActivityResponse - 151, // 151: temporal.server.api.historyservice.v1.HistoryService.PauseWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.PauseWorkflowExecutionResponse - 152, // 152: temporal.server.api.historyservice.v1.HistoryService.UnpauseWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionResponse - 153, // 153: temporal.server.api.historyservice.v1.HistoryService.StartNexusOperation:output_type -> temporal.server.api.historyservice.v1.StartNexusOperationResponse - 154, // 154: temporal.server.api.historyservice.v1.HistoryService.CancelNexusOperation:output_type -> temporal.server.api.historyservice.v1.CancelNexusOperationResponse - 155, // 155: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionTimeSkipping:output_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse - 78, // [78:156] is the sub-list for method output_type - 0, // [0:78] is the sub-list for method input_type + 3, // 3: temporal.server.api.historyservice.v1.HistoryService.GetStreamReplaySlices:input_type -> temporal.server.api.historyservice.v1.GetStreamReplaySlicesRequest + 4, // 4: temporal.server.api.historyservice.v1.HistoryService.ResetStickyTaskQueue:input_type -> temporal.server.api.historyservice.v1.ResetStickyTaskQueueRequest + 5, // 5: temporal.server.api.historyservice.v1.HistoryService.RecordWorkflowTaskStarted:input_type -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedRequest + 6, // 6: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskStarted:input_type -> temporal.server.api.historyservice.v1.RecordActivityTaskStartedRequest + 7, // 7: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskCompleted:input_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedRequest + 8, // 8: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskFailed:input_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedRequest + 9, // 9: temporal.server.api.historyservice.v1.HistoryService.IsWorkflowTaskValid:input_type -> temporal.server.api.historyservice.v1.IsWorkflowTaskValidRequest + 10, // 10: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskHeartbeat:input_type -> temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatRequest + 11, // 11: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCompleted:input_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCompletedRequest + 12, // 12: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskFailed:input_type -> temporal.server.api.historyservice.v1.RespondActivityTaskFailedRequest + 13, // 13: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCanceled:input_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCanceledRequest + 14, // 14: temporal.server.api.historyservice.v1.HistoryService.IsActivityTaskValid:input_type -> temporal.server.api.historyservice.v1.IsActivityTaskValidRequest + 15, // 15: temporal.server.api.historyservice.v1.HistoryService.SignalWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.SignalWorkflowExecutionRequest + 16, // 16: temporal.server.api.historyservice.v1.HistoryService.SignalWithStartWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionRequest + 17, // 17: temporal.server.api.historyservice.v1.HistoryService.ExecuteMultiOperation:input_type -> temporal.server.api.historyservice.v1.ExecuteMultiOperationRequest + 18, // 18: temporal.server.api.historyservice.v1.HistoryService.RemoveSignalMutableState:input_type -> temporal.server.api.historyservice.v1.RemoveSignalMutableStateRequest + 19, // 19: temporal.server.api.historyservice.v1.HistoryService.TerminateWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.TerminateWorkflowExecutionRequest + 20, // 20: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.DeleteWorkflowExecutionRequest + 21, // 21: temporal.server.api.historyservice.v1.HistoryService.ResetWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.ResetWorkflowExecutionRequest + 22, // 22: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecutionOptions:input_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsRequest + 23, // 23: temporal.server.api.historyservice.v1.HistoryService.RequestCancelWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionRequest + 24, // 24: temporal.server.api.historyservice.v1.HistoryService.ScheduleWorkflowTask:input_type -> temporal.server.api.historyservice.v1.ScheduleWorkflowTaskRequest + 25, // 25: temporal.server.api.historyservice.v1.HistoryService.VerifyFirstWorkflowTaskScheduled:input_type -> temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledRequest + 26, // 26: temporal.server.api.historyservice.v1.HistoryService.RecordChildExecutionCompleted:input_type -> temporal.server.api.historyservice.v1.RecordChildExecutionCompletedRequest + 27, // 27: temporal.server.api.historyservice.v1.HistoryService.VerifyChildExecutionCompletionRecorded:input_type -> temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedRequest + 28, // 28: temporal.server.api.historyservice.v1.HistoryService.DescribeWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.DescribeWorkflowExecutionRequest + 29, // 29: temporal.server.api.historyservice.v1.HistoryService.ReplicateEventsV2:input_type -> temporal.server.api.historyservice.v1.ReplicateEventsV2Request + 30, // 30: temporal.server.api.historyservice.v1.HistoryService.ReplicateWorkflowState:input_type -> temporal.server.api.historyservice.v1.ReplicateWorkflowStateRequest + 31, // 31: temporal.server.api.historyservice.v1.HistoryService.SyncShardStatus:input_type -> temporal.server.api.historyservice.v1.SyncShardStatusRequest + 32, // 32: temporal.server.api.historyservice.v1.HistoryService.SyncActivity:input_type -> temporal.server.api.historyservice.v1.SyncActivityRequest + 33, // 33: temporal.server.api.historyservice.v1.HistoryService.DescribeMutableState:input_type -> temporal.server.api.historyservice.v1.DescribeMutableStateRequest + 34, // 34: temporal.server.api.historyservice.v1.HistoryService.DescribeHistoryHost:input_type -> temporal.server.api.historyservice.v1.DescribeHistoryHostRequest + 35, // 35: temporal.server.api.historyservice.v1.HistoryService.CloseShard:input_type -> temporal.server.api.historyservice.v1.CloseShardRequest + 36, // 36: temporal.server.api.historyservice.v1.HistoryService.GetShard:input_type -> temporal.server.api.historyservice.v1.GetShardRequest + 37, // 37: temporal.server.api.historyservice.v1.HistoryService.RemoveTask:input_type -> temporal.server.api.historyservice.v1.RemoveTaskRequest + 38, // 38: temporal.server.api.historyservice.v1.HistoryService.GetReplicationMessages:input_type -> temporal.server.api.historyservice.v1.GetReplicationMessagesRequest + 39, // 39: temporal.server.api.historyservice.v1.HistoryService.GetDLQReplicationMessages:input_type -> temporal.server.api.historyservice.v1.GetDLQReplicationMessagesRequest + 40, // 40: temporal.server.api.historyservice.v1.HistoryService.QueryWorkflow:input_type -> temporal.server.api.historyservice.v1.QueryWorkflowRequest + 41, // 41: temporal.server.api.historyservice.v1.HistoryService.ReapplyEvents:input_type -> temporal.server.api.historyservice.v1.ReapplyEventsRequest + 42, // 42: temporal.server.api.historyservice.v1.HistoryService.GetDLQMessages:input_type -> temporal.server.api.historyservice.v1.GetDLQMessagesRequest + 43, // 43: temporal.server.api.historyservice.v1.HistoryService.PurgeDLQMessages:input_type -> temporal.server.api.historyservice.v1.PurgeDLQMessagesRequest + 44, // 44: temporal.server.api.historyservice.v1.HistoryService.MergeDLQMessages:input_type -> temporal.server.api.historyservice.v1.MergeDLQMessagesRequest + 45, // 45: temporal.server.api.historyservice.v1.HistoryService.RefreshWorkflowTasks:input_type -> temporal.server.api.historyservice.v1.RefreshWorkflowTasksRequest + 46, // 46: temporal.server.api.historyservice.v1.HistoryService.GenerateLastHistoryReplicationTasks:input_type -> temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksRequest + 47, // 47: temporal.server.api.historyservice.v1.HistoryService.GetReplicationStatus:input_type -> temporal.server.api.historyservice.v1.GetReplicationStatusRequest + 48, // 48: temporal.server.api.historyservice.v1.HistoryService.RebuildMutableState:input_type -> temporal.server.api.historyservice.v1.RebuildMutableStateRequest + 49, // 49: temporal.server.api.historyservice.v1.HistoryService.ImportWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.ImportWorkflowExecutionRequest + 50, // 50: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowVisibilityRecord:input_type -> temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordRequest + 51, // 51: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionRequest + 52, // 52: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionUpdate:input_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateRequest + 53, // 53: temporal.server.api.historyservice.v1.HistoryService.StreamWorkflowReplicationMessages:input_type -> temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesRequest + 54, // 54: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistory:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryRequest + 55, // 55: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistoryReverse:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseRequest + 56, // 56: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistoryV2:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Request + 57, // 57: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistory:input_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryRequest + 58, // 58: temporal.server.api.historyservice.v1.HistoryService.ForceDeleteWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionRequest + 59, // 59: temporal.server.api.historyservice.v1.HistoryService.DeleteExecution:input_type -> temporal.server.api.historyservice.v1.DeleteExecutionRequest + 60, // 60: temporal.server.api.historyservice.v1.HistoryService.GetDLQTasks:input_type -> temporal.server.api.historyservice.v1.GetDLQTasksRequest + 61, // 61: temporal.server.api.historyservice.v1.HistoryService.DeleteDLQTasks:input_type -> temporal.server.api.historyservice.v1.DeleteDLQTasksRequest + 62, // 62: temporal.server.api.historyservice.v1.HistoryService.ListQueues:input_type -> temporal.server.api.historyservice.v1.ListQueuesRequest + 63, // 63: temporal.server.api.historyservice.v1.HistoryService.AddTasks:input_type -> temporal.server.api.historyservice.v1.AddTasksRequest + 64, // 64: temporal.server.api.historyservice.v1.HistoryService.ListTasks:input_type -> temporal.server.api.historyservice.v1.ListTasksRequest + 65, // 65: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperation:input_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationRequest + 66, // 66: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperationChasm:input_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationChasmRequest + 67, // 67: temporal.server.api.historyservice.v1.HistoryService.InvokeStateMachineMethod:input_type -> temporal.server.api.historyservice.v1.InvokeStateMachineMethodRequest + 68, // 68: temporal.server.api.historyservice.v1.HistoryService.DeepHealthCheck:input_type -> temporal.server.api.historyservice.v1.DeepHealthCheckRequest + 69, // 69: temporal.server.api.historyservice.v1.HistoryService.SyncWorkflowState:input_type -> temporal.server.api.historyservice.v1.SyncWorkflowStateRequest + 70, // 70: temporal.server.api.historyservice.v1.HistoryService.UpdateActivityOptions:input_type -> temporal.server.api.historyservice.v1.UpdateActivityOptionsRequest + 71, // 71: temporal.server.api.historyservice.v1.HistoryService.PauseActivity:input_type -> temporal.server.api.historyservice.v1.PauseActivityRequest + 72, // 72: temporal.server.api.historyservice.v1.HistoryService.UnpauseActivity:input_type -> temporal.server.api.historyservice.v1.UnpauseActivityRequest + 73, // 73: temporal.server.api.historyservice.v1.HistoryService.ResetActivity:input_type -> temporal.server.api.historyservice.v1.ResetActivityRequest + 74, // 74: temporal.server.api.historyservice.v1.HistoryService.PauseWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.PauseWorkflowExecutionRequest + 75, // 75: temporal.server.api.historyservice.v1.HistoryService.UnpauseWorkflowExecution:input_type -> temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionRequest + 76, // 76: temporal.server.api.historyservice.v1.HistoryService.StartNexusOperation:input_type -> temporal.server.api.historyservice.v1.StartNexusOperationRequest + 77, // 77: temporal.server.api.historyservice.v1.HistoryService.CancelNexusOperation:input_type -> temporal.server.api.historyservice.v1.CancelNexusOperationRequest + 78, // 78: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionTimeSkipping:input_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingRequest + 79, // 79: temporal.server.api.historyservice.v1.HistoryService.StartWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.StartWorkflowExecutionResponse + 80, // 80: temporal.server.api.historyservice.v1.HistoryService.GetMutableState:output_type -> temporal.server.api.historyservice.v1.GetMutableStateResponse + 81, // 81: temporal.server.api.historyservice.v1.HistoryService.PollMutableState:output_type -> temporal.server.api.historyservice.v1.PollMutableStateResponse + 82, // 82: temporal.server.api.historyservice.v1.HistoryService.GetStreamReplaySlices:output_type -> temporal.server.api.historyservice.v1.GetStreamReplaySlicesResponse + 83, // 83: temporal.server.api.historyservice.v1.HistoryService.ResetStickyTaskQueue:output_type -> temporal.server.api.historyservice.v1.ResetStickyTaskQueueResponse + 84, // 84: temporal.server.api.historyservice.v1.HistoryService.RecordWorkflowTaskStarted:output_type -> temporal.server.api.historyservice.v1.RecordWorkflowTaskStartedResponse + 85, // 85: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskStarted:output_type -> temporal.server.api.historyservice.v1.RecordActivityTaskStartedResponse + 86, // 86: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskCompleted:output_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskCompletedResponse + 87, // 87: temporal.server.api.historyservice.v1.HistoryService.RespondWorkflowTaskFailed:output_type -> temporal.server.api.historyservice.v1.RespondWorkflowTaskFailedResponse + 88, // 88: temporal.server.api.historyservice.v1.HistoryService.IsWorkflowTaskValid:output_type -> temporal.server.api.historyservice.v1.IsWorkflowTaskValidResponse + 89, // 89: temporal.server.api.historyservice.v1.HistoryService.RecordActivityTaskHeartbeat:output_type -> temporal.server.api.historyservice.v1.RecordActivityTaskHeartbeatResponse + 90, // 90: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCompleted:output_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCompletedResponse + 91, // 91: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskFailed:output_type -> temporal.server.api.historyservice.v1.RespondActivityTaskFailedResponse + 92, // 92: temporal.server.api.historyservice.v1.HistoryService.RespondActivityTaskCanceled:output_type -> temporal.server.api.historyservice.v1.RespondActivityTaskCanceledResponse + 93, // 93: temporal.server.api.historyservice.v1.HistoryService.IsActivityTaskValid:output_type -> temporal.server.api.historyservice.v1.IsActivityTaskValidResponse + 94, // 94: temporal.server.api.historyservice.v1.HistoryService.SignalWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.SignalWorkflowExecutionResponse + 95, // 95: temporal.server.api.historyservice.v1.HistoryService.SignalWithStartWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.SignalWithStartWorkflowExecutionResponse + 96, // 96: temporal.server.api.historyservice.v1.HistoryService.ExecuteMultiOperation:output_type -> temporal.server.api.historyservice.v1.ExecuteMultiOperationResponse + 97, // 97: temporal.server.api.historyservice.v1.HistoryService.RemoveSignalMutableState:output_type -> temporal.server.api.historyservice.v1.RemoveSignalMutableStateResponse + 98, // 98: temporal.server.api.historyservice.v1.HistoryService.TerminateWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.TerminateWorkflowExecutionResponse + 99, // 99: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.DeleteWorkflowExecutionResponse + 100, // 100: temporal.server.api.historyservice.v1.HistoryService.ResetWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.ResetWorkflowExecutionResponse + 101, // 101: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecutionOptions:output_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionOptionsResponse + 102, // 102: temporal.server.api.historyservice.v1.HistoryService.RequestCancelWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.RequestCancelWorkflowExecutionResponse + 103, // 103: temporal.server.api.historyservice.v1.HistoryService.ScheduleWorkflowTask:output_type -> temporal.server.api.historyservice.v1.ScheduleWorkflowTaskResponse + 104, // 104: temporal.server.api.historyservice.v1.HistoryService.VerifyFirstWorkflowTaskScheduled:output_type -> temporal.server.api.historyservice.v1.VerifyFirstWorkflowTaskScheduledResponse + 105, // 105: temporal.server.api.historyservice.v1.HistoryService.RecordChildExecutionCompleted:output_type -> temporal.server.api.historyservice.v1.RecordChildExecutionCompletedResponse + 106, // 106: temporal.server.api.historyservice.v1.HistoryService.VerifyChildExecutionCompletionRecorded:output_type -> temporal.server.api.historyservice.v1.VerifyChildExecutionCompletionRecordedResponse + 107, // 107: temporal.server.api.historyservice.v1.HistoryService.DescribeWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.DescribeWorkflowExecutionResponse + 108, // 108: temporal.server.api.historyservice.v1.HistoryService.ReplicateEventsV2:output_type -> temporal.server.api.historyservice.v1.ReplicateEventsV2Response + 109, // 109: temporal.server.api.historyservice.v1.HistoryService.ReplicateWorkflowState:output_type -> temporal.server.api.historyservice.v1.ReplicateWorkflowStateResponse + 110, // 110: temporal.server.api.historyservice.v1.HistoryService.SyncShardStatus:output_type -> temporal.server.api.historyservice.v1.SyncShardStatusResponse + 111, // 111: temporal.server.api.historyservice.v1.HistoryService.SyncActivity:output_type -> temporal.server.api.historyservice.v1.SyncActivityResponse + 112, // 112: temporal.server.api.historyservice.v1.HistoryService.DescribeMutableState:output_type -> temporal.server.api.historyservice.v1.DescribeMutableStateResponse + 113, // 113: temporal.server.api.historyservice.v1.HistoryService.DescribeHistoryHost:output_type -> temporal.server.api.historyservice.v1.DescribeHistoryHostResponse + 114, // 114: temporal.server.api.historyservice.v1.HistoryService.CloseShard:output_type -> temporal.server.api.historyservice.v1.CloseShardResponse + 115, // 115: temporal.server.api.historyservice.v1.HistoryService.GetShard:output_type -> temporal.server.api.historyservice.v1.GetShardResponse + 116, // 116: temporal.server.api.historyservice.v1.HistoryService.RemoveTask:output_type -> temporal.server.api.historyservice.v1.RemoveTaskResponse + 117, // 117: temporal.server.api.historyservice.v1.HistoryService.GetReplicationMessages:output_type -> temporal.server.api.historyservice.v1.GetReplicationMessagesResponse + 118, // 118: temporal.server.api.historyservice.v1.HistoryService.GetDLQReplicationMessages:output_type -> temporal.server.api.historyservice.v1.GetDLQReplicationMessagesResponse + 119, // 119: temporal.server.api.historyservice.v1.HistoryService.QueryWorkflow:output_type -> temporal.server.api.historyservice.v1.QueryWorkflowResponse + 120, // 120: temporal.server.api.historyservice.v1.HistoryService.ReapplyEvents:output_type -> temporal.server.api.historyservice.v1.ReapplyEventsResponse + 121, // 121: temporal.server.api.historyservice.v1.HistoryService.GetDLQMessages:output_type -> temporal.server.api.historyservice.v1.GetDLQMessagesResponse + 122, // 122: temporal.server.api.historyservice.v1.HistoryService.PurgeDLQMessages:output_type -> temporal.server.api.historyservice.v1.PurgeDLQMessagesResponse + 123, // 123: temporal.server.api.historyservice.v1.HistoryService.MergeDLQMessages:output_type -> temporal.server.api.historyservice.v1.MergeDLQMessagesResponse + 124, // 124: temporal.server.api.historyservice.v1.HistoryService.RefreshWorkflowTasks:output_type -> temporal.server.api.historyservice.v1.RefreshWorkflowTasksResponse + 125, // 125: temporal.server.api.historyservice.v1.HistoryService.GenerateLastHistoryReplicationTasks:output_type -> temporal.server.api.historyservice.v1.GenerateLastHistoryReplicationTasksResponse + 126, // 126: temporal.server.api.historyservice.v1.HistoryService.GetReplicationStatus:output_type -> temporal.server.api.historyservice.v1.GetReplicationStatusResponse + 127, // 127: temporal.server.api.historyservice.v1.HistoryService.RebuildMutableState:output_type -> temporal.server.api.historyservice.v1.RebuildMutableStateResponse + 128, // 128: temporal.server.api.historyservice.v1.HistoryService.ImportWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.ImportWorkflowExecutionResponse + 129, // 129: temporal.server.api.historyservice.v1.HistoryService.DeleteWorkflowVisibilityRecord:output_type -> temporal.server.api.historyservice.v1.DeleteWorkflowVisibilityRecordResponse + 130, // 130: temporal.server.api.historyservice.v1.HistoryService.UpdateWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.UpdateWorkflowExecutionResponse + 131, // 131: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionUpdate:output_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionUpdateResponse + 132, // 132: temporal.server.api.historyservice.v1.HistoryService.StreamWorkflowReplicationMessages:output_type -> temporal.server.api.historyservice.v1.StreamWorkflowReplicationMessagesResponse + 133, // 133: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistory:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryResponse + 134, // 134: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionHistoryReverse:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionHistoryReverseResponse + 135, // 135: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistoryV2:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryV2Response + 136, // 136: temporal.server.api.historyservice.v1.HistoryService.GetWorkflowExecutionRawHistory:output_type -> temporal.server.api.historyservice.v1.GetWorkflowExecutionRawHistoryResponse + 137, // 137: temporal.server.api.historyservice.v1.HistoryService.ForceDeleteWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.ForceDeleteWorkflowExecutionResponse + 138, // 138: temporal.server.api.historyservice.v1.HistoryService.DeleteExecution:output_type -> temporal.server.api.historyservice.v1.DeleteExecutionResponse + 139, // 139: temporal.server.api.historyservice.v1.HistoryService.GetDLQTasks:output_type -> temporal.server.api.historyservice.v1.GetDLQTasksResponse + 140, // 140: temporal.server.api.historyservice.v1.HistoryService.DeleteDLQTasks:output_type -> temporal.server.api.historyservice.v1.DeleteDLQTasksResponse + 141, // 141: temporal.server.api.historyservice.v1.HistoryService.ListQueues:output_type -> temporal.server.api.historyservice.v1.ListQueuesResponse + 142, // 142: temporal.server.api.historyservice.v1.HistoryService.AddTasks:output_type -> temporal.server.api.historyservice.v1.AddTasksResponse + 143, // 143: temporal.server.api.historyservice.v1.HistoryService.ListTasks:output_type -> temporal.server.api.historyservice.v1.ListTasksResponse + 144, // 144: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperation:output_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationResponse + 145, // 145: temporal.server.api.historyservice.v1.HistoryService.CompleteNexusOperationChasm:output_type -> temporal.server.api.historyservice.v1.CompleteNexusOperationChasmResponse + 146, // 146: temporal.server.api.historyservice.v1.HistoryService.InvokeStateMachineMethod:output_type -> temporal.server.api.historyservice.v1.InvokeStateMachineMethodResponse + 147, // 147: temporal.server.api.historyservice.v1.HistoryService.DeepHealthCheck:output_type -> temporal.server.api.historyservice.v1.DeepHealthCheckResponse + 148, // 148: temporal.server.api.historyservice.v1.HistoryService.SyncWorkflowState:output_type -> temporal.server.api.historyservice.v1.SyncWorkflowStateResponse + 149, // 149: temporal.server.api.historyservice.v1.HistoryService.UpdateActivityOptions:output_type -> temporal.server.api.historyservice.v1.UpdateActivityOptionsResponse + 150, // 150: temporal.server.api.historyservice.v1.HistoryService.PauseActivity:output_type -> temporal.server.api.historyservice.v1.PauseActivityResponse + 151, // 151: temporal.server.api.historyservice.v1.HistoryService.UnpauseActivity:output_type -> temporal.server.api.historyservice.v1.UnpauseActivityResponse + 152, // 152: temporal.server.api.historyservice.v1.HistoryService.ResetActivity:output_type -> temporal.server.api.historyservice.v1.ResetActivityResponse + 153, // 153: temporal.server.api.historyservice.v1.HistoryService.PauseWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.PauseWorkflowExecutionResponse + 154, // 154: temporal.server.api.historyservice.v1.HistoryService.UnpauseWorkflowExecution:output_type -> temporal.server.api.historyservice.v1.UnpauseWorkflowExecutionResponse + 155, // 155: temporal.server.api.historyservice.v1.HistoryService.StartNexusOperation:output_type -> temporal.server.api.historyservice.v1.StartNexusOperationResponse + 156, // 156: temporal.server.api.historyservice.v1.HistoryService.CancelNexusOperation:output_type -> temporal.server.api.historyservice.v1.CancelNexusOperationResponse + 157, // 157: temporal.server.api.historyservice.v1.HistoryService.PollWorkflowExecutionTimeSkipping:output_type -> temporal.server.api.historyservice.v1.PollWorkflowExecutionTimeSkippingResponse + 79, // [79:158] is the sub-list for method output_type + 0, // [0:79] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/api/historyservice/v1/service_grpc.pb.go b/api/historyservice/v1/service_grpc.pb.go index 79e609f4a33..a6c13f620cd 100644 --- a/api/historyservice/v1/service_grpc.pb.go +++ b/api/historyservice/v1/service_grpc.pb.go @@ -23,6 +23,7 @@ const ( HistoryService_StartWorkflowExecution_FullMethodName = "/temporal.server.api.historyservice.v1.HistoryService/StartWorkflowExecution" HistoryService_GetMutableState_FullMethodName = "/temporal.server.api.historyservice.v1.HistoryService/GetMutableState" HistoryService_PollMutableState_FullMethodName = "/temporal.server.api.historyservice.v1.HistoryService/PollMutableState" + HistoryService_GetStreamReplaySlices_FullMethodName = "/temporal.server.api.historyservice.v1.HistoryService/GetStreamReplaySlices" HistoryService_ResetStickyTaskQueue_FullMethodName = "/temporal.server.api.historyservice.v1.HistoryService/ResetStickyTaskQueue" HistoryService_RecordWorkflowTaskStarted_FullMethodName = "/temporal.server.api.historyservice.v1.HistoryService/RecordWorkflowTaskStarted" HistoryService_RecordActivityTaskStarted_FullMethodName = "/temporal.server.api.historyservice.v1.HistoryService/RecordActivityTaskStarted" @@ -117,6 +118,10 @@ type HistoryServiceClient interface { // It fails with 'EntityNotExistError' if specified workflow execution in unknown to the service. // It returns CurrentBranchChangedError if the workflow version branch has changed. PollMutableState(ctx context.Context, in *PollMutableStateRequest, opts ...grpc.CallOption) (*PollMutableStateResponse, error) + // Returns the stream ranges the workflow's completed tasks recorded, re-read + // from their streams and tagged with the event that recorded each, so a task + // built without RecordWorkflowTaskStarted can still replay a consumer. + GetStreamReplaySlices(ctx context.Context, in *GetStreamReplaySlicesRequest, opts ...grpc.CallOption) (*GetStreamReplaySlicesResponse, error) // Reset the sticky task queue related information in mutable state of a given workflow. // Things cleared are: // 1. StickyTaskQueue @@ -428,6 +433,15 @@ func (c *historyServiceClient) PollMutableState(ctx context.Context, in *PollMut return out, nil } +func (c *historyServiceClient) GetStreamReplaySlices(ctx context.Context, in *GetStreamReplaySlicesRequest, opts ...grpc.CallOption) (*GetStreamReplaySlicesResponse, error) { + out := new(GetStreamReplaySlicesResponse) + err := c.cc.Invoke(ctx, HistoryService_GetStreamReplaySlices_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *historyServiceClient) ResetStickyTaskQueue(ctx context.Context, in *ResetStickyTaskQueueRequest, opts ...grpc.CallOption) (*ResetStickyTaskQueueResponse, error) { out := new(ResetStickyTaskQueueResponse) err := c.cc.Invoke(ctx, HistoryService_ResetStickyTaskQueue_FullMethodName, in, out, opts...) @@ -1142,6 +1156,10 @@ type HistoryServiceServer interface { // It fails with 'EntityNotExistError' if specified workflow execution in unknown to the service. // It returns CurrentBranchChangedError if the workflow version branch has changed. PollMutableState(context.Context, *PollMutableStateRequest) (*PollMutableStateResponse, error) + // Returns the stream ranges the workflow's completed tasks recorded, re-read + // from their streams and tagged with the event that recorded each, so a task + // built without RecordWorkflowTaskStarted can still replay a consumer. + GetStreamReplaySlices(context.Context, *GetStreamReplaySlicesRequest) (*GetStreamReplaySlicesResponse, error) // Reset the sticky task queue related information in mutable state of a given workflow. // Things cleared are: // 1. StickyTaskQueue @@ -1432,6 +1450,9 @@ func (UnimplementedHistoryServiceServer) GetMutableState(context.Context, *GetMu func (UnimplementedHistoryServiceServer) PollMutableState(context.Context, *PollMutableStateRequest) (*PollMutableStateResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method PollMutableState not implemented") } +func (UnimplementedHistoryServiceServer) GetStreamReplaySlices(context.Context, *GetStreamReplaySlicesRequest) (*GetStreamReplaySlicesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetStreamReplaySlices not implemented") +} func (UnimplementedHistoryServiceServer) ResetStickyTaskQueue(context.Context, *ResetStickyTaskQueueRequest) (*ResetStickyTaskQueueResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ResetStickyTaskQueue not implemented") } @@ -1724,6 +1745,24 @@ func _HistoryService_PollMutableState_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _HistoryService_GetStreamReplaySlices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetStreamReplaySlicesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HistoryServiceServer).GetStreamReplaySlices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: HistoryService_GetStreamReplaySlices_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HistoryServiceServer).GetStreamReplaySlices(ctx, req.(*GetStreamReplaySlicesRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _HistoryService_ResetStickyTaskQueue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ResetStickyTaskQueueRequest) if err := dec(in); err != nil { @@ -3101,6 +3140,10 @@ var HistoryService_ServiceDesc = grpc.ServiceDesc{ MethodName: "PollMutableState", Handler: _HistoryService_PollMutableState_Handler, }, + { + MethodName: "GetStreamReplaySlices", + Handler: _HistoryService_GetStreamReplaySlices_Handler, + }, { MethodName: "ResetStickyTaskQueue", Handler: _HistoryService_ResetStickyTaskQueue_Handler, diff --git a/api/historyservicemock/v1/service_grpc.pb.mock.go b/api/historyservicemock/v1/service_grpc.pb.mock.go index 6f4d8d32ff7..421c6c539fa 100644 --- a/api/historyservicemock/v1/service_grpc.pb.mock.go +++ b/api/historyservicemock/v1/service_grpc.pb.mock.go @@ -503,6 +503,26 @@ func (mr *MockHistoryServiceClientMockRecorder) GetShard(ctx, in any, opts ...an return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShard", reflect.TypeOf((*MockHistoryServiceClient)(nil).GetShard), varargs...) } +// GetStreamReplaySlices mocks base method. +func (m *MockHistoryServiceClient) GetStreamReplaySlices(ctx context.Context, in *historyservice.GetStreamReplaySlicesRequest, opts ...grpc.CallOption) (*historyservice.GetStreamReplaySlicesResponse, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetStreamReplaySlices", varargs...) + ret0, _ := ret[0].(*historyservice.GetStreamReplaySlicesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetStreamReplaySlices indicates an expected call of GetStreamReplaySlices. +func (mr *MockHistoryServiceClientMockRecorder) GetStreamReplaySlices(ctx, in any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStreamReplaySlices", reflect.TypeOf((*MockHistoryServiceClient)(nil).GetStreamReplaySlices), varargs...) +} + // GetWorkflowExecutionHistory mocks base method. func (m *MockHistoryServiceClient) GetWorkflowExecutionHistory(ctx context.Context, in *historyservice.GetWorkflowExecutionHistoryRequest, opts ...grpc.CallOption) (*historyservice.GetWorkflowExecutionHistoryResponse, error) { m.ctrl.T.Helper() @@ -2110,6 +2130,21 @@ func (mr *MockHistoryServiceServerMockRecorder) GetShard(arg0, arg1 any) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShard", reflect.TypeOf((*MockHistoryServiceServer)(nil).GetShard), arg0, arg1) } +// GetStreamReplaySlices mocks base method. +func (m *MockHistoryServiceServer) GetStreamReplaySlices(arg0 context.Context, arg1 *historyservice.GetStreamReplaySlicesRequest) (*historyservice.GetStreamReplaySlicesResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetStreamReplaySlices", arg0, arg1) + ret0, _ := ret[0].(*historyservice.GetStreamReplaySlicesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetStreamReplaySlices indicates an expected call of GetStreamReplaySlices. +func (mr *MockHistoryServiceServerMockRecorder) GetStreamReplaySlices(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStreamReplaySlices", reflect.TypeOf((*MockHistoryServiceServer)(nil).GetStreamReplaySlices), arg0, arg1) +} + // GetWorkflowExecutionHistory mocks base method. func (m *MockHistoryServiceServer) GetWorkflowExecutionHistory(arg0 context.Context, arg1 *historyservice.GetWorkflowExecutionHistoryRequest) (*historyservice.GetWorkflowExecutionHistoryResponse, error) { m.ctrl.T.Helper() diff --git a/client/history/client_gen.go b/client/history/client_gen.go index c594c7f17cd..7475fa3369e 100644 --- a/client/history/client_gen.go +++ b/client/history/client_gen.go @@ -399,6 +399,26 @@ func (c *clientImpl) GetShard( return response, nil } +func (c *clientImpl) GetStreamReplaySlices( + ctx context.Context, + request *historyservice.GetStreamReplaySlicesRequest, + opts ...grpc.CallOption, +) (*historyservice.GetStreamReplaySlicesResponse, error) { + shardID := c.shardIDFromWorkflowID(request.GetNamespaceId(), request.GetExecution().GetWorkflowId()) + var response *historyservice.GetStreamReplaySlicesResponse + op := func(ctx context.Context, client historyservice.HistoryServiceClient) error { + var err error + ctx, cancel := c.createContext(ctx) + defer cancel() + response, err = client.GetStreamReplaySlices(ctx, request, opts...) + return err + } + if err := c.executeWithRedirect(ctx, shardID, op); err != nil { + return nil, err + } + return response, nil +} + func (c *clientImpl) GetWorkflowExecutionHistory( ctx context.Context, request *historyservice.GetWorkflowExecutionHistoryRequest, diff --git a/client/history/metric_client_gen.go b/client/history/metric_client_gen.go index 8d4cad72aa5..78ccfda73a6 100644 --- a/client/history/metric_client_gen.go +++ b/client/history/metric_client_gen.go @@ -317,6 +317,20 @@ func (c *metricClient) GetShard( return c.client.GetShard(ctx, request, opts...) } +func (c *metricClient) GetStreamReplaySlices( + ctx context.Context, + request *historyservice.GetStreamReplaySlicesRequest, + opts ...grpc.CallOption, +) (_ *historyservice.GetStreamReplaySlicesResponse, retError error) { + + metricsHandler, startTime := c.startMetricsRecording(ctx, "HistoryClientGetStreamReplaySlices") + defer func() { + c.finishMetricsRecording(metricsHandler, startTime, retError) + }() + + return c.client.GetStreamReplaySlices(ctx, request, opts...) +} + func (c *metricClient) GetWorkflowExecutionHistory( ctx context.Context, request *historyservice.GetWorkflowExecutionHistoryRequest, diff --git a/client/history/retryable_client_gen.go b/client/history/retryable_client_gen.go index 31e48612095..a06b9118827 100644 --- a/client/history/retryable_client_gen.go +++ b/client/history/retryable_client_gen.go @@ -356,6 +356,21 @@ func (c *retryableClient) GetShard( return resp, err } +func (c *retryableClient) GetStreamReplaySlices( + ctx context.Context, + request *historyservice.GetStreamReplaySlicesRequest, + opts ...grpc.CallOption, +) (*historyservice.GetStreamReplaySlicesResponse, error) { + var resp *historyservice.GetStreamReplaySlicesResponse + op := func(ctx context.Context) error { + var err error + resp, err = c.client.GetStreamReplaySlices(ctx, request, opts...) + return err + } + err := backoff.ThrottleRetryContext(ctx, op, c.policy, c.isRetryable) + return resp, err +} + func (c *retryableClient) GetWorkflowExecutionHistory( ctx context.Context, request *historyservice.GetWorkflowExecutionHistoryRequest, diff --git a/common/rpc/interceptor/logtags/history_service_server_gen.go b/common/rpc/interceptor/logtags/history_service_server_gen.go index 038ccbf9c40..43dcc9e7b17 100644 --- a/common/rpc/interceptor/logtags/history_service_server_gen.go +++ b/common/rpc/interceptor/logtags/history_service_server_gen.go @@ -141,6 +141,13 @@ func (wt *WorkflowTags) extractFromHistoryServiceServerMessage(message any) []ta return nil case *historyservice.GetShardResponse: return nil + case *historyservice.GetStreamReplaySlicesRequest: + return []tag.Tag{ + tag.WorkflowID(r.GetExecution().GetWorkflowId()), + tag.WorkflowRunID(r.GetExecution().GetRunId()), + } + case *historyservice.GetStreamReplaySlicesResponse: + return nil case *historyservice.GetWorkflowExecutionHistoryRequest: return []tag.Tag{ tag.WorkflowID(r.GetRequest().GetExecution().GetWorkflowId()), 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 811b0dcc07c..40fe5e92ef7 100644 --- a/proto/internal/temporal/server/api/historyservice/v1/request_response.proto +++ b/proto/internal/temporal/server/api/historyservice/v1/request_response.proto @@ -123,6 +123,22 @@ message StartWorkflowExecutionResponse { string first_execution_run_id = 7; } +// Asks for the stream ranges a workflow's completed tasks recorded, re-read from +// their streams, for a task that is built without RecordWorkflowTaskStarted. +// A query dispatched straight through matching is the one such task. +message GetStreamReplaySlicesRequest { + option (routing).workflow_id = "execution.workflow_id"; + + string namespace_id = 1; + temporal.api.common.v1.WorkflowExecution execution = 2; +} + +message GetStreamReplaySlicesResponse { + // Every slice is tagged with the completed event that recorded its range. + // Empty when the workflow consumes no stream. + repeated temporal.api.stream.v1.StreamSlice stream_slices = 1; +} + message GetMutableStateRequest { option (routing).workflow_id = "execution.workflow_id"; diff --git a/proto/internal/temporal/server/api/historyservice/v1/service.proto b/proto/internal/temporal/server/api/historyservice/v1/service.proto index 58b0019b9f7..d19d6b13e7a 100644 --- a/proto/internal/temporal/server/api/historyservice/v1/service.proto +++ b/proto/internal/temporal/server/api/historyservice/v1/service.proto @@ -32,6 +32,13 @@ service HistoryService { option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_LONG_POLL; } + // Returns the stream ranges the workflow's completed tasks recorded, re-read + // from their streams and tagged with the event that recorded each, so a task + // built without RecordWorkflowTaskStarted can still replay a consumer. + rpc GetStreamReplaySlices(GetStreamReplaySlicesRequest) returns (GetStreamReplaySlicesResponse) { + option (temporal.server.api.common.v1.api_category).category = API_CATEGORY_STANDARD; + } + // Reset the sticky task queue related information in mutable state of a given workflow. // Things cleared are: // 1. StickyTaskQueue diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go index dec0ee5a586..dd5b16980bf 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_slices.go +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -15,8 +15,10 @@ import ( "go.temporal.io/server/chasm" "go.temporal.io/server/chasm/lib/stream" chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common" "go.temporal.io/server/common/definition" "go.temporal.io/server/common/failure" + "go.temporal.io/server/common/locks" "go.temporal.io/server/common/persistence" "go.temporal.io/server/common/persistence/serialization" "go.temporal.io/server/service/history/api" @@ -96,6 +98,36 @@ type streamOrigin struct { committed int64 } +// streamOrigins describes every stream the workflow consumes from a read-only +// view of its component, for a task that is built without delivering anything +// and so has no other way of learning what a cold replay owes it. +func streamOrigins( + ctx context.Context, + ms historyi.MutableState, +) (map[string]streamOrigin, error) { + if !ms.HasChasmWorkflowComponent() { + return nil, nil + } + wf, chasmCtx, err := ms.ChasmWorkflowComponentReadOnly(ctx) + if err != nil { + return nil, err + } + if len(wf.StreamCursors) == 0 { + return nil, nil + } + addresses := make(map[string]streamOrigin, len(wf.StreamCursors)) + for name, field := range wf.StreamCursors { + cursor := field.Get(chasmCtx) + addresses[cursor.StreamID()] = streamOrigin{ + external: cursor.IsExternal(), + name: name, + start: cursor.StartOffset(), + committed: cursor.Offset(), + } + } + return addresses, nil +} + // deliveryFrontier is the offset a delivery clips to. For a stream this // execution owns it is read directly; for one in another execution it is // whatever that stream last pushed here, because reading the real value would @@ -483,6 +515,100 @@ func attachReplaySlices( return nil } +// ReplaySlicesForQuery re-supplies the recorded ranges for a query dispatched +// straight through matching. +// +// Such a query carries the workflow's whole history to a worker that may never +// have seen the execution, exactly like a task after a cache miss, but it is +// built without RecordWorkflowTaskStarted and so never passes through the +// delivery above. The history is read from the branch rather than from a +// response, which is the only difference from the task path. +// +// The lease is held only long enough to read the cursors and the branch, and +// released before any page or stream is read, as on the task path. A range the +// stream can no longer serve is a failed precondition here rather than a failed +// task, because there is no task to fail: the query is refused and the +// workflow's next real task will fail with the cause. +func ReplaySlicesForQuery( + ctx context.Context, + shardContext historyi.ShardContext, + workflowConsistencyChecker api.WorkflowConsistencyChecker, + workflowKey definition.WorkflowKey, + pageSize int32, +) ([]*streampb.StreamSlice, error) { + lease, err := workflowConsistencyChecker.GetWorkflowLease( + ctx, nil, workflowKey, locks.PriorityHigh) + if err != nil { + return nil, err + } + ms := lease.GetMutableState() + addresses, err := streamOrigins(ctx, ms) + if err != nil || len(addresses) == 0 { + lease.GetReleaseFn()(nil) + return nil, err + } + branchToken, err := ms.GetCurrentBranchToken() + if err != nil { + lease.GetReleaseFn()(nil) + return nil, err + } + nextEventID := ms.GetNextEventID() + consumer := ms.GetWorkflowKey() + lease.GetReleaseFn()(nil) + + supply := &replaySupply{ + ctx: ctx, + consumer: consumer, + addresses: addresses, + reached: make(map[string]int64, len(addresses)), + } + var token []byte + for pages := 1; ; pages++ { + if pages > maxReplayPages { + return nil, unavailablef( + "replaying workflow %q needs more than %d pages of history to find the stream "+ + "ranges its tasks consumed", consumer.GetWorkflowID(), maxReplayPages) + } + blobs, _, next, err := persistence.ReadFullPageRawEvents( + ctx, shardContext.GetExecutionManager(), &persistence.ReadHistoryBranchRequest{ + BranchToken: branchToken, + MinEventID: common.FirstEventID, + MaxEventID: nextEventID, + PageSize: int(pageSize), + NextPageToken: token, + ShardID: shardContext.GetShardID(), + }) + if err != nil { + return nil, err + } + events, err := decodeEventBlobs(blobs) + if err != nil { + return nil, err + } + if err := supply.collect(events); err != nil { + return nil, err + } + if len(next) == 0 { + break + } + token = next + } + if err := supply.checkCoverage(); err != nil { + return nil, err + } + return supply.slices, nil +} + +// AsRefusal turns a range that cannot be served into the error a caller with +// no task to fail should return. Any other error is handed back unchanged. +func AsRefusal(err error) error { + var unavailable *rangeUnavailable + if errors.As(err, &unavailable) { + return serviceerror.NewFailedPrecondition(unavailable.Error()) + } + return err +} + // collect re-reads every range the events on one page recorded. func (s *replaySupply) collect(events []*historypb.HistoryEvent) error { for _, event := range events { diff --git a/service/history/handler.go b/service/history/handler.go index c4aa4ea9f3b..f63609d89f9 100644 --- a/service/history/handler.go +++ b/service/history/handler.go @@ -880,6 +880,33 @@ func (h *Handler) GetMutableState(ctx context.Context, request *historyservice.G return resp, nil } +// GetStreamReplaySlices re-supplies the stream ranges a workflow's completed tasks recorded. +func (h *Handler) GetStreamReplaySlices( + ctx context.Context, + request *historyservice.GetStreamReplaySlicesRequest, +) (*historyservice.GetStreamReplaySlicesResponse, error) { + namespaceID := namespace.ID(request.GetNamespaceId()) + if namespaceID == "" { + return nil, h.convertError(errNamespaceNotSet) + } + + workflowID := request.GetExecution().GetWorkflowId() + shardContext, err := h.controller.GetShardByNamespaceWorkflow(namespaceID, workflowID) + if err != nil { + return nil, h.convertError(err) + } + engine, err := shardContext.GetEngine(ctx) + if err != nil { + return nil, h.convertError(err) + } + + resp, err := engine.GetStreamReplaySlices(ctx, request) + if err != nil { + return nil, h.convertError(err) + } + return resp, nil +} + // PollMutableState - returns the id of the next event in the execution's history func (h *Handler) PollMutableState(ctx context.Context, request *historyservice.PollMutableStateRequest) (*historyservice.PollMutableStateResponse, error) { namespaceID := namespace.ID(request.GetNamespaceId()) diff --git a/service/history/history_engine.go b/service/history/history_engine.go index c561e8efc2f..43407e3c359 100644 --- a/service/history/history_engine.go +++ b/service/history/history_engine.go @@ -23,6 +23,7 @@ import ( "go.temporal.io/server/common/collection" "go.temporal.io/server/common/definition" "go.temporal.io/server/common/headers" + "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" @@ -538,6 +539,43 @@ func (e *historyEngineImpl) PollMutableState( }, nil } +// GetStreamReplaySlices re-supplies the stream ranges the workflow's completed +// tasks recorded, for a query task built outside RecordWorkflowTaskStarted. +func (e *historyEngineImpl) GetStreamReplaySlices( + ctx context.Context, + request *historyservice.GetStreamReplaySlicesRequest, +) (*historyservice.GetStreamReplaySlicesResponse, error) { + namespaceID := namespace.ID(request.GetNamespaceId()) + if err := api.ValidateNamespaceUUID(namespaceID); err != nil { + return nil, err + } + nsEntry, err := e.shardContext.GetNamespaceRegistry().GetNamespaceByID(namespaceID) + if err != nil { + return nil, err + } + runID := request.GetExecution().GetRunId() + if runID == "" { + runID, err = e.workflowConsistencyChecker.GetCurrentWorkflowRunID( + ctx, request.GetNamespaceId(), request.GetExecution().GetWorkflowId(), locks.PriorityHigh) + if err != nil { + return nil, err + } + } + workflowKey := definition.NewWorkflowKey( + request.GetNamespaceId(), request.GetExecution().GetWorkflowId(), runID) + slices, err := recordworkflowtaskstarted.ReplaySlicesForQuery( + ctx, + e.shardContext, + e.workflowConsistencyChecker, + workflowKey, + int32(e.config.HistoryMaxPageSize(nsEntry.Name().String())), + ) + if err != nil { + return nil, recordworkflowtaskstarted.AsRefusal(err) + } + return &historyservice.GetStreamReplaySlicesResponse{StreamSlices: slices}, nil +} + func (e *historyEngineImpl) QueryWorkflow( ctx context.Context, request *historyservice.QueryWorkflowRequest, diff --git a/service/history/interfaces/engine.go b/service/history/interfaces/engine.go index 1c03f0e583f..669595c3098 100644 --- a/service/history/interfaces/engine.go +++ b/service/history/interfaces/engine.go @@ -29,6 +29,7 @@ type ( StartWorkflowExecution(ctx context.Context, request *historyservice.StartWorkflowExecutionRequest) (*historyservice.StartWorkflowExecutionResponse, error) GetMutableState(ctx context.Context, request *historyservice.GetMutableStateRequest) (*historyservice.GetMutableStateResponse, error) PollMutableState(ctx context.Context, request *historyservice.PollMutableStateRequest) (*historyservice.PollMutableStateResponse, error) + GetStreamReplaySlices(ctx context.Context, request *historyservice.GetStreamReplaySlicesRequest) (*historyservice.GetStreamReplaySlicesResponse, error) DescribeMutableState(ctx context.Context, request *historyservice.DescribeMutableStateRequest) (*historyservice.DescribeMutableStateResponse, error) ResetStickyTaskQueue(ctx context.Context, resetRequest *historyservice.ResetStickyTaskQueueRequest) (*historyservice.ResetStickyTaskQueueResponse, error) DescribeWorkflowExecution(ctx context.Context, request *historyservice.DescribeWorkflowExecutionRequest) (*historyservice.DescribeWorkflowExecutionResponse, error) diff --git a/service/history/interfaces/engine_mock.go b/service/history/interfaces/engine_mock.go index b2ca488f358..5936e91939b 100644 --- a/service/history/interfaces/engine_mock.go +++ b/service/history/interfaces/engine_mock.go @@ -280,6 +280,21 @@ func (mr *MockEngineMockRecorder) GetReplicationTasksIter(ctx, pollingCluster, m return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationTasksIter", reflect.TypeOf((*MockEngine)(nil).GetReplicationTasksIter), ctx, pollingCluster, minInclusiveTaskID, maxExclusiveTaskID) } +// GetStreamReplaySlices mocks base method. +func (m *MockEngine) GetStreamReplaySlices(ctx context.Context, request *historyservice.GetStreamReplaySlicesRequest) (*historyservice.GetStreamReplaySlicesResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetStreamReplaySlices", ctx, request) + ret0, _ := ret[0].(*historyservice.GetStreamReplaySlicesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetStreamReplaySlices indicates an expected call of GetStreamReplaySlices. +func (mr *MockEngineMockRecorder) GetStreamReplaySlices(ctx, request any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStreamReplaySlices", reflect.TypeOf((*MockEngine)(nil).GetStreamReplaySlices), ctx, request) +} + // GetWorkflowExecutionHistory mocks base method. func (m *MockEngine) GetWorkflowExecutionHistory(ctx context.Context, request *historyservice.GetWorkflowExecutionHistoryRequest) (*historyservice.GetWorkflowExecutionHistoryResponseWithRaw, error) { m.ctrl.T.Helper() diff --git a/service/matching/matching_engine.go b/service/matching/matching_engine.go index bb60c55ce42..b67e036559b 100644 --- a/service/matching/matching_engine.go +++ b/service/matching/matching_engine.go @@ -19,6 +19,7 @@ import ( enumspb "go.temporal.io/api/enums/v1" historypb "go.temporal.io/api/history/v1" "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" "go.temporal.io/api/workflowservice/v1" deploymentspb "go.temporal.io/server/api/deployment/v1" @@ -754,6 +755,16 @@ pollLoop: return emptyPollWorkflowTaskQueueResponse, nil } + // After the history, so every range the history records is + // re-supplied. Read the other way round a task could complete in + // between and the worker would see a recorded range with no bytes. + streamSlices, err := e.getStreamSlicesForQueryTask( + ctx, req.GetNamespaceId(), task, isStickyEnabled) + if err != nil { + _ = e.deliverQueryResult(task.query.taskID, &queryResult{internalError: err}) + return emptyPollWorkflowTaskQueueResponse, nil + } + resp := &historyservice.RecordWorkflowTaskStartedResponse{ PreviousStartedEventId: mutableStateResp.PreviousStartedEventId, NextEventId: mutableStateResp.NextEventId, @@ -765,6 +776,7 @@ pollLoop: Attempt: 1, History: hist, NextPageToken: nextPageToken, + StreamSlices: streamSlices, } // Local query match. Emit the dispatch latency metric. This metric does not include the query response time. @@ -863,6 +875,33 @@ pollLoop: } } +// getStreamSlicesForQueryTask asks History for the stream ranges the +// workflow's history records, re-read from their streams. +// +// A query task is built here without RecordWorkflowTaskStarted, so the +// delivery that attaches those ranges to an ordinary task never runs for it. +// Only a non-sticky query needs them: a sticky one goes to a worker that still +// holds the execution and has nothing to replay. +func (e *matchingEngineImpl) getStreamSlicesForQueryTask( + ctx context.Context, + namespaceID string, + task *internalTask, + isStickyEnabled bool, +) ([]*streampb.StreamSlice, error) { + if isStickyEnabled { + return nil, nil + } + resp, err := e.historyClient.GetStreamReplaySlices(ctx, + &historyservice.GetStreamReplaySlicesRequest{ + NamespaceId: namespaceID, + Execution: task.workflowExecution(), + }) + if err != nil { + return nil, err + } + return resp.GetStreamSlices(), nil +} + func (e *matchingEngineImpl) getHistoryForQueryTask( ctx context.Context, nsID namespace.ID, diff --git a/service/matching/matching_engine_test.go b/service/matching/matching_engine_test.go index 62cce1c554c..69163759c38 100644 --- a/service/matching/matching_engine_test.go +++ b/service/matching/matching_engine_test.go @@ -28,6 +28,7 @@ import ( nexuspb "go.temporal.io/api/nexus/v1" querypb "go.temporal.io/api/query/v1" "go.temporal.io/api/serviceerror" + streampb "go.temporal.io/api/stream/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" "go.temporal.io/api/workflowservice/v1" clockspb "go.temporal.io/server/api/clock/v1" @@ -6166,6 +6167,72 @@ func TestConvertPollWorkflowTaskQueueResponse_FieldMapping(t *testing.T) { assert.Equal(t, inputResp.History.Events[0].EventId, result.History.Events[0].EventId) } +// A non-sticky query task asks History for the recorded stream ranges; a +// sticky one does not, since its worker still holds the execution. +func TestGetStreamSlicesForQueryTask(t *testing.T) { + t.Parallel() + + nsID := namespace.ID("test-namespace-id") + task := &internalTask{ + namespace: namespace.Name("test-namespace"), + event: &genericTaskInfo{ + AllocatedTaskInfo: &persistencespb.AllocatedTaskInfo{ + Data: &persistencespb.TaskInfo{ + NamespaceId: nsID.String(), + WorkflowId: "test-workflow-id", + RunId: "test-run-id", + }, + }, + }, + } + resupplied := []*streampb.StreamSlice{{ + StreamId: "output", RunId: "test-run-id", FromOffset: 0, ToOffset: 2, + WorkflowTaskCompletedEventId: 4, + }} + + t.Run("sticky asks nothing", func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + mockHistoryClient := historyservicemock.NewMockHistoryServiceClient(ctrl) + engine := &matchingEngineImpl{historyClient: mockHistoryClient, logger: log.NewNoopLogger()} + + slices, err := engine.getStreamSlicesForQueryTask(context.Background(), nsID.String(), task, true) + require.NoError(t, err) + require.Empty(t, slices) + }) + + t.Run("non-sticky carries what History re-supplies", func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + mockHistoryClient := historyservicemock.NewMockHistoryServiceClient(ctrl) + mockHistoryClient.EXPECT().GetStreamReplaySlices(gomock.Any(), + &historyservice.GetStreamReplaySlicesRequest{ + NamespaceId: nsID.String(), + Execution: task.workflowExecution(), + }).Return(&historyservice.GetStreamReplaySlicesResponse{StreamSlices: resupplied}, nil). + Times(1) + engine := &matchingEngineImpl{historyClient: mockHistoryClient, logger: log.NewNoopLogger()} + + slices, err := engine.getStreamSlicesForQueryTask( + context.Background(), nsID.String(), task, false) + require.NoError(t, err) + require.Len(t, slices, 1) + require.Equal(t, int64(4), slices[0].GetWorkflowTaskCompletedEventId()) + }) + + t.Run("a refusal fails the query", func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + mockHistoryClient := historyservicemock.NewMockHistoryServiceClient(ctrl) + mockHistoryClient.EXPECT().GetStreamReplaySlices(gomock.Any(), gomock.Any()).Return( + nil, serviceerror.NewFailedPrecondition("stream no longer holds the range")).Times(1) + engine := &matchingEngineImpl{historyClient: mockHistoryClient, logger: log.NewNoopLogger()} + + _, err := engine.getStreamSlicesForQueryTask(context.Background(), nsID.String(), task, false) + require.ErrorContains(t, err, "no longer holds") + }) +} + func TestGetHistoryForQueryTask(t *testing.T) { t.Parallel() diff --git a/tests/stream_query_test.go b/tests/stream_query_test.go new file mode 100644 index 00000000000..20052370f49 --- /dev/null +++ b/tests/stream_query_test.go @@ -0,0 +1,137 @@ +package tests + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + querypb "go.temporal.io/api/query/v1" + "go.temporal.io/api/workflowservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common/payloads" + "go.temporal.io/server/tests/testcore" +) + +// A query with no workflow task in flight is dispatched straight through +// matching, on a task built without RecordWorkflowTaskStarted. That task +// carries the whole history to whichever worker polls it, which may never have +// seen the execution, so it has to carry the recorded ranges too or the worker +// cannot replay a consuming workflow far enough to answer. +// +// The query is answered by a second poller that took part in none of the +// earlier tasks, from the slices on the query task alone. +func TestQueryTaskCarriesTheConsumedRanges(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-query-") + + task := 0 + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + worker := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "worker", + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { + task++ + if task == 1 { + return publishCommand("answer-1", "answer-2"), nil + } + return nil, nil + }, + Logger: env.Logger, + T: t, + } + + _, err := worker.PollAndProcessWorkflowTask() + require.NoError(t, err) + + _, err = s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + StreamName: chasmworkflow.DefaultStreamName, StartOffset: 0, + }, + }) + require.NoError(t, err) + + // The subscription schedules the task that consumes the backlog. + _, err = worker.PollAndProcessWorkflowTask() + require.NoError(t, err) + consumedAt := completedEventWithCursors(t, env.GetHistory(s.ns, execution)) + + var queryTask *workflowservice.PollWorkflowTaskQueueResponse + //nolint:staticcheck // SA1019: consistent with the worker above. + coldWorker := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "cold-worker", + WorkflowTaskHandler: func( + *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { + t.Fatal("the query must arrive as a query task, not a workflow task") + return nil, nil + }, + QueryHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) (*commonpb.Payloads, error) { + queryTask = resp + // Answer from the task alone, which is all a replaying worker has. + var seen []string + for _, slice := range resp.GetStreamSlices() { + for _, record := range slice.GetRecords() { + seen = append(seen, string(record.GetBody().GetData())) + } + } + return payloads.EncodeString(strings.Join(seen, ",")), nil + }, + Logger: env.Logger, + T: t, + } + + type answer struct { + resp *workflowservice.QueryWorkflowResponse + err error + } + answered := make(chan answer, 1) + go func() { + resp, err := env.FrontendClient().QueryWorkflow(s.ctx(), &workflowservice.QueryWorkflowRequest{ + Namespace: s.ns, + Execution: execution, + Query: &querypb.WorkflowQuery{QueryType: "consumed"}, + }) + answered <- answer{resp: resp, err: err} + }() + + res, err := coldWorker.PollAndProcessWorkflowTask() + require.NoError(t, err) + require.True(t, res.IsQueryTask, "the query is dispatched on its own task") + require.NotNil(t, queryTask.GetQuery()) + require.NotEmpty(t, queryTask.GetHistory().GetEvents(), "a non-sticky query carries full history") + + replayed := sliceForEvent(queryTask.GetStreamSlices(), consumedAt) + require.NotNil(t, replayed, "the query task carries the range recorded at event %d", consumedAt) + require.Equal(t, int64(0), replayed.GetFromOffset()) + require.Equal(t, int64(2), replayed.GetToOffset()) + require.Equal(t, execution.GetRunId(), replayed.GetRunId(), "the slice names the run holding the stream") + require.Equal(t, []string{"answer-1", "answer-2"}, apiBodies(replayed.GetRecords())) + + // Nothing on a query task is about to be consumed, so every slice is a + // re-supply for a completion the history already holds. + for _, slice := range queryTask.GetStreamSlices() { + require.NotZero(t, slice.GetWorkflowTaskCompletedEventId(), + "a query task must not carry an untagged slice, it starts no task") + } + + got := <-answered + require.NoError(t, got.err) + var result string + require.NoError(t, payloads.Decode(got.resp.GetQueryResult(), &result)) + require.Equal(t, "answer-1,answer-2", result, + "the query answer is computed from the re-supplied records") +} From 3f66c5bf7360c3adbd75bc37395094855149e40e Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 14:23:09 -0700 Subject: [PATCH 45/47] Shortened the stream metadata table. --- common/api/metadata.go | 43 +++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/common/api/metadata.go b/common/api/metadata.go index 8abfee41c66..d7d4dbef5fe 100644 --- a/common/api/metadata.go +++ b/common/api/metadata.go @@ -223,21 +223,34 @@ var ( // table does not make. The two internal calls History makes on itself are // admin so that no namespace-level role can reach them through a frontend. streamServiceMetadata = map[string]MethodMetadata{ - "CreateStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, - "AddMessages": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, - "FinishWriting": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, - "SubscribeWorkflow": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, - "PollMessages": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingCapable}, - "DescribeStream": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingNone}, - "PollWorkflowMessages": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingCapable}, - "DescribeWorkflowStream": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingNone}, - "AddWorkflowMessages": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, - "RegisterStreamConsumer": {Scope: ScopeNamespace, Access: AccessAdmin, Polling: PollingNone}, - "AdvanceConsumerHead": {Scope: ScopeNamespace, Access: AccessAdmin, Polling: PollingNone}, - "CloseStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, - "TruncateStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, - "ListStreams": {Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingNone}, - "DeleteStream": {Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone}, + "CreateStream": namespaceWrite, + "AddMessages": namespaceWrite, + "FinishWriting": namespaceWrite, + "SubscribeWorkflow": namespaceWrite, + "PollMessages": namespaceReadPoll, + "DescribeStream": namespaceRead, + "PollWorkflowMessages": namespaceReadPoll, + "DescribeWorkflowStream": namespaceRead, + "AddWorkflowMessages": namespaceWrite, + "RegisterStreamConsumer": namespaceAdmin, + "AdvanceConsumerHead": namespaceAdmin, + "CloseStream": namespaceWrite, + "TruncateStream": namespaceWrite, + "ListStreams": namespaceRead, + "DeleteStream": namespaceWrite, + } + + namespaceRead = MethodMetadata{ + Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingNone, + } + namespaceReadPoll = MethodMetadata{ + Scope: ScopeNamespace, Access: AccessReadOnly, Polling: PollingCapable, + } + namespaceWrite = MethodMetadata{ + Scope: ScopeNamespace, Access: AccessWrite, Polling: PollingNone, + } + namespaceAdmin = MethodMetadata{ + Scope: ScopeNamespace, Access: AccessAdmin, Polling: PollingNone, } ) From 125e9d3f29773a94400b6c4b56b09f2e9a842b91 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 14:41:41 -0700 Subject: [PATCH 46/47] Carried stream subscriptions through a workflow reset. A reset run is rebuilt from events, and the cursors were CHASM state the events never touched, so the new run had no subscription and could not replay the ranges its copied history records. The rebuild now recreates cursors from the events, the reset copies what only the base run knows and gives the new run streams of its own from the inherited offset, and replay reads each range from the run that holds it. --- chasm/lib/stream/cursor.go | 20 ++ chasm/lib/stream/stream.go | 19 +- chasm/lib/workflow/stream_commands.go | 36 +- chasm/lib/workflow/stream_cursor_test.go | 156 +++++++++ chasm/lib/workflow/workflow.go | 158 +++++++++ .../stream_routing_test.go | 4 +- .../stream_slices.go | 103 ++++-- .../stream_slices_test.go | 125 +++++++ service/history/ndc/workflow_resetter.go | 43 +++ .../workflow/mutable_state_rebuilder.go | 23 ++ tests/stream_reset_test.go | 310 ++++++++++++++++++ 11 files changed, 961 insertions(+), 36 deletions(-) create mode 100644 service/history/api/recordworkflowtaskstarted/stream_slices_test.go create mode 100644 tests/stream_reset_test.go diff --git a/chasm/lib/stream/cursor.go b/chasm/lib/stream/cursor.go index adb433ce709..143fc23f0b8 100644 --- a/chasm/lib/stream/cursor.go +++ b/chasm/lib/stream/cursor.go @@ -139,3 +139,23 @@ func (c *Cursor) AdvanceKnownHead(_ chasm.MutableContext, head int64) { func (c *Cursor) StartOffset() int64 { return c.State.StartOffset } + +// Restore puts the cursor where a completed task's event says it stood, for a +// run being rebuilt from its history. It only moves forward: the events are +// applied in order, and a range folded in earlier is never taken back. +func (c *Cursor) Restore(_ chasm.MutableContext, offset int64) { + if offset > c.State.Offset { + c.State.Offset = offset + } + if offset > c.State.KnownHead { + c.State.KnownHead = offset + } +} + +// MarkExternal says the stream lives in another execution, which the subscribe +// event alone cannot tell a rebuilt run, and carries over the frontier that +// stream last pushed at the run this one was rebuilt from. +func (c *Cursor) MarkExternal(mctx chasm.MutableContext, knownHead int64) { + c.State.External = true + c.AdvanceKnownHead(mctx, knownHead) +} diff --git a/chasm/lib/stream/stream.go b/chasm/lib/stream/stream.go index f54a41f5fcd..12243b09b8d 100644 --- a/chasm/lib/stream/stream.go +++ b/chasm/lib/stream/stream.go @@ -56,6 +56,12 @@ type NewStreamRequest struct { // child of the root, so an attached stream carries none and is found // through its owner instead of through ListStreams. Attached bool + + // StartOffset is where the stream's offsets begin. Zero for a new stream. + // A run created by a reset inherits a cursor whose position is written in + // its History, so the stream it goes on reading has to continue that + // offset space rather than start over at zero. + StartOffset int64 } type AddMessagesRequest struct { @@ -87,6 +93,9 @@ type AddMessagesResult struct { } func NewStream(ctx chasm.MutableContext, req NewStreamRequest) (*Stream, error) { + if req.StartOffset < 0 { + return nil, serviceerror.NewInvalidArgument("start offset cannot be negative") + } visibility := chasm.NewEmptyField[*chasm.Visibility]() if !req.Attached { visibility = chasm.NewComponentField(ctx, chasm.NewVisibility(ctx)) @@ -95,10 +104,12 @@ func NewStream(ctx chasm.MutableContext, req NewStreamRequest) (*Stream, error) Visibility: visibility, Batches: make(chasm.Map[int64, *commonpb.DataBlob]), State: &streamlib.StreamState{ - Lifecycle: req.Lifecycle, - Budget: req.Budget, - Producers: make(map[string]*streamlib.ProducerCursor), - Consumers: make(map[string]*streamlib.ConsumerCursor), + HeadOffset: req.StartOffset, + BaseOffset: req.StartOffset, + Lifecycle: req.Lifecycle, + Budget: req.Budget, + Producers: make(map[string]*streamlib.ProducerCursor), + Consumers: make(map[string]*streamlib.ConsumerCursor), }, }, nil } diff --git a/chasm/lib/workflow/stream_commands.go b/chasm/lib/workflow/stream_commands.go index 22400d2f7d8..0c6ea260709 100644 --- a/chasm/lib/workflow/stream_commands.go +++ b/chasm/lib/workflow/stream_commands.go @@ -168,9 +168,32 @@ func (streamSubscribedEvent) Type() enumspb.EventType { func (streamSubscribedEvent) IsWorkflowTaskTrigger() bool { return false } -// The cursor lives in CHASM state, which is persisted and rebuilt with the -// execution, so there is nothing for replication or reset to reconstruct here. -func (streamSubscribedEvent) Apply(chasm.MutableContext, *Workflow, *historypb.HistoryEvent) error { +// Apply recreates the cursor for a run rebuilt from its history, which is how a +// reset run comes to exist. The event records the stream and where reading +// began; the ranges consumed since are folded in as the completed events that +// carry them are applied. Whether the stream lives in this execution or in +// another is not in the event, so the rebuilt cursor is owned until the reset +// copies that from the run it was rebuilt from. +func (streamSubscribedEvent) Apply( + mctx chasm.MutableContext, + wf *Workflow, + event *historypb.HistoryEvent, +) error { + attrs := event.GetWorkflowStreamSubscribedEventAttributes() + if _, ok := wf.StreamCursors[attrs.GetStreamId()]; ok { + return nil + } + cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ + StreamID: attrs.GetStreamId(), + StartOffset: attrs.GetStartOffset(), + }) + if err != nil { + return err + } + if wf.StreamCursors == nil { + wf.StreamCursors = make(chasm.Map[string, *stream.Cursor]) + } + wf.StreamCursors[attrs.GetStreamId()] = chasm.NewComponentField(mctx, cursor) return nil } @@ -277,6 +300,13 @@ func (w *Workflow) RecordStreamRecordsAppended( // streamNamed returns the workflow's stream of that name, creating it on first // use. Implicit creation is deliberate: a workflow publishing to its own output // should not have to coordinate with anyone about who creates it. +// +// The stream belongs to this run. After a reset the new run publishes to and +// subscribes on streams of its own, created here on first use, and the run it +// was reset from keeps the records its own history refers to. A stream the +// reset run inherited a subscription to is created by the reset itself, at the +// offset that subscription stood at, so it is already here by the time a +// command names it. func (w *Workflow) streamNamed( ctx chasm.MutableContext, name string, diff --git a/chasm/lib/workflow/stream_cursor_test.go b/chasm/lib/workflow/stream_cursor_test.go index db65d0cc2fc..dfe88da5ae0 100644 --- a/chasm/lib/workflow/stream_cursor_test.go +++ b/chasm/lib/workflow/stream_cursor_test.go @@ -242,3 +242,159 @@ func TestConsumerOutrunByTruncationIsToldSo(t *testing.T) { require.Equal(t, int64(3), state.GetBaseOffset(), "the floor moved, which is what the consumer has to find out about") } + +func newStreamCursorTestContextForRun(runID string) chasm.MutableContext { + return &chasm.MockMutableContext{ + MockContext: chasm.MockContext{ + HandleExecutionKey: func() chasm.ExecutionKey { + return chasm.ExecutionKey{NamespaceID: "ns-1", BusinessID: "wf-1", RunID: runID} + }, + }, + } +} + +func subscribedEvent(streamID string, startOffset int64) *historypb.HistoryEvent { + return &historypb.HistoryEvent{ + EventType: enumspb.EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED, + Attributes: &historypb.HistoryEvent_WorkflowStreamSubscribedEventAttributes{ + WorkflowStreamSubscribedEventAttributes: &historypb.WorkflowStreamSubscribedEventAttributes{ + StreamId: streamID, + StartOffset: startOffset, + }, + }, + } +} + +// A run rebuilt from its history, which is what a reset produces, gets its +// cursors back from the events alone: the subscribe event places the cursor +// and each completed task's recorded range moves it. +func TestRebuildRecreatesTheCursorFromItsEvents(t *testing.T) { + ctx := newStreamCursorTestContext() + w := &Workflow{} + + require.NoError(t, streamSubscribedEvent{}.Apply(ctx, w, subscribedEvent("inputs", 1))) + cursor := w.StreamCursors["inputs"].Get(ctx) + require.Equal(t, int64(1), cursor.StartOffset()) + require.Equal(t, int64(1), cursor.Offset()) + require.False(t, cursor.IsExternal(), "the event cannot say where the stream lives") + + require.NoError(t, w.ApplyConsumedStreamRanges(ctx, []*streampb.StreamRange{ + {StreamId: "inputs", FromOffset: 1, ToOffset: 4}, + {StreamId: "out-of-band", FromOffset: 3, ToOffset: 9}, + })) + require.Equal(t, int64(4), cursor.Offset()) + require.Equal(t, int64(1), cursor.StartOffset(), "where reading began does not move") + outOfBand, ok := w.StreamCursors["out-of-band"] + require.True(t, ok, "a recorded range proves a subscription the events never mentioned") + require.Equal(t, int64(3), outOfBand.Get(ctx).StartOffset()) + require.Equal(t, int64(9), outOfBand.Get(ctx).Offset()) + + // The same subscribe event applied twice, as a resubscribe would leave in + // History, must not rewind the cursor. + require.NoError(t, streamSubscribedEvent{}.Apply(ctx, w, subscribedEvent("inputs", 1))) + require.Equal(t, int64(4), w.StreamCursors["inputs"].Get(ctx).Offset()) +} + +// A reset run's cursor on a stream the base run owned gets a stream of the +// reset run's own, starting where the cursor stands, so the ranges below it +// stay in the base run and everything from here on is the reset run's. +func TestResetRunInheritsAnOwnedStreamAtItsCursor(t *testing.T) { + baseCtx := newStreamCursorTestContextForRun("base-run") + base := &Workflow{} + base.Streams = chasm.Map[string, *stream.Stream]{ + DefaultStreamName: chasm.NewComponentField(baseCtx, newAttachedStream(t, baseCtx, 4)), + } + _, err := base.SubscribeToOwnedStream(baseCtx, DefaultStreamName, 0, stream.DefaultLimits()) + require.NoError(t, err) + + resetCtx := newStreamCursorTestContextForRun("reset-run") + reset := &Workflow{} + require.NoError(t, + streamSubscribedEvent{}.Apply(resetCtx, reset, subscribedEvent(DefaultStreamName, 0))) + require.NoError(t, reset.ApplyConsumedStreamRanges(resetCtx, []*streampb.StreamRange{ + {StreamId: DefaultStreamName, FromOffset: 0, ToOffset: 2}, + })) + + require.NoError(t, reset.InheritStreamsOnReset(resetCtx, base, baseCtx, stream.DefaultLimits())) + + cursor := reset.StreamCursors[DefaultStreamName].Get(resetCtx) + require.False(t, cursor.IsExternal()) + require.Equal(t, int64(2), cursor.Offset()) + + own := reset.OwnedStream(resetCtx, DefaultStreamName) + require.NotNil(t, own, "the reset run reads and writes a stream of its own") + state, err := own.Snapshot(resetCtx, struct{}{}) + require.NoError(t, err) + require.Equal(t, int64(2), state.GetBaseOffset(), "the stream continues the offset space") + require.Equal(t, int64(2), state.GetHeadOffset()) + require.NotNil(t, state.GetBudget(), "an owned stream is budgeted like one created by a publish") + pin := state.GetConsumers()[streamConsumerID(DefaultStreamName)] + require.NotNil(t, pin, "the reset run pins its own stream") + require.Equal(t, "reset-run", pin.GetRunId()) + require.Equal(t, int64(2), pin.GetReplayFloor()) + + // The base run's stream is untouched: it still holds what the reset run's + // history refers to. + baseState, err := base.OwnedStream(baseCtx, DefaultStreamName).Snapshot(baseCtx, struct{}{}) + require.NoError(t, err) + require.Equal(t, int64(0), baseState.GetBaseOffset()) + require.Equal(t, int64(4), baseState.GetHeadOffset()) + + // A publish on the reset run lands after the inherited position. + result, err := own.AddMessages(resetCtx, stream.AddMessagesRequest{ + Records: []*streamlib.StreamRecord{{Kind: streampb.STREAM_RECORD_KIND_DATA}}, + Limits: stream.DefaultLimits(), + }) + require.NoError(t, err) + require.Equal(t, int64(2), result.FirstOffset) +} + +// A reset run's cursor on a stream in another execution stays on that stream +// and learns what the base run knew of its frontier. +func TestResetRunInheritsAnExternalCursorAsExternal(t *testing.T) { + baseCtx := newStreamCursorTestContextForRun("base-run") + base := &Workflow{} + _, err := base.SubscribeToExternalStream(baseCtx, ExternalStreamSubscription{ + StreamID: "shared", StartOffset: 0, KnownHead: 7, + }) + require.NoError(t, err) + + resetCtx := newStreamCursorTestContextForRun("reset-run") + reset := &Workflow{} + require.NoError(t, streamSubscribedEvent{}.Apply(resetCtx, reset, subscribedEvent("shared", 0))) + require.NoError(t, reset.ApplyConsumedStreamRanges(resetCtx, []*streampb.StreamRange{ + {StreamId: "shared", FromOffset: 0, ToOffset: 3}, + })) + + require.NoError(t, reset.InheritStreamsOnReset(resetCtx, base, baseCtx, stream.DefaultLimits())) + + cursor := reset.StreamCursors["shared"].Get(resetCtx) + require.True(t, cursor.IsExternal()) + require.Equal(t, int64(3), cursor.Offset()) + require.Equal(t, int64(7), cursor.KnownHead(), "the frontier the base run last knew carries over") + require.Nil(t, reset.OwnedStream(resetCtx, "shared"), "no stream of its own for an external one") +} + +// A subscription made through the service leaves no event, so a reset run whose +// history records nothing for it is given the cursor from the base run, where +// that subscription began. +func TestResetRunCarriesASubscriptionTheEventsNeverMentioned(t *testing.T) { + baseCtx := newStreamCursorTestContextForRun("base-run") + base := &Workflow{} + base.Streams = chasm.Map[string, *stream.Stream]{ + "inputs": chasm.NewComponentField(baseCtx, newAttachedStream(t, baseCtx, 4)), + } + _, err := base.SubscribeToOwnedStream(baseCtx, "inputs", 1, stream.DefaultLimits()) + require.NoError(t, err) + + resetCtx := newStreamCursorTestContextForRun("reset-run") + reset := &Workflow{} + require.NoError(t, reset.InheritStreamsOnReset(resetCtx, base, baseCtx, stream.DefaultLimits())) + + cursor := reset.StreamCursors["inputs"].Get(resetCtx) + require.Equal(t, int64(1), cursor.StartOffset()) + require.Equal(t, int64(1), cursor.Offset()) + state, err := reset.OwnedStream(resetCtx, "inputs").Snapshot(resetCtx, struct{}{}) + require.NoError(t, err) + require.Equal(t, int64(1), state.GetBaseOffset()) +} diff --git a/chasm/lib/workflow/workflow.go b/chasm/lib/workflow/workflow.go index 1c32b7df32f..225ba0e1cec 100644 --- a/chasm/lib/workflow/workflow.go +++ b/chasm/lib/workflow/workflow.go @@ -229,6 +229,164 @@ func (w *Workflow) ImportStreamSubscriptions( return nil } +// ApplyConsumedStreamRanges puts the cursors where the completed event says +// they stood, for a run being rebuilt from its history. +// +// A range for a stream with no cursor belongs to a subscription made out of +// band, through the service rather than by a command, which leaves no event of +// its own. The range is proof the subscription was live, so the cursor is +// created from it: the first range a subscription records begins where it +// started reading. +func (w *Workflow) ApplyConsumedStreamRanges( + mctx chasm.MutableContext, + ranges []*streampb.StreamRange, +) error { + for _, recorded := range ranges { + field, ok := w.StreamCursors[recorded.GetStreamId()] + if !ok { + cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ + StreamID: recorded.GetStreamId(), + StartOffset: recorded.GetFromOffset(), + }) + if err != nil { + return err + } + if w.StreamCursors == nil { + w.StreamCursors = make(chasm.Map[string, *stream.Cursor]) + } + field = chasm.NewComponentField(mctx, cursor) + w.StreamCursors[recorded.GetStreamId()] = field + } + field.Get(mctx).Restore(mctx, recorded.GetToOffset()) + } + return nil +} + +// InheritStreamsOnReset finishes the cursors a reset run rebuilt from its +// history with what the events could not say, read from the run it was reset +// from. +// +// Every subscription the base run held is carried. One the rebuild recreated +// keeps the position its events gave it; one the events never mentioned, made +// out of band and never delivered to before the reset point, starts where it +// started in the base run. A cursor on a stream in another execution keeps +// reading that stream; it is marked so and given the frontier the base run +// last knew. A cursor on a stream the base run owned gets a stream of this +// run's own, starting at the offset the cursor stands at: the ranges below it +// are in the base run's stream, which is where replay re-reads them, and +// everything from here on is this run's. Continuing the offset space is what +// keeps a range in this run's History unambiguous about which run holds it. +func (w *Workflow) InheritStreamsOnReset( + mctx chasm.MutableContext, + base *Workflow, + baseCtx chasm.Context, + limits stream.Limits, +) error { + if w.StreamCursors == nil { + w.StreamCursors = make(chasm.Map[string, *stream.Cursor]) + } + if err := w.carryBaseCursors(mctx, base, baseCtx); err != nil { + return err + } + + names := make([]string, 0, len(w.StreamCursors)) + for name := range w.StreamCursors { + names = append(names, name) + } + slices.Sort(names) + + for _, name := range names { + cursor := w.StreamCursors[name].Get(mctx) + if baseCursor := cursorNamed(base, baseCtx, name); baseCursor != nil && baseCursor.IsExternal() { + cursor.MarkExternal(mctx, baseCursor.KnownHead()) + continue + } + if err := w.ownStreamFrom(mctx, name, cursor.Offset(), limits); err != nil { + return err + } + } + return nil +} + +// cursorNamed returns a workflow's cursor by stream name, or nil when the +// workflow is absent or holds none by that name. +func cursorNamed(w *Workflow, ctx chasm.Context, name string) *stream.Cursor { + if w == nil { + return nil + } + field, ok := w.StreamCursors[name] + if !ok { + return nil + } + return field.Get(ctx) +} + +// carryBaseCursors creates a cursor for every subscription the base run held +// that the rebuild did not recreate, starting where it started in the base run. +func (w *Workflow) carryBaseCursors( + mctx chasm.MutableContext, + base *Workflow, + baseCtx chasm.Context, +) error { + if base == nil { + return nil + } + for name, field := range base.StreamCursors { + if _, ok := w.StreamCursors[name]; ok { + continue + } + baseCursor := field.Get(baseCtx) + cursor, err := stream.NewCursor(mctx, stream.NewCursorRequest{ + StreamID: baseCursor.StreamID(), + StartOffset: baseCursor.StartOffset(), + }) + if err != nil { + return err + } + w.StreamCursors[name] = chasm.NewComponentField(mctx, cursor) + } + return nil +} + +// ownStreamFrom gives this run a stream of its own by that name, beginning at +// the given offset and pinned by this run's consumer there, unless it has one. +func (w *Workflow) ownStreamFrom( + mctx chasm.MutableContext, + name string, + offset int64, + limits stream.Limits, +) error { + if _, ok := w.Streams[name]; ok { + return nil + } + if w.Streams == nil { + w.Streams = make(chasm.Map[string, *stream.Stream]) + } + created, err := stream.NewStream(mctx, stream.NewStreamRequest{ + Attached: true, + StartOffset: offset, + Budget: &streamlib.StreamBudget{ + MaxItems: int64(limits.OwnedStreamMaxItems), + MaxBytes: int64(limits.OwnedStreamMaxBytes), + }, + }) + if err != nil { + return err + } + key := mctx.ExecutionKey() + if _, err := created.RegisterConsumer(mctx, stream.ConsumerRegistration{ + ConsumerID: streamConsumerID(name), + WorkflowID: key.BusinessID, + RunID: key.RunID, + Offset: offset, + MaxConsumers: limits.MaxConsumersPerStream, + }); err != nil { + return err + } + w.Streams[name] = chasm.NewComponentField(mctx, created) + return nil +} + // AdvanceKnownHead records how far a stream in another execution has moved. // // A workflow cannot read that frontier itself while closing its own diff --git a/service/history/api/recordworkflowtaskstarted/stream_routing_test.go b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go index 28589f344a5..4d91e339a6b 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_routing_test.go +++ b/service/history/api/recordworkflowtaskstarted/stream_routing_test.go @@ -67,7 +67,7 @@ func TestExternalStreamLiveAndReplayUseRoutedPayloadRead(t *testing.T) { if replay { window, err = readRecordedRange(ctx, definition.NewWorkflowKey("namespace-id", "consumer", "consumer-run"), - streamOrigin{external: true}, "remote-source", 4, 6) + streamOrigin{external: true}, "consumer-run", "remote-source", 4, 6) } else { window, err = readWindowFor(ctx, nil, nil, "namespace-id", "input", true, "remote-source", 4, 6) } @@ -158,6 +158,6 @@ func TestOwnedReplayPinsConsumerRun(t *testing.T) { }) _, err := readRecordedRange(chasm.NewEngineContext(context.Background(), engine), definition.NewWorkflowKey("ns", "consumer", "consumer-run"), - streamOrigin{name: "owned"}, "owned", 4, 6) + streamOrigin{name: "owned"}, "consumer-run", "owned", 4, 6) require.NoError(t, err) } diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices.go b/service/history/api/recordworkflowtaskstarted/stream_slices.go index dd5b16980bf..c4f8f5ae3ac 100644 --- a/service/history/api/recordworkflowtaskstarted/stream_slices.go +++ b/service/history/api/recordworkflowtaskstarted/stream_slices.go @@ -368,13 +368,16 @@ type ownedRange struct { // readRecordedRange re-supplies a range a completed task recorded. // -// It runs after the execution lock is released, so the consumer's own component -// is read back through the engine. Standalone sources use the routed service -// client, since their shards can belong to a different history host. +// It runs after the execution lock is released, so an owned stream is read +// back through the engine, from the run that holds it: the consumer itself, or +// the run the consumer was reset from for a range recorded before the reset. +// Standalone sources use the routed service client, since their shards can +// belong to a different history host. func readRecordedRange( ctx context.Context, consumer definition.WorkflowKey, origin streamOrigin, + ownerRunID string, streamID string, from, to int64, ) (stream.Window, error) { @@ -386,7 +389,7 @@ func readRecordedRange( chasm.NewComponentRef[*chasmworkflow.Workflow](chasm.ExecutionKey{ NamespaceID: consumer.NamespaceID, BusinessID: consumer.WorkflowID, - RunID: consumer.RunID, + RunID: ownerRunID, }), func(wf *chasmworkflow.Workflow, cctx chasm.Context, r ownedRange) (stream.Window, error) { s := wf.OwnedStream(cctx, r.name) @@ -412,6 +415,20 @@ type replaySupply struct { // can tell a complete re-supply from a short one. reached map[string]int64 slices []*streampb.StreamSlice + + // Ranges recorded since the last reset point, waiting to learn which run's + // stream holds them. A reset copies the base run's history into the new + // run, so the ranges before the reset point were consumed from the base + // run's owned streams, and the run they belong to is only named by the + // event that marks the reset point, after them in the history. Ranges + // after the last reset point belong to the consumer itself. + pending []recordedRange +} + +// recordedRange is one consumed range and the completion that recorded it. +type recordedRange struct { + eventID int64 + recorded *streampb.StreamRange } // attachReplaySlices re-supplies the payloads for ranges that earlier workflow @@ -508,6 +525,9 @@ func attachReplaySlices( } } + if err := supply.finish(); err != nil { + return err + } if err := supply.checkCoverage(); err != nil { return err } @@ -593,6 +613,9 @@ func ReplaySlicesForQuery( } token = next } + if err := supply.finish(); err != nil { + return nil, err + } if err := supply.checkCoverage(); err != nil { return nil, err } @@ -609,49 +632,75 @@ func AsRefusal(err error) error { return err } -// collect re-reads every range the events on one page recorded. +// collect gathers every range the events on one page recorded and re-reads the +// ones whose run it can name. A reset point names the run for everything +// gathered before it. func (s *replaySupply) collect(events []*historypb.HistoryEvent) error { for _, event := range events { + if attrs := event.GetWorkflowTaskFailedEventAttributes(); attrs != nil && + attrs.GetCause() == enumspb.WORKFLOW_TASK_FAILED_CAUSE_RESET_WORKFLOW && + attrs.GetBaseRunId() != "" { + if err := s.flush(attrs.GetBaseRunId()); err != nil { + return err + } + continue + } for _, recorded := range event.GetWorkflowTaskCompletedEventAttributes().GetConsumedStreamRanges() { - address, ok := s.addresses[recorded.GetStreamId()] - if !ok { + if _, ok := s.addresses[recorded.GetStreamId()]; !ok { // A subscription the workflow has since dropped. The range is // still part of its history, but nothing is consuming it now. continue } + s.pending = append(s.pending, recordedRange{eventID: event.GetEventId(), recorded: recorded}) + } + } + return nil +} - records, ownerRunID, err := s.recordsFor(address, recorded) - if err != nil { - return err - } +// finish re-reads the ranges recorded after the last reset point, which the +// consumer's own streams hold. Called once every page has been walked. +func (s *replaySupply) finish() error { + return s.flush(s.consumer.RunID) +} - if to := recorded.GetToOffset(); to > s.reached[recorded.GetStreamId()] { - s.reached[recorded.GetStreamId()] = to - } +// flush re-reads every range gathered since the last reset point from the run +// named for it, in the order the tasks recorded them. +func (s *replaySupply) flush(ownerRunID string) error { + for _, r := range s.pending { + address := s.addresses[r.recorded.GetStreamId()] + records, runID, err := s.recordsFor(address, ownerRunID, r.recorded) + if err != nil { + return err + } - // Attached even when empty: the task observed nothing, and replay - // has to reproduce that rather than infer it from an absence. - s.slices = append(s.slices, &streampb.StreamSlice{ - StreamId: recorded.GetStreamId(), - RunId: ownerRunID, - FromOffset: recorded.GetFromOffset(), - ToOffset: recorded.GetToOffset(), - Records: records, - WorkflowTaskCompletedEventId: event.GetEventId(), - }) + if to := r.recorded.GetToOffset(); to > s.reached[r.recorded.GetStreamId()] { + s.reached[r.recorded.GetStreamId()] = to } + + // Attached even when empty: the task observed nothing, and replay + // has to reproduce that rather than infer it from an absence. + s.slices = append(s.slices, &streampb.StreamSlice{ + StreamId: r.recorded.GetStreamId(), + RunId: runID, + FromOffset: r.recorded.GetFromOffset(), + ToOffset: r.recorded.GetToOffset(), + Records: records, + WorkflowTaskCompletedEventId: r.eventID, + }) } + s.pending = nil return nil } // recordsFor re-reads one recorded range, or returns nothing for a range that // recorded an empty observation. The run id is the execution holding the -// stream, which for an owned stream is the consumer itself. +// stream: for an owned stream the run named for the range, for a standalone +// one whatever run the read reports. func (s *replaySupply) recordsFor( address streamOrigin, + ownerRunID string, recorded *streampb.StreamRange, ) ([]*streampb.StreamRecord, string, error) { - ownerRunID := s.consumer.RunID if recorded.GetToOffset() <= recorded.GetFromOffset() { if address.external { ownerRunID = "" @@ -659,7 +708,7 @@ func (s *replaySupply) recordsFor( return nil, ownerRunID, nil } - w, err := readRecordedRange(s.ctx, s.consumer, address, + w, err := readRecordedRange(s.ctx, s.consumer, address, ownerRunID, recorded.GetStreamId(), recorded.GetFromOffset(), recorded.GetToOffset()) if err != nil { // Truncation and deletion are the reachable causes, and neither is diff --git a/service/history/api/recordworkflowtaskstarted/stream_slices_test.go b/service/history/api/recordworkflowtaskstarted/stream_slices_test.go new file mode 100644 index 00000000000..f0e63b2a107 --- /dev/null +++ b/service/history/api/recordworkflowtaskstarted/stream_slices_test.go @@ -0,0 +1,125 @@ +package recordworkflowtaskstarted + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/server/chasm" + "go.temporal.io/server/common/definition" + "go.uber.org/mock/gomock" +) + +func completedWithRange(eventID int64, from, to int64) *historypb.HistoryEvent { + return &historypb.HistoryEvent{ + EventId: eventID, + EventType: enumspb.EVENT_TYPE_WORKFLOW_TASK_COMPLETED, + Attributes: &historypb.HistoryEvent_WorkflowTaskCompletedEventAttributes{ + WorkflowTaskCompletedEventAttributes: &historypb.WorkflowTaskCompletedEventAttributes{ + ConsumedStreamRanges: []*streampb.StreamRange{ + {StreamId: "output", FromOffset: from, ToOffset: to}, + }, + }, + }, + } +} + +func resetPoint(eventID int64, baseRunID string) *historypb.HistoryEvent { + return &historypb.HistoryEvent{ + EventId: eventID, + EventType: enumspb.EVENT_TYPE_WORKFLOW_TASK_FAILED, + Attributes: &historypb.HistoryEvent_WorkflowTaskFailedEventAttributes{ + WorkflowTaskFailedEventAttributes: &historypb.WorkflowTaskFailedEventAttributes{ + Cause: enumspb.WORKFLOW_TASK_FAILED_CAUSE_RESET_WORKFLOW, + BaseRunId: baseRunID, + NewRunId: "reset-run", + }, + }, + } +} + +// A reset copies the base run's history into the new run. The ranges recorded +// before the reset point were consumed from the base run's own stream, so +// replay has to read them from that run and say so on the slice, while the +// ranges recorded after it are the reset run's own. A reset of a reset run +// leaves two such points, one per run in the chain. +func TestReplayReadsRangesBeforeAResetFromTheRunThatHoldsThem(t *testing.T) { + var readFrom []string + engine := chasm.NewMockEngine(gomock.NewController(t)) + engine.EXPECT().ReadComponent(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func( + _ context.Context, + ref chasm.ComponentRef, + _ func(chasm.Context, chasm.Component) error, + _ ...chasm.TransitionOption, + ) error { + require.Equal(t, "consumer", ref.BusinessID) + readFrom = append(readFrom, ref.RunID) + return nil + }).AnyTimes() + + supply := &replaySupply{ + ctx: chasm.NewEngineContext(context.Background(), engine), + consumer: definition.NewWorkflowKey("ns", "consumer", "reset-run"), + addresses: map[string]streamOrigin{"output": {name: "output"}}, + reached: map[string]int64{}, + } + + // First page: the oldest run's era ends with the first reset point. + require.NoError(t, supply.collect([]*historypb.HistoryEvent{ + completedWithRange(4, 0, 2), + completedWithRange(8, 2, 2), + resetPoint(10, "first-run"), + })) + // Second page: the middle run's era, then the reset that made this run. + require.NoError(t, supply.collect([]*historypb.HistoryEvent{ + completedWithRange(14, 2, 5), + resetPoint(16, "second-run"), + completedWithRange(20, 5, 5), + completedWithRange(24, 5, 7), + })) + require.NoError(t, supply.finish()) + require.NoError(t, supply.checkCoverage()) + + require.Equal(t, []string{"first-run", "second-run", "reset-run"}, readFrom, + "every non-empty range is read from the run whose era recorded it") + + runs := make([]string, 0, len(supply.slices)) + events := make([]int64, 0, len(supply.slices)) + for _, slice := range supply.slices { + runs = append(runs, slice.GetRunId()) + events = append(events, slice.GetWorkflowTaskCompletedEventId()) + } + require.Equal(t, []int64{4, 8, 14, 20, 24}, events, "slices keep the order the tasks ran in") + require.Equal(t, []string{"first-run", "first-run", "second-run", "reset-run", "reset-run"}, runs, + "an empty range names its era's run too") +} + +// Without a reset point every owned range is the consumer's own. +func TestReplayWithoutAResetReadsFromTheConsumer(t *testing.T) { + engine := chasm.NewMockEngine(gomock.NewController(t)) + engine.EXPECT().ReadComponent(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func( + _ context.Context, + ref chasm.ComponentRef, + _ func(chasm.Context, chasm.Component) error, + _ ...chasm.TransitionOption, + ) error { + require.Equal(t, "consumer-run", ref.RunID) + return nil + }).Times(1) + + supply := &replaySupply{ + ctx: chasm.NewEngineContext(context.Background(), engine), + consumer: definition.NewWorkflowKey("ns", "consumer", "consumer-run"), + addresses: map[string]streamOrigin{"output": {name: "output"}}, + reached: map[string]int64{}, + } + require.NoError(t, supply.collect([]*historypb.HistoryEvent{completedWithRange(4, 0, 3)})) + require.NoError(t, supply.finish()) + require.Len(t, supply.slices, 1) + require.Equal(t, "consumer-run", supply.slices[0].GetRunId()) +} diff --git a/service/history/ndc/workflow_resetter.go b/service/history/ndc/workflow_resetter.go index 8266d05288b..992b2ae9b55 100644 --- a/service/history/ndc/workflow_resetter.go +++ b/service/history/ndc/workflow_resetter.go @@ -254,6 +254,10 @@ func (r *workflowResetterImpl) ResetWorkflow( defer func() { resetWorkflow.GetReleaseFn()(retError) }() resetMS := resetWorkflow.GetMutableState() + err = r.inheritStreams(ctx, namespaceEntry, baseWorkflow.GetMutableState(), resetMS) + if err != nil { + return err + } if err := reapplyEventsFn(ctx, resetMS); err != nil { return err } @@ -287,6 +291,45 @@ func (r *workflowResetterImpl) ResetWorkflow( return nil } +// inheritStreams completes the stream cursors the rebuild recreated from the +// reset run's events with what only the base run's state can say: which +// streams live in other executions, and the frontier those last pushed. A +// cursor on a stream the base run owned gets a stream of the reset run's own, +// continuing the offset space from where the cursor stands. +func (r *workflowResetterImpl) inheritStreams( + ctx context.Context, + namespaceEntry *namespace.Namespace, + baseMS historyi.MutableState, + resetMS historyi.MutableState, +) error { + if !resetMS.HasChasmWorkflowComponent() { + return nil + } + // Read-only first: a reset run with no subscription should not pay a node + // in its first transaction for a check that finds nothing. + readOnly, _, err := resetMS.ChasmWorkflowComponentReadOnly(ctx) + if err != nil { + return err + } + if len(readOnly.StreamCursors) == 0 { + return nil + } + + var base *chasmworkflow.Workflow + var baseCtx chasm.Context + if baseMS.HasChasmWorkflowComponent() { + if base, baseCtx, err = baseMS.ChasmWorkflowComponentReadOnly(ctx); err != nil { + return err + } + } + reset, resetCtx, err := resetMS.ChasmWorkflowComponent(ctx) + if err != nil { + return err + } + limits := r.shardContext.GetConfig().Stream.LimitsFor(namespaceEntry.Name().String()) + return reset.InheritStreamsOnReset(resetCtx, base, baseCtx, limits) +} + func (r *workflowResetterImpl) prepareResetWorkflow( ctx context.Context, namespaceID namespace.ID, diff --git a/service/history/workflow/mutable_state_rebuilder.go b/service/history/workflow/mutable_state_rebuilder.go index 6e1fb1cc892..55edf9745c4 100644 --- a/service/history/workflow/mutable_state_rebuilder.go +++ b/service/history/workflow/mutable_state_rebuilder.go @@ -261,6 +261,9 @@ func (b *MutableStateRebuilderImpl) applyEvents( ); err != nil { return nil, err } + if err := b.applyConsumedStreamRanges(ctx, event); err != nil { + return nil, err + } case enumspb.EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: if err := b.mutableState.ApplyWorkflowTaskTimedOutEvent( @@ -732,6 +735,26 @@ func (b *MutableStateRebuilderImpl) applyStateMachineEvent( return b.applyHSMEvent(event) } +// applyConsumedStreamRanges moves the stream cursors to where the completed +// event says they stood. The cursors are CHASM state and the ranges ride the +// event, so a run rebuilt from its history, a reset run above all, ends up with +// its cursors exactly where the events put them. +func (b *MutableStateRebuilderImpl) applyConsumedStreamRanges( + ctx context.Context, + event *historypb.HistoryEvent, +) error { + ranges := event.GetWorkflowTaskCompletedEventAttributes().GetConsumedStreamRanges() + if len(ranges) == 0 || !b.mutableState.ChasmEnabled() { + return nil + } + b.mutableState.EnsureChasmWorkflowComponent(ctx) + wf, chasmCtx, err := b.mutableState.ChasmWorkflowComponent(ctx) + if err != nil { + return err + } + return wf.ApplyConsumedStreamRanges(chasmCtx, ranges) +} + // applyHSMEvent applies an event to the HSM tree func (b *MutableStateRebuilderImpl) applyHSMEvent(event *historypb.HistoryEvent) error { def, ok := b.shard.StateMachineRegistry().EventDefinition(event.GetEventType()) diff --git a/tests/stream_reset_test.go b/tests/stream_reset_test.go new file mode 100644 index 00000000000..b6a48d523c9 --- /dev/null +++ b/tests/stream_reset_test.go @@ -0,0 +1,310 @@ +package tests + +import ( + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/api/workflowservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/tests/testcore" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// nthCompletedEvent returns the id of the n-th WorkflowTaskCompleted event, +// counting from one. +func nthCompletedEvent(t *testing.T, events []*historypb.HistoryEvent, n int) int64 { + t.Helper() + seen := 0 + for _, e := range events { + if e.GetEventType() == enumspb.EVENT_TYPE_WORKFLOW_TASK_COMPLETED { + seen++ + if seen == n { + return e.GetEventId() + } + } + } + t.Fatalf("history has %d completed tasks, wanted %d", seen, n) + return 0 +} + +func resetTo( + t *testing.T, s *streamTestEnv, execution *commonpb.WorkflowExecution, eventID int64, +) string { + t.Helper() + resp, err := s.env.FrontendClient().ResetWorkflowExecution(s.ctx(), + &workflowservice.ResetWorkflowExecutionRequest{ + Namespace: s.ns, + WorkflowExecution: execution, + Reason: "stream reset test", + WorkflowTaskFinishEventId: eventID, + RequestId: uuid.NewString(), + }) + require.NoError(t, err) + return resp.GetRunId() +} + +// A reset copies the base run's history into a new run, and that history +// records ranges the base run consumed from a stream it owned. The reset run +// replays them from the base run's stream, with each slice naming that run, +// and from the reset point on it consumes and publishes on a stream of its own +// that continues the offset space where the inherited cursor stood. +func TestResetReplaysTheBaseRunsRangesAndGoesOnWithItsOwnStream(t *testing.T) { + // Dedicated, because the cold replay at the end evicts the cached + // workflow context through CloseShard. + env := testcore.NewEnv(t, testcore.WithDedicatedCluster()) + s := newStreamTestEnvFrom(t, env) + execution, tq := startConsumer(t, s, "stream-wf-reset-") + + var delivered [][]*streampb.StreamSlice + var commands [][]*commandpb.Command + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + next := commands[0] + commands = commands[1:] + return next, nil + }, + Logger: env.Logger, + T: t, + } + runTask := func(cmds []*commandpb.Command) []*streampb.StreamSlice { + t.Helper() + commands = append(commands, cmds) + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + return delivered[len(delivered)-1] + } + + // Base run: publish two, subscribe, consume them, publish a third, consume it. + runTask(publishCommand("before-1", "before-2")) + _, err := s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + StreamName: chasmworkflow.DefaultStreamName, StartOffset: 0, + }, + }) + require.NoError(t, err) + consumed := currentSlice(t, runTask(nil)) + require.Equal(t, []string{"before-1", "before-2"}, apiBodies(consumed.GetRecords())) + + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + runTask(publishCommand("before-3")) + third := currentSlice(t, runTask(nil)) + require.Equal(t, []string{"before-3"}, apiBodies(third.GetRecords())) + + baseEvents := env.GetHistory(s.ns, execution) + consumedAt := completedEventWithCursors(t, baseEvents) + + // Reset to the third task: the reset run keeps the first two completions, + // so its cursor stands at offset 2 and nothing of the third publish is its. + resetRunID := resetTo(t, s, execution, nthCompletedEvent(t, baseEvents, 3)) + resetRun := &commonpb.WorkflowExecution{WorkflowId: execution.GetWorkflowId(), RunId: resetRunID} + + // The reset run's first task replays the range recorded before the reset + // point from the base run's stream, and consumes nothing new of its own. + first := runTask(nil) + replayed := sliceForEvent(first, consumedAt) + require.NotNil(t, replayed, "the copied completion at event %d is re-supplied", consumedAt) + require.Equal(t, execution.GetRunId(), replayed.GetRunId(), + "a range recorded before the reset point is read from the base run") + require.Equal(t, int64(0), replayed.GetFromOffset()) + require.Equal(t, int64(2), replayed.GetToOffset()) + require.Equal(t, []string{"before-1", "before-2"}, apiBodies(replayed.GetRecords())) + live := currentSlice(t, first) + require.Equal(t, resetRunID, live.GetRunId(), + "from the reset point on the stream is the reset run's") + require.Equal(t, int64(2), live.GetFromOffset(), "the inherited cursor keeps its position") + require.Equal(t, int64(2), live.GetToOffset()) + + // The reset run publishes and consumes on its own stream, whose offsets + // continue from where the cursor stood. + signalWorkflow(t, s, execution.GetWorkflowId(), resetRunID) + runTask(publishCommand("after-1")) + own := currentSlice(t, runTask(nil)) + require.Equal(t, resetRunID, own.GetRunId()) + require.Equal(t, int64(2), own.GetFromOffset()) + require.Equal(t, int64(3), own.GetToOffset()) + require.Equal(t, []string{"after-1"}, apiBodies(own.GetRecords())) + + // Seen from outside: the base run still holds every record its history + // refers to, and the reset run's stream starts at the inherited offset. + basePoll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + OwnerRunId: execution.GetRunId(), FromOffset: 0, + }, + }) + require.NoError(t, err) + require.Equal(t, []string{"before-1", "before-2", "before-3"}, + bodies(basePoll.GetFrontendResponse().GetRecords())) + + resetPoll, err := s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + OwnerRunId: resetRunID, FromOffset: 2, + }, + }) + require.NoError(t, err) + require.Equal(t, []string{"after-1"}, bodies(resetPoll.GetFrontendResponse().GetRecords())) + require.Equal(t, []int64{2}, offsets(resetPoll.GetFrontendResponse().GetRecords())) + + _, err = s.client.PollWorkflowMessages(s.ctx(), &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), + OwnerRunId: resetRunID, FromOffset: 0, + }, + }) + require.Equal(t, codes.FailedPrecondition, status.Code(err), + "a reader following the chain into the reset run has to start at the offset it begins at") + + // A cold replay of the reset run re-supplies both eras from the runs that + // hold them, tagged with the completions that recorded them. + env.CloseShard(env.NamespaceID().String(), execution.GetWorkflowId()) + signalWorkflow(t, s, execution.GetWorkflowId(), resetRunID) + cold := runTask(nil) + fromBase := sliceForEvent(cold, consumedAt) + require.NotNil(t, fromBase) + require.Equal(t, execution.GetRunId(), fromBase.GetRunId()) + require.Equal(t, []string{"before-1", "before-2"}, apiBodies(fromBase.GetRecords())) + ownAt := completedEventWithCursorsAfter(t, env.GetHistory(s.ns, resetRun), consumedAt) + fromReset := sliceForEvent(cold, ownAt) + require.NotNil(t, fromReset) + require.Equal(t, resetRunID, fromReset.GetRunId()) + require.Equal(t, int64(2), fromReset.GetFromOffset()) + require.Equal(t, int64(3), fromReset.GetToOffset()) + require.Equal(t, []string{"after-1"}, apiBodies(fromReset.GetRecords())) + + // The base run names its successor, which is how a client following the + // chain finds the reset run. + described, err := env.FrontendClient().DescribeWorkflowExecution(s.ctx(), + &workflowservice.DescribeWorkflowExecutionRequest{Namespace: s.ns, Execution: execution}) + require.NoError(t, err) + require.Equal(t, enumspb.WORKFLOW_EXECUTION_STATUS_TERMINATED, + described.GetWorkflowExecutionInfo().GetStatus()) + require.Equal(t, resetRunID, described.GetWorkflowExtendedInfo().GetResetRunId()) +} + +// completedEventWithCursorsAfter returns the id of the first WorkflowTaskCompleted +// event after the given one that recorded a non-empty consumed range. +func completedEventWithCursorsAfter( + t *testing.T, events []*historypb.HistoryEvent, after int64, +) int64 { + t.Helper() + for _, e := range events { + if e.GetEventId() <= after { + continue + } + for _, c := range e.GetWorkflowTaskCompletedEventAttributes().GetConsumedStreamRanges() { + if c.GetToOffset() > c.GetFromOffset() { + return e.GetEventId() + } + } + } + t.Fatal("no completed event after the given one recorded a consumed range") + return 0 +} + +// A reset terminates the base run. Its pin on a stream in another execution is +// released the way a closed run's is, when the stream next tells its consumers +// the frontier moved and finds the run over, and the reset run, which carries +// the subscription on, takes the pin over with the floor its cursor began at. +func TestResetHandsTheExternalPinToTheResetRun(t *testing.T) { + env := testcore.NewEnv(t) + s := newStreamTestEnvFrom(t, env) + + streamID := "reset-shared-stream-" + uuid.NewString() + s.create(s.ctx(), t, streamID) + execution, tq := startConsumer(t, s, "stream-wf-reset-ext-") + + var delivered [][]*streampb.StreamSlice + //nolint:staticcheck // SA1019: consistent with the other stream tests. + poller := &testcore.TaskPoller{ + Client: env.FrontendClient(), + Namespace: s.ns, + TaskQueue: tq, + Identity: "tester", + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { + delivered = append(delivered, resp.GetStreamSlices()) + return nil, nil + }, + Logger: env.Logger, + T: t, + } + runTask := func() []*streampb.StreamSlice { + t.Helper() + _, err := poller.PollAndProcessWorkflowTask() + require.NoError(t, err) + return delivered[len(delivered)-1] + } + + runTask() + _, err := s.client.SubscribeWorkflow(s.ctx(), &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: s.ns, WorkflowId: execution.GetWorkflowId(), StreamId: streamID, StartOffset: 0, + }, + }) + require.NoError(t, err) + _, err = s.add(s.ctx(), t, streamID, + &streamlib.AddMessagesInput{Records: streamMsgs("t", "one", "two")}) + require.NoError(t, err) + consumed := currentSlice(t, runTask()) + require.Equal(t, int64(2), consumed.GetToOffset()) + + signalWorkflow(t, s, execution.GetWorkflowId(), execution.GetRunId()) + runTask() + baseEvents := env.GetHistory(s.ns, execution) + consumedAt := completedEventWithCursors(t, baseEvents) + + resetRunID := resetTo(t, s, execution, nthCompletedEvent(t, baseEvents, 3)) + + // The reset run replays the base run's range from the standalone stream + // and stands at the same offset. + first := runTask() + replayed := sliceForEvent(first, consumedAt) + require.NotNil(t, replayed) + require.Equal(t, []string{"one", "two"}, apiBodies(replayed.GetRecords())) + require.Equal(t, int64(2), currentSlice(t, first).GetFromOffset()) + + // The pin is still the base run's until the stream next notifies. + before := describeStream(t, s, streamID) + require.Len(t, before.GetConsumers(), 1) + for _, consumer := range before.GetConsumers() { + require.Equal(t, execution.GetRunId(), consumer.GetRunId()) + } + + // An append pushes the frontier at the pinned run, finds it terminated, and + // hands the pin to the run that now carries the subscription. + _, err = s.add(s.ctx(), t, streamID, + &streamlib.AddMessagesInput{Records: streamMsgs("t", "three")}) + require.NoError(t, err) + afterReset := currentSlice(t, runTask()) + require.Equal(t, int64(2), afterReset.GetFromOffset(), + "the reset run resumes where the base stopped") + require.Equal(t, int64(3), afterReset.GetToOffset()) + require.Equal(t, []string{"three"}, apiBodies(afterReset.GetRecords())) + + after := describeStream(t, s, streamID) + require.Len(t, after.GetConsumers(), 1, "one pin, keyed to the run that consumes") + for _, consumer := range after.GetConsumers() { + require.Equal(t, resetRunID, consumer.GetRunId()) + require.Equal(t, int64(0), consumer.GetReplayFloor(), + "the floor is where the inherited subscription began, which its replay depends on") + } +} From 19b4452be899cdd95c378060413d15f6455ac947 Mon Sep 17 00:00:00 2001 From: Moe Dashti Date: Mon, 21 Sep 2026 14:52:58 -0700 Subject: [PATCH 47/47] Characterized a stream under namespace failover in both replication modes. State-based replication carries the stream, its batches and the cursor to the standby, and the workflow goes on consuming and publishing there. Event-based replication rebuilds only the cursor from the recorded ranges, so the second suite pins that the stream does not follow the namespace yet. --- tests/xdc/stream_failover_test.go | 349 ++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 tests/xdc/stream_failover_test.go diff --git a/tests/xdc/stream_failover_test.go b/tests/xdc/stream_failover_test.go new file mode 100644 index 00000000000..0e03ada39ac --- /dev/null +++ b/tests/xdc/stream_failover_test.go @@ -0,0 +1,349 @@ +package xdc + +import ( + "context" + "slices" + "strings" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + streampb "go.temporal.io/api/stream/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/server/api/adminservice/v1" + streamlib "go.temporal.io/server/chasm/lib/stream/gen/streampb/v1" + chasmworkflow "go.temporal.io/server/chasm/lib/workflow" + "go.temporal.io/server/common/dynamicconfig" + "go.temporal.io/server/common/testing/await" + "go.temporal.io/server/tests/testcore" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/types/known/durationpb" +) + +// streamFailoverBase drives one workflow that owns a stream with records and +// an active subscription on the active cluster, so each suite below can fail +// the namespace over and look at what the standby has. +type streamFailoverBase struct { + xdcBaseSuite +} + +// StreamFailoverSuite runs with state-based replication, the mode that carries +// a CHASM tree between clusters. +type StreamFailoverSuite struct { + streamFailoverBase +} + +// StreamEventReplicationSuite runs with event-based replication, where only +// what History events carry reaches the standby. +type StreamEventReplicationSuite struct { + streamFailoverBase +} + +func TestStreamFailoverSuite(t *testing.T) { + t.Parallel() + s := &StreamFailoverSuite{} + s.enableTransitionHistory = true + suite.Run(t, s) +} + +func TestStreamEventReplicationSuite(t *testing.T) { + t.Parallel() + s := &StreamEventReplicationSuite{} + s.enableTransitionHistory = false + suite.Run(t, s) +} + +func (s *streamFailoverBase) SetupSuite() { + s.dynamicConfigOverrides = map[dynamicconfig.Key]any{ + dynamicconfig.EnableChasm.Key(): true, + dynamicconfig.TransferProcessorMaxPollInterval.Key(): 1 * time.Second, + } + s.setupSuite() +} + +func (s *streamFailoverBase) SetupTest() { + s.setupTest() +} + +func (s *streamFailoverBase) TearDownSuite() { + s.tearDownSuite() +} + +func (s *streamFailoverBase) streamClient(clusterIndex int) streamlib.StreamServiceClient { + conn, err := grpc.NewClient(s.clusters[clusterIndex].Host().FrontendGRPCAddress(), + grpc.WithTransportCredentials(insecure.NewCredentials())) + s.NoError(err) + s.T().Cleanup(func() { _ = conn.Close() }) + return streamlib.NewStreamServiceClient(conn) +} + +// streamNodesOn lists the CHASM node paths of the execution on one cluster that +// belong to its streams and cursors, sorted, or nil when the execution is not +// there. A stream a workflow owns is `Streams#`, its records are data +// nodes under `Streams$$Batches#`, and the subscription is +// `StreamCursors#`. +func (s *streamFailoverBase) streamNodesOn( + ctx context.Context, clusterIndex int, ns string, execution *commonpb.WorkflowExecution, +) []string { + resp, err := s.clusters[clusterIndex].AdminClient().DescribeMutableState(ctx, + &adminservice.DescribeMutableStateRequest{Namespace: ns, Execution: execution}) + if err != nil { + return nil + } + var paths []string + for path := range resp.GetDatabaseMutableState().GetChasmNodes() { + if strings.Contains(path, "Stream") { + paths = append(paths, path) + } + } + slices.Sort(paths) + return paths +} + +func apiBodies(records []*streampb.StreamRecord) []string { + out := make([]string, 0, len(records)) + for _, r := range records { + out = append(out, string(r.GetBody().GetData())) + } + return out +} + +func storedBodies(records []*streamlib.StreamRecord) []string { + out := make([]string, 0, len(records)) + for _, r := range records { + out = append(out, string(r.GetBody().GetData())) + } + return out +} + +func publishCommand(bodies ...string) []*commandpb.Command { + records := make([]*streampb.StreamRecord, len(bodies)) + for i, b := range bodies { + records[i] = &streampb.StreamRecord{Body: &commonpb.Payload{Data: []byte(b)}} + } + return []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + Records: records, + }, + }, + }} +} + +// streamingWorkflow is one execution driven by a raw poller on whichever +// cluster is asked, handing each task the commands queued for it. +type streamingWorkflow struct { + s *streamFailoverBase + ns string + execution *commonpb.WorkflowExecution + tq *taskqueuepb.TaskQueue + + delivered [][]*streampb.StreamSlice + commands [][]*commandpb.Command +} + +func (w *streamingWorkflow) runTask( + clusterIndex int, cmds []*commandpb.Command, +) []*streampb.StreamSlice { + w.commands = append(w.commands, cmds) + //nolint:staticcheck // SA1019: only the deprecated poller can emit this command type. + poller := &testcore.TaskPoller{ + Client: w.s.clusters[clusterIndex].FrontendClient(), + Namespace: w.ns, + TaskQueue: w.tq, + Identity: "tester", + WorkflowTaskHandler: func( + resp *workflowservice.PollWorkflowTaskQueueResponse, + ) ([]*commandpb.Command, error) { + w.delivered = append(w.delivered, resp.GetStreamSlices()) + next := w.commands[0] + w.commands = w.commands[1:] + return next, nil + }, + Logger: w.s.logger, + T: w.s.T(), + } + _, err := poller.PollAndProcessWorkflowTask() + w.s.NoError(err) + return w.delivered[len(w.delivered)-1] +} + +func (w *streamingWorkflow) currentSlice(delivered []*streampb.StreamSlice) *streampb.StreamSlice { + for _, sl := range delivered { + if sl.GetWorkflowTaskCompletedEventId() == 0 { + return sl + } + } + w.s.FailNow("no slice for the current task") + return nil +} + +func (w *streamingWorkflow) signal(ctx context.Context, clusterIndex int) { + _, err := w.s.clusters[clusterIndex].FrontendClient().SignalWorkflowExecution(ctx, + &workflowservice.SignalWorkflowExecutionRequest{ + Namespace: w.ns, WorkflowExecution: w.execution, + SignalName: "wake", Identity: "tester", RequestId: uuid.NewString(), + }) + w.s.NoError(err) +} + +// startStreamingWorkflow runs a workflow on the active cluster through one +// publish of three records, a subscription to that stream and the task that +// consumes them, then returns it with the stream nodes the active cluster holds. +func (s *streamFailoverBase) startStreamingWorkflow( + ctx context.Context, ns string, +) (*streamingWorkflow, []string) { + id := "stream-failover-" + uuid.NewString() + tq := &taskqueuepb.TaskQueue{Name: id + "-tq", Kind: enumspb.TASK_QUEUE_KIND_NORMAL} + we, err := s.clusters[0].FrontendClient().StartWorkflowExecution(ctx, + &workflowservice.StartWorkflowExecutionRequest{ + RequestId: uuid.NewString(), + Namespace: ns, + WorkflowId: id, + WorkflowType: &commonpb.WorkflowType{Name: "stream-consumer"}, + TaskQueue: tq, + WorkflowRunTimeout: durationpb.New(300 * time.Second), + WorkflowTaskTimeout: durationpb.New(10 * time.Second), + Identity: "tester", + }) + s.NoError(err) + w := &streamingWorkflow{ + s: s, + ns: ns, + execution: &commonpb.WorkflowExecution{WorkflowId: id, RunId: we.GetRunId()}, + tq: tq, + } + + w.runTask(0, publishCommand("a", "b", "c")) + _, err = s.streamClient(0).SubscribeWorkflow(ctx, &streamlib.SubscribeWorkflowRequest{ + FrontendRequest: &streamlib.SubscribeWorkflowInput{ + Namespace: ns, WorkflowId: id, StreamName: chasmworkflow.DefaultStreamName, StartOffset: 0, + }, + }) + s.NoError(err) + consumed := w.currentSlice(w.runTask(0, nil)) + s.Equal([]string{"a", "b", "c"}, apiBodies(consumed.GetRecords())) + + activeNodes := s.streamNodesOn(ctx, 0, ns, w.execution) + s.Equal([]string{ + "StreamCursors", "StreamCursors#output", + "Streams", "Streams#output", "Streams$output$Batches", "Streams$output$Batches#0", + }, activeNodes, "the active cluster holds the stream, its one batch and the cursor") + return w, activeNodes +} + +// With state-based replication the stream follows the namespace to the +// standby with its records, its consumer state and the subscription, and the +// workflow keeps consuming and publishing there. +func (s *StreamFailoverSuite) TestStreamFollowsTheNamespaceToTheStandby() { + ns := s.createGlobalNamespace() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + w, activeNodes := s.startStreamingWorkflow(ctx, ns) + + // The standby holds the same tree before the namespace moves. + await.Require(ctx, s.T(), func(t *await.T) { + require.Equal(t, activeNodes, s.streamNodesOn(ctx, 1, ns, w.execution), + "the standby's CHASM tree must carry the stream, its batches and the cursor") + }, replicationWaitTime, replicationCheckInterval) + + s.failover(ns, 0, s.clusters[1].ClusterName(), 2) + + // The records and the frontier are readable on the new active cluster. + described, err := s.streamClient(1).DescribeWorkflowStream(ctx, + &streamlib.DescribeWorkflowStreamRequest{ + FrontendRequest: &streamlib.DescribeWorkflowStreamInput{ + Namespace: ns, WorkflowId: w.execution.GetWorkflowId(), + }, + }) + s.NoError(err) + s.Equal(int64(3), described.GetFrontendResponse().GetState().GetHeadOffset()) + + polled, err := s.streamClient(1).PollWorkflowMessages(ctx, &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: ns, WorkflowId: w.execution.GetWorkflowId(), FromOffset: 0, + }, + }) + s.NoError(err) + s.Equal([]string{"a", "b", "c"}, storedBodies(polled.GetFrontendResponse().GetRecords())) + + // The workflow goes on consuming and publishing there. Its first task on + // the new cluster is a cold one, so the range it consumed before the + // failover is re-supplied from the replicated stream. + w.signal(ctx, 1) + afterFailover := w.runTask(1, publishCommand("d")) + var replayed *streampb.StreamSlice + for _, sl := range afterFailover { + if sl.GetWorkflowTaskCompletedEventId() != 0 && sl.GetToOffset() > sl.GetFromOffset() { + replayed = sl + } + } + s.NotNil(replayed, "the cold task on the new cluster re-supplies the consumed range") + s.Equal([]string{"a", "b", "c"}, apiBodies(replayed.GetRecords())) + s.Equal(int64(3), w.currentSlice(afterFailover).GetFromOffset()) + + next := w.currentSlice(w.runTask(1, nil)) + s.Equal(int64(3), next.GetFromOffset()) + s.Equal(int64(4), next.GetToOffset()) + s.Equal([]string{"d"}, apiBodies(next.GetRecords())) + + polled, err = s.streamClient(1).PollWorkflowMessages(ctx, &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: ns, WorkflowId: w.execution.GetWorkflowId(), FromOffset: 0, + }, + }) + s.NoError(err) + s.Equal([]string{"a", "b", "c", "d"}, storedBodies(polled.GetFrontendResponse().GetRecords())) +} + +// With event-based replication the standby rebuilds its state from History +// events, and the events carry the subscription and the offsets it consumed +// but never a record. So the standby ends up with the cursor and without the +// stream: the frontier reads as an unwritten stream, no record can be polled, +// and the workflow's first task there cannot find the stream its cursor names. +// Carrying the stream under this mode would take a replication task of its +// own for the component, the way state-based replication ships the CHASM +// tree; the appended event cannot carry it without putting payloads in History. +func (s *StreamEventReplicationSuite) TestStreamDoesNotReplicateUnderEventBasedReplicationYet() { + ns := s.createGlobalNamespace() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + w, _ := s.startStreamingWorkflow(ctx, ns) + + // Every replication task has been acknowledged by the standby, so what it + // holds now is what this mode carries. + s.waitForClusterSynced() + await.Require(ctx, s.T(), func(t *await.T) { + require.Equal(t, []string{"StreamCursors", "StreamCursors#output"}, + s.streamNodesOn(ctx, 1, ns, w.execution), + "the standby rebuilds the cursor from the recorded ranges and nothing of the stream") + }, replicationWaitTime, replicationCheckInterval) + + s.failover(ns, 0, s.clusters[1].ClusterName(), 2) + + described, err := s.streamClient(1).DescribeWorkflowStream(ctx, + &streamlib.DescribeWorkflowStreamRequest{ + FrontendRequest: &streamlib.DescribeWorkflowStreamInput{ + Namespace: ns, WorkflowId: w.execution.GetWorkflowId(), + }, + }) + s.NoError(err) + s.Equal(int64(0), described.GetFrontendResponse().GetState().GetHeadOffset(), + "the new active cluster sees a stream nothing has written to") + + polled, err := s.streamClient(1).PollWorkflowMessages(ctx, &streamlib.PollWorkflowMessagesRequest{ + FrontendRequest: &streamlib.PollWorkflowMessagesInput{ + Namespace: ns, WorkflowId: w.execution.GetWorkflowId(), FromOffset: 0, + }, + }) + s.NoError(err) + s.Empty(polled.GetFrontendResponse().GetRecords(), "the records did not follow the namespace") +}