From 6f6249b372647df7a443e060864fd36e112de2a5 Mon Sep 17 00:00:00 2001 From: Shaddoll Date: Fri, 15 Nov 2024 16:44:27 -0800 Subject: [PATCH] Implement matching API to allow updating task list partition config (#6472) ## What? This is a large PR that completes the implementation of the migration of task list partition config from dynamic config to database. With this PR, we're able to start the migration process. This PR does the following: - **Functionality change**: - Introduce new matching API allowing us to Update and Refresh task list partition config which is stored in database - Implement Frontend's UpdateTaskListPartitionConfig API - Update DescribeTaskList API and the admin CLI command to include task list partition config in the result. - **Observability Improvement**: - Update Matching's Task List Manager to emit metrics for task list partition config - Update Partition Config Provider to emit metrics for task list partition config - **Bug fix**: - In https://github.com/uber/cadence/pull/6427, we forgot update the partition config inside PollForActivityTask, AddActivityTask, AddDecisionTask methods. - **Documentation Improvement**: - Update document for scalable tasklist ## Test This PR has unit tests for all components. The end-to-end tests was done manually by running workflows in dev2. --- .gen/proto/matching/v1/service.pb.go | 1341 ++++++++++++++--- .gen/proto/matching/v1/service.pb.yarpc.go | 421 ++++-- client/clientfactory.go | 2 +- client/matching/client.go | 66 +- client/matching/client_test.go | 95 ++ client/matching/interface.go | 2 + client/matching/interface_mock.go | 40 + client/matching/partition_config_provider.go | 31 +- .../partition_config_provider_test.go | 4 +- .../errorinjectors/matching_generated.go | 40 + client/wrappers/grpc/matching_generated.go | 10 + client/wrappers/metered/matching_generated.go | 28 + .../wrappers/retryable/matching_generated.go | 22 + client/wrappers/thrift/matching_generated.go | 8 + client/wrappers/timeout/matching_generated.go | 12 + common/log/tag/values.go | 24 +- common/metrics/defs.go | 82 +- common/metrics/tags.go | 9 + common/types/mapper/proto/matching.go | 86 +- common/types/mapper/proto/matching_test.go | 24 + common/types/matching.go | 32 + common/types/matching_test.go | 60 + common/types/testdata/service_matching.go | 14 + docs/scalable_tasklist.md | 11 + .../uber/cadence/matching/v1/service.proto | 33 + service/frontend/admin/handler.go | 44 +- service/frontend/admin/handler_test.go | 216 +++ service/matching/config/config.go | 115 +- service/matching/config/config_test.go | 77 +- service/matching/handler/engine.go | 79 + service/matching/handler/engine_test.go | 406 +++++ service/matching/handler/handler.go | 49 + service/matching/handler/handler_test.go | 106 ++ service/matching/handler/interfaces.go | 4 + service/matching/handler/interfaces_mock.go | 60 + service/matching/tasklist/db.go | 22 + service/matching/tasklist/identifier.go | 4 +- service/matching/tasklist/interfaces.go | 2 + service/matching/tasklist/interfaces_mock.go | 28 + service/matching/tasklist/matcher.go | 33 +- service/matching/tasklist/matcher_test.go | 4 +- .../matching/tasklist/task_list_manager.go | 194 ++- .../tasklist/task_list_manager_test.go | 497 +++++- .../wrappers/grpc/grpc_handler_generated.go | 10 + tools/cli/admin_task_list_commands.go | 34 +- tools/cli/admin_task_list_commands_test.go | 2 +- tools/cli/task_list_commands.go | 7 +- 47 files changed, 3898 insertions(+), 592 deletions(-) diff --git a/.gen/proto/matching/v1/service.pb.go b/.gen/proto/matching/v1/service.pb.go index bfe34279f4e..9c8b26e5d74 100644 --- a/.gen/proto/matching/v1/service.pb.go +++ b/.gen/proto/matching/v1/service.pb.go @@ -1449,11 +1449,12 @@ func (m *DescribeTaskListRequest) GetDomainId() string { } type DescribeTaskListResponse struct { - Pollers []*v1.PollerInfo `protobuf:"bytes,1,rep,name=pollers,proto3" json:"pollers,omitempty"` - TaskListStatus *v1.TaskListStatus `protobuf:"bytes,2,opt,name=task_list_status,json=taskListStatus,proto3" json:"task_list_status,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Pollers []*v1.PollerInfo `protobuf:"bytes,1,rep,name=pollers,proto3" json:"pollers,omitempty"` + TaskListStatus *v1.TaskListStatus `protobuf:"bytes,2,opt,name=task_list_status,json=taskListStatus,proto3" json:"task_list_status,omitempty"` + PartitionConfig *v1.TaskListPartitionConfig `protobuf:"bytes,3,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *DescribeTaskListResponse) Reset() { *m = DescribeTaskListResponse{} } @@ -1503,6 +1504,13 @@ func (m *DescribeTaskListResponse) GetTaskListStatus() *v1.TaskListStatus { return nil } +func (m *DescribeTaskListResponse) GetPartitionConfig() *v1.TaskListPartitionConfig { + if m != nil { + return m.PartitionConfig + } + return nil +} + type ListTaskListPartitionsRequest struct { Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,2,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` @@ -1715,6 +1723,228 @@ func (m *GetTaskListsByDomainResponse) GetActivityTaskListMap() map[string]*Desc return nil } +type UpdateTaskListPartitionConfigRequest struct { + DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` + TaskList *v1.TaskList `protobuf:"bytes,2,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` + TaskListType v1.TaskListType `protobuf:"varint,3,opt,name=task_list_type,json=taskListType,proto3,enum=uber.cadence.api.v1.TaskListType" json:"task_list_type,omitempty"` + PartitionConfig *v1.TaskListPartitionConfig `protobuf:"bytes,4,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateTaskListPartitionConfigRequest) Reset() { *m = UpdateTaskListPartitionConfigRequest{} } +func (m *UpdateTaskListPartitionConfigRequest) String() string { return proto.CompactTextString(m) } +func (*UpdateTaskListPartitionConfigRequest) ProtoMessage() {} +func (*UpdateTaskListPartitionConfigRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_826e827d3aabf7fc, []int{22} +} +func (m *UpdateTaskListPartitionConfigRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateTaskListPartitionConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateTaskListPartitionConfigRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateTaskListPartitionConfigRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateTaskListPartitionConfigRequest.Merge(m, src) +} +func (m *UpdateTaskListPartitionConfigRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateTaskListPartitionConfigRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateTaskListPartitionConfigRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateTaskListPartitionConfigRequest proto.InternalMessageInfo + +func (m *UpdateTaskListPartitionConfigRequest) GetDomainId() string { + if m != nil { + return m.DomainId + } + return "" +} + +func (m *UpdateTaskListPartitionConfigRequest) GetTaskList() *v1.TaskList { + if m != nil { + return m.TaskList + } + return nil +} + +func (m *UpdateTaskListPartitionConfigRequest) GetTaskListType() v1.TaskListType { + if m != nil { + return m.TaskListType + } + return v1.TaskListType_TASK_LIST_TYPE_INVALID +} + +func (m *UpdateTaskListPartitionConfigRequest) GetPartitionConfig() *v1.TaskListPartitionConfig { + if m != nil { + return m.PartitionConfig + } + return nil +} + +type UpdateTaskListPartitionConfigResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateTaskListPartitionConfigResponse) Reset() { *m = UpdateTaskListPartitionConfigResponse{} } +func (m *UpdateTaskListPartitionConfigResponse) String() string { return proto.CompactTextString(m) } +func (*UpdateTaskListPartitionConfigResponse) ProtoMessage() {} +func (*UpdateTaskListPartitionConfigResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_826e827d3aabf7fc, []int{23} +} +func (m *UpdateTaskListPartitionConfigResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateTaskListPartitionConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateTaskListPartitionConfigResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateTaskListPartitionConfigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateTaskListPartitionConfigResponse.Merge(m, src) +} +func (m *UpdateTaskListPartitionConfigResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateTaskListPartitionConfigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateTaskListPartitionConfigResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateTaskListPartitionConfigResponse proto.InternalMessageInfo + +type RefreshTaskListPartitionConfigRequest struct { + DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` + TaskList *v1.TaskList `protobuf:"bytes,2,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` + TaskListType v1.TaskListType `protobuf:"varint,3,opt,name=task_list_type,json=taskListType,proto3,enum=uber.cadence.api.v1.TaskListType" json:"task_list_type,omitempty"` + PartitionConfig *v1.TaskListPartitionConfig `protobuf:"bytes,4,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RefreshTaskListPartitionConfigRequest) Reset() { *m = RefreshTaskListPartitionConfigRequest{} } +func (m *RefreshTaskListPartitionConfigRequest) String() string { return proto.CompactTextString(m) } +func (*RefreshTaskListPartitionConfigRequest) ProtoMessage() {} +func (*RefreshTaskListPartitionConfigRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_826e827d3aabf7fc, []int{24} +} +func (m *RefreshTaskListPartitionConfigRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RefreshTaskListPartitionConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RefreshTaskListPartitionConfigRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RefreshTaskListPartitionConfigRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RefreshTaskListPartitionConfigRequest.Merge(m, src) +} +func (m *RefreshTaskListPartitionConfigRequest) XXX_Size() int { + return m.Size() +} +func (m *RefreshTaskListPartitionConfigRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RefreshTaskListPartitionConfigRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RefreshTaskListPartitionConfigRequest proto.InternalMessageInfo + +func (m *RefreshTaskListPartitionConfigRequest) GetDomainId() string { + if m != nil { + return m.DomainId + } + return "" +} + +func (m *RefreshTaskListPartitionConfigRequest) GetTaskList() *v1.TaskList { + if m != nil { + return m.TaskList + } + return nil +} + +func (m *RefreshTaskListPartitionConfigRequest) GetTaskListType() v1.TaskListType { + if m != nil { + return m.TaskListType + } + return v1.TaskListType_TASK_LIST_TYPE_INVALID +} + +func (m *RefreshTaskListPartitionConfigRequest) GetPartitionConfig() *v1.TaskListPartitionConfig { + if m != nil { + return m.PartitionConfig + } + return nil +} + +type RefreshTaskListPartitionConfigResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RefreshTaskListPartitionConfigResponse) Reset() { + *m = RefreshTaskListPartitionConfigResponse{} +} +func (m *RefreshTaskListPartitionConfigResponse) String() string { return proto.CompactTextString(m) } +func (*RefreshTaskListPartitionConfigResponse) ProtoMessage() {} +func (*RefreshTaskListPartitionConfigResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_826e827d3aabf7fc, []int{25} +} +func (m *RefreshTaskListPartitionConfigResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RefreshTaskListPartitionConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RefreshTaskListPartitionConfigResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RefreshTaskListPartitionConfigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RefreshTaskListPartitionConfigResponse.Merge(m, src) +} +func (m *RefreshTaskListPartitionConfigResponse) XXX_Size() int { + return m.Size() +} +func (m *RefreshTaskListPartitionConfigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RefreshTaskListPartitionConfigResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RefreshTaskListPartitionConfigResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*TaskListPartitionConfig)(nil), "uber.cadence.matching.v1.TaskListPartitionConfig") proto.RegisterType((*PollForDecisionTaskRequest)(nil), "uber.cadence.matching.v1.PollForDecisionTaskRequest") @@ -1743,6 +1973,10 @@ func init() { proto.RegisterType((*GetTaskListsByDomainResponse)(nil), "uber.cadence.matching.v1.GetTaskListsByDomainResponse") proto.RegisterMapType((map[string]*DescribeTaskListResponse)(nil), "uber.cadence.matching.v1.GetTaskListsByDomainResponse.ActivityTaskListMapEntry") proto.RegisterMapType((map[string]*DescribeTaskListResponse)(nil), "uber.cadence.matching.v1.GetTaskListsByDomainResponse.DecisionTaskListMapEntry") + proto.RegisterType((*UpdateTaskListPartitionConfigRequest)(nil), "uber.cadence.matching.v1.UpdateTaskListPartitionConfigRequest") + proto.RegisterType((*UpdateTaskListPartitionConfigResponse)(nil), "uber.cadence.matching.v1.UpdateTaskListPartitionConfigResponse") + proto.RegisterType((*RefreshTaskListPartitionConfigRequest)(nil), "uber.cadence.matching.v1.RefreshTaskListPartitionConfigRequest") + proto.RegisterType((*RefreshTaskListPartitionConfigResponse)(nil), "uber.cadence.matching.v1.RefreshTaskListPartitionConfigResponse") } func init() { @@ -1750,145 +1984,152 @@ func init() { } var fileDescriptor_826e827d3aabf7fc = []byte{ - // 2199 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcf, 0x6f, 0xdc, 0xc6, - 0xf5, 0x07, 0xf5, 0x6b, 0xb5, 0x6f, 0xa5, 0x95, 0x3c, 0x76, 0x64, 0x7a, 0x6d, 0xcb, 0xf2, 0xe6, - 0x9b, 0x44, 0xdf, 0x22, 0xa5, 0x2c, 0x25, 0x76, 0x1d, 0x07, 0x45, 0x21, 0x4b, 0x96, 0xbd, 0x45, - 0x5d, 0x3b, 0xb4, 0xea, 0x00, 0x45, 0x60, 0x62, 0x96, 0x1c, 0x69, 0x59, 0xed, 0x92, 0x34, 0x67, - 0xb8, 0xca, 0xf6, 0xd0, 0x43, 0xd1, 0x16, 0x05, 0x7a, 0x2b, 0xda, 0xbf, 0xa0, 0x39, 0xf6, 0x0f, - 0xe9, 0xb1, 0xc7, 0x02, 0x41, 0x81, 0xc2, 0x40, 0x0f, 0x3d, 0xb6, 0x7f, 0x41, 0x31, 0xc3, 0x21, - 0x97, 0xdc, 0x1d, 0xee, 0x0f, 0x49, 0x4e, 0x7a, 0xd3, 0xcc, 0xbc, 0xdf, 0xf3, 0xde, 0xfb, 0xbc, - 0xa1, 0x16, 0xde, 0x8f, 0x9a, 0x24, 0xdc, 0xb2, 0xb1, 0x43, 0x3c, 0x9b, 0x6c, 0x75, 0x30, 0xb3, - 0x5b, 0xae, 0x77, 0xbc, 0xd5, 0xdd, 0xde, 0xa2, 0x24, 0xec, 0xba, 0x36, 0x31, 0x82, 0xd0, 0x67, - 0x3e, 0xd2, 0x39, 0x9d, 0x21, 0xe9, 0x8c, 0x84, 0xce, 0xe8, 0x6e, 0xd7, 0xd6, 0x8f, 0x7d, 0xff, - 0xb8, 0x4d, 0xb6, 0x04, 0x5d, 0x33, 0x3a, 0xda, 0x72, 0xa2, 0x10, 0x33, 0xd7, 0xf7, 0x62, 0xce, - 0xda, 0xad, 0xc1, 0x73, 0xe6, 0x76, 0x08, 0x65, 0xb8, 0x13, 0x48, 0x82, 0x21, 0x01, 0xa7, 0x21, - 0x0e, 0x02, 0x12, 0x52, 0x79, 0xbe, 0x91, 0x33, 0x11, 0x07, 0x2e, 0xb7, 0xce, 0xf6, 0x3b, 0x9d, - 0xbe, 0x0a, 0x15, 0xc5, 0xeb, 0x88, 0x84, 0x3d, 0x49, 0x50, 0x57, 0x11, 0x30, 0x4c, 0x4f, 0xda, - 0x2e, 0x65, 0x92, 0x66, 0x53, 0x45, 0x23, 0x83, 0x60, 0x9d, 0xfa, 0xe1, 0x09, 0x09, 0x25, 0xe5, - 0x77, 0xc6, 0x51, 0x1e, 0xb5, 0xfd, 0x53, 0x49, 0x7b, 0x5b, 0x45, 0xdb, 0x72, 0x29, 0xf3, 0x53, - 0xe3, 0xfe, 0x2f, 0x47, 0x42, 0x5b, 0x38, 0x24, 0xce, 0x30, 0xd5, 0x7b, 0x05, 0x54, 0x79, 0x2f, - 0xea, 0x7f, 0xd4, 0xe0, 0xea, 0x21, 0xa6, 0x27, 0x3f, 0x72, 0x29, 0x7b, 0x8e, 0x43, 0xe6, 0xf2, - 0x9b, 0xd8, 0xf3, 0xbd, 0x23, 0xf7, 0x18, 0xe9, 0x50, 0xea, 0x92, 0x90, 0xba, 0xbe, 0xa7, 0x6b, - 0x1b, 0xda, 0xe6, 0xac, 0x99, 0x2c, 0x91, 0x01, 0x97, 0xbd, 0xa8, 0x63, 0x85, 0x04, 0x3b, 0x56, - 0x90, 0x70, 0x51, 0x7d, 0x66, 0x43, 0xdb, 0x9c, 0x37, 0x2f, 0x79, 0x51, 0xc7, 0x24, 0xd8, 0x49, - 0xc5, 0x51, 0x74, 0x07, 0xae, 0x70, 0xfa, 0xd3, 0xd0, 0x65, 0x24, 0xcb, 0x30, 0x2b, 0x18, 0x90, - 0x17, 0x75, 0x3e, 0xe7, 0x47, 0x7d, 0x8e, 0xfa, 0xbf, 0x35, 0xa8, 0x3d, 0xf7, 0xdb, 0xed, 0x03, - 0x3f, 0xdc, 0x27, 0xb6, 0xcb, 0xb5, 0x72, 0x33, 0x4d, 0xf2, 0x3a, 0x22, 0x94, 0xa1, 0x06, 0x94, - 0xc2, 0xf8, 0x4f, 0x61, 0x5a, 0x65, 0x67, 0xcb, 0xc8, 0x25, 0x1c, 0x0e, 0x5c, 0xa3, 0xbb, 0x6d, - 0x14, 0x4b, 0x30, 0x13, 0x7e, 0x74, 0x1d, 0xca, 0x8e, 0xdf, 0xc1, 0xae, 0x67, 0xb9, 0x8e, 0xf0, - 0xa0, 0x6c, 0x2e, 0xc6, 0x1b, 0x0d, 0x87, 0x1f, 0x06, 0x7e, 0xbb, 0x4d, 0x42, 0x7e, 0x38, 0x1b, - 0x1f, 0xc6, 0x1b, 0x0d, 0x07, 0xbd, 0x07, 0xd5, 0x23, 0x3f, 0x3c, 0xc5, 0xa1, 0x43, 0x1c, 0xeb, - 0x28, 0xf4, 0x3b, 0xfa, 0x9c, 0xa0, 0x58, 0x4e, 0x77, 0x0f, 0x42, 0xbf, 0x83, 0x3e, 0x80, 0x15, - 0x97, 0xfa, 0x6d, 0x91, 0xe3, 0xd6, 0x71, 0xe8, 0x47, 0x81, 0x3e, 0x2f, 0xe8, 0xaa, 0xe9, 0xf6, - 0x63, 0xbe, 0x5b, 0xff, 0x3d, 0xc0, 0x75, 0xa5, 0xc5, 0x34, 0xf0, 0x3d, 0x4a, 0xd0, 0x4d, 0x00, - 0x7e, 0x7b, 0x16, 0xf3, 0x4f, 0x48, 0x7c, 0x25, 0x4b, 0x66, 0x99, 0xef, 0x1c, 0xf2, 0x0d, 0xf4, - 0x13, 0x40, 0x49, 0x32, 0x59, 0xe4, 0x4b, 0x62, 0x47, 0x5c, 0xb2, 0xf0, 0xa8, 0xb2, 0xf3, 0xbe, - 0x32, 0x3c, 0x9f, 0x4b, 0xf2, 0x47, 0x09, 0xb5, 0x79, 0xe9, 0x74, 0x70, 0x0b, 0x1d, 0xc0, 0x72, - 0x2a, 0x96, 0xf5, 0x02, 0x22, 0xc2, 0x50, 0xd9, 0xb9, 0x3d, 0x52, 0xe2, 0x61, 0x2f, 0x20, 0xe6, - 0xd2, 0x69, 0x66, 0x85, 0x5e, 0xc2, 0xb5, 0x20, 0x24, 0x5d, 0xd7, 0x8f, 0xa8, 0x45, 0x19, 0x0e, - 0x19, 0x71, 0x2c, 0xd2, 0x25, 0x1e, 0xe3, 0xa1, 0x9d, 0x13, 0x32, 0xaf, 0x1b, 0x71, 0x69, 0x1b, - 0x49, 0x69, 0x1b, 0x0d, 0x8f, 0xdd, 0xfb, 0xf8, 0x25, 0x6e, 0x47, 0xc4, 0x5c, 0x4b, 0xb8, 0x5f, - 0xc4, 0xcc, 0x8f, 0x38, 0x6f, 0xc3, 0x41, 0x9b, 0xb0, 0x3a, 0x24, 0x6e, 0x5e, 0xa4, 0x6b, 0x95, - 0xe6, 0x29, 0x75, 0x28, 0x61, 0xc6, 0x48, 0x27, 0x60, 0xfa, 0x82, 0x48, 0xbc, 0x64, 0x89, 0xea, - 0xb0, 0xec, 0x91, 0x2f, 0x59, 0x5f, 0x40, 0x49, 0x08, 0xa8, 0xf0, 0xcd, 0x84, 0xfb, 0x43, 0x40, - 0x4d, 0x6c, 0x9f, 0xb4, 0xfd, 0x63, 0xcb, 0xf6, 0x23, 0x8f, 0x59, 0x2d, 0xd7, 0x63, 0xfa, 0xa2, - 0x20, 0x5c, 0x95, 0x27, 0x7b, 0xfc, 0xe0, 0x89, 0xeb, 0x31, 0x74, 0x1f, 0x74, 0xca, 0x5c, 0xfb, - 0xa4, 0xd7, 0xbf, 0x0a, 0x8b, 0x78, 0xb8, 0xd9, 0x26, 0x8e, 0x5e, 0xde, 0xd0, 0x36, 0x17, 0xcd, - 0xb5, 0xf8, 0x3c, 0x0d, 0xf4, 0xa3, 0xf8, 0x14, 0xdd, 0x87, 0x79, 0xd1, 0x8a, 0x74, 0x10, 0x31, - 0xa9, 0x8f, 0x8c, 0xf3, 0x67, 0x9c, 0xd2, 0x8c, 0x19, 0x90, 0x09, 0xcb, 0x8e, 0xcc, 0x1b, 0xcb, - 0xf5, 0x8e, 0x7c, 0xbd, 0x22, 0x24, 0x7c, 0x37, 0x2f, 0x21, 0x6e, 0x05, 0x5c, 0xc8, 0x61, 0x88, - 0x3d, 0xea, 0x12, 0x8f, 0x25, 0xd9, 0xd6, 0xf0, 0x8e, 0x7c, 0x73, 0xc9, 0xc9, 0xac, 0xd0, 0x2b, - 0xb8, 0x31, 0x9c, 0x54, 0x96, 0x48, 0x43, 0xde, 0x45, 0xf4, 0x25, 0xa1, 0xe2, 0xa6, 0xd2, 0xc8, - 0xa4, 0xaf, 0x98, 0xd7, 0x86, 0xb2, 0x2a, 0x39, 0xe2, 0x9d, 0x24, 0x0e, 0x3a, 0xef, 0x5d, 0xc4, - 0x4a, 0xfa, 0xcd, 0x72, 0xdc, 0x49, 0xc4, 0xd1, 0x0b, 0x7e, 0xf2, 0x52, 0x76, 0x9e, 0xdb, 0xb0, - 0xd4, 0x0c, 0xb1, 0x67, 0xb7, 0x64, 0x15, 0x54, 0x45, 0x15, 0x54, 0xe2, 0xbd, 0xb8, 0x0e, 0x76, - 0xa1, 0x4a, 0xed, 0x16, 0x71, 0xa2, 0x36, 0x71, 0x2c, 0x0e, 0x1e, 0xfa, 0x8a, 0x30, 0xb2, 0x36, - 0x94, 0x5d, 0x87, 0x09, 0xb2, 0x98, 0xcb, 0x29, 0x07, 0xdf, 0x43, 0xdf, 0x87, 0xa5, 0x24, 0xa7, - 0x84, 0x80, 0xd5, 0xb1, 0x02, 0x2a, 0x92, 0x5e, 0xb0, 0x7f, 0x01, 0x25, 0x7e, 0x23, 0x2e, 0xa1, - 0xfa, 0xa5, 0x8d, 0xd9, 0xcd, 0xca, 0xce, 0x43, 0xa3, 0x08, 0x0e, 0x8d, 0x11, 0x05, 0x6f, 0x7c, - 0x16, 0x0b, 0x79, 0xe4, 0xb1, 0xb0, 0x67, 0x26, 0x22, 0x79, 0xc8, 0x98, 0xcf, 0x70, 0xdb, 0x92, - 0x0d, 0xdf, 0x6a, 0xf6, 0x18, 0xa1, 0x3a, 0x12, 0x99, 0x78, 0x49, 0x1c, 0x3d, 0x89, 0x4f, 0x1e, - 0xf2, 0x03, 0xf4, 0x05, 0xac, 0xa6, 0x2d, 0xd7, 0xb2, 0x45, 0x6b, 0xd7, 0x2f, 0x0b, 0x87, 0xb6, - 0x8b, 0xcd, 0x2a, 0xc0, 0x04, 0x73, 0x25, 0xc8, 0x6f, 0xd4, 0x5e, 0xc1, 0x52, 0xd6, 0x4c, 0xb4, - 0x0a, 0xb3, 0x27, 0xa4, 0x27, 0xba, 0x53, 0xd9, 0xe4, 0x7f, 0xf2, 0x84, 0xee, 0xf2, 0x0a, 0x96, - 0xad, 0x68, 0xa2, 0x84, 0x16, 0x0c, 0x0f, 0x66, 0xee, 0x6b, 0x59, 0x20, 0xd8, 0xb5, 0x99, 0xdb, - 0x75, 0x59, 0xef, 0xec, 0x40, 0xa0, 0x90, 0xf0, 0xbf, 0x08, 0x04, 0xff, 0x5a, 0x4c, 0x81, 0x20, - 0x6f, 0xf1, 0xb7, 0x0a, 0x04, 0xb7, 0xa0, 0x82, 0xa5, 0x35, 0xfd, 0x20, 0x40, 0xb2, 0xd5, 0x70, - 0x38, 0x52, 0xa4, 0x04, 0x02, 0x29, 0xe6, 0x46, 0x20, 0x45, 0xea, 0x98, 0x40, 0x0a, 0x9c, 0x59, - 0xa1, 0x1d, 0x98, 0x77, 0xbd, 0x20, 0x62, 0x22, 0x3a, 0x95, 0x9d, 0x1b, 0xea, 0x1b, 0xc5, 0xbd, - 0xb6, 0x8f, 0x1d, 0x33, 0x26, 0x55, 0x14, 0xfd, 0xc2, 0x79, 0x8b, 0xbe, 0x34, 0x5d, 0xd1, 0x1f, - 0xc2, 0xb5, 0x44, 0x9e, 0xc5, 0x7c, 0xcb, 0x6e, 0xfb, 0x94, 0x08, 0x41, 0x7e, 0x14, 0xc3, 0x44, - 0x65, 0xe7, 0xda, 0x90, 0xac, 0x7d, 0x39, 0xfb, 0x9a, 0x6b, 0x09, 0xef, 0xa1, 0xbf, 0xc7, 0x39, - 0x0f, 0x63, 0x46, 0xf4, 0x63, 0x58, 0x13, 0x4a, 0x86, 0x45, 0x96, 0xc7, 0x89, 0xbc, 0x2c, 0x18, - 0x07, 0xe4, 0x1d, 0xc0, 0xa5, 0x16, 0xc1, 0x21, 0x6b, 0x12, 0xcc, 0x52, 0x51, 0x30, 0x4e, 0xd4, - 0x6a, 0xca, 0x93, 0xc8, 0xc9, 0x60, 0x69, 0x25, 0x8f, 0xa5, 0xaf, 0x60, 0x3d, 0x7f, 0x13, 0x96, - 0x7f, 0x64, 0xb1, 0x96, 0x4b, 0xad, 0x84, 0x61, 0x69, 0x6c, 0x60, 0x6b, 0xb9, 0x9b, 0x79, 0x76, - 0x74, 0xd8, 0x72, 0xe9, 0xae, 0x94, 0xdf, 0xc8, 0x7a, 0xe0, 0x10, 0x86, 0xdd, 0x36, 0x15, 0x78, - 0x31, 0x2e, 0x53, 0xfa, 0x4e, 0xec, 0xc7, 0x5c, 0xc3, 0xa3, 0x4d, 0xf5, 0x6c, 0xa3, 0xcd, 0x07, - 0xb0, 0x92, 0xca, 0x89, 0x3b, 0x86, 0x80, 0x9c, 0xb2, 0x59, 0x4d, 0xb6, 0xf7, 0xc5, 0x2e, 0xfa, - 0x08, 0x16, 0x5a, 0x04, 0x3b, 0x24, 0x94, 0x88, 0x72, 0x5d, 0xa9, 0xe9, 0x89, 0x20, 0x31, 0x25, - 0xe9, 0xdb, 0xed, 0xdf, 0xf5, 0xbf, 0xcd, 0xc1, 0xda, 0xae, 0xe3, 0xa8, 0x86, 0xec, 0x5c, 0x43, - 0xd4, 0x06, 0x1a, 0xe2, 0x5b, 0x6a, 0x32, 0x0f, 0xa0, 0xdc, 0x1f, 0x2e, 0x66, 0x27, 0x19, 0x2e, - 0x16, 0x59, 0x32, 0x4b, 0xdc, 0x82, 0x4a, 0x5a, 0x81, 0x72, 0xa6, 0x9c, 0x35, 0x21, 0xd9, 0x6a, - 0x38, 0x83, 0x25, 0x2a, 0x0b, 0x4b, 0x16, 0xc1, 0xfc, 0x14, 0x25, 0x2a, 0x46, 0xd0, 0xa4, 0x14, - 0x1e, 0xc0, 0x02, 0xf5, 0xa3, 0xd0, 0x8e, 0x5b, 0x4e, 0x75, 0x10, 0xe0, 0x32, 0xf3, 0x16, 0xa6, - 0x27, 0x2f, 0x04, 0xa5, 0x29, 0x39, 0x14, 0xc8, 0x51, 0x52, 0x21, 0x47, 0xa0, 0x48, 0x81, 0x45, - 0x31, 0x59, 0x3c, 0x2a, 0x4e, 0x01, 0xf5, 0xad, 0x1a, 0x03, 0x19, 0x11, 0x0f, 0x17, 0x43, 0xb0, - 0xfe, 0x10, 0xae, 0xa8, 0x08, 0x15, 0xf0, 0x7e, 0x25, 0x0b, 0xef, 0xe5, 0x2c, 0x74, 0x9f, 0xc2, - 0xd5, 0x21, 0x1b, 0x24, 0x82, 0xa9, 0x72, 0x5a, 0xbb, 0xb0, 0x9c, 0xfe, 0xcf, 0xbc, 0xc8, 0x69, - 0xd5, 0xbc, 0xf0, 0x6d, 0xe4, 0x34, 0x7f, 0xa1, 0x88, 0xeb, 0xb6, 0xfa, 0xaa, 0x63, 0xf4, 0xac, - 0xc6, 0xfb, 0xfb, 0x89, 0x01, 0xb9, 0xec, 0x9f, 0x3b, 0x57, 0xf6, 0xcf, 0x4f, 0x97, 0xfd, 0x0b, - 0xe7, 0xcf, 0xfe, 0xd2, 0x05, 0x64, 0xff, 0xa2, 0x2a, 0xfb, 0x3d, 0xd0, 0x71, 0xe6, 0x2a, 0xf7, - 0x5d, 0x1a, 0xf0, 0xac, 0xe0, 0xef, 0x13, 0x89, 0x82, 0x3b, 0x23, 0xaa, 0xa0, 0x80, 0xd3, 0x2c, - 0x94, 0xa9, 0xac, 0x36, 0x98, 0xa0, 0xda, 0x14, 0xf9, 0xf6, 0x0d, 0x56, 0xdb, 0xd7, 0xb3, 0xa0, - 0x17, 0x39, 0x8b, 0x7e, 0x08, 0x2b, 0x7d, 0x50, 0x16, 0xaf, 0x2a, 0x59, 0x6e, 0x6a, 0xac, 0x93, - 0xef, 0x07, 0xf1, 0xf4, 0x35, 0xfb, 0x83, 0x95, 0x58, 0x0f, 0xcd, 0x49, 0x33, 0xd3, 0xcd, 0x49, - 0x99, 0xc9, 0x61, 0x76, 0xda, 0xc9, 0x61, 0xee, 0xe2, 0x27, 0x87, 0xf9, 0x8b, 0x99, 0x1c, 0x16, - 0x2e, 0x6c, 0x72, 0x28, 0xa9, 0x26, 0x07, 0xd9, 0x4b, 0x95, 0xaf, 0x81, 0xb7, 0xdb, 0x4b, 0xbf, - 0xd6, 0xe0, 0x8a, 0x78, 0x94, 0x25, 0x5e, 0x24, 0x9d, 0x74, 0x6f, 0xf0, 0xe5, 0xf5, 0xff, 0x4a, - 0xe7, 0x55, 0xbc, 0x13, 0xbe, 0xb9, 0xce, 0x33, 0x0b, 0x4c, 0xf6, 0x24, 0xab, 0xff, 0x49, 0x83, - 0x77, 0x06, 0x2c, 0x94, 0x51, 0xfd, 0x01, 0x2c, 0x89, 0xaf, 0x2a, 0x56, 0x48, 0x68, 0xd4, 0x4e, - 0x7c, 0x1c, 0x9d, 0x27, 0x15, 0xc1, 0x61, 0x0a, 0x06, 0xd4, 0x80, 0x6a, 0x22, 0xe0, 0x67, 0xc4, - 0x66, 0xc4, 0x19, 0xf9, 0xfe, 0x8d, 0xdf, 0xbd, 0x92, 0xd2, 0x5c, 0x7e, 0x9d, 0x5d, 0xd6, 0xff, - 0xa9, 0xc1, 0x46, 0x6c, 0x98, 0x23, 0xe8, 0xb8, 0xbf, 0x7b, 0x7e, 0x27, 0x68, 0x13, 0x4e, 0x2c, - 0x43, 0xf9, 0x6c, 0xf0, 0x3e, 0xee, 0x2a, 0x15, 0x8d, 0x93, 0xf3, 0x0d, 0xdc, 0xcd, 0x55, 0x28, - 0x09, 0x5e, 0x39, 0xa3, 0x95, 0xcd, 0x05, 0xbe, 0x6c, 0x38, 0xf5, 0x77, 0xe1, 0xf6, 0x08, 0xf3, - 0xe2, 0x8b, 0xa9, 0xff, 0x5d, 0x83, 0x1b, 0x7b, 0xd8, 0xb3, 0x49, 0xfb, 0x59, 0xc4, 0x28, 0xc3, - 0x9e, 0xe3, 0x7a, 0xc7, 0xfc, 0xb5, 0x3c, 0x11, 0xc4, 0xe7, 0xde, 0xf1, 0x33, 0x03, 0xef, 0xf8, - 0xc7, 0x50, 0x4d, 0x9d, 0xea, 0x7f, 0xeb, 0xac, 0x16, 0x94, 0x75, 0xe2, 0x59, 0x5c, 0xd6, 0x2c, - 0xb3, 0x3a, 0x0f, 0x8e, 0xd7, 0x6f, 0xc1, 0xcd, 0x02, 0xf7, 0x64, 0x00, 0x7e, 0x01, 0x57, 0xf7, - 0x09, 0xb5, 0x43, 0xb7, 0x49, 0x52, 0x76, 0xe9, 0xfa, 0xc1, 0x60, 0x0e, 0x7c, 0xa8, 0xd4, 0x5a, - 0xc0, 0x3e, 0xd9, 0xd5, 0xd7, 0xbf, 0xd2, 0x40, 0x1f, 0x96, 0x20, 0xcb, 0xe6, 0x13, 0x28, 0xc5, - 0xe1, 0xa4, 0xba, 0x26, 0x20, 0xf3, 0x56, 0xe1, 0xf7, 0x18, 0x12, 0x0a, 0x1c, 0x4e, 0xe8, 0xd1, - 0x53, 0x58, 0xed, 0x47, 0x9f, 0x32, 0xcc, 0x22, 0x2a, 0x4b, 0xe6, 0xdd, 0x91, 0xb1, 0x7b, 0x21, - 0x48, 0xcd, 0x2a, 0xcb, 0xad, 0xeb, 0x14, 0x6e, 0x8a, 0xfb, 0x18, 0x6c, 0x74, 0x34, 0x09, 0xd6, - 0x1a, 0x2c, 0xc8, 0x96, 0x1b, 0x27, 0x89, 0x5c, 0xe5, 0x2f, 0x6f, 0x66, 0xba, 0xcb, 0xfb, 0xcd, - 0x0c, 0xac, 0x17, 0x69, 0x95, 0x11, 0x7a, 0x0d, 0x37, 0xfb, 0x5f, 0x49, 0x52, 0x7f, 0x33, 0xff, - 0x14, 0x89, 0xe3, 0x66, 0x8c, 0x54, 0x99, 0xca, 0x7d, 0x4a, 0x18, 0x76, 0x30, 0xc3, 0x66, 0x2d, - 0x3b, 0xce, 0xe4, 0x55, 0x73, 0x95, 0xe9, 0x87, 0x61, 0xa5, 0xca, 0x99, 0xb3, 0xa9, 0x74, 0x32, - 0xa3, 0x7d, 0x5e, 0x65, 0xfd, 0x2e, 0x5c, 0x7f, 0x4c, 0xd2, 0x30, 0xd0, 0x87, 0xbd, 0x18, 0xc7, - 0xc6, 0xc4, 0xbe, 0xfe, 0xd5, 0x1c, 0xdc, 0x50, 0xf3, 0xc9, 0xe8, 0xfd, 0x4a, 0x83, 0x35, 0x85, - 0x2f, 0x1d, 0x1c, 0xc8, 0xb8, 0x3d, 0x2b, 0xc6, 0xbc, 0x51, 0x82, 0x8d, 0xfd, 0x01, 0x5f, 0x9e, - 0xe2, 0x20, 0x1e, 0xd6, 0x2e, 0x3b, 0xc3, 0x27, 0xc2, 0x0c, 0xc5, 0x2d, 0x72, 0x33, 0x66, 0xce, - 0x65, 0xc6, 0xee, 0xc0, 0x2d, 0xf6, 0xcd, 0xc0, 0xc3, 0x27, 0xb5, 0x9f, 0xf3, 0x4a, 0x54, 0xdb, - 0xad, 0x98, 0x1d, 0x9f, 0xe4, 0x3f, 0xc4, 0x8e, 0x18, 0x9a, 0x8b, 0xca, 0x3b, 0x33, 0x6f, 0x72, - 0xdd, 0x45, 0xc6, 0xbe, 0x6d, 0xdd, 0x3b, 0x7f, 0x06, 0xa8, 0x3c, 0x95, 0x3c, 0xbb, 0xcf, 0x1b, - 0xe8, 0x97, 0x1a, 0x5c, 0x56, 0x7c, 0x48, 0x47, 0x1f, 0x4f, 0xf9, 0xdd, 0x5d, 0x24, 0x67, 0xed, - 0xee, 0x99, 0xbe, 0xd6, 0x67, 0x8d, 0xc8, 0x06, 0x66, 0x02, 0x23, 0x14, 0x0f, 0x87, 0x09, 0x8c, - 0x50, 0x0e, 0x83, 0x5d, 0x58, 0x19, 0x78, 0x73, 0xa3, 0x3b, 0xd3, 0x7e, 0x22, 0xa8, 0x6d, 0x4f, - 0xc1, 0x91, 0xd3, 0x9b, 0xf3, 0xfb, 0xce, 0xb4, 0x8f, 0xa5, 0x31, 0x7a, 0x95, 0xfe, 0x06, 0xb0, - 0x9c, 0x9b, 0xdf, 0x90, 0x51, 0x2c, 0x43, 0x35, 0x8a, 0xd6, 0xb6, 0x26, 0xa6, 0x97, 0x1a, 0xff, - 0xa0, 0xc1, 0xb5, 0xc2, 0x29, 0x05, 0x3d, 0x28, 0x16, 0x37, 0x6e, 0xf2, 0xaa, 0x7d, 0x7a, 0x26, - 0x5e, 0x69, 0xd6, 0x6f, 0x35, 0x78, 0x47, 0x39, 0x37, 0xa0, 0x7b, 0xc5, 0x62, 0x47, 0xcd, 0x51, - 0xb5, 0xef, 0x4d, 0xcd, 0x27, 0x4d, 0xe9, 0xc1, 0xea, 0x60, 0x11, 0xa3, 0xed, 0x69, 0x0a, 0x3e, - 0xd6, 0x7f, 0x86, 0x1e, 0x81, 0x7e, 0xa7, 0xc1, 0x9a, 0x1a, 0x7f, 0xd1, 0x08, 0x77, 0x46, 0xce, - 0x09, 0xb5, 0xfb, 0xd3, 0x33, 0x4a, 0x6b, 0x7e, 0xad, 0xc1, 0x15, 0x55, 0xb7, 0x47, 0x77, 0xa7, - 0x45, 0x87, 0xd8, 0x92, 0x7b, 0x67, 0x03, 0x95, 0x87, 0x8f, 0xff, 0xf2, 0x66, 0x5d, 0xfb, 0xeb, - 0x9b, 0x75, 0xed, 0x1f, 0x6f, 0xd6, 0xb5, 0x9f, 0x7e, 0x72, 0xec, 0xb2, 0x56, 0xd4, 0x34, 0x6c, - 0xbf, 0xb3, 0x95, 0xfb, 0x91, 0x88, 0x71, 0x4c, 0xbc, 0xf8, 0x57, 0x35, 0xd9, 0x1f, 0xf6, 0x7c, - 0x9a, 0xfc, 0xdd, 0xdd, 0x6e, 0x2e, 0x88, 0xd3, 0x8f, 0xfe, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x61, - 0x6e, 0x31, 0xa3, 0x06, 0x24, 0x00, 0x00, + // 2313 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xdf, 0x6f, 0x1c, 0x49, + 0xf1, 0xd7, 0xf8, 0xd7, 0xda, 0xb5, 0xf6, 0xda, 0xe9, 0xe4, 0x9c, 0xc9, 0x26, 0x76, 0x9c, 0xbd, + 0x4b, 0xe2, 0xef, 0x57, 0xc7, 0x3a, 0xf6, 0x5d, 0x42, 0x2e, 0x27, 0x08, 0x8e, 0x1d, 0x27, 0x8b, + 0x08, 0xc9, 0x4d, 0x7c, 0x89, 0x84, 0x4e, 0x19, 0xb5, 0x77, 0x7a, 0xbd, 0x83, 0x77, 0x67, 0x26, + 0xd3, 0x3d, 0xeb, 0x5b, 0x1e, 0x78, 0x40, 0x80, 0x90, 0x78, 0x43, 0xf0, 0x0e, 0x1c, 0x7f, 0x0c, + 0x8f, 0x3c, 0x22, 0x9d, 0x90, 0x50, 0x24, 0x1e, 0x78, 0x04, 0x89, 0x47, 0x24, 0xd4, 0x3f, 0x66, + 0x77, 0x66, 0xb7, 0x67, 0x7f, 0xd8, 0xce, 0x1d, 0x0f, 0xbc, 0xb9, 0xbb, 0xab, 0xaa, 0xab, 0xaa, + 0xab, 0xea, 0x53, 0x35, 0x6b, 0xb8, 0x11, 0x1d, 0x90, 0x70, 0xa3, 0x8a, 0x1d, 0xe2, 0x55, 0xc9, + 0x46, 0x13, 0xb3, 0x6a, 0xdd, 0xf5, 0x0e, 0x37, 0x5a, 0x9b, 0x1b, 0x94, 0x84, 0x2d, 0xb7, 0x4a, + 0xca, 0x41, 0xe8, 0x33, 0x1f, 0x99, 0x9c, 0xae, 0xac, 0xe8, 0xca, 0x31, 0x5d, 0xb9, 0xb5, 0x59, + 0x5c, 0x3d, 0xf4, 0xfd, 0xc3, 0x06, 0xd9, 0x10, 0x74, 0x07, 0x51, 0x6d, 0xc3, 0x89, 0x42, 0xcc, + 0x5c, 0xdf, 0x93, 0x9c, 0xc5, 0xab, 0xbd, 0xe7, 0xcc, 0x6d, 0x12, 0xca, 0x70, 0x33, 0x50, 0x04, + 0x7d, 0x02, 0x8e, 0x43, 0x1c, 0x04, 0x24, 0xa4, 0xea, 0x7c, 0x2d, 0xa5, 0x22, 0x0e, 0x5c, 0xae, + 0x5d, 0xd5, 0x6f, 0x36, 0xbb, 0x57, 0xe8, 0x28, 0x5e, 0x47, 0x24, 0x6c, 0x2b, 0x82, 0x92, 0x8e, + 0x80, 0x61, 0x7a, 0xd4, 0x70, 0x29, 0x53, 0x34, 0xeb, 0x3a, 0x1a, 0xe5, 0x04, 0xfb, 0xd8, 0x0f, + 0x8f, 0x48, 0xa8, 0x28, 0xff, 0x7f, 0x18, 0x65, 0xad, 0xe1, 0x1f, 0x2b, 0xda, 0x6b, 0x3a, 0xda, + 0xba, 0x4b, 0x99, 0xdf, 0x51, 0xee, 0xbd, 0x14, 0x09, 0xad, 0xe3, 0x90, 0x38, 0xfd, 0x54, 0xd7, + 0x33, 0xa8, 0xd2, 0x56, 0x94, 0x7e, 0x63, 0xc0, 0xc5, 0x7d, 0x4c, 0x8f, 0xbe, 0xe7, 0x52, 0xf6, + 0x0c, 0x87, 0xcc, 0xe5, 0x2f, 0xb1, 0xe3, 0x7b, 0x35, 0xf7, 0x10, 0x99, 0x90, 0x6b, 0x91, 0x90, + 0xba, 0xbe, 0x67, 0x1a, 0x6b, 0xc6, 0xfa, 0xa4, 0x15, 0x2f, 0x51, 0x19, 0xce, 0x7b, 0x51, 0xd3, + 0x0e, 0x09, 0x76, 0xec, 0x20, 0xe6, 0xa2, 0xe6, 0xc4, 0x9a, 0xb1, 0x3e, 0x6d, 0x9d, 0xf3, 0xa2, + 0xa6, 0x45, 0xb0, 0xd3, 0x11, 0x47, 0xd1, 0x2d, 0xb8, 0xc0, 0xe9, 0x8f, 0x43, 0x97, 0x91, 0x24, + 0xc3, 0xa4, 0x60, 0x40, 0x5e, 0xd4, 0x7c, 0xc9, 0x8f, 0xba, 0x1c, 0xa5, 0x7f, 0x18, 0x50, 0x7c, + 0xe6, 0x37, 0x1a, 0x7b, 0x7e, 0xb8, 0x4b, 0xaa, 0x2e, 0xbf, 0x95, 0xab, 0x69, 0x91, 0xd7, 0x11, + 0xa1, 0x0c, 0x55, 0x20, 0x17, 0xca, 0x3f, 0x85, 0x6a, 0xf9, 0xad, 0x8d, 0x72, 0x2a, 0xe0, 0x70, + 0xe0, 0x96, 0x5b, 0x9b, 0xe5, 0x6c, 0x09, 0x56, 0xcc, 0x8f, 0x2e, 0xc3, 0x9c, 0xe3, 0x37, 0xb1, + 0xeb, 0xd9, 0xae, 0x23, 0x2c, 0x98, 0xb3, 0x66, 0xe5, 0x46, 0xc5, 0xe1, 0x87, 0x81, 0xdf, 0x68, + 0x90, 0x90, 0x1f, 0x4e, 0xca, 0x43, 0xb9, 0x51, 0x71, 0xd0, 0x75, 0x28, 0xd4, 0xfc, 0xf0, 0x18, + 0x87, 0x0e, 0x71, 0xec, 0x5a, 0xe8, 0x37, 0xcd, 0x29, 0x41, 0xb1, 0xd0, 0xd9, 0xdd, 0x0b, 0xfd, + 0x26, 0xba, 0x09, 0x8b, 0x2e, 0xf5, 0x1b, 0x22, 0xc6, 0xed, 0xc3, 0xd0, 0x8f, 0x02, 0x73, 0x5a, + 0xd0, 0x15, 0x3a, 0xdb, 0x8f, 0xf8, 0x6e, 0xe9, 0x57, 0x00, 0x97, 0xb5, 0x1a, 0xd3, 0xc0, 0xf7, + 0x28, 0x41, 0x2b, 0x00, 0xfc, 0xf5, 0x6c, 0xe6, 0x1f, 0x11, 0xf9, 0x24, 0xf3, 0xd6, 0x1c, 0xdf, + 0xd9, 0xe7, 0x1b, 0xe8, 0x53, 0x40, 0x71, 0x30, 0xd9, 0xe4, 0x73, 0x52, 0x8d, 0xb8, 0x64, 0x61, + 0x51, 0x7e, 0xeb, 0x86, 0xd6, 0x3d, 0x2f, 0x15, 0xf9, 0xc3, 0x98, 0xda, 0x3a, 0x77, 0xdc, 0xbb, + 0x85, 0xf6, 0x60, 0xa1, 0x23, 0x96, 0xb5, 0x03, 0x22, 0xdc, 0x90, 0xdf, 0xba, 0x36, 0x50, 0xe2, + 0x7e, 0x3b, 0x20, 0xd6, 0xfc, 0x71, 0x62, 0x85, 0x5e, 0xc0, 0xa5, 0x20, 0x24, 0x2d, 0xd7, 0x8f, + 0xa8, 0x4d, 0x19, 0x0e, 0x19, 0x71, 0x6c, 0xd2, 0x22, 0x1e, 0xe3, 0xae, 0x9d, 0x12, 0x32, 0x2f, + 0x97, 0x65, 0x6a, 0x97, 0xe3, 0xd4, 0x2e, 0x57, 0x3c, 0x76, 0xe7, 0xc3, 0x17, 0xb8, 0x11, 0x11, + 0x6b, 0x39, 0xe6, 0x7e, 0x2e, 0x99, 0x1f, 0x72, 0xde, 0x8a, 0x83, 0xd6, 0x61, 0xa9, 0x4f, 0xdc, + 0xb4, 0x08, 0xd7, 0x02, 0x4d, 0x53, 0x9a, 0x90, 0xc3, 0x8c, 0x91, 0x66, 0xc0, 0xcc, 0x19, 0x11, + 0x78, 0xf1, 0x12, 0x95, 0x60, 0xc1, 0x23, 0x9f, 0xb3, 0xae, 0x80, 0x9c, 0x10, 0x90, 0xe7, 0x9b, + 0x31, 0xf7, 0xfb, 0x80, 0x0e, 0x70, 0xf5, 0xa8, 0xe1, 0x1f, 0xda, 0x55, 0x3f, 0xf2, 0x98, 0x5d, + 0x77, 0x3d, 0x66, 0xce, 0x0a, 0xc2, 0x25, 0x75, 0xb2, 0xc3, 0x0f, 0x1e, 0xbb, 0x1e, 0x43, 0x77, + 0xc1, 0xa4, 0xcc, 0xad, 0x1e, 0xb5, 0xbb, 0x4f, 0x61, 0x13, 0x0f, 0x1f, 0x34, 0x88, 0x63, 0xce, + 0xad, 0x19, 0xeb, 0xb3, 0xd6, 0xb2, 0x3c, 0xef, 0x38, 0xfa, 0xa1, 0x3c, 0x45, 0x77, 0x61, 0x5a, + 0x94, 0x22, 0x13, 0x84, 0x4f, 0x4a, 0x03, 0xfd, 0xfc, 0x09, 0xa7, 0xb4, 0x24, 0x03, 0xb2, 0x60, + 0xc1, 0x51, 0x71, 0x63, 0xbb, 0x5e, 0xcd, 0x37, 0xf3, 0x42, 0xc2, 0x37, 0xd2, 0x12, 0x64, 0x29, + 0xe0, 0x42, 0xf6, 0x43, 0xec, 0x51, 0x97, 0x78, 0x2c, 0x8e, 0xb6, 0x8a, 0x57, 0xf3, 0xad, 0x79, + 0x27, 0xb1, 0x42, 0xaf, 0xe0, 0x4a, 0x7f, 0x50, 0xd9, 0x22, 0x0c, 0x79, 0x15, 0x31, 0xe7, 0xc5, + 0x15, 0x2b, 0x5a, 0x25, 0xe3, 0xba, 0x62, 0x5d, 0xea, 0x8b, 0xaa, 0xf8, 0x88, 0x57, 0x12, 0xe9, + 0x74, 0x5e, 0xbb, 0x88, 0x1d, 0xd7, 0x9b, 0x05, 0x59, 0x49, 0xc4, 0xd1, 0x73, 0x7e, 0xf2, 0x42, + 0x55, 0x9e, 0x6b, 0x30, 0x7f, 0x10, 0x62, 0xaf, 0x5a, 0x57, 0x59, 0x50, 0x10, 0x59, 0x90, 0x97, + 0x7b, 0x32, 0x0f, 0xb6, 0xa1, 0x40, 0xab, 0x75, 0xe2, 0x44, 0x0d, 0xe2, 0xd8, 0x1c, 0x3c, 0xcc, + 0x45, 0xa1, 0x64, 0xb1, 0x2f, 0xba, 0xf6, 0x63, 0x64, 0xb1, 0x16, 0x3a, 0x1c, 0x7c, 0x0f, 0x7d, + 0x0b, 0xe6, 0xe3, 0x98, 0x12, 0x02, 0x96, 0x86, 0x0a, 0xc8, 0x2b, 0x7a, 0xc1, 0xfe, 0x19, 0xe4, + 0xf8, 0x8b, 0xb8, 0x84, 0x9a, 0xe7, 0xd6, 0x26, 0xd7, 0xf3, 0x5b, 0x0f, 0xca, 0x59, 0x70, 0x58, + 0x1e, 0x90, 0xf0, 0xe5, 0x4f, 0xa4, 0x90, 0x87, 0x1e, 0x0b, 0xdb, 0x56, 0x2c, 0x92, 0xbb, 0x8c, + 0xf9, 0x0c, 0x37, 0x6c, 0x55, 0xf0, 0xed, 0x83, 0x36, 0x23, 0xd4, 0x44, 0x22, 0x12, 0xcf, 0x89, + 0xa3, 0xc7, 0xf2, 0xe4, 0x01, 0x3f, 0x40, 0x9f, 0xc1, 0x52, 0xa7, 0xe4, 0xda, 0x55, 0x51, 0xda, + 0xcd, 0xf3, 0xc2, 0xa0, 0xcd, 0x6c, 0xb5, 0x32, 0x30, 0xc1, 0x5a, 0x0c, 0xd2, 0x1b, 0xc5, 0x57, + 0x30, 0x9f, 0x54, 0x13, 0x2d, 0xc1, 0xe4, 0x11, 0x69, 0x8b, 0xea, 0x34, 0x67, 0xf1, 0x3f, 0x79, + 0x40, 0xb7, 0x78, 0x06, 0xab, 0x52, 0x34, 0x52, 0x40, 0x0b, 0x86, 0x7b, 0x13, 0x77, 0x8d, 0x24, + 0x10, 0x6c, 0x57, 0x99, 0xdb, 0x72, 0x59, 0xfb, 0xe4, 0x40, 0xa0, 0x91, 0xf0, 0xdf, 0x08, 0x04, + 0x7f, 0x9f, 0xed, 0x00, 0x41, 0x5a, 0xe3, 0xaf, 0x15, 0x08, 0xae, 0x42, 0x1e, 0x2b, 0x6d, 0xba, + 0x4e, 0x80, 0x78, 0xab, 0xe2, 0x70, 0xa4, 0xe8, 0x10, 0x08, 0xa4, 0x98, 0x1a, 0x80, 0x14, 0x1d, + 0xc3, 0x04, 0x52, 0xe0, 0xc4, 0x0a, 0x6d, 0xc1, 0xb4, 0xeb, 0x05, 0x11, 0x13, 0xde, 0xc9, 0x6f, + 0x5d, 0xd1, 0xbf, 0x28, 0x6e, 0x37, 0x7c, 0xec, 0x58, 0x92, 0x54, 0x93, 0xf4, 0x33, 0xa7, 0x4d, + 0xfa, 0xdc, 0x78, 0x49, 0xbf, 0x0f, 0x97, 0x62, 0x79, 0x36, 0xf3, 0xed, 0x6a, 0xc3, 0xa7, 0x44, + 0x08, 0xf2, 0x23, 0x09, 0x13, 0xf9, 0xad, 0x4b, 0x7d, 0xb2, 0x76, 0x55, 0xef, 0x6b, 0x2d, 0xc7, + 0xbc, 0xfb, 0xfe, 0x0e, 0xe7, 0xdc, 0x97, 0x8c, 0xe8, 0xfb, 0xb0, 0x2c, 0x2e, 0xe9, 0x17, 0x39, + 0x37, 0x4c, 0xe4, 0x79, 0xc1, 0xd8, 0x23, 0x6f, 0x0f, 0xce, 0xd5, 0x09, 0x0e, 0xd9, 0x01, 0xc1, + 0xac, 0x23, 0x0a, 0x86, 0x89, 0x5a, 0xea, 0xf0, 0xc4, 0x72, 0x12, 0x58, 0x9a, 0x4f, 0x63, 0xe9, + 0x2b, 0x58, 0x4d, 0xbf, 0x84, 0xed, 0xd7, 0x6c, 0x56, 0x77, 0xa9, 0x1d, 0x33, 0xcc, 0x0f, 0x75, + 0x6c, 0x31, 0xf5, 0x32, 0x4f, 0x6b, 0xfb, 0x75, 0x97, 0x6e, 0x2b, 0xf9, 0x95, 0xa4, 0x05, 0x0e, + 0x61, 0xd8, 0x6d, 0x50, 0x81, 0x17, 0xc3, 0x22, 0xa5, 0x6b, 0xc4, 0xae, 0xe4, 0xea, 0x6f, 0x6d, + 0x0a, 0x27, 0x6b, 0x6d, 0x6e, 0xc2, 0x62, 0x47, 0x8e, 0xac, 0x18, 0x02, 0x72, 0xe6, 0xac, 0x42, + 0xbc, 0xbd, 0x2b, 0x76, 0xd1, 0x07, 0x30, 0x53, 0x27, 0xd8, 0x21, 0xa1, 0x42, 0x94, 0xcb, 0xda, + 0x9b, 0x1e, 0x0b, 0x12, 0x4b, 0x91, 0xbe, 0xdd, 0xfa, 0x5d, 0xfa, 0xf3, 0x14, 0x2c, 0x6f, 0x3b, + 0x8e, 0xae, 0xc9, 0x4e, 0x15, 0x44, 0xa3, 0xa7, 0x20, 0xbe, 0xa5, 0x22, 0x73, 0x0f, 0xe6, 0xba, + 0xcd, 0xc5, 0xe4, 0x28, 0xcd, 0xc5, 0x2c, 0x8b, 0x7b, 0x89, 0xab, 0x90, 0xef, 0x64, 0xa0, 0xea, + 0x29, 0x27, 0x2d, 0x88, 0xb7, 0x2a, 0x4e, 0x6f, 0x8a, 0xaa, 0xc4, 0x52, 0x49, 0x30, 0x3d, 0x46, + 0x8a, 0x8a, 0x16, 0x34, 0x4e, 0x85, 0x7b, 0x30, 0x43, 0xfd, 0x28, 0xac, 0xca, 0x92, 0x53, 0xe8, + 0x05, 0xb8, 0x44, 0xbf, 0x85, 0xe9, 0xd1, 0x73, 0x41, 0x69, 0x29, 0x0e, 0x0d, 0x72, 0xe4, 0x74, + 0xc8, 0x11, 0x68, 0x42, 0x60, 0x56, 0x74, 0x16, 0x0f, 0xb3, 0x43, 0x40, 0xff, 0xaa, 0xe5, 0x9e, + 0x88, 0x90, 0xcd, 0x45, 0x1f, 0xac, 0x3f, 0x80, 0x0b, 0x3a, 0x42, 0x0d, 0xbc, 0x5f, 0x48, 0xc2, + 0xfb, 0x5c, 0x12, 0xba, 0x8f, 0xe1, 0x62, 0x9f, 0x0e, 0x0a, 0xc1, 0x74, 0x31, 0x6d, 0x9c, 0x59, + 0x4c, 0xff, 0x73, 0x5a, 0xc4, 0xb4, 0xae, 0x5f, 0xf8, 0x3a, 0x62, 0x9a, 0x4f, 0x28, 0xe2, 0xb9, + 0xed, 0xee, 0xd5, 0x12, 0x3d, 0x0b, 0x72, 0x7f, 0x37, 0x56, 0x20, 0x15, 0xfd, 0x53, 0xa7, 0x8a, + 0xfe, 0xe9, 0xf1, 0xa2, 0x7f, 0xe6, 0xf4, 0xd1, 0x9f, 0x3b, 0x83, 0xe8, 0x9f, 0xd5, 0x45, 0xbf, + 0x07, 0x26, 0x4e, 0x3c, 0xe5, 0xae, 0x4b, 0x03, 0x1e, 0x15, 0x7c, 0x3e, 0x51, 0x28, 0xb8, 0x35, + 0x20, 0x0b, 0x32, 0x38, 0xad, 0x4c, 0x99, 0xda, 0x6c, 0x83, 0x11, 0xb2, 0x4d, 0x13, 0x6f, 0x5f, + 0x61, 0xb6, 0x7d, 0x39, 0x09, 0x66, 0x96, 0xb1, 0xe8, 0xbb, 0xb0, 0xd8, 0x05, 0x65, 0x31, 0x55, + 0xa9, 0x74, 0xd3, 0x63, 0x9d, 0x9a, 0x1f, 0xc4, 0xe8, 0x6b, 0x75, 0x1b, 0x2b, 0xb1, 0xee, 0xeb, + 0x93, 0x26, 0xc6, 0xeb, 0x93, 0x12, 0x9d, 0xc3, 0xe4, 0xb8, 0x9d, 0xc3, 0xd4, 0xd9, 0x77, 0x0e, + 0xd3, 0x67, 0xd3, 0x39, 0xcc, 0x9c, 0x59, 0xe7, 0x90, 0xd3, 0x75, 0x0e, 0xaa, 0x96, 0x6a, 0xa7, + 0x81, 0xb7, 0x5b, 0x4b, 0xbf, 0x34, 0xe0, 0x82, 0x18, 0xca, 0x62, 0x2b, 0xe2, 0x4a, 0xba, 0xd3, + 0x3b, 0x79, 0xfd, 0x9f, 0xd6, 0x78, 0x1d, 0xef, 0x88, 0x33, 0xd7, 0x69, 0x7a, 0x81, 0xd1, 0x46, + 0xb2, 0xd2, 0x17, 0x06, 0xbc, 0xd3, 0xa3, 0xa1, 0xf2, 0xea, 0x7d, 0x98, 0x17, 0x5f, 0x55, 0xec, + 0x90, 0xd0, 0xa8, 0x11, 0xdb, 0x38, 0x38, 0x4e, 0xf2, 0x82, 0xc3, 0x12, 0x0c, 0xa8, 0x02, 0x85, + 0x58, 0xc0, 0x0f, 0x49, 0x95, 0x11, 0x67, 0xe0, 0xfc, 0x2b, 0xe7, 0x5e, 0x45, 0x69, 0x2d, 0xbc, + 0x4e, 0x2e, 0x4b, 0x7f, 0x33, 0x60, 0x4d, 0x2a, 0xe6, 0x08, 0x3a, 0x6e, 0xef, 0x8e, 0xdf, 0x0c, + 0x1a, 0x84, 0x13, 0x2b, 0x57, 0x3e, 0xed, 0x7d, 0x8f, 0xdb, 0xda, 0x8b, 0x86, 0xc9, 0xf9, 0x0a, + 0xde, 0xe6, 0x22, 0xe4, 0x04, 0xaf, 0xea, 0xd1, 0xe6, 0xac, 0x19, 0xbe, 0xac, 0x38, 0xa5, 0x77, + 0xe1, 0xda, 0x00, 0xf5, 0xe4, 0xc3, 0x94, 0xfe, 0x62, 0xc0, 0x95, 0x1d, 0xec, 0x55, 0x49, 0xe3, + 0x69, 0xc4, 0x28, 0xc3, 0x9e, 0xe3, 0x7a, 0x87, 0x7c, 0x5a, 0x1e, 0x09, 0xe2, 0x53, 0x73, 0xfc, + 0x44, 0xcf, 0x1c, 0xff, 0x08, 0x0a, 0x1d, 0xa3, 0xba, 0xdf, 0x3a, 0x0b, 0x19, 0x69, 0x1d, 0x5b, + 0x26, 0xd3, 0x9a, 0x25, 0x56, 0xa7, 0xc1, 0xf1, 0xd2, 0x55, 0x58, 0xc9, 0x30, 0x4f, 0x39, 0xe0, + 0xc7, 0x70, 0x71, 0x97, 0xd0, 0x6a, 0xe8, 0x1e, 0x90, 0x0e, 0xbb, 0x32, 0x7d, 0xaf, 0x37, 0x06, + 0xde, 0xd7, 0xde, 0x9a, 0xc1, 0x3e, 0xda, 0xd3, 0x97, 0xfe, 0x6d, 0x80, 0xd9, 0x2f, 0x41, 0xa5, + 0xcd, 0x47, 0x90, 0x93, 0xee, 0xa4, 0xa6, 0x21, 0x20, 0xf3, 0x6a, 0xe6, 0xf7, 0x18, 0x12, 0x0a, + 0x1c, 0x8e, 0xe9, 0xd1, 0x13, 0x58, 0xea, 0x7a, 0x9f, 0x32, 0xcc, 0x22, 0xaa, 0x52, 0xe6, 0xdd, + 0x81, 0xbe, 0x7b, 0x2e, 0x48, 0xad, 0x02, 0x4b, 0xad, 0xd1, 0x4b, 0x4d, 0x59, 0x9c, 0x1c, 0xe0, + 0x94, 0x91, 0x2b, 0x22, 0x85, 0x15, 0xf1, 0xd0, 0xbd, 0xf4, 0x34, 0x7e, 0x85, 0x65, 0x98, 0x51, + 0xb5, 0x5c, 0x46, 0x9f, 0x5a, 0xa5, 0xa3, 0x62, 0x62, 0xbc, 0xa8, 0xf8, 0xf9, 0x04, 0xac, 0x66, + 0xdd, 0xaa, 0x5c, 0xff, 0x1a, 0x56, 0xba, 0x9f, 0x5f, 0x3a, 0x8e, 0x4c, 0xfc, 0xda, 0x22, 0x1f, + 0xa4, 0x3c, 0x9a, 0xf5, 0x4f, 0x08, 0xc3, 0x0e, 0x66, 0xd8, 0x2a, 0x26, 0xfb, 0xa4, 0xf4, 0xd5, + 0xfc, 0xca, 0xce, 0x17, 0x67, 0xed, 0x95, 0x13, 0x27, 0xbb, 0xd2, 0x49, 0xcc, 0x0c, 0xe9, 0x2b, + 0x4b, 0xb7, 0xe1, 0xf2, 0x23, 0xd2, 0x71, 0x03, 0x7d, 0xd0, 0x96, 0x00, 0x39, 0xc4, 0xf7, 0xa5, + 0x3f, 0x4c, 0xc1, 0x15, 0x3d, 0x9f, 0xf2, 0xde, 0x4f, 0x0d, 0x58, 0xd6, 0xd8, 0xd2, 0xc4, 0x81, + 0xf2, 0xdb, 0xd3, 0x6c, 0x30, 0x1d, 0x24, 0xb8, 0xbc, 0xdb, 0x63, 0xcb, 0x13, 0x1c, 0xc8, 0x2e, + 0xf0, 0xbc, 0xd3, 0x7f, 0x22, 0xd4, 0xd0, 0xbc, 0x22, 0x57, 0x63, 0xe2, 0x54, 0x6a, 0x6c, 0xf7, + 0xbc, 0x62, 0x57, 0x0d, 0xdc, 0x7f, 0x52, 0xfc, 0x11, 0x4f, 0x71, 0xbd, 0xde, 0x9a, 0xa6, 0xf4, + 0x71, 0xfa, 0x0b, 0xef, 0x80, 0x6e, 0x3c, 0xab, 0x6e, 0x24, 0x1a, 0x59, 0x7e, 0x77, 0x96, 0xb2, + 0x6f, 0xfb, 0xee, 0xd2, 0xef, 0x26, 0xe0, 0xbd, 0x4f, 0x03, 0x07, 0x33, 0x92, 0x55, 0x0e, 0x46, + 0x01, 0x99, 0x53, 0x24, 0xfa, 0xd9, 0x61, 0x90, 0xae, 0xfe, 0x4d, 0x9d, 0x45, 0xfd, 0xbb, 0x09, + 0xd7, 0x87, 0xb8, 0x48, 0x01, 0xd5, 0xef, 0x27, 0xe0, 0xba, 0x45, 0x6a, 0x21, 0xa1, 0xf5, 0xff, + 0x79, 0x33, 0xcb, 0x9b, 0xeb, 0x70, 0x63, 0x98, 0x8f, 0xa4, 0x3b, 0xb7, 0xfe, 0x35, 0x0f, 0xf9, + 0x27, 0x2a, 0x9e, 0xb7, 0x9f, 0x55, 0xd0, 0x4f, 0x0c, 0x38, 0xaf, 0xf9, 0xf5, 0x08, 0x7d, 0x38, + 0xe6, 0x8f, 0x4d, 0xe2, 0x09, 0x8a, 0xb7, 0x4f, 0xf4, 0x13, 0x55, 0x52, 0x89, 0x64, 0xd2, 0x8e, + 0xa0, 0x84, 0x66, 0x5a, 0x1e, 0x41, 0x09, 0xed, 0x04, 0xd4, 0x82, 0xc5, 0x9e, 0x0f, 0x4d, 0xe8, + 0xd6, 0xb8, 0xdf, 0xc5, 0x8a, 0x9b, 0x63, 0x70, 0xa4, 0xee, 0x4d, 0xd9, 0x7d, 0x6b, 0xdc, 0x2f, + 0x04, 0x43, 0xee, 0xd5, 0xda, 0x1b, 0xc0, 0x42, 0x6a, 0x68, 0x41, 0xe5, 0x6c, 0x19, 0xba, 0xf9, + 0xab, 0xb8, 0x31, 0x32, 0xbd, 0xba, 0xf1, 0xd7, 0x06, 0x5c, 0xca, 0x6c, 0xcd, 0xd1, 0xbd, 0x6c, + 0x71, 0xc3, 0xc6, 0x8d, 0xe2, 0xc7, 0x27, 0xe2, 0x55, 0x6a, 0xfd, 0xc2, 0x80, 0x77, 0xb4, 0xcd, + 0x32, 0xba, 0x93, 0x2d, 0x76, 0xd0, 0xf0, 0x50, 0xfc, 0xe6, 0xd8, 0x7c, 0x4a, 0x95, 0x36, 0x2c, + 0xf5, 0x02, 0x0c, 0xda, 0x1c, 0x07, 0x8c, 0xe4, 0xfd, 0x27, 0xc0, 0x2f, 0xf4, 0x4b, 0x03, 0x96, + 0xf5, 0xbd, 0x21, 0x1a, 0x60, 0xce, 0xc0, 0x1e, 0xb6, 0x78, 0x77, 0x7c, 0x46, 0xa5, 0xcd, 0xcf, + 0x0c, 0xb8, 0xa0, 0xeb, 0x44, 0xd0, 0xed, 0x71, 0x3b, 0x17, 0xa9, 0xc9, 0x9d, 0x93, 0x35, 0x3c, + 0xe8, 0xb7, 0x06, 0xac, 0x0c, 0xc4, 0x29, 0xf4, 0xed, 0x6c, 0xc9, 0xa3, 0xf4, 0x00, 0xc5, 0xfb, + 0x27, 0xe6, 0x57, 0x2a, 0x7e, 0x61, 0xc0, 0xea, 0xe0, 0xe2, 0x8f, 0xee, 0x0f, 0x4a, 0x8f, 0x11, + 0xa0, 0xb5, 0xf8, 0x9d, 0x93, 0x0b, 0x90, 0x5a, 0x3e, 0x78, 0xf4, 0xc7, 0x37, 0xab, 0xc6, 0x9f, + 0xde, 0xac, 0x1a, 0x7f, 0x7d, 0xb3, 0x6a, 0xfc, 0xe0, 0xa3, 0x43, 0x97, 0xd5, 0xa3, 0x83, 0x72, + 0xd5, 0x6f, 0x6e, 0xa4, 0xfe, 0xc5, 0xac, 0x7c, 0x48, 0x3c, 0xf9, 0x3f, 0x79, 0xc9, 0x7f, 0x0b, + 0xfc, 0x38, 0xfe, 0xbb, 0xb5, 0x79, 0x30, 0x23, 0x4e, 0x3f, 0xf8, 0x4f, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x55, 0x97, 0xef, 0x77, 0x44, 0x28, 0x00, 0x00, } func (m *TaskListPartitionConfig) Marshal() (dAtA []byte, err error) { @@ -3274,6 +3515,18 @@ func (m *DescribeTaskListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.PartitionConfig != nil { + { + size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } if m.TaskListStatus != nil { { size, err := m.TaskListStatus.MarshalToSizedBuffer(dAtA[:i]) @@ -3517,45 +3770,225 @@ func (m *GetTaskListsByDomainResponse) MarshalToSizedBuffer(dAtA []byte) (int, e return len(dAtA) - i, nil } -func encodeVarintService(dAtA []byte, offset int, v uint64) int { - offset -= sovService(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *UpdateTaskListPartitionConfigRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return base + return dAtA[:n], nil } -func (m *TaskListPartitionConfig) Size() (n int) { - if m == nil { - return 0 - } + +func (m *UpdateTaskListPartitionConfigRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateTaskListPartitionConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Version != 0 { - n += 1 + sovService(uint64(m.Version)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.NumReadPartitions != 0 { - n += 1 + sovService(uint64(m.NumReadPartitions)) + if m.PartitionConfig != nil { + { + size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } - if m.NumWritePartitions != 0 { - n += 1 + sovService(uint64(m.NumWritePartitions)) + if m.TaskListType != 0 { + i = encodeVarintService(dAtA, i, uint64(m.TaskListType)) + i-- + dAtA[i] = 0x18 } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if m.TaskList != nil { + { + size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 } - return n + if len(m.DomainId) > 0 { + i -= len(m.DomainId) + copy(dAtA[i:], m.DomainId) + i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *PollForDecisionTaskRequest) Size() (n int) { - if m == nil { - return 0 +func (m *UpdateTaskListPartitionConfigResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } + return dAtA[:n], nil +} + +func (m *UpdateTaskListPartitionConfigResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateTaskListPartitionConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Request != nil { + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil +} + +func (m *RefreshTaskListPartitionConfigRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RefreshTaskListPartitionConfigRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RefreshTaskListPartitionConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.PartitionConfig != nil { + { + size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.TaskListType != 0 { + i = encodeVarintService(dAtA, i, uint64(m.TaskListType)) + i-- + dAtA[i] = 0x18 + } + if m.TaskList != nil { + { + size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.DomainId) > 0 { + i -= len(m.DomainId) + copy(dAtA[i:], m.DomainId) + i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RefreshTaskListPartitionConfigResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RefreshTaskListPartitionConfigResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RefreshTaskListPartitionConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil +} + +func encodeVarintService(dAtA []byte, offset int, v uint64) int { + offset -= sovService(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *TaskListPartitionConfig) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovService(uint64(m.Version)) + } + if m.NumReadPartitions != 0 { + n += 1 + sovService(uint64(m.NumReadPartitions)) + } + if m.NumWritePartitions != 0 { + n += 1 + sovService(uint64(m.NumWritePartitions)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *PollForDecisionTaskRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } @@ -4116,6 +4549,10 @@ func (m *DescribeTaskListResponse) Size() (n int) { l = m.TaskListStatus.Size() n += 1 + l + sovService(uint64(l)) } + if m.PartitionConfig != nil { + l = m.PartitionConfig.Size() + n += 1 + l + sovService(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -4220,6 +4657,84 @@ func (m *GetTaskListsByDomainResponse) Size() (n int) { return n } +func (m *UpdateTaskListPartitionConfigRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DomainId) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } + if m.TaskList != nil { + l = m.TaskList.Size() + n += 1 + l + sovService(uint64(l)) + } + if m.TaskListType != 0 { + n += 1 + sovService(uint64(m.TaskListType)) + } + if m.PartitionConfig != nil { + l = m.PartitionConfig.Size() + n += 1 + l + sovService(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *UpdateTaskListPartitionConfigResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RefreshTaskListPartitionConfigRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DomainId) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } + if m.TaskList != nil { + l = m.TaskList.Size() + n += 1 + l + sovService(uint64(l)) + } + if m.TaskListType != 0 { + n += 1 + sovService(uint64(m.TaskListType)) + } + if m.PartitionConfig != nil { + l = m.PartitionConfig.Size() + n += 1 + l + sovService(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RefreshTaskListPartitionConfigResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovService(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -8393,6 +8908,42 @@ func (m *DescribeTaskListResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PartitionConfig == nil { + m.PartitionConfig = &v1.TaskListPartitionConfig{} + } + if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) @@ -9045,6 +9596,456 @@ func (m *GetTaskListsByDomainResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *UpdateTaskListPartitionConfigRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateTaskListPartitionConfigRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateTaskListPartitionConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DomainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TaskList == nil { + m.TaskList = &v1.TaskList{} + } + if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskListType", wireType) + } + m.TaskListType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TaskListType |= v1.TaskListType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PartitionConfig == nil { + m.PartitionConfig = &v1.TaskListPartitionConfig{} + } + if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateTaskListPartitionConfigResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateTaskListPartitionConfigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateTaskListPartitionConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RefreshTaskListPartitionConfigRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RefreshTaskListPartitionConfigRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RefreshTaskListPartitionConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DomainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TaskList == nil { + m.TaskList = &v1.TaskList{} + } + if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskListType", wireType) + } + m.TaskListType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TaskListType |= v1.TaskListType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PartitionConfig == nil { + m.PartitionConfig = &v1.TaskListPartitionConfig{} + } + if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RefreshTaskListPartitionConfigResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RefreshTaskListPartitionConfigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RefreshTaskListPartitionConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipService(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/.gen/proto/matching/v1/service.pb.yarpc.go b/.gen/proto/matching/v1/service.pb.yarpc.go index eb5e05a7d2e..871ecfa3f48 100644 --- a/.gen/proto/matching/v1/service.pb.yarpc.go +++ b/.gen/proto/matching/v1/service.pb.yarpc.go @@ -54,6 +54,8 @@ type MatchingAPIYARPCClient interface { DescribeTaskList(context.Context, *DescribeTaskListRequest, ...yarpc.CallOption) (*DescribeTaskListResponse, error) ListTaskListPartitions(context.Context, *ListTaskListPartitionsRequest, ...yarpc.CallOption) (*ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *GetTaskListsByDomainRequest, ...yarpc.CallOption) (*GetTaskListsByDomainResponse, error) + UpdateTaskListPartitionConfig(context.Context, *UpdateTaskListPartitionConfigRequest, ...yarpc.CallOption) (*UpdateTaskListPartitionConfigResponse, error) + RefreshTaskListPartitionConfig(context.Context, *RefreshTaskListPartitionConfigRequest, ...yarpc.CallOption) (*RefreshTaskListPartitionConfigResponse, error) } func newMatchingAPIYARPCClient(clientConfig transport.ClientConfig, anyResolver jsonpb.AnyResolver, options ...protobuf.ClientOption) MatchingAPIYARPCClient { @@ -84,6 +86,8 @@ type MatchingAPIYARPCServer interface { DescribeTaskList(context.Context, *DescribeTaskListRequest) (*DescribeTaskListResponse, error) ListTaskListPartitions(context.Context, *ListTaskListPartitionsRequest) (*ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *GetTaskListsByDomainRequest) (*GetTaskListsByDomainResponse, error) + UpdateTaskListPartitionConfig(context.Context, *UpdateTaskListPartitionConfigRequest) (*UpdateTaskListPartitionConfigResponse, error) + RefreshTaskListPartitionConfig(context.Context, *RefreshTaskListPartitionConfigRequest) (*RefreshTaskListPartitionConfigResponse, error) } type buildMatchingAPIYARPCProceduresParams struct { @@ -197,6 +201,26 @@ func buildMatchingAPIYARPCProcedures(params buildMatchingAPIYARPCProceduresParam }, ), }, + { + MethodName: "UpdateTaskListPartitionConfig", + Handler: protobuf.NewUnaryHandler( + protobuf.UnaryHandlerParams{ + Handle: handler.UpdateTaskListPartitionConfig, + NewRequest: newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest, + AnyResolver: params.AnyResolver, + }, + ), + }, + { + MethodName: "RefreshTaskListPartitionConfig", + Handler: protobuf.NewUnaryHandler( + protobuf.UnaryHandlerParams{ + Handle: handler.RefreshTaskListPartitionConfig, + NewRequest: newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest, + AnyResolver: params.AnyResolver, + }, + ), + }, }, OnewayHandlerParams: []protobuf.BuildProceduresOnewayHandlerParams{}, StreamHandlerParams: []protobuf.BuildProceduresStreamHandlerParams{}, @@ -435,6 +459,30 @@ func (c *_MatchingAPIYARPCCaller) GetTaskListsByDomain(ctx context.Context, requ return response, err } +func (c *_MatchingAPIYARPCCaller) UpdateTaskListPartitionConfig(ctx context.Context, request *UpdateTaskListPartitionConfigRequest, options ...yarpc.CallOption) (*UpdateTaskListPartitionConfigResponse, error) { + responseMessage, err := c.streamClient.Call(ctx, "UpdateTaskListPartitionConfig", request, newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse, options...) + if responseMessage == nil { + return nil, err + } + response, ok := responseMessage.(*UpdateTaskListPartitionConfigResponse) + if !ok { + return nil, protobuf.CastError(emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse, responseMessage) + } + return response, err +} + +func (c *_MatchingAPIYARPCCaller) RefreshTaskListPartitionConfig(ctx context.Context, request *RefreshTaskListPartitionConfigRequest, options ...yarpc.CallOption) (*RefreshTaskListPartitionConfigResponse, error) { + responseMessage, err := c.streamClient.Call(ctx, "RefreshTaskListPartitionConfig", request, newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse, options...) + if responseMessage == nil { + return nil, err + } + response, ok := responseMessage.(*RefreshTaskListPartitionConfigResponse) + if !ok { + return nil, protobuf.CastError(emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse, responseMessage) + } + return response, err +} + type _MatchingAPIYARPCHandler struct { server MatchingAPIYARPCServer } @@ -599,6 +647,38 @@ func (h *_MatchingAPIYARPCHandler) GetTaskListsByDomain(ctx context.Context, req return response, err } +func (h *_MatchingAPIYARPCHandler) UpdateTaskListPartitionConfig(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { + var request *UpdateTaskListPartitionConfigRequest + var ok bool + if requestMessage != nil { + request, ok = requestMessage.(*UpdateTaskListPartitionConfigRequest) + if !ok { + return nil, protobuf.CastError(emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest, requestMessage) + } + } + response, err := h.server.UpdateTaskListPartitionConfig(ctx, request) + if response == nil { + return nil, err + } + return response, err +} + +func (h *_MatchingAPIYARPCHandler) RefreshTaskListPartitionConfig(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { + var request *RefreshTaskListPartitionConfigRequest + var ok bool + if requestMessage != nil { + request, ok = requestMessage.(*RefreshTaskListPartitionConfigRequest) + if !ok { + return nil, protobuf.CastError(emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest, requestMessage) + } + } + response, err := h.server.RefreshTaskListPartitionConfig(ctx, request) + if response == nil { + return nil, err + } + return response, err +} + func newMatchingAPIServicePollForDecisionTaskYARPCRequest() proto.Message { return &PollForDecisionTaskRequest{} } @@ -679,169 +759,196 @@ func newMatchingAPIServiceGetTaskListsByDomainYARPCResponse() proto.Message { return &GetTaskListsByDomainResponse{} } +func newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest() proto.Message { + return &UpdateTaskListPartitionConfigRequest{} +} + +func newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse() proto.Message { + return &UpdateTaskListPartitionConfigResponse{} +} + +func newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest() proto.Message { + return &RefreshTaskListPartitionConfigRequest{} +} + +func newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse() proto.Message { + return &RefreshTaskListPartitionConfigResponse{} +} + var ( - emptyMatchingAPIServicePollForDecisionTaskYARPCRequest = &PollForDecisionTaskRequest{} - emptyMatchingAPIServicePollForDecisionTaskYARPCResponse = &PollForDecisionTaskResponse{} - emptyMatchingAPIServicePollForActivityTaskYARPCRequest = &PollForActivityTaskRequest{} - emptyMatchingAPIServicePollForActivityTaskYARPCResponse = &PollForActivityTaskResponse{} - emptyMatchingAPIServiceAddDecisionTaskYARPCRequest = &AddDecisionTaskRequest{} - emptyMatchingAPIServiceAddDecisionTaskYARPCResponse = &AddDecisionTaskResponse{} - emptyMatchingAPIServiceAddActivityTaskYARPCRequest = &AddActivityTaskRequest{} - emptyMatchingAPIServiceAddActivityTaskYARPCResponse = &AddActivityTaskResponse{} - emptyMatchingAPIServiceQueryWorkflowYARPCRequest = &QueryWorkflowRequest{} - emptyMatchingAPIServiceQueryWorkflowYARPCResponse = &QueryWorkflowResponse{} - emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCRequest = &RespondQueryTaskCompletedRequest{} - emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCResponse = &RespondQueryTaskCompletedResponse{} - emptyMatchingAPIServiceCancelOutstandingPollYARPCRequest = &CancelOutstandingPollRequest{} - emptyMatchingAPIServiceCancelOutstandingPollYARPCResponse = &CancelOutstandingPollResponse{} - emptyMatchingAPIServiceDescribeTaskListYARPCRequest = &DescribeTaskListRequest{} - emptyMatchingAPIServiceDescribeTaskListYARPCResponse = &DescribeTaskListResponse{} - emptyMatchingAPIServiceListTaskListPartitionsYARPCRequest = &ListTaskListPartitionsRequest{} - emptyMatchingAPIServiceListTaskListPartitionsYARPCResponse = &ListTaskListPartitionsResponse{} - emptyMatchingAPIServiceGetTaskListsByDomainYARPCRequest = &GetTaskListsByDomainRequest{} - emptyMatchingAPIServiceGetTaskListsByDomainYARPCResponse = &GetTaskListsByDomainResponse{} + emptyMatchingAPIServicePollForDecisionTaskYARPCRequest = &PollForDecisionTaskRequest{} + emptyMatchingAPIServicePollForDecisionTaskYARPCResponse = &PollForDecisionTaskResponse{} + emptyMatchingAPIServicePollForActivityTaskYARPCRequest = &PollForActivityTaskRequest{} + emptyMatchingAPIServicePollForActivityTaskYARPCResponse = &PollForActivityTaskResponse{} + emptyMatchingAPIServiceAddDecisionTaskYARPCRequest = &AddDecisionTaskRequest{} + emptyMatchingAPIServiceAddDecisionTaskYARPCResponse = &AddDecisionTaskResponse{} + emptyMatchingAPIServiceAddActivityTaskYARPCRequest = &AddActivityTaskRequest{} + emptyMatchingAPIServiceAddActivityTaskYARPCResponse = &AddActivityTaskResponse{} + emptyMatchingAPIServiceQueryWorkflowYARPCRequest = &QueryWorkflowRequest{} + emptyMatchingAPIServiceQueryWorkflowYARPCResponse = &QueryWorkflowResponse{} + emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCRequest = &RespondQueryTaskCompletedRequest{} + emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCResponse = &RespondQueryTaskCompletedResponse{} + emptyMatchingAPIServiceCancelOutstandingPollYARPCRequest = &CancelOutstandingPollRequest{} + emptyMatchingAPIServiceCancelOutstandingPollYARPCResponse = &CancelOutstandingPollResponse{} + emptyMatchingAPIServiceDescribeTaskListYARPCRequest = &DescribeTaskListRequest{} + emptyMatchingAPIServiceDescribeTaskListYARPCResponse = &DescribeTaskListResponse{} + emptyMatchingAPIServiceListTaskListPartitionsYARPCRequest = &ListTaskListPartitionsRequest{} + emptyMatchingAPIServiceListTaskListPartitionsYARPCResponse = &ListTaskListPartitionsResponse{} + emptyMatchingAPIServiceGetTaskListsByDomainYARPCRequest = &GetTaskListsByDomainRequest{} + emptyMatchingAPIServiceGetTaskListsByDomainYARPCResponse = &GetTaskListsByDomainResponse{} + emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest = &UpdateTaskListPartitionConfigRequest{} + emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse = &UpdateTaskListPartitionConfigResponse{} + emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest = &RefreshTaskListPartitionConfigRequest{} + emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse = &RefreshTaskListPartitionConfigResponse{} ) var yarpcFileDescriptorClosure826e827d3aabf7fc = [][]byte{ // uber/cadence/matching/v1/service.proto []byte{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0x5b, 0x6f, 0xdb, 0xc8, - 0x15, 0x06, 0x7d, 0x93, 0x75, 0x64, 0xcb, 0xce, 0x24, 0xeb, 0x30, 0xca, 0xcd, 0xd1, 0x76, 0x77, - 0xdd, 0x62, 0x4b, 0xc7, 0xde, 0x4d, 0x9a, 0x4d, 0x50, 0x14, 0xbe, 0x26, 0x2a, 0x9a, 0x26, 0xcb, - 0xb8, 0x59, 0xa0, 0x58, 0x84, 0x18, 0x91, 0x63, 0x8b, 0xb5, 0x44, 0x32, 0x9c, 0xa1, 0xbc, 0xea, - 0x43, 0x1f, 0x8a, 0xb6, 0x28, 0xd0, 0xb7, 0xa2, 0xfd, 0x05, 0xdd, 0xc7, 0xfe, 0xa0, 0x02, 0x8b, - 0x3e, 0xf6, 0xa1, 0x8f, 0xed, 0x2f, 0x28, 0x66, 0x38, 0xa4, 0x48, 0x69, 0xa8, 0x8b, 0xed, 0xec, - 0xf6, 0xcd, 0x33, 0x73, 0xee, 0x73, 0xce, 0xf9, 0xce, 0xd0, 0x82, 0x0f, 0xa3, 0x26, 0x09, 0x37, - 0x6d, 0xec, 0x10, 0xcf, 0x26, 0x9b, 0x1d, 0xcc, 0xec, 0x96, 0xeb, 0x9d, 0x6c, 0x76, 0xb7, 0x36, - 0x29, 0x09, 0xbb, 0xae, 0x4d, 0x8c, 0x20, 0xf4, 0x99, 0x8f, 0x74, 0x4e, 0x67, 0x48, 0x3a, 0x23, - 0xa1, 0x33, 0xba, 0x5b, 0xb5, 0x3b, 0x27, 0xbe, 0x7f, 0xd2, 0x26, 0x9b, 0x82, 0xae, 0x19, 0x1d, - 0x6f, 0x3a, 0x51, 0x88, 0x99, 0xeb, 0x7b, 0x31, 0x67, 0xed, 0xee, 0xe0, 0x39, 0x73, 0x3b, 0x84, - 0x32, 0xdc, 0x09, 0x24, 0xc1, 0x90, 0x80, 0xb3, 0x10, 0x07, 0x01, 0x09, 0xa9, 0x3c, 0x5f, 0xcf, - 0x99, 0x88, 0x03, 0x97, 0x5b, 0x67, 0xfb, 0x9d, 0x4e, 0x5f, 0x85, 0x8a, 0xe2, 0x6d, 0x44, 0xc2, - 0x9e, 0x24, 0xa8, 0xab, 0x08, 0x18, 0xa6, 0xa7, 0x6d, 0x97, 0x32, 0x49, 0xb3, 0xa1, 0xa2, 0x91, - 0x41, 0xb0, 0xce, 0xfc, 0xf0, 0x94, 0x84, 0x92, 0xf2, 0x07, 0xe3, 0x28, 0x8f, 0xdb, 0xfe, 0x99, - 0xa4, 0xbd, 0xa7, 0xa2, 0x6d, 0xb9, 0x94, 0xf9, 0xa9, 0x71, 0xdf, 0xcb, 0x91, 0xd0, 0x16, 0x0e, - 0x89, 0x33, 0x4c, 0xf5, 0x41, 0x01, 0x55, 0xde, 0x8b, 0xfa, 0x5f, 0x35, 0xb8, 0x7e, 0x84, 0xe9, - 0xe9, 0xcf, 0x5c, 0xca, 0x5e, 0xe2, 0x90, 0xb9, 0xfc, 0x26, 0xf6, 0x7c, 0xef, 0xd8, 0x3d, 0x41, - 0x3a, 0x94, 0xba, 0x24, 0xa4, 0xae, 0xef, 0xe9, 0xda, 0xba, 0xb6, 0x31, 0x6b, 0x26, 0x4b, 0x64, - 0xc0, 0x55, 0x2f, 0xea, 0x58, 0x21, 0xc1, 0x8e, 0x15, 0x24, 0x5c, 0x54, 0x9f, 0x59, 0xd7, 0x36, - 0xe6, 0xcd, 0x2b, 0x5e, 0xd4, 0x31, 0x09, 0x76, 0x52, 0x71, 0x14, 0xdd, 0x87, 0x6b, 0x9c, 0xfe, - 0x2c, 0x74, 0x19, 0xc9, 0x32, 0xcc, 0x0a, 0x06, 0xe4, 0x45, 0x9d, 0x2f, 0xf8, 0x51, 0x9f, 0xa3, - 0xfe, 0x1f, 0x0d, 0x6a, 0x2f, 0xfd, 0x76, 0xfb, 0xd0, 0x0f, 0xf7, 0x89, 0xed, 0x72, 0xad, 0xdc, - 0x4c, 0x93, 0xbc, 0x8d, 0x08, 0x65, 0xa8, 0x01, 0xa5, 0x30, 0xfe, 0x53, 0x98, 0x56, 0xd9, 0xde, - 0x34, 0x72, 0x09, 0x87, 0x03, 0xd7, 0xe8, 0x6e, 0x19, 0xc5, 0x12, 0xcc, 0x84, 0x1f, 0xdd, 0x84, - 0xb2, 0xe3, 0x77, 0xb0, 0xeb, 0x59, 0xae, 0x23, 0x3c, 0x28, 0x9b, 0x8b, 0xf1, 0x46, 0xc3, 0xe1, - 0x87, 0x81, 0xdf, 0x6e, 0x93, 0x90, 0x1f, 0xce, 0xc6, 0x87, 0xf1, 0x46, 0xc3, 0x41, 0x1f, 0x40, - 0xf5, 0xd8, 0x0f, 0xcf, 0x70, 0xe8, 0x10, 0xc7, 0x3a, 0x0e, 0xfd, 0x8e, 0x3e, 0x27, 0x28, 0x96, - 0xd3, 0xdd, 0xc3, 0xd0, 0xef, 0xa0, 0x8f, 0x60, 0xc5, 0xa5, 0x7e, 0x5b, 0xe4, 0xb8, 0x75, 0x12, - 0xfa, 0x51, 0xa0, 0xcf, 0x0b, 0xba, 0x6a, 0xba, 0xfd, 0x94, 0xef, 0xd6, 0xff, 0x0c, 0x70, 0x53, - 0x69, 0x31, 0x0d, 0x7c, 0x8f, 0x12, 0x74, 0x1b, 0x80, 0xdf, 0x9e, 0xc5, 0xfc, 0x53, 0x12, 0x5f, - 0xc9, 0x92, 0x59, 0xe6, 0x3b, 0x47, 0x7c, 0x03, 0xfd, 0x02, 0x50, 0x92, 0x4c, 0x16, 0xf9, 0x8a, - 0xd8, 0x11, 0x97, 0x2c, 0x3c, 0xaa, 0x6c, 0x7f, 0xa8, 0x0c, 0xcf, 0x17, 0x92, 0xfc, 0x20, 0xa1, - 0x36, 0xaf, 0x9c, 0x0d, 0x6e, 0xa1, 0x43, 0x58, 0x4e, 0xc5, 0xb2, 0x5e, 0x40, 0x44, 0x18, 0x2a, - 0xdb, 0xf7, 0x46, 0x4a, 0x3c, 0xea, 0x05, 0xc4, 0x5c, 0x3a, 0xcb, 0xac, 0xd0, 0x6b, 0xb8, 0x11, - 0x84, 0xa4, 0xeb, 0xfa, 0x11, 0xb5, 0x28, 0xc3, 0x21, 0x23, 0x8e, 0x45, 0xba, 0xc4, 0x63, 0x3c, - 0xb4, 0x73, 0x42, 0xe6, 0x4d, 0x23, 0x2e, 0x6d, 0x23, 0x29, 0x6d, 0xa3, 0xe1, 0xb1, 0x87, 0x9f, - 0xbe, 0xc6, 0xed, 0x88, 0x98, 0x6b, 0x09, 0xf7, 0xab, 0x98, 0xf9, 0x80, 0xf3, 0x36, 0x1c, 0xb4, - 0x01, 0xab, 0x43, 0xe2, 0xe6, 0x45, 0xba, 0x56, 0x69, 0x9e, 0x52, 0x87, 0x12, 0x66, 0x8c, 0x74, - 0x02, 0xa6, 0x2f, 0x88, 0xc4, 0x4b, 0x96, 0xa8, 0x0e, 0xcb, 0x1e, 0xf9, 0x8a, 0xf5, 0x05, 0x94, - 0x84, 0x80, 0x0a, 0xdf, 0x4c, 0xb8, 0x3f, 0x06, 0xd4, 0xc4, 0xf6, 0x69, 0xdb, 0x3f, 0xb1, 0x6c, - 0x3f, 0xf2, 0x98, 0xd5, 0x72, 0x3d, 0xa6, 0x2f, 0x0a, 0xc2, 0x55, 0x79, 0xb2, 0xc7, 0x0f, 0x9e, - 0xb9, 0x1e, 0x43, 0x8f, 0x40, 0xa7, 0xcc, 0xb5, 0x4f, 0x7b, 0xfd, 0xab, 0xb0, 0x88, 0x87, 0x9b, - 0x6d, 0xe2, 0xe8, 0xe5, 0x75, 0x6d, 0x63, 0xd1, 0x5c, 0x8b, 0xcf, 0xd3, 0x40, 0x1f, 0xc4, 0xa7, - 0xe8, 0x11, 0xcc, 0x8b, 0x56, 0xa4, 0x83, 0x88, 0x49, 0x7d, 0x64, 0x9c, 0x3f, 0xe7, 0x94, 0x66, - 0xcc, 0x80, 0x4c, 0x58, 0x76, 0x64, 0xde, 0x58, 0xae, 0x77, 0xec, 0xeb, 0x15, 0x21, 0xe1, 0x87, - 0x79, 0x09, 0x71, 0x2b, 0xe0, 0x42, 0x8e, 0x42, 0xec, 0x51, 0x97, 0x78, 0x2c, 0xc9, 0xb6, 0x86, - 0x77, 0xec, 0x9b, 0x4b, 0x4e, 0x66, 0x85, 0xde, 0xc0, 0xad, 0xe1, 0xa4, 0xb2, 0x44, 0x1a, 0xf2, - 0x2e, 0xa2, 0x2f, 0x09, 0x15, 0xb7, 0x95, 0x46, 0x26, 0x7d, 0xc5, 0xbc, 0x31, 0x94, 0x55, 0xc9, - 0x11, 0xef, 0x24, 0x71, 0xd0, 0x79, 0xef, 0x22, 0x56, 0xd2, 0x6f, 0x96, 0xe3, 0x4e, 0x22, 0x8e, - 0x5e, 0xf1, 0x93, 0xd7, 0xb2, 0xf3, 0xdc, 0x83, 0xa5, 0x66, 0x88, 0x3d, 0xbb, 0x25, 0xab, 0xa0, - 0x2a, 0xaa, 0xa0, 0x12, 0xef, 0xc5, 0x75, 0xb0, 0x03, 0x55, 0x6a, 0xb7, 0x88, 0x13, 0xb5, 0x89, - 0x63, 0x71, 0xf0, 0xd0, 0x57, 0x84, 0x91, 0xb5, 0xa1, 0xec, 0x3a, 0x4a, 0x90, 0xc5, 0x5c, 0x4e, - 0x39, 0xf8, 0x1e, 0xfa, 0x31, 0x2c, 0x25, 0x39, 0x25, 0x04, 0xac, 0x8e, 0x15, 0x50, 0x91, 0xf4, - 0x82, 0xfd, 0x4b, 0x28, 0xf1, 0x1b, 0x71, 0x09, 0xd5, 0xaf, 0xac, 0xcf, 0x6e, 0x54, 0xb6, 0x77, - 0x8d, 0x22, 0x38, 0x34, 0x46, 0x14, 0xbc, 0xf1, 0x79, 0x2c, 0xe4, 0xc0, 0x63, 0x61, 0xcf, 0x4c, - 0x44, 0xf2, 0x90, 0x31, 0x9f, 0xe1, 0xb6, 0x25, 0x1b, 0xbe, 0xd5, 0xec, 0x31, 0x42, 0x75, 0x24, - 0x32, 0xf1, 0x8a, 0x38, 0x7a, 0x16, 0x9f, 0xec, 0xf2, 0x03, 0xf4, 0x25, 0xac, 0xa6, 0x2d, 0xd7, - 0xb2, 0x45, 0x6b, 0xd7, 0xaf, 0x0a, 0x87, 0xb6, 0x8a, 0xcd, 0x2a, 0xc0, 0x04, 0x73, 0x25, 0xc8, - 0x6f, 0xd4, 0xde, 0xc0, 0x52, 0xd6, 0x4c, 0xb4, 0x0a, 0xb3, 0xa7, 0xa4, 0x27, 0xba, 0x53, 0xd9, - 0xe4, 0x7f, 0xf2, 0x84, 0xee, 0xf2, 0x0a, 0x96, 0xad, 0x68, 0xa2, 0x84, 0x16, 0x0c, 0x8f, 0x67, - 0x1e, 0x69, 0x59, 0x20, 0xd8, 0xb1, 0x99, 0xdb, 0x75, 0x59, 0xef, 0xfc, 0x40, 0xa0, 0x90, 0xf0, - 0xff, 0x08, 0x04, 0xff, 0x5e, 0x4c, 0x81, 0x20, 0x6f, 0xf1, 0x77, 0x0a, 0x04, 0x77, 0xa1, 0x82, - 0xa5, 0x35, 0xfd, 0x20, 0x40, 0xb2, 0xd5, 0x70, 0x38, 0x52, 0xa4, 0x04, 0x02, 0x29, 0xe6, 0x46, - 0x20, 0x45, 0xea, 0x98, 0x40, 0x0a, 0x9c, 0x59, 0xa1, 0x6d, 0x98, 0x77, 0xbd, 0x20, 0x62, 0x22, - 0x3a, 0x95, 0xed, 0x5b, 0xea, 0x1b, 0xc5, 0xbd, 0xb6, 0x8f, 0x1d, 0x33, 0x26, 0x55, 0x14, 0xfd, - 0xc2, 0x45, 0x8b, 0xbe, 0x34, 0x5d, 0xd1, 0x1f, 0xc1, 0x8d, 0x44, 0x9e, 0xc5, 0x7c, 0xcb, 0x6e, - 0xfb, 0x94, 0x08, 0x41, 0x7e, 0x14, 0xc3, 0x44, 0x65, 0xfb, 0xc6, 0x90, 0xac, 0x7d, 0x39, 0xfb, - 0x9a, 0x6b, 0x09, 0xef, 0x91, 0xbf, 0xc7, 0x39, 0x8f, 0x62, 0x46, 0xf4, 0x73, 0x58, 0x13, 0x4a, - 0x86, 0x45, 0x96, 0xc7, 0x89, 0xbc, 0x2a, 0x18, 0x07, 0xe4, 0x1d, 0xc2, 0x95, 0x16, 0xc1, 0x21, - 0x6b, 0x12, 0xcc, 0x52, 0x51, 0x30, 0x4e, 0xd4, 0x6a, 0xca, 0x93, 0xc8, 0xc9, 0x60, 0x69, 0x25, - 0x8f, 0xa5, 0x6f, 0xe0, 0x4e, 0xfe, 0x26, 0x2c, 0xff, 0xd8, 0x62, 0x2d, 0x97, 0x5a, 0x09, 0xc3, - 0xd2, 0xd8, 0xc0, 0xd6, 0x72, 0x37, 0xf3, 0xe2, 0xf8, 0xa8, 0xe5, 0xd2, 0x1d, 0x29, 0xbf, 0x91, - 0xf5, 0xc0, 0x21, 0x0c, 0xbb, 0x6d, 0x2a, 0xf0, 0x62, 0x5c, 0xa6, 0xf4, 0x9d, 0xd8, 0x8f, 0xb9, - 0x86, 0x47, 0x9b, 0xea, 0xf9, 0x46, 0x9b, 0x8f, 0x60, 0x25, 0x95, 0x13, 0x77, 0x0c, 0x01, 0x39, - 0x65, 0xb3, 0x9a, 0x6c, 0xef, 0x8b, 0x5d, 0xf4, 0x09, 0x2c, 0xb4, 0x08, 0x76, 0x48, 0x28, 0x11, - 0xe5, 0xa6, 0x52, 0xd3, 0x33, 0x41, 0x62, 0x4a, 0xd2, 0x77, 0xdb, 0xbf, 0xeb, 0xff, 0x98, 0x83, - 0xb5, 0x1d, 0xc7, 0x51, 0x0d, 0xd9, 0xb9, 0x86, 0xa8, 0x0d, 0x34, 0xc4, 0x77, 0xd4, 0x64, 0x1e, - 0x43, 0xb9, 0x3f, 0x5c, 0xcc, 0x4e, 0x32, 0x5c, 0x2c, 0xb2, 0x64, 0x96, 0xb8, 0x0b, 0x95, 0xb4, - 0x02, 0xe5, 0x4c, 0x39, 0x6b, 0x42, 0xb2, 0xd5, 0x70, 0x06, 0x4b, 0x54, 0x16, 0x96, 0x2c, 0x82, - 0xf9, 0x29, 0x4a, 0x54, 0x8c, 0xa0, 0x49, 0x29, 0x3c, 0x86, 0x05, 0xea, 0x47, 0xa1, 0x1d, 0xb7, - 0x9c, 0xea, 0x20, 0xc0, 0x65, 0xe6, 0x2d, 0x4c, 0x4f, 0x5f, 0x09, 0x4a, 0x53, 0x72, 0x28, 0x90, - 0xa3, 0xa4, 0x42, 0x8e, 0x40, 0x91, 0x02, 0x8b, 0x62, 0xb2, 0x38, 0x28, 0x4e, 0x01, 0xf5, 0xad, - 0x1a, 0x03, 0x19, 0x11, 0x0f, 0x17, 0x43, 0xb0, 0xbe, 0x0b, 0xd7, 0x54, 0x84, 0x0a, 0x78, 0xbf, - 0x96, 0x85, 0xf7, 0x72, 0x16, 0xba, 0xcf, 0xe0, 0xfa, 0x90, 0x0d, 0x12, 0xc1, 0x54, 0x39, 0xad, - 0x5d, 0x5a, 0x4e, 0xff, 0x77, 0x5e, 0xe4, 0xb4, 0x6a, 0x5e, 0xf8, 0x2e, 0x72, 0x9a, 0xbf, 0x50, - 0xc4, 0x75, 0x5b, 0x7d, 0xd5, 0x31, 0x7a, 0x56, 0xe3, 0xfd, 0xfd, 0xc4, 0x80, 0x5c, 0xf6, 0xcf, - 0x5d, 0x28, 0xfb, 0xe7, 0xa7, 0xcb, 0xfe, 0x85, 0x8b, 0x67, 0x7f, 0xe9, 0x12, 0xb2, 0x7f, 0x51, - 0x95, 0xfd, 0x1e, 0xe8, 0x38, 0x73, 0x95, 0xfb, 0x2e, 0x0d, 0x78, 0x56, 0xf0, 0xf7, 0x89, 0x44, - 0xc1, 0xed, 0x11, 0x55, 0x50, 0xc0, 0x69, 0x16, 0xca, 0x54, 0x56, 0x1b, 0x4c, 0x50, 0x6d, 0x8a, - 0x7c, 0xfb, 0x16, 0xab, 0xed, 0x9b, 0x59, 0xd0, 0x8b, 0x9c, 0x45, 0x3f, 0x85, 0x95, 0x3e, 0x28, - 0x8b, 0x57, 0x95, 0x2c, 0x37, 0x35, 0xd6, 0xc9, 0xf7, 0x83, 0x78, 0xfa, 0x9a, 0xfd, 0xc1, 0x4a, - 0xac, 0x87, 0xe6, 0xa4, 0x99, 0xe9, 0xe6, 0xa4, 0xcc, 0xe4, 0x30, 0x3b, 0xed, 0xe4, 0x30, 0x77, - 0xf9, 0x93, 0xc3, 0xfc, 0xe5, 0x4c, 0x0e, 0x0b, 0x97, 0x36, 0x39, 0x94, 0x54, 0x93, 0x83, 0xec, - 0xa5, 0xca, 0xd7, 0xc0, 0xbb, 0xed, 0xa5, 0xdf, 0x68, 0x70, 0x4d, 0x3c, 0xca, 0x12, 0x2f, 0x92, - 0x4e, 0xba, 0x37, 0xf8, 0xf2, 0xfa, 0xbe, 0xd2, 0x79, 0x15, 0xef, 0x84, 0x6f, 0xae, 0x8b, 0xcc, - 0x02, 0x93, 0x3d, 0xc9, 0xea, 0x7f, 0xd3, 0xe0, 0xbd, 0x01, 0x0b, 0x65, 0x54, 0x7f, 0x02, 0x4b, - 0xe2, 0xab, 0x8a, 0x15, 0x12, 0x1a, 0xb5, 0x13, 0x1f, 0x47, 0xe7, 0x49, 0x45, 0x70, 0x98, 0x82, - 0x01, 0x35, 0xa0, 0x9a, 0x08, 0xf8, 0x15, 0xb1, 0x19, 0x71, 0x46, 0xbe, 0x7f, 0xe3, 0x77, 0xaf, - 0xa4, 0x34, 0x97, 0xdf, 0x66, 0x97, 0xf5, 0x7f, 0x69, 0xb0, 0x1e, 0x1b, 0xe6, 0x08, 0x3a, 0xee, - 0xef, 0x9e, 0xdf, 0x09, 0xda, 0x84, 0x13, 0xcb, 0x50, 0xbe, 0x18, 0xbc, 0x8f, 0x07, 0x4a, 0x45, - 0xe3, 0xe4, 0x7c, 0x0b, 0x77, 0x73, 0x1d, 0x4a, 0x82, 0x57, 0xce, 0x68, 0x65, 0x73, 0x81, 0x2f, - 0x1b, 0x4e, 0xfd, 0x7d, 0xb8, 0x37, 0xc2, 0xbc, 0xf8, 0x62, 0xea, 0xff, 0xd4, 0xe0, 0xd6, 0x1e, - 0xf6, 0x6c, 0xd2, 0x7e, 0x11, 0x31, 0xca, 0xb0, 0xe7, 0xb8, 0xde, 0x09, 0x7f, 0x2d, 0x4f, 0x04, - 0xf1, 0xb9, 0x77, 0xfc, 0xcc, 0xc0, 0x3b, 0xfe, 0x29, 0x54, 0x53, 0xa7, 0xfa, 0xdf, 0x3a, 0xab, - 0x05, 0x65, 0x9d, 0x78, 0x16, 0x97, 0x35, 0xcb, 0xac, 0x2e, 0x82, 0xe3, 0xf5, 0xbb, 0x70, 0xbb, - 0xc0, 0x3d, 0x19, 0x80, 0xdf, 0xc0, 0xf5, 0x7d, 0x42, 0xed, 0xd0, 0x6d, 0x92, 0x94, 0x5d, 0xba, - 0x7e, 0x38, 0x98, 0x03, 0x1f, 0x2b, 0xb5, 0x16, 0xb0, 0x4f, 0x76, 0xf5, 0xf5, 0xaf, 0x35, 0xd0, - 0x87, 0x25, 0xc8, 0xb2, 0xf9, 0x0c, 0x4a, 0x71, 0x38, 0xa9, 0xae, 0x09, 0xc8, 0xbc, 0x5b, 0xf8, - 0x3d, 0x86, 0x84, 0x02, 0x87, 0x13, 0x7a, 0xf4, 0x1c, 0x56, 0xfb, 0xd1, 0xa7, 0x0c, 0xb3, 0x88, - 0xca, 0x92, 0x79, 0x7f, 0x64, 0xec, 0x5e, 0x09, 0x52, 0xb3, 0xca, 0x72, 0xeb, 0x3a, 0x85, 0xdb, - 0xe2, 0x3e, 0x06, 0x1b, 0x1d, 0x4d, 0x82, 0xb5, 0x06, 0x0b, 0xb2, 0xe5, 0xc6, 0x49, 0x22, 0x57, - 0xf9, 0xcb, 0x9b, 0x99, 0xee, 0xf2, 0xfe, 0x30, 0x03, 0x77, 0x8a, 0xb4, 0xca, 0x08, 0xbd, 0x85, - 0xdb, 0xfd, 0xaf, 0x24, 0xa9, 0xbf, 0x99, 0x7f, 0x8a, 0xc4, 0x71, 0x33, 0x46, 0xaa, 0x4c, 0xe5, - 0x3e, 0x27, 0x0c, 0x3b, 0x98, 0x61, 0xb3, 0x96, 0x1d, 0x67, 0xf2, 0xaa, 0xb9, 0xca, 0xf4, 0xc3, - 0xb0, 0x52, 0xe5, 0xcc, 0xf9, 0x54, 0x3a, 0x99, 0xd1, 0x3e, 0xaf, 0xb2, 0xfe, 0x00, 0x6e, 0x3e, - 0x25, 0x69, 0x18, 0xe8, 0x6e, 0x2f, 0xc6, 0xb1, 0x31, 0xb1, 0xaf, 0x7f, 0x3d, 0x07, 0xb7, 0xd4, - 0x7c, 0x32, 0x7a, 0xbf, 0xd3, 0x60, 0x4d, 0xe1, 0x4b, 0x07, 0x07, 0x32, 0x6e, 0x2f, 0x8a, 0x31, - 0x6f, 0x94, 0x60, 0x63, 0x7f, 0xc0, 0x97, 0xe7, 0x38, 0x88, 0x87, 0xb5, 0xab, 0xce, 0xf0, 0x89, - 0x30, 0x43, 0x71, 0x8b, 0xdc, 0x8c, 0x99, 0x0b, 0x99, 0xb1, 0x33, 0x70, 0x8b, 0x7d, 0x33, 0xf0, - 0xf0, 0x49, 0xed, 0xd7, 0xbc, 0x12, 0xd5, 0x76, 0x2b, 0x66, 0xc7, 0x67, 0xf9, 0x0f, 0xb1, 0x23, - 0x86, 0xe6, 0xa2, 0xf2, 0xce, 0xcc, 0x9b, 0x5c, 0x77, 0x91, 0xb1, 0xef, 0x5a, 0xf7, 0xf6, 0xdf, - 0x01, 0x2a, 0xcf, 0x25, 0xcf, 0xce, 0xcb, 0x06, 0xfa, 0xad, 0x06, 0x57, 0x15, 0x1f, 0xd2, 0xd1, - 0xa7, 0x53, 0x7e, 0x77, 0x17, 0xc9, 0x59, 0x7b, 0x70, 0xae, 0xaf, 0xf5, 0x59, 0x23, 0xb2, 0x81, - 0x99, 0xc0, 0x08, 0xc5, 0xc3, 0x61, 0x02, 0x23, 0x94, 0xc3, 0x60, 0x17, 0x56, 0x06, 0xde, 0xdc, - 0xe8, 0xfe, 0xb4, 0x9f, 0x08, 0x6a, 0x5b, 0x53, 0x70, 0xe4, 0xf4, 0xe6, 0xfc, 0xbe, 0x3f, 0xed, - 0x63, 0x69, 0x8c, 0x5e, 0xa5, 0xbf, 0x01, 0x2c, 0xe7, 0xe6, 0x37, 0x64, 0x14, 0xcb, 0x50, 0x8d, - 0xa2, 0xb5, 0xcd, 0x89, 0xe9, 0xa5, 0xc6, 0xbf, 0x68, 0x70, 0xa3, 0x70, 0x4a, 0x41, 0x8f, 0x8b, - 0xc5, 0x8d, 0x9b, 0xbc, 0x6a, 0x4f, 0xce, 0xc5, 0x2b, 0xcd, 0xfa, 0xa3, 0x06, 0xef, 0x29, 0xe7, - 0x06, 0xf4, 0xb0, 0x58, 0xec, 0xa8, 0x39, 0xaa, 0xf6, 0xa3, 0xa9, 0xf9, 0xa4, 0x29, 0x3d, 0x58, - 0x1d, 0x2c, 0x62, 0xb4, 0x35, 0x4d, 0xc1, 0xc7, 0xfa, 0xcf, 0xd1, 0x23, 0xd0, 0x9f, 0x34, 0x58, - 0x53, 0xe3, 0x2f, 0x1a, 0xe1, 0xce, 0xc8, 0x39, 0xa1, 0xf6, 0x68, 0x7a, 0x46, 0x69, 0xcd, 0xef, - 0x35, 0xb8, 0xa6, 0xea, 0xf6, 0xe8, 0xc1, 0xb4, 0xe8, 0x10, 0x5b, 0xf2, 0xf0, 0x7c, 0xa0, 0xb2, - 0xfb, 0xe4, 0x97, 0x9f, 0x9d, 0xb8, 0xac, 0x15, 0x35, 0x0d, 0xdb, 0xef, 0x6c, 0xe6, 0x7e, 0x18, - 0x62, 0x9c, 0x10, 0x2f, 0xfe, 0x25, 0x4d, 0xf6, 0xc7, 0x3c, 0x4f, 0x92, 0xbf, 0xbb, 0x5b, 0xcd, - 0x05, 0x71, 0xfa, 0xc9, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xac, 0xe9, 0x33, 0xfa, 0x23, - 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xdd, 0x6f, 0x1c, 0x49, + 0x11, 0xd7, 0xf8, 0x6b, 0xed, 0x5a, 0x7b, 0xed, 0x74, 0x72, 0xce, 0x64, 0x13, 0x27, 0xce, 0xde, + 0x25, 0x31, 0xe8, 0x58, 0xc7, 0xbe, 0x4b, 0xc8, 0x25, 0x82, 0xe0, 0xd8, 0x71, 0xb2, 0x88, 0x90, + 0xdc, 0xc4, 0x97, 0x48, 0xe8, 0x94, 0x51, 0x7b, 0xa7, 0xed, 0x1d, 0xbc, 0x3b, 0x33, 0x99, 0xee, + 0x59, 0xdf, 0xf2, 0xc0, 0x03, 0x02, 0x84, 0xc4, 0x1b, 0x82, 0x77, 0xe0, 0xf8, 0xa3, 0x90, 0x4e, + 0x3c, 0xf2, 0xc0, 0x23, 0x48, 0x3c, 0x22, 0xa1, 0xfe, 0x98, 0xd9, 0x99, 0xdd, 0x9e, 0xfd, 0xb0, + 0x9d, 0x3b, 0x1e, 0x78, 0x73, 0x77, 0x57, 0x55, 0x57, 0x55, 0x57, 0xd5, 0xaf, 0x6a, 0xd6, 0x70, + 0x33, 0xda, 0x27, 0xe1, 0x7a, 0x1d, 0x3b, 0xc4, 0xab, 0x93, 0xf5, 0x16, 0x66, 0xf5, 0x86, 0xeb, + 0x1d, 0xae, 0xb7, 0x37, 0xd6, 0x29, 0x09, 0xdb, 0x6e, 0x9d, 0x54, 0x83, 0xd0, 0x67, 0x3e, 0x32, + 0x39, 0x5d, 0x55, 0xd1, 0x55, 0x63, 0xba, 0x6a, 0x7b, 0xa3, 0x7c, 0xf5, 0xd0, 0xf7, 0x0f, 0x9b, + 0x64, 0x5d, 0xd0, 0xed, 0x47, 0x07, 0xeb, 0x4e, 0x14, 0x62, 0xe6, 0xfa, 0x9e, 0xe4, 0x2c, 0x5f, + 0xeb, 0x3d, 0x67, 0x6e, 0x8b, 0x50, 0x86, 0x5b, 0x81, 0x22, 0xe8, 0x13, 0x70, 0x1c, 0xe2, 0x20, + 0x20, 0x21, 0x55, 0xe7, 0xab, 0x19, 0x15, 0x71, 0xe0, 0x72, 0xed, 0xea, 0x7e, 0xab, 0xd5, 0xbd, + 0x42, 0x47, 0xf1, 0x36, 0x22, 0x61, 0x47, 0x11, 0x54, 0x74, 0x04, 0x0c, 0xd3, 0xa3, 0xa6, 0x4b, + 0x99, 0xa2, 0x59, 0xd3, 0xd1, 0x28, 0x27, 0xd8, 0xc7, 0x7e, 0x78, 0x44, 0x42, 0x45, 0xf9, 0xed, + 0x61, 0x94, 0x07, 0x4d, 0xff, 0x58, 0xd1, 0x5e, 0xd7, 0xd1, 0x36, 0x5c, 0xca, 0xfc, 0x44, 0xb9, + 0x0f, 0x32, 0x24, 0xb4, 0x81, 0x43, 0xe2, 0xf4, 0x53, 0xdd, 0xc8, 0xa1, 0xca, 0x5a, 0x51, 0xf9, + 0x83, 0x01, 0x17, 0xf7, 0x30, 0x3d, 0xfa, 0x91, 0x4b, 0xd9, 0x0b, 0x1c, 0x32, 0x97, 0xbf, 0xc4, + 0xb6, 0xef, 0x1d, 0xb8, 0x87, 0xc8, 0x84, 0x42, 0x9b, 0x84, 0xd4, 0xf5, 0x3d, 0xd3, 0x58, 0x35, + 0xd6, 0x26, 0xad, 0x78, 0x89, 0xaa, 0x70, 0xde, 0x8b, 0x5a, 0x76, 0x48, 0xb0, 0x63, 0x07, 0x31, + 0x17, 0x35, 0x27, 0x56, 0x8d, 0xb5, 0x69, 0xeb, 0x9c, 0x17, 0xb5, 0x2c, 0x82, 0x9d, 0x44, 0x1c, + 0x45, 0xb7, 0xe1, 0x02, 0xa7, 0x3f, 0x0e, 0x5d, 0x46, 0xd2, 0x0c, 0x93, 0x82, 0x01, 0x79, 0x51, + 0xeb, 0x35, 0x3f, 0xea, 0x72, 0x54, 0xfe, 0x69, 0x40, 0xf9, 0x85, 0xdf, 0x6c, 0xee, 0xfa, 0xe1, + 0x0e, 0xa9, 0xbb, 0xfc, 0x56, 0xae, 0xa6, 0x45, 0xde, 0x46, 0x84, 0x32, 0x54, 0x83, 0x42, 0x28, + 0xff, 0x14, 0xaa, 0x15, 0x37, 0xd7, 0xab, 0x99, 0x80, 0xc3, 0x81, 0x5b, 0x6d, 0x6f, 0x54, 0xf3, + 0x25, 0x58, 0x31, 0x3f, 0xba, 0x0c, 0x73, 0x8e, 0xdf, 0xc2, 0xae, 0x67, 0xbb, 0x8e, 0xb0, 0x60, + 0xce, 0x9a, 0x95, 0x1b, 0x35, 0x87, 0x1f, 0x06, 0x7e, 0xb3, 0x49, 0x42, 0x7e, 0x38, 0x29, 0x0f, + 0xe5, 0x46, 0xcd, 0x41, 0x37, 0xa0, 0x74, 0xe0, 0x87, 0xc7, 0x38, 0x74, 0x88, 0x63, 0x1f, 0x84, + 0x7e, 0xcb, 0x9c, 0x12, 0x14, 0x0b, 0xc9, 0xee, 0x6e, 0xe8, 0xb7, 0xd0, 0x2d, 0x58, 0x74, 0xa9, + 0xdf, 0x14, 0x31, 0x6e, 0x1f, 0x86, 0x7e, 0x14, 0x98, 0xd3, 0x82, 0xae, 0x94, 0x6c, 0x3f, 0xe1, + 0xbb, 0x95, 0xdf, 0x01, 0x5c, 0xd6, 0x6a, 0x4c, 0x03, 0xdf, 0xa3, 0x04, 0xad, 0x00, 0xf0, 0xd7, + 0xb3, 0x99, 0x7f, 0x44, 0xe4, 0x93, 0xcc, 0x5b, 0x73, 0x7c, 0x67, 0x8f, 0x6f, 0xa0, 0xcf, 0x00, + 0xc5, 0xc1, 0x64, 0x93, 0x2f, 0x48, 0x3d, 0xe2, 0x92, 0x85, 0x45, 0xc5, 0xcd, 0x9b, 0x5a, 0xf7, + 0xbc, 0x56, 0xe4, 0x8f, 0x63, 0x6a, 0xeb, 0xdc, 0x71, 0xef, 0x16, 0xda, 0x85, 0x85, 0x44, 0x2c, + 0xeb, 0x04, 0x44, 0xb8, 0xa1, 0xb8, 0x79, 0x7d, 0xa0, 0xc4, 0xbd, 0x4e, 0x40, 0xac, 0xf9, 0xe3, + 0xd4, 0x0a, 0xbd, 0x82, 0x4b, 0x41, 0x48, 0xda, 0xae, 0x1f, 0x51, 0x9b, 0x32, 0x1c, 0x32, 0xe2, + 0xd8, 0xa4, 0x4d, 0x3c, 0xc6, 0x5d, 0x3b, 0x25, 0x64, 0x5e, 0xae, 0xca, 0xd4, 0xae, 0xc6, 0xa9, + 0x5d, 0xad, 0x79, 0xec, 0xee, 0xc7, 0xaf, 0x70, 0x33, 0x22, 0xd6, 0x72, 0xcc, 0xfd, 0x52, 0x32, + 0x3f, 0xe6, 0xbc, 0x35, 0x07, 0xad, 0xc1, 0x52, 0x9f, 0xb8, 0x69, 0x11, 0xae, 0x25, 0x9a, 0xa5, + 0x34, 0xa1, 0x80, 0x19, 0x23, 0xad, 0x80, 0x99, 0x33, 0x22, 0xf0, 0xe2, 0x25, 0xaa, 0xc0, 0x82, + 0x47, 0xbe, 0x60, 0x5d, 0x01, 0x05, 0x21, 0xa0, 0xc8, 0x37, 0x63, 0xee, 0x0f, 0x01, 0xed, 0xe3, + 0xfa, 0x51, 0xd3, 0x3f, 0xb4, 0xeb, 0x7e, 0xe4, 0x31, 0xbb, 0xe1, 0x7a, 0xcc, 0x9c, 0x15, 0x84, + 0x4b, 0xea, 0x64, 0x9b, 0x1f, 0x3c, 0x75, 0x3d, 0x86, 0xee, 0x81, 0x49, 0x99, 0x5b, 0x3f, 0xea, + 0x74, 0x9f, 0xc2, 0x26, 0x1e, 0xde, 0x6f, 0x12, 0xc7, 0x9c, 0x5b, 0x35, 0xd6, 0x66, 0xad, 0x65, + 0x79, 0x9e, 0x38, 0xfa, 0xb1, 0x3c, 0x45, 0xf7, 0x60, 0x5a, 0x94, 0x22, 0x13, 0x84, 0x4f, 0x2a, + 0x03, 0xfd, 0xfc, 0x29, 0xa7, 0xb4, 0x24, 0x03, 0xb2, 0x60, 0xc1, 0x51, 0x71, 0x63, 0xbb, 0xde, + 0x81, 0x6f, 0x16, 0x85, 0x84, 0xef, 0x64, 0x25, 0xc8, 0x52, 0xc0, 0x85, 0xec, 0x85, 0xd8, 0xa3, + 0x2e, 0xf1, 0x58, 0x1c, 0x6d, 0x35, 0xef, 0xc0, 0xb7, 0xe6, 0x9d, 0xd4, 0x0a, 0xbd, 0x81, 0x2b, + 0xfd, 0x41, 0x65, 0x8b, 0x30, 0xe4, 0x55, 0xc4, 0x9c, 0x17, 0x57, 0xac, 0x68, 0x95, 0x8c, 0xeb, + 0x8a, 0x75, 0xa9, 0x2f, 0xaa, 0xe2, 0x23, 0x5e, 0x49, 0xa4, 0xd3, 0x79, 0xed, 0x22, 0x76, 0x5c, + 0x6f, 0x16, 0x64, 0x25, 0x11, 0x47, 0x2f, 0xf9, 0xc9, 0x2b, 0x55, 0x79, 0xae, 0xc3, 0xfc, 0x7e, + 0x88, 0xbd, 0x7a, 0x43, 0x65, 0x41, 0x49, 0x64, 0x41, 0x51, 0xee, 0xc9, 0x3c, 0xd8, 0x82, 0x12, + 0xad, 0x37, 0x88, 0x13, 0x35, 0x89, 0x63, 0x73, 0xf0, 0x30, 0x17, 0x85, 0x92, 0xe5, 0xbe, 0xe8, + 0xda, 0x8b, 0x91, 0xc5, 0x5a, 0x48, 0x38, 0xf8, 0x1e, 0xfa, 0x1e, 0xcc, 0xc7, 0x31, 0x25, 0x04, + 0x2c, 0x0d, 0x15, 0x50, 0x54, 0xf4, 0x82, 0xfd, 0x73, 0x28, 0xf0, 0x17, 0x71, 0x09, 0x35, 0xcf, + 0xad, 0x4e, 0xae, 0x15, 0x37, 0x1f, 0x55, 0xf3, 0xe0, 0xb0, 0x3a, 0x20, 0xe1, 0xab, 0x9f, 0x4a, + 0x21, 0x8f, 0x3d, 0x16, 0x76, 0xac, 0x58, 0x24, 0x77, 0x19, 0xf3, 0x19, 0x6e, 0xda, 0xaa, 0xe0, + 0xdb, 0xfb, 0x1d, 0x46, 0xa8, 0x89, 0x44, 0x24, 0x9e, 0x13, 0x47, 0x4f, 0xe5, 0xc9, 0x23, 0x7e, + 0x80, 0x3e, 0x87, 0xa5, 0xa4, 0xe4, 0xda, 0x75, 0x51, 0xda, 0xcd, 0xf3, 0xc2, 0xa0, 0x8d, 0x7c, + 0xb5, 0x72, 0x30, 0xc1, 0x5a, 0x0c, 0xb2, 0x1b, 0xe5, 0x37, 0x30, 0x9f, 0x56, 0x13, 0x2d, 0xc1, + 0xe4, 0x11, 0xe9, 0x88, 0xea, 0x34, 0x67, 0xf1, 0x3f, 0x79, 0x40, 0xb7, 0x79, 0x06, 0xab, 0x52, + 0x34, 0x52, 0x40, 0x0b, 0x86, 0xfb, 0x13, 0xf7, 0x8c, 0x34, 0x10, 0x6c, 0xd5, 0x99, 0xdb, 0x76, + 0x59, 0xe7, 0xe4, 0x40, 0xa0, 0x91, 0xf0, 0xbf, 0x08, 0x04, 0xff, 0x98, 0x4d, 0x80, 0x20, 0xab, + 0xf1, 0x37, 0x0a, 0x04, 0xd7, 0xa0, 0x88, 0x95, 0x36, 0x5d, 0x27, 0x40, 0xbc, 0x55, 0x73, 0x38, + 0x52, 0x24, 0x04, 0x02, 0x29, 0xa6, 0x06, 0x20, 0x45, 0x62, 0x98, 0x40, 0x0a, 0x9c, 0x5a, 0xa1, + 0x4d, 0x98, 0x76, 0xbd, 0x20, 0x62, 0xc2, 0x3b, 0xc5, 0xcd, 0x2b, 0xfa, 0x17, 0xc5, 0x9d, 0xa6, + 0x8f, 0x1d, 0x4b, 0x92, 0x6a, 0x92, 0x7e, 0xe6, 0xb4, 0x49, 0x5f, 0x18, 0x2f, 0xe9, 0xf7, 0xe0, + 0x52, 0x2c, 0xcf, 0x66, 0xbe, 0x5d, 0x6f, 0xfa, 0x94, 0x08, 0x41, 0x7e, 0x24, 0x61, 0xa2, 0xb8, + 0x79, 0xa9, 0x4f, 0xd6, 0x8e, 0xea, 0x7d, 0xad, 0xe5, 0x98, 0x77, 0xcf, 0xdf, 0xe6, 0x9c, 0x7b, + 0x92, 0x11, 0xfd, 0x18, 0x96, 0xc5, 0x25, 0xfd, 0x22, 0xe7, 0x86, 0x89, 0x3c, 0x2f, 0x18, 0x7b, + 0xe4, 0xed, 0xc2, 0xb9, 0x06, 0xc1, 0x21, 0xdb, 0x27, 0x98, 0x25, 0xa2, 0x60, 0x98, 0xa8, 0xa5, + 0x84, 0x27, 0x96, 0x93, 0xc2, 0xd2, 0x62, 0x16, 0x4b, 0xdf, 0xc0, 0xd5, 0xec, 0x4b, 0xd8, 0xfe, + 0x81, 0xcd, 0x1a, 0x2e, 0xb5, 0x63, 0x86, 0xf9, 0xa1, 0x8e, 0x2d, 0x67, 0x5e, 0xe6, 0xf9, 0xc1, + 0x5e, 0xc3, 0xa5, 0x5b, 0x4a, 0x7e, 0x2d, 0x6d, 0x81, 0x43, 0x18, 0x76, 0x9b, 0x54, 0xe0, 0xc5, + 0xb0, 0x48, 0xe9, 0x1a, 0xb1, 0x23, 0xb9, 0xfa, 0x5b, 0x9b, 0xd2, 0xc9, 0x5a, 0x9b, 0x5b, 0xb0, + 0x98, 0xc8, 0x91, 0x15, 0x43, 0x40, 0xce, 0x9c, 0x55, 0x8a, 0xb7, 0x77, 0xc4, 0x2e, 0xfa, 0x08, + 0x66, 0x1a, 0x04, 0x3b, 0x24, 0x54, 0x88, 0x72, 0x59, 0x7b, 0xd3, 0x53, 0x41, 0x62, 0x29, 0xd2, + 0x77, 0x5b, 0xbf, 0x2b, 0x7f, 0x9d, 0x82, 0xe5, 0x2d, 0xc7, 0xd1, 0x35, 0xd9, 0x99, 0x82, 0x68, + 0xf4, 0x14, 0xc4, 0x77, 0x54, 0x64, 0xee, 0xc3, 0x5c, 0xb7, 0xb9, 0x98, 0x1c, 0xa5, 0xb9, 0x98, + 0x65, 0x71, 0x2f, 0x71, 0x0d, 0x8a, 0x49, 0x06, 0xaa, 0x9e, 0x72, 0xd2, 0x82, 0x78, 0xab, 0xe6, + 0xf4, 0xa6, 0xa8, 0x4a, 0x2c, 0x95, 0x04, 0xd3, 0x63, 0xa4, 0xa8, 0x68, 0x41, 0xe3, 0x54, 0xb8, + 0x0f, 0x33, 0xd4, 0x8f, 0xc2, 0xba, 0x2c, 0x39, 0xa5, 0x5e, 0x80, 0x4b, 0xf5, 0x5b, 0x98, 0x1e, + 0xbd, 0x14, 0x94, 0x96, 0xe2, 0xd0, 0x20, 0x47, 0x41, 0x87, 0x1c, 0x81, 0x26, 0x04, 0x66, 0x45, + 0x67, 0xf1, 0x38, 0x3f, 0x04, 0xf4, 0xaf, 0x5a, 0xed, 0x89, 0x08, 0xd9, 0x5c, 0xf4, 0xc1, 0xfa, + 0x23, 0xb8, 0xa0, 0x23, 0xd4, 0xc0, 0xfb, 0x85, 0x34, 0xbc, 0xcf, 0xa5, 0xa1, 0xfb, 0x18, 0x2e, + 0xf6, 0xe9, 0xa0, 0x10, 0x4c, 0x17, 0xd3, 0xc6, 0x99, 0xc5, 0xf4, 0xbf, 0xa6, 0x45, 0x4c, 0xeb, + 0xfa, 0x85, 0x6f, 0x22, 0xa6, 0xf9, 0x84, 0x22, 0x9e, 0xdb, 0xee, 0x5e, 0x2d, 0xd1, 0xb3, 0x24, + 0xf7, 0x77, 0x62, 0x05, 0x32, 0xd1, 0x3f, 0x75, 0xaa, 0xe8, 0x9f, 0x1e, 0x2f, 0xfa, 0x67, 0x4e, + 0x1f, 0xfd, 0x85, 0x33, 0x88, 0xfe, 0x59, 0x5d, 0xf4, 0x7b, 0x60, 0xe2, 0xd4, 0x53, 0xee, 0xb8, + 0x34, 0xe0, 0x51, 0xc1, 0xe7, 0x13, 0x85, 0x82, 0x9b, 0x03, 0xb2, 0x20, 0x87, 0xd3, 0xca, 0x95, + 0xa9, 0xcd, 0x36, 0x18, 0x21, 0xdb, 0x34, 0xf1, 0xf6, 0x35, 0x66, 0xdb, 0x57, 0x93, 0x60, 0xe6, + 0x19, 0x8b, 0x7e, 0x08, 0x8b, 0x5d, 0x50, 0x16, 0x53, 0x95, 0x4a, 0x37, 0x3d, 0xd6, 0xa9, 0xf9, + 0x41, 0x8c, 0xbe, 0x56, 0xb7, 0xb1, 0x12, 0xeb, 0xbe, 0x3e, 0x69, 0x62, 0xbc, 0x3e, 0x29, 0xd5, + 0x39, 0x4c, 0x8e, 0xdb, 0x39, 0x4c, 0x9d, 0x7d, 0xe7, 0x30, 0x7d, 0x36, 0x9d, 0xc3, 0xcc, 0x99, + 0x75, 0x0e, 0x05, 0x5d, 0xe7, 0xa0, 0x6a, 0xa9, 0x76, 0x1a, 0x78, 0xb7, 0xb5, 0xf4, 0x2b, 0x03, + 0x2e, 0x88, 0xa1, 0x2c, 0xb6, 0x22, 0xae, 0xa4, 0xdb, 0xbd, 0x93, 0xd7, 0xb7, 0xb4, 0xc6, 0xeb, + 0x78, 0x47, 0x9c, 0xb9, 0x4e, 0xd3, 0x0b, 0x8c, 0x36, 0x92, 0x55, 0xbe, 0x34, 0xe0, 0xbd, 0x1e, + 0x0d, 0x95, 0x57, 0x1f, 0xc2, 0xbc, 0xf8, 0xaa, 0x62, 0x87, 0x84, 0x46, 0xcd, 0xd8, 0xc6, 0xc1, + 0x71, 0x52, 0x14, 0x1c, 0x96, 0x60, 0x40, 0x35, 0x28, 0xc5, 0x02, 0x7e, 0x4a, 0xea, 0x8c, 0x38, + 0x03, 0xe7, 0x5f, 0x39, 0xf7, 0x2a, 0x4a, 0x6b, 0xe1, 0x6d, 0x7a, 0x59, 0xf9, 0xbb, 0x01, 0xab, + 0x52, 0x31, 0x47, 0xd0, 0x71, 0x7b, 0xb7, 0xfd, 0x56, 0xd0, 0x24, 0x9c, 0x58, 0xb9, 0xf2, 0x79, + 0xef, 0x7b, 0xdc, 0xd1, 0x5e, 0x34, 0x4c, 0xce, 0xd7, 0xf0, 0x36, 0x17, 0xa1, 0x20, 0x78, 0x55, + 0x8f, 0x36, 0x67, 0xcd, 0xf0, 0x65, 0xcd, 0xa9, 0xbc, 0x0f, 0xd7, 0x07, 0xa8, 0x27, 0x1f, 0xa6, + 0xf2, 0x37, 0x03, 0xae, 0x6c, 0x63, 0xaf, 0x4e, 0x9a, 0xcf, 0x23, 0x46, 0x19, 0xf6, 0x1c, 0xd7, + 0x3b, 0xe4, 0xd3, 0xf2, 0x48, 0x10, 0x9f, 0x99, 0xe3, 0x27, 0x7a, 0xe6, 0xf8, 0x27, 0x50, 0x4a, + 0x8c, 0xea, 0x7e, 0xeb, 0x2c, 0xe5, 0xa4, 0x75, 0x6c, 0x99, 0x4c, 0x6b, 0x96, 0x5a, 0x9d, 0x06, + 0xc7, 0x2b, 0xd7, 0x60, 0x25, 0xc7, 0x3c, 0xe5, 0x80, 0x9f, 0xc3, 0xc5, 0x1d, 0x42, 0xeb, 0xa1, + 0xbb, 0x4f, 0x12, 0x76, 0x65, 0xfa, 0x6e, 0x6f, 0x0c, 0x7c, 0xa8, 0xbd, 0x35, 0x87, 0x7d, 0xb4, + 0xa7, 0xaf, 0xfc, 0xc7, 0x00, 0xb3, 0x5f, 0x82, 0x4a, 0x9b, 0x4f, 0xa0, 0x20, 0xdd, 0x49, 0x4d, + 0x43, 0x40, 0xe6, 0xb5, 0xdc, 0xef, 0x31, 0x24, 0x14, 0x38, 0x1c, 0xd3, 0xa3, 0x67, 0xb0, 0xd4, + 0xf5, 0x3e, 0x65, 0x98, 0x45, 0x54, 0xa5, 0xcc, 0xfb, 0x03, 0x7d, 0xf7, 0x52, 0x90, 0x5a, 0x25, + 0x96, 0x59, 0xa3, 0xd7, 0x9a, 0xb2, 0x38, 0x39, 0xc0, 0x29, 0x23, 0x57, 0x44, 0x0a, 0x2b, 0xe2, + 0xa1, 0x7b, 0xe9, 0x69, 0xfc, 0x0a, 0xcb, 0x30, 0xa3, 0x6a, 0xb9, 0x8c, 0x3e, 0xb5, 0xca, 0x46, + 0xc5, 0xc4, 0x78, 0x51, 0xf1, 0xeb, 0x09, 0xb8, 0x9a, 0x77, 0xab, 0x72, 0xfd, 0x5b, 0x58, 0xe9, + 0x7e, 0x7e, 0x49, 0x1c, 0x99, 0xfa, 0xb5, 0x45, 0x3e, 0x48, 0x75, 0x34, 0xeb, 0x9f, 0x11, 0x86, + 0x1d, 0xcc, 0xb0, 0x55, 0x4e, 0xf7, 0x49, 0xd9, 0xab, 0xf9, 0x95, 0xc9, 0x17, 0x67, 0xed, 0x95, + 0x13, 0x27, 0xbb, 0xd2, 0x49, 0xcd, 0x0c, 0xd9, 0x2b, 0x2b, 0x77, 0xe0, 0xf2, 0x13, 0x92, 0xb8, + 0x81, 0x3e, 0xea, 0x48, 0x80, 0x1c, 0xe2, 0xfb, 0xca, 0x5f, 0xa6, 0xe0, 0x8a, 0x9e, 0x4f, 0x79, + 0xef, 0x97, 0x06, 0x2c, 0x6b, 0x6c, 0x69, 0xe1, 0x40, 0xf9, 0xed, 0x79, 0x3e, 0x98, 0x0e, 0x12, + 0x5c, 0xdd, 0xe9, 0xb1, 0xe5, 0x19, 0x0e, 0x64, 0x17, 0x78, 0xde, 0xe9, 0x3f, 0x11, 0x6a, 0x68, + 0x5e, 0x91, 0xab, 0x31, 0x71, 0x2a, 0x35, 0xb6, 0x7a, 0x5e, 0xb1, 0xab, 0x06, 0xee, 0x3f, 0x29, + 0xff, 0x8c, 0xa7, 0xb8, 0x5e, 0x6f, 0x4d, 0x53, 0xfa, 0x34, 0xfb, 0x85, 0x77, 0x40, 0x37, 0x9e, + 0x57, 0x37, 0x52, 0x8d, 0x2c, 0xbf, 0x3b, 0x4f, 0xd9, 0x77, 0x7d, 0x77, 0xe5, 0x4f, 0x13, 0xf0, + 0xc1, 0x67, 0x81, 0x83, 0x19, 0xc9, 0x2b, 0x07, 0xa3, 0x80, 0xcc, 0x29, 0x12, 0xfd, 0xec, 0x30, + 0x48, 0x57, 0xff, 0xa6, 0xce, 0xa2, 0xfe, 0xdd, 0x82, 0x1b, 0x43, 0x5c, 0xa4, 0x80, 0xea, 0xcf, + 0x13, 0x70, 0xc3, 0x22, 0x07, 0x21, 0xa1, 0x8d, 0xff, 0x7b, 0x33, 0xcf, 0x9b, 0x6b, 0x70, 0x73, + 0x98, 0x8f, 0xa4, 0x3b, 0x37, 0xff, 0x3d, 0x0f, 0xc5, 0x67, 0x2a, 0x9e, 0xb7, 0x5e, 0xd4, 0xd0, + 0x2f, 0x0c, 0x38, 0xaf, 0xf9, 0xf5, 0x08, 0x7d, 0x3c, 0xe6, 0x8f, 0x4d, 0xe2, 0x09, 0xca, 0x77, + 0x4e, 0xf4, 0x13, 0x55, 0x5a, 0x89, 0x74, 0xd2, 0x8e, 0xa0, 0x84, 0x66, 0x5a, 0x1e, 0x41, 0x09, + 0xed, 0x04, 0xd4, 0x86, 0xc5, 0x9e, 0x0f, 0x4d, 0xe8, 0xf6, 0xb8, 0xdf, 0xc5, 0xca, 0x1b, 0x63, + 0x70, 0x64, 0xee, 0xcd, 0xd8, 0x7d, 0x7b, 0xdc, 0x2f, 0x04, 0x43, 0xee, 0xd5, 0xda, 0x1b, 0xc0, + 0x42, 0x66, 0x68, 0x41, 0xd5, 0x7c, 0x19, 0xba, 0xf9, 0xab, 0xbc, 0x3e, 0x32, 0xbd, 0xba, 0xf1, + 0xf7, 0x06, 0x5c, 0xca, 0x6d, 0xcd, 0xd1, 0xfd, 0x7c, 0x71, 0xc3, 0xc6, 0x8d, 0xf2, 0x83, 0x13, + 0xf1, 0x2a, 0xb5, 0x7e, 0x63, 0xc0, 0x7b, 0xda, 0x66, 0x19, 0xdd, 0xcd, 0x17, 0x3b, 0x68, 0x78, + 0x28, 0x7f, 0x77, 0x6c, 0x3e, 0xa5, 0x4a, 0x07, 0x96, 0x7a, 0x01, 0x06, 0x6d, 0x8c, 0x03, 0x46, + 0xf2, 0xfe, 0x13, 0xe0, 0x17, 0xfa, 0xad, 0x01, 0xcb, 0xfa, 0xde, 0x10, 0x0d, 0x30, 0x67, 0x60, + 0x0f, 0x5b, 0xbe, 0x37, 0x3e, 0xa3, 0xd2, 0xe6, 0x57, 0x06, 0x5c, 0xd0, 0x75, 0x22, 0xe8, 0xce, + 0xb8, 0x9d, 0x8b, 0xd4, 0xe4, 0xee, 0xc9, 0x1a, 0x1e, 0xf4, 0x47, 0x03, 0x56, 0x06, 0xe2, 0x14, + 0xfa, 0x7e, 0xbe, 0xe4, 0x51, 0x7a, 0x80, 0xf2, 0xc3, 0x13, 0xf3, 0x2b, 0x15, 0xbf, 0x34, 0xe0, + 0xea, 0xe0, 0xe2, 0x8f, 0x1e, 0x0e, 0x4a, 0x8f, 0x11, 0xa0, 0xb5, 0xfc, 0x83, 0x93, 0x0b, 0x90, + 0x5a, 0x3e, 0x7a, 0xf0, 0x93, 0x4f, 0x0e, 0x5d, 0xd6, 0x88, 0xf6, 0xab, 0x75, 0xbf, 0xb5, 0x9e, + 0xf9, 0xb7, 0xb2, 0xea, 0x21, 0xf1, 0xe4, 0xff, 0xe1, 0xa5, 0xff, 0x15, 0xf0, 0x41, 0xfc, 0x77, + 0x7b, 0x63, 0x7f, 0x46, 0x9c, 0x7e, 0xf4, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0xef, 0x5a, + 0x97, 0x38, 0x28, 0x00, 0x00, }, // google/protobuf/duration.proto []byte{ diff --git a/client/clientfactory.go b/client/clientfactory.go index 93b5cf116d3..bf0e1c86385 100644 --- a/client/clientfactory.go +++ b/client/clientfactory.go @@ -151,7 +151,7 @@ func (cf *rpcClientFactory) NewMatchingClientWithTimeout( peerResolver := matching.NewPeerResolver(cf.resolver, namedPort) - partitionConfigProvider := matching.NewPartitionConfigProvider(cf.logger, domainIDToName, cf.dynConfig) + partitionConfigProvider := matching.NewPartitionConfigProvider(cf.logger, cf.metricsClient, domainIDToName, cf.dynConfig) defaultLoadBalancer := matching.NewLoadBalancer(partitionConfigProvider) roundRobinLoadBalancer := matching.NewRoundRobinLoadBalancer(partitionConfigProvider) weightedLoadBalancer := matching.NewWeightedLoadBalancer(roundRobinLoadBalancer, partitionConfigProvider, cf.logger) diff --git a/client/matching/client.go b/client/matching/client.go index 6dd15b637c9..01686cb953d 100644 --- a/client/matching/client.go +++ b/client/matching/client.go @@ -65,12 +65,24 @@ func (c *clientImpl) AddActivityTask( persistence.TaskListTypeActivity, request.GetForwardedFrom(), ) + originalTaskListName := request.TaskList.GetName() request.TaskList.Name = partition peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } - return c.client.AddActivityTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) + resp, err := c.client.AddActivityTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) + if err != nil { + return nil, err + } + request.TaskList.Name = originalTaskListName + c.provider.UpdatePartitionConfig( + request.GetDomainUUID(), + *request.TaskList, + persistence.TaskListTypeActivity, + resp.PartitionConfig, + ) + return resp, nil } func (c *clientImpl) AddDecisionTask( @@ -84,12 +96,24 @@ func (c *clientImpl) AddDecisionTask( persistence.TaskListTypeDecision, request.GetForwardedFrom(), ) + originalTaskListName := request.TaskList.GetName() request.TaskList.Name = partition peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } - return c.client.AddDecisionTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) + resp, err := c.client.AddDecisionTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) + if err != nil { + return nil, err + } + request.TaskList.Name = originalTaskListName + c.provider.UpdatePartitionConfig( + request.GetDomainUUID(), + *request.TaskList, + persistence.TaskListTypeDecision, + resp.PartitionConfig, + ) + return resp, nil } func (c *clientImpl) PollForActivityTask( @@ -103,13 +127,25 @@ func (c *clientImpl) PollForActivityTask( persistence.TaskListTypeActivity, request.GetForwardedFrom(), ) + originalTaskListName := request.PollRequest.GetTaskList().GetName() request.PollRequest.TaskList.Name = partition peer, err := c.peerResolver.FromTaskList(request.PollRequest.TaskList.GetName()) if err != nil { return nil, err } // TODO: update activity response to include backlog count hint and update the weight for partitions - return c.client.PollForActivityTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) + resp, err := c.client.PollForActivityTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) + if err != nil { + return nil, err + } + request.PollRequest.TaskList.Name = originalTaskListName + c.provider.UpdatePartitionConfig( + request.GetDomainUUID(), + *request.PollRequest.GetTaskList(), + persistence.TaskListTypeActivity, + resp.PartitionConfig, + ) + return resp, nil } func (c *clientImpl) PollForDecisionTask( @@ -263,3 +299,27 @@ func (c *clientImpl) GetTaskListsByDomain( ActivityTaskListMap: activityTaskListMap, }, nil } + +func (c *clientImpl) UpdateTaskListPartitionConfig( + ctx context.Context, + request *types.MatchingUpdateTaskListPartitionConfigRequest, + opts ...yarpc.CallOption, +) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { + peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) + if err != nil { + return nil, err + } + return c.client.UpdateTaskListPartitionConfig(ctx, request, append(opts, yarpc.WithShardKey(peer))...) +} + +func (c *clientImpl) RefreshTaskListPartitionConfig( + ctx context.Context, + request *types.MatchingRefreshTaskListPartitionConfigRequest, + opts ...yarpc.CallOption, +) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { + peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) + if err != nil { + return nil, err + } + return c.client.RefreshTaskListPartitionConfig(ctx, request, append(opts, yarpc.WithShardKey(peer))...) +} diff --git a/client/matching/client_test.go b/client/matching/client_test.go index d71ec7d2dc5..695bf5c3a8f 100644 --- a/client/matching/client_test.go +++ b/client/matching/client_test.go @@ -163,6 +163,7 @@ func TestClient_withResponse(t *testing.T) { balancer.EXPECT().PickWritePartition(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeActivity, "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddActivityTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.AddActivityTaskResponse{}, nil) + mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeActivity, nil) }, want: &types.AddActivityTaskResponse{}, }, @@ -198,6 +199,7 @@ func TestClient_withResponse(t *testing.T) { balancer.EXPECT().PickWritePartition(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeDecision, "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.AddDecisionTaskResponse{}, nil) + mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeDecision, nil) }, want: &types.AddDecisionTaskResponse{}, }, @@ -233,6 +235,7 @@ func TestClient_withResponse(t *testing.T) { balancer.EXPECT().PickReadPartition(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeActivity, "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingPollForActivityTaskResponse{}, nil) + mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeActivity, nil) }, want: &types.MatchingPollForActivityTaskResponse{}, }, @@ -431,6 +434,74 @@ func TestClient_withResponse(t *testing.T) { want: nil, wantError: true, }, + { + name: "UpdateTaskListPartitionConfig", + op: func(c Client) (any, error) { + return c.UpdateTaskListPartitionConfig(context.Background(), testMatchingUpdateTaskListPartitionConfigRequest()) + }, + mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { + p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) + c.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingUpdateTaskListPartitionConfigResponse{}, nil) + }, + want: &types.MatchingUpdateTaskListPartitionConfigResponse{}, + }, + { + name: "UpdateTaskListPartitionConfig - Error in resolving peer", + op: func(c Client) (any, error) { + return c.UpdateTaskListPartitionConfig(context.Background(), testMatchingUpdateTaskListPartitionConfigRequest()) + }, + mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { + p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) + }, + want: nil, + wantError: true, + }, + { + name: "UpdateTaskListPartitionConfig - Error while listing tasklist partitions", + op: func(c Client) (any, error) { + return c.UpdateTaskListPartitionConfig(context.Background(), testMatchingUpdateTaskListPartitionConfigRequest()) + }, + mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { + p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) + c.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) + }, + want: nil, + wantError: true, + }, + { + name: "RefreshTaskListPartitionConfig", + op: func(c Client) (any, error) { + return c.RefreshTaskListPartitionConfig(context.Background(), testMatchingRefreshTaskListPartitionConfigRequest()) + }, + mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { + p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) + c.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil) + }, + want: &types.MatchingRefreshTaskListPartitionConfigResponse{}, + }, + { + name: "RefreshTaskListPartitionConfig - Error in resolving peer", + op: func(c Client) (any, error) { + return c.RefreshTaskListPartitionConfig(context.Background(), testMatchingRefreshTaskListPartitionConfigRequest()) + }, + mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { + p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) + }, + want: nil, + wantError: true, + }, + { + name: "RefreshTaskListPartitionConfig - Error while listing tasklist partitions", + op: func(c Client) (any, error) { + return c.RefreshTaskListPartitionConfig(context.Background(), testMatchingRefreshTaskListPartitionConfigRequest()) + }, + mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { + p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) + c.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) + }, + want: nil, + wantError: true, + }, } for _, tt := range tests { tt := tt @@ -526,3 +597,27 @@ func testGetTaskListsByDomainRequest() *types.GetTaskListsByDomainRequest { Domain: _testDomain, } } + +func testMatchingUpdateTaskListPartitionConfigRequest() *types.MatchingUpdateTaskListPartitionConfigRequest { + return &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: _testDomainUUID, + TaskList: &types.TaskList{Name: _testTaskList}, + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 2, + }, + } +} + +func testMatchingRefreshTaskListPartitionConfigRequest() *types.MatchingRefreshTaskListPartitionConfigRequest { + return &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: _testDomainUUID, + TaskList: &types.TaskList{Name: _testTaskList}, + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 2, + }, + } +} diff --git a/client/matching/interface.go b/client/matching/interface.go index bc43f6a453c..261b86c356c 100644 --- a/client/matching/interface.go +++ b/client/matching/interface.go @@ -48,4 +48,6 @@ type Client interface { PollForDecisionTask(context.Context, *types.MatchingPollForDecisionTaskRequest, ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) QueryWorkflow(context.Context, *types.MatchingQueryWorkflowRequest, ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) RespondQueryTaskCompleted(context.Context, *types.MatchingRespondQueryTaskCompletedRequest, ...yarpc.CallOption) error + UpdateTaskListPartitionConfig(context.Context, *types.MatchingUpdateTaskListPartitionConfigRequest, ...yarpc.CallOption) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) + RefreshTaskListPartitionConfig(context.Context, *types.MatchingRefreshTaskListPartitionConfigRequest, ...yarpc.CallOption) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) } diff --git a/client/matching/interface_mock.go b/client/matching/interface_mock.go index fccb6ba34e3..d840a15287f 100644 --- a/client/matching/interface_mock.go +++ b/client/matching/interface_mock.go @@ -238,6 +238,26 @@ func (mr *MockClientMockRecorder) QueryWorkflow(arg0, arg1 interface{}, arg2 ... return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockClient)(nil).QueryWorkflow), varargs...) } +// RefreshTaskListPartitionConfig mocks base method. +func (m *MockClient) RefreshTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingRefreshTaskListPartitionConfigRequest, arg2 ...yarpc.CallOption) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", varargs...) + ret0, _ := ret[0].(*types.MatchingRefreshTaskListPartitionConfigResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. +func (mr *MockClientMockRecorder) RefreshTaskListPartitionConfig(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockClient)(nil).RefreshTaskListPartitionConfig), varargs...) +} + // RespondQueryTaskCompleted mocks base method. func (m *MockClient) RespondQueryTaskCompleted(arg0 context.Context, arg1 *types.MatchingRespondQueryTaskCompletedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() @@ -256,3 +276,23 @@ func (mr *MockClientMockRecorder) RespondQueryTaskCompleted(arg0, arg1 interface varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondQueryTaskCompleted", reflect.TypeOf((*MockClient)(nil).RespondQueryTaskCompleted), varargs...) } + +// UpdateTaskListPartitionConfig mocks base method. +func (m *MockClient) UpdateTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingUpdateTaskListPartitionConfigRequest, arg2 ...yarpc.CallOption) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", varargs...) + ret0, _ := ret[0].(*types.MatchingUpdateTaskListPartitionConfigResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. +func (mr *MockClientMockRecorder) UpdateTaskListPartitionConfig(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockClient)(nil).UpdateTaskListPartitionConfig), varargs...) +} diff --git a/client/matching/partition_config_provider.go b/client/matching/partition_config_provider.go index 90bd71beb7b..ec431062408 100644 --- a/client/matching/partition_config_provider.go +++ b/client/matching/partition_config_provider.go @@ -31,6 +31,8 @@ import ( "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" + "github.com/uber/cadence/common/metrics" + "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) @@ -55,6 +57,7 @@ type ( partitionConfigProviderImpl struct { configCache cache.Cache logger log.Logger + metricsClient metrics.Client domainIDToName func(string) (string, error) enableReadFromCache dynamicconfig.BoolPropertyFnWithTaskListInfoFilters nReadPartitions dynamicconfig.IntPropertyFnWithTaskListInfoFilters @@ -74,11 +77,13 @@ func (c *syncedTaskListPartitionConfig) updateConfig(newConfig types.TaskListPar func NewPartitionConfigProvider( logger log.Logger, + metricsClient metrics.Client, domainIDToName func(string) (string, error), dc *dynamicconfig.Collection, ) PartitionConfigProvider { return &partitionConfigProviderImpl{ logger: logger, + metricsClient: metricsClient, domainIDToName: domainIDToName, enableReadFromCache: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicconfig.MatchingEnableGetNumberOfPartitionsFromCache), nReadPartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingNumTasklistReadPartitions), @@ -106,8 +111,15 @@ func (p *partitionConfigProviderImpl) GetNumberOfReadPartitions(domainID string, return 1 } c.RLock() - defer c.RUnlock() - return int(c.NumReadPartitions) + v := c.Version + w := c.NumWritePartitions + r := c.NumReadPartitions + c.RUnlock() + scope := p.metricsClient.Scope(metrics.PartitionConfigProviderScope, metrics.DomainTag(domainName), metrics.TaskListRootPartitionTag(taskList.GetName()), getTaskListTypeTag(taskListType)) + scope.UpdateGauge(metrics.TaskListPartitionConfigNumReadGauge, float64(r)) + scope.UpdateGauge(metrics.TaskListPartitionConfigNumWriteGauge, float64(w)) + scope.UpdateGauge(metrics.TaskListPartitionConfigVersionGauge, float64(v)) + return int(r) } func (p *partitionConfigProviderImpl) GetNumberOfWritePartitions(domainID string, taskList types.TaskList, taskListType int) int { @@ -133,6 +145,10 @@ func (p *partitionConfigProviderImpl) GetNumberOfWritePartitions(domainID string w := c.NumWritePartitions r := c.NumReadPartitions c.RUnlock() + scope := p.metricsClient.Scope(metrics.PartitionConfigProviderScope, metrics.DomainTag(domainName), metrics.TaskListRootPartitionTag(taskList.GetName()), getTaskListTypeTag(taskListType)) + scope.UpdateGauge(metrics.TaskListPartitionConfigNumReadGauge, float64(r)) + scope.UpdateGauge(metrics.TaskListPartitionConfigNumWriteGauge, float64(w)) + scope.UpdateGauge(metrics.TaskListPartitionConfigVersionGauge, float64(v)) if w > r { p.logger.Warn("Number of write partitions exceeds number of read partitions, using number of read partitions", tag.WorkflowDomainID(domainID), tag.WorkflowTaskListName(taskList.GetName()), tag.WorkflowTaskListType(taskListType), tag.Dynamic("read-partition", r), tag.Dynamic("write-partition", w), tag.Dynamic("config-version", v)) return int(r) @@ -188,3 +204,14 @@ func (p *partitionConfigProviderImpl) getPartitionConfig(domainID string, taskLi } return c } + +func getTaskListTypeTag(taskListType int) metrics.Tag { + switch taskListType { + case persistence.TaskListTypeActivity: + return metrics.TaskListTypeTag("activity") + case persistence.TaskListTypeDecision: + return metrics.TaskListTypeTag("decision") + default: + return metrics.TaskListTypeTag("") + } +} diff --git a/client/matching/partition_config_provider_test.go b/client/matching/partition_config_provider_test.go index 8aaf62e2b64..e8b8f650da7 100644 --- a/client/matching/partition_config_provider_test.go +++ b/client/matching/partition_config_provider_test.go @@ -34,6 +34,7 @@ import ( "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" + "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) @@ -65,6 +66,7 @@ func setUpMocksForPartitionConfigProvider(t *testing.T, enableReadFromCache bool return &partitionConfigProviderImpl{ configCache: mockCache, logger: logger, + metricsClient: metrics.NewNoopMetricsClient(), domainIDToName: domainIDToName, enableReadFromCache: dynamicconfig.GetBoolPropertyFilteredByTaskListInfo(enableReadFromCache), nReadPartitions: dynamicconfig.GetIntPropertyFilteredByTaskListInfo(3), @@ -78,7 +80,7 @@ func TestNewPartitionConfigProvider(t *testing.T) { domainIDToName := func(domainID string) (string, error) { return "test-domain", nil } - p := NewPartitionConfigProvider(logger, domainIDToName, dc) + p := NewPartitionConfigProvider(logger, metrics.NewNoopMetricsClient(), domainIDToName, dc) assert.NotNil(t, p) pImpl, ok := p.(*partitionConfigProviderImpl) assert.True(t, ok) diff --git a/client/wrappers/errorinjectors/matching_generated.go b/client/wrappers/errorinjectors/matching_generated.go index 9ca509b9e76..1f1e2436646 100644 --- a/client/wrappers/errorinjectors/matching_generated.go +++ b/client/wrappers/errorinjectors/matching_generated.go @@ -242,6 +242,26 @@ func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQ return } +func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { + fakeErr := c.fakeErrFn(c.errorRate) + var forwardCall bool + if forwardCall = c.forwardCallFn(fakeErr); forwardCall { + mp2, err = c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) + } + + if fakeErr != nil { + c.logger.Error(msgMatchingInjectedFakeErr, + tag.MatchingClientOperationRefreshTaskListPartitionConfig, + tag.Error(fakeErr), + tag.Bool(forwardCall), + tag.ClientError(err), + ) + err = fakeErr + return + } + return +} + func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool @@ -261,3 +281,23 @@ func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *typ } return } + +func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { + fakeErr := c.fakeErrFn(c.errorRate) + var forwardCall bool + if forwardCall = c.forwardCallFn(fakeErr); forwardCall { + mp2, err = c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) + } + + if fakeErr != nil { + c.logger.Error(msgMatchingInjectedFakeErr, + tag.MatchingClientOperationUpdateTaskListPartitionConfig, + tag.Error(fakeErr), + tag.Bool(forwardCall), + tag.ClientError(err), + ) + err = fakeErr + return + } + return +} diff --git a/client/wrappers/grpc/matching_generated.go b/client/wrappers/grpc/matching_generated.go index c940c071afc..c6b435c36c0 100644 --- a/client/wrappers/grpc/matching_generated.go +++ b/client/wrappers/grpc/matching_generated.go @@ -80,7 +80,17 @@ func (g matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQu return proto.ToMatchingQueryWorkflowResponse(response), proto.ToError(err) } +func (g matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { + response, err := g.c.RefreshTaskListPartitionConfig(ctx, proto.FromMatchingRefreshTaskListPartitionConfigRequest(mp1), p1...) + return proto.ToMatchingRefreshTaskListPartitionConfigResponse(response), proto.ToError(err) +} + func (g matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondQueryTaskCompleted(ctx, proto.FromMatchingRespondQueryTaskCompletedRequest(mp1), p1...) return proto.ToError(err) } + +func (g matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { + response, err := g.c.UpdateTaskListPartitionConfig(ctx, proto.FromMatchingUpdateTaskListPartitionConfigRequest(mp1), p1...) + return proto.ToMatchingUpdateTaskListPartitionConfigResponse(response), proto.ToError(err) +} diff --git a/client/wrappers/metered/matching_generated.go b/client/wrappers/metered/matching_generated.go index d191b7326d8..2a5a9dcee27 100644 --- a/client/wrappers/metered/matching_generated.go +++ b/client/wrappers/metered/matching_generated.go @@ -178,6 +178,20 @@ func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQ return qp1, err } +func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { + c.metricsClient.IncCounter(metrics.MatchingClientRefreshTaskListPartitionConfigScope, metrics.CadenceClientRequests) + c.emitForwardedFromStats(metrics.MatchingClientRefreshTaskListPartitionConfigScope, mp1) + + sw := c.metricsClient.StartTimer(metrics.MatchingClientRefreshTaskListPartitionConfigScope, metrics.CadenceClientLatency) + mp2, err = c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) + sw.Stop() + + if err != nil { + c.metricsClient.IncCounter(metrics.MatchingClientRefreshTaskListPartitionConfigScope, metrics.CadenceClientFailures) + } + return mp2, err +} + func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { c.metricsClient.IncCounter(metrics.MatchingClientRespondQueryTaskCompletedScope, metrics.CadenceClientRequests) c.emitForwardedFromStats(metrics.MatchingClientRespondQueryTaskCompletedScope, mp1) @@ -192,6 +206,20 @@ func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *typ return err } +func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { + c.metricsClient.IncCounter(metrics.MatchingClientUpdateTaskListPartitionConfigScope, metrics.CadenceClientRequests) + c.emitForwardedFromStats(metrics.MatchingClientUpdateTaskListPartitionConfigScope, mp1) + + sw := c.metricsClient.StartTimer(metrics.MatchingClientUpdateTaskListPartitionConfigScope, metrics.CadenceClientLatency) + mp2, err = c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) + sw.Stop() + + if err != nil { + c.metricsClient.IncCounter(metrics.MatchingClientUpdateTaskListPartitionConfigScope, metrics.CadenceClientFailures) + } + return mp2, err +} + type forwardedRequest interface { GetForwardedFrom() string GetTaskList() *types.TaskList diff --git a/client/wrappers/retryable/matching_generated.go b/client/wrappers/retryable/matching_generated.go index 656e6cbcd93..4a4b6ca66bf 100644 --- a/client/wrappers/retryable/matching_generated.go +++ b/client/wrappers/retryable/matching_generated.go @@ -148,9 +148,31 @@ func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQ return resp, err } +func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { + var resp *types.MatchingRefreshTaskListPartitionConfigResponse + op := func() error { + var err error + resp, err = c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) + return err + } + err = c.throttleRetry.Do(ctx, op) + return resp, err +} + func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { op := func() error { return c.client.RespondQueryTaskCompleted(ctx, mp1, p1...) } return c.throttleRetry.Do(ctx, op) } + +func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { + var resp *types.MatchingUpdateTaskListPartitionConfigResponse + op := func() error { + var err error + resp, err = c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) + return err + } + err = c.throttleRetry.Do(ctx, op) + return resp, err +} diff --git a/client/wrappers/thrift/matching_generated.go b/client/wrappers/thrift/matching_generated.go index 0b64f7874a0..40c0a6bfaf3 100644 --- a/client/wrappers/thrift/matching_generated.go +++ b/client/wrappers/thrift/matching_generated.go @@ -86,7 +86,15 @@ func (g matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQu return thrift.ToMatchingQueryWorkflowResponse(response), thrift.ToError(err) } +func (g matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { + return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) +} + func (g matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondQueryTaskCompleted(ctx, thrift.FromMatchingRespondQueryTaskCompletedRequest(mp1), p1...) return thrift.ToError(err) } + +func (g matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { + return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) +} diff --git a/client/wrappers/timeout/matching_generated.go b/client/wrappers/timeout/matching_generated.go index 16ed25dc0aa..3fc7db6c43a 100644 --- a/client/wrappers/timeout/matching_generated.go +++ b/client/wrappers/timeout/matching_generated.go @@ -110,8 +110,20 @@ func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQ return c.client.QueryWorkflow(ctx, mp1, p1...) } +func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { + ctx, cancel := createContext(ctx, c.timeout) + defer cancel() + return c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) +} + func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondQueryTaskCompleted(ctx, mp1, p1...) } + +func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { + ctx, cancel := createContext(ctx, c.timeout) + defer cancel() + return c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) +} diff --git a/common/log/tag/values.go b/common/log/tag/values.go index 31cf4d43926..0eef3b9e2b0 100644 --- a/common/log/tag/values.go +++ b/common/log/tag/values.go @@ -420,17 +420,19 @@ var ( HistoryClientOperationRespondDecisionTaskFailed = clientOperation("history-respond-decision-task-failed") HistoryClientOperationRatelimitUpdate = clientOperation("history-ratelimit-update") - MatchingClientOperationAddActivityTask = clientOperation("matching-add-activity-task") - MatchingClientOperationAddDecisionTask = clientOperation("matching-add-decision-task") - MatchingClientOperationPollForActivityTask = clientOperation("matching-poll-for-activity-task") - MatchingClientOperationPollForDecisionTask = clientOperation("matching-poll-for-decision-task") - MatchingClientOperationQueryWorkflow = clientOperation("matching-query-wf") - MatchingClientOperationQueryTaskCompleted = clientOperation("matching-query-task-completed") - MatchingClientOperationCancelOutstandingPoll = clientOperation("matching-cancel-outstanding-poll") - MatchingClientOperationDescribeTaskList = clientOperation("matching-describe-task-list") - MatchingClientOperationListTaskListPartitions = clientOperation("matching-list-task-list-partitions") - MatchingClientOperationGetTaskListsByDomain = clientOperation("matching-get-task-list-for-domain") - MatchingClientOperationRespondQueryTaskCompleted = clientOperation("matching-respond-query-task-completed") + MatchingClientOperationAddActivityTask = clientOperation("matching-add-activity-task") + MatchingClientOperationAddDecisionTask = clientOperation("matching-add-decision-task") + MatchingClientOperationPollForActivityTask = clientOperation("matching-poll-for-activity-task") + MatchingClientOperationPollForDecisionTask = clientOperation("matching-poll-for-decision-task") + MatchingClientOperationQueryWorkflow = clientOperation("matching-query-wf") + MatchingClientOperationQueryTaskCompleted = clientOperation("matching-query-task-completed") + MatchingClientOperationCancelOutstandingPoll = clientOperation("matching-cancel-outstanding-poll") + MatchingClientOperationDescribeTaskList = clientOperation("matching-describe-task-list") + MatchingClientOperationListTaskListPartitions = clientOperation("matching-list-task-list-partitions") + MatchingClientOperationGetTaskListsByDomain = clientOperation("matching-get-task-list-for-domain") + MatchingClientOperationRespondQueryTaskCompleted = clientOperation("matching-respond-query-task-completed") + MatchingClientOperationUpdateTaskListPartitionConfig = clientOperation("matching-update-task-list-partition-config") + MatchingClientOperationRefreshTaskListPartitionConfig = clientOperation("matching-refresh-task-list-partition-config") ) // Pre-defined values for TagIDType diff --git a/common/metrics/defs.go b/common/metrics/defs.go index 1924604ec84..59c44e45ccc 100644 --- a/common/metrics/defs.go +++ b/common/metrics/defs.go @@ -427,6 +427,11 @@ const ( MatchingClientListTaskListPartitionsScope // MatchingClientGetTaskListsByDomainScope tracks RPC calls to matching service MatchingClientGetTaskListsByDomainScope + // MatchingClientUpdateTaskListPartitionConfigScope tracks RPC calls to matching service + MatchingClientUpdateTaskListPartitionConfigScope + // MatchingClientRefreshTaskListPartitionConfigScope tracks RPC calls to matching service + MatchingClientRefreshTaskListPartitionConfigScope + // FrontendClientDeprecateDomainScope tracks RPC calls to frontend service FrontendClientDeprecateDomainScope // FrontendClientDescribeDomainScope tracks RPC calls to frontend service @@ -851,6 +856,9 @@ const ( // P2PRPCPeerChooserScope is the metrics scope for P2P RPC peer chooser P2PRPCPeerChooserScope + // PartitionConfigProviderScope is the metrics scope for Partition Config Provider + PartitionConfigProviderScope + NumCommonScopes ) @@ -924,6 +932,8 @@ const ( GetDomainAsyncWorkflowConfiguraton // UpdateDomainAsyncWorkflowConfiguraton is the scope for updating domain async workflow configuration UpdateDomainAsyncWorkflowConfiguraton + // UpdateTaskListPartitionConfig is the scope for update task list partition config + UpdateTaskListPartitionConfig NumAdminScopes ) @@ -1331,6 +1341,10 @@ const ( MatchingListTaskListPartitionsScope // MatchingGetTaskListsByDomainScope tracks GetTaskListsByDomain API calls received by service MatchingGetTaskListsByDomainScope + // MatchingUpdateTaskListPartitionConfigScope tracks UpdateTaskListPartitionConfig API calls received by service + MatchingUpdateTaskListPartitionConfigScope + // MatchingRefreshTaskListPartitionConfigScope tracks RefreshTaskListPartitionConfig API calls received by service + MatchingRefreshTaskListPartitionConfigScope NumMatchingScopes ) @@ -1533,16 +1547,19 @@ var ScopeDefs = map[ServiceIdx]map[int]scopeDefinition{ HistoryClientWfIDCacheScope: {operation: "HistoryClientWfIDCache", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRatelimitUpdateScope: {operation: "HistoryClientRatelimitUpdate", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, - MatchingClientPollForDecisionTaskScope: {operation: "MatchingClientPollForDecisionTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientPollForActivityTaskScope: {operation: "MatchingClientPollForActivityTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientAddActivityTaskScope: {operation: "MatchingClientAddActivityTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientAddDecisionTaskScope: {operation: "MatchingClientAddDecisionTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientQueryWorkflowScope: {operation: "MatchingClientQueryWorkflow", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientRespondQueryTaskCompletedScope: {operation: "MatchingClientRespondQueryTaskCompleted", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientCancelOutstandingPollScope: {operation: "MatchingClientCancelOutstandingPoll", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientDescribeTaskListScope: {operation: "MatchingClientDescribeTaskList", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientListTaskListPartitionsScope: {operation: "MatchingClientListTaskListPartitions", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, - MatchingClientGetTaskListsByDomainScope: {operation: "MatchingClientGetTaskListsByDomain", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientPollForDecisionTaskScope: {operation: "MatchingClientPollForDecisionTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientPollForActivityTaskScope: {operation: "MatchingClientPollForActivityTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientAddActivityTaskScope: {operation: "MatchingClientAddActivityTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientAddDecisionTaskScope: {operation: "MatchingClientAddDecisionTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientQueryWorkflowScope: {operation: "MatchingClientQueryWorkflow", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientRespondQueryTaskCompletedScope: {operation: "MatchingClientRespondQueryTaskCompleted", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientCancelOutstandingPollScope: {operation: "MatchingClientCancelOutstandingPoll", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientDescribeTaskListScope: {operation: "MatchingClientDescribeTaskList", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientListTaskListPartitionsScope: {operation: "MatchingClientListTaskListPartitions", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientGetTaskListsByDomainScope: {operation: "MatchingClientGetTaskListsByDomain", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientUpdateTaskListPartitionConfigScope: {operation: "MatchingClientUpdateTaskListPartitionConfig", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + MatchingClientRefreshTaskListPartitionConfigScope: {operation: "MatchingClientRefreshTaskListPartitionConfig", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, + FrontendClientDeprecateDomainScope: {operation: "FrontendClientDeprecateDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientDescribeDomainScope: {operation: "FrontendClientDescribeDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientDescribeTaskListScope: {operation: "FrontendClientDescribeTaskList", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, @@ -1748,7 +1765,8 @@ var ScopeDefs = map[ServiceIdx]map[int]scopeDefinition{ GlobalRatelimiter: {operation: "GlobalRatelimiter"}, GlobalRatelimiterAggregator: {operation: "GlobalRatelimiterAggregator"}, - P2PRPCPeerChooserScope: {operation: "P2PRPCPeerChooser"}, + P2PRPCPeerChooserScope: {operation: "P2PRPCPeerChooser"}, + PartitionConfigProviderScope: {operation: "PartitionConfigProvider"}, }, // Frontend Scope Names Frontend: { @@ -1787,6 +1805,7 @@ var ScopeDefs = map[ServiceIdx]map[int]scopeDefinition{ UpdateDomainIsolationGroups: {operation: "UpdateDomainIsolationGroups"}, GetDomainAsyncWorkflowConfiguraton: {operation: "GetDomainAsyncWorkflowConfiguraton"}, UpdateDomainAsyncWorkflowConfiguraton: {operation: "UpdateDomainAsyncWorkflowConfiguraton"}, + UpdateTaskListPartitionConfig: {operation: "UpdateTaskListPartitionConfig"}, FrontendRestartWorkflowExecutionScope: {operation: "RestartWorkflowExecution"}, FrontendStartWorkflowExecutionScope: {operation: "StartWorkflowExecution"}, @@ -1976,18 +1995,20 @@ var ScopeDefs = map[ServiceIdx]map[int]scopeDefinition{ }, // Matching Scope Names Matching: { - MatchingPollForDecisionTaskScope: {operation: "PollForDecisionTask"}, - MatchingPollForActivityTaskScope: {operation: "PollForActivityTask"}, - MatchingAddActivityTaskScope: {operation: "AddActivityTask"}, - MatchingAddDecisionTaskScope: {operation: "AddDecisionTask"}, - MatchingAddTaskScope: {operation: "AddTask"}, - MatchingTaskListMgrScope: {operation: "TaskListMgr"}, - MatchingQueryWorkflowScope: {operation: "QueryWorkflow"}, - MatchingRespondQueryTaskCompletedScope: {operation: "RespondQueryTaskCompleted"}, - MatchingCancelOutstandingPollScope: {operation: "CancelOutstandingPoll"}, - MatchingDescribeTaskListScope: {operation: "DescribeTaskList"}, - MatchingListTaskListPartitionsScope: {operation: "ListTaskListPartitions"}, - MatchingGetTaskListsByDomainScope: {operation: "GetTaskListsByDomain"}, + MatchingPollForDecisionTaskScope: {operation: "PollForDecisionTask"}, + MatchingPollForActivityTaskScope: {operation: "PollForActivityTask"}, + MatchingAddActivityTaskScope: {operation: "AddActivityTask"}, + MatchingAddDecisionTaskScope: {operation: "AddDecisionTask"}, + MatchingAddTaskScope: {operation: "AddTask"}, + MatchingTaskListMgrScope: {operation: "TaskListMgr"}, + MatchingQueryWorkflowScope: {operation: "QueryWorkflow"}, + MatchingRespondQueryTaskCompletedScope: {operation: "RespondQueryTaskCompleted"}, + MatchingCancelOutstandingPollScope: {operation: "CancelOutstandingPoll"}, + MatchingDescribeTaskListScope: {operation: "DescribeTaskList"}, + MatchingListTaskListPartitionsScope: {operation: "ListTaskListPartitions"}, + MatchingGetTaskListsByDomainScope: {operation: "GetTaskListsByDomain"}, + MatchingUpdateTaskListPartitionConfigScope: {operation: "UpdateTaskListPartitionConfig"}, + MatchingRefreshTaskListPartitionConfigScope: {operation: "RefreshTaskListPartitionConfig"}, }, // Worker Scope Names Worker: { @@ -2255,6 +2276,10 @@ const ( P2PPeersCount P2PPeerAdded P2PPeerRemoved + // task list partition config metrics + TaskListPartitionConfigVersionGauge + TaskListPartitionConfigNumReadGauge + TaskListPartitionConfigNumWriteGauge NumCommonMetrics // Needs to be last on this list for iota numbering ) @@ -2580,6 +2605,7 @@ const ( IsolationTaskMatchPerTaskListCounter PollerPerTaskListCounter PollerInvalidIsolationGroupCounter + TaskListPartitionUpdateFailedCounter TaskListManagersGauge TaskLagPerTaskListGauge TaskBacklogPerTaskListGauge @@ -2950,9 +2976,12 @@ var MetricDefs = map[ServiceIdx]map[int]metricDefinition{ GlobalRatelimiterRemovedLimits: {metricName: "global_ratelimiter_removed_limits", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterRemovedHostLimits: {metricName: "global_ratelimiter_removed_host_limits", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, - P2PPeersCount: {metricName: "peers_count", metricType: Gauge}, - P2PPeerAdded: {metricName: "peer_added", metricType: Counter}, - P2PPeerRemoved: {metricName: "peer_removed", metricType: Counter}, + P2PPeersCount: {metricName: "peers_count", metricType: Gauge}, + P2PPeerAdded: {metricName: "peer_added", metricType: Counter}, + P2PPeerRemoved: {metricName: "peer_removed", metricType: Counter}, + TaskListPartitionConfigVersionGauge: {metricName: "task_list_partition_config_version", metricType: Gauge}, + TaskListPartitionConfigNumReadGauge: {metricName: "task_list_partition_config_num_read", metricType: Gauge}, + TaskListPartitionConfigNumWriteGauge: {metricName: "task_list_partition_config_num_write", metricType: Gauge}, }, History: { TaskRequests: {metricName: "task_requests", metricType: Counter}, @@ -3266,6 +3295,7 @@ var MetricDefs = map[ServiceIdx]map[int]metricDefinition{ IsolationTaskMatchPerTaskListCounter: {metricName: "isolation_task_matches_per_tl", metricType: Counter}, PollerPerTaskListCounter: {metricName: "poller_count_per_tl", metricRollupName: "poller_count"}, PollerInvalidIsolationGroupCounter: {metricName: "poller_invalid_isolation_group_per_tl", metricType: Counter}, + TaskListPartitionUpdateFailedCounter: {metricName: "tasklist_partition_update_failed_per_tl", metricType: Counter}, TaskListManagersGauge: {metricName: "tasklist_managers", metricType: Gauge}, TaskLagPerTaskListGauge: {metricName: "task_lag_per_tl", metricType: Gauge}, TaskBacklogPerTaskListGauge: {metricName: "task_backlog_per_tl", metricType: Gauge}, diff --git a/common/metrics/tags.go b/common/metrics/tags.go index 8607b3bef89..3368a9cb518 100644 --- a/common/metrics/tags.go +++ b/common/metrics/tags.go @@ -43,6 +43,7 @@ const ( activeCluster = "active_cluster" taskList = "tasklist" taskListType = "tasklistType" + taskListRootPartition = "tasklist_root_partition" workflowType = "workflowType" activityType = "activityType" decisionType = "decisionType" @@ -175,6 +176,14 @@ func TaskListTypeTag(value string) Tag { return metricWithUnknown(taskListType, value) } +// TaskListRootPartition returns a new task list root partition tag. +func TaskListRootPartitionTag(value string) Tag { + if len(value) == 0 { + value = unknownValue + } + return simpleMetric{key: taskListRootPartition, value: sanitizer.Value(value)} +} + // WorkflowTypeTag returns a new workflow type tag. func WorkflowTypeTag(value string) Tag { return metricWithUnknown(workflowType, value) diff --git a/common/types/mapper/proto/matching.go b/common/types/mapper/proto/matching.go index 589797dc3fe..015b2729051 100644 --- a/common/types/mapper/proto/matching.go +++ b/common/types/mapper/proto/matching.go @@ -217,8 +217,9 @@ func FromMatchingDescribeTaskListResponse(t *types.DescribeTaskListResponse) *ma return nil } return &matchingv1.DescribeTaskListResponse{ - Pollers: FromPollerInfoArray(t.Pollers), - TaskListStatus: FromTaskListStatus(t.TaskListStatus), + Pollers: FromPollerInfoArray(t.Pollers), + TaskListStatus: FromTaskListStatus(t.TaskListStatus), + PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), } } @@ -227,8 +228,9 @@ func ToMatchingDescribeTaskListResponse(t *matchingv1.DescribeTaskListResponse) return nil } return &types.DescribeTaskListResponse{ - Pollers: ToPollerInfoArray(t.Pollers), - TaskListStatus: ToTaskListStatus(t.TaskListStatus), + Pollers: ToPollerInfoArray(t.Pollers), + TaskListStatus: ToTaskListStatus(t.TaskListStatus), + PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), } } @@ -579,3 +581,79 @@ func ToMatchingRespondQueryTaskCompletedRequest(t *matchingv1.RespondQueryTaskCo TaskID: t.TaskId, } } + +func FromMatchingUpdateTaskListPartitionConfigRequest(t *types.MatchingUpdateTaskListPartitionConfigRequest) *matchingv1.UpdateTaskListPartitionConfigRequest { + if t == nil { + return nil + } + return &matchingv1.UpdateTaskListPartitionConfigRequest{ + DomainId: t.DomainUUID, + TaskList: FromTaskList(t.TaskList), + TaskListType: FromTaskListType(t.TaskListType), + PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), + } +} + +func ToMatchingUpdateTaskListPartitionConfigRequest(t *matchingv1.UpdateTaskListPartitionConfigRequest) *types.MatchingUpdateTaskListPartitionConfigRequest { + if t == nil { + return nil + } + return &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: t.DomainId, + TaskList: ToTaskList(t.TaskList), + TaskListType: ToTaskListType(t.TaskListType), + PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), + } +} + +func FromMatchingRefreshTaskListPartitionConfigRequest(t *types.MatchingRefreshTaskListPartitionConfigRequest) *matchingv1.RefreshTaskListPartitionConfigRequest { + if t == nil { + return nil + } + return &matchingv1.RefreshTaskListPartitionConfigRequest{ + DomainId: t.DomainUUID, + TaskList: FromTaskList(t.TaskList), + TaskListType: FromTaskListType(t.TaskListType), + PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), + } +} + +func ToMatchingRefreshTaskListPartitionConfigRequest(t *matchingv1.RefreshTaskListPartitionConfigRequest) *types.MatchingRefreshTaskListPartitionConfigRequest { + if t == nil { + return nil + } + return &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: t.DomainId, + TaskList: ToTaskList(t.TaskList), + TaskListType: ToTaskListType(t.TaskListType), + PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), + } +} + +func FromMatchingUpdateTaskListPartitionConfigResponse(t *types.MatchingUpdateTaskListPartitionConfigResponse) *matchingv1.UpdateTaskListPartitionConfigResponse { + if t == nil { + return nil + } + return &matchingv1.UpdateTaskListPartitionConfigResponse{} +} + +func ToMatchingUpdateTaskListPartitionConfigResponse(t *matchingv1.UpdateTaskListPartitionConfigResponse) *types.MatchingUpdateTaskListPartitionConfigResponse { + if t == nil { + return nil + } + return &types.MatchingUpdateTaskListPartitionConfigResponse{} +} + +func FromMatchingRefreshTaskListPartitionConfigResponse(t *types.MatchingRefreshTaskListPartitionConfigResponse) *matchingv1.RefreshTaskListPartitionConfigResponse { + if t == nil { + return nil + } + return &matchingv1.RefreshTaskListPartitionConfigResponse{} +} + +func ToMatchingRefreshTaskListPartitionConfigResponse(t *matchingv1.RefreshTaskListPartitionConfigResponse) *types.MatchingRefreshTaskListPartitionConfigResponse { + if t == nil { + return nil + } + return &types.MatchingRefreshTaskListPartitionConfigResponse{} +} diff --git a/common/types/mapper/proto/matching_test.go b/common/types/mapper/proto/matching_test.go index 92f68547fd9..63bb9fcc155 100644 --- a/common/types/mapper/proto/matching_test.go +++ b/common/types/mapper/proto/matching_test.go @@ -148,3 +148,27 @@ func TestMatchingGetTaskListsByDomainResponse(t *testing.T) { assert.Equal(t, item, ToMatchingGetTaskListsByDomainResponse(FromMatchingGetTaskListsByDomainResponse(item))) } } + +func TestMatchingUpdateTaskListPartitionConfigRequest(t *testing.T) { + for _, item := range []*types.MatchingUpdateTaskListPartitionConfigRequest{nil, {}, &testdata.MatchingUpdateTaskListPartitionConfigRequest} { + assert.Equal(t, item, ToMatchingUpdateTaskListPartitionConfigRequest(FromMatchingUpdateTaskListPartitionConfigRequest(item))) + } +} + +func TestMatchingUpdateTaskListPartitionConfigResponse(t *testing.T) { + for _, item := range []*types.MatchingUpdateTaskListPartitionConfigResponse{nil, {}} { + assert.Equal(t, item, ToMatchingUpdateTaskListPartitionConfigResponse(FromMatchingUpdateTaskListPartitionConfigResponse(item))) + } +} + +func TestMatchingRefreshTaskListPartitionConfigRequest(t *testing.T) { + for _, item := range []*types.MatchingRefreshTaskListPartitionConfigRequest{nil, {}, &testdata.MatchingRefreshTaskListPartitionConfigRequest} { + assert.Equal(t, item, ToMatchingRefreshTaskListPartitionConfigRequest(FromMatchingRefreshTaskListPartitionConfigRequest(item))) + } +} + +func TestMatchingRefreshTaskListPartitionConfigResponse(t *testing.T) { + for _, item := range []*types.MatchingRefreshTaskListPartitionConfigResponse{nil, {}} { + assert.Equal(t, item, ToMatchingRefreshTaskListPartitionConfigResponse(FromMatchingRefreshTaskListPartitionConfigResponse(item))) + } +} diff --git a/common/types/matching.go b/common/types/matching.go index fa538686d4c..d82892a7db6 100644 --- a/common/types/matching.go +++ b/common/types/matching.go @@ -646,3 +646,35 @@ const ( // TaskSourceDbBacklog is an option for TaskSource TaskSourceDbBacklog ) + +type MatchingUpdateTaskListPartitionConfigRequest struct { + DomainUUID string + TaskList *TaskList + TaskListType *TaskListType + PartitionConfig *TaskListPartitionConfig +} + +func (v *MatchingUpdateTaskListPartitionConfigRequest) GetTaskListType() (o TaskListType) { + if v != nil && v.TaskListType != nil { + return *v.TaskListType + } + return +} + +type MatchingUpdateTaskListPartitionConfigResponse struct{} + +type MatchingRefreshTaskListPartitionConfigRequest struct { + DomainUUID string + TaskList *TaskList + TaskListType *TaskListType + PartitionConfig *TaskListPartitionConfig +} + +func (v *MatchingRefreshTaskListPartitionConfigRequest) GetTaskListType() (o TaskListType) { + if v != nil && v.TaskListType != nil { + return *v.TaskListType + } + return +} + +type MatchingRefreshTaskListPartitionConfigResponse struct{} diff --git a/common/types/matching_test.go b/common/types/matching_test.go index 9f2c3db2a97..f4711ad3768 100644 --- a/common/types/matching_test.go +++ b/common/types/matching_test.go @@ -1721,3 +1721,63 @@ func TestTaskSource_MarshalText(t *testing.T) { }) } } + +func TestMatchingUpdateTaskListPartitionConfigRequest_GetTaskListType(t *testing.T) { + tests := []struct { + name string + req *MatchingUpdateTaskListPartitionConfigRequest + want TaskListType + }{ + { + name: "nil request", + req: nil, + want: TaskListTypeDecision, + }, + { + name: "empty request", + req: &MatchingUpdateTaskListPartitionConfigRequest{}, + want: TaskListTypeDecision, + }, + { + name: "non empty request", + req: &MatchingUpdateTaskListPartitionConfigRequest{TaskListType: TaskListTypeActivity.Ptr()}, + want: TaskListTypeActivity, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.req.GetTaskListType() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestMatchingRefreshTaskListPartitionConfigRequest_GetTaskListType(t *testing.T) { + tests := []struct { + name string + req *MatchingRefreshTaskListPartitionConfigRequest + want TaskListType + }{ + { + name: "nil request", + req: nil, + want: TaskListTypeDecision, + }, + { + name: "empty request", + req: &MatchingRefreshTaskListPartitionConfigRequest{}, + want: TaskListTypeDecision, + }, + { + name: "non empty request", + req: &MatchingRefreshTaskListPartitionConfigRequest{TaskListType: TaskListTypeActivity.Ptr()}, + want: TaskListTypeActivity, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.req.GetTaskListType() + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/common/types/testdata/service_matching.go b/common/types/testdata/service_matching.go index db49bd79a7c..d721ae87195 100644 --- a/common/types/testdata/service_matching.go +++ b/common/types/testdata/service_matching.go @@ -174,4 +174,18 @@ var ( WorkflowType: &WorkflowType, WorkflowDomain: DomainName, } + + MatchingUpdateTaskListPartitionConfigRequest = types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: DomainID, + TaskList: &TaskList, + TaskListType: &TaskListType, + PartitionConfig: &TaskListPartitionConfig, + } + + MatchingRefreshTaskListPartitionConfigRequest = types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: DomainID, + TaskList: &TaskList, + TaskListType: &TaskListType, + PartitionConfig: &TaskListPartitionConfig, + } ) diff --git a/docs/scalable_tasklist.md b/docs/scalable_tasklist.md index 7059e911a23..35ea3f62aeb 100644 --- a/docs/scalable_tasklist.md +++ b/docs/scalable_tasklist.md @@ -20,6 +20,17 @@ The number of partitions of a tasklist is configured by 2 dynamicconfigs: 1. [matching.numTasklistReadPartitions](https://github.com/uber/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3350) 2. [matching.numTasklistWritePartitions](https://github.com/uber/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3344) +We're migrating this configuration from dynamicconfig to database. The following dynamicconfig is used to control where to read the number of partitions from: +- [matching.enableGetNumberOfPartitionsFromCache](https://github.com/uber/cadence/blob/v1.2.15-prerelease02/common/dynamicconfig/constants.go#L4008) +To update the number of partitions, use the following CLI command: +``` +cadence admin tasklist update-partition -h +``` +To get the number of partitions from database, use the following CLI command: +``` +cadence admin tasklist describe -h +``` + The tree-structure and forwarding mechanism is configured by these dynamicconfigs: 1. [matching.forwarderMaxChildrenPerNode](https://github.com/uber/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3374) diff --git a/proto/internal/uber/cadence/matching/v1/service.proto b/proto/internal/uber/cadence/matching/v1/service.proto index 482b756ae5e..020405ebcfa 100644 --- a/proto/internal/uber/cadence/matching/v1/service.proto +++ b/proto/internal/uber/cadence/matching/v1/service.proto @@ -83,6 +83,18 @@ service MatchingAPI { // GetTaskListsByDomain returns all tasklist for a given domain rpc GetTaskListsByDomain(GetTaskListsByDomainRequest) returns (GetTaskListsByDomainResponse); + + // UpdateTaskListPartitionConfig is called to update the partition config of a task list in the database + // and notify all partitions of the task list to update the cache of partition config of the task list + // his API is used by frontend service to forward update request initiated mostly from CLI tool to update + // the partition config of a task list and notify all partitions of the task list to update their cache of task list partition config. + rpc UpdateTaskListPartitionConfig(UpdateTaskListPartitionConfigRequest) returns (UpdateTaskListPartitionConfigResponse); + + // RefreshTaskListPartitionConfig is called to update the cache of partition config of a task list + // This API is mainly used by matching service to notify a task list partition to update its cache + // of task list partition config. It can also be used by frontend service to forward request initiated + // from admin CLI tool to sync the cache of task list partition config if something goes wrong. + rpc RefreshTaskListPartitionConfig(RefreshTaskListPartitionConfigRequest) returns (RefreshTaskListPartitionConfigResponse); } message TaskListPartitionConfig { @@ -231,6 +243,7 @@ message DescribeTaskListRequest { message DescribeTaskListResponse { repeated api.v1.PollerInfo pollers = 1; api.v1.TaskListStatus task_list_status = 2; + api.v1.TaskListPartitionConfig partition_config = 3; } message ListTaskListPartitionsRequest { @@ -251,3 +264,23 @@ message GetTaskListsByDomainResponse { map decision_task_list_map = 1; map activity_task_list_map = 2; } + +message UpdateTaskListPartitionConfigRequest { + string domain_id = 1; + api.v1.TaskList task_list = 2; + api.v1.TaskListType task_list_type = 3; + api.v1.TaskListPartitionConfig partition_config = 4; +} + +message UpdateTaskListPartitionConfigResponse {} + +message RefreshTaskListPartitionConfigRequest { + string domain_id = 1; + api.v1.TaskList task_list = 2; + api.v1.TaskListType task_list_type = 3; + api.v1.TaskListPartitionConfig partition_config = 4; +} + +message RefreshTaskListPartitionConfigResponse { + +} diff --git a/service/frontend/admin/handler.go b/service/frontend/admin/handler.go index 67d18434d16..d81c194cfd7 100644 --- a/service/frontend/admin/handler.go +++ b/service/frontend/admin/handler.go @@ -1751,7 +1751,7 @@ func (adh *adminHandlerImpl) UpdateDomainIsolationGroups(ctx context.Context, re func (adh *adminHandlerImpl) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest) (_ *types.GetDomainAsyncWorkflowConfiguratonResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() - scope, sw := adh.startRequestProfile(ctx, metrics.UpdateDomainAsyncWorkflowConfiguraton) + scope, sw := adh.startRequestProfile(ctx, metrics.GetDomainAsyncWorkflowConfiguraton) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) @@ -1778,7 +1778,47 @@ func (adh *adminHandlerImpl) UpdateDomainAsyncWorkflowConfiguraton(ctx context.C } func (adh *adminHandlerImpl) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest) (_ *types.UpdateTaskListPartitionConfigResponse, retError error) { - return nil, errors.New("not implemented") + defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() + scope, sw := adh.startRequestProfile(ctx, metrics.UpdateTaskListPartitionConfig) + defer sw.Stop() + if request == nil { + return nil, adh.error(validate.ErrRequestNotSet, scope) + } + domainID, err := adh.GetDomainCache().GetDomainID(request.Domain) + if err != nil { + return nil, adh.error(err, scope) + } + if request.TaskList == nil { + return nil, adh.error(validate.ErrTaskListNotSet, scope) + } + if request.TaskList.Kind == nil { + return nil, adh.error(&types.BadRequestError{Message: "Task list kind not set."}, scope) + } + if *request.TaskList.Kind != types.TaskListKindNormal { + return nil, adh.error(&types.BadRequestError{Message: "Only normal tasklist's partition config can be updated."}, scope) + } + if request.TaskListType == nil { + return nil, adh.error(&types.BadRequestError{Message: "Task list type not set."}, scope) + } + if request.PartitionConfig == nil { + return nil, adh.error(&types.BadRequestError{Message: "Task list partition config is not set in the request."}, scope) + } + if request.PartitionConfig.NumWritePartitions > request.PartitionConfig.NumReadPartitions { + return nil, adh.error(&types.BadRequestError{Message: "The number of write partitions cannot be larger than the number of read partitions."}, scope) + } + if request.PartitionConfig.NumWritePartitions <= 0 { + return nil, adh.error(&types.BadRequestError{Message: "The number of partitions must be larger than 0."}, scope) + } + _, err = adh.GetMatchingClient().UpdateTaskListPartitionConfig(ctx, &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: domainID, + TaskList: request.TaskList, + TaskListType: request.TaskListType, + PartitionConfig: request.PartitionConfig, + }) + if err != nil { + return nil, err + } + return &types.UpdateTaskListPartitionConfigResponse{}, nil } func convertFromDataBlob(blob *types.DataBlob) (interface{}, error) { diff --git a/service/frontend/admin/handler_test.go b/service/frontend/admin/handler_test.go index f58f0280640..3252161a59f 100644 --- a/service/frontend/admin/handler_test.go +++ b/service/frontend/admin/handler_test.go @@ -38,6 +38,7 @@ import ( "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" + "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/asyncworkflow/queueconfigapi" "github.com/uber/cadence/common/backoff" @@ -2738,3 +2739,218 @@ func TestListDynamicConfig(t *testing.T) { }) } } + +func TestUpdateTaskListPartitionConfig(t *testing.T) { + domainName := "domain-name" + domainID := "domain-id" + taskListName := "task-list" + kind := types.TaskListKindNormal + taskListType := types.TaskListTypeActivity + partitionConfig := &types.TaskListPartitionConfig{ + NumReadPartitions: 2, + NumWritePartitions: 1, + } + + testCases := []struct { + name string + req *types.UpdateTaskListPartitionConfigRequest + setupMocks func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) + expectError bool + expectedError string + }{ + { + name: "success", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, + TaskListType: &taskListType, + PartitionConfig: partitionConfig, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + mockMatchingClient.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: domainID, + TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, + TaskListType: &taskListType, + PartitionConfig: partitionConfig, + }).Return(nil, nil) + }, + expectError: false, + }, + { + name: "request not set", + req: nil, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + // no mocks needed as the function should exit early + }, + expectError: true, + expectedError: validate.ErrRequestNotSet.Error(), + }, + { + name: "task list not set", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + }, + expectError: true, + expectedError: validate.ErrTaskListNotSet.Error(), + }, + { + name: "task list kind not set", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{Name: taskListName}, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + }, + expectError: true, + expectedError: "Task list kind not set.", + }, + { + name: "invalid task list kind", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{Name: taskListName, Kind: types.TaskListKindSticky.Ptr()}, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + }, + expectError: true, + expectedError: "Only normal tasklist's partition config can be updated.", + }, + { + name: "task list type not set", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + }, + expectError: true, + expectedError: "Task list type not set.", + }, + { + name: "partition config not set", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, + TaskListType: &taskListType, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + }, + expectError: true, + expectedError: "Task list partition config is not set in the request.", + }, + { + name: "invalid partition config: write partitions > read partitions", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{ + Name: taskListName, + Kind: &kind, + }, + TaskListType: &taskListType, + PartitionConfig: &types.TaskListPartitionConfig{ + NumReadPartitions: 1, + NumWritePartitions: 2, + }, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + }, + expectError: true, + expectedError: "The number of write partitions cannot be larger than the number of read partitions.", + }, + { + name: "invalid partition config: write partitions <= 0", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{ + Name: taskListName, + Kind: &kind, + }, + TaskListType: &taskListType, + PartitionConfig: &types.TaskListPartitionConfig{ + NumReadPartitions: 2, + NumWritePartitions: 0, + }, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + }, + expectError: true, + expectedError: "The number of partitions must be larger than 0.", + }, + { + name: "domain cache error", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{ + Name: taskListName, + Kind: &kind, + }, + TaskListType: &taskListType, + PartitionConfig: partitionConfig, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return("", errors.New("domain cache error")) + }, + expectError: true, + expectedError: "domain cache error", + }, + { + name: "matching client error", + req: &types.UpdateTaskListPartitionConfigRequest{ + Domain: domainName, + TaskList: &types.TaskList{ + Name: taskListName, + Kind: &kind, + }, + TaskListType: &taskListType, + PartitionConfig: partitionConfig, + }, + setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { + mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) + mockMatchingClient.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: domainID, + TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, + TaskListType: &taskListType, + PartitionConfig: partitionConfig, + }).Return(nil, errors.New("matching client error")) + }, + expectError: true, + expectedError: "matching client error", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + mockDomainCache := cache.NewMockDomainCache(ctrl) + mockMatchingClient := matching.NewMockClient(ctrl) + tc.setupMocks(mockMatchingClient, mockDomainCache) + adh := adminHandlerImpl{ + Resource: &resource.Test{ + Logger: testlogger.New(t), + MetricsClient: metrics.NewNoopMetricsClient(), + DomainCache: mockDomainCache, + MatchingClient: mockMatchingClient, + }, + } + + resp, err := adh.UpdateTaskListPartitionConfig(context.Background(), tc.req) + + if tc.expectError { + assert.ErrorContains(t, err, tc.expectedError) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + } + }) + } +} diff --git a/service/matching/config/config.go b/service/matching/config/config.go index d50292fbc3e..06616746fe8 100644 --- a/service/matching/config/config.go +++ b/service/matching/config/config.go @@ -39,20 +39,21 @@ type ( ShutdownDrainDuration dynamicconfig.DurationPropertyFn // taskListManager configuration - RangeSize int64 - GetTasksBatchSize dynamicconfig.IntPropertyFnWithTaskListInfoFilters - UpdateAckInterval dynamicconfig.DurationPropertyFnWithTaskListInfoFilters - IdleTasklistCheckInterval dynamicconfig.DurationPropertyFnWithTaskListInfoFilters - MaxTasklistIdleTime dynamicconfig.DurationPropertyFnWithTaskListInfoFilters - NumTasklistWritePartitions dynamicconfig.IntPropertyFnWithTaskListInfoFilters - NumTasklistReadPartitions dynamicconfig.IntPropertyFnWithTaskListInfoFilters - ForwarderMaxOutstandingPolls dynamicconfig.IntPropertyFnWithTaskListInfoFilters - ForwarderMaxOutstandingTasks dynamicconfig.IntPropertyFnWithTaskListInfoFilters - ForwarderMaxRatePerSecond dynamicconfig.IntPropertyFnWithTaskListInfoFilters - ForwarderMaxChildrenPerNode dynamicconfig.IntPropertyFnWithTaskListInfoFilters - AsyncTaskDispatchTimeout dynamicconfig.DurationPropertyFnWithTaskListInfoFilters - LocalPollWaitTime dynamicconfig.DurationPropertyFnWithTaskListInfoFilters - LocalTaskWaitTime dynamicconfig.DurationPropertyFnWithTaskListInfoFilters + RangeSize int64 + GetTasksBatchSize dynamicconfig.IntPropertyFnWithTaskListInfoFilters + UpdateAckInterval dynamicconfig.DurationPropertyFnWithTaskListInfoFilters + IdleTasklistCheckInterval dynamicconfig.DurationPropertyFnWithTaskListInfoFilters + MaxTasklistIdleTime dynamicconfig.DurationPropertyFnWithTaskListInfoFilters + NumTasklistWritePartitions dynamicconfig.IntPropertyFnWithTaskListInfoFilters + NumTasklistReadPartitions dynamicconfig.IntPropertyFnWithTaskListInfoFilters + ForwarderMaxOutstandingPolls dynamicconfig.IntPropertyFnWithTaskListInfoFilters + ForwarderMaxOutstandingTasks dynamicconfig.IntPropertyFnWithTaskListInfoFilters + ForwarderMaxRatePerSecond dynamicconfig.IntPropertyFnWithTaskListInfoFilters + ForwarderMaxChildrenPerNode dynamicconfig.IntPropertyFnWithTaskListInfoFilters + AsyncTaskDispatchTimeout dynamicconfig.DurationPropertyFnWithTaskListInfoFilters + LocalPollWaitTime dynamicconfig.DurationPropertyFnWithTaskListInfoFilters + LocalTaskWaitTime dynamicconfig.DurationPropertyFnWithTaskListInfoFilters + EnableGetNumberOfPartitionsFromCache dynamicconfig.BoolPropertyFnWithTaskListInfoFilters // Time to hold a poll request before returning an empty response if there are no tasks LongPollExpirationInterval dynamicconfig.DurationPropertyFnWithTaskListInfoFilters @@ -109,10 +110,11 @@ type ( LocalPollWaitTime func() time.Duration LocalTaskWaitTime func() time.Duration // taskWriter configuration - OutstandingTaskAppendsThreshold func() int - MaxTaskBatchSize func() int - NumWritePartitions func() int - NumReadPartitions func() int + OutstandingTaskAppendsThreshold func() int + MaxTaskBatchSize func() int + NumWritePartitions func() int + NumReadPartitions func() int + EnableGetNumberOfPartitionsFromCache func() bool // isolation configuration EnableTasklistIsolation func() bool // A function which returns all the isolation groups @@ -130,43 +132,44 @@ type ( // NewConfig returns new service config with default values func NewConfig(dc *dynamicconfig.Collection, hostName string, getIsolationGroups func() []string) *Config { return &Config{ - PersistenceMaxQPS: dc.GetIntProperty(dynamicconfig.MatchingPersistenceMaxQPS), - PersistenceGlobalMaxQPS: dc.GetIntProperty(dynamicconfig.MatchingPersistenceGlobalMaxQPS), - EnableSyncMatch: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicconfig.MatchingEnableSyncMatch), - UserRPS: dc.GetIntProperty(dynamicconfig.MatchingUserRPS), - WorkerRPS: dc.GetIntProperty(dynamicconfig.MatchingWorkerRPS), - DomainUserRPS: dc.GetIntPropertyFilteredByDomain(dynamicconfig.MatchingDomainUserRPS), - DomainWorkerRPS: dc.GetIntPropertyFilteredByDomain(dynamicconfig.MatchingDomainWorkerRPS), - RangeSize: 100000, - GetTasksBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingGetTasksBatchSize), - UpdateAckInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MatchingUpdateAckInterval), - IdleTasklistCheckInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MatchingIdleTasklistCheckInterval), - MaxTasklistIdleTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MaxTasklistIdleTime), - LongPollExpirationInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MatchingLongPollExpirationInterval), - MinTaskThrottlingBurstSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingMinTaskThrottlingBurstSize), - MaxTaskDeleteBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingMaxTaskDeleteBatchSize), - OutstandingTaskAppendsThreshold: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingOutstandingTaskAppendsThreshold), - MaxTaskBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingMaxTaskBatchSize), - ThrottledLogRPS: dc.GetIntProperty(dynamicconfig.MatchingThrottledLogRPS), - NumTasklistWritePartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingNumTasklistWritePartitions), - NumTasklistReadPartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingNumTasklistReadPartitions), - ForwarderMaxOutstandingPolls: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxOutstandingPolls), - ForwarderMaxOutstandingTasks: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxOutstandingTasks), - ForwarderMaxRatePerSecond: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxRatePerSecond), - ForwarderMaxChildrenPerNode: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxChildrenPerNode), - ShutdownDrainDuration: dc.GetDurationProperty(dynamicconfig.MatchingShutdownDrainDuration), - EnableDebugMode: dc.GetBoolProperty(dynamicconfig.EnableDebugMode)(), - EnableTaskInfoLogByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicconfig.MatchingEnableTaskInfoLogByDomainID), - ActivityTaskSyncMatchWaitTime: dc.GetDurationPropertyFilteredByDomain(dynamicconfig.MatchingActivityTaskSyncMatchWaitTime), - EnableTasklistIsolation: dc.GetBoolPropertyFilteredByDomain(dynamicconfig.EnableTasklistIsolation), - AsyncTaskDispatchTimeout: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.AsyncTaskDispatchTimeout), - EnableTasklistOwnershipGuard: dc.GetBoolProperty(dynamicconfig.MatchingEnableTasklistGuardAgainstOwnershipShardLoss), - LocalPollWaitTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.LocalPollWaitTime), - LocalTaskWaitTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.LocalTaskWaitTime), - HostName: hostName, - TaskDispatchRPS: 100000.0, - TaskDispatchRPSTTL: time.Minute, - MaxTimeBetweenTaskDeletes: time.Second, - AllIsolationGroups: getIsolationGroups, + PersistenceMaxQPS: dc.GetIntProperty(dynamicconfig.MatchingPersistenceMaxQPS), + PersistenceGlobalMaxQPS: dc.GetIntProperty(dynamicconfig.MatchingPersistenceGlobalMaxQPS), + EnableSyncMatch: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicconfig.MatchingEnableSyncMatch), + UserRPS: dc.GetIntProperty(dynamicconfig.MatchingUserRPS), + WorkerRPS: dc.GetIntProperty(dynamicconfig.MatchingWorkerRPS), + DomainUserRPS: dc.GetIntPropertyFilteredByDomain(dynamicconfig.MatchingDomainUserRPS), + DomainWorkerRPS: dc.GetIntPropertyFilteredByDomain(dynamicconfig.MatchingDomainWorkerRPS), + RangeSize: 100000, + GetTasksBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingGetTasksBatchSize), + UpdateAckInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MatchingUpdateAckInterval), + IdleTasklistCheckInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MatchingIdleTasklistCheckInterval), + MaxTasklistIdleTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MaxTasklistIdleTime), + LongPollExpirationInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.MatchingLongPollExpirationInterval), + MinTaskThrottlingBurstSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingMinTaskThrottlingBurstSize), + MaxTaskDeleteBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingMaxTaskDeleteBatchSize), + OutstandingTaskAppendsThreshold: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingOutstandingTaskAppendsThreshold), + MaxTaskBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingMaxTaskBatchSize), + ThrottledLogRPS: dc.GetIntProperty(dynamicconfig.MatchingThrottledLogRPS), + NumTasklistWritePartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingNumTasklistWritePartitions), + NumTasklistReadPartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingNumTasklistReadPartitions), + ForwarderMaxOutstandingPolls: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxOutstandingPolls), + ForwarderMaxOutstandingTasks: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxOutstandingTasks), + ForwarderMaxRatePerSecond: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxRatePerSecond), + ForwarderMaxChildrenPerNode: dc.GetIntPropertyFilteredByTaskListInfo(dynamicconfig.MatchingForwarderMaxChildrenPerNode), + EnableGetNumberOfPartitionsFromCache: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicconfig.MatchingEnableGetNumberOfPartitionsFromCache), + ShutdownDrainDuration: dc.GetDurationProperty(dynamicconfig.MatchingShutdownDrainDuration), + EnableDebugMode: dc.GetBoolProperty(dynamicconfig.EnableDebugMode)(), + EnableTaskInfoLogByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicconfig.MatchingEnableTaskInfoLogByDomainID), + ActivityTaskSyncMatchWaitTime: dc.GetDurationPropertyFilteredByDomain(dynamicconfig.MatchingActivityTaskSyncMatchWaitTime), + EnableTasklistIsolation: dc.GetBoolPropertyFilteredByDomain(dynamicconfig.EnableTasklistIsolation), + AsyncTaskDispatchTimeout: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.AsyncTaskDispatchTimeout), + EnableTasklistOwnershipGuard: dc.GetBoolProperty(dynamicconfig.MatchingEnableTasklistGuardAgainstOwnershipShardLoss), + LocalPollWaitTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.LocalPollWaitTime), + LocalTaskWaitTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicconfig.LocalTaskWaitTime), + HostName: hostName, + TaskDispatchRPS: 100000.0, + TaskDispatchRPSTTL: time.Minute, + MaxTimeBetweenTaskDeletes: time.Second, + AllIsolationGroups: getIsolationGroups, } } diff --git a/service/matching/config/config_test.go b/service/matching/config/config_test.go index 4434779e1df..51df19b4a6a 100644 --- a/service/matching/config/config_test.go +++ b/service/matching/config/config_test.go @@ -42,44 +42,45 @@ type configTestCase struct { func TestNewConfig(t *testing.T) { hostname := "hostname" fields := map[string]configTestCase{ - "PersistenceMaxQPS": {dynamicconfig.MatchingPersistenceMaxQPS, 1}, - "PersistenceGlobalMaxQPS": {dynamicconfig.MatchingPersistenceGlobalMaxQPS, 2}, - "EnableSyncMatch": {dynamicconfig.MatchingEnableSyncMatch, true}, - "UserRPS": {dynamicconfig.MatchingUserRPS, 3}, - "WorkerRPS": {dynamicconfig.MatchingWorkerRPS, 4}, - "DomainUserRPS": {dynamicconfig.MatchingDomainUserRPS, 5}, - "DomainWorkerRPS": {dynamicconfig.MatchingDomainWorkerRPS, 6}, - "RangeSize": {nil, int64(100000)}, - "GetTasksBatchSize": {dynamicconfig.MatchingGetTasksBatchSize, 7}, - "UpdateAckInterval": {dynamicconfig.MatchingUpdateAckInterval, time.Duration(8)}, - "IdleTasklistCheckInterval": {dynamicconfig.MatchingIdleTasklistCheckInterval, time.Duration(9)}, - "MaxTasklistIdleTime": {dynamicconfig.MaxTasklistIdleTime, time.Duration(10)}, - "LongPollExpirationInterval": {dynamicconfig.MatchingLongPollExpirationInterval, time.Duration(11)}, - "MinTaskThrottlingBurstSize": {dynamicconfig.MatchingMinTaskThrottlingBurstSize, 12}, - "MaxTaskDeleteBatchSize": {dynamicconfig.MatchingMaxTaskDeleteBatchSize, 13}, - "OutstandingTaskAppendsThreshold": {dynamicconfig.MatchingOutstandingTaskAppendsThreshold, 14}, - "MaxTaskBatchSize": {dynamicconfig.MatchingMaxTaskBatchSize, 15}, - "ThrottledLogRPS": {dynamicconfig.MatchingThrottledLogRPS, 16}, - "NumTasklistWritePartitions": {dynamicconfig.MatchingNumTasklistWritePartitions, 17}, - "NumTasklistReadPartitions": {dynamicconfig.MatchingNumTasklistReadPartitions, 18}, - "ForwarderMaxOutstandingPolls": {dynamicconfig.MatchingForwarderMaxOutstandingPolls, 19}, - "ForwarderMaxOutstandingTasks": {dynamicconfig.MatchingForwarderMaxOutstandingTasks, 20}, - "ForwarderMaxRatePerSecond": {dynamicconfig.MatchingForwarderMaxRatePerSecond, 21}, - "ForwarderMaxChildrenPerNode": {dynamicconfig.MatchingForwarderMaxChildrenPerNode, 22}, - "ShutdownDrainDuration": {dynamicconfig.MatchingShutdownDrainDuration, time.Duration(23)}, - "EnableDebugMode": {dynamicconfig.EnableDebugMode, false}, - "EnableTaskInfoLogByDomainID": {dynamicconfig.MatchingEnableTaskInfoLogByDomainID, true}, - "ActivityTaskSyncMatchWaitTime": {dynamicconfig.MatchingActivityTaskSyncMatchWaitTime, time.Duration(24)}, - "EnableTasklistIsolation": {dynamicconfig.EnableTasklistIsolation, false}, - "AsyncTaskDispatchTimeout": {dynamicconfig.AsyncTaskDispatchTimeout, time.Duration(25)}, - "LocalPollWaitTime": {dynamicconfig.LocalPollWaitTime, time.Duration(10)}, - "LocalTaskWaitTime": {dynamicconfig.LocalTaskWaitTime, time.Duration(10)}, - "HostName": {nil, hostname}, - "TaskDispatchRPS": {nil, 100000.0}, - "TaskDispatchRPSTTL": {nil, time.Minute}, - "MaxTimeBetweenTaskDeletes": {nil, time.Second}, - "AllIsolationGroups": {nil, []string{"zone-1", "zone-2"}}, - "EnableTasklistOwnershipGuard": {dynamicconfig.MatchingEnableTasklistGuardAgainstOwnershipShardLoss, false}, + "PersistenceMaxQPS": {dynamicconfig.MatchingPersistenceMaxQPS, 1}, + "PersistenceGlobalMaxQPS": {dynamicconfig.MatchingPersistenceGlobalMaxQPS, 2}, + "EnableSyncMatch": {dynamicconfig.MatchingEnableSyncMatch, true}, + "UserRPS": {dynamicconfig.MatchingUserRPS, 3}, + "WorkerRPS": {dynamicconfig.MatchingWorkerRPS, 4}, + "DomainUserRPS": {dynamicconfig.MatchingDomainUserRPS, 5}, + "DomainWorkerRPS": {dynamicconfig.MatchingDomainWorkerRPS, 6}, + "RangeSize": {nil, int64(100000)}, + "GetTasksBatchSize": {dynamicconfig.MatchingGetTasksBatchSize, 7}, + "UpdateAckInterval": {dynamicconfig.MatchingUpdateAckInterval, time.Duration(8)}, + "IdleTasklistCheckInterval": {dynamicconfig.MatchingIdleTasklistCheckInterval, time.Duration(9)}, + "MaxTasklistIdleTime": {dynamicconfig.MaxTasklistIdleTime, time.Duration(10)}, + "LongPollExpirationInterval": {dynamicconfig.MatchingLongPollExpirationInterval, time.Duration(11)}, + "MinTaskThrottlingBurstSize": {dynamicconfig.MatchingMinTaskThrottlingBurstSize, 12}, + "MaxTaskDeleteBatchSize": {dynamicconfig.MatchingMaxTaskDeleteBatchSize, 13}, + "OutstandingTaskAppendsThreshold": {dynamicconfig.MatchingOutstandingTaskAppendsThreshold, 14}, + "MaxTaskBatchSize": {dynamicconfig.MatchingMaxTaskBatchSize, 15}, + "ThrottledLogRPS": {dynamicconfig.MatchingThrottledLogRPS, 16}, + "NumTasklistWritePartitions": {dynamicconfig.MatchingNumTasklistWritePartitions, 17}, + "NumTasklistReadPartitions": {dynamicconfig.MatchingNumTasklistReadPartitions, 18}, + "ForwarderMaxOutstandingPolls": {dynamicconfig.MatchingForwarderMaxOutstandingPolls, 19}, + "ForwarderMaxOutstandingTasks": {dynamicconfig.MatchingForwarderMaxOutstandingTasks, 20}, + "ForwarderMaxRatePerSecond": {dynamicconfig.MatchingForwarderMaxRatePerSecond, 21}, + "ForwarderMaxChildrenPerNode": {dynamicconfig.MatchingForwarderMaxChildrenPerNode, 22}, + "ShutdownDrainDuration": {dynamicconfig.MatchingShutdownDrainDuration, time.Duration(23)}, + "EnableDebugMode": {dynamicconfig.EnableDebugMode, false}, + "EnableTaskInfoLogByDomainID": {dynamicconfig.MatchingEnableTaskInfoLogByDomainID, true}, + "ActivityTaskSyncMatchWaitTime": {dynamicconfig.MatchingActivityTaskSyncMatchWaitTime, time.Duration(24)}, + "EnableTasklistIsolation": {dynamicconfig.EnableTasklistIsolation, false}, + "AsyncTaskDispatchTimeout": {dynamicconfig.AsyncTaskDispatchTimeout, time.Duration(25)}, + "LocalPollWaitTime": {dynamicconfig.LocalPollWaitTime, time.Duration(10)}, + "LocalTaskWaitTime": {dynamicconfig.LocalTaskWaitTime, time.Duration(10)}, + "HostName": {nil, hostname}, + "TaskDispatchRPS": {nil, 100000.0}, + "TaskDispatchRPSTTL": {nil, time.Minute}, + "MaxTimeBetweenTaskDeletes": {nil, time.Second}, + "AllIsolationGroups": {nil, []string{"zone-1", "zone-2"}}, + "EnableTasklistOwnershipGuard": {dynamicconfig.MatchingEnableTasklistGuardAgainstOwnershipShardLoss, false}, + "EnableGetNumberOfPartitionsFromCache": {dynamicconfig.MatchingEnableGetNumberOfPartitionsFromCache, false}, } client := dynamicconfig.NewInMemoryClient() for fieldName, expected := range fields { diff --git a/service/matching/handler/engine.go b/service/matching/handler/engine.go index caab0347452..94e9080c4ce 100644 --- a/service/matching/handler/engine.go +++ b/service/matching/handler/engine.go @@ -1011,6 +1011,85 @@ func (e *matchingEngineImpl) GetTaskListsByDomain( return e.getTaskListByDomainLocked(domainID), nil } +func (e *matchingEngineImpl) UpdateTaskListPartitionConfig( + hCtx *handlerContext, + request *types.MatchingUpdateTaskListPartitionConfigRequest, +) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { + domainID := request.DomainUUID + taskListName := request.TaskList.GetName() + taskListKind := request.TaskList.GetKind() + taskListType := persistence.TaskListTypeDecision + if request.GetTaskListType() == types.TaskListTypeActivity { + taskListType = persistence.TaskListTypeActivity + } + if taskListKind != types.TaskListKindNormal { + return nil, &types.BadRequestError{Message: "Only normal tasklist's partition config can be updated."} + } + if request.PartitionConfig == nil { + return nil, &types.BadRequestError{Message: "Task list partition config is not set in the request."} + } + if request.PartitionConfig.NumWritePartitions > request.PartitionConfig.NumReadPartitions { + return nil, &types.BadRequestError{Message: "The number of write partitions cannot be larger than the number of read partitions."} + } + if request.PartitionConfig.NumWritePartitions <= 0 { + return nil, &types.BadRequestError{Message: "The number of partitions must be larger than 0."} + } + taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) + if err != nil { + return nil, err + } + if !taskListID.IsRoot() { + return nil, &types.BadRequestError{Message: "Only root partition's partition config can be updated."} + } + tlMgr, err := e.getTaskListManager(taskListID, &taskListKind) + if err != nil { + return nil, err + } + err = tlMgr.UpdateTaskListPartitionConfig(hCtx.Context, request.PartitionConfig) + if err != nil { + return nil, err + } + return &types.MatchingUpdateTaskListPartitionConfigResponse{}, nil +} + +func (e *matchingEngineImpl) RefreshTaskListPartitionConfig( + hCtx *handlerContext, + request *types.MatchingRefreshTaskListPartitionConfigRequest, +) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { + domainID := request.DomainUUID + taskListName := request.TaskList.GetName() + taskListKind := request.TaskList.GetKind() + taskListType := persistence.TaskListTypeDecision + if request.GetTaskListType() == types.TaskListTypeActivity { + taskListType = persistence.TaskListTypeActivity + } + if taskListKind != types.TaskListKindNormal { + return nil, &types.BadRequestError{Message: "Only normal tasklist's partition config can be updated."} + } + if request.PartitionConfig != nil && request.PartitionConfig.NumWritePartitions > request.PartitionConfig.NumReadPartitions { + return nil, &types.BadRequestError{Message: "The number of write partitions cannot be larger than the number of read partitions."} + } + if request.PartitionConfig != nil && request.PartitionConfig.NumWritePartitions <= 0 { + return nil, &types.BadRequestError{Message: "The number of partitions must be larger than 0."} + } + taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) + if err != nil { + return nil, err + } + if taskListID.IsRoot() && request.PartitionConfig != nil { + return nil, &types.BadRequestError{Message: "PartitionConfig must be nil for root partition."} + } + tlMgr, err := e.getTaskListManager(taskListID, &taskListKind) + if err != nil { + return nil, err + } + err = tlMgr.RefreshTaskListPartitionConfig(hCtx.Context, request.PartitionConfig) + if err != nil { + return nil, err + } + return &types.MatchingRefreshTaskListPartitionConfigResponse{}, nil +} + func (e *matchingEngineImpl) getHostInfo(partitionKey string) (string, error) { host, err := e.membershipResolver.Lookup(service.Matching, partitionKey) if err != nil { diff --git a/service/matching/handler/engine_test.go b/service/matching/handler/engine_test.go index 6e0437671d3..0f3baa5647e 100644 --- a/service/matching/handler/engine_test.go +++ b/service/matching/handler/engine_test.go @@ -725,3 +725,409 @@ func TestShutDownTasklistsNotOwned(t *testing.T) { assert.NoError(t, err) } + +func TestUpdateTaskListPartitionConfig(t *testing.T) { + testCases := []struct { + name string + req *types.MatchingUpdateTaskListPartitionConfigRequest + hCtx *handlerContext + mockSetup func(*tasklist.MockManager) + expectError bool + expectedError string + }{ + { + name: "success", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }).Return(nil) + }, + expectError: false, + }, + { + name: "tasklist manager error", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }).Return(errors.New("tasklist manager error")) + }, + expectError: true, + expectedError: "tasklist manager error", + }, + { + name: "non root partition error", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "/__cadence_sys/test-tasklist/1", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "Only root partition's partition config can be updated.", + }, + { + name: "invalid tasklist name", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "/__cadence_sys/test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "invalid partitioned task list name /__cadence_sys/test-tasklist", + }, + { + name: "invalid partition config", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 3, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "The number of write partitions cannot be larger than the number of read partitions.", + }, + { + name: "invalid partition config - 2", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: -1, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "The number of partitions must be larger than 0.", + }, + { + name: "nil partition config", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "Task list partition config is not set in the request.", + }, + { + name: "invalid tasklist kind", + req: &types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + Kind: types.TaskListKindSticky.Ptr(), + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "Only normal tasklist's partition config can be updated.", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockManager := tasklist.NewMockManager(mockCtrl) + tc.mockSetup(mockManager) + tasklistID, err := tasklist.NewIdentifier("test-domain-id", "test-tasklist", 1) + require.NoError(t, err) + engine := &matchingEngineImpl{ + taskLists: map[tasklist.Identifier]tasklist.Manager{ + *tasklistID: mockManager, + }, + timeSource: clock.NewRealTimeSource(), + } + _, err = engine.UpdateTaskListPartitionConfig(tc.hCtx, tc.req) + if tc.expectError { + assert.ErrorContains(t, err, tc.expectedError) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestRefreshTaskListPartitionConfig(t *testing.T) { + testCases := []struct { + name string + req *types.MatchingRefreshTaskListPartitionConfigRequest + hCtx *handlerContext + mockSetup func(*tasklist.MockManager) + expectError bool + expectedError string + }{ + { + name: "success", + req: &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "/__cadence_sys/test-tasklist/1", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + mockManager.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }).Return(nil) + }, + expectError: false, + }, + { + name: "tasklist manager error", + req: &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "/__cadence_sys/test-tasklist/1", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + mockManager.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }).Return(errors.New("tasklist manager error")) + }, + expectError: true, + expectedError: "tasklist manager error", + }, + { + name: "invalid tasklist name", + req: &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "/__cadence_sys/test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "invalid partitioned task list name /__cadence_sys/test-tasklist", + }, + { + name: "invalid partition config", + req: &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 3, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "The number of write partitions cannot be larger than the number of read partitions.", + }, + { + name: "invalid partition config - 2", + req: &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: -1, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "The number of partitions must be larger than 0.", + }, + { + name: "invalid tasklist kind", + req: &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + Kind: types.TaskListKindSticky.Ptr(), + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "Only normal tasklist's partition config can be updated.", + }, + { + name: "invalid request for root partition", + req: &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{ + Name: "test-tasklist", + }, + TaskListType: types.TaskListTypeActivity.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + hCtx: &handlerContext{ + Context: context.Background(), + }, + mockSetup: func(mockManager *tasklist.MockManager) { + }, + expectError: true, + expectedError: "PartitionConfig must be nil for root partition.", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockManager := tasklist.NewMockManager(mockCtrl) + tc.mockSetup(mockManager) + tasklistID, err := tasklist.NewIdentifier("test-domain-id", "test-tasklist", 1) + require.NoError(t, err) + tasklistID2, err := tasklist.NewIdentifier("test-domain-id", "/__cadence_sys/test-tasklist/1", 1) + require.NoError(t, err) + engine := &matchingEngineImpl{ + taskLists: map[tasklist.Identifier]tasklist.Manager{ + *tasklistID: mockManager, + *tasklistID2: mockManager, + }, + timeSource: clock.NewRealTimeSource(), + } + _, err = engine.RefreshTaskListPartitionConfig(tc.hCtx, tc.req) + if tc.expectError { + assert.ErrorContains(t, err, tc.expectedError) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/service/matching/handler/handler.go b/service/matching/handler/handler.go index b0766e3a07b..a746b37b582 100644 --- a/service/matching/handler/handler.go +++ b/service/matching/handler/handler.go @@ -415,6 +415,55 @@ func (h *handlerImpl) GetTaskListsByDomain( return response, hCtx.handleErr(err) } +func (h *handlerImpl) UpdateTaskListPartitionConfig( + ctx context.Context, + request *types.MatchingUpdateTaskListPartitionConfigRequest, +) (resp *types.MatchingUpdateTaskListPartitionConfigResponse, retError error) { + defer func() { log.CapturePanic(recover(), h.logger, &retError) }() + + domainName := h.domainName(request.DomainUUID) + hCtx := h.newHandlerContext( + ctx, + domainName, + request.TaskList, + metrics.MatchingUpdateTaskListPartitionConfigScope, + ) + + sw := hCtx.startProfiling(&h.startWG) + defer sw.Stop() + + if ok := h.userRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { + return nil, hCtx.handleErr(errMatchingHostThrottle) + } + + response, err := h.engine.UpdateTaskListPartitionConfig(hCtx, request) + return response, hCtx.handleErr(err) +} + +func (h *handlerImpl) RefreshTaskListPartitionConfig( + ctx context.Context, + request *types.MatchingRefreshTaskListPartitionConfigRequest, +) (resp *types.MatchingRefreshTaskListPartitionConfigResponse, retError error) { + defer func() { log.CapturePanic(recover(), h.logger, &retError) }() + + domainName := h.domainName(request.DomainUUID) + hCtx := h.newHandlerContext( + ctx, + domainName, + request.TaskList, + metrics.MatchingRefreshTaskListPartitionConfigScope, + ) + + sw := hCtx.startProfiling(&h.startWG) + defer sw.Stop() + + // Count the request in the RPS, but we still accept it even if RPS is exceeded + h.userRateLimiter.Allow(quotas.Info{Domain: domainName}) + + response, err := h.engine.RefreshTaskListPartitionConfig(hCtx, request) + return response, hCtx.handleErr(err) +} + func (h *handlerImpl) domainName(id string) string { domainName, err := h.domainCache.GetDomainName(id) if err != nil { diff --git a/service/matching/handler/handler_test.go b/service/matching/handler/handler_test.go index 642eecd0383..7da2054f479 100644 --- a/service/matching/handler/handler_test.go +++ b/service/matching/handler/handler_test.go @@ -788,3 +788,109 @@ func (s *handlerSuite) TestDomainName() { } } +func (s *handlerSuite) TestRefreshTaskListPartitionConfig() { + request := types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{Name: "test-task-list"}, + } + + testCases := []struct { + name string + setupMocks func() + want *types.MatchingRefreshTaskListPartitionConfigResponse + err error + }{ + { + name: "Success case", + setupMocks: func() { + s.mockLimiter.EXPECT().Allow().Return(true).Times(1) + s.mockEngine.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &request). + Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil).Times(1) + }, + want: &types.MatchingRefreshTaskListPartitionConfigResponse{}, + }, + { + name: "Error case - RefreshTaskListPartitionConfig failed", + setupMocks: func() { + s.mockLimiter.EXPECT().Allow().Return(true).Times(1) + s.mockEngine.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &request). + Return(nil, errors.New("refresh-tasklist-error")).Times(1) + }, + err: &types.InternalServiceError{Message: "refresh-tasklist-error"}, + }, + } + + for _, tc := range testCases { + s.T().Run(tc.name, func(t *testing.T) { + tc.setupMocks() + s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) + + resp, err := s.handler.RefreshTaskListPartitionConfig(context.Background(), &request) + + if tc.err != nil { + s.Error(err) + s.Equal(tc.err, err) + } else { + s.NoError(err) + s.Equal(tc.want, resp) + } + }) + } +} + +func (s *handlerSuite) TestUpdateTaskListPartitionConfig() { + request := types.MatchingUpdateTaskListPartitionConfigRequest{ + DomainUUID: "test-domain-id", + TaskList: &types.TaskList{Name: "test-task-list"}, + } + + testCases := []struct { + name string + setupMocks func() + want *types.MatchingUpdateTaskListPartitionConfigResponse + err error + }{ + { + name: "Success case", + setupMocks: func() { + s.mockLimiter.EXPECT().Allow().Return(true).Times(1) + s.mockEngine.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &request). + Return(&types.MatchingUpdateTaskListPartitionConfigResponse{}, nil).Times(1) + }, + want: &types.MatchingUpdateTaskListPartitionConfigResponse{}, + }, + { + name: "Error case - rate limiter not allowed", + setupMocks: func() { + s.mockLimiter.EXPECT().Allow().Return(false).Times(1) + }, + err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, + }, + { + name: "Error case - UpdateTaskListPartitionConfig failed", + setupMocks: func() { + s.mockLimiter.EXPECT().Allow().Return(true).Times(1) + s.mockEngine.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &request). + Return(nil, errors.New("update-tasklist-error")).Times(1) + }, + err: &types.InternalServiceError{Message: "update-tasklist-error"}, + }, + } + + for _, tc := range testCases { + s.T().Run(tc.name, func(t *testing.T) { + tc.setupMocks() + s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) + + resp, err := s.handler.UpdateTaskListPartitionConfig(context.Background(), &request) + + if tc.err != nil { + s.Error(err) + s.Equal(tc.err, err) + } else { + s.NoError(err) + s.Equal(tc.want, resp) + } + }) + } +} diff --git a/service/matching/handler/interfaces.go b/service/matching/handler/interfaces.go index 696dda897d5..ec1846aa3a2 100644 --- a/service/matching/handler/interfaces.go +++ b/service/matching/handler/interfaces.go @@ -46,6 +46,8 @@ type ( DescribeTaskList(hCtx *handlerContext, request *types.MatchingDescribeTaskListRequest) (*types.DescribeTaskListResponse, error) ListTaskListPartitions(hCtx *handlerContext, request *types.MatchingListTaskListPartitionsRequest) (*types.ListTaskListPartitionsResponse, error) GetTaskListsByDomain(hCtx *handlerContext, request *types.GetTaskListsByDomainRequest) (*types.GetTaskListsByDomainResponse, error) + UpdateTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) + RefreshTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) } // Handler interface for matching service @@ -63,5 +65,7 @@ type ( PollForDecisionTask(context.Context, *types.MatchingPollForDecisionTaskRequest) (*types.MatchingPollForDecisionTaskResponse, error) QueryWorkflow(context.Context, *types.MatchingQueryWorkflowRequest) (*types.QueryWorkflowResponse, error) RespondQueryTaskCompleted(context.Context, *types.MatchingRespondQueryTaskCompletedRequest) error + UpdateTaskListPartitionConfig(context.Context, *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) + RefreshTaskListPartitionConfig(context.Context, *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) } ) diff --git a/service/matching/handler/interfaces_mock.go b/service/matching/handler/interfaces_mock.go index e749ccefc7e..4d6761445f0 100644 --- a/service/matching/handler/interfaces_mock.go +++ b/service/matching/handler/interfaces_mock.go @@ -192,6 +192,21 @@ func (mr *MockEngineMockRecorder) QueryWorkflow(hCtx, request interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockEngine)(nil).QueryWorkflow), hCtx, request) } +// RefreshTaskListPartitionConfig mocks base method. +func (m *MockEngine) RefreshTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", hCtx, request) + ret0, _ := ret[0].(*types.MatchingRefreshTaskListPartitionConfigResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. +func (mr *MockEngineMockRecorder) RefreshTaskListPartitionConfig(hCtx, request interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockEngine)(nil).RefreshTaskListPartitionConfig), hCtx, request) +} + // RespondQueryTaskCompleted mocks base method. func (m *MockEngine) RespondQueryTaskCompleted(hCtx *handlerContext, request *types.MatchingRespondQueryTaskCompletedRequest) error { m.ctrl.T.Helper() @@ -230,6 +245,21 @@ func (mr *MockEngineMockRecorder) Stop() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockEngine)(nil).Stop)) } +// UpdateTaskListPartitionConfig mocks base method. +func (m *MockEngine) UpdateTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", hCtx, request) + ret0, _ := ret[0].(*types.MatchingUpdateTaskListPartitionConfigResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. +func (mr *MockEngineMockRecorder) UpdateTaskListPartitionConfig(hCtx, request interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockEngine)(nil).UpdateTaskListPartitionConfig), hCtx, request) +} + // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller @@ -402,6 +432,21 @@ func (mr *MockHandlerMockRecorder) QueryWorkflow(arg0, arg1 interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockHandler)(nil).QueryWorkflow), arg0, arg1) } +// RefreshTaskListPartitionConfig mocks base method. +func (m *MockHandler) RefreshTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", arg0, arg1) + ret0, _ := ret[0].(*types.MatchingRefreshTaskListPartitionConfigResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. +func (mr *MockHandlerMockRecorder) RefreshTaskListPartitionConfig(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockHandler)(nil).RefreshTaskListPartitionConfig), arg0, arg1) +} + // RespondQueryTaskCompleted mocks base method. func (m *MockHandler) RespondQueryTaskCompleted(arg0 context.Context, arg1 *types.MatchingRespondQueryTaskCompletedRequest) error { m.ctrl.T.Helper() @@ -439,3 +484,18 @@ func (mr *MockHandlerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockHandler)(nil).Stop)) } + +// UpdateTaskListPartitionConfig mocks base method. +func (m *MockHandler) UpdateTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", arg0, arg1) + ret0, _ := ret[0].(*types.MatchingUpdateTaskListPartitionConfigResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. +func (mr *MockHandlerMockRecorder) UpdateTaskListPartitionConfig(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockHandler)(nil).UpdateTaskListPartitionConfig), arg0, arg1) +} diff --git a/service/matching/tasklist/db.go b/service/matching/tasklist/db.go index a29330e515a..446407b0fe8 100644 --- a/service/matching/tasklist/db.go +++ b/service/matching/tasklist/db.go @@ -136,6 +136,28 @@ func (db *taskListDB) UpdateState(ackLevel int64) error { return nil } +func (db *taskListDB) UpdateTaskListPartitionConfig(partitionConfig *persistence.TaskListPartitionConfig) error { + db.Lock() + defer db.Unlock() + _, err := db.store.UpdateTaskList(context.Background(), &persistence.UpdateTaskListRequest{ + TaskListInfo: &persistence.TaskListInfo{ + DomainID: db.domainID, + Name: db.taskListName, + TaskType: db.taskType, + AckLevel: db.ackLevel, + RangeID: db.rangeID, + Kind: db.taskListKind, + AdaptivePartitionConfig: partitionConfig, + }, + DomainName: db.domainName, + }) + if err != nil { + return err + } + db.partitionConfig = partitionConfig + return nil +} + // CreateTasks creates a batch of given tasks for this task list func (db *taskListDB) CreateTasks(tasks []*persistence.CreateTaskInfo) (*persistence.CreateTasksResponse, error) { db.Lock() diff --git a/service/matching/tasklist/identifier.go b/service/matching/tasklist/identifier.go index c1e866ab0fc..4f62c40ba10 100644 --- a/service/matching/tasklist/identifier.go +++ b/service/matching/tasklist/identifier.go @@ -99,10 +99,10 @@ func (tn *qualifiedTaskListName) Parent(degree int) string { return "" } pid := (tn.partition+degree-1)/degree - 1 - return tn.mkName(pid) + return tn.GetPartition(pid) } -func (tn *qualifiedTaskListName) mkName(partition int) string { +func (tn *qualifiedTaskListName) GetPartition(partition int) string { if partition == 0 { return tn.baseName } diff --git a/service/matching/tasklist/interfaces.go b/service/matching/tasklist/interfaces.go index 1b2596bef01..12e8218a69b 100644 --- a/service/matching/tasklist/interfaces.go +++ b/service/matching/tasklist/interfaces.go @@ -60,6 +60,8 @@ type ( GetTaskListKind() types.TaskListKind TaskListID() *Identifier TaskListPartitionConfig() *types.TaskListPartitionConfig + UpdateTaskListPartitionConfig(context.Context, *types.TaskListPartitionConfig) error + RefreshTaskListPartitionConfig(context.Context, *types.TaskListPartitionConfig) error } TaskMatcher interface { diff --git a/service/matching/tasklist/interfaces_mock.go b/service/matching/tasklist/interfaces_mock.go index 9ef04d8edd3..0892d65d3d9 100644 --- a/service/matching/tasklist/interfaces_mock.go +++ b/service/matching/tasklist/interfaces_mock.go @@ -186,6 +186,20 @@ func (mr *MockManagerMockRecorder) HasPollerAfter(accessTime interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPollerAfter", reflect.TypeOf((*MockManager)(nil).HasPollerAfter), accessTime) } +// RefreshTaskListPartitionConfig mocks base method. +func (m *MockManager) RefreshTaskListPartitionConfig(arg0 context.Context, arg1 *types.TaskListPartitionConfig) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. +func (mr *MockManagerMockRecorder) RefreshTaskListPartitionConfig(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockManager)(nil).RefreshTaskListPartitionConfig), arg0, arg1) +} + // Start mocks base method. func (m *MockManager) Start() error { m.ctrl.T.Helper() @@ -254,6 +268,20 @@ func (mr *MockManagerMockRecorder) TaskListPartitionConfig() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskListPartitionConfig", reflect.TypeOf((*MockManager)(nil).TaskListPartitionConfig)) } +// UpdateTaskListPartitionConfig mocks base method. +func (m *MockManager) UpdateTaskListPartitionConfig(arg0 context.Context, arg1 *types.TaskListPartitionConfig) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. +func (mr *MockManagerMockRecorder) UpdateTaskListPartitionConfig(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockManager)(nil).UpdateTaskListPartitionConfig), arg0, arg1) +} + // MockTaskMatcher is a mock of TaskMatcher interface. type MockTaskMatcher struct { ctrl *gomock.Controller diff --git a/service/matching/tasklist/matcher.go b/service/matching/tasklist/matcher.go index 6c924891c7b..1ba1610d250 100644 --- a/service/matching/tasklist/matcher.go +++ b/service/matching/tasklist/matcher.go @@ -66,6 +66,8 @@ type taskMatcherImpl struct { tasklist *Identifier tasklistKind types.TaskListKind + + numReadPartitionsFn func(*config.TaskListConfig) int } // ErrTasklistThrottled implies a tasklist was throttled @@ -81,7 +83,9 @@ func newTaskMatcher( isolationGroups []string, log log.Logger, tasklist *Identifier, - tasklistKind types.TaskListKind) TaskMatcher { + tasklistKind types.TaskListKind, + numReadPartitionsFn func(*config.TaskListConfig) int, +) TaskMatcher { dPtr := config.TaskDispatchRPS limiter := quotas.NewRateLimiter(&dPtr, config.TaskDispatchRPSTTL, config.MinTaskThrottlingBurstSize()) isolatedTaskC := make(map[string]chan *InternalTask) @@ -92,18 +96,19 @@ func newTaskMatcher( cancelCtx, cancelFunc := context.WithCancel(context.Background()) return &taskMatcherImpl{ - log: log, - limiter: limiter, - scope: scope, - fwdr: fwdr, - taskC: make(chan *InternalTask), - isolatedTaskC: isolatedTaskC, - queryTaskC: make(chan *InternalTask), - config: config, - tasklist: tasklist, - tasklistKind: tasklistKind, - cancelCtx: cancelCtx, - cancelFunc: cancelFunc, + log: log, + limiter: limiter, + scope: scope, + fwdr: fwdr, + taskC: make(chan *InternalTask), + isolatedTaskC: isolatedTaskC, + queryTaskC: make(chan *InternalTask), + config: config, + tasklist: tasklist, + tasklistKind: tasklistKind, + cancelCtx: cancelCtx, + cancelFunc: cancelFunc, + numReadPartitionsFn: numReadPartitionsFn, } } @@ -449,7 +454,7 @@ func (tm *taskMatcherImpl) UpdateRatelimit(rps *float64) { return } rate := *rps - nPartitions := tm.config.NumReadPartitions() + nPartitions := tm.numReadPartitionsFn(tm.config) if rate > float64(nPartitions) { // divide the rate equally across all partitions rate = rate / float64(nPartitions) diff --git a/service/matching/tasklist/matcher_test.go b/service/matching/tasklist/matcher_test.go index 98d9fe4f075..041930883f4 100644 --- a/service/matching/tasklist/matcher_test.go +++ b/service/matching/tasklist/matcher_test.go @@ -85,11 +85,11 @@ func (t *MatcherTestSuite) SetupTest() { t.cfg = tlCfg t.isolationGroups = []string{"dca1", "dca2"} t.fwdr = newForwarder(&t.cfg.ForwarderConfig, t.taskList, types.TaskListKindNormal, t.client, []string{"dca1", "dca2"}, metrics.NoopScope(metrics.Matching)) - t.matcher = newTaskMatcher(tlCfg, t.fwdr, metrics.NoopScope(metrics.Matching), []string{"dca1", "dca2"}, loggerimpl.NewNopLogger(), t.taskList, types.TaskListKindNormal).(*taskMatcherImpl) + t.matcher = newTaskMatcher(tlCfg, t.fwdr, metrics.NoopScope(metrics.Matching), []string{"dca1", "dca2"}, loggerimpl.NewNopLogger(), t.taskList, types.TaskListKindNormal, func(cfg *config.TaskListConfig) int { return tlCfg.NumReadPartitions() }).(*taskMatcherImpl) rootTaskList := NewTestTaskListID(t.T(), t.taskList.GetDomainID(), t.taskList.Parent(20), persistence.TaskListTypeDecision) rootTasklistCfg := newTaskListConfig(rootTaskList, cfg, testDomainName) - t.rootMatcher = newTaskMatcher(rootTasklistCfg, nil, metrics.NoopScope(metrics.Matching), []string{"dca1", "dca2"}, loggerimpl.NewNopLogger(), t.taskList, types.TaskListKindNormal).(*taskMatcherImpl) + t.rootMatcher = newTaskMatcher(rootTasklistCfg, nil, metrics.NoopScope(metrics.Matching), []string{"dca1", "dca2"}, loggerimpl.NewNopLogger(), t.taskList, types.TaskListKindNormal, func(cfg *config.TaskListConfig) int { return tlCfg.NumReadPartitions() }).(*taskMatcherImpl) } func (t *MatcherTestSuite) TearDownTest() { diff --git a/service/matching/tasklist/task_list_manager.go b/service/matching/tasklist/task_list_manager.go index 3531fa36658..8a371c91156 100644 --- a/service/matching/tasklist/task_list_manager.go +++ b/service/matching/tasklist/task_list_manager.go @@ -28,11 +28,14 @@ import ( "errors" "fmt" "math" + "reflect" "sort" "sync" "sync/atomic" "time" + "golang.org/x/sync/errgroup" + "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" @@ -105,6 +108,7 @@ type ( logger log.Logger scope metrics.Scope timeSource clock.TimeSource + matchingClient matching.Client domainName string // pollerHistory stores poller which poll from this tasklist in last few minutes pollerHistory poller.History @@ -119,6 +123,7 @@ type ( startWG sync.WaitGroup // ensures that background processes do not start until setup is ready stopped int32 closeCallback func(Manager) + throttleRetry *backoff.ThrottleRetry qpsTracker stats.QPSTracker @@ -145,7 +150,7 @@ func NewManager( closeCallback func(Manager), taskList *Identifier, taskListKind *types.TaskListKind, - config *config.Config, + cfg *config.Config, timeSource clock.TimeSource, createTime time.Time, ) (Manager, error) { @@ -154,7 +159,7 @@ func NewManager( return nil, err } - taskListConfig := newTaskListConfig(taskList, config, domainName) + taskListConfig := newTaskListConfig(taskList, cfg, domainName) if taskListKind == nil { normalTaskListKind := types.TaskListKindNormal @@ -178,11 +183,16 @@ func NewManager( taskAckManager: messaging.NewAckManager(logger), taskGC: newTaskGC(db, taskListConfig), config: taskListConfig, + matchingClient: matchingClient, outstandingPollsMap: make(map[string]outstandingPollerInfo), domainName: domainName, scope: scope, timeSource: timeSource, closeCallback: closeCallback, + throttleRetry: backoff.NewThrottleRetry( + backoff.WithRetryPolicy(persistenceOperationRetryPolicy), + backoff.WithRetryableError(persistence.IsTransientError), + ), } tlMgr.pollerHistory = poller.NewPollerHistory(func() { @@ -198,13 +208,19 @@ func NewManager( tlMgr.qpsTracker = stats.NewEmaFixedWindowQPSTracker(timeSource, 0.5, 10*time.Second) var isolationGroups []string if tlMgr.isIsolationMatcherEnabled() { - isolationGroups = config.AllIsolationGroups() + isolationGroups = cfg.AllIsolationGroups() } var fwdr Forwarder if tlMgr.isFowardingAllowed(taskList, *taskListKind) { fwdr = newForwarder(&taskListConfig.ForwarderConfig, taskList, *taskListKind, matchingClient, isolationGroups, scope) } - tlMgr.matcher = newTaskMatcher(taskListConfig, fwdr, tlMgr.scope, isolationGroups, tlMgr.logger, taskList, *taskListKind).(*taskMatcherImpl) + numReadPartitionsFn := func(cfg *config.TaskListConfig) int { + if cfg.EnableGetNumberOfPartitionsFromCache() { + return int(tlMgr.TaskListPartitionConfig().NumReadPartitions) + } + return cfg.NumReadPartitions() + } + tlMgr.matcher = newTaskMatcher(taskListConfig, fwdr, tlMgr.scope, isolationGroups, tlMgr.logger, taskList, *taskListKind, numReadPartitionsFn).(*taskMatcherImpl) tlMgr.taskWriter = newTaskWriter(tlMgr) tlMgr.taskReader = newTaskReader(tlMgr, isolationGroups) tlMgr.startWG.Add(1) @@ -216,21 +232,35 @@ func NewManager( func (c *taskListManagerImpl) Start() error { defer c.startWG.Done() + if !c.taskListID.IsRoot() && c.taskListKind == types.TaskListKindNormal { + var info *persistence.TaskListInfo + err := c.throttleRetry.Do(context.Background(), func() error { + var err error + info, err = c.db.GetTaskListInfo(c.taskListID.GetRoot()) + return err + }) + if err != nil { + // This is an edge case, and only occur before we fully migrate partition config to database for a tasklist. + // Currently, if a task list is configured with multiple partitions in dynamicconfig before the creation of the task list, + // a non-root partition can receive a request before the root partition and when it task list manager tries to read partition config from the root partition it will get this error. + // For example, if in the dynamicconfig we set all task list to have 2 partitions by default, all the non-root partitions of newly created task lists will get this error. + // This will not happen once we fully migrate partition config to database. Because in that case, root partition will always be created before non-root partitions. + var e *types.EntityNotExistsError + if !errors.As(err, &e) { + c.Stop() + return err + } + } else { + c.partitionConfig = info.AdaptivePartitionConfig.ToInternalType() + } + } if err := c.taskWriter.Start(); err != nil { c.Stop() return err } - c.loadTaskListPartitionConfig() - if c.taskListID.IsRoot() && c.taskListKind != types.TaskListKindSticky { + if c.taskListID.IsRoot() && c.taskListKind == types.TaskListKindNormal { c.partitionConfig = c.db.PartitionConfig().ToInternalType() - if c.partitionConfig == nil { - c.partitionConfig = &types.TaskListPartitionConfig{ - Version: 0, - NumReadPartitions: 1, - NumWritePartitions: 1, - } - } - c.logger.Info("get task list partition config from db", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Dynamic("config", c.partitionConfig)) + c.logger.Info("get task list partition config from db", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Dynamic("task-list-partition-config", c.partitionConfig)) } c.liveness.Start() c.taskReader.Start() @@ -268,49 +298,116 @@ func (c *taskListManagerImpl) handleErr(err error) error { return err } -func (c *taskListManagerImpl) loadTaskListPartitionConfig() { - if c.taskListID.IsRoot() { - return - } +func (c *taskListManagerImpl) TaskListPartitionConfig() *types.TaskListPartitionConfig { c.partitionConfigLock.RLock() - if c.partitionConfig != nil { - c.partitionConfigLock.RUnlock() - return + defer c.partitionConfigLock.RUnlock() + if c.partitionConfig == nil { + return nil } - c.partitionConfigLock.RUnlock() + config := *c.partitionConfig + c.logger.Debug("get task list partition config from db", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Dynamic("config", config)) + c.scope.Tagged(metrics.TaskListRootPartitionTag(c.taskListID.GetRoot())).UpdateGauge(metrics.TaskListPartitionConfigNumReadGauge, float64(config.NumReadPartitions)) + c.scope.Tagged(metrics.TaskListRootPartitionTag(c.taskListID.GetRoot())).UpdateGauge(metrics.TaskListPartitionConfigNumWriteGauge, float64(config.NumWritePartitions)) + c.scope.Tagged(metrics.TaskListRootPartitionTag(c.taskListID.GetRoot())).UpdateGauge(metrics.TaskListPartitionConfigVersionGauge, float64(config.Version)) + return &config +} - c.partitionConfigLock.Lock() - if c.partitionConfig != nil { +func isTaskListPartitionConfigEqual(a types.TaskListPartitionConfig, b types.TaskListPartitionConfig) bool { + a.Version = b.Version + return reflect.DeepEqual(a, b) +} + +func (c *taskListManagerImpl) RefreshTaskListPartitionConfig(ctx context.Context, config *types.TaskListPartitionConfig) error { + c.startWG.Wait() + if config == nil { + // if config is nil, we'll reload it from database + var info *persistence.TaskListInfo + err := c.throttleRetry.Do(ctx, func() error { + var err error + info, err = c.db.GetTaskListInfo(c.taskListID.GetRoot()) + return err + }) + if err != nil { + return err + } + config = info.AdaptivePartitionConfig.ToInternalType() + c.partitionConfigLock.Lock() + c.partitionConfig = config c.partitionConfigLock.Unlock() - return + return nil } + c.partitionConfigLock.Lock() defer c.partitionConfigLock.Unlock() - info, err := c.db.GetTaskListInfo(c.taskListID.GetRoot()) - if err != nil { - // Given current set up, it's possible that the root partition is created after non-root partition - // In this case, we don't fail the start for now, but set the config to nil. - // We'll check if the field is nil, if it is, we'll reload it from database on demand. - c.logger.Error("failed to get tasklist info of root partition", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Error(err)) - return + if c.partitionConfig == nil || c.partitionConfig.Version < config.Version { + c.partitionConfig = config } - c.partitionConfig = info.AdaptivePartitionConfig.ToInternalType() - if c.partitionConfig == nil { - c.partitionConfig = &types.TaskListPartitionConfig{ - Version: 0, - NumReadPartitions: 1, - NumWritePartitions: 1, + return nil +} + +func (c *taskListManagerImpl) UpdateTaskListPartitionConfig(ctx context.Context, config *types.TaskListPartitionConfig) error { + c.startWG.Wait() + var version int64 + numberOfPartitionsToRefresh := 1 + c.partitionConfigLock.Lock() + if c.partitionConfig != nil { + numberOfPartitionsToRefresh = int(c.partitionConfig.NumReadPartitions) + if isTaskListPartitionConfigEqual(*c.partitionConfig, *config) { + c.partitionConfigLock.Unlock() + return nil } + version = c.partitionConfig.Version + } + err := c.throttleRetry.Do(ctx, func() error { + return c.db.UpdateTaskListPartitionConfig(&persistence.TaskListPartitionConfig{ + Version: version + 1, + NumReadPartitions: int(config.NumReadPartitions), + NumWritePartitions: int(config.NumWritePartitions), + }) + }) + if err != nil { + c.partitionConfigLock.Unlock() + // We're not sure whether the update was persisted or not, + // Stop the tasklist manager and let it be reloaded + c.scope.IncCounter(metrics.TaskListPartitionUpdateFailedCounter) + c.Stop() + return err } - c.logger.Info("get task list partition config from db", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Dynamic("config", c.partitionConfig)) + c.partitionConfig = c.db.PartitionConfig().ToInternalType() + currentConfig := *c.partitionConfig + numberOfPartitionsToRefresh = common.MaxInt(numberOfPartitionsToRefresh, int(currentConfig.NumReadPartitions)) + c.partitionConfigLock.Unlock() + // push update notification to all non-root partitions + c.notifyPartitionConfig(ctx, currentConfig, numberOfPartitionsToRefresh) + return nil } -func (c *taskListManagerImpl) TaskListPartitionConfig() *types.TaskListPartitionConfig { - c.partitionConfigLock.RLock() - defer c.partitionConfigLock.RUnlock() - if c.partitionConfig != nil { - c.logger.Debug("get task list partition config from db", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Dynamic("config", c.partitionConfig)) +func (c *taskListManagerImpl) notifyPartitionConfig(ctx context.Context, config types.TaskListPartitionConfig, count int) { + taskListType := types.TaskListTypeDecision.Ptr() + if c.taskListID.GetType() == persistence.TaskListTypeActivity { + taskListType = types.TaskListTypeActivity.Ptr() + } + g := &errgroup.Group{} + for i := 1; i < count; i++ { + taskListName := c.taskListID.GetPartition(i) + g.Go(func() (e error) { + defer func() { log.CapturePanic(recover(), c.logger, &e) }() + + _, e = c.matchingClient.RefreshTaskListPartitionConfig(ctx, &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: c.taskListID.GetDomainID(), + TaskList: &types.TaskList{Name: taskListName}, + TaskListType: taskListType, + PartitionConfig: &config, + }) + if e != nil { + c.logger.Error("failed to notify partition", tag.Error(e), tag.Dynamic("task-list-partition-name", taskListName)) + } + return e + }) + } + err := g.Wait() + if err != nil { + c.logger.Error("failed to notify all partitions", tag.Error(err)) } - return c.partitionConfig } // AddTask adds a task to the task list. This method will first attempt a synchronous @@ -323,7 +420,6 @@ func (c *taskListManagerImpl) AddTask(ctx context.Context, params AddTaskParams) c.Stop() return false, errShutdown } - c.loadTaskListPartitionConfig() if params.ForwardedFrom == "" { // request sent by history service c.liveness.MarkAlive() @@ -410,7 +506,6 @@ func (c *taskListManagerImpl) DispatchQueryTask( request *types.MatchingQueryWorkflowRequest, ) (*types.QueryWorkflowResponse, error) { c.startWG.Wait() - c.loadTaskListPartitionConfig() task := newInternalQueryTask(taskID, request) return c.matcher.OfferQuery(ctx, task) } @@ -428,7 +523,6 @@ func (c *taskListManagerImpl) GetTask( return nil, ErrNoTasks } c.liveness.MarkAlive() - c.loadTaskListPartitionConfig() // TODO: consider return early if QPS and backlog count are both 0, // since there is no task to be returned task, err := c.getTask(ctx, maxDispatchPerSecond) @@ -530,6 +624,7 @@ func (c *taskListManagerImpl) CancelPoller(pollerID string) { // (readLevel, ackLevel, backlogCountHint and taskIDBlock). func (c *taskListManagerImpl) DescribeTaskList(includeTaskListStatus bool) *types.DescribeTaskListResponse { response := &types.DescribeTaskListResponse{Pollers: c.GetAllPollerInfo()} + response.PartitionConfig = c.TaskListPartitionConfig() if !includeTaskListStatus { return response } @@ -833,6 +928,9 @@ func newTaskListConfig(id *Identifier, cfg *config.Config, domainName string) *c NumReadPartitions: func() int { return common.MaxInt(1, cfg.NumTasklistReadPartitions(domainName, taskListName, taskType)) }, + EnableGetNumberOfPartitionsFromCache: func() bool { + return cfg.EnableGetNumberOfPartitionsFromCache(domainName, id.GetRoot(), taskType) + }, AsyncTaskDispatchTimeout: func() time.Duration { return cfg.AsyncTaskDispatchTimeout(domainName, taskListName, taskType) }, diff --git a/service/matching/tasklist/task_list_manager_test.go b/service/matching/tasklist/task_list_manager_test.go index 57163c836e3..a9512d6b3c1 100644 --- a/service/matching/tasklist/task_list_manager_test.go +++ b/service/matching/tasklist/task_list_manager_test.go @@ -33,7 +33,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" + "golang.org/x/sync/errgroup" + "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" @@ -50,6 +52,50 @@ import ( "github.com/uber/cadence/service/matching/poller" ) +type mockDeps struct { + mockDomainCache *cache.MockDomainCache + mockTaskManager *persistence.MockTaskManager + mockPartitioner *partition.MockPartitioner + mockMatchingClient *matching.MockClient + mockTimeSource clock.MockedTimeSource + dynamicClient dynamicconfig.Client +} + +func setupMocksForTaskListManager(t *testing.T, taskListID *Identifier, taskListKind types.TaskListKind) (*taskListManagerImpl, *mockDeps) { + ctrl := gomock.NewController(t) + dynamicClient := dynamicconfig.NewInMemoryClient() + logger := testlogger.New(t) + metricsClient := metrics.NewNoopMetricsClient() + clusterMetadata := cluster.GetTestClusterMetadata(true) + deps := &mockDeps{ + mockDomainCache: cache.NewMockDomainCache(ctrl), + mockTaskManager: persistence.NewMockTaskManager(ctrl), + mockPartitioner: partition.NewMockPartitioner(ctrl), + mockMatchingClient: matching.NewMockClient(ctrl), + mockTimeSource: clock.NewMockedTimeSource(), + dynamicClient: dynamicClient, + } + deps.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("domainName", nil).Times(1) + config := config.NewConfig(dynamicconfig.NewCollection(dynamicClient, logger), "hostname", getIsolationgroupsHelper) + tlm, err := NewManager( + deps.mockDomainCache, + logger, + metricsClient, + deps.mockTaskManager, + clusterMetadata, + deps.mockPartitioner, + deps.mockMatchingClient, + func(Manager) {}, + taskListID, + &taskListKind, + config, + deps.mockTimeSource, + deps.mockTimeSource.Now(), + ) + require.NoError(t, err) + return tlm.(*taskListManagerImpl), deps +} + func defaultTestConfig() *config.Config { config := config.NewConfig(dynamicconfig.NewNopCollection(), "some random hostname", getIsolationgroupsHelper) config.LongPollExpirationInterval = dynamicconfig.GetDurationPropertyFnFilteredByTaskListInfo(100 * time.Millisecond) @@ -929,55 +975,418 @@ func getIsolationgroupsHelper() []string { return []string{"datacenterA", "datacenterB"} } -func TestLoadTaskListPartitionConfig(t *testing.T) { - ctrl := gomock.NewController(t) - mockPartitioner := partition.NewMockPartitioner(ctrl) - mockPartitioner.EXPECT().GetIsolationGroupByDomainID(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil).AnyTimes() - mockDomainCache := cache.NewMockDomainCache(ctrl) - mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.CreateDomainCacheEntry("domainName"), nil).AnyTimes() - mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("domainName", nil).AnyTimes() +func TestRefreshTaskListPartitionConfig(t *testing.T) { + testCases := []struct { + name string + req *types.TaskListPartitionConfig + originalConfig *types.TaskListPartitionConfig + setupMocks func(*mockDeps) + expectedConfig *types.TaskListPartitionConfig + expectError bool + expectedError string + }{ + { + name: "success - refresh from request", + req: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + setupMocks: func(m *mockDeps) {}, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + }, + { + name: "success - ignore older version", + req: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + originalConfig: &types.TaskListPartitionConfig{ + Version: 3, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + setupMocks: func(m *mockDeps) {}, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 3, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + }, + { + name: "success - refresh from database", + originalConfig: &types.TaskListPartitionConfig{ + Version: 3, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + setupMocks: func(deps *mockDeps) { + deps.mockTaskManager.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ + DomainID: "domain-id", + DomainName: "domainName", + TaskList: "tl", + TaskType: persistence.TaskListTypeDecision, + }).Return(&persistence.GetTaskListResponse{ + TaskListInfo: &persistence.TaskListInfo{ + AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ + Version: 4, + NumReadPartitions: 10, + NumWritePartitions: 10, + }, + }, + }, nil) + }, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 4, + NumReadPartitions: 10, + NumWritePartitions: 10, + }, + }, + { + name: "failed to refresh from database", + originalConfig: &types.TaskListPartitionConfig{ + Version: 3, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + setupMocks: func(deps *mockDeps) { + deps.mockTaskManager.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ + DomainID: "domain-id", + DomainName: "domainName", + TaskList: "tl", + TaskType: persistence.TaskListTypeDecision, + }).Return(nil, errors.New("some error")) + }, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 3, + NumReadPartitions: 2, + NumWritePartitions: 2, + }, + expectError: true, + expectedError: "some error", + }, + } - mockTm := persistence.NewMockTaskManager(ctrl) - mockTm.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ - DomainID: "domain", - DomainName: "domainName", - TaskList: "tasklist", - TaskType: persistence.TaskListTypeActivity, - }).Return(nil, errors.New("error")).Times(1) - mockTm.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ - DomainID: "domain", - DomainName: "domainName", - TaskList: "tasklist", - TaskType: persistence.TaskListTypeActivity, - }).Return(&persistence.GetTaskListResponse{ - TaskListInfo: &persistence.TaskListInfo{ - AdaptivePartitionConfig: nil, + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) + require.NoError(t, err) + tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) + tc.setupMocks(deps) + tlm.partitionConfig = tc.originalConfig + tlm.startWG.Done() + + err = tlm.RefreshTaskListPartitionConfig(context.Background(), tc.req) + if tc.expectError { + assert.ErrorContains(t, err, tc.expectedError) + assert.Equal(t, tc.expectedConfig, tlm.TaskListPartitionConfig()) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expectedConfig, tlm.TaskListPartitionConfig()) + } + }) + } +} + +func TestUpdateTaskListPartitionConfig(t *testing.T) { + testCases := []struct { + name string + req *types.TaskListPartitionConfig + originalConfig *types.TaskListPartitionConfig + setupMocks func(*mockDeps) + expectedConfig *types.TaskListPartitionConfig + expectError bool + expectedError string + }{ + { + name: "success - no op", + req: &types.TaskListPartitionConfig{ + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + originalConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + setupMocks: func(m *mockDeps) {}, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + }, + { + name: "success - update", + req: &types.TaskListPartitionConfig{ + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + originalConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + setupMocks: func(deps *mockDeps) { + deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), &persistence.UpdateTaskListRequest{ + DomainName: "domainName", + TaskListInfo: &persistence.TaskListInfo{ + DomainID: "domain-id", + Name: "tl", + AckLevel: 0, + RangeID: 0, + Kind: persistence.TaskListKindNormal, + AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }, + }).Return(&persistence.UpdateTaskListResponse{}, nil) + deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "domain-id", + TaskList: &types.TaskList{Name: "/__cadence_sys/tl/1"}, + TaskListType: types.TaskListTypeDecision.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil) + deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "domain-id", + TaskList: &types.TaskList{Name: "/__cadence_sys/tl/2"}, + TaskListType: types.TaskListTypeDecision.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil) + }, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }, + { + name: "success - push failures are ignored", + req: &types.TaskListPartitionConfig{ + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + originalConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + setupMocks: func(deps *mockDeps) { + deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), &persistence.UpdateTaskListRequest{ + DomainName: "domainName", + TaskListInfo: &persistence.TaskListInfo{ + DomainID: "domain-id", + Name: "tl", + AckLevel: 0, + RangeID: 0, + Kind: persistence.TaskListKindNormal, + AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }, + }).Return(&persistence.UpdateTaskListResponse{}, nil) + deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "domain-id", + TaskList: &types.TaskList{Name: "/__cadence_sys/tl/1"}, + TaskListType: types.TaskListTypeDecision.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }).Return(nil, errors.New("matching client error")) + deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ + DomainUUID: "domain-id", + TaskList: &types.TaskList{Name: "/__cadence_sys/tl/2"}, + TaskListType: types.TaskListTypeDecision.Ptr(), + PartitionConfig: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }).Return(nil, errors.New("matching client error")) + }, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }, + { + name: "failed to update", + req: &types.TaskListPartitionConfig{ + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + originalConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + setupMocks: func(deps *mockDeps) { + deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), &persistence.UpdateTaskListRequest{ + DomainName: "domainName", + TaskListInfo: &persistence.TaskListInfo{ + DomainID: "domain-id", + Name: "tl", + AckLevel: 0, + RangeID: 0, + Kind: persistence.TaskListKindNormal, + AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ + Version: 2, + NumReadPartitions: 3, + NumWritePartitions: 1, + }, + }, + }).Return(nil, errors.New("some error")) + }, + expectedConfig: &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + expectError: true, + expectedError: "some error", }, - }, nil).Times(1) + } - tlID, err := NewIdentifier("domain", "/__cadence_sys/tasklist/1", persistence.TaskListTypeActivity) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) + require.NoError(t, err) + tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) + tc.setupMocks(deps) + tlm.partitionConfig = tc.originalConfig + tlm.startWG.Done() + + err = tlm.UpdateTaskListPartitionConfig(context.Background(), tc.req) + if tc.expectError { + assert.ErrorContains(t, err, tc.expectedError) + assert.Equal(t, int32(1), tlm.stopped) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tc.expectedConfig, tlm.TaskListPartitionConfig()) + }) + } +} + +func TestRefreshTaskListPartitionConfigConcurrency(t *testing.T) { + tlID, err := NewIdentifier("domain-id", "/__cadence_sys/tl/1", persistence.TaskListTypeDecision) require.NoError(t, err) + tlm, _ := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) + tlm.startWG.Done() + + var g errgroup.Group + for i := 0; i < 100; i++ { + v := i + g.Go(func() error { + return tlm.RefreshTaskListPartitionConfig(context.Background(), &types.TaskListPartitionConfig{Version: int64(v), NumReadPartitions: int32(v), NumWritePartitions: int32(v)}) + }) + } + require.NoError(t, g.Wait()) + assert.Equal(t, int64(99), tlm.TaskListPartitionConfig().Version) +} - tlMgr, err := NewManager( - mockDomainCache, - testlogger.New(t), - metrics.NewClient(tally.NoopScope, metrics.Matching), - mockTm, - cluster.GetTestClusterMetadata(true), - mockPartitioner, - nil, - func(Manager) {}, - tlID, - types.TaskListKindNormal.Ptr(), - defaultTestConfig(), - clock.NewRealTimeSource(), - time.Now()) +func TestUpdateTaskListPartitionConfigConcurrency(t *testing.T) { + tlID, err := NewIdentifier("domain-id", "/__cadence_sys/tl/1", persistence.TaskListTypeDecision) + require.NoError(t, err) + tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) + deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any()).Return(&persistence.UpdateTaskListResponse{}, nil).AnyTimes() + deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), gomock.Any()).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil).AnyTimes() + tlm.startWG.Done() + + var g errgroup.Group + for i := 0; i < 100; i++ { + v := i + g.Go(func() error { + return tlm.UpdateTaskListPartitionConfig(context.Background(), &types.TaskListPartitionConfig{NumReadPartitions: int32(v), NumWritePartitions: int32(v)}) + }) + } + require.NoError(t, g.Wait()) + assert.Equal(t, int64(100), tlm.TaskListPartitionConfig().Version) +} - tlm := tlMgr.(*taskListManagerImpl) - tlm.loadTaskListPartitionConfig() +func TestManagerStart_RootPartition(t *testing.T) { + tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) + require.NoError(t, err) + tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) + deps.mockTaskManager.EXPECT().LeaseTaskList(gomock.Any(), &persistence.LeaseTaskListRequest{ + DomainID: "domain-id", + DomainName: "domainName", + TaskList: "tl", + TaskType: persistence.TaskListTypeDecision, + }).Return(&persistence.LeaseTaskListResponse{ + TaskListInfo: &persistence.TaskListInfo{ + DomainID: "domain-id", + Name: "tl", + Kind: persistence.TaskListKindNormal, + AckLevel: 0, + RangeID: 0, + }, + }, nil) + assert.NoError(t, tlm.Start()) assert.Nil(t, tlm.TaskListPartitionConfig()) - tlm.loadTaskListPartitionConfig() - assert.Equal(t, &types.TaskListPartitionConfig{NumReadPartitions: 1, NumWritePartitions: 1}, tlm.TaskListPartitionConfig()) - tlm.loadTaskListPartitionConfig() - assert.Equal(t, &types.TaskListPartitionConfig{NumReadPartitions: 1, NumWritePartitions: 1}, tlm.TaskListPartitionConfig()) +} + +func TestManagerStart_NonRootPartition(t *testing.T) { + tlID, err := NewIdentifier("domain-id", "/__cadence_sys/tl/1", persistence.TaskListTypeDecision) + require.NoError(t, err) + tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) + deps.mockTaskManager.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ + DomainID: "domain-id", + DomainName: "domainName", + TaskList: "tl", + TaskType: persistence.TaskListTypeDecision, + }).Return(&persistence.GetTaskListResponse{ + TaskListInfo: &persistence.TaskListInfo{ + DomainID: "domain-id", + Name: "tl", + Kind: persistence.TaskListKindNormal, + AckLevel: 0, + RangeID: 0, + AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, + }, + }, nil) + deps.mockTaskManager.EXPECT().LeaseTaskList(gomock.Any(), &persistence.LeaseTaskListRequest{ + DomainID: "domain-id", + DomainName: "domainName", + TaskList: "/__cadence_sys/tl/1", + TaskType: persistence.TaskListTypeDecision, + }).Return(&persistence.LeaseTaskListResponse{ + TaskListInfo: &persistence.TaskListInfo{ + DomainID: "domain-id", + Name: "/__cadence_sys/tl/1", + Kind: persistence.TaskListKindNormal, + AckLevel: 0, + RangeID: 0, + }, + }, nil) + assert.NoError(t, tlm.Start()) + assert.Equal(t, &types.TaskListPartitionConfig{ + Version: 1, + NumReadPartitions: 3, + NumWritePartitions: 3, + }, tlm.TaskListPartitionConfig()) } diff --git a/service/matching/wrappers/grpc/grpc_handler_generated.go b/service/matching/wrappers/grpc/grpc_handler_generated.go index 0e614a93806..575e119b568 100644 --- a/service/matching/wrappers/grpc/grpc_handler_generated.go +++ b/service/matching/wrappers/grpc/grpc_handler_generated.go @@ -87,7 +87,17 @@ func (g GRPCHandler) QueryWorkflow(ctx context.Context, request *matchingv1.Quer return proto.FromMatchingQueryWorkflowResponse(response), proto.FromError(err) } +func (g GRPCHandler) RefreshTaskListPartitionConfig(ctx context.Context, request *matchingv1.RefreshTaskListPartitionConfigRequest) (*matchingv1.RefreshTaskListPartitionConfigResponse, error) { + response, err := g.h.RefreshTaskListPartitionConfig(ctx, proto.ToMatchingRefreshTaskListPartitionConfigRequest(request)) + return proto.FromMatchingRefreshTaskListPartitionConfigResponse(response), proto.FromError(err) +} + func (g GRPCHandler) RespondQueryTaskCompleted(ctx context.Context, request *matchingv1.RespondQueryTaskCompletedRequest) (*matchingv1.RespondQueryTaskCompletedResponse, error) { err := g.h.RespondQueryTaskCompleted(ctx, proto.ToMatchingRespondQueryTaskCompletedRequest(request)) return &matchingv1.RespondQueryTaskCompletedResponse{}, proto.FromError(err) } + +func (g GRPCHandler) UpdateTaskListPartitionConfig(ctx context.Context, request *matchingv1.UpdateTaskListPartitionConfigRequest) (*matchingv1.UpdateTaskListPartitionConfigResponse, error) { + response, err := g.h.UpdateTaskListPartitionConfig(ctx, proto.ToMatchingUpdateTaskListPartitionConfigRequest(request)) + return proto.FromMatchingUpdateTaskListPartitionConfigResponse(response), proto.FromError(err) +} diff --git a/tools/cli/admin_task_list_commands.go b/tools/cli/admin_task_list_commands.go index 0f705aa22fa..eedcaac61f9 100644 --- a/tools/cli/admin_task_list_commands.go +++ b/tools/cli/admin_task_list_commands.go @@ -22,6 +22,7 @@ package cli import ( "fmt" + "io" "os" "strings" @@ -45,6 +46,11 @@ type ( StartID int64 `header:"Lease Start TaskID"` EndID int64 `header:"Lease End TaskID"` } + TaskListPartitionConfigRow struct { + Version int64 `header:"Version"` + NumReadPartitions int32 `header:"Number of Read Partitions"` + NumWritePartitions int32 `header:"Number of Write Partitions"` + } ) // AdminDescribeTaskList displays poller and status information of task list. @@ -87,16 +93,21 @@ func AdminDescribeTaskList(c *cli.Context) error { if taskListStatus == nil { return commoncli.Problem(colorMagenta("No tasklist status information."), nil) } - if err := printTaskListStatus(taskListStatus); err != nil { + if err := printTaskListStatus(getDeps(c).Output(), taskListStatus); err != nil { return fmt.Errorf("failed to print task list status: %w", err) } - fmt.Printf("\n") - + getDeps(c).Output().Write([]byte("\n")) + if response.PartitionConfig != nil { + if err := printTaskListPartitionConfig(getDeps(c).Output(), response.PartitionConfig); err != nil { + return fmt.Errorf("failed to print task list partition config: %w", err) + } + getDeps(c).Output().Write([]byte("\n")) + } pollers := response.Pollers if len(pollers) == 0 { return commoncli.Problem(colorMagenta("No poller for tasklist: "+taskList), nil) } - return printTaskListPollers(pollers, taskListType) + return printTaskListPollers(getDeps(c).Output(), pollers, taskListType) } // AdminListTaskList displays all task lists under a domain. @@ -134,7 +145,7 @@ func AdminListTaskList(c *cli.Context) error { return RenderTable(os.Stdout, table, RenderOptions{Color: true, Border: true}) } -func printTaskListStatus(taskListStatus *types.TaskListStatus) error { +func printTaskListStatus(w io.Writer, taskListStatus *types.TaskListStatus) error { table := []TaskListStatusRow{{ ReadLevel: taskListStatus.GetReadLevel(), AckLevel: taskListStatus.GetAckLevel(), @@ -143,7 +154,16 @@ func printTaskListStatus(taskListStatus *types.TaskListStatus) error { StartID: taskListStatus.GetTaskIDBlock().GetStartID(), EndID: taskListStatus.GetTaskIDBlock().GetEndID(), }} - return RenderTable(os.Stdout, table, RenderOptions{Color: true}) + return RenderTable(w, table, RenderOptions{Color: true}) +} + +func printTaskListPartitionConfig(w io.Writer, config *types.TaskListPartitionConfig) error { + table := TaskListPartitionConfigRow{ + Version: config.Version, + NumReadPartitions: config.NumReadPartitions, + NumWritePartitions: config.NumWritePartitions, + } + return RenderTable(w, table, RenderOptions{Color: true}) } func AdminUpdateTaskListPartitionConfig(c *cli.Context) error { @@ -182,7 +202,7 @@ func AdminUpdateTaskListPartitionConfig(c *cli.Context) error { } _, err = adminClient.UpdateTaskListPartitionConfig(ctx, &types.UpdateTaskListPartitionConfigRequest{ Domain: domain, - TaskList: &types.TaskList{Name: taskList}, + TaskList: &types.TaskList{Name: taskList, Kind: types.TaskListKindNormal.Ptr()}, TaskListType: taskListType, PartitionConfig: &types.TaskListPartitionConfig{ NumReadPartitions: int32(numReadPartitions), diff --git a/tools/cli/admin_task_list_commands_test.go b/tools/cli/admin_task_list_commands_test.go index e480bac2067..29a1f9d3328 100644 --- a/tools/cli/admin_task_list_commands_test.go +++ b/tools/cli/admin_task_list_commands_test.go @@ -280,7 +280,7 @@ func TestAdminUpdateTaskListPartitionConfig(t *testing.T) { client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", - TaskList: &types.TaskList{Name: "test-tasklist"}, + TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ NumReadPartitions: 2, diff --git a/tools/cli/task_list_commands.go b/tools/cli/task_list_commands.go index 27c04871952..b9526378d65 100644 --- a/tools/cli/task_list_commands.go +++ b/tools/cli/task_list_commands.go @@ -21,6 +21,7 @@ package cli import ( + "io" "os" "time" @@ -81,7 +82,7 @@ func DescribeTaskList(c *cli.Context) error { return commoncli.Problem(colorMagenta("No poller for tasklist: "+taskList), nil) } - return printTaskListPollers(pollers, taskListType) + return printTaskListPollers(getDeps(c).Output(), pollers, taskListType) } // ListTaskListPartitions gets all the tasklist partition and host information. @@ -121,7 +122,7 @@ func ListTaskListPartitions(c *cli.Context) error { return nil } -func printTaskListPollers(pollers []*types.PollerInfo, taskListType types.TaskListType) error { +func printTaskListPollers(w io.Writer, pollers []*types.PollerInfo, taskListType types.TaskListType) error { table := []TaskListPollerRow{} for _, poller := range pollers { table = append(table, TaskListPollerRow{ @@ -129,7 +130,7 @@ func printTaskListPollers(pollers []*types.PollerInfo, taskListType types.TaskLi DecisionIdentity: poller.GetIdentity(), LastAccessTime: time.Unix(0, poller.GetLastAccessTime())}) } - return RenderTable(os.Stdout, table, RenderOptions{Color: true, PrintDateTime: true, OptionalColumns: map[string]bool{ + return RenderTable(w, table, RenderOptions{Color: true, PrintDateTime: true, OptionalColumns: map[string]bool{ "Activity Poller Identity": taskListType == types.TaskListTypeActivity, "Decision Poller Identity": taskListType == types.TaskListTypeDecision, }})