From 9e9638677a35384e9acd12a1ecca1390fdf72b3e Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Wed, 14 Aug 2024 15:43:00 -0700
Subject: [PATCH 001/188] feat: autoscaling for MonoVertex (#1927)
Signed-off-by: Derek Wang
Signed-off-by: Sidhant Kohli
Signed-off-by: Vigith Maurice
Signed-off-by: Keran Yang
Co-authored-by: Sidhant Kohli
Co-authored-by: Vigith Maurice
Co-authored-by: Keran Yang
---
hack/generate-proto.sh | 2 +
.../numaflow/v1alpha1/mono_vertex_types.go | 22 ++
pkg/apis/numaflow/v1alpha1/pipeline_types.go | 2 +
pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go | 299 ++++++++++++++
pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go | 156 ++++++++
pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto | 47 +++
.../proto/mvtxdaemon/mvtxdaemon_grpc.pb.go | 126 ++++++
.../server/service/rater/pod_tracker.go | 5 +-
pkg/metrics/metrics.go | 2 +-
pkg/mvtxdaemon/client/doc.go | 26 ++
pkg/mvtxdaemon/client/grpc_client.go | 63 +++
pkg/mvtxdaemon/client/grpc_client_test.go | 17 +
pkg/mvtxdaemon/client/interface.go | 29 ++
pkg/mvtxdaemon/client/restful_client.go | 91 +++++
pkg/mvtxdaemon/client/restful_client_test.go | 17 +
pkg/mvtxdaemon/server/daemon_server.go | 28 +-
pkg/mvtxdaemon/server/metrics.go | 4 +-
pkg/mvtxdaemon/server/service/mvtx_service.go | 119 ++++++
.../server/service/rater/goleak_test.go | 28 ++
pkg/mvtxdaemon/server/service/rater/helper.go | 123 ++++++
.../server/service/rater/helper_test.go | 288 ++++++++++++++
.../server/service/rater/options.go | 53 +++
.../server/service/rater/pod_tracker.go | 178 +++++++++
.../server/service/rater/pod_tracker_test.go | 141 +++++++
pkg/mvtxdaemon/server/service/rater/rater.go | 270 +++++++++++++
.../server/service/rater/rater_test.go | 134 +++++++
.../service/rater/timestamped_counts.go | 73 ++++
.../service/rater/timestamped_counts_test.go | 64 +++
pkg/reconciler/cmd/start.go | 13 +-
pkg/reconciler/metrics.go | 25 +-
pkg/reconciler/monovertex/controller.go | 31 +-
pkg/reconciler/monovertex/scaling/doc.go | 25 ++
pkg/reconciler/monovertex/scaling/options.go | 57 +++
pkg/reconciler/monovertex/scaling/scaling.go | 366 ++++++++++++++++++
.../monovertex/scaling/scaling_test.go | 17 +
pkg/reconciler/vertex/scaling/doc.go | 2 +-
pkg/reconciler/vertex/scaling/scaling.go | 10 +-
.../rater => shared/util}/uniq_str_list.go | 2 +-
.../util}/uniq_str_list_test.go | 2 +-
rust/Cargo.lock | 33 +-
rust/monovertex/Cargo.toml | 4 +-
rust/monovertex/src/forwarder.rs | 41 +-
rust/monovertex/src/lib.rs | 2 +-
rust/monovertex/src/metrics.rs | 254 ++++++++----
server/apis/interface.go | 1 +
server/apis/v1/handler.go | 113 ++++--
server/cmd/server/start.go | 1 +
server/cmd/server/start_test.go | 4 +-
server/routes/routes.go | 2 +
49 files changed, 3255 insertions(+), 157 deletions(-)
create mode 100644 pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go
create mode 100644 pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go
create mode 100644 pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
create mode 100644 pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go
create mode 100644 pkg/mvtxdaemon/client/doc.go
create mode 100644 pkg/mvtxdaemon/client/grpc_client.go
create mode 100644 pkg/mvtxdaemon/client/grpc_client_test.go
create mode 100644 pkg/mvtxdaemon/client/interface.go
create mode 100644 pkg/mvtxdaemon/client/restful_client.go
create mode 100644 pkg/mvtxdaemon/client/restful_client_test.go
create mode 100644 pkg/mvtxdaemon/server/service/mvtx_service.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/goleak_test.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/helper.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/helper_test.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/options.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/pod_tracker.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/pod_tracker_test.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/rater.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/rater_test.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/timestamped_counts.go
create mode 100644 pkg/mvtxdaemon/server/service/rater/timestamped_counts_test.go
create mode 100644 pkg/reconciler/monovertex/scaling/doc.go
create mode 100644 pkg/reconciler/monovertex/scaling/options.go
create mode 100644 pkg/reconciler/monovertex/scaling/scaling.go
create mode 100644 pkg/reconciler/monovertex/scaling/scaling_test.go
rename pkg/{daemon/server/service/rater => shared/util}/uniq_str_list.go (99%)
rename pkg/{daemon/server/service/rater => shared/util}/uniq_str_list_test.go (99%)
diff --git a/hack/generate-proto.sh b/hack/generate-proto.sh
index 614ded3e8a..bf970ce318 100755
--- a/hack/generate-proto.sh
+++ b/hack/generate-proto.sh
@@ -75,6 +75,8 @@ gen-protoc(){
gen-protoc pkg/apis/proto/daemon/daemon.proto
+gen-protoc pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
+
gen-protoc pkg/apis/proto/isb/message.proto
gen-protoc pkg/apis/proto/wmb/wmb.proto
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index b4a372ae45..ac64c7e2a6 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -123,6 +123,16 @@ func (mv MonoVertex) GetDaemonDeploymentName() string {
return fmt.Sprintf("%s-mv-daemon", mv.Name)
}
+func (mv MonoVertex) GetDaemonServiceURL() string {
+ // Note: the format of the URL is also used in `server/apis/v1/handler.go`
+ // Do not change it without updating the handler.
+ return fmt.Sprintf("%s.%s.svc:%d", mv.GetDaemonServiceName(), mv.Namespace, MonoVertexDaemonServicePort)
+}
+
+func (mv MonoVertex) Scalable() bool {
+ return !mv.Spec.Scale.Disabled
+}
+
func (mv MonoVertex) GetDaemonServiceObj() *corev1.Service {
labels := map[string]string{
KeyPartOf: Project,
@@ -518,6 +528,18 @@ func (mvs *MonoVertexStatus) MarkPhaseRunning() {
mvs.MarkPhase(MonoVertexPhaseRunning, "", "")
}
+// IsHealthy indicates whether the MonoVertex is in healthy status
+func (mvs *MonoVertexStatus) IsHealthy() bool {
+ switch mvs.Phase {
+ case MonoVertexPhaseFailed:
+ return false
+ case MonoVertexPhaseRunning:
+ return mvs.IsReady()
+ default:
+ return false
+ }
+}
+
// +kubebuilder:object:root=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type MonoVertexList struct {
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types.go b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
index b6fe89e85a..010b53bf20 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
@@ -200,6 +200,8 @@ func (p Pipeline) GetDaemonDeploymentName() string {
}
func (p Pipeline) GetDaemonServiceURL() string {
+ // Note: the format of the URL is also used in `server/apis/v1/handler.go`
+ // Do not change it without updating the handler.
return fmt.Sprintf("%s.%s.svc:%d", p.GetDaemonServiceName(), p.Namespace, DaemonServicePort)
}
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go
new file mode 100644
index 0000000000..10d3e2350c
--- /dev/null
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go
@@ -0,0 +1,299 @@
+//
+//Copyright 2022 The Numaproj Authors.
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.34.2
+// protoc v5.27.2
+// source: pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
+
+package mvtxdaemon
+
+import (
+ _ "google.golang.org/genproto/googleapis/api/annotations"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ emptypb "google.golang.org/protobuf/types/known/emptypb"
+ wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// MonoVertexMetrics is used to provide information about the mono vertex including processing rate.
+type MonoVertexMetrics struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ MonoVertex string `protobuf:"bytes,1,opt,name=monoVertex,proto3" json:"monoVertex,omitempty"`
+ // Processing rate in the past period of time, 1m, 5m, 15m, default
+ ProcessingRates map[string]*wrapperspb.DoubleValue `protobuf:"bytes,2,rep,name=processingRates,proto3" json:"processingRates,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+ // Pending in the past period of time, 1m, 5m, 15m, default
+ Pendings map[string]*wrapperspb.Int64Value `protobuf:"bytes,3,rep,name=pendings,proto3" json:"pendings,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *MonoVertexMetrics) Reset() {
+ *x = MonoVertexMetrics{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *MonoVertexMetrics) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MonoVertexMetrics) ProtoMessage() {}
+
+func (x *MonoVertexMetrics) ProtoReflect() protoreflect.Message {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use MonoVertexMetrics.ProtoReflect.Descriptor instead.
+func (*MonoVertexMetrics) Descriptor() ([]byte, []int) {
+ return file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *MonoVertexMetrics) GetMonoVertex() string {
+ if x != nil {
+ return x.MonoVertex
+ }
+ return ""
+}
+
+func (x *MonoVertexMetrics) GetProcessingRates() map[string]*wrapperspb.DoubleValue {
+ if x != nil {
+ return x.ProcessingRates
+ }
+ return nil
+}
+
+func (x *MonoVertexMetrics) GetPendings() map[string]*wrapperspb.Int64Value {
+ if x != nil {
+ return x.Pendings
+ }
+ return nil
+}
+
+type GetMonoVertexMetricsResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Metrics *MonoVertexMetrics `protobuf:"bytes,1,opt,name=metrics,proto3" json:"metrics,omitempty"`
+}
+
+func (x *GetMonoVertexMetricsResponse) Reset() {
+ *x = GetMonoVertexMetricsResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *GetMonoVertexMetricsResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetMonoVertexMetricsResponse) ProtoMessage() {}
+
+func (x *GetMonoVertexMetricsResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetMonoVertexMetricsResponse.ProtoReflect.Descriptor instead.
+func (*GetMonoVertexMetricsResponse) Descriptor() ([]byte, []int) {
+ return file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *GetMonoVertexMetricsResponse) GetMetrics() *MonoVertexMetrics {
+ if x != nil {
+ return x.Metrics
+ }
+ return nil
+}
+
+var File_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto protoreflect.FileDescriptor
+
+var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDesc = []byte{
+ 0x0a, 0x2a, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x2f, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d, 0x76, 0x74, 0x78,
+ 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6d, 0x76,
+ 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x22, 0x96, 0x03, 0x0a, 0x11, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74,
+ 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x6f, 0x6e,
+ 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
+ 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x12, 0x5c, 0x0a, 0x0f, 0x70, 0x72, 0x6f,
+ 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
+ 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x61, 0x74, 0x65,
+ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69,
+ 0x6e, 0x67, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x08, 0x70, 0x65, 0x6e, 0x64, 0x69,
+ 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6d, 0x76, 0x74, 0x78,
+ 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65,
+ 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
+ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73,
+ 0x1a, 0x60, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x61,
+ 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62,
+ 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+ 0x38, 0x01, 0x1a, 0x58, 0x0a, 0x0d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x45, 0x6e,
+ 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75,
+ 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x57, 0x0a, 0x1c,
+ 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x4d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07,
+ 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
+ 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x6f, 0x6e, 0x6f, 0x56,
+ 0x65, 0x72, 0x74, 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07, 0x6d, 0x65,
+ 0x74, 0x72, 0x69, 0x63, 0x73, 0x32, 0x8c, 0x01, 0x0a, 0x17, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65,
+ 0x72, 0x74, 0x65, 0x78, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x12, 0x71, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74,
+ 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+ 0x79, 0x1a, 0x28, 0x2e, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47,
+ 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72,
+ 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4,
+ 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x6e, 0x75, 0x6d, 0x61, 0x70, 0x72, 0x6f, 0x6a, 0x2f, 0x6e, 0x75, 0x6d, 0x61,
+ 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescOnce sync.Once
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescData = file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDesc
+)
+
+func file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescGZIP() []byte {
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescOnce.Do(func() {
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescData)
+ })
+ return file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescData
+}
+
+var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_goTypes = []any{
+ (*MonoVertexMetrics)(nil), // 0: mvtxdaemon.MonoVertexMetrics
+ (*GetMonoVertexMetricsResponse)(nil), // 1: mvtxdaemon.GetMonoVertexMetricsResponse
+ nil, // 2: mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry
+ nil, // 3: mvtxdaemon.MonoVertexMetrics.PendingsEntry
+ (*wrapperspb.DoubleValue)(nil), // 4: google.protobuf.DoubleValue
+ (*wrapperspb.Int64Value)(nil), // 5: google.protobuf.Int64Value
+ (*emptypb.Empty)(nil), // 6: google.protobuf.Empty
+}
+var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_depIdxs = []int32{
+ 2, // 0: mvtxdaemon.MonoVertexMetrics.processingRates:type_name -> mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry
+ 3, // 1: mvtxdaemon.MonoVertexMetrics.pendings:type_name -> mvtxdaemon.MonoVertexMetrics.PendingsEntry
+ 0, // 2: mvtxdaemon.GetMonoVertexMetricsResponse.metrics:type_name -> mvtxdaemon.MonoVertexMetrics
+ 4, // 3: mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry.value:type_name -> google.protobuf.DoubleValue
+ 5, // 4: mvtxdaemon.MonoVertexMetrics.PendingsEntry.value:type_name -> google.protobuf.Int64Value
+ 6, // 5: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexMetrics:input_type -> google.protobuf.Empty
+ 1, // 6: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexMetrics:output_type -> mvtxdaemon.GetMonoVertexMetricsResponse
+ 6, // [6:7] is the sub-list for method output_type
+ 5, // [5:6] is the sub-list for method input_type
+ 5, // [5:5] is the sub-list for extension type_name
+ 5, // [5:5] is the sub-list for extension extendee
+ 0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_init() }
+func file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_init() {
+ if File_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[0].Exporter = func(v any, i int) any {
+ switch v := v.(*MonoVertexMetrics); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[1].Exporter = func(v any, i int) any {
+ switch v := v.(*GetMonoVertexMetricsResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_goTypes,
+ DependencyIndexes: file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_depIdxs,
+ MessageInfos: file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes,
+ }.Build()
+ File_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto = out.File
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDesc = nil
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_goTypes = nil
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_depIdxs = nil
+}
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go
new file mode 100644
index 0000000000..97c8075676
--- /dev/null
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go
@@ -0,0 +1,156 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
+
+/*
+Package mvtxdaemon is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package mvtxdaemon
+
+import (
+ "context"
+ "io"
+ "net/http"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/known/emptypb"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = metadata.Join
+
+func request_MonoVertexDaemonService_GetMonoVertexMetrics_0(ctx context.Context, marshaler runtime.Marshaler, client MonoVertexDaemonServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq emptypb.Empty
+ var metadata runtime.ServerMetadata
+
+ msg, err := client.GetMonoVertexMetrics(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_MonoVertexDaemonService_GetMonoVertexMetrics_0(ctx context.Context, marshaler runtime.Marshaler, server MonoVertexDaemonServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq emptypb.Empty
+ var metadata runtime.ServerMetadata
+
+ msg, err := server.GetMonoVertexMetrics(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+// RegisterMonoVertexDaemonServiceHandlerServer registers the http handlers for service MonoVertexDaemonService to "mux".
+// UnaryRPC :call MonoVertexDaemonServiceServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMonoVertexDaemonServiceHandlerFromEndpoint instead.
+func RegisterMonoVertexDaemonServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MonoVertexDaemonServiceServer) error {
+
+ mux.Handle("GET", pattern_MonoVertexDaemonService_GetMonoVertexMetrics_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/mvtxdaemon.MonoVertexDaemonService/GetMonoVertexMetrics", runtime.WithHTTPPathPattern("/api/v1/metrics"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_MonoVertexDaemonService_GetMonoVertexMetrics_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_MonoVertexDaemonService_GetMonoVertexMetrics_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+// RegisterMonoVertexDaemonServiceHandlerFromEndpoint is same as RegisterMonoVertexDaemonServiceHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterMonoVertexDaemonServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+ conn, err := grpc.DialContext(ctx, endpoint, opts...)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ return
+ }
+ go func() {
+ <-ctx.Done()
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ }()
+ }()
+
+ return RegisterMonoVertexDaemonServiceHandler(ctx, mux, conn)
+}
+
+// RegisterMonoVertexDaemonServiceHandler registers the http handlers for service MonoVertexDaemonService to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterMonoVertexDaemonServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return RegisterMonoVertexDaemonServiceHandlerClient(ctx, mux, NewMonoVertexDaemonServiceClient(conn))
+}
+
+// RegisterMonoVertexDaemonServiceHandlerClient registers the http handlers for service MonoVertexDaemonService
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MonoVertexDaemonServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MonoVertexDaemonServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "MonoVertexDaemonServiceClient" to call the correct interceptors.
+func RegisterMonoVertexDaemonServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MonoVertexDaemonServiceClient) error {
+
+ mux.Handle("GET", pattern_MonoVertexDaemonService_GetMonoVertexMetrics_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/mvtxdaemon.MonoVertexDaemonService/GetMonoVertexMetrics", runtime.WithHTTPPathPattern("/api/v1/metrics"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_MonoVertexDaemonService_GetMonoVertexMetrics_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_MonoVertexDaemonService_GetMonoVertexMetrics_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+var (
+ pattern_MonoVertexDaemonService_GetMonoVertexMetrics_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "metrics"}, ""))
+)
+
+var (
+ forward_MonoVertexDaemonService_GetMonoVertexMetrics_0 = runtime.ForwardResponseMessage
+)
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
new file mode 100644
index 0000000000..512b5bf515
--- /dev/null
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
@@ -0,0 +1,47 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+syntax = "proto3";
+
+option go_package = "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon";
+
+import "google/api/annotations.proto";
+import "google/protobuf/empty.proto";
+import "google/protobuf/wrappers.proto";
+
+package mvtxdaemon;
+
+// MonoVertexMetrics is used to provide information about the mono vertex including processing rate.
+message MonoVertexMetrics {
+ string monoVertex = 1;
+ // Processing rate in the past period of time, 1m, 5m, 15m, default
+ map processingRates = 2;
+ // Pending in the past period of time, 1m, 5m, 15m, default
+ map pendings = 3;
+}
+
+message GetMonoVertexMetricsResponse {
+ MonoVertexMetrics metrics = 1;
+}
+
+// MonoVertexDaemonService is a grpc service that is used to provide APIs for giving any MonoVertex information.
+service MonoVertexDaemonService {
+
+ rpc GetMonoVertexMetrics (google.protobuf.Empty) returns (GetMonoVertexMetricsResponse) {
+ option (google.api.http).get = "/api/v1/metrics";
+ };
+
+}
\ No newline at end of file
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go b/pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go
new file mode 100644
index 0000000000..1dd50188d7
--- /dev/null
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go
@@ -0,0 +1,126 @@
+//
+//Copyright 2022 The Numaproj Authors.
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc v5.27.2
+// source: pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
+
+package mvtxdaemon
+
+import (
+ context "context"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ emptypb "google.golang.org/protobuf/types/known/emptypb"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+ MonoVertexDaemonService_GetMonoVertexMetrics_FullMethodName = "/mvtxdaemon.MonoVertexDaemonService/GetMonoVertexMetrics"
+)
+
+// MonoVertexDaemonServiceClient is the client API for MonoVertexDaemonService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type MonoVertexDaemonServiceClient interface {
+ GetMonoVertexMetrics(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetMonoVertexMetricsResponse, error)
+}
+
+type monoVertexDaemonServiceClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewMonoVertexDaemonServiceClient(cc grpc.ClientConnInterface) MonoVertexDaemonServiceClient {
+ return &monoVertexDaemonServiceClient{cc}
+}
+
+func (c *monoVertexDaemonServiceClient) GetMonoVertexMetrics(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetMonoVertexMetricsResponse, error) {
+ out := new(GetMonoVertexMetricsResponse)
+ err := c.cc.Invoke(ctx, MonoVertexDaemonService_GetMonoVertexMetrics_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// MonoVertexDaemonServiceServer is the server API for MonoVertexDaemonService service.
+// All implementations must embed UnimplementedMonoVertexDaemonServiceServer
+// for forward compatibility
+type MonoVertexDaemonServiceServer interface {
+ GetMonoVertexMetrics(context.Context, *emptypb.Empty) (*GetMonoVertexMetricsResponse, error)
+ mustEmbedUnimplementedMonoVertexDaemonServiceServer()
+}
+
+// UnimplementedMonoVertexDaemonServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedMonoVertexDaemonServiceServer struct {
+}
+
+func (UnimplementedMonoVertexDaemonServiceServer) GetMonoVertexMetrics(context.Context, *emptypb.Empty) (*GetMonoVertexMetricsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GetMonoVertexMetrics not implemented")
+}
+func (UnimplementedMonoVertexDaemonServiceServer) mustEmbedUnimplementedMonoVertexDaemonServiceServer() {
+}
+
+// UnsafeMonoVertexDaemonServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to MonoVertexDaemonServiceServer will
+// result in compilation errors.
+type UnsafeMonoVertexDaemonServiceServer interface {
+ mustEmbedUnimplementedMonoVertexDaemonServiceServer()
+}
+
+func RegisterMonoVertexDaemonServiceServer(s grpc.ServiceRegistrar, srv MonoVertexDaemonServiceServer) {
+ s.RegisterService(&MonoVertexDaemonService_ServiceDesc, srv)
+}
+
+func _MonoVertexDaemonService_GetMonoVertexMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(emptypb.Empty)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MonoVertexDaemonServiceServer).GetMonoVertexMetrics(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: MonoVertexDaemonService_GetMonoVertexMetrics_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MonoVertexDaemonServiceServer).GetMonoVertexMetrics(ctx, req.(*emptypb.Empty))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// MonoVertexDaemonService_ServiceDesc is the grpc.ServiceDesc for MonoVertexDaemonService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var MonoVertexDaemonService_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "mvtxdaemon.MonoVertexDaemonService",
+ HandlerType: (*MonoVertexDaemonServiceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "GetMonoVertexMetrics",
+ Handler: _MonoVertexDaemonService_GetMonoVertexMetrics_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto",
+}
diff --git a/pkg/daemon/server/service/rater/pod_tracker.go b/pkg/daemon/server/service/rater/pod_tracker.go
index c693630941..bcda964ebc 100644
--- a/pkg/daemon/server/service/rater/pod_tracker.go
+++ b/pkg/daemon/server/service/rater/pod_tracker.go
@@ -28,6 +28,7 @@ import (
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/shared/logging"
+ "github.com/numaproj/numaflow/pkg/shared/util"
)
// podInfoSeparator is used as a separator to split the pod key
@@ -41,7 +42,7 @@ type PodTracker struct {
pipeline *v1alpha1.Pipeline
log *zap.SugaredLogger
httpClient metricsHttpClient
- activePods *UniqueStringList
+ activePods *util.UniqueStringList
refreshInterval time.Duration
}
@@ -55,7 +56,7 @@ func NewPodTracker(ctx context.Context, p *v1alpha1.Pipeline, opts ...PodTracker
},
Timeout: time.Second,
},
- activePods: NewUniqueStringList(),
+ activePods: util.NewUniqueStringList(),
refreshInterval: 30 * time.Second, // Default refresh interval for updating the active pod set
}
diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go
index be3b34aff7..f4e394004e 100644
--- a/pkg/metrics/metrics.go
+++ b/pkg/metrics/metrics.go
@@ -28,10 +28,10 @@ const (
LabelISBService = "isbsvc"
LabelPipeline = "pipeline"
LabelVertex = "vertex"
- LabelMonoVertex = "mono_vertex"
LabelVertexReplicaIndex = "replica"
LabelVertexType = "vertex_type"
LabelPartitionName = "partition_name"
+ LabelMonoVertexName = "mvtx_name"
LabelReason = "reason"
)
diff --git a/pkg/mvtxdaemon/client/doc.go b/pkg/mvtxdaemon/client/doc.go
new file mode 100644
index 0000000000..8e7e39073b
--- /dev/null
+++ b/pkg/mvtxdaemon/client/doc.go
@@ -0,0 +1,26 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package client is used to create the MonoVertex daemon service client.
+//
+// There are 2 clients available.
+//
+// 1. gRPC client
+// func NewGRPCClient(address string) (MonoVertexDaemonClient, error)
+//
+// 2. RESTful client
+// func NewRESTfulClient(address string) (MonoVertexDaemonClient, error)
+package client
diff --git a/pkg/mvtxdaemon/client/grpc_client.go b/pkg/mvtxdaemon/client/grpc_client.go
new file mode 100644
index 0000000000..c7fb80d6fc
--- /dev/null
+++ b/pkg/mvtxdaemon/client/grpc_client.go
@@ -0,0 +1,63 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+ "crypto/tls"
+
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/protobuf/types/known/emptypb"
+
+ "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon"
+)
+
+type grpcClient struct {
+ client mvtxdaemon.MonoVertexDaemonServiceClient
+ conn *grpc.ClientConn
+}
+
+var _ MonoVertexDaemonClient = (*grpcClient)(nil)
+
+func NewGRPCClient(address string) (MonoVertexDaemonClient, error) {
+ config := &tls.Config{
+ InsecureSkipVerify: true,
+ }
+ conn, err := grpc.Dial(address, grpc.WithTransportCredentials(credentials.NewTLS(config)))
+ if err != nil {
+ return nil, err
+ }
+ daemonClient := mvtxdaemon.NewMonoVertexDaemonServiceClient(conn)
+ return &grpcClient{conn: conn, client: daemonClient}, nil
+}
+
+func (dc *grpcClient) GetMonoVertexMetrics(ctx context.Context) (*mvtxdaemon.MonoVertexMetrics, error) {
+ if rspn, err := dc.client.GetMonoVertexMetrics(ctx, &emptypb.Empty{}); err != nil {
+ return nil, err
+ } else {
+ return rspn.Metrics, nil
+ }
+}
+
+// Close function closes the gRPC connection, it has to be called after a daemon client has finished all its jobs.
+func (dc *grpcClient) Close() error {
+ if dc.conn != nil {
+ return dc.conn.Close()
+ }
+ return nil
+}
diff --git a/pkg/mvtxdaemon/client/grpc_client_test.go b/pkg/mvtxdaemon/client/grpc_client_test.go
new file mode 100644
index 0000000000..b11582d4fd
--- /dev/null
+++ b/pkg/mvtxdaemon/client/grpc_client_test.go
@@ -0,0 +1,17 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
diff --git a/pkg/mvtxdaemon/client/interface.go b/pkg/mvtxdaemon/client/interface.go
new file mode 100644
index 0000000000..71a1a4aeaf
--- /dev/null
+++ b/pkg/mvtxdaemon/client/interface.go
@@ -0,0 +1,29 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+ "io"
+
+ "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon"
+)
+
+type MonoVertexDaemonClient interface {
+ io.Closer
+ GetMonoVertexMetrics(ctx context.Context) (*mvtxdaemon.MonoVertexMetrics, error)
+}
diff --git a/pkg/mvtxdaemon/client/restful_client.go b/pkg/mvtxdaemon/client/restful_client.go
new file mode 100644
index 0000000000..7409e19734
--- /dev/null
+++ b/pkg/mvtxdaemon/client/restful_client.go
@@ -0,0 +1,91 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+
+ "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon"
+)
+
+var (
+ // Use JSONPb to unmarshal the response, it is needed to unmarshal the response with google.protobuf.* data types.
+ jsonMarshaller = new(runtime.JSONPb)
+)
+
+type restfulClient struct {
+ hostURL string
+ httpClient *http.Client
+}
+
+var _ MonoVertexDaemonClient = (*restfulClient)(nil)
+
+func NewRESTfulClient(address string) (MonoVertexDaemonClient, error) {
+ if !strings.HasPrefix(address, "https://") {
+ address = "https://" + address
+ }
+ client := &restfulClient{
+ hostURL: address,
+ httpClient: &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ },
+ Timeout: time.Second * 1,
+ },
+ }
+ return client, nil
+}
+
+func (rc *restfulClient) Close() error {
+ return nil
+}
+
+func unmarshalResponse[T any](r *http.Response) (*T, error) {
+ if r.StatusCode >= 300 {
+ return nil, fmt.Errorf("unexpected response %v: %s", r.StatusCode, r.Status)
+ }
+ data, err := io.ReadAll(r.Body)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read data from response body, %w", err)
+ }
+ var t T
+ if err := jsonMarshaller.Unmarshal(data, &t); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal response body to %T, %w", t, err)
+ }
+ return &t, nil
+}
+
+func (rc *restfulClient) GetMonoVertexMetrics(ctx context.Context) (*mvtxdaemon.MonoVertexMetrics, error) {
+ resp, err := rc.httpClient.Get(fmt.Sprintf("%s/api/v1/metrics", rc.hostURL))
+ if err != nil {
+ return nil, fmt.Errorf("failed to call get mono vertex metrics RESTful API, %w", err)
+ }
+ defer func() { _ = resp.Body.Close() }()
+ if res, err := unmarshalResponse[mvtxdaemon.GetMonoVertexMetricsResponse](resp); err != nil {
+ return nil, err
+ } else {
+ return res.Metrics, nil
+ }
+}
diff --git a/pkg/mvtxdaemon/client/restful_client_test.go b/pkg/mvtxdaemon/client/restful_client_test.go
new file mode 100644
index 0000000000..b11582d4fd
--- /dev/null
+++ b/pkg/mvtxdaemon/client/restful_client_test.go
@@ -0,0 +1,17 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
diff --git a/pkg/mvtxdaemon/server/daemon_server.go b/pkg/mvtxdaemon/server/daemon_server.go
index 29553493e6..f6ce1c9ec7 100644
--- a/pkg/mvtxdaemon/server/daemon_server.go
+++ b/pkg/mvtxdaemon/server/daemon_server.go
@@ -37,7 +37,9 @@ import (
"github.com/numaproj/numaflow"
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
- "github.com/numaproj/numaflow/pkg/apis/proto/daemon"
+ "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon"
+ "github.com/numaproj/numaflow/pkg/mvtxdaemon/server/service"
+ rateServer "github.com/numaproj/numaflow/pkg/mvtxdaemon/server/service/rater"
"github.com/numaproj/numaflow/pkg/shared/logging"
sharedtls "github.com/numaproj/numaflow/pkg/shared/tls"
)
@@ -57,6 +59,8 @@ func (ds *daemonServer) Run(ctx context.Context) error {
var (
err error
)
+ // rater is used to calculate the processing rate of the mono vertex
+ rater := rateServer.NewRater(ctx, ds.monoVtx)
// Start listener
var conn net.Listener
@@ -73,11 +77,11 @@ func (ds *daemonServer) Run(ctx context.Context) error {
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{*cer}, MinVersion: tls.VersionTLS12}
- grpcServer, err := ds.newGRPCServer()
+ grpcServer, err := ds.newGRPCServer(rater)
if err != nil {
return fmt.Errorf("failed to create grpc server: %w", err)
}
- httpServer := ds.newHTTPServer(ctx, v1alpha1.DaemonServicePort, tlsConfig)
+ httpServer := ds.newHTTPServer(ctx, v1alpha1.MonoVertexDaemonServicePort, tlsConfig)
conn = tls.NewListener(conn, tlsConfig)
// Cmux is used to support servicing gRPC and HTTP1.1+JSON on the same port
@@ -89,15 +93,22 @@ func (ds *daemonServer) Run(ctx context.Context) error {
go func() { _ = httpServer.Serve(httpL) }()
go func() { _ = tcpm.Serve() }()
+ // Start the rater
+ go func() {
+ if err := rater.Start(ctx); err != nil {
+ log.Panic(fmt.Errorf("failed to start the rater: %w", err))
+ }
+ }()
+
version := numaflow.GetVersion()
- mono_vertex_info.WithLabelValues(version.Version, version.Platform, ds.monoVtx.Name).Set(1)
+ monoVertexInfo.WithLabelValues(version.Version, version.Platform, ds.monoVtx.Name).Set(1)
log.Infof("MonoVertex daemon server started successfully on %s", address)
<-ctx.Done()
return nil
}
-func (ds *daemonServer) newGRPCServer() (*grpc.Server, error) {
+func (ds *daemonServer) newGRPCServer(rater rateServer.MonoVtxRatable) (*grpc.Server, error) {
// "Prometheus histograms are a great way to measure latency distributions of your RPCs.
// However, since it is a bad practice to have metrics of high cardinality the latency monitoring metrics are disabled by default.
// To enable them please call the following in your server initialization code:"
@@ -111,6 +122,11 @@ func (ds *daemonServer) newGRPCServer() (*grpc.Server, error) {
}
grpcServer := grpc.NewServer(sOpts...)
grpc_prometheus.Register(grpcServer)
+ mvtxService, err := service.NewMoveVertexService(ds.monoVtx, rater)
+ if err != nil {
+ return nil, err
+ }
+ mvtxdaemon.RegisterMonoVertexDaemonServiceServer(grpcServer, mvtxService)
return grpcServer, nil
}
@@ -133,7 +149,7 @@ func (ds *daemonServer) newHTTPServer(ctx context.Context, port int, tlsConfig *
return key, true
}),
)
- if err := daemon.RegisterDaemonServiceHandlerFromEndpoint(ctx, gwmux, endpoint, dialOpts); err != nil {
+ if err := mvtxdaemon.RegisterMonoVertexDaemonServiceHandlerFromEndpoint(ctx, gwmux, endpoint, dialOpts); err != nil {
log.Errorw("Failed to register daemon handler on HTTP Server", zap.Error(err))
}
mux := http.NewServeMux()
diff --git a/pkg/mvtxdaemon/server/metrics.go b/pkg/mvtxdaemon/server/metrics.go
index f0aa155c31..f3c0c30796 100644
--- a/pkg/mvtxdaemon/server/metrics.go
+++ b/pkg/mvtxdaemon/server/metrics.go
@@ -24,9 +24,9 @@ import (
)
var (
- mono_vertex_info = promauto.NewGaugeVec(prometheus.GaugeOpts{
+ monoVertexInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
Subsystem: "monovtx",
Name: "build_info",
Help: "A metric with a constant value '1', labeled by Numaflow binary version and platform, as well as the mono vertex name",
- }, []string{metrics.LabelVersion, metrics.LabelPlatform, metrics.LabelMonoVertex})
+ }, []string{metrics.LabelVersion, metrics.LabelPlatform, metrics.LabelMonoVertexName})
)
diff --git a/pkg/mvtxdaemon/server/service/mvtx_service.go b/pkg/mvtxdaemon/server/service/mvtx_service.go
new file mode 100644
index 0000000000..40a2b2972c
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/mvtx_service.go
@@ -0,0 +1,119 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package service
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "net/http"
+ "time"
+
+ "go.uber.org/zap"
+ "google.golang.org/protobuf/types/known/emptypb"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+
+ "github.com/prometheus/common/expfmt"
+
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon"
+ "github.com/numaproj/numaflow/pkg/metrics"
+ raterPkg "github.com/numaproj/numaflow/pkg/mvtxdaemon/server/service/rater"
+ "github.com/numaproj/numaflow/pkg/shared/logging"
+)
+
+// MonoVtxPendingMetric is the metric emitted from the MonoVtx lag reader for pending stats
+// Note: Please keep consistent with the definitions in rust/monovertex/sc/metrics.rs
+const MonoVtxPendingMetric = "monovtx_pending"
+
+type MoveVertexService struct {
+ mvtxdaemon.UnimplementedMonoVertexDaemonServiceServer
+ monoVtx *v1alpha1.MonoVertex
+ httpClient *http.Client
+ rater raterPkg.MonoVtxRatable
+}
+
+var _ mvtxdaemon.MonoVertexDaemonServiceServer = (*MoveVertexService)(nil)
+
+// NewMoveVertexService returns a new instance of MoveVertexService
+func NewMoveVertexService(
+ monoVtx *v1alpha1.MonoVertex,
+ rater raterPkg.MonoVtxRatable,
+) (*MoveVertexService, error) {
+ mv := MoveVertexService{
+ monoVtx: monoVtx,
+ httpClient: &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ },
+ Timeout: time.Second * 3,
+ },
+ rater: rater,
+ }
+ return &mv, nil
+}
+
+func (mvs *MoveVertexService) GetMonoVertexMetrics(ctx context.Context, empty *emptypb.Empty) (*mvtxdaemon.GetMonoVertexMetricsResponse, error) {
+ resp := new(mvtxdaemon.GetMonoVertexMetricsResponse)
+ collectedMetrics := new(mvtxdaemon.MonoVertexMetrics)
+ collectedMetrics.MonoVertex = mvs.monoVtx.Name
+ collectedMetrics.Pendings = mvs.getPending(ctx)
+ collectedMetrics.ProcessingRates = mvs.rater.GetRates()
+ resp.Metrics = collectedMetrics
+ return resp, nil
+}
+
+// getPending returns the pending count for the mono vertex
+func (mvs *MoveVertexService) getPending(ctx context.Context) map[string]*wrapperspb.Int64Value {
+ log := logging.FromContext(ctx)
+ headlessServiceName := mvs.monoVtx.GetHeadlessServiceName()
+ pendingMap := make(map[string]*wrapperspb.Int64Value)
+
+ // Get the headless service name
+ // We can query the metrics endpoint of the (i)th pod to obtain this value.
+ // example for 0th pod : https://simple-mono-vertex-mv-0.simple-mono-vertex-mv-headless:2469/metrics
+ url := fmt.Sprintf("https://%s-mv-0.%s.%s.svc:%v/metrics", mvs.monoVtx.Name, headlessServiceName, mvs.monoVtx.Namespace, v1alpha1.MonoVertexMetricsPort)
+ if res, err := mvs.httpClient.Get(url); err != nil {
+ log.Debugf("Error reading the metrics endpoint, it might be because of mono vertex scaling down to 0: %f", err.Error())
+ return nil
+ } else {
+ // expfmt Parser from prometheus to parse the metrics
+ textParser := expfmt.TextParser{}
+ result, err := textParser.TextToMetricFamilies(res.Body)
+ if err != nil {
+ log.Errorw("Error in parsing to prometheus metric families", zap.Error(err))
+ return nil
+ }
+
+ // Get the pending messages
+ if value, ok := result[MonoVtxPendingMetric]; ok {
+ metricsList := value.GetMetric()
+ for _, metric := range metricsList {
+ labels := metric.GetLabel()
+ lookback := ""
+ for _, label := range labels {
+ if label.GetName() == metrics.LabelPeriod {
+ lookback = label.GetValue()
+ break
+ }
+ }
+ pendingMap[lookback] = wrapperspb.Int64(int64(metric.Gauge.GetValue()))
+ }
+ }
+ }
+ return pendingMap
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/goleak_test.go b/pkg/mvtxdaemon/server/service/rater/goleak_test.go
new file mode 100644
index 0000000000..58abf6f6be
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/goleak_test.go
@@ -0,0 +1,28 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "testing"
+
+ "go.uber.org/goleak"
+)
+
+// apply go leak verification to all tests in this package
+func TestMain(m *testing.M) {
+ goleak.VerifyTestMain(m)
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/helper.go b/pkg/mvtxdaemon/server/service/rater/helper.go
new file mode 100644
index 0000000000..0973b3c5cb
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/helper.go
@@ -0,0 +1,123 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "time"
+
+ sharedqueue "github.com/numaproj/numaflow/pkg/shared/queue"
+)
+
+const (
+ // indexNotFound is returned when the start index cannot be found in the queue.
+ indexNotFound = -1
+)
+
+// UpdateCount updates the count for a given timestamp in the queue.
+func UpdateCount(q *sharedqueue.OverflowQueue[*TimestampedCounts], time int64, podReadCounts *PodReadCount) {
+ items := q.Items()
+
+ // find the element matching the input timestamp and update it
+ for _, i := range items {
+ if i.timestamp == time {
+ i.Update(podReadCounts)
+ return
+ }
+ }
+
+ // if we cannot find a matching element, it means we need to add a new timestamped count to the queue
+ tc := NewTimestampedCounts(time)
+ tc.Update(podReadCounts)
+ q.Append(tc)
+}
+
+// CalculateRate calculates the rate of a MonoVertex for a given lookback period.
+func CalculateRate(q *sharedqueue.OverflowQueue[*TimestampedCounts], lookbackSeconds int64) float64 {
+ counts := q.Items()
+ if len(counts) <= 1 {
+ return 0
+ }
+ startIndex := findStartIndex(lookbackSeconds, counts)
+ // we consider the last but one element as the end index because the last element might be incomplete
+ // we can be sure that the last but one element in the queue is complete.
+ endIndex := len(counts) - 2
+ if startIndex == indexNotFound {
+ return 0
+ }
+
+ // time diff in seconds.
+ timeDiff := counts[endIndex].timestamp - counts[startIndex].timestamp
+ if timeDiff == 0 {
+ // if the time difference is 0, we return 0 to avoid division by 0
+ // this should not happen in practice because we are using a 10s interval
+ return 0
+ }
+
+ delta := float64(0)
+ for i := startIndex; i < endIndex; i++ {
+ // calculate the difference between the current and previous pod count snapshots
+ delta += calculatePodDelta(counts[i], counts[i+1])
+ }
+ return delta / float64(timeDiff)
+}
+
+// findStartIndex finds the index of the first element in the queue that is within the lookback seconds
+func findStartIndex(lookbackSeconds int64, counts []*TimestampedCounts) int {
+ n := len(counts)
+ now := time.Now().Truncate(CountWindow).Unix()
+ if n < 2 || now-counts[n-2].timestamp > lookbackSeconds {
+ // if the second last element is already outside the lookback window, we return indexNotFound
+ return indexNotFound
+ }
+
+ startIndex := n - 2
+ left := 0
+ right := n - 2
+ lastTimestamp := now - lookbackSeconds
+ for left <= right {
+ mid := left + (right-left)/2
+ if counts[mid].timestamp >= lastTimestamp {
+ startIndex = mid
+ right = mid - 1
+ } else {
+ left = mid + 1
+ }
+ }
+ return startIndex
+}
+
+// calculatePodDelta calculates the difference between the current and previous pod count snapshots
+func calculatePodDelta(tc1, tc2 *TimestampedCounts) float64 {
+ delta := float64(0)
+ if tc1 == nil || tc2 == nil {
+ // we calculate delta only when both input timestamped counts are non-nil
+ return delta
+ }
+ prevPodReadCount := tc1.PodCountSnapshot()
+ currPodReadCount := tc2.PodCountSnapshot()
+ for podName, readCount := range currPodReadCount {
+ currCount := readCount
+ prevCount := prevPodReadCount[podName]
+ // pod delta will be equal to current count in case of restart
+ podDelta := currCount
+ if currCount >= prevCount {
+ podDelta = currCount - prevCount
+ }
+ delta += podDelta
+ }
+ return delta
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/helper_test.go b/pkg/mvtxdaemon/server/service/rater/helper_test.go
new file mode 100644
index 0000000000..6ac878244c
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/helper_test.go
@@ -0,0 +1,288 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+
+ sharedqueue "github.com/numaproj/numaflow/pkg/shared/queue"
+)
+
+const TestTime = 1620000000
+
+func TestUpdateCount(t *testing.T) {
+ t.Run("givenTimeExistsPodExistsCountAvailable_whenUpdate_thenUpdatePodPartitionCount", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ q.Append(tc)
+
+ UpdateCount(q, TestTime, &PodReadCount{"pod1", 20.0})
+
+ assert.Equal(t, 1, q.Length())
+ assert.Equal(t, 20.0, q.Items()[0].podReadCounts["pod1"])
+ })
+
+ t.Run("givenTimeExistsPodNotExistsCountAvailable_whenUpdate_thenAddPodCount", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 20.0})
+ q.Append(tc)
+
+ UpdateCount(q, TestTime, &PodReadCount{"pod2", 10.0})
+
+ assert.Equal(t, 1, q.Length())
+ assert.Equal(t, 20.0, q.Items()[0].podReadCounts["pod1"])
+ assert.Equal(t, 10.0, q.Items()[0].podReadCounts["pod2"])
+ })
+
+ t.Run("givenTimeExistsPodExistsCountNotAvailable_whenUpdate_thenNotUpdatePod", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ q.Append(tc)
+
+ UpdateCount(q, TestTime, nil)
+
+ assert.Equal(t, 1, q.Length())
+ assert.Equal(t, 1, len(q.Items()[0].podReadCounts))
+ assert.Equal(t, 10.0, q.Items()[0].podReadCounts["pod1"])
+ })
+
+ t.Run("givenTimeExistsPodNotExistsCountNotAvailable_whenUpdate_thenNoUpdate", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ q.Append(tc)
+
+ UpdateCount(q, TestTime, nil)
+
+ assert.Equal(t, 1, q.Length())
+ assert.Equal(t, 10.0, q.Items()[0].podReadCounts["pod1"])
+ })
+
+ t.Run("givenTimeNotExistsCountAvailable_whenUpdate_thenAddNewItem", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ q.Append(tc)
+
+ UpdateCount(q, TestTime+1, &PodReadCount{"pod1", 20.0})
+
+ assert.Equal(t, 2, q.Length())
+ assert.Equal(t, 10.0, q.Items()[0].podReadCounts["pod1"])
+ assert.Equal(t, 20.0, q.Items()[1].podReadCounts["pod1"])
+ })
+
+ t.Run("givenTimeNotExistsCountNotAvailable_whenUpdate_thenAddEmptyItem", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ q.Append(tc)
+
+ UpdateCount(q, TestTime+1, nil)
+
+ assert.Equal(t, 2, q.Length())
+ assert.Equal(t, 10.0, q.Items()[0].podReadCounts["pod1"])
+ assert.Equal(t, 0, len(q.Items()[1].podReadCounts))
+ })
+}
+
+func TestCalculateRate(t *testing.T) {
+ t.Run("givenCollectedTimeLessThanTwo_whenCalculateRate_thenReturnZero", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ // no data
+ assert.Equal(t, 0.0, CalculateRate(q, 10))
+
+ // only one data
+ now := time.Now()
+ tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
+ tc1.Update(&PodReadCount{"pod1", 5.0})
+ q.Append(tc1)
+ assert.Equal(t, 0.0, CalculateRate(q, 10))
+ })
+
+ t.Run("singlePod_givenCountIncreases_whenCalculateRate_thenReturnRate", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ now := time.Now()
+
+ tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
+ tc1.Update(&PodReadCount{"pod1", 5.0})
+ q.Append(tc1)
+ tc2 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 10)
+ tc2.Update(&PodReadCount{"pod1", 10.0})
+ q.Append(tc2)
+ tc3 := NewTimestampedCounts(now.Truncate(CountWindow).Unix())
+ tc3.Update(&PodReadCount{"pod1", 20.0})
+ q.Append(tc3)
+
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 5))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 15))
+ // tc1 and tc2 are used to calculate the rate
+ assert.Equal(t, 0.5, CalculateRate(q, 25))
+ // tc1 and tc2 are used to calculate the rate
+ assert.Equal(t, 0.5, CalculateRate(q, 100))
+ })
+
+ t.Run("singlePod_givenCountDecreases_whenCalculateRate_thenReturnRate", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ now := time.Now()
+
+ tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 30)
+ tc1.Update(&PodReadCount{"pod1", 200.0})
+ q.Append(tc1)
+ tc2 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
+ tc2.Update(&PodReadCount{"pod1", 100.0})
+ q.Append(tc2)
+ tc3 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 10)
+ tc3.Update(&PodReadCount{"pod1", 50.0})
+ q.Append(tc3)
+ tc4 := NewTimestampedCounts(now.Truncate(CountWindow).Unix())
+ tc4.Update(&PodReadCount{"pod1", 80.0})
+ q.Append(tc4)
+
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 5))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 15))
+ // tc2 and tc3 are used to calculate the rate
+ assert.Equal(t, 5.0, CalculateRate(q, 25))
+ // tc1, 2 and 3 are used to calculate the rate
+ assert.Equal(t, 7.5, CalculateRate(q, 35))
+ // tc1, 2 and 3 are used to calculate the rate
+ assert.Equal(t, 7.5, CalculateRate(q, 100))
+ })
+
+ t.Run("multiplePods_givenCountIncreases_whenCalculateRate_thenReturnRate", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ now := time.Now()
+
+ tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 30)
+ tc1.Update(&PodReadCount{"pod1", 50.0})
+ tc1.Update(&PodReadCount{"pod2", 100.0})
+ q.Append(tc1)
+ tc2 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
+ tc2.Update(&PodReadCount{"pod1", 100.0})
+ tc2.Update(&PodReadCount{"pod2", 200.0})
+ q.Append(tc2)
+ tc3 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 10)
+ tc3.Update(&PodReadCount{"pod1", 200.0})
+ tc3.Update(&PodReadCount{"pod2", 300.0})
+ q.Append(tc3)
+
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 5))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 15))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 25))
+ // tc1 and tc2 are used to calculate the rate
+ assert.Equal(t, 15.0, CalculateRate(q, 35))
+ })
+
+ t.Run("multiplePods_givenCountDecreases_whenCalculateRate_thenReturnRate", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ now := time.Now()
+
+ tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 30)
+ tc1.Update(&PodReadCount{"pod1", 200.0})
+ tc1.Update(&PodReadCount{"pod2", 300.0})
+ q.Append(tc1)
+ tc2 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
+ tc2.Update(&PodReadCount{"pod1", 100.0})
+ tc2.Update(&PodReadCount{"pod2", 200.0})
+ q.Append(tc2)
+ tc3 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 10)
+ tc3.Update(&PodReadCount{"pod1", 50.0})
+ tc3.Update(&PodReadCount{"pod2", 100.0})
+ q.Append(tc3)
+
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 5))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 15))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 25))
+ // tc1 and tc2 are used to calculate the rate
+ assert.Equal(t, 30.0, CalculateRate(q, 35))
+ })
+
+ t.Run("multiplePods_givenOnePodRestarts_whenCalculateRate_thenReturnRate", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ now := time.Now()
+
+ tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 30)
+ tc1.Update(&PodReadCount{"pod1", 50.0})
+ tc1.Update(&PodReadCount{"pod2", 300.0})
+ q.Append(tc1)
+ tc2 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
+ tc2.Update(&PodReadCount{"pod1", 100.0})
+ tc2.Update(&PodReadCount{"pod2", 200.0})
+ q.Append(tc2)
+ tc3 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 10)
+ tc3.Update(&PodReadCount{"pod1", 200.0})
+ tc3.Update(&PodReadCount{"pod2", 100.0})
+ q.Append(tc3)
+
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 5))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 15))
+ // no enough data collected within lookback seconds, expect rate 0
+ assert.Equal(t, 0.0, CalculateRate(q, 25))
+ // tc1 and tc2 are used to calculate the rate
+ assert.Equal(t, 25.0, CalculateRate(q, 35))
+ })
+
+ t.Run("multiplePods_givenPodsComeAndGo_whenCalculateRate_thenReturnRate", func(t *testing.T) {
+ q := sharedqueue.New[*TimestampedCounts](1800)
+ now := time.Now()
+
+ tc1 := NewTimestampedCounts(now.Truncate(time.Second*10).Unix() - 30)
+ tc1.Update(&PodReadCount{"pod1", 200.0})
+ tc1.Update(&PodReadCount{"pod2", 90.0})
+ tc1.Update(&PodReadCount{"pod3", 50.0})
+ q.Append(tc1)
+ tc2 := NewTimestampedCounts(now.Truncate(time.Second*10).Unix() - 20)
+ tc2.Update(&PodReadCount{"pod1", 100.0})
+ tc2.Update(&PodReadCount{"pod2", 200.0})
+ q.Append(tc2)
+ tc3 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 10)
+ tc3.Update(&PodReadCount{"pod1", 50.0})
+ tc3.Update(&PodReadCount{"pod2", 300.0})
+ tc3.Update(&PodReadCount{"pod4", 100.0})
+ q.Append(tc3)
+
+ tc4 := NewTimestampedCounts(now.Truncate(CountWindow).Unix())
+ tc4.Update(&PodReadCount{"pod2", 400.0})
+ tc4.Update(&PodReadCount{"pod3", 200.0})
+ tc4.Update(&PodReadCount{"pod100", 200.0})
+ q.Append(tc4)
+
+ // vertex rate
+ assert.Equal(t, 0.0, CalculateRate(q, 5))
+ assert.Equal(t, 0.0, CalculateRate(q, 15))
+ assert.Equal(t, 25.0, CalculateRate(q, 25))
+ assert.Equal(t, 23.0, CalculateRate(q, 35))
+ assert.Equal(t, 23.0, CalculateRate(q, 100))
+ })
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/options.go b/pkg/mvtxdaemon/server/service/rater/options.go
new file mode 100644
index 0000000000..03fdc53d04
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/options.go
@@ -0,0 +1,53 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+type options struct {
+ // Number of workers working on collecting counts of processed messages.
+ workers int
+ // Time in milliseconds, each element in the work queue will be picked up in an interval of this period of time.
+ taskInterval int
+}
+
+type Option func(*options)
+
+func defaultOptions() *options {
+ // A simple example of how these numbers work together:
+ // Assuming we have 200 tasks, we have 20 workers, each worker will be responsible for approximately 10 tasks during one iteration.
+ // The task interval is 5 seconds, which means each task need to be picked up by a worker every 5 seconds.
+ // Hence, a worker needs to finish processing 1 task in 0.5 second.
+ // Translating to numaflow language, for a 200-pod pipeline, a worker needs to finish scraping 1 pod in 0.5 second, which is a reasonable number.
+ return &options{
+ workers: 20,
+ // ensure that each task is picked up at least once within a CountWindow by defining taskInterval as half of CountWindow.
+ // if a CountWindow misses one pod, when calculating the delta with the next window, for that specific pod,
+ // we will count the total processed count as delta, which is wrong and eventually leads to incorrect high processing rate.
+ taskInterval: int(CountWindow.Milliseconds() / 2),
+ }
+}
+
+func WithWorkers(n int) Option {
+ return func(o *options) {
+ o.workers = n
+ }
+}
+
+func WithTaskInterval(n int) Option {
+ return func(o *options) {
+ o.taskInterval = n
+ }
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/pod_tracker.go b/pkg/mvtxdaemon/server/service/rater/pod_tracker.go
new file mode 100644
index 0000000000..1a8a3fd2b4
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/pod_tracker.go
@@ -0,0 +1,178 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "crypto/tls"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+
+ "go.uber.org/zap"
+ "golang.org/x/net/context"
+
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ "github.com/numaproj/numaflow/pkg/shared/logging"
+ "github.com/numaproj/numaflow/pkg/shared/util"
+)
+
+// podInfoSeparator is used as a separator to split the pod key
+// to get the pipeline name, vertex name, and pod index.
+// "*" is chosen because it is not allowed in all the above fields.
+const podInfoSeparator = "*"
+
+// PodTracker maintains a set of active pods for a MonoVertex
+// It periodically sends http requests to pods to check if they are still active
+type PodTracker struct {
+ monoVertex *v1alpha1.MonoVertex
+ log *zap.SugaredLogger
+ httpClient metricsHttpClient
+ activePods *util.UniqueStringList
+ refreshInterval time.Duration
+}
+type PodTrackerOption func(*PodTracker)
+
+func NewPodTracker(ctx context.Context, mv *v1alpha1.MonoVertex, opts ...PodTrackerOption) *PodTracker {
+ pt := &PodTracker{
+ monoVertex: mv,
+ log: logging.FromContext(ctx).Named("PodTracker"),
+ httpClient: &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ },
+ Timeout: time.Second,
+ },
+ activePods: util.NewUniqueStringList(),
+ refreshInterval: 30 * time.Second, // Default refresh interval for updating the active pod set
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(pt)
+ }
+ }
+ return pt
+}
+
+// WithRefreshInterval sets how often to refresh the rate metrics.
+func WithRefreshInterval(d time.Duration) PodTrackerOption {
+ return func(r *PodTracker) {
+ r.refreshInterval = d
+ }
+}
+
+func (pt *PodTracker) Start(ctx context.Context) error {
+ pt.log.Debugf("Starting tracking active pods for MonoVertex %s...", pt.monoVertex.Name)
+ go pt.trackActivePods(ctx)
+ return nil
+}
+
+func (pt *PodTracker) trackActivePods(ctx context.Context) {
+ ticker := time.NewTicker(pt.refreshInterval)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ctx.Done():
+ pt.log.Infof("Context is cancelled. Stopping tracking active pods for MonoVertex %s...", pt.monoVertex.Name)
+ return
+ case <-ticker.C:
+ pt.updateActivePods()
+ }
+ }
+}
+
+// updateActivePods checks the status of all pods and updates the activePods set accordingly.
+func (pt *PodTracker) updateActivePods() {
+ for i := 0; i < int(pt.monoVertex.Spec.Scale.GetMaxReplicas()); i++ {
+ podName := fmt.Sprintf("%s-mv-%d", pt.monoVertex.Name, i)
+ podKey := pt.getPodKey(i)
+ if pt.isActive(podName) {
+ pt.activePods.PushBack(podKey)
+ } else {
+ pt.activePods.Remove(podKey)
+ }
+ }
+ pt.log.Debugf("Finished updating the active pod set: %v", pt.activePods.ToString())
+}
+
+func (pt *PodTracker) getPodKey(index int) string {
+ // podKey is used as a unique identifier for the pod, it is used by worker to determine the count of processed messages of the pod.
+ // we use the monoVertex name and the pod index to create a unique identifier.
+ // For example, if the monoVertex name is "simple-mono-vertex" and the pod index is 0, the podKey will be "simple-mono-vertex*0".
+ // This way, we can easily identify the pod based on its key.
+ return strings.Join([]string{pt.monoVertex.Name, fmt.Sprintf("%d", index)}, podInfoSeparator)
+}
+
+// IsActive returns true if the pod is active, false otherwise.
+func (pt *PodTracker) IsActive(podKey string) bool {
+ return pt.activePods.Contains(podKey)
+}
+
+func (pt *PodTracker) isActive(podName string) bool {
+ headlessSvc := pt.monoVertex.GetHeadlessServiceName()
+ // using the MonoVertex headless service to check if a pod exists or not.
+ // example for 0th pod: https://simple-mono-vertex-mv-0.simple-mono-vertex-mv-headless.default.svc:2469/metrics
+ url := fmt.Sprintf("https://%s.%s.%s.svc:%v/metrics", podName, headlessSvc, pt.monoVertex.Namespace, v1alpha1.MonoVertexMetricsPort)
+ resp, err := pt.httpClient.Head(url)
+ if err != nil {
+ pt.log.Debugf("Sending HEAD request to pod %s is unsuccessful: %v, treating the pod as inactive", podName, err)
+ return false
+ }
+ pt.log.Debugf("Sending HEAD request to pod %s is successful, treating the pod as active", podName)
+ _ = resp.Body.Close()
+ return true
+}
+
+// GetActivePodsCount returns the number of active pods.
+func (pt *PodTracker) GetActivePodsCount() int {
+ return pt.activePods.Length()
+}
+
+// podInfo represents the information of a pod that is used for tracking the processing rate
+type podInfo struct {
+ monoVertexName string
+ replica int
+ podName string
+}
+
+func (pt *PodTracker) GetPodInfo(key string) (*podInfo, error) {
+ pi := strings.Split(key, podInfoSeparator)
+ if len(pi) != 2 {
+ return nil, fmt.Errorf("invalid key %q", key)
+ }
+ replica, err := strconv.Atoi(pi[1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid replica in key %q", key)
+ }
+ return &podInfo{
+ monoVertexName: pi[0],
+ replica: replica,
+ podName: strings.Join([]string{pi[0], "mv", pi[1]}, "-"),
+ }, nil
+}
+
+// LeastRecentlyUsed returns the least recently used pod from the active pod list.
+// if there are no active pods, it returns an empty string.
+func (pt *PodTracker) LeastRecentlyUsed() string {
+ if e := pt.activePods.Front(); e != "" {
+ pt.activePods.MoveToBack(e)
+ return e
+ }
+ return ""
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/pod_tracker_test.go b/pkg/mvtxdaemon/server/service/rater/pod_tracker_test.go
new file mode 100644
index 0000000000..3338fd3ecf
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/pod_tracker_test.go
@@ -0,0 +1,141 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+)
+
+type trackerMockHttpClient struct {
+ podsCount int32
+ lock *sync.RWMutex
+}
+
+func (m *trackerMockHttpClient) setPodsCount(count int32) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ m.podsCount = count
+}
+
+func (m *trackerMockHttpClient) Get(url string) (*http.Response, error) {
+ return nil, nil
+}
+
+func (m *trackerMockHttpClient) Head(url string) (*http.Response, error) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ for i := 0; i < int(m.podsCount); i++ {
+ if strings.Contains(url, "p-mv-"+strconv.Itoa(i)+".p-mv-headless.default.svc:2469/metrics") {
+ return &http.Response{
+ StatusCode: 200,
+ Body: io.NopCloser(bytes.NewReader([]byte(``)))}, nil
+ }
+ }
+
+ return nil, fmt.Errorf("pod not found")
+}
+
+func TestPodTracker_Start(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
+ lookBackSeconds := uint32(30)
+ defer cancel()
+ pipeline := &v1alpha1.MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "p",
+ Namespace: "default",
+ },
+ Spec: v1alpha1.MonoVertexSpec{
+ Scale: v1alpha1.Scale{LookbackSeconds: &lookBackSeconds},
+ },
+ }
+ tracker := NewPodTracker(ctx, pipeline, WithRefreshInterval(time.Second))
+ tracker.httpClient = &trackerMockHttpClient{
+ podsCount: 10,
+ lock: &sync.RWMutex{},
+ }
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if err := tracker.Start(ctx); err != nil {
+ log.Fatalf("failed to start tracker: %v", err)
+ }
+ }()
+
+ for tracker.GetActivePodsCount() != 10 {
+ select {
+ case <-ctx.Done():
+ t.Fatalf("incorrect active pods %v", ctx.Err())
+ default:
+ time.Sleep(100 * time.Millisecond)
+ }
+ }
+
+ tracker.httpClient.(*trackerMockHttpClient).setPodsCount(5)
+
+ for tracker.GetActivePodsCount() != 5 {
+ select {
+ case <-ctx.Done():
+ t.Fatalf("incorrect active pods %v", ctx.Err())
+ default:
+ time.Sleep(100 * time.Millisecond)
+ }
+ }
+ cancel()
+ wg.Wait()
+
+ assert.Equal(t, "p*0", tracker.LeastRecentlyUsed())
+ assert.Equal(t, "p*1", tracker.LeastRecentlyUsed())
+ assert.Equal(t, true, tracker.IsActive("p*4"))
+ assert.Equal(t, false, tracker.IsActive("p*5"))
+}
+
+func TestPodTracker_GetPodInfo(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
+ defer cancel()
+ tracker := NewPodTracker(ctx, nil, WithRefreshInterval(time.Second))
+ // error scenario - more than 3 fields
+ podInfo, err := tracker.GetPodInfo("p*v*0*1")
+ assert.Nilf(t, podInfo, "podInfo should be nil")
+ assert.ErrorContains(t, err, "invalid key")
+
+ // common scenario - get the pod info
+ podInfo, err = tracker.GetPodInfo("p*0")
+ assert.Nilf(t, err, "error should be nil")
+ assert.Equal(t, "p", podInfo.monoVertexName)
+ assert.Equal(t, "p-mv-0", podInfo.podName)
+
+ // common scenario - incorrect the pod info
+ podInfo, err = tracker.GetPodInfo("p*avc")
+ assert.Nilf(t, podInfo, "podInfo should be nil")
+ assert.ErrorContains(t, err, "invalid replica in key")
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/rater.go b/pkg/mvtxdaemon/server/service/rater/rater.go
new file mode 100644
index 0000000000..d160edd838
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/rater.go
@@ -0,0 +1,270 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "crypto/tls"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/prometheus/common/expfmt"
+ "go.uber.org/zap"
+ "golang.org/x/net/context"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ "github.com/numaproj/numaflow/pkg/shared/logging"
+ sharedqueue "github.com/numaproj/numaflow/pkg/shared/queue"
+)
+
+const CountWindow = time.Second * 10
+const MonoVtxReadMetricName = "monovtx_read_total"
+
+// MonoVtxRatable is the interface for the Rater struct.
+type MonoVtxRatable interface {
+ Start(ctx context.Context) error
+ GetRates() map[string]*wrapperspb.DoubleValue
+}
+
+var _ MonoVtxRatable = (*Rater)(nil)
+
+// metricsHttpClient interface for the GET/HEAD call to metrics endpoint.
+// Had to add this an interface for testing
+type metricsHttpClient interface {
+ Get(url string) (*http.Response, error)
+ Head(url string) (*http.Response, error)
+}
+
+// fixedLookbackSeconds always maintain rate metrics for the following lookback seconds (1m, 5m, 15m)
+var fixedLookbackSeconds = map[string]int64{"1m": 60, "5m": 300, "15m": 900}
+
+// Rater is a struct that maintains information about the processing rate of the MonoVertex.
+// It monitors the number of processed messages for each pod in a MonoVertex and calculates the rate.
+type Rater struct {
+ monoVertex *v1alpha1.MonoVertex
+ httpClient metricsHttpClient
+ log *zap.SugaredLogger
+ // podTracker keeps track of active pods and their counts
+ podTracker *PodTracker
+ // timestampedPodCounts is a queue of timestamped counts for the MonoVertex
+ timestampedPodCounts *sharedqueue.OverflowQueue[*TimestampedCounts]
+ // userSpecifiedLookBackSeconds is the user-specified lookback seconds for that MonoVertex
+ userSpecifiedLookBackSeconds int64
+ options *options
+}
+
+// PodReadCount is a struct to maintain count of messages read by a pod of MonoVertex
+type PodReadCount struct {
+ // pod name of the pod
+ name string
+ // represents the count of messages read by the pod
+ readCount float64
+}
+
+// Name returns the pod name
+func (p *PodReadCount) Name() string {
+ return p.name
+}
+
+// ReadCount returns the value of the messages read by the Pod
+func (p *PodReadCount) ReadCount() float64 {
+ return p.readCount
+}
+
+func NewRater(ctx context.Context, mv *v1alpha1.MonoVertex, opts ...Option) *Rater {
+ rater := Rater{
+ monoVertex: mv,
+ httpClient: &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ },
+ Timeout: time.Second * 1,
+ },
+ log: logging.FromContext(ctx).Named("Rater"),
+ options: defaultOptions(),
+ }
+
+ rater.podTracker = NewPodTracker(ctx, mv)
+ // maintain the total counts of the last 30 minutes(1800 seconds) since we support 1m, 5m, 15m lookback seconds.
+ rater.timestampedPodCounts = sharedqueue.New[*TimestampedCounts](int(1800 / CountWindow.Seconds()))
+ rater.userSpecifiedLookBackSeconds = int64(mv.Spec.Scale.GetLookbackSeconds())
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(rater.options)
+ }
+ }
+ return &rater
+}
+
+// Function monitor() defines each of the worker's jobs.
+// It waits for keys in the channel, and starts a monitoring job
+func (r *Rater) monitor(ctx context.Context, id int, keyCh <-chan string) {
+ r.log.Infof("Started monitoring worker %v", id)
+ for {
+ select {
+ case <-ctx.Done():
+ r.log.Infof("Stopped monitoring worker %v", id)
+ return
+ case key := <-keyCh:
+ if err := r.monitorOnePod(ctx, key, id); err != nil {
+ r.log.Errorw("Failed to monitor a pod", zap.String("pod", key), zap.Error(err))
+ }
+ }
+ }
+}
+
+// monitorOnePod monitors a single pod and updates the rate metrics for the given pod.
+func (r *Rater) monitorOnePod(ctx context.Context, key string, worker int) error {
+ log := logging.FromContext(ctx).With("worker", fmt.Sprint(worker)).With("podKey", key)
+ log.Debugf("Working on key: %s", key)
+ pInfo, err := r.podTracker.GetPodInfo(key)
+ if err != nil {
+ return err
+ }
+ var podReadCount *PodReadCount
+ if r.podTracker.IsActive(key) {
+ podReadCount = r.getPodReadCounts(pInfo.podName)
+ if podReadCount == nil {
+ log.Debugf("Failed retrieving total podReadCounts for pod %s", pInfo.podName)
+ }
+ } else {
+ log.Debugf("Pod %s does not exist, updating it with nil...", pInfo.podName)
+ podReadCount = nil
+ }
+ now := time.Now().Add(CountWindow).Truncate(CountWindow).Unix()
+ UpdateCount(r.timestampedPodCounts, now, podReadCount)
+ return nil
+}
+
+// getPodReadCounts returns the total number of messages read by the pod
+// It fetches the total pod read counts from the Prometheus metrics endpoint.
+func (r *Rater) getPodReadCounts(podName string) *PodReadCount {
+ headlessServiceName := r.monoVertex.GetHeadlessServiceName()
+ // scrape the read total metric from pod metric port
+ // example for 0th pod: https://simple-mono-vertex-mv-0.simple-mono-vertex-mv-headless.default.svc:2469/metrics
+ url := fmt.Sprintf("https://%s.%s.%s.svc:%v/metrics", podName, headlessServiceName, r.monoVertex.Namespace, v1alpha1.MonoVertexMetricsPort)
+ resp, err := r.httpClient.Get(url)
+ if err != nil {
+ r.log.Errorf("[MonoVertex name %s, pod name %s]: failed reading the metrics endpoint, %v", r.monoVertex.Name, podName, err.Error())
+ return nil
+ }
+ defer resp.Body.Close()
+
+ textParser := expfmt.TextParser{}
+ result, err := textParser.TextToMetricFamilies(resp.Body)
+ if err != nil {
+ r.log.Errorf("[MonoVertex name %s, pod name %s]: failed parsing to prometheus metric families, %v", r.monoVertex.Name, podName, err.Error())
+ return nil
+ }
+
+ if value, ok := result[MonoVtxReadMetricName]; ok && value != nil && len(value.GetMetric()) > 0 {
+ metricsList := value.GetMetric()
+ // Each pod should be emitting only one metric with this name, so we should be able to take the first value
+ // from the results safely.
+ // We use Untyped here as the counter metric family shows up as untyped from the rust client
+ // TODO(MonoVertex): Check further on this to understand why not type is counter
+ podReadCount := &PodReadCount{podName, metricsList[0].Untyped.GetValue()}
+ return podReadCount
+ } else {
+ r.log.Errorf("[MonoVertex name %s, pod name %s]: failed getting the read total metric, the metric is not available.", r.monoVertex.Name, podName)
+ return nil
+ }
+}
+
+// GetRates returns the rate metrics for the MonoVertex.
+// It calculates the rate metrics for the given lookback seconds.
+func (r *Rater) GetRates() map[string]*wrapperspb.DoubleValue {
+ r.log.Debugf("Current timestampedPodCounts for MonoVertex %s is: %v", r.monoVertex.Name, r.timestampedPodCounts)
+ var result = make(map[string]*wrapperspb.DoubleValue)
+ // calculate rates for each lookback seconds
+ for n, i := range r.buildLookbackSecondsMap() {
+ rate := CalculateRate(r.timestampedPodCounts, i)
+ result[n] = wrapperspb.Double(rate)
+ }
+ r.log.Debugf("Got rates for MonoVertex %s: %v", r.monoVertex.Name, result)
+ return result
+}
+
+func (r *Rater) buildLookbackSecondsMap() map[string]int64 {
+ lookbackSecondsMap := map[string]int64{"default": r.userSpecifiedLookBackSeconds}
+ for k, v := range fixedLookbackSeconds {
+ lookbackSecondsMap[k] = v
+ }
+ return lookbackSecondsMap
+}
+
+func (r *Rater) Start(ctx context.Context) error {
+ r.log.Infof("Starting rater...")
+ keyCh := make(chan string)
+ ctx, cancel := context.WithCancel(logging.WithLogger(ctx, r.log))
+ defer cancel()
+
+ go func() {
+ err := r.podTracker.Start(ctx)
+ if err != nil {
+ r.log.Errorw("Failed to start pod tracker", zap.Error(err))
+ }
+ }()
+
+ // Worker group
+ for i := 1; i <= r.options.workers; i++ {
+ go r.monitor(ctx, i, keyCh)
+ }
+
+ // Function assign() sends the least recently used podKey to the channel so that it can be picked up by a worker.
+ assign := func() {
+ if e := r.podTracker.LeastRecentlyUsed(); e != "" {
+ keyCh <- e
+ return
+ }
+ }
+
+ // Following for loop keeps calling assign() function to assign monitoring tasks to the workers.
+ // It makes sure each element in the list will be assigned every N milliseconds.
+ for {
+ select {
+ case <-ctx.Done():
+ r.log.Info("Shutting down monitoring job assigner")
+ return nil
+ default:
+ assign()
+ // Make sure each of the key will be assigned at least every taskInterval milliseconds.
+ sleep(ctx, time.Millisecond*time.Duration(func() int {
+ l := r.podTracker.GetActivePodsCount()
+ if l == 0 {
+ return r.options.taskInterval
+ }
+ result := r.options.taskInterval / l
+ if result > 0 {
+ return result
+ }
+ return 1
+ }()))
+ }
+ }
+}
+
+// sleep function uses a select statement to check if the context is canceled before sleeping for the given duration
+// it helps ensure the sleep will be released when the context is canceled, allowing the goroutine to exit gracefully
+func sleep(ctx context.Context, duration time.Duration) {
+ select {
+ case <-ctx.Done():
+ case <-time.After(duration):
+ }
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/rater_test.go b/pkg/mvtxdaemon/server/service/rater/rater_test.go
new file mode 100644
index 0000000000..26b7e2f3bb
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/rater_test.go
@@ -0,0 +1,134 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "sync"
+ "testing"
+ "time"
+
+ "golang.org/x/net/context"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+)
+
+type raterMockHttpClient struct {
+ podOneCount int64
+ podTwoCount int64
+ lock *sync.RWMutex
+}
+
+func (m *raterMockHttpClient) Get(url string) (*http.Response, error) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ if url == "https://p-mv-0.p-mv-headless.default.svc:2469/metrics" {
+ m.podOneCount = m.podOneCount + 20
+ resp := &http.Response{
+ StatusCode: 200,
+ // we use the default monovertex forwarder metric name "monovtx_read_total" is used to retrieve the metric
+ Body: io.NopCloser(bytes.NewReader([]byte(fmt.Sprintf(`
+# HELP monovtx_read A Counter to keep track of the total number of messages read from the source.
+# TYPE monovtx_read counter
+monovtx_read_total{mvtx_name="simple-mono-vertex",mvtx_replica="0"} %d
+`, m.podOneCount))))}
+ return resp, nil
+ } else if url == "https://p-mv-1.p-mv-headless.default.svc:2469/metrics" {
+ m.podTwoCount = m.podTwoCount + 60
+ resp := &http.Response{
+ StatusCode: 200,
+ Body: io.NopCloser(bytes.NewReader([]byte(fmt.Sprintf(`
+# HELP monovtx_read A Counter to keep track of the total number of messages read from the source.
+# TYPE monovtx_read counter
+monovtx_read_total{mvtx_name="simple-mono-vertex",mvtx_replica="1"} %d
+`, m.podTwoCount))))}
+ return resp, nil
+ } else {
+ return nil, nil
+ }
+}
+
+func (m *raterMockHttpClient) Head(url string) (*http.Response, error) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ if url == "https://p-mv-0.p-mv-headless.default.svc:2469/metrics" {
+ return &http.Response{
+ StatusCode: 200,
+ Body: io.NopCloser(bytes.NewReader([]byte(``)))}, nil
+ } else if url == "https://p-mv-1.p-mv-headless.default.svc:2469/metrics" {
+ return &http.Response{
+ StatusCode: 200,
+ Body: io.NopCloser(bytes.NewReader([]byte(``)))}, nil
+ } else {
+ return nil, fmt.Errorf("unknown url: %s", url)
+ }
+}
+
+// TestRater_Start tests the rater by mocking the http client
+// we mock the metrics endpoint of the pods and increment the read count by 20 for pod one, and 60 for pod two,
+// then we verify that the rate calculator is able to calculate a positive rate for the vertex
+// note: this test doesn't test the accuracy of the calculated rate, the calculation is tested by helper_test.go
+func TestRater_Start(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*29)
+ lookBackSeconds := uint32(30)
+ defer cancel()
+ pipeline := &v1alpha1.MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "p",
+ Namespace: "default",
+ },
+ Spec: v1alpha1.MonoVertexSpec{
+ Scale: v1alpha1.Scale{LookbackSeconds: &lookBackSeconds},
+ },
+ }
+ r := NewRater(ctx, pipeline, WithTaskInterval(1000))
+ podTracker := NewPodTracker(ctx, pipeline, WithRefreshInterval(time.Second*1))
+ podTracker.httpClient = &raterMockHttpClient{podOneCount: 0, podTwoCount: 0, lock: &sync.RWMutex{}}
+ r.httpClient = &raterMockHttpClient{podOneCount: 0, podTwoCount: 0, lock: &sync.RWMutex{}}
+ r.podTracker = podTracker
+
+ timer := time.NewTimer(60 * time.Second)
+ succeedChan := make(chan struct{})
+ go func() {
+ if err := r.Start(ctx); err != nil {
+ log.Fatalf("failed to start rater: %v", err)
+ }
+ }()
+ go func() {
+ for {
+ if r.GetRates()["default"].GetValue() <= 0 {
+ time.Sleep(time.Second)
+ } else {
+ succeedChan <- struct{}{}
+ break
+ }
+ }
+ }()
+ select {
+ case <-succeedChan:
+ time.Sleep(time.Second)
+ break
+ case <-timer.C:
+ t.Fatalf("timed out waiting for rate to be calculated")
+ }
+ timer.Stop()
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/timestamped_counts.go b/pkg/mvtxdaemon/server/service/rater/timestamped_counts.go
new file mode 100644
index 0000000000..ee2a13519b
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/timestamped_counts.go
@@ -0,0 +1,73 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "fmt"
+ "sync"
+)
+
+// TimestampedCounts track the total count of processed messages for a list of pods at a given timestamp
+type TimestampedCounts struct {
+ // timestamp in seconds is the time when the count is recorded
+ timestamp int64
+ // the key of podReadCounts represents the pod name, the value represents a count of messages processed by the pod
+ podReadCounts map[string]float64
+ lock *sync.RWMutex
+}
+
+func NewTimestampedCounts(t int64) *TimestampedCounts {
+ return &TimestampedCounts{
+ timestamp: t,
+ podReadCounts: make(map[string]float64),
+ lock: new(sync.RWMutex),
+ }
+}
+
+// Update updates the count of processed messages for a pod
+func (tc *TimestampedCounts) Update(podReadCount *PodReadCount) {
+ tc.lock.Lock()
+ defer tc.lock.Unlock()
+ if podReadCount == nil {
+ // we choose to skip updating when podReadCounts is nil, instead of removing the pod from the map.
+ // imagine if the getPodReadCounts call fails to scrape the readCount metric, and it's NOT because the pod is down.
+ // in this case getPodReadCounts returns nil.
+ // if we remove the pod from the map and then the next scrape successfully gets the readCount, we can reach a state that in the timestamped counts,
+ // for this single pod, at t1, readCount is 123456, at t2, the map doesn't contain this pod and t3, readCount is 123457.
+ // when calculating the rate, as we sum up deltas among timestamps, we will get 123457 total delta instead of the real delta 1.
+ // one occurrence of such case can lead to extremely high rate and mess up the autoscaling.
+ // hence we'd rather keep the readCount as it is to avoid wrong rate calculation.
+ return
+ }
+ tc.podReadCounts[podReadCount.Name()] = podReadCount.ReadCount()
+}
+
+// PodCountSnapshot returns a copy of podReadCounts
+// it's used to ensure the returned map is not modified by other goroutines
+func (tc *TimestampedCounts) PodCountSnapshot() map[string]float64 {
+ tc.lock.RLock()
+ defer tc.lock.RUnlock()
+ return tc.podReadCounts
+}
+
+// String returns a string representation of the TimestampedCounts
+// it's used for debugging purpose
+func (tc *TimestampedCounts) String() string {
+ tc.lock.RLock()
+ defer tc.lock.RUnlock()
+ return fmt.Sprintf("{timestamp: %d, podReadCounts: %v}", tc.timestamp, tc.podReadCounts)
+}
diff --git a/pkg/mvtxdaemon/server/service/rater/timestamped_counts_test.go b/pkg/mvtxdaemon/server/service/rater/timestamped_counts_test.go
new file mode 100644
index 0000000000..75d0eda781
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/rater/timestamped_counts_test.go
@@ -0,0 +1,64 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rater
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestNewTimestampedCounts(t *testing.T) {
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ assert.Equal(t, int64(TestTime), tc.timestamp)
+ assert.Equal(t, 1, len(tc.podReadCounts))
+ assert.Equal(t, "{timestamp: 1620000000, podReadCounts: map[pod1:10]}", tc.String())
+}
+
+func TestTimestampedCounts_Update(t *testing.T) {
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ assert.Equal(t, 10.0, tc.podReadCounts["pod1"])
+ tc.Update(&PodReadCount{"pod1", 20.0})
+ assert.Equal(t, 20.0, tc.podReadCounts["pod1"])
+ tc.Update(&PodReadCount{"pod2", 30.0})
+ assert.Equal(t, 30.0, tc.podReadCounts["pod2"])
+ assert.Equal(t, 2, len(tc.podReadCounts))
+ tc.Update(nil)
+ assert.Equal(t, 2, len(tc.podReadCounts))
+ assert.Equal(t, 20, int(tc.podReadCounts["pod1"]))
+ assert.Equal(t, 30, int(tc.podReadCounts["pod2"]))
+
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ assert.Equal(t, 10, int(tc.podReadCounts["pod1"]))
+ tc.Update(&PodReadCount{"pod2", 20.0})
+ assert.Equal(t, 20, int(tc.podReadCounts["pod2"]))
+
+ tc2 := NewTimestampedCounts(TestTime + 1)
+ tc2.Update(&PodReadCount{"pod1", 40.0})
+ assert.Equal(t, 40.0, tc2.podReadCounts["pod1"])
+ tc2.Update(&PodReadCount{"pod2", 10.0})
+ assert.Equal(t, 10.0, tc2.podReadCounts["pod2"])
+}
+
+func TestTimestampedPodCounts_Snapshot(t *testing.T) {
+ tc := NewTimestampedCounts(TestTime)
+ tc.Update(&PodReadCount{"pod1", 10.0})
+ tc.Update(&PodReadCount{"pod2", 20.0})
+ assert.Equal(t, map[string]float64{"pod1": 10.0, "pod2": 20.0}, tc.PodCountSnapshot())
+}
diff --git a/pkg/reconciler/cmd/start.go b/pkg/reconciler/cmd/start.go
index f7fa5f295f..0565aefad1 100644
--- a/pkg/reconciler/cmd/start.go
+++ b/pkg/reconciler/cmd/start.go
@@ -40,6 +40,7 @@ import (
"github.com/numaproj/numaflow/pkg/reconciler"
isbsvcctrl "github.com/numaproj/numaflow/pkg/reconciler/isbsvc"
monovtxctrl "github.com/numaproj/numaflow/pkg/reconciler/monovertex"
+ mvtxscaling "github.com/numaproj/numaflow/pkg/reconciler/monovertex/scaling"
plctrl "github.com/numaproj/numaflow/pkg/reconciler/pipeline"
vertexctrl "github.com/numaproj/numaflow/pkg/reconciler/vertex"
"github.com/numaproj/numaflow/pkg/reconciler/vertex/scaling"
@@ -237,8 +238,9 @@ func Start(namespaced bool, managedNamespace string) {
}
// MonoVertex controller
+ mvtxAutoscaler := mvtxscaling.NewScaler(mgr.GetClient(), mvtxscaling.WithWorkers(20))
monoVertexController, err := controller.New(dfv1.ControllerMonoVertex, mgr, controller.Options{
- Reconciler: monovtxctrl.NewReconciler(mgr.GetClient(), mgr.GetScheme(), config, image, logger, mgr.GetEventRecorderFor(dfv1.ControllerMonoVertex)),
+ Reconciler: monovtxctrl.NewReconciler(mgr.GetClient(), mgr.GetScheme(), config, image, mvtxAutoscaler, logger, mgr.GetEventRecorderFor(dfv1.ControllerMonoVertex)),
})
if err != nil {
logger.Fatalw("Unable to set up MonoVertex controller", zap.Error(err))
@@ -276,9 +278,14 @@ func Start(namespaced bool, managedNamespace string) {
logger.Fatalw("Unable to watch Deployments", zap.Error(err))
}
- // Add autoscaling runner
+ // Add Vertex autoscaling runner
if err := mgr.Add(LeaderElectionRunner(autoscaler.Start)); err != nil {
- logger.Fatalw("Unable to add autoscaling runner", zap.Error(err))
+ logger.Fatalw("Unable to add Vertex autoscaling runner", zap.Error(err))
+ }
+
+ // Add MonoVertex autoscaling runner
+ if err := mgr.Add(LeaderElectionRunner(mvtxAutoscaler.Start)); err != nil {
+ logger.Fatalw("Unable to add MonoVertex autoscaling runner", zap.Error(err))
}
version := numaflow.GetVersion()
diff --git a/pkg/reconciler/metrics.go b/pkg/reconciler/metrics.go
index 5f92049f2d..ce96436556 100644
--- a/pkg/reconciler/metrics.go
+++ b/pkg/reconciler/metrics.go
@@ -45,6 +45,12 @@ var (
Help: "A metric to indicate whether the Pipeline is healthy. '1' means healthy, '0' means unhealthy",
}, []string{metrics.LabelNamespace, metrics.LabelISBService})
+ MonoVertexHealth = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Subsystem: "controller",
+ Name: "monovtx_health",
+ Help: "A metric to indicate whether the MonoVertex is healthy. '1' means healthy, '0' means unhealthy",
+ }, []string{metrics.LabelNamespace, metrics.LabelMonoVertexName})
+
// JetStreamISBSvcReplicas indicates the replicas of a JetStream ISB Service.
JetStreamISBSvcReplicas = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Subsystem: "controller",
@@ -72,8 +78,25 @@ var (
Name: "vertex_current_replicas",
Help: "A metric indicates the current replicas of a Vertex",
}, []string{metrics.LabelNamespace, metrics.LabelPipeline, metrics.LabelVertex})
+
+ // MonoVertexDesiredReplicas indicates the desired replicas of a MonoVertex.
+ MonoVertexDesiredReplicas = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Subsystem: "controller",
+ Name: "monovtx_desired_replicas",
+ Help: "A metric indicates the desired replicas of a MonoVertex",
+ }, []string{metrics.LabelNamespace, metrics.LabelMonoVertexName})
+
+ // MonoVertexCurrentReplicas indicates the current replicas of a MonoVertex.
+ MonoVertexCurrentReplicas = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Subsystem: "controller",
+ Name: "monovtx_current_replicas",
+ Help: "A metric indicates the current replicas of a MonoVertex",
+ }, []string{metrics.LabelNamespace, metrics.LabelMonoVertexName})
)
func init() {
- ctrlmetrics.Registry.MustRegister(BuildInfo, ISBSvcHealth, PipelineHealth, JetStreamISBSvcReplicas, RedisISBSvcReplicas, VertexDesiredReplicas, VertexCurrentReplicas)
+ ctrlmetrics.Registry.MustRegister(BuildInfo, ISBSvcHealth, PipelineHealth,
+ MonoVertexHealth, JetStreamISBSvcReplicas, RedisISBSvcReplicas,
+ VertexDesiredReplicas, VertexCurrentReplicas, MonoVertexDesiredReplicas,
+ MonoVertexCurrentReplicas)
}
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index 20246b8f9c..108580f3dc 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -39,6 +39,7 @@ import (
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/reconciler"
+ mvtxscaling "github.com/numaproj/numaflow/pkg/reconciler/monovertex/scaling"
"github.com/numaproj/numaflow/pkg/shared/logging"
sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
)
@@ -52,11 +53,12 @@ type monoVertexReconciler struct {
image string
logger *zap.SugaredLogger
+ scaler *mvtxscaling.Scaler
recorder record.EventRecorder
}
-func NewReconciler(client client.Client, scheme *runtime.Scheme, config *reconciler.GlobalConfig, image string, logger *zap.SugaredLogger, recorder record.EventRecorder) reconcile.Reconciler {
- return &monoVertexReconciler{client: client, scheme: scheme, config: config, image: image, logger: logger, recorder: recorder}
+func NewReconciler(client client.Client, scheme *runtime.Scheme, config *reconciler.GlobalConfig, image string, scaler *mvtxscaling.Scaler, logger *zap.SugaredLogger, recorder record.EventRecorder) reconcile.Reconciler {
+ return &monoVertexReconciler{client: client, scheme: scheme, config: config, image: image, scaler: scaler, logger: logger, recorder: recorder}
}
func (mr *monoVertexReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
@@ -87,13 +89,28 @@ func (mr *monoVertexReconciler) Reconcile(ctx context.Context, req ctrl.Request)
// reconcile does the real logic.
func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.MonoVertex) (ctrl.Result, error) {
log := logging.FromContext(ctx)
+ mVtxKey := mvtxscaling.KeyOfMonoVertex(*monoVtx)
if !monoVtx.DeletionTimestamp.IsZero() {
log.Info("Deleting mono vertex")
+ mr.scaler.StopWatching(mVtxKey)
+ // Clean up metrics
+ _ = reconciler.MonoVertexHealth.DeleteLabelValues(monoVtx.Namespace, monoVtx.Name)
+ _ = reconciler.MonoVertexDesiredReplicas.DeleteLabelValues(monoVtx.Namespace, monoVtx.Name)
+ _ = reconciler.MonoVertexCurrentReplicas.DeleteLabelValues(monoVtx.Namespace, monoVtx.Name)
return ctrl.Result{}, nil
}
- monoVtx.Status.SetObservedGeneration(monoVtx.Generation)
+ // Set metrics
+ defer func() {
+ if monoVtx.Status.IsHealthy() {
+ reconciler.MonoVertexHealth.WithLabelValues(monoVtx.Namespace, monoVtx.Name).Set(1)
+ } else {
+ reconciler.MonoVertexHealth.WithLabelValues(monoVtx.Namespace, monoVtx.Name).Set(0)
+ }
+ }()
+ monoVtx.Status.SetObservedGeneration(monoVtx.Generation)
+ mr.scaler.StartWatching(mVtxKey)
// TODO: handle lifecycle changes
// Regular mono vertex change
@@ -137,13 +154,19 @@ func (mr *monoVertexReconciler) reconcileNonLifecycleChanges(ctx context.Context
}
func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1.MonoVertex) error {
+ desiredReplicas := monoVtx.GetReplicas()
+ // Set metrics
+ defer func() {
+ reconciler.MonoVertexDesiredReplicas.WithLabelValues(monoVtx.Namespace, monoVtx.Name).Set(float64(desiredReplicas))
+ reconciler.MonoVertexCurrentReplicas.WithLabelValues(monoVtx.Namespace, monoVtx.Name).Set(float64(monoVtx.Status.Replicas))
+ }()
+
log := logging.FromContext(ctx)
existingPods, err := mr.findExistingPods(ctx, monoVtx)
if err != nil {
mr.markDeploymentFailedAndLogEvent(monoVtx, false, log, "FindExistingPodFailed", err.Error(), "Failed to find existing mono vertex pods", zap.Error(err))
return err
}
- desiredReplicas := monoVtx.GetReplicas()
for replica := 0; replica < desiredReplicas; replica++ {
podSpec, err := mr.buildPodSpec(monoVtx)
if err != nil {
diff --git a/pkg/reconciler/monovertex/scaling/doc.go b/pkg/reconciler/monovertex/scaling/doc.go
new file mode 100644
index 0000000000..9a3d2ba933
--- /dev/null
+++ b/pkg/reconciler/monovertex/scaling/doc.go
@@ -0,0 +1,25 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package scaling provides the autoscaling capability for MonoVertex objects.
+//
+// A workqueue is implemented in this package to watch monovertices in the cluster,
+// calculate the desired replica number for each of them periodically, and
+// patch the MonoVertex spec.
+//
+// Function StartWatching() and StopWatching() are also provided in the package,
+// so that monovertices can be added into and removed from the workqueue.
+package scaling
diff --git a/pkg/reconciler/monovertex/scaling/options.go b/pkg/reconciler/monovertex/scaling/options.go
new file mode 100644
index 0000000000..10ed212181
--- /dev/null
+++ b/pkg/reconciler/monovertex/scaling/options.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package scaling
+
+type options struct {
+ // Number of workers working on autoscaling.
+ workers int
+ // Time in milliseconds, each element in the work queue will be picked up in an interval of this period of time.
+ taskInterval int
+ // size of the daemon clients cache.
+ clientsCacheSize int
+}
+
+type Option func(*options)
+
+func defaultOptions() *options {
+ return &options{
+ workers: 20,
+ taskInterval: 30000,
+ clientsCacheSize: 500,
+ }
+}
+
+// WithWorkers sets the number of workers working on autoscaling.
+func WithWorkers(n int) Option {
+ return func(o *options) {
+ o.workers = n
+ }
+}
+
+// WithTaskInterval sets the interval of picking up a task from the work queue.
+func WithTaskInterval(n int) Option {
+ return func(o *options) {
+ o.taskInterval = n
+ }
+}
+
+// WithClientsCacheSize sets the size of the daemon clients cache.
+func WithClientsCacheSize(n int) Option {
+ return func(o *options) {
+ o.clientsCacheSize = n
+ }
+}
diff --git a/pkg/reconciler/monovertex/scaling/scaling.go b/pkg/reconciler/monovertex/scaling/scaling.go
new file mode 100644
index 0000000000..7557b236b4
--- /dev/null
+++ b/pkg/reconciler/monovertex/scaling/scaling.go
@@ -0,0 +1,366 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package scaling
+
+import (
+ "container/list"
+ "context"
+ "encoding/json"
+ "fmt"
+ "math"
+ "strings"
+ "sync"
+ "time"
+
+ lru "github.com/hashicorp/golang-lru/v2"
+ "go.uber.org/zap"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/utils/ptr"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ "github.com/numaproj/numaflow/pkg/isb"
+ mvtxdaemonclient "github.com/numaproj/numaflow/pkg/mvtxdaemon/client"
+ "github.com/numaproj/numaflow/pkg/shared/logging"
+)
+
+type Scaler struct {
+ client client.Client
+ monoVtxMap map[string]*list.Element
+ // List of the mono vertex namespaced name, format is "namespace/name"
+ monoVtxList *list.List
+ lock *sync.RWMutex
+ options *options
+ // Cache to store the vertex metrics such as pending message number
+ monoVtxMetricsCache *lru.Cache[string, int64]
+ mvtxDaemonClientsCache *lru.Cache[string, mvtxdaemonclient.MonoVertexDaemonClient]
+}
+
+// NewScaler returns a Scaler instance.
+func NewScaler(client client.Client, opts ...Option) *Scaler {
+ scalerOpts := defaultOptions()
+ for _, opt := range opts {
+ if opt != nil {
+ opt(scalerOpts)
+ }
+ }
+ s := &Scaler{
+ client: client,
+ options: scalerOpts,
+ monoVtxMap: make(map[string]*list.Element),
+ monoVtxList: list.New(),
+ lock: new(sync.RWMutex),
+ }
+ // cache the clients
+ s.mvtxDaemonClientsCache, _ = lru.NewWithEvict[string, mvtxdaemonclient.MonoVertexDaemonClient](s.options.clientsCacheSize, func(key string, value mvtxdaemonclient.MonoVertexDaemonClient) {
+ _ = value.Close()
+ })
+ monoVtxMetricsCache, _ := lru.New[string, int64](10000)
+ s.monoVtxMetricsCache = monoVtxMetricsCache
+ return s
+}
+
+// Contains returns if the Scaler contains the key.
+func (s *Scaler) Contains(key string) bool {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ _, ok := s.monoVtxMap[key]
+ return ok
+}
+
+// Length returns how many vertices are being watched for autoscaling
+func (s *Scaler) Length() int {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ return s.monoVtxList.Len()
+}
+
+// StartWatching put a key (namespace/name) into the Scaler
+func (s *Scaler) StartWatching(key string) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ if _, ok := s.monoVtxMap[key]; !ok {
+ s.monoVtxMap[key] = s.monoVtxList.PushBack(key)
+ }
+}
+
+// StopWatching stops autoscaling on the key (namespace/name)
+func (s *Scaler) StopWatching(key string) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ if e, ok := s.monoVtxMap[key]; ok {
+ _ = s.monoVtxList.Remove(e)
+ delete(s.monoVtxMap, key)
+ }
+}
+
+// Function scale() defines each of the worker's job.
+// It waits for keys in the channel, and starts a scaling job
+func (s *Scaler) scale(ctx context.Context, id int, keyCh <-chan string) {
+ log := logging.FromContext(ctx)
+ log.Infof("Started MonoVertex autoscaling worker %v", id)
+ for {
+ select {
+ case <-ctx.Done():
+ log.Infof("Stopped MonoVertex autoscaling worker %v", id)
+ return
+ case key := <-keyCh:
+ if err := s.scaleOneMonoVertex(ctx, key, id); err != nil {
+ log.Errorw("Failed to scale a MonoVertex", zap.String("monoVtx", key), zap.Error(err))
+ }
+ }
+ }
+}
+
+// scaleOneMonoVertex implements the detailed logic of scaling up/down a MonoVertex.
+//
+// desiredReplicas = currentReplicas * pending / (targetProcessingTime * rate)
+func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int) error {
+ log := logging.FromContext(ctx).With("worker", fmt.Sprint(worker)).With("monoVtxKey", key)
+ log.Debugf("Working on key: %s.", key)
+ strs := strings.Split(key, "/")
+ if len(strs) != 2 {
+ return fmt.Errorf("invalid key %q", key)
+ }
+ namespace := strs[0]
+ monoVtxName := strs[1]
+ monoVtx := &dfv1.MonoVertex{}
+ if err := s.client.Get(ctx, client.ObjectKey{Namespace: namespace, Name: monoVtxName}, monoVtx); err != nil {
+ if apierrors.IsNotFound(err) {
+ s.StopWatching(key)
+ log.Info("No corresponding MonoVertex found, stopped watching.")
+ return nil
+ }
+ return fmt.Errorf("failed to query MonoVertex object of key %q, %w", key, err)
+ }
+ if !monoVtx.GetDeletionTimestamp().IsZero() {
+ s.StopWatching(key)
+ log.Debug("MonoVertex being deleted.")
+ return nil
+ }
+ if !monoVtx.Scalable() {
+ s.StopWatching(key) // Remove it in case it's watched.
+ return nil
+ }
+ secondsSinceLastScale := time.Since(monoVtx.Status.LastScaledAt.Time).Seconds()
+ scaleDownCooldown := float64(monoVtx.Spec.Scale.GetScaleDownCooldownSeconds())
+ scaleUpCooldown := float64(monoVtx.Spec.Scale.GetScaleUpCooldownSeconds())
+ if secondsSinceLastScale < scaleDownCooldown && secondsSinceLastScale < scaleUpCooldown {
+ // Skip scaling without needing further calculation
+ log.Infof("Cooldown period, skip scaling.")
+ return nil
+ }
+ if monoVtx.Status.Phase != dfv1.MonoVertexPhaseRunning {
+ log.Infof("MonoVertex not in Running phase, skip scaling.")
+ return nil
+ }
+ // TODO: lifecycle
+ // if monoVtx.Spec.Lifecycle.GetDesiredPhase() != dfv1.MonoVertexPhaseRunning {
+ // log.Info("MonoVertex is pausing, skip scaling.")
+ // return nil
+ // }
+ if int(monoVtx.Status.Replicas) != monoVtx.GetReplicas() {
+ log.Infof("MonoVertex %s might be under processing, replicas mismatch, skip scaling.", monoVtx.Name)
+ return nil
+ }
+
+ var err error
+ daemonClient, _ := s.mvtxDaemonClientsCache.Get(monoVtx.GetDaemonServiceURL())
+ if daemonClient == nil {
+ daemonClient, err = mvtxdaemonclient.NewGRPCClient(monoVtx.GetDaemonServiceURL())
+ if err != nil {
+ return fmt.Errorf("failed to get daemon service client for MonoVertex %s, %w", monoVtx.Name, err)
+ }
+ s.mvtxDaemonClientsCache.Add(monoVtx.GetDaemonServiceURL(), daemonClient)
+ }
+
+ if monoVtx.Status.Replicas == 0 { // Was scaled to 0
+ // Periodically wake them up from 0 replicas to 1, to peek for the incoming messages
+ if secondsSinceLastScale >= float64(monoVtx.Spec.Scale.GetZeroReplicaSleepSeconds()) {
+ log.Infof("MonoVertex %s has slept %v seconds, scaling up to peek.", monoVtx.Name, secondsSinceLastScale)
+ return s.patchMonoVertexReplicas(ctx, monoVtx, 1)
+ } else {
+ log.Infof("MonoVertex %q has slept %v seconds, hasn't reached zeroReplicaSleepSeconds (%v seconds), skip scaling.", monoVtx.Name, secondsSinceLastScale, monoVtx.Spec.Scale.GetZeroReplicaSleepSeconds())
+ return nil
+ }
+ }
+
+ vMetrics, err := daemonClient.GetMonoVertexMetrics(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to get metrics of mono vertex key %q, %w", key, err)
+ }
+ totalRate := float64(0)
+ totalPending := int64(0)
+ rate, existing := vMetrics.ProcessingRates["default"]
+ // If rate is not available, we skip scaling.
+ if !existing || rate.GetValue() < 0 { // Rate not available
+ log.Infof("MonoVertex %s has no rate information, skip scaling.", monoVtxName)
+ return nil
+ } else {
+ totalRate = rate.GetValue()
+ }
+ pending, existing := vMetrics.Pendings["default"]
+ if !existing || pending.GetValue() < 0 || pending.GetValue() == isb.PendingNotAvailable {
+ // Pending not available, we don't do anything
+ log.Infof("MonoVertex %s has no pending messages information, skip scaling.", monoVtxName)
+ return nil
+ } else {
+ totalPending = pending.GetValue()
+ }
+
+ desired := s.desiredReplicas(ctx, monoVtx, totalRate, totalPending)
+ log.Infof("Calculated desired replica number of MonoVertex %q is: %d.", monoVtx.Name, desired)
+ max := monoVtx.Spec.Scale.GetMaxReplicas()
+ min := monoVtx.Spec.Scale.GetMinReplicas()
+ if desired > max {
+ desired = max
+ log.Infof("Calculated desired replica number %d of MonoVertex %q is greater than max, using max %d.", monoVtxName, desired, max)
+ }
+ if desired < min {
+ desired = min
+ log.Infof("Calculated desired replica number %d of MonoVertex %q is smaller than min, using min %d.", monoVtxName, desired, min)
+ }
+ current := int32(monoVtx.GetReplicas())
+ if current > max || current < min { // Someone might have manually scaled up/down the MonoVertex
+ return s.patchMonoVertexReplicas(ctx, monoVtx, desired)
+ }
+ maxAllowed := int32(monoVtx.Spec.Scale.GetReplicasPerScale())
+ if desired < current {
+ diff := current - desired
+ if diff > maxAllowed {
+ diff = maxAllowed
+ }
+ if secondsSinceLastScale < scaleDownCooldown {
+ log.Infof("Cooldown period for scaling down, skip scaling.")
+ return nil
+ }
+ return s.patchMonoVertexReplicas(ctx, monoVtx, current-diff) // We scale down gradually
+ }
+ if desired > current {
+ diff := desired - current
+ if diff > maxAllowed {
+ diff = maxAllowed
+ }
+ if secondsSinceLastScale < scaleUpCooldown {
+ log.Infof("Cooldown period for scaling up, skip scaling.")
+ return nil
+ }
+ return s.patchMonoVertexReplicas(ctx, monoVtx, current+diff) // We scale up gradually
+ }
+ return nil
+}
+
+func (s *Scaler) desiredReplicas(_ context.Context, monoVtx *dfv1.MonoVertex, processingRate float64, pending int64) int32 {
+ // Since pending contains the pending acks, if both totalRate and totalPending are 0, we scale down to 0
+ if pending == 0 && processingRate == 0 {
+ return 0
+ }
+ if processingRate == 0 { // Something is wrong, we don't do anything.
+ return int32(monoVtx.Status.Replicas)
+ }
+
+ var desired int32
+ // We calculate the time of finishing processing the pending messages,
+ // and then we know how many replicas are needed to get them done in target seconds.
+ desired = int32(math.Round(((float64(pending) / processingRate) / float64(monoVtx.Spec.Scale.GetTargetProcessingSeconds())) * float64(monoVtx.Status.Replicas)))
+
+ // we only scale down to zero when the pending and rate are both zero.
+ if desired == 0 {
+ desired = 1
+ }
+ if desired > int32(pending) && pending > 0 { // For some corner cases, we don't want to scale up to more than pending.
+ desired = int32(pending)
+ }
+ return desired
+}
+
+// Start function starts the autoscaling worker group.
+// Each worker keeps picking up scaling tasks (which contains mono vertex keys) to calculate the desired replicas,
+// and patch the mono vertex spec with the new replica number if needed.
+func (s *Scaler) Start(ctx context.Context) error {
+ log := logging.FromContext(ctx).Named("mvtx-autoscaler")
+ log.Info("Starting MonoVertex autoscaler...")
+ keyCh := make(chan string)
+ ctx, cancel := context.WithCancel(logging.WithLogger(ctx, log))
+ defer cancel()
+ // Worker group
+ for i := 1; i <= s.options.workers; i++ {
+ go s.scale(ctx, i, keyCh)
+ }
+
+ // Function assign() moves an element in the list from the front to the back,
+ // and send to the channel so that it can be picked up by a worker.
+ assign := func() {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ if s.monoVtxList.Len() == 0 {
+ return
+ }
+ e := s.monoVtxList.Front()
+ if key, ok := e.Value.(string); ok {
+ s.monoVtxList.MoveToBack(e)
+ keyCh <- key
+ }
+ }
+
+ // Following for loop keeps calling assign() function to assign scaling tasks to the workers.
+ // It makes sure each element in the list will be assigned every N milliseconds.
+ for {
+ select {
+ case <-ctx.Done():
+ log.Info("Shutting down mono vertex autoscaling job assigner.")
+ // clear the daemon clients cache
+ s.mvtxDaemonClientsCache.Purge()
+ return nil
+ default:
+ assign()
+ }
+ // Make sure each of the key will be assigned at most every N milliseconds.
+ time.Sleep(time.Millisecond * time.Duration(func() int {
+ l := s.Length()
+ if l == 0 {
+ return s.options.taskInterval
+ }
+ result := s.options.taskInterval / l
+ if result > 0 {
+ return result
+ }
+ return 1
+ }()))
+ }
+}
+
+func (s *Scaler) patchMonoVertexReplicas(ctx context.Context, monoVtx *dfv1.MonoVertex, desiredReplicas int32) error {
+ log := logging.FromContext(ctx)
+ origin := monoVtx.Spec.Replicas
+ monoVtx.Spec.Replicas = ptr.To[int32](desiredReplicas)
+ body, err := json.Marshal(monoVtx)
+ if err != nil {
+ return fmt.Errorf("failed to marshal MonoVertex object to json, %w", err)
+ }
+ if err := s.client.Patch(ctx, monoVtx, client.RawPatch(types.MergePatchType, body)); err != nil && !apierrors.IsNotFound(err) {
+ return fmt.Errorf("failed to patch MonoVertex replicas, %w", err)
+ }
+ log.Infow("Auto scaling - mono vertex replicas changed.", zap.Int32p("from", origin), zap.Int32("to", desiredReplicas), zap.String("namespace", monoVtx.Namespace), zap.String("vertex", monoVtx.Name))
+ return nil
+}
+
+// KeyOfMonoVertex returns the unique key of a MonoVertex
+func KeyOfMonoVertex(monoVtx dfv1.MonoVertex) string {
+ return fmt.Sprintf("%s/%s", monoVtx.Namespace, monoVtx.Name)
+}
diff --git a/pkg/reconciler/monovertex/scaling/scaling_test.go b/pkg/reconciler/monovertex/scaling/scaling_test.go
new file mode 100644
index 0000000000..f0823fe1b5
--- /dev/null
+++ b/pkg/reconciler/monovertex/scaling/scaling_test.go
@@ -0,0 +1,17 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package scaling
diff --git a/pkg/reconciler/vertex/scaling/doc.go b/pkg/reconciler/vertex/scaling/doc.go
index a5004a6899..37f3dec1b8 100644
--- a/pkg/reconciler/vertex/scaling/doc.go
+++ b/pkg/reconciler/vertex/scaling/doc.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-// Package scaling provides the autoscaling capability for Numaflow.
+// Package scaling provides the autoscaling capability for Vertex objects.
//
// A workqueue is implemented in this package to watch vertices in the cluster,
// calculate the desired replica number for each of them periodically, and
diff --git a/pkg/reconciler/vertex/scaling/scaling.go b/pkg/reconciler/vertex/scaling/scaling.go
index 6384e12726..741f11a668 100644
--- a/pkg/reconciler/vertex/scaling/scaling.go
+++ b/pkg/reconciler/vertex/scaling/scaling.go
@@ -113,11 +113,11 @@ func (s *Scaler) StopWatching(key string) {
// It waits for keys in the channel, and starts a scaling job
func (s *Scaler) scale(ctx context.Context, id int, keyCh <-chan string) {
log := logging.FromContext(ctx)
- log.Infof("Started autoscaling worker %v", id)
+ log.Infof("Started Vertex autoscaling worker %v", id)
for {
select {
case <-ctx.Done():
- log.Infof("Stopped scaling worker %v", id)
+ log.Infof("Stopped Vertex autoscaling worker %v", id)
return
case key := <-keyCh:
if err := s.scaleOneVertex(ctx, key, id); err != nil {
@@ -385,7 +385,7 @@ func (s *Scaler) desiredReplicas(_ context.Context, vertex *dfv1.Vertex, partiti
if desired == 0 {
desired = 1
}
- if desired > int32(pending) { // For some corner cases, we don't want to scale up to more than pending.
+ if desired > int32(pending) && pending > 0 { // For some corner cases, we don't want to scale up to more than pending.
desired = int32(pending)
}
// maxDesired is the max of all partitions
@@ -400,8 +400,8 @@ func (s *Scaler) desiredReplicas(_ context.Context, vertex *dfv1.Vertex, partiti
// Each worker keeps picking up scaling tasks (which contains vertex keys) to calculate the desired replicas,
// and patch the vertex spec with the new replica number if needed.
func (s *Scaler) Start(ctx context.Context) error {
- log := logging.FromContext(ctx).Named("autoscaler")
- log.Info("Starting autoscaler...")
+ log := logging.FromContext(ctx).Named("vertex-autoscaler")
+ log.Info("Starting vertex autoscaler...")
keyCh := make(chan string)
ctx, cancel := context.WithCancel(logging.WithLogger(ctx, log))
defer cancel()
diff --git a/pkg/daemon/server/service/rater/uniq_str_list.go b/pkg/shared/util/uniq_str_list.go
similarity index 99%
rename from pkg/daemon/server/service/rater/uniq_str_list.go
rename to pkg/shared/util/uniq_str_list.go
index 8bc13f103d..4a5c09be85 100644
--- a/pkg/daemon/server/service/rater/uniq_str_list.go
+++ b/pkg/shared/util/uniq_str_list.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package rater
+package util
import (
"container/list"
diff --git a/pkg/daemon/server/service/rater/uniq_str_list_test.go b/pkg/shared/util/uniq_str_list_test.go
similarity index 99%
rename from pkg/daemon/server/service/rater/uniq_str_list_test.go
rename to pkg/shared/util/uniq_str_list_test.go
index 94f958e0cc..ab88096bf4 100644
--- a/pkg/daemon/server/service/rater/uniq_str_list_test.go
+++ b/pkg/shared/util/uniq_str_list_test.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package rater
+package util
import (
"testing"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index db2f0404bd..3b4bfaa19b 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -627,6 +627,12 @@ dependencies = [
"const-random",
]
+[[package]]
+name = "dtoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
+
[[package]]
name = "dunce"
version = "1.0.5"
@@ -1566,12 +1572,12 @@ dependencies = [
"bytes",
"chrono",
"hyper-util",
- "metrics",
- "metrics-exporter-prometheus",
"numaflow 0.1.0 (git+https://github.com/numaproj/numaflow-rs.git?branch=main)",
"numaflow-models",
"once_cell",
+ "parking_lot",
"pep440_rs",
+ "prometheus-client",
"prost",
"prost-types",
"rcgen",
@@ -2050,6 +2056,29 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "prometheus-client"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca"
+dependencies = [
+ "dtoa",
+ "itoa",
+ "parking_lot",
+ "prometheus-client-derive-encode",
+]
+
+[[package]]
+name = "prometheus-client-derive-encode"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "prost"
version = "0.13.1"
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index 3e98b10d69..4efb9658b2 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -17,8 +17,6 @@ prost = "0.13.1"
prost-types = "0.13.1"
chrono = "0.4.31"
base64 = "0.22.1"
-metrics = { version = "0.23.0", default-features = false }
-metrics-exporter-prometheus = { version = "0.15.3", default-features = false }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
hyper-util = "0.1.6"
tower = "0.4.13"
@@ -33,6 +31,8 @@ serde = { version = "1.0.204", features = ["derive"] }
semver = "1.0"
pep440_rs = "0.6.6"
backoff = { path = "../backoff" }
+parking_lot = "0.12.3"
+prometheus-client = "0.22.3"
[dev-dependencies]
tower = "0.4.13"
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index 2efd447784..f774cc80b0 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -1,15 +1,12 @@
use crate::config::config;
use crate::error::{Error, Result};
use crate::message::Offset;
-use crate::metrics::{
- FORWARDER_ACK_TOTAL, FORWARDER_READ_BYTES_TOTAL, FORWARDER_READ_TOTAL, FORWARDER_WRITE_TOTAL,
- MONO_VERTEX_NAME, PARTITION_LABEL, REPLICA_LABEL, VERTEX_TYPE_LABEL,
-};
+use crate::metrics;
+use crate::metrics::forward_metrics;
use crate::sink::{proto, SinkClient};
use crate::source::SourceClient;
use crate::transformer::TransformerClient;
use chrono::Utc;
-use metrics::counter;
use std::collections::HashMap;
use tokio::task::JoinSet;
use tokio::time::sleep;
@@ -17,8 +14,6 @@ use tokio_util::sync::CancellationToken;
use tracing::info;
use tracing::log::warn;
-const MONO_VERTEX_TYPE: &str = "mono_vertex";
-
/// Forwarder is responsible for reading messages from the source, applying transformation if
/// transformer is present, writing the messages to the sink, and then acknowledging the messages
/// back to the source.
@@ -38,15 +33,7 @@ impl Forwarder {
transformer_client: Option,
cln_token: CancellationToken,
) -> Result {
- let common_labels = vec![
- (
- MONO_VERTEX_NAME.to_string(),
- config().mono_vertex_name.clone(),
- ),
- (VERTEX_TYPE_LABEL.to_string(), MONO_VERTEX_TYPE.to_string()),
- (REPLICA_LABEL.to_string(), config().replica.to_string()),
- (PARTITION_LABEL.to_string(), "0".to_string()),
- ];
+ let common_labels = metrics::forward_metrics_labels().clone();
Ok(Self {
source_client,
@@ -76,14 +63,14 @@ impl Forwarder {
// Read messages from the source
let messages = result?;
info!("Read batch size: {} and latency - {}ms", messages.len(), start_time.elapsed().as_millis());
-
+ // emit metrics
+ let msg_count = messages.len() as u64;
// collect all the offsets as the transformer can drop (via filter) messages
let offsets = messages.iter().map(|msg| msg.offset.clone()).collect::>();
-
messages_count += messages.len() as u64;
let bytes_count = messages.iter().map(|msg| msg.value.len() as u64).sum::();
- counter!(FORWARDER_READ_TOTAL, &self.common_labels).increment(messages_count);
- counter!(FORWARDER_READ_BYTES_TOTAL, &self.common_labels).increment(bytes_count);
+ forward_metrics().monovtx_read_total.get_or_create(&self.common_labels).inc_by(msg_count);
+ forward_metrics().monovtx_read_bytes_total.get_or_create(&self.common_labels).inc_by(bytes_count);
// Apply transformation if transformer is present
let transformed_messages = if let Some(transformer_client) = &self.transformer_client {
@@ -107,10 +94,14 @@ impl Forwarder {
messages
};
+ let transformed_msg_count = transformed_messages.len() as u64;
+ forward_metrics().monovtx_sink_write_total.get_or_create(&self.common_labels).inc_by(transformed_msg_count);
+
// Write messages to the sink
// TODO: should we retry writing? what if the error is transient?
// we could rely on gRPC retries and say that any error that is bubbled up is worthy of non-0 exit.
// we need to confirm this via FMEA tests.
+
let mut retry_messages = transformed_messages;
let mut attempts = 0;
let mut error_map = HashMap::new();
@@ -125,7 +116,6 @@ impl Forwarder {
.filter(|result| result.status != proto::Status::Success as i32)
.map(|result| result.id.clone())
.collect();
-
attempts += 1;
if failed_ids.is_empty() {
@@ -154,13 +144,12 @@ impl Forwarder {
attempts, error_map
)));
}
-
// Acknowledge the messages back to the source
let start_time = tokio::time::Instant::now();
self.source_client.ack_fn(offsets).await?;
info!("Ack latency - {}ms", start_time.elapsed().as_millis());
-
- counter!(FORWARDER_ACK_TOTAL, &self.common_labels).increment(messages_count);
+ // increment the acked messages count metric
+ forward_metrics().monovtx_ack_total.get_or_create(&self.common_labels).inc_by(msg_count);
}
}
// if the last forward was more than 1 second ago, forward a chunk print the number of messages forwarded
@@ -173,6 +162,10 @@ impl Forwarder {
messages_count = 0;
last_forwarded_at = std::time::Instant::now();
}
+ forward_metrics()
+ .monovtx_processing_time
+ .get_or_create(&self.common_labels)
+ .observe(start_time.elapsed().as_micros() as f64);
}
Ok(())
}
diff --git a/rust/monovertex/src/lib.rs b/rust/monovertex/src/lib.rs
index 823ffc6870..f3864f7531 100644
--- a/rust/monovertex/src/lib.rs
+++ b/rust/monovertex/src/lib.rs
@@ -24,7 +24,7 @@ use tracing_subscriber::EnvFilter;
/// - Send Acknowledgement back to the Source
pub mod error;
-pub mod metrics;
+mod metrics;
pub mod source;
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index f3d5421dbc..cd792d9693 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -1,15 +1,13 @@
-use std::future::ready;
use std::net::SocketAddr;
-use std::sync::Arc;
+use std::sync::{Arc, OnceLock};
use std::time::Duration;
+use axum::body::Body;
use axum::extract::State;
-use axum::http::StatusCode;
+use axum::http::{Response, StatusCode};
use axum::response::IntoResponse;
use axum::{routing::get, Router};
use axum_server::tls_rustls::RustlsConfig;
-use metrics::describe_counter;
-use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle};
use rcgen::{generate_simple_self_signed, CertifiedKey};
use tokio::net::{TcpListener, ToSocketAddrs};
use tokio::sync::Mutex;
@@ -17,22 +15,36 @@ use tokio::task::JoinHandle;
use tokio::time;
use tracing::{debug, error, info};
+use crate::config::config;
use crate::error::Error;
use crate::sink::SinkClient;
use crate::source::SourceClient;
use crate::transformer::TransformerClient;
+use prometheus_client::encoding::text::encode;
+use prometheus_client::metrics::counter::Counter;
+use prometheus_client::metrics::family::Family;
+use prometheus_client::metrics::gauge::Gauge;
+use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
+use prometheus_client::registry::Registry;
// Define the labels for the metrics
-pub const MONO_VERTEX_NAME: &str = "vertex";
-pub const REPLICA_LABEL: &str = "replica";
-pub const PARTITION_LABEL: &str = "partition_name";
-pub const VERTEX_TYPE_LABEL: &str = "vertex_type";
+// Note: Please keep consistent with the definitions in MonoVertex daemon
+pub const MONO_VERTEX_NAME_LABEL: &str = "mvtx_name";
+pub const REPLICA_LABEL: &str = "mvtx_replica";
+const PENDING_PERIOD_LABEL: &str = "period";
// Define the metrics
-pub const FORWARDER_READ_TOTAL: &str = "forwarder_read_total";
-pub const FORWARDER_READ_BYTES_TOTAL: &str = "forwarder_read_bytes_total";
-pub const FORWARDER_ACK_TOTAL: &str = "forwarder_ack_total";
-pub const FORWARDER_WRITE_TOTAL: &str = "forwarder_write_total";
+// Note: We do not add a suffix to the metric name, as the suffix is inferred through the metric type
+// by the prometheus client library
+// refer: https://github.com/prometheus/client_rust/blob/master/src/registry.rs#L102
+
+// Note: Please keep consistent with the definitions in MonoVertex daemon
+const MONOVTX_READ_TOTAL: &str = "monovtx_read";
+const MONOVTX_READ_BYTES_TOTAL: &str = "monovtx_read_bytes";
+const MONOVTX_ACK_TOTAL: &str = "monovtx_ack";
+const MONOVTX_SINK_WRITE_TOTAL: &str = "monovtx_sink_write";
+const MONOVTX_PROCESSING_TIME: &str = "monovtx_processing_time";
+const MONOVTX_PENDING: &str = "monovtx_pending";
#[derive(Clone)]
pub(crate) struct MetricsState {
@@ -41,6 +53,151 @@ pub(crate) struct MetricsState {
pub transformer_client: Option,
}
+/// The global register of all metrics.
+#[derive(Default)]
+pub struct GlobalRegistry {
+ // It is okay to use std mutex because we register each metric only one time.
+ pub registry: parking_lot::Mutex,
+}
+
+impl GlobalRegistry {
+ fn new() -> Self {
+ GlobalRegistry {
+ // Create a new registry for the metrics
+ registry: parking_lot::Mutex::new(Registry::default()),
+ }
+ }
+}
+
+/// GLOBAL_REGISTER is the static global registry which is initialized
+// only once.
+static GLOBAL_REGISTER: OnceLock = OnceLock::new();
+
+/// global_registry is a helper function to get the GLOBAL_REGISTER
+fn global_registry() -> &'static GlobalRegistry {
+ GLOBAL_REGISTER.get_or_init(GlobalRegistry::new)
+}
+
+// TODO: let's do sub-registry for forwarder so tomorrow we can add sink and source metrics.
+/// MonoVtxMetrics is a struct which is used for storing the metrics related to MonoVertex
+// These fields are exposed as pub to be used by other modules for
+// changing the value of the metrics
+// Each metric is defined as family of metrics, which means that they can be
+// differentiated by their label values assigned.
+// The labels are provided in the form of Vec<(String, String)
+// The second argument is the metric kind.
+pub struct MonoVtxMetrics {
+ pub monovtx_read_total: Family, Counter>,
+ pub monovtx_read_bytes_total: Family, Counter>,
+ pub monovtx_ack_total: Family, Counter>,
+ pub monovtx_sink_write_total: Family, Counter>,
+ pub monovtx_processing_time: Family, Histogram>,
+ pub monovtx_pending: Family, Gauge>,
+}
+
+/// impl the MonoVtxMetrics struct and create a new object
+impl MonoVtxMetrics {
+ fn new() -> Self {
+ let monovtx_read_total = Family::, Counter>::default();
+ let monovtx_ack_total = Family::, Counter>::default();
+ let monovtx_read_bytes_total = Family::, Counter>::default();
+ let monovtx_sink_write_total = Family::, Counter>::default();
+
+ let monovtx_processing_time =
+ Family::, Histogram>::new_with_constructor(|| {
+ Histogram::new(exponential_buckets(100.0, 60000000.0 * 15.0, 10))
+ });
+ let monovtx_pending = Family::, Gauge>::default();
+
+ let metrics = Self {
+ monovtx_read_total,
+ monovtx_read_bytes_total,
+ monovtx_ack_total,
+ monovtx_sink_write_total,
+ monovtx_processing_time,
+ monovtx_pending,
+ };
+
+ let mut registry = global_registry().registry.lock();
+ // Register all the metrics to the global registry
+ registry.register(
+ MONOVTX_READ_TOTAL,
+ "A Counter to keep track of the total number of messages read from the source",
+ metrics.monovtx_read_total.clone(),
+ );
+ registry.register(
+ MONOVTX_SINK_WRITE_TOTAL,
+ "A Counter to keep track of the total number of messages written to the sink",
+ metrics.monovtx_sink_write_total.clone(),
+ );
+ registry.register(
+ MONOVTX_ACK_TOTAL,
+ "A Counter to keep track of the total number of messages acknowledged by the sink",
+ metrics.monovtx_ack_total.clone(),
+ );
+ registry.register(
+ MONOVTX_PROCESSING_TIME,
+ "A Histogram to keep track of the total time taken to forward a chunk, the time is in microseconds",
+ metrics.monovtx_processing_time.clone(),
+ );
+ registry.register(
+ MONOVTX_READ_BYTES_TOTAL,
+ "A Counter to keep track of the total number of bytes read from the source",
+ metrics.monovtx_read_bytes_total.clone(),
+ );
+ registry.register(
+ MONOVTX_PENDING,
+ "A Gauge to keep track of the total number of pending messages for the monovtx",
+ metrics.monovtx_pending.clone(),
+ );
+
+ metrics
+ }
+}
+
+/// MONOVTX_METRICS is the MonoVtxMetrics object which stores the metrics
+static MONOVTX_METRICS: OnceLock = OnceLock::new();
+
+// forward_metrics is a helper function used to fetch the
+// MonoVtxMetrics object
+pub(crate) fn forward_metrics() -> &'static MonoVtxMetrics {
+ MONOVTX_METRICS.get_or_init(|| {
+ let metrics = MonoVtxMetrics::new();
+ metrics
+ })
+}
+
+/// MONOVTX_METRICS_LABELS are used to store the common labels used in the metrics
+static MONOVTX_METRICS_LABELS: OnceLock> = OnceLock::new();
+
+// forward_metrics_labels is a helper function used to fetch the
+// MONOVTX_METRICS_LABELS object
+pub(crate) fn forward_metrics_labels() -> &'static Vec<(String, String)> {
+ crate::metrics::MONOVTX_METRICS_LABELS.get_or_init(|| {
+ let common_labels = vec![
+ (
+ MONO_VERTEX_NAME_LABEL.to_string(),
+ config().mono_vertex_name.clone(),
+ ),
+ (REPLICA_LABEL.to_string(), config().replica.to_string()),
+ ];
+ common_labels
+ })
+}
+
+// metrics_handler is used to generate and return a snapshot of the
+// current state of the metrics in the global registry
+pub async fn metrics_handler() -> impl IntoResponse {
+ let state = global_registry().registry.lock();
+ let mut buffer = String::new();
+ encode(&mut buffer, &*state).unwrap();
+ debug!("Exposing Metrics: {:?}", buffer);
+ Response::builder()
+ .status(StatusCode::OK)
+ .body(Body::from(buffer))
+ .unwrap()
+}
+
/// Collect and emit prometheus metrics.
/// Metrics router and server over HTTP endpoint.
// This is not used currently
@@ -54,17 +211,11 @@ pub(crate) async fn start_metrics_http_server(
where
A: ToSocketAddrs + std::fmt::Debug,
{
- // setup_metrics_recorder should only be invoked once
- let recorder_handle = setup_metrics_recorder()?;
-
- let metrics_app = metrics_router(
- recorder_handle,
- MetricsState {
- source_client,
- sink_client,
- transformer_client,
- },
- );
+ let metrics_app = metrics_router(MetricsState {
+ source_client,
+ sink_client,
+ transformer_client,
+ });
let listener = TcpListener::bind(&addr)
.await
@@ -92,10 +243,7 @@ pub(crate) async fn start_metrics_https_server(
.await
.map_err(|e| Error::MetricsError(format!("Creating tlsConfig from pem: {}", e)))?;
- // setup_metrics_recorder should only be invoked once
- let recorder_handle = setup_metrics_recorder()?;
-
- let metrics_app = metrics_router(recorder_handle, metrics_state);
+ let metrics_app = metrics_router(metrics_state);
axum_server::bind_rustls(addr, tls_config)
.serve(metrics_app.into_make_service())
@@ -106,9 +254,9 @@ pub(crate) async fn start_metrics_https_server(
}
/// router for metrics and k8s health endpoints
-fn metrics_router(recorder_handle: PrometheusHandle, metrics_state: MetricsState) -> Router {
+fn metrics_router(metrics_state: MetricsState) -> Router {
Router::new()
- .route("/metrics", get(move || ready(recorder_handle.render())))
+ .route("/metrics", get(metrics_handler))
.route("/livez", get(livez))
.route("/readyz", get(readyz))
.route("/sidecar-livez", get(sidecar_livez))
@@ -138,45 +286,6 @@ async fn sidecar_livez(State(mut state): State) -> impl IntoRespon
StatusCode::NO_CONTENT
}
-/// setup the Prometheus metrics recorder.
-fn setup_metrics_recorder() -> crate::Result {
- // 1 micro-sec < t < 1000 seconds
- let log_to_power_of_sqrt2_bins: [f64; 62] = (0..62)
- .map(|i| 2_f64.sqrt().powf(i as f64))
- .collect::>()
- .try_into()
- .unwrap();
-
- let prometheus_handle = PrometheusBuilder::new()
- .set_buckets_for_metric(
- Matcher::Full("fac_total_duration_micros".to_string()), // fac == forward-a-chunk
- &log_to_power_of_sqrt2_bins,
- )
- .map_err(|e| Error::MetricsError(format!("Prometheus install_recorder: {}", e)))?
- .install_recorder()
- .map_err(|e| Error::MetricsError(format!("Prometheus install_recorder: {}", e)))?;
-
- // Define forwarder metrics
- describe_counter!(
- FORWARDER_READ_TOTAL,
- "Total number of Data Messages Read in the forwarder"
- );
- describe_counter!(
- FORWARDER_READ_BYTES_TOTAL,
- "Total number of bytes read in the forwarder"
- );
- describe_counter!(
- FORWARDER_ACK_TOTAL,
- "Total number of acknowledgments by the forwarder"
- );
- describe_counter!(
- FORWARDER_WRITE_TOTAL,
- "Total number of Data Messages written by the forwarder"
- );
-
- Ok(prometheus_handle)
-}
-
const MAX_PENDING_STATS: usize = 1800;
// Pending info with timestamp
@@ -319,7 +428,12 @@ async fn expose_pending_metrics(
for (label, seconds) in &lookback_seconds_map {
let pending = calculate_pending(*seconds, &pending_stats).await;
if pending != -1 {
- // TODO: emit it as a metric
+ let mut metric_labels = forward_metrics_labels().clone();
+ metric_labels.push((PENDING_PERIOD_LABEL.to_string(), label.to_string()));
+ forward_metrics()
+ .monovtx_pending
+ .get_or_create(&metric_labels)
+ .set(pending);
info!("Pending messages ({}): {}", label, pending);
}
}
diff --git a/server/apis/interface.go b/server/apis/interface.go
index ff41eb3ebe..2e2c87d30f 100644
--- a/server/apis/interface.go
+++ b/server/apis/interface.go
@@ -46,4 +46,5 @@ type Handler interface {
GetMonoVertex(c *gin.Context)
ListMonoVertexPods(c *gin.Context)
CreateMonoVertex(c *gin.Context)
+ GetMonoVertexMetrics(c *gin.Context)
}
diff --git a/server/apis/v1/handler.go b/server/apis/v1/handler.go
index 59dc00c810..fb53cfbe42 100644
--- a/server/apis/v1/handler.go
+++ b/server/apis/v1/handler.go
@@ -46,6 +46,7 @@ import (
dfv1versiond "github.com/numaproj/numaflow/pkg/client/clientset/versioned"
dfv1clients "github.com/numaproj/numaflow/pkg/client/clientset/versioned/typed/numaflow/v1alpha1"
daemonclient "github.com/numaproj/numaflow/pkg/daemon/client"
+ mvtdaemonclient "github.com/numaproj/numaflow/pkg/mvtxdaemon/client"
"github.com/numaproj/numaflow/pkg/shared/util"
"github.com/numaproj/numaflow/pkg/webhook/validator"
"github.com/numaproj/numaflow/server/authn"
@@ -89,14 +90,15 @@ func WithReadOnlyMode() HandlerOption {
}
type handler struct {
- kubeClient kubernetes.Interface
- metricsClient *metricsversiond.Clientset
- numaflowClient dfv1clients.NumaflowV1alpha1Interface
- daemonClientsCache *lru.Cache[string, daemonclient.DaemonClient]
- dexObj *DexObject
- localUsersAuthObject *LocalUsersAuthObject
- healthChecker *HealthChecker
- opts *handlerOptions
+ kubeClient kubernetes.Interface
+ metricsClient *metricsversiond.Clientset
+ numaflowClient dfv1clients.NumaflowV1alpha1Interface
+ daemonClientsCache *lru.Cache[string, daemonclient.DaemonClient]
+ mvtDaemonClientsCache *lru.Cache[string, mvtdaemonclient.MonoVertexDaemonClient]
+ dexObj *DexObject
+ localUsersAuthObject *LocalUsersAuthObject
+ healthChecker *HealthChecker
+ opts *handlerOptions
}
// NewHandler is used to provide a new instance of the handler type
@@ -118,6 +120,9 @@ func NewHandler(ctx context.Context, dexObj *DexObject, localUsersAuthObject *Lo
daemonClientsCache, _ := lru.NewWithEvict[string, daemonclient.DaemonClient](500, func(key string, value daemonclient.DaemonClient) {
_ = value.Close()
})
+ mvtDaemonClientsCache, _ := lru.NewWithEvict[string, mvtdaemonclient.MonoVertexDaemonClient](500, func(key string, value mvtdaemonclient.MonoVertexDaemonClient) {
+ _ = value.Close()
+ })
o := defaultHandlerOptions()
for _, opt := range opts {
if opt != nil {
@@ -125,14 +130,15 @@ func NewHandler(ctx context.Context, dexObj *DexObject, localUsersAuthObject *Lo
}
}
return &handler{
- kubeClient: kubeClient,
- metricsClient: metricsClient,
- numaflowClient: numaflowClient,
- daemonClientsCache: daemonClientsCache,
- dexObj: dexObj,
- localUsersAuthObject: localUsersAuthObject,
- healthChecker: NewHealthChecker(ctx),
- opts: o,
+ kubeClient: kubeClient,
+ metricsClient: metricsClient,
+ numaflowClient: numaflowClient,
+ daemonClientsCache: daemonClientsCache,
+ mvtDaemonClientsCache: mvtDaemonClientsCache,
+ dexObj: dexObj,
+ localUsersAuthObject: localUsersAuthObject,
+ healthChecker: NewHealthChecker(ctx),
+ opts: o,
}, nil
}
@@ -434,7 +440,7 @@ func (h *handler) GetPipeline(c *gin.Context) {
}
// get pipeline lag
- client, err := h.getDaemonClient(ns, pipeline)
+ client, err := h.getPipelineDaemonClient(ns, pipeline)
if err != nil || client == nil {
h.respondWithError(c, fmt.Sprintf("failed to get daemon service client for pipeline %q, %s", pipeline, err.Error()))
return
@@ -551,7 +557,9 @@ func (h *handler) DeletePipeline(c *gin.Context) {
}
// cleanup client after successfully deleting pipeline
- h.daemonClientsCache.Remove(daemonSvcAddress(ns, pipeline))
+ // NOTE: if a pipeline was deleted by not through UI, the cache will not be updated,
+ // the entry becomes invalid and will be evicted only after the cache is full.
+ h.daemonClientsCache.Remove(pipelineDaemonSvcAddress(ns, pipeline))
c.JSON(http.StatusOK, NewNumaflowAPIResponse(nil, nil))
}
@@ -739,7 +747,7 @@ func (h *handler) DeleteInterStepBufferService(c *gin.Context) {
func (h *handler) ListPipelineBuffers(c *gin.Context) {
ns, pipeline := c.Param("namespace"), c.Param("pipeline")
- client, err := h.getDaemonClient(ns, pipeline)
+ client, err := h.getPipelineDaemonClient(ns, pipeline)
if err != nil || client == nil {
h.respondWithError(c, fmt.Sprintf("failed to get daemon service client for pipeline %q, %s", pipeline, err.Error()))
return
@@ -758,7 +766,7 @@ func (h *handler) ListPipelineBuffers(c *gin.Context) {
func (h *handler) GetPipelineWatermarks(c *gin.Context) {
ns, pipeline := c.Param("namespace"), c.Param("pipeline")
- client, err := h.getDaemonClient(ns, pipeline)
+ client, err := h.getPipelineDaemonClient(ns, pipeline)
if err != nil || client == nil {
h.respondWithError(c, fmt.Sprintf("failed to get daemon service client for pipeline %q, %s", pipeline, err.Error()))
return
@@ -850,7 +858,7 @@ func (h *handler) GetVerticesMetrics(c *gin.Context) {
return
}
- client, err := h.getDaemonClient(ns, pipeline)
+ client, err := h.getPipelineDaemonClient(ns, pipeline)
if err != nil || client == nil {
h.respondWithError(c, fmt.Sprintf("failed to get daemon service client for pipeline %q, %s", pipeline, err.Error()))
return
@@ -1013,7 +1021,7 @@ func (h *handler) GetPipelineStatus(c *gin.Context) {
}
// Get a new daemon client for the given pipeline
- client, err := h.getDaemonClient(ns, pipeline)
+ client, err := h.getPipelineDaemonClient(ns, pipeline)
if err != nil || client == nil {
h.respondWithError(c, fmt.Sprintf("failed to get daemon service client for pipeline %q, %s", pipeline, err.Error()))
return
@@ -1118,6 +1126,25 @@ func (h *handler) ListMonoVertexPods(c *gin.Context) {
c.JSON(http.StatusOK, NewNumaflowAPIResponse(nil, pods.Items))
}
+// GetMonoVertexMetrics is used to provide information about one mono vertex, including processing rates.
+func (h *handler) GetMonoVertexMetrics(c *gin.Context) {
+ ns, monoVertex := c.Param("namespace"), c.Param("mono-vertex")
+
+ client, err := h.getMonoVertexDaemonClient(ns, monoVertex)
+ if err != nil || client == nil {
+ h.respondWithError(c, fmt.Sprintf("failed to get daemon service client for mono vertex %q, %s", monoVertex, err.Error()))
+ return
+ }
+
+ metrics, err := client.GetMonoVertexMetrics(c)
+ if err != nil {
+ h.respondWithError(c, fmt.Sprintf("Failed to get the mono vertex metrics: namespace %q mono vertex %q: %s", ns, monoVertex, err.Error()))
+ return
+ }
+
+ c.JSON(http.StatusOK, NewNumaflowAPIResponse(nil, metrics))
+}
+
// getAllNamespaces is a utility used to fetch all the namespaces in the cluster
// except the kube system namespaces
func getAllNamespaces(h *handler) ([]string, error) {
@@ -1312,26 +1339,54 @@ func validatePipelinePatch(patch []byte) error {
return nil
}
-func daemonSvcAddress(ns, pipeline string) string {
- return fmt.Sprintf("%s.%s.svc:%d", fmt.Sprintf("%s-daemon-svc", pipeline), ns, dfv1.DaemonServicePort)
+func pipelineDaemonSvcAddress(ns, pipelineName string) string {
+ // the format is consistent with what we defined in GetDaemonServiceURL in `pkg/apis/numaflow/v1alpha1/pipeline_types.go`
+ // do not change it without changing the other.
+ return fmt.Sprintf("%s.%s.svc:%d", fmt.Sprintf("%s-daemon-svc", pipelineName), ns, dfv1.DaemonServicePort)
}
-func (h *handler) getDaemonClient(ns, pipeline string) (daemonclient.DaemonClient, error) {
- if dClient, ok := h.daemonClientsCache.Get(daemonSvcAddress(ns, pipeline)); !ok {
+func monoVertexDaemonSvcAddress(ns, monoVertexName string) string {
+ // the format is consistent with what we defined in GetDaemonServiceURL in `pkg/apis/numaflow/v1alpha1/mono_vertex_types.go`
+ // do not change it without changing the other.
+ return fmt.Sprintf("%s.%s.svc:%d", fmt.Sprintf("%s-mv-daemon-svc", monoVertexName), ns, dfv1.MonoVertexDaemonServicePort)
+}
+
+func (h *handler) getPipelineDaemonClient(ns, pipeline string) (daemonclient.DaemonClient, error) {
+ if dClient, ok := h.daemonClientsCache.Get(pipelineDaemonSvcAddress(ns, pipeline)); !ok {
var err error
var c daemonclient.DaemonClient
// Default to use gRPC client
if strings.EqualFold(h.opts.daemonClientProtocol, "http") {
- c, err = daemonclient.NewRESTfulDaemonServiceClient(daemonSvcAddress(ns, pipeline))
+ c, err = daemonclient.NewRESTfulDaemonServiceClient(pipelineDaemonSvcAddress(ns, pipeline))
} else {
- c, err = daemonclient.NewGRPCDaemonServiceClient(daemonSvcAddress(ns, pipeline))
+ c, err = daemonclient.NewGRPCDaemonServiceClient(pipelineDaemonSvcAddress(ns, pipeline))
}
if err != nil {
return nil, err
}
- h.daemonClientsCache.Add(daemonSvcAddress(ns, pipeline), c)
+ h.daemonClientsCache.Add(pipelineDaemonSvcAddress(ns, pipeline), c)
return c, nil
} else {
return dClient, nil
}
}
+
+func (h *handler) getMonoVertexDaemonClient(ns, mvtName string) (mvtdaemonclient.MonoVertexDaemonClient, error) {
+ if mvtDaemonClient, ok := h.mvtDaemonClientsCache.Get(monoVertexDaemonSvcAddress(ns, mvtName)); !ok {
+ var err error
+ var c mvtdaemonclient.MonoVertexDaemonClient
+ // Default to use gRPC client
+ if strings.EqualFold(h.opts.daemonClientProtocol, "http") {
+ c, err = mvtdaemonclient.NewRESTfulClient(monoVertexDaemonSvcAddress(ns, mvtName))
+ } else {
+ c, err = mvtdaemonclient.NewGRPCClient(monoVertexDaemonSvcAddress(ns, mvtName))
+ }
+ if err != nil {
+ return nil, err
+ }
+ h.mvtDaemonClientsCache.Add(monoVertexDaemonSvcAddress(ns, mvtName), c)
+ return c, nil
+ } else {
+ return mvtDaemonClient, nil
+ }
+}
diff --git a/server/cmd/server/start.go b/server/cmd/server/start.go
index eccfca45a3..16e52eaf62 100644
--- a/server/cmd/server/start.go
+++ b/server/cmd/server/start.go
@@ -203,6 +203,7 @@ func CreateAuthRouteMap(baseHref string) authz.RouteMap {
"GET:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
"GET:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices/:mono-vertex": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
"GET:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices/:mono-vertex/pods": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
+ "GET:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices/:mono-vertex/metrics": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
"POST:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
}
}
diff --git a/server/cmd/server/start_test.go b/server/cmd/server/start_test.go
index 1df0d5b8f9..b8abd6291e 100644
--- a/server/cmd/server/start_test.go
+++ b/server/cmd/server/start_test.go
@@ -25,12 +25,12 @@ import (
func TestCreateAuthRouteMap(t *testing.T) {
t.Run("empty base", func(t *testing.T) {
got := CreateAuthRouteMap("")
- assert.Equal(t, 28, len(got))
+ assert.Equal(t, 29, len(got))
})
t.Run("customize base", func(t *testing.T) {
got := CreateAuthRouteMap("abcdefg")
- assert.Equal(t, 28, len(got))
+ assert.Equal(t, 29, len(got))
for k := range got {
assert.Contains(t, k, "abcdefg")
}
diff --git a/server/routes/routes.go b/server/routes/routes.go
index deb0cbcb6e..68064e8c52 100644
--- a/server/routes/routes.go
+++ b/server/routes/routes.go
@@ -161,6 +161,8 @@ func v1Routes(ctx context.Context, r gin.IRouter, dexObj *v1.DexObject, localUse
r.GET("/namespaces/:namespace/mono-vertices/:mono-vertex/pods", handler.ListMonoVertexPods)
// Create a mono vertex.
r.POST("/namespaces/:namespace/mono-vertices", handler.CreateMonoVertex)
+ // Get the metrics of a mono vertex.
+ r.GET("/namespaces/:namespace/mono-vertices/:mono-vertex/metrics", handler.GetMonoVertexMetrics)
}
// authMiddleware is the middleware for AuthN/AuthZ.
From c4b5d05c24c189684043688fa657295bf4495dcd Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Thu, 15 Aug 2024 11:27:36 -0700
Subject: [PATCH 002/188] fix: default resources mutated when applying
templates (#1948)
Signed-off-by: Derek Wang
---
.../numaflow/v1alpha1/container_builder.go | 2 +-
.../numaflow/v1alpha1/vertex_types_test.go | 29 +++++++++++++++++++
2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/pkg/apis/numaflow/v1alpha1/container_builder.go b/pkg/apis/numaflow/v1alpha1/container_builder.go
index 0730993246..dcded47e9c 100644
--- a/pkg/apis/numaflow/v1alpha1/container_builder.go
+++ b/pkg/apis/numaflow/v1alpha1/container_builder.go
@@ -25,7 +25,7 @@ func (b containerBuilder) init(req getContainerReq) containerBuilder {
b.Image = req.image
b.ImagePullPolicy = req.imagePullPolicy
b.Name = CtrMain
- b.Resources = req.resources
+ b.Resources = *req.resources.DeepCopy()
b.VolumeMounts = req.volumeMounts
return b
}
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
index a664fee1d0..4336e15e31 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
+ resource "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
)
@@ -198,7 +199,15 @@ func TestGetPodSpec(t *testing.T) {
{Name: "test-env", Value: "test-val"},
},
SideInputsStoreName: "test-store",
+ DefaultResources: corev1.ResourceRequirements{
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("100m"),
+ corev1.ResourceMemory: resource.MustParse("100Mi"),
+ },
+ Limits: corev1.ResourceList{},
+ },
}
+
t.Run("test source", func(t *testing.T) {
testObj := testVertex.DeepCopy()
testObj.Spec.Source = &Source{}
@@ -215,6 +224,18 @@ func TestGetPodSpec(t *testing.T) {
DNSPolicy: corev1.DNSClusterFirstWithHostNet,
DNSConfig: &corev1.PodDNSConfig{Nameservers: []string{"aaa.aaa"}},
}
+ testObj.Spec.ContainerTemplate = &ContainerTemplate{
+ Resources: corev1.ResourceRequirements{
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("200m"),
+ corev1.ResourceMemory: resource.MustParse("200Mi"),
+ },
+ Limits: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("200m"),
+ corev1.ResourceMemory: resource.MustParse("200Mi"),
+ },
+ },
+ }
s, err := testObj.GetPodSpec(req)
assert.NoError(t, err)
assert.NotNil(t, s.NodeSelector)
@@ -252,6 +273,14 @@ func TestGetPodSpec(t *testing.T) {
assert.Contains(t, s.Containers[0].Args, "--type="+string(VertexTypeSource))
assert.Equal(t, 1, len(s.InitContainers))
assert.Equal(t, CtrInit, s.InitContainers[0].Name)
+ assert.Equal(t, "200m", s.Containers[0].Resources.Requests.Cpu().String())
+ assert.Equal(t, "200m", s.Containers[0].Resources.Limits.Cpu().String())
+ assert.Equal(t, "200Mi", s.Containers[0].Resources.Requests.Memory().String())
+ assert.Equal(t, "200Mi", s.Containers[0].Resources.Limits.Memory().String())
+ assert.Equal(t, "100m", s.InitContainers[0].Resources.Requests.Cpu().String())
+ assert.Equal(t, "100Mi", s.InitContainers[0].Resources.Requests.Memory().String())
+ assert.Equal(t, "0", s.InitContainers[0].Resources.Limits.Cpu().String())
+ assert.Equal(t, "0", s.InitContainers[0].Resources.Limits.Memory().String())
})
t.Run("test sink", func(t *testing.T) {
From 7b85e89f307c2286c5b57daf3fb17d70a08107c0 Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Fri, 16 Aug 2024 08:29:07 +0530
Subject: [PATCH 003/188] test: add mono vertex e2e tests (#1945)
Signed-off-by: Yashash H L
Signed-off-by: Keran Yang
Co-authored-by: Keran Yang
---
.github/workflows/ci.yaml | 2 +-
pkg/reconciler/monovertex/controller.go | 20 ++--
test/diamond-e2e/diamond_test.go | 20 ++--
test/diamond-e2e/testdata/cycle-backward.yaml | 4 +
test/diamond-e2e/testdata/cycle-to-self.yaml | 4 +
...-on-map-pipeline.yaml => join-on-map.yaml} | 4 +
...duce-pipeline.yaml => join-on-reduce.yaml} | 4 +
test/diamond-e2e/testdata/join-on-sink.yaml | 5 +-
test/e2e-api/redis.go | 6 +-
test/e2e/functional_test.go | 31 +++--
test/e2e/testdata/even-odd.yaml | 14 +++
test/e2e/testdata/simple-fallback.yaml | 4 +
test/e2e/testdata/udf-filtering.yaml | 4 +
test/fixtures/e2e_suite.go | 27 +++--
test/fixtures/expect.go | 78 +++++++++----
test/fixtures/given.go | 70 +++++++++---
test/fixtures/redis.go | 7 +-
test/fixtures/redis_check.go | 24 ++--
test/fixtures/util.go | 71 ++++++++++++
test/fixtures/when.go | 106 ++++++++++++++----
test/http-e2e/http_test.go | 10 +-
.../testdata/http-source-with-auth.yaml | 4 +
test/http-e2e/testdata/http-source.yaml | 4 +
test/idle-source-e2e/idle_source_test.go | 8 +-
.../testdata/idle-source-reduce-pipeline.yaml | 4 +
.../testdata/kafka-pipeline.yaml | 4 +
test/jetstream-e2e/jetstream_test.go | 5 +-
.../testdata/jetstream-source-pipeline.yaml | 4 +
test/monovertex-e2e/monovertex_test.go | 32 ++++++
.../mono-vertex-with-transformer.yaml | 24 ++++
test/nats-e2e/nats_test.go | 2 +-
.../testdata/nats-source-pipeline.yaml | 4 +
test/reduce-one-e2e/reduce_one_test.go | 28 ++---
.../testdata/complex-reduce-pipeline.yaml | 4 +
.../complex-sliding-window-pipeline.yaml | 4 +
.../simple-keyed-reduce-pipeline.yaml | 4 +
.../simple-non-keyed-reduce-pipeline.yaml | 4 +
.../testdata/simple-reduce-pipeline-wal.yaml | 4 +
test/reduce-two-e2e/reduce_two_test.go | 28 ++---
.../reduce-stream/reduce-stream-go.yaml | 4 +
.../reduce-stream/reduce-stream-java.yaml | 4 +
...ple-session-keyed-counter-pipeline-go.yaml | 4 +
...e-session-keyed-counter-pipeline-java.yaml | 4 +
.../simple-session-sum-pipeline.yaml | 4 +
.../sideinput-e2e_sink_source_test.go | 10 +-
test/sideinputs-e2e/sideinput_test.go | 6 +-
.../testdata/map-sideinput-pipeline.yaml | 4 +
.../testdata/reduce-sideinput-pipeline.yaml | 4 +
...ideinput_sink.yaml => sideinput-sink.yaml} | 5 +-
...nput_source.yaml => sideinput-source.yaml} | 3 +
.../testdata/source-filtering.yaml | 4 +
test/transformer-e2e/transformer_test.go | 12 +-
52 files changed, 562 insertions(+), 192 deletions(-)
rename test/diamond-e2e/testdata/{join-on-map-pipeline.yaml => join-on-map.yaml} (82%)
rename test/diamond-e2e/testdata/{join-on-reduce-pipeline.yaml => join-on-reduce.yaml} (91%)
create mode 100644 test/monovertex-e2e/monovertex_test.go
create mode 100644 test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
rename test/sideinputs-e2e/testdata/{sideinput_sink.yaml => sideinput-sink.yaml} (89%)
rename test/sideinputs-e2e/testdata/{sideinput_source.yaml => sideinput-source.yaml} (91%)
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 064afdb38c..ba128ef042 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -151,7 +151,7 @@ jobs:
fail-fast: false
matrix:
driver: [jetstream]
- case: [e2e, diamond-e2e, transformer-e2e, kafka-e2e, http-e2e, nats-e2e, jetstream-e2e, sdks-e2e, reduce-one-e2e, reduce-two-e2e, udsource-e2e, api-e2e, sideinputs-e2e, idle-source-e2e]
+ case: [e2e, diamond-e2e, transformer-e2e, kafka-e2e, http-e2e, nats-e2e, jetstream-e2e, sdks-e2e, reduce-one-e2e, reduce-two-e2e, udsource-e2e, api-e2e, sideinputs-e2e, idle-source-e2e, monovertex-e2e]
include:
- driver: redis
case: e2e
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index 108580f3dc..b345369c66 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -186,21 +186,21 @@ func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1
}
}
if needToCreate {
- labels := map[string]string{}
+ podLabels := map[string]string{}
annotations := map[string]string{}
if x := monoVtx.Spec.Metadata; x != nil {
for k, v := range x.Annotations {
annotations[k] = v
}
for k, v := range x.Labels {
- labels[k] = v
+ podLabels[k] = v
}
}
- labels[dfv1.KeyPartOf] = dfv1.Project
- labels[dfv1.KeyManagedBy] = dfv1.ControllerMonoVertex
- labels[dfv1.KeyComponent] = dfv1.ComponentMonoVertex
- labels[dfv1.KeyAppName] = monoVtx.Name
- labels[dfv1.KeyMonoVertexName] = monoVtx.Name
+ podLabels[dfv1.KeyPartOf] = dfv1.Project
+ podLabels[dfv1.KeyManagedBy] = dfv1.ControllerMonoVertex
+ podLabels[dfv1.KeyComponent] = dfv1.ComponentMonoVertex
+ podLabels[dfv1.KeyAppName] = monoVtx.Name
+ podLabels[dfv1.KeyMonoVertexName] = monoVtx.Name
annotations[dfv1.KeyHash] = hash
annotations[dfv1.KeyReplica] = strconv.Itoa(replica)
// Defaults to udf
@@ -209,7 +209,7 @@ func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1
ObjectMeta: metav1.ObjectMeta{
Namespace: monoVtx.Namespace,
Name: podNamePrefix + sharedutil.RandomLowerCaseString(5),
- Labels: labels,
+ Labels: podLabels,
Annotations: annotations,
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(monoVtx.GetObjectMeta(), dfv1.MonoVertexGroupVersionKind)},
},
@@ -392,10 +392,10 @@ func (mr *monoVertexReconciler) createOrUpdateDaemonDeployment(ctx context.Conte
return nil
}
-func (r *monoVertexReconciler) findExistingPods(ctx context.Context, monoVtx *dfv1.MonoVertex) (map[string]corev1.Pod, error) {
+func (mr *monoVertexReconciler) findExistingPods(ctx context.Context, monoVtx *dfv1.MonoVertex) (map[string]corev1.Pod, error) {
pods := &corev1.PodList{}
selector, _ := labels.Parse(dfv1.KeyComponent + "=" + dfv1.ComponentMonoVertex + "," + dfv1.KeyMonoVertexName + "=" + monoVtx.Name)
- if err := r.client.List(ctx, pods, &client.ListOptions{Namespace: monoVtx.Namespace, LabelSelector: selector}); err != nil {
+ if err := mr.client.List(ctx, pods, &client.ListOptions{Namespace: monoVtx.Namespace, LabelSelector: selector}); err != nil {
return nil, fmt.Errorf("failed to list mono vertex pods: %w", err)
}
result := make(map[string]corev1.Pod)
diff --git a/test/diamond-e2e/diamond_test.go b/test/diamond-e2e/diamond_test.go
index cdb98c7783..b30c72c7c0 100644
--- a/test/diamond-e2e/diamond_test.go
+++ b/test/diamond-e2e/diamond_test.go
@@ -45,7 +45,7 @@ func (s *DiamondSuite) TestJoinOnReducePipeline() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
- w := s.Given().Pipeline("@testdata/join-on-reduce-pipeline.yaml").
+ w := s.Given().Pipeline("@testdata/join-on-reduce.yaml").
When().
CreatePipelineAndWait()
defer w.DeletePipelineAndWait()
@@ -74,13 +74,13 @@ func (s *DiamondSuite) TestJoinOnReducePipeline() {
}()
// todo: this only tests for one occurrence: ideally should verify all
w.Expect().
- SinkContains("sink", "40"). // per 10-second window: (10 * 2) * 2 atoi vertices
- SinkContains("sink", "80") // per 10-second window: 10 * (1 + 3) * 2 atoi vertices
+ RedisSinkContains("join-on-reduce-sink", "40"). // per 10-second window: (10 * 2) * 2 atoi vertices
+ RedisSinkContains("join-on-reduce-sink", "80") // per 10-second window: 10 * (1 + 3) * 2 atoi vertices
done <- struct{}{}
}
func (s *DiamondSuite) TestJoinOnMapPipeline() {
- w := s.Given().Pipeline("@testdata/join-on-map-pipeline.yaml").
+ w := s.Given().Pipeline("@testdata/join-on-map.yaml").
When().
CreatePipelineAndWait()
defer w.DeletePipelineAndWait()
@@ -93,8 +93,8 @@ func (s *DiamondSuite) TestJoinOnMapPipeline() {
w.SendMessageTo(pipelineName, "in-1", NewHttpPostRequest().WithBody([]byte("2")))
w.Expect().
- SinkContains("sink", "1").
- SinkContains("sink", "2")
+ RedisSinkContains("join-on-map-sink", "1").
+ RedisSinkContains("join-on-map-sink", "2")
}
func (s *DiamondSuite) TestJoinOnSinkVertex() {
@@ -110,8 +110,8 @@ func (s *DiamondSuite) TestJoinOnSinkVertex() {
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("888888"))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("888889")))
- w.Expect().SinkContains("out", "888888")
- w.Expect().SinkContains("out", "888889")
+ w.Expect().RedisSinkContains("join-on-sink-out", "888888")
+ w.Expect().RedisSinkContains("join-on-sink-out", "888889")
}
func (s *DiamondSuite) TestCycleToSelf() {
@@ -136,7 +136,7 @@ func (s *DiamondSuite) TestCycleToSelf() {
}
}
for i := 0; i < 10; i++ {
- w.Expect().SinkContains("out", msgs[i])
+ w.Expect().RedisSinkContains("cycle-to-self-out", msgs[i])
}
}
@@ -162,7 +162,7 @@ func (s *DiamondSuite) TestCycleBackward() {
}
}
for i := 0; i < 10; i++ {
- w.Expect().SinkContains("out", msgs[i])
+ w.Expect().RedisSinkContains("cycle-backward-out", msgs[i])
}
}
diff --git a/test/diamond-e2e/testdata/cycle-backward.yaml b/test/diamond-e2e/testdata/cycle-backward.yaml
index 5505af8e0f..7312fc6cce 100644
--- a/test/diamond-e2e/testdata/cycle-backward.yaml
+++ b/test/diamond-e2e/testdata/cycle-backward.yaml
@@ -26,6 +26,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "cycle-backward-out"
edges:
- from: in
to: cat
diff --git a/test/diamond-e2e/testdata/cycle-to-self.yaml b/test/diamond-e2e/testdata/cycle-to-self.yaml
index 063354d5ff..eaa7bf9cfa 100644
--- a/test/diamond-e2e/testdata/cycle-to-self.yaml
+++ b/test/diamond-e2e/testdata/cycle-to-self.yaml
@@ -22,6 +22,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "cycle-to-self-out"
edges:
- from: in
to: retry
diff --git a/test/diamond-e2e/testdata/join-on-map-pipeline.yaml b/test/diamond-e2e/testdata/join-on-map.yaml
similarity index 82%
rename from test/diamond-e2e/testdata/join-on-map-pipeline.yaml
rename to test/diamond-e2e/testdata/join-on-map.yaml
index 286a075d8c..6ca69cda24 100644
--- a/test/diamond-e2e/testdata/join-on-map-pipeline.yaml
+++ b/test/diamond-e2e/testdata/join-on-map.yaml
@@ -32,6 +32,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "join-on-map-sink"
edges:
- from: in-0
to: cat
diff --git a/test/diamond-e2e/testdata/join-on-reduce-pipeline.yaml b/test/diamond-e2e/testdata/join-on-reduce.yaml
similarity index 91%
rename from test/diamond-e2e/testdata/join-on-reduce-pipeline.yaml
rename to test/diamond-e2e/testdata/join-on-reduce.yaml
index 2f2066a2f0..f0ec43e19f 100644
--- a/test/diamond-e2e/testdata/join-on-reduce-pipeline.yaml
+++ b/test/diamond-e2e/testdata/join-on-reduce.yaml
@@ -53,6 +53,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "join-on-reduce-sink"
edges:
- from: in
to: atoi-0
diff --git a/test/diamond-e2e/testdata/join-on-sink.yaml b/test/diamond-e2e/testdata/join-on-sink.yaml
index 46a7294922..1f1a442b24 100644
--- a/test/diamond-e2e/testdata/join-on-sink.yaml
+++ b/test/diamond-e2e/testdata/join-on-sink.yaml
@@ -28,7 +28,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
-
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "join-on-sink-out"
edges:
- from: in
to: even-or-odd
diff --git a/test/e2e-api/redis.go b/test/e2e-api/redis.go
index ea373634ac..228da31dec 100644
--- a/test/e2e-api/redis.go
+++ b/test/e2e-api/redis.go
@@ -18,7 +18,6 @@ package main
import (
"context"
- "fmt"
"log"
"net/http"
"net/url"
@@ -62,8 +61,7 @@ func (h *RedisController) GetMsgCountContains(w http.ResponseWriter, r *http.Req
redisClient := h.getRedisClient()
- pipelineName := r.URL.Query().Get("pipelineName")
- sinkName := r.URL.Query().Get("sinkName")
+ keyName := r.URL.Query().Get("keyName")
targetStr, err := url.QueryUnescape(r.URL.Query().Get("targetStr"))
if err != nil {
log.Println(err)
@@ -71,7 +69,7 @@ func (h *RedisController) GetMsgCountContains(w http.ResponseWriter, r *http.Req
return
}
- count, err := redisClient.HGet(context.Background(), fmt.Sprintf("%s:%s", pipelineName, sinkName), targetStr).Result()
+ count, err := redisClient.HGet(context.Background(), keyName, targetStr).Result()
if err != nil {
log.Println(err)
diff --git a/test/e2e/functional_test.go b/test/e2e/functional_test.go
index 573e0d6f8f..3e9c1d70dd 100644
--- a/test/e2e/functional_test.go
+++ b/test/e2e/functional_test.go
@@ -169,15 +169,14 @@ func (s *FunctionalSuite) TestUDFFiltering() {
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte(expect3))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte(expect4)))
- w.Expect().SinkContains("out", expect3)
- w.Expect().SinkContains("out", expect4)
- w.Expect().SinkNotContains("out", expect0)
- w.Expect().SinkNotContains("out", expect1)
- w.Expect().SinkNotContains("out", expect2)
+ w.Expect().RedisSinkContains("udf-filtering-out", expect3)
+ w.Expect().RedisSinkContains("udf-filtering-out", expect4)
+ w.Expect().RedisSinkNotContains("udf-filtering-out", expect0)
+ w.Expect().RedisSinkNotContains("udf-filtering-out", expect1)
+ w.Expect().RedisSinkNotContains("udf-filtering-out", expect2)
}
func (s *FunctionalSuite) TestConditionalForwarding() {
-
// FIXME: flaky when redis is used as isb
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
s.T().SkipNow()
@@ -196,17 +195,17 @@ func (s *FunctionalSuite) TestConditionalForwarding() {
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("888889"))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("not an integer")))
- w.Expect().SinkContains("even-sink", "888888")
- w.Expect().SinkNotContains("even-sink", "888889")
- w.Expect().SinkNotContains("even-sink", "not an integer")
+ w.Expect().RedisSinkContains("even-odd-even-sink", "888888")
+ w.Expect().RedisSinkNotContains("even-odd-even-sink", "888889")
+ w.Expect().RedisSinkNotContains("even-odd-even-sink", "not an integer")
- w.Expect().SinkContains("odd-sink", "888889")
- w.Expect().SinkNotContains("odd-sink", "888888")
- w.Expect().SinkNotContains("odd-sink", "not an integer")
+ w.Expect().RedisSinkContains("even-odd-odd-sink", "888889")
+ w.Expect().RedisSinkNotContains("even-odd-odd-sink", "888888")
+ w.Expect().RedisSinkNotContains("even-odd-odd-sink", "not an integer")
- w.Expect().SinkContains("number-sink", "888888")
- w.Expect().SinkContains("number-sink", "888889")
- w.Expect().SinkNotContains("number-sink", "not an integer")
+ w.Expect().RedisSinkContains("even-odd-number-sink", "888888")
+ w.Expect().RedisSinkContains("even-odd-number-sink", "888889")
+ w.Expect().RedisSinkNotContains("even-odd-number-sink", "not an integer")
}
func (s *FunctionalSuite) TestDropOnFull() {
@@ -354,7 +353,7 @@ func (s *FunctionalSuite) TestFallbackSink() {
// wait for all the pods to come up
w.Expect().VertexPodsRunning()
- w.Expect().SinkContains("output", "fallback-message")
+ w.Expect().RedisSinkContains("simple-fallback-output", "fallback-message")
}
func TestFunctionalSuite(t *testing.T) {
diff --git a/test/e2e/testdata/even-odd.yaml b/test/e2e/testdata/even-odd.yaml
index 0c3da8cdd5..7cc6822243 100644
--- a/test/e2e/testdata/even-odd.yaml
+++ b/test/e2e/testdata/even-odd.yaml
@@ -22,18 +22,32 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "even-odd-even-sink"
- name: odd-sink
sink:
udsink:
container:
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "even-odd-odd-sink"
- name: number-sink
sink:
udsink:
container:
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "even-odd-number-sink"
edges:
- from: in
to: even-or-odd
diff --git a/test/e2e/testdata/simple-fallback.yaml b/test/e2e/testdata/simple-fallback.yaml
index de76a06d59..0e39df2ddf 100644
--- a/test/e2e/testdata/simple-fallback.yaml
+++ b/test/e2e/testdata/simple-fallback.yaml
@@ -27,6 +27,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "simple-fallback-output"
edges:
- from: in
to: cat
diff --git a/test/e2e/testdata/udf-filtering.yaml b/test/e2e/testdata/udf-filtering.yaml
index 8cc332ddfc..1c8c79057c 100644
--- a/test/e2e/testdata/udf-filtering.yaml
+++ b/test/e2e/testdata/udf-filtering.yaml
@@ -20,6 +20,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "udf-filtering-out"
edges:
- from: in
to: p1
diff --git a/test/fixtures/e2e_suite.go b/test/fixtures/e2e_suite.go
index 3012be8754..64b27e3eb9 100644
--- a/test/fixtures/e2e_suite.go
+++ b/test/fixtures/e2e_suite.go
@@ -80,12 +80,13 @@ spec:
type E2ESuite struct {
suite.Suite
- restConfig *rest.Config
- isbSvcClient flowpkg.InterStepBufferServiceInterface
- pipelineClient flowpkg.PipelineInterface
- vertexClient flowpkg.VertexInterface
- kubeClient kubernetes.Interface
- stopch chan struct{}
+ restConfig *rest.Config
+ isbSvcClient flowpkg.InterStepBufferServiceInterface
+ pipelineClient flowpkg.PipelineInterface
+ vertexClient flowpkg.VertexInterface
+ monoVertexClient flowpkg.MonoVertexInterface
+ kubeClient kubernetes.Interface
+ stopch chan struct{}
}
func (s *E2ESuite) SetupSuite() {
@@ -98,6 +99,7 @@ func (s *E2ESuite) SetupSuite() {
s.isbSvcClient = flowversiond.NewForConfigOrDie(s.restConfig).NumaflowV1alpha1().InterStepBufferServices(Namespace)
s.pipelineClient = flowversiond.NewForConfigOrDie(s.restConfig).NumaflowV1alpha1().Pipelines(Namespace)
s.vertexClient = flowversiond.NewForConfigOrDie(s.restConfig).NumaflowV1alpha1().Vertices(Namespace)
+ s.monoVertexClient = flowversiond.NewForConfigOrDie(s.restConfig).NumaflowV1alpha1().MonoVertices(Namespace)
// Clean up resources if any
s.deleteResources([]schema.GroupVersionResource{
@@ -182,12 +184,13 @@ func (s *E2ESuite) deleteResources(resources []schema.GroupVersionResource) {
func (s *E2ESuite) Given() *Given {
return &Given{
- t: s.T(),
- isbSvcClient: s.isbSvcClient,
- pipelineClient: s.pipelineClient,
- vertexClient: s.vertexClient,
- restConfig: s.restConfig,
- kubeClient: s.kubeClient,
+ t: s.T(),
+ isbSvcClient: s.isbSvcClient,
+ pipelineClient: s.pipelineClient,
+ vertexClient: s.vertexClient,
+ monoVertexClient: s.monoVertexClient,
+ restConfig: s.restConfig,
+ kubeClient: s.kubeClient,
}
}
diff --git a/test/fixtures/expect.go b/test/fixtures/expect.go
index 6438959786..2e550653fe 100644
--- a/test/fixtures/expect.go
+++ b/test/fixtures/expect.go
@@ -32,32 +32,38 @@ import (
)
type Expect struct {
- t *testing.T
- isbSvcClient flowpkg.InterStepBufferServiceInterface
- pipelineClient flowpkg.PipelineInterface
- vertexClient flowpkg.VertexInterface
- isbSvc *dfv1.InterStepBufferService
- pipeline *dfv1.Pipeline
- restConfig *rest.Config
- kubeClient kubernetes.Interface
+ t *testing.T
+ isbSvcClient flowpkg.InterStepBufferServiceInterface
+ pipelineClient flowpkg.PipelineInterface
+ vertexClient flowpkg.VertexInterface
+ monoVertexClient flowpkg.MonoVertexInterface
+ isbSvc *dfv1.InterStepBufferService
+ pipeline *dfv1.Pipeline
+ monoVertex *dfv1.MonoVertex
+ restConfig *rest.Config
+ kubeClient kubernetes.Interface
}
-func (t *Expect) SinkContains(sinkName string, targetStr string, opts ...SinkCheckOption) *Expect {
+// RedisSinkContains checks if the target string is written to the redis sink
+// hashKey is the hash key environment variable set by the sink
+// targetStr is the target string to check
+func (t *Expect) RedisSinkContains(hashKey string, targetStr string, opts ...SinkCheckOption) *Expect {
t.t.Helper()
ctx := context.Background()
- contains := RedisContains(ctx, t.pipeline.Name, sinkName, targetStr, opts...)
- if !contains {
- t.t.Fatalf("Expected redis contains target string %s written by pipeline %s, sink %s.", targetStr, t.pipeline.Name, sinkName)
+ if contains := redisContains(ctx, hashKey, targetStr, opts...); !contains {
+ t.t.Fatalf("Expected redis contains target string %s written under hash key %s.", targetStr, hashKey)
}
return t
}
-func (t *Expect) SinkNotContains(sinkName string, targetStr string, opts ...SinkCheckOption) *Expect {
+// RedisSinkNotContains checks if the target string is not written to the redis sink
+// hashKey is the hash key environment variable set by the sink
+// targetStr is the target string to check
+func (t *Expect) RedisSinkNotContains(hashKey string, targetStr string, opts ...SinkCheckOption) *Expect {
t.t.Helper()
ctx := context.Background()
- notContains := RedisNotContains(ctx, t.pipeline.Name, sinkName, targetStr, opts...)
- if !notContains {
- t.t.Fatalf("Not expected redis contains target string %s written by pipeline %s, sink %s.", targetStr, t.pipeline.Name, sinkName)
+ if notContain := redisNotContains(ctx, hashKey, targetStr, opts...); !notContain {
+ t.t.Fatalf("Not expected redis contains target string %s written under hash key %s.", targetStr, hashKey)
}
return t
}
@@ -112,6 +118,14 @@ func (t *Expect) VertexPodsRunning() *Expect {
return t
}
+func (t *Expect) MonoVertexPodsRunning() *Expect {
+ t.t.Helper()
+ if err := WaitForMonoVertexPodRunning(t.kubeClient, t.monoVertexClient, Namespace, t.monoVertex.Name, 2*time.Minute); err != nil {
+ t.t.Fatalf("Expected mono vertex %q pod running: %v", t.monoVertex.Name, err)
+ }
+ return t
+}
+
func (t *Expect) VertexSizeScaledTo(v string, size int) *Expect {
t.t.Helper()
ctx := context.Background()
@@ -141,6 +155,20 @@ func (t *Expect) VertexPodLogContains(vertexName, regex string, opts ...PodLogCh
return t
}
+func (t *Expect) MonoVertexPodLogContains(regex string, opts ...PodLogCheckOption) *Expect {
+ t.t.Helper()
+ ctx := context.Background()
+ contains, err := MonoVertexPodLogContains(ctx, t.kubeClient, Namespace, t.monoVertex.Name, regex, opts...)
+ if err != nil {
+ t.t.Fatalf("Failed to check mono vertex %q pod logs: %v", t.monoVertex.Name, err)
+ }
+ if !contains {
+ t.t.Fatalf("Expected mono vertex [%q] pod log to contain [%q] but didn't.", t.monoVertex.Name, regex)
+ }
+ t.t.Logf("Expected mono vertex %q pod contains %q", t.monoVertex.Name, regex)
+ return t
+}
+
func (t *Expect) VertexPodLogNotContains(vertexName, regex string, opts ...PodLogCheckOption) *Expect {
t.t.Helper()
ctx := context.Background()
@@ -178,13 +206,15 @@ func (t *Expect) DaemonPodLogContains(pipelineName, regex string, opts ...PodLog
func (t *Expect) When() *When {
return &When{
- t: t.t,
- isbSvcClient: t.isbSvcClient,
- pipelineClient: t.pipelineClient,
- vertexClient: t.vertexClient,
- isbSvc: t.isbSvc,
- pipeline: t.pipeline,
- restConfig: t.restConfig,
- kubeClient: t.kubeClient,
+ t: t.t,
+ isbSvcClient: t.isbSvcClient,
+ pipelineClient: t.pipelineClient,
+ vertexClient: t.vertexClient,
+ monoVertexClient: t.monoVertexClient,
+ isbSvc: t.isbSvc,
+ pipeline: t.pipeline,
+ monoVertex: t.monoVertex,
+ restConfig: t.restConfig,
+ kubeClient: t.kubeClient,
}
}
diff --git a/test/fixtures/given.go b/test/fixtures/given.go
index 448f8e10f6..30a4ab01a7 100644
--- a/test/fixtures/given.go
+++ b/test/fixtures/given.go
@@ -21,23 +21,26 @@ import (
"strings"
"testing"
- dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
- flowpkg "github.com/numaproj/numaflow/pkg/client/clientset/versioned/typed/numaflow/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/yaml"
+
+ dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ flowpkg "github.com/numaproj/numaflow/pkg/client/clientset/versioned/typed/numaflow/v1alpha1"
)
type Given struct {
- t *testing.T
- isbSvcClient flowpkg.InterStepBufferServiceInterface
- pipelineClient flowpkg.PipelineInterface
- vertexClient flowpkg.VertexInterface
- isbSvc *dfv1.InterStepBufferService
- pipeline *dfv1.Pipeline
- restConfig *rest.Config
- kubeClient kubernetes.Interface
+ t *testing.T
+ isbSvcClient flowpkg.InterStepBufferServiceInterface
+ pipelineClient flowpkg.PipelineInterface
+ vertexClient flowpkg.VertexInterface
+ monoVertexClient flowpkg.MonoVertexInterface
+ isbSvc *dfv1.InterStepBufferService
+ pipeline *dfv1.Pipeline
+ monoVertex *dfv1.MonoVertex
+ restConfig *rest.Config
+ kubeClient kubernetes.Interface
}
// creates an ISBSvc based on the parameter, this may be:
@@ -76,6 +79,23 @@ func (g *Given) Pipeline(text string) *Given {
return g
}
+// / creates a MonoVertex based on the parameter, this may be:
+//
+// 1. A file name if it starts with "@"
+// 2. Raw YAML.
+func (g *Given) MonoVertex(text string) *Given {
+ g.t.Helper()
+ g.monoVertex = &dfv1.MonoVertex{}
+ g.readResource(text, g.monoVertex)
+ l := g.monoVertex.GetLabels()
+ if l == nil {
+ l = map[string]string{}
+ }
+ l[Label] = LabelValue
+ g.monoVertex.SetLabels(l)
+ return g
+}
+
func (g *Given) WithPipeline(p *dfv1.Pipeline) *Given {
g.t.Helper()
g.pipeline = p
@@ -89,6 +109,18 @@ func (g *Given) WithPipeline(p *dfv1.Pipeline) *Given {
return g
}
+func (g *Given) WithMonoVertex(mv *dfv1.MonoVertex) *Given {
+ g.t.Helper()
+ g.monoVertex = mv
+ l := g.monoVertex.GetLabels()
+ if l == nil {
+ l = map[string]string{}
+ }
+ l[Label] = LabelValue
+ g.monoVertex.SetLabels(l)
+ return g
+}
+
func (g *Given) readResource(text string, v metav1.Object) {
g.t.Helper()
var file string
@@ -122,13 +154,15 @@ func (g *Given) readResource(text string, v metav1.Object) {
func (g *Given) When() *When {
return &When{
- t: g.t,
- isbSvcClient: g.isbSvcClient,
- pipelineClient: g.pipelineClient,
- vertexClient: g.vertexClient,
- isbSvc: g.isbSvc,
- pipeline: g.pipeline,
- restConfig: g.restConfig,
- kubeClient: g.kubeClient,
+ t: g.t,
+ isbSvcClient: g.isbSvcClient,
+ pipelineClient: g.pipelineClient,
+ vertexClient: g.vertexClient,
+ monoVertexClient: g.monoVertexClient,
+ isbSvc: g.isbSvc,
+ pipeline: g.pipeline,
+ monoVertex: g.monoVertex,
+ restConfig: g.restConfig,
+ kubeClient: g.kubeClient,
}
}
diff --git a/test/fixtures/redis.go b/test/fixtures/redis.go
index 7f0b4c6e7e..93d5d4996f 100644
--- a/test/fixtures/redis.go
+++ b/test/fixtures/redis.go
@@ -22,9 +22,10 @@ import (
"strconv"
)
-// GetMsgCountContains returns number of occurrences of the targetStr in redis that are written by pipelineName, sinkName.
-func GetMsgCountContains(pipelineName, sinkName, targetStr string) int {
- str := InvokeE2EAPI("/redis/get-msg-count-contains?pipelineName=%s&sinkName=%s&targetStr=%s", pipelineName, sinkName, url.QueryEscape(targetStr))
+// getMsgCountContains returns the number of occurrences of the targetStr in redis
+// that are written by under hash key keyName.
+func getMsgCountContains(keyName, targetStr string) int {
+ str := InvokeE2EAPI("/redis/get-msg-count-contains?keyName=%s&targetStr=%s", keyName, url.QueryEscape(targetStr))
count, err := strconv.Atoi(str)
if err != nil {
panic(fmt.Sprintf("Can't parse string %s to an integer.", str))
diff --git a/test/fixtures/redis_check.go b/test/fixtures/redis_check.go
index b73078a3e5..b784aa2d61 100644
--- a/test/fixtures/redis_check.go
+++ b/test/fixtures/redis_check.go
@@ -24,8 +24,8 @@ import (
// Retry checking redis every 5 seconds.
const retryInterval = time.Second * 5
-// RedisNotContains verifies that there is no occurrence of targetStr in redis that is written by pipelineName, sinkName.
-func RedisNotContains(ctx context.Context, pipelineName, sinkName, targetStr string, opts ...SinkCheckOption) bool {
+// redisNotContains verifies that there is no occurrence of targetStr in redis that is written under hashKey.
+func redisNotContains(ctx context.Context, hashKey, targetStr string, opts ...SinkCheckOption) bool {
o := defaultRedisCheckOptions()
for _, opt := range opts {
if opt != nil {
@@ -34,14 +34,13 @@ func RedisNotContains(ctx context.Context, pipelineName, sinkName, targetStr str
}
ctx, cancel := context.WithTimeout(ctx, o.timeout)
defer cancel()
-
return runChecks(ctx, func() bool {
- return !redisContains(pipelineName, sinkName, targetStr, 1)
+ return !redisContainsCount(hashKey, targetStr, 1)
})
}
-// RedisContains verifies that there are targetStr in redis written by pipelineName, sinkName.
-func RedisContains(ctx context.Context, pipelineName, sinkName, targetStr string, opts ...SinkCheckOption) bool {
+// redisContains verifies that there are targetStr in redis written under hashKey.
+func redisContains(ctx context.Context, hashKey, targetStr string, opts ...SinkCheckOption) bool {
o := defaultRedisCheckOptions()
for _, opt := range opts {
if opt != nil {
@@ -50,15 +49,14 @@ func RedisContains(ctx context.Context, pipelineName, sinkName, targetStr string
}
ctx, cancel := context.WithTimeout(ctx, o.timeout)
defer cancel()
-
return runChecks(ctx, func() bool {
- return redisContains(pipelineName, sinkName, targetStr, o.count)
+ return redisContainsCount(hashKey, targetStr, o.count)
})
}
-func redisContains(pipelineName, sinkName, targetStr string, expectedCount int) bool {
- // If number of matches is higher than expected, we treat it as passing the check.
- return GetMsgCountContains(pipelineName, sinkName, targetStr) >= expectedCount
+func redisContainsCount(hashKey, targetStr string, expectedCount int) bool {
+ // If the number of matches is higher than expected, we treat it as passing the check.
+ return getMsgCountContains(hashKey, targetStr) >= expectedCount
}
type redisCheckOptions struct {
@@ -96,8 +94,8 @@ type CheckFunc func() bool
// runChecks executes a performChecks function with retry strategy (retryInterval with timeout).
// If performChecks doesn't pass within timeout, runChecks returns false indicating the checks have failed.
// This is to mitigate the problem that we don't know exactly when a numaflow pipeline finishes processing our test data.
-// Please notice such approach is not strictly accurate as there can be case where runChecks passes before pipeline finishes processing data.
-// Which could result in false positive test results. e.g. checking data doesn't exist can pass before data gets persisted to redis.
+// Please notice such an approach is not strictly accurate as there can be a case where runChecks passes before the pipeline finishes processing data.
+// Which could result in false positive test results. E.g., checking data doesn't exist can pass before data gets persisted to redis.
func runChecks(ctx context.Context, performChecks CheckFunc) bool {
ticker := time.NewTicker(retryInterval)
defer ticker.Stop()
diff --git a/test/fixtures/util.go b/test/fixtures/util.go
index 73b42b9f88..2c8c8a0ae1 100644
--- a/test/fixtures/util.go
+++ b/test/fixtures/util.go
@@ -230,6 +230,65 @@ func WaitForPipelineRunning(ctx context.Context, pipelineClient flowpkg.Pipeline
}
}
+func WaitForMonoVertexRunning(ctx context.Context, monoVertexClient flowpkg.MonoVertexInterface, monoVertexName string, timeout time.Duration) error {
+ fieldSelector := "metadata.name=" + monoVertexName
+ opts := metav1.ListOptions{FieldSelector: fieldSelector}
+ watch, err := monoVertexClient.Watch(ctx, opts)
+ if err != nil {
+ return err
+ }
+ defer watch.Stop()
+ timeoutCh := make(chan bool, 1)
+ go func() {
+ time.Sleep(timeout)
+ timeoutCh <- true
+ }()
+ for {
+ select {
+ case event := <-watch.ResultChan():
+ i, ok := event.Object.(*dfv1.MonoVertex)
+ if ok {
+ if i.Status.Phase == dfv1.MonoVertexPhaseRunning {
+ return nil
+ }
+ } else {
+ return fmt.Errorf("not monovertex")
+ }
+ case <-timeoutCh:
+ return fmt.Errorf("timeout after %v waiting for MonoVertex running", timeout)
+ }
+ }
+}
+
+func WaitForMonoVertexPodRunning(kubeClient kubernetes.Interface, monoVertexClient flowpkg.MonoVertexInterface, namespace, monoVertexName string, timeout time.Duration) error {
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+ labelSelector := fmt.Sprintf("%s=%s,%s=%s", dfv1.KeyMonoVertexName, monoVertexName, dfv1.KeyComponent, dfv1.ComponentMonoVertex)
+ for {
+ select {
+ case <-ctx.Done():
+ return fmt.Errorf("timeout after %v waiting for monovertex pod running", timeout)
+ default:
+ }
+ monoVertex, err := monoVertexClient.Get(ctx, monoVertexName, metav1.GetOptions{})
+ if err != nil {
+ return fmt.Errorf("error getting the monovertex: %w", err)
+ }
+ podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector})
+ if err != nil {
+ return fmt.Errorf("error getting monovertex pod list: %w", err)
+ }
+ ok := len(podList.Items) > 0 && len(podList.Items) == monoVertex.GetReplicas() // pod number should equal to desired replicas
+ for _, p := range podList.Items {
+ ok = ok && p.Status.Phase == corev1.PodRunning
+ }
+ if ok {
+ return nil
+ }
+ time.Sleep(2 * time.Second)
+ }
+}
+
func WaitForVertexPodRunning(kubeClient kubernetes.Interface, vertexClient flowpkg.VertexInterface, namespace, pipelineName, vertexName string, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
@@ -359,6 +418,18 @@ func VertexPodLogContains(ctx context.Context, kubeClient kubernetes.Interface,
return PodsLogContains(ctx, kubeClient, namespace, regex, podList, opts...), nil
}
+func MonoVertexPodLogContains(ctx context.Context, kubeClient kubernetes.Interface, namespace, mvName, regex string, opts ...PodLogCheckOption) (bool, error) {
+ labelSelector := fmt.Sprintf("%s=%s,%s=%s", dfv1.KeyMonoVertexName, mvName, dfv1.KeyComponent, dfv1.ComponentMonoVertex)
+ podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector})
+ if err != nil {
+ return false, fmt.Errorf("error getting monovertex pods: %w", err)
+ }
+ if len(podList.Items) == 0 {
+ return false, fmt.Errorf("no monovertex pods found")
+ }
+ return PodsLogContains(ctx, kubeClient, namespace, regex, podList, opts...), nil
+}
+
func DaemonPodLogContains(ctx context.Context, kubeClient kubernetes.Interface, namespace, pipelineName, regex string, opts ...PodLogCheckOption) (bool, error) {
labelSelector := fmt.Sprintf("%s=%s,%s=%s", dfv1.KeyPipelineName, pipelineName, dfv1.KeyComponent, dfv1.ComponentDaemon)
podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"})
diff --git a/test/fixtures/when.go b/test/fixtures/when.go
index 35295d6dfb..7ab85ae772 100644
--- a/test/fixtures/when.go
+++ b/test/fixtures/when.go
@@ -31,14 +31,16 @@ import (
)
type When struct {
- t *testing.T
- isbSvcClient flowpkg.InterStepBufferServiceInterface
- pipelineClient flowpkg.PipelineInterface
- vertexClient flowpkg.VertexInterface
- isbSvc *dfv1.InterStepBufferService
- pipeline *dfv1.Pipeline
- restConfig *rest.Config
- kubeClient kubernetes.Interface
+ t *testing.T
+ isbSvcClient flowpkg.InterStepBufferServiceInterface
+ pipelineClient flowpkg.PipelineInterface
+ vertexClient flowpkg.VertexInterface
+ monoVertexClient flowpkg.MonoVertexInterface
+ isbSvc *dfv1.InterStepBufferService
+ pipeline *dfv1.Pipeline
+ monoVertex *dfv1.MonoVertex
+ restConfig *rest.Config
+ kubeClient kubernetes.Interface
portForwarderStopChannels map[string]chan struct{}
streamLogsStopChannels map[string]chan struct{}
@@ -101,6 +103,26 @@ func (w *When) CreatePipelineAndWait() *When {
return w
}
+func (w *When) CreateMonoVertexAndWait() *When {
+ w.t.Helper()
+ if w.monoVertex == nil {
+ w.t.Fatal("No MonoVertex to create")
+ }
+ w.t.Log("Creating MonoVertex", w.monoVertex.Name)
+ ctx := context.Background()
+ i, err := w.monoVertexClient.Create(ctx, w.monoVertex, metav1.CreateOptions{})
+ if err != nil {
+ w.t.Fatal(err)
+ } else {
+ w.monoVertex = i
+ }
+ // wait
+ if err := WaitForMonoVertexRunning(ctx, w.monoVertexClient, w.monoVertex.Name, defaultTimeout); err != nil {
+ w.t.Fatal(err)
+ }
+ return w
+}
+
func (w *When) DeletePipelineAndWait() *When {
w.t.Helper()
if w.pipeline == nil {
@@ -133,6 +155,38 @@ func (w *When) DeletePipelineAndWait() *When {
}
}
+func (w *When) DeleteMonoVertexAndWait() *When {
+ w.t.Helper()
+ if w.monoVertex == nil {
+ w.t.Fatal("No MonoVertex to delete")
+ }
+ w.t.Log("Deleting MonoVertex", w.monoVertex.Name)
+ ctx := context.Background()
+ if err := w.monoVertexClient.Delete(ctx, w.monoVertex.Name, metav1.DeleteOptions{}); err != nil {
+ w.t.Fatal(err)
+ }
+
+ timeout := defaultTimeout
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+ labelSelector := fmt.Sprintf("%s=%s", dfv1.KeyMonoVertexName, w.monoVertex.Name)
+ for {
+ select {
+ case <-ctx.Done():
+ w.t.Fatalf("Timeout after %v waiting for mono vertex pods terminating", timeout)
+ default:
+ }
+ podList, err := w.kubeClient.CoreV1().Pods(Namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector})
+ if err != nil {
+ w.t.Fatalf("Error getting mono vertex pods: %v", err)
+ }
+ if len(podList.Items) == 0 {
+ return w
+ }
+ time.Sleep(2 * time.Second)
+ }
+}
+
func (w *When) WaitForISBSvcReady() *When {
w.t.Helper()
ctx := context.Background()
@@ -281,26 +335,30 @@ func (w *When) Exec(name string, args []string, block func(t *testing.T, output
func (w *When) Given() *Given {
return &Given{
- t: w.t,
- isbSvcClient: w.isbSvcClient,
- pipelineClient: w.pipelineClient,
- vertexClient: w.vertexClient,
- isbSvc: w.isbSvc,
- pipeline: w.pipeline,
- restConfig: w.restConfig,
- kubeClient: w.kubeClient,
+ t: w.t,
+ isbSvcClient: w.isbSvcClient,
+ pipelineClient: w.pipelineClient,
+ vertexClient: w.vertexClient,
+ monoVertexClient: w.monoVertexClient,
+ isbSvc: w.isbSvc,
+ pipeline: w.pipeline,
+ monoVertex: w.monoVertex,
+ restConfig: w.restConfig,
+ kubeClient: w.kubeClient,
}
}
func (w *When) Expect() *Expect {
return &Expect{
- t: w.t,
- isbSvcClient: w.isbSvcClient,
- pipelineClient: w.pipelineClient,
- vertexClient: w.vertexClient,
- isbSvc: w.isbSvc,
- pipeline: w.pipeline,
- restConfig: w.restConfig,
- kubeClient: w.kubeClient,
+ t: w.t,
+ isbSvcClient: w.isbSvcClient,
+ pipelineClient: w.pipelineClient,
+ vertexClient: w.vertexClient,
+ monoVertexClient: w.monoVertexClient,
+ isbSvc: w.isbSvc,
+ pipeline: w.pipeline,
+ monoVertex: w.monoVertex,
+ restConfig: w.restConfig,
+ kubeClient: w.kubeClient,
}
}
diff --git a/test/http-e2e/http_test.go b/test/http-e2e/http_test.go
index ce46e23aef..a5e7058dee 100644
--- a/test/http-e2e/http_test.go
+++ b/test/http-e2e/http_test.go
@@ -47,16 +47,16 @@ func (s *HTTPSuite) TestHTTPSourcePipeline() {
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("no-id"))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("no-id")))
// No x-numaflow-id, expect 2 outputs
- w.Expect().SinkContains("out", "no-id", SinkCheckWithContainCount(2))
+ w.Expect().RedisSinkContains("http-source-out", "no-id", SinkCheckWithContainCount(2))
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("with-id")).WithHeader("x-numaflow-id", "101")).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("with-id")).WithHeader("x-numaflow-id", "101"))
// With same x-numaflow-id, expect 1 output
- w.Expect().SinkContains("out", "with-id", SinkCheckWithContainCount(1))
+ w.Expect().RedisSinkContains("http-source-out", "with-id", SinkCheckWithContainCount(1))
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("with-id")).WithHeader("x-numaflow-id", "102"))
// With a new x-numaflow-id, expect 2 outputs
- w.Expect().SinkContains("out", "with-id", SinkCheckWithContainCount(2))
+ w.Expect().RedisSinkContains("http-source-out", "with-id", SinkCheckWithContainCount(2))
}
func (s *HTTPSuite) TestHTTPSourceAuthPipeline() {
@@ -71,8 +71,8 @@ func (s *HTTPSuite) TestHTTPSourceAuthPipeline() {
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("no-auth"))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("with-auth")).WithHeader("Authorization", "Bearer faketoken"))
- w.Expect().SinkContains("out", "with-auth")
- w.Expect().SinkNotContains("out", "no-auth")
+ w.Expect().RedisSinkContains("http-auth-source-out", "with-auth")
+ w.Expect().RedisSinkNotContains("http-auth-source-out", "no-auth")
}
func TestHTTPSuite(t *testing.T) {
diff --git a/test/http-e2e/testdata/http-source-with-auth.yaml b/test/http-e2e/testdata/http-source-with-auth.yaml
index a96cd3b58e..4b27a58cee 100644
--- a/test/http-e2e/testdata/http-source-with-auth.yaml
+++ b/test/http-e2e/testdata/http-source-with-auth.yaml
@@ -18,6 +18,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "http-auth-source-out"
edges:
- from: in
to: out
diff --git a/test/http-e2e/testdata/http-source.yaml b/test/http-e2e/testdata/http-source.yaml
index 3f81fc1902..c67d366ddb 100644
--- a/test/http-e2e/testdata/http-source.yaml
+++ b/test/http-e2e/testdata/http-source.yaml
@@ -17,6 +17,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "http-source-out"
edges:
- from: in
to: out
diff --git a/test/idle-source-e2e/idle_source_test.go b/test/idle-source-e2e/idle_source_test.go
index faf5be1f00..ef5e3eff98 100644
--- a/test/idle-source-e2e/idle_source_test.go
+++ b/test/idle-source-e2e/idle_source_test.go
@@ -87,8 +87,8 @@ func (is *IdleSourceSuite) TestIdleKeyedReducePipelineWithHttpSource() {
// since the key can be even or odd and the window duration is 10s
// the sum should be 20(for even) and 40(for odd)
w.Expect().
- SinkContains("sink", "20", SinkCheckWithTimeout(300*time.Second)).
- SinkContains("sink", "40", SinkCheckWithTimeout(300*time.Second))
+ RedisSinkContains("http-idle-source-sink", "20", SinkCheckWithTimeout(300*time.Second)).
+ RedisSinkContains("http-idle-source-sink", "40", SinkCheckWithTimeout(300*time.Second))
done <- struct{}{}
}
@@ -138,9 +138,9 @@ func (is *IdleSourceSuite) TestIdleKeyedReducePipelineWithKafkaSource() {
}()
// since the window duration is 10 second, so the count of event will be 20, when sending data to both partitions.
- w.Expect().SinkContains("sink", "20", SinkCheckWithTimeout(300*time.Second))
+ w.Expect().RedisSinkContains("kafka-idle-source-sink", "20", SinkCheckWithTimeout(300*time.Second))
// since the window duration is 10 second, so the count of event will be 10, when sending data to only one partition.
- w.Expect().SinkContains("sink", "10", SinkCheckWithTimeout(300*time.Second))
+ w.Expect().RedisSinkContains("kafka-idle-source-sink", "10", SinkCheckWithTimeout(300*time.Second))
done <- struct{}{}
}
diff --git a/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml b/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml
index 68bd2f15b0..2fc2a0a89e 100644
--- a/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml
+++ b/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml
@@ -50,6 +50,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "http-idle-source-sink"
edges:
- from: in
to: atoi
diff --git a/test/idle-source-e2e/testdata/kafka-pipeline.yaml b/test/idle-source-e2e/testdata/kafka-pipeline.yaml
index fdc4e33bf0..3af968f58d 100644
--- a/test/idle-source-e2e/testdata/kafka-pipeline.yaml
+++ b/test/idle-source-e2e/testdata/kafka-pipeline.yaml
@@ -53,6 +53,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "kafka-idle-source-sink"
edges:
- from: kafka-in
to: count-event
diff --git a/test/jetstream-e2e/jetstream_test.go b/test/jetstream-e2e/jetstream_test.go
index 40687c1f17..06aa24d05a 100644
--- a/test/jetstream-e2e/jetstream_test.go
+++ b/test/jetstream-e2e/jetstream_test.go
@@ -21,8 +21,9 @@ package jetstream_e2e
import (
"testing"
- "github.com/numaproj/numaflow/test/fixtures"
"github.com/stretchr/testify/suite"
+
+ "github.com/numaproj/numaflow/test/fixtures"
)
//go:generate kubectl -n numaflow-system delete statefulset nats --ignore-not-found=true
@@ -47,7 +48,7 @@ func (ns *JetstreamSuite) TestJetstreamSource() {
// wait for all the pods to come up
w.Expect().VertexPodsRunning()
- w.Expect().SinkContains("out", msgPayload, fixtures.SinkCheckWithContainCount(100))
+ w.Expect().RedisSinkContains("jetstream-source-e2e-out", msgPayload, fixtures.SinkCheckWithContainCount(100))
}
func TestJetstreamSuite(t *testing.T) {
diff --git a/test/jetstream-e2e/testdata/jetstream-source-pipeline.yaml b/test/jetstream-e2e/testdata/jetstream-source-pipeline.yaml
index 1f01be02db..c1a3766530 100644
--- a/test/jetstream-e2e/testdata/jetstream-source-pipeline.yaml
+++ b/test/jetstream-e2e/testdata/jetstream-source-pipeline.yaml
@@ -26,6 +26,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "jetstream-source-e2e-out"
edges:
- from: in
to: p1
diff --git a/test/monovertex-e2e/monovertex_test.go b/test/monovertex-e2e/monovertex_test.go
new file mode 100644
index 0000000000..51d9135c56
--- /dev/null
+++ b/test/monovertex-e2e/monovertex_test.go
@@ -0,0 +1,32 @@
+package monovertex_e2e
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+
+ . "github.com/numaproj/numaflow/test/fixtures"
+)
+
+type MonoVertexSuite struct {
+ E2ESuite
+}
+
+func (s *MonoVertexSuite) TestMonoVertexWithTransformer() {
+ w := s.Given().MonoVertex("@testdata/mono-vertex-with-transformer.yaml").
+ When().CreateMonoVertexAndWait()
+ defer w.DeleteMonoVertexAndWait()
+
+ w.Expect().MonoVertexPodsRunning()
+
+ // Expect the messages to be processed by the transformer.
+ w.Expect().MonoVertexPodLogContains("AssignEventTime", PodLogCheckOptionWithContainer("transformer"))
+
+ // Expect the messages to reach the sink.
+ w.Expect().RedisSinkContains("transformer-mono-vertex", "199")
+ w.Expect().RedisSinkContains("transformer-mono-vertex", "200")
+}
+
+func TestMonoVertexSuite(t *testing.T) {
+ suite.Run(t, new(MonoVertexSuite))
+}
diff --git a/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml b/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
new file mode 100644
index 0000000000..66f1bb34bd
--- /dev/null
+++ b/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
@@ -0,0 +1,24 @@
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: MonoVertex
+metadata:
+ name: transformer-mono-vertex
+spec:
+ source:
+ udsource:
+ container:
+ image: quay.io/numaio/numaflow-go/source-simple-source:stable
+ imagePullPolicy: Always
+ transformer:
+ container:
+ image: quay.io/numaio/numaflow-go/mapt-assign-event-time:stable
+ imagePullPolicy: Always
+ sink:
+ udsink:
+ container:
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ image: quay.io/numaio/numaflow-go/redis-sink:stable
+ imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # Use the name of the mono vertex as the key
+ value: "transformer-mono-vertex"
\ No newline at end of file
diff --git a/test/nats-e2e/nats_test.go b/test/nats-e2e/nats_test.go
index 504432214e..72d2c70e99 100644
--- a/test/nats-e2e/nats_test.go
+++ b/test/nats-e2e/nats_test.go
@@ -45,7 +45,7 @@ func (ns *NatsSuite) TestNatsSource() {
w.Expect().VertexPodsRunning()
PumpNatsSubject(subject, 100, 20*time.Millisecond, 10, "test-message")
- w.Expect().SinkContains("out", "test-message", SinkCheckWithContainCount(100))
+ w.Expect().RedisSinkContains("nats-source-e2e-out", "test-message", SinkCheckWithContainCount(100))
}
func TestNatsSuite(t *testing.T) {
diff --git a/test/nats-e2e/testdata/nats-source-pipeline.yaml b/test/nats-e2e/testdata/nats-source-pipeline.yaml
index bab98eeb52..3dd0d965df 100644
--- a/test/nats-e2e/testdata/nats-source-pipeline.yaml
+++ b/test/nats-e2e/testdata/nats-source-pipeline.yaml
@@ -27,6 +27,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "nats-source-e2e-out"
edges:
- from: in
to: p1
diff --git a/test/reduce-one-e2e/reduce_one_test.go b/test/reduce-one-e2e/reduce_one_test.go
index 6fa78f92f1..4cf592e1cf 100644
--- a/test/reduce-one-e2e/reduce_one_test.go
+++ b/test/reduce-one-e2e/reduce_one_test.go
@@ -72,8 +72,8 @@ func (r *ReduceSuite) TestSimpleKeyedReducePipeline() {
// since the key can be even or odd and the window duration is 10s
// the sum should be 20(for even) and 40(for odd)
w.Expect().
- SinkContains("sink", "40").
- SinkContains("sink", "20")
+ RedisSinkContains("simple-sum-sink", "40").
+ RedisSinkContains("simple-sum-sink", "20")
done <- struct{}{}
}
@@ -117,7 +117,7 @@ func (r *ReduceSuite) TestSimpleNonKeyedReducePipeline() {
// since there is no key, all the messages will be assigned to same window
// the sum should be 60(since the window is 10s)
- w.Expect().SinkContains("sink", "60")
+ w.Expect().RedisSinkContains("reduce-sum-sink", "60")
done <- struct{}{}
}
@@ -161,7 +161,7 @@ func (r *ReduceSuite) TestComplexReducePipelineKeyedNonKeyed() {
// since the key can be even or odd and the first window duration is 10s(which is keyed)
// and the second window duration is 60s(non-keyed)
// the sum should be 180(60 + 120)
- w.Expect().SinkContains("sink", "180")
+ w.Expect().RedisSinkContains("complex-sum-sink", "180")
done <- struct{}{}
}
@@ -219,10 +219,10 @@ func (r *ReduceSuite) TestSimpleReducePipelineFailOverUsingWAL() {
}()
w.Expect().
- SinkContains("sink", "38").
- SinkContains("sink", "76").
- SinkContains("sink", "120").
- SinkContains("sink", "240")
+ RedisSinkContains("even-odd-sum-sink", "38").
+ RedisSinkContains("even-odd-sum-sink", "76").
+ RedisSinkContains("even-odd-sum-sink", "120").
+ RedisSinkContains("even-odd-sum-sink", "240")
done <- struct{}{}
}
@@ -280,12 +280,12 @@ func (r *ReduceSuite) TestComplexSlidingWindowPipeline() {
// we only have to extend the timeout for the first output to be produced. for the rest,
// we just need to wait for the default timeout for the rest of the outputs since its synchronous
w.Expect().
- SinkContains("sink", "30").
- SinkContains("sink", "60").
- SinkNotContains("sink", "80").
- SinkContains("sink", "90").
- SinkContains("sink", "180").
- SinkNotContains("sink", "210")
+ RedisSinkContains("complex-sliding-sum-sink", "30").
+ RedisSinkContains("complex-sliding-sum-sink", "60").
+ RedisSinkNotContains("complex-sliding-sum-sink", "80").
+ RedisSinkContains("complex-sliding-sum-sink", "90").
+ RedisSinkContains("complex-sliding-sum-sink", "180").
+ RedisSinkNotContains("complex-sliding-sum-sink", "210")
done <- struct{}{}
}
diff --git a/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml b/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml
index 0c745be3b6..d7a56c5f0e 100644
--- a/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml
@@ -57,6 +57,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "complex-sum-sink"
edges:
- from: in
to: atoi
diff --git a/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml b/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml
index 2eafdd6f85..8c19e22d72 100644
--- a/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml
@@ -89,6 +89,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "complex-sliding-sum-sink"
edges:
- from: in
to: atoi
diff --git a/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml b/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
index 51e62d0613..25e1976331 100644
--- a/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
@@ -46,6 +46,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "simple-sum-sink"
edges:
- from: in
to: atoi
diff --git a/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml b/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml
index 65b80c2dc0..d5a932a7c5 100644
--- a/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml
@@ -45,6 +45,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "reduce-sum-sink"
edges:
- from: in
to: atoi
diff --git a/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml b/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml
index e77479eaad..25a6770a5d 100644
--- a/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml
+++ b/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml
@@ -44,6 +44,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "even-odd-sum-sink"
edges:
- from: in
to: atoi
diff --git a/test/reduce-two-e2e/reduce_two_test.go b/test/reduce-two-e2e/reduce_two_test.go
index 3af75820fd..7a1815f2c2 100644
--- a/test/reduce-two-e2e/reduce_two_test.go
+++ b/test/reduce-two-e2e/reduce_two_test.go
@@ -83,9 +83,9 @@ func (r *ReduceSuite) testReduceStream(lang string) {
// The reduce stream application summarizes the input messages and returns the sum when the sum is greater than 100.
// Since we are sending 3s, the first returned message should be 102.
// There should be no other values.
- w.Expect().SinkContains("sink", "102")
- w.Expect().SinkNotContains("sink", "99")
- w.Expect().SinkNotContains("sink", "105")
+ w.Expect().RedisSinkContains(pipelineName+"-sink", "102")
+ w.Expect().RedisSinkNotContains(pipelineName+"-sink", "99")
+ w.Expect().RedisSinkNotContains(pipelineName+"sink", "105")
done <- struct{}{}
}
@@ -132,7 +132,7 @@ func (r *ReduceSuite) TestSimpleSessionPipeline() {
}
}()
- w.Expect().SinkContains("sink", "1000")
+ w.Expect().RedisSinkContains("simple-session-sum-sink", "1000")
done <- struct{}{}
}
@@ -190,11 +190,11 @@ func (r *ReduceSuite) testSimpleSessionKeyedPipeline(lang string) {
}
}()
- w.Expect().SinkContains("sink", "5")
- w.Expect().SinkNotContains("sink", "4", SinkCheckWithTimeout(20*time.Second))
- w.Expect().SinkNotContains("sink", "3", SinkCheckWithTimeout(20*time.Second))
- w.Expect().SinkNotContains("sink", "2", SinkCheckWithTimeout(20*time.Second))
- w.Expect().SinkNotContains("sink", "1", SinkCheckWithTimeout(20*time.Second))
+ w.Expect().RedisSinkContains(pipelineName+"-sink", "5")
+ w.Expect().RedisSinkNotContains(pipelineName+"-sink", "4", SinkCheckWithTimeout(20*time.Second))
+ w.Expect().RedisSinkNotContains(pipelineName+"-sink", "3", SinkCheckWithTimeout(20*time.Second))
+ w.Expect().RedisSinkNotContains(pipelineName+"-sink", "2", SinkCheckWithTimeout(20*time.Second))
+ w.Expect().RedisSinkNotContains(pipelineName+"-sink", "1", SinkCheckWithTimeout(20*time.Second))
done <- struct{}{}
}
@@ -255,11 +255,11 @@ func (r *ReduceSuite) TestSimpleSessionPipelineFailOverUsingWAL() {
}()
w.Expect().
- SinkContains("sink", "5").
- SinkNotContains("sink", "4", SinkCheckWithTimeout(20*time.Second)).
- SinkNotContains("sink", "3", SinkCheckWithTimeout(20*time.Second)).
- SinkNotContains("sink", "2", SinkCheckWithTimeout(20*time.Second)).
- SinkNotContains("sink", "1", SinkCheckWithTimeout(20*time.Second))
+ RedisSinkContains("simple-session-counter-go-sink", "5").
+ RedisSinkNotContains("simple-session-counter-go-sink", "4", SinkCheckWithTimeout(20*time.Second)).
+ RedisSinkNotContains("simple-session-counter-go-sink", "3", SinkCheckWithTimeout(20*time.Second)).
+ RedisSinkNotContains("simple-session-counter-go-sink", "2", SinkCheckWithTimeout(20*time.Second)).
+ RedisSinkNotContains("simple-session-counter-go-sink", "1", SinkCheckWithTimeout(20*time.Second))
done <- struct{}{}
}
diff --git a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml
index 250c6c126f..d80138366e 100644
--- a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml
+++ b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml
@@ -42,6 +42,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "reduce-stream-go-sink"
edges:
- from: in
to: atoi
diff --git a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml
index ebdd2b4c8a..b94e7fd649 100644
--- a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml
+++ b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml
@@ -42,6 +42,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "reduce-stream-java-sink"
edges:
- from: in
to: atoi
diff --git a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml
index ff2ab4a2ee..a2d88efeb6 100644
--- a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml
+++ b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml
@@ -43,6 +43,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "simple-session-counter-go-sink"
edges:
- from: in
to: even-odd
diff --git a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml
index 91a9d168ed..f468710f03 100644
--- a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml
+++ b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml
@@ -48,6 +48,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "simple-session-counter-java-sink"
edges:
- from: in
to: even-odd
diff --git a/test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml b/test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml
index 57f8cdadf4..6f1259e085 100644
--- a/test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml
+++ b/test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml
@@ -36,6 +36,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "simple-session-sum-sink"
edges:
- from: in
to: compute-sum
diff --git a/test/sideinputs-e2e/sideinput-e2e_sink_source_test.go b/test/sideinputs-e2e/sideinput-e2e_sink_source_test.go
index dfec57977c..be95e4c3f5 100644
--- a/test/sideinputs-e2e/sideinput-e2e_sink_source_test.go
+++ b/test/sideinputs-e2e/sideinput-e2e_sink_source_test.go
@@ -40,16 +40,14 @@ func (s *SideInputUDSSuite) setUpTests(pipeLineFile string) *When {
}
func (s *SideInputUDSSuite) TestSinkWithSideInput() {
-
// the side inputs feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
s.T().SkipNow()
}
- w := s.setUpTests("@testdata/sideinput_sink.yaml")
+ w := s.setUpTests("@testdata/sideinput-sink.yaml")
defer w.DeletePipelineAndWait()
- w.Expect().SinkContains("redis-uds", "e2e-even", SinkCheckWithTimeout(2*time.Minute))
-
+ w.Expect().RedisSinkContains("sideinput-sink-test-redis-uds", "e2e-even", SinkCheckWithTimeout(2*time.Minute))
}
func (s *SideInputUDSSuite) TestSourceWithSideInput() {
@@ -59,9 +57,9 @@ func (s *SideInputUDSSuite) TestSourceWithSideInput() {
s.T().SkipNow()
}
- w := s.setUpTests("@testdata/sideinput_source.yaml")
+ w := s.setUpTests("@testdata/sideinput-source.yaml")
defer w.DeletePipelineAndWait()
- w.Expect().SinkContains("redis-uds", "e2e-even", SinkCheckWithTimeout(2*time.Minute))
+ w.Expect().RedisSinkContains("sideinput-source-test-redis-uds", "e2e-even", SinkCheckWithTimeout(2*time.Minute))
}
diff --git a/test/sideinputs-e2e/sideinput_test.go b/test/sideinputs-e2e/sideinput_test.go
index 1b4c18b2ac..8f1e156926 100644
--- a/test/sideinputs-e2e/sideinput_test.go
+++ b/test/sideinputs-e2e/sideinput_test.go
@@ -70,8 +70,8 @@ func (s *SideInputSuite) TestSimpleMapSideInputPipeline() {
}()
// map-even-data and map-odd-data message is generated based on map and side input data.
- w.Expect().SinkContains("sink", "map-even-data")
- w.Expect().SinkContains("sink", "map-odd-data")
+ w.Expect().RedisSinkContains("map-sideinput-pipeline-sink", "map-even-data")
+ w.Expect().RedisSinkContains("map-sideinput-pipeline-sink", "map-odd-data")
done <- struct{}{}
}
@@ -114,7 +114,7 @@ func (s *SideInputSuite) TestSimpleReduceSideInputPipeline() {
}()
// here reduce-side-input text is generated based on reduce and side input data.
- w.Expect().SinkContains("sink", "reduce_sideinput")
+ w.Expect().RedisSinkContains("reduce-sideinput-pipeline-sink", "reduce_sideinput")
done <- struct{}{}
}
diff --git a/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml b/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml
index 30079de488..cb3ea152ed 100644
--- a/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml
+++ b/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml
@@ -32,6 +32,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "map-sideinput-pipeline-sink"
edges:
- from: in
to: si-e2e
diff --git a/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml b/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml
index b5f253c0fe..028a20125e 100644
--- a/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml
+++ b/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml
@@ -49,6 +49,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "reduce-sideinput-pipeline-sink"
edges:
- from: in
to: atoi
diff --git a/test/sideinputs-e2e/testdata/sideinput_sink.yaml b/test/sideinputs-e2e/testdata/sideinput-sink.yaml
similarity index 89%
rename from test/sideinputs-e2e/testdata/sideinput_sink.yaml
rename to test/sideinputs-e2e/testdata/sideinput-sink.yaml
index a9cd439d4a..f172b4ec31 100644
--- a/test/sideinputs-e2e/testdata/sideinput_sink.yaml
+++ b/test/sideinputs-e2e/testdata/sideinput-sink.yaml
@@ -26,8 +26,11 @@ spec:
udsink:
container:
# see https://github.com/numaproj/numaflow-go/tree/main/pkg/sideinput/examples/sink_sideinput
- image: quay.io/numaio/numaflow-go/redis-sink-with-sideinput:stable
+ image: quay.io/numaio/numaflow-go/redis-sink-with-sideinput:hash
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ value: "sideinput-sink-test-redis-uds"
sideInputs:
- myticker
diff --git a/test/sideinputs-e2e/testdata/sideinput_source.yaml b/test/sideinputs-e2e/testdata/sideinput-source.yaml
similarity index 91%
rename from test/sideinputs-e2e/testdata/sideinput_source.yaml
rename to test/sideinputs-e2e/testdata/sideinput-source.yaml
index 62754d05b1..6c21edd717 100644
--- a/test/sideinputs-e2e/testdata/sideinput_source.yaml
+++ b/test/sideinputs-e2e/testdata/sideinput-source.yaml
@@ -31,6 +31,9 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ value: "sideinput-source-test-redis-uds"
sideInputs:
- myticker
edges:
diff --git a/test/transformer-e2e/testdata/source-filtering.yaml b/test/transformer-e2e/testdata/source-filtering.yaml
index 670e9f87fd..549fddec63 100644
--- a/test/transformer-e2e/testdata/source-filtering.yaml
+++ b/test/transformer-e2e/testdata/source-filtering.yaml
@@ -19,6 +19,10 @@ spec:
# A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
+ env:
+ - name: SINK_HASH_KEY
+ # The key is set in the format of "pipeline_name-vertex_name"
+ value: "source-filtering-out"
edges:
- from: in
to: out
diff --git a/test/transformer-e2e/transformer_test.go b/test/transformer-e2e/transformer_test.go
index 8f0a226c79..888b3ee3fb 100644
--- a/test/transformer-e2e/transformer_test.go
+++ b/test/transformer-e2e/transformer_test.go
@@ -61,11 +61,11 @@ func (s *TransformerSuite) TestSourceFiltering() {
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte(expect3))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte(expect4)))
- w.Expect().SinkContains("out", expect3)
- w.Expect().SinkContains("out", expect4)
- w.Expect().SinkNotContains("out", expect0)
- w.Expect().SinkNotContains("out", expect1)
- w.Expect().SinkNotContains("out", expect2)
+ w.Expect().RedisSinkContains("source-filtering-out", expect3)
+ w.Expect().RedisSinkContains("source-filtering-out", expect4)
+ w.Expect().RedisSinkNotContains("source-filtering-out", expect0)
+ w.Expect().RedisSinkNotContains("source-filtering-out", expect1)
+ w.Expect().RedisSinkNotContains("source-filtering-out", expect2)
}
func (s *TransformerSuite) TestTimeExtractionFilter() {
@@ -84,7 +84,7 @@ func (s *TransformerSuite) TestTimeExtractionFilter() {
testMsgTwo := `{"id": 101, "msg": "test", "time": "2021-01-18T21:54:42.123Z", "desc": "A bad ID."}`
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte(testMsgTwo)))
- w.Expect().SinkNotContains("out", testMsgTwo)
+ w.Expect().RedisSinkNotContains("time-extraction-filter-out", testMsgTwo)
}
func (s *TransformerSuite) TestBuiltinEventTimeExtractor() {
From c14abd5de5cfc4d88f396c17231233b4e9fc2c5f Mon Sep 17 00:00:00 2001
From: Vedant Gupta <49195734+veds-g@users.noreply.github.com>
Date: Fri, 16 Aug 2024 09:23:59 +0530
Subject: [PATCH 004/188] feat: Mono vertex UI (#1941)
Signed-off-by: veds-g
---
ui/src/components/common/Routes/index.tsx | 8 +-
.../partials/VertexDetails/index.tsx | 28 +-
.../partials/ProcessingRates/index.tsx | 14 +-
.../partials/VertexUpdate/index.tsx | 8 +-
ui/src/components/pages/MonoVertex/index.tsx | 224 +++++++
.../partials/MonoVertexStatus/index.tsx | 58 ++
.../partials/MonoVertexStatus/style.css | 11 +
.../MonoVertexSummaryStatus/index.tsx | 135 ++++
.../MonoVertexSummaryStatus/style.css | 20 +
ui/src/components/pages/MonoVertex/style.css | 14 +
ui/src/components/pages/Namespace/index.tsx | 27 +-
.../partials/MonoVertexCard/index.tsx | 580 ++++++++++++++++++
.../partials/MonoVertexCard/style.css | 10 +
.../PipelineListing/index.tsx | 143 +++--
.../NamespaceListingWrapper/PipelinesTypes.ts | 14 +
.../NamespaceListingWrapper/index.tsx | 2 +
ui/src/components/pages/Pipeline/index.tsx | 3 +-
.../pages/Pipeline/partials/Graph/index.tsx | 43 +-
.../Graph/partials/CustomNode/index.tsx | 132 +++-
.../Graph/partials/CustomNode/style.css | 261 ++++----
.../Graph/partials/NodeInfo/index.tsx | 1 +
.../partials/NodeInfo/partials/Pods/index.tsx | 3 +-
.../plugin/NumaflowMonitorApp/App.tsx | 8 -
ui/src/components/plugin/Routes/Routes.tsx | 6 +-
ui/src/images/monoVertex.svg | 28 +
ui/src/images/transformer.svg | 10 +
ui/src/types/declarations/graph.d.ts | 2 +
ui/src/types/declarations/namespace.d.ts | 2 +
ui/src/types/declarations/pipeline.d.ts | 39 ++
ui/src/types/declarations/pods.d.ts | 1 +
.../fetchWrappers/clusterSummaryFetch.ts | 16 +-
ui/src/utils/fetchWrappers/monoVertexFetch.ts | 119 ++++
.../fetchWrappers/monoVertexUpdateFetch.ts | 92 +++
.../fetchWrappers/namespaceK8sEventsFetch.ts | 8 +-
.../fetchWrappers/namespaceSummaryFetch.ts | 83 ++-
.../utils/fetcherHooks/monoVertexViewFetch.ts | 278 +++++++++
.../utils/fetcherHooks/podsViewFetch.test.ts | 3 +
ui/src/utils/fetcherHooks/podsViewFetch.ts | 12 +-
38 files changed, 2192 insertions(+), 254 deletions(-)
create mode 100644 ui/src/components/pages/MonoVertex/index.tsx
create mode 100644 ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/index.tsx
create mode 100644 ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/style.css
create mode 100644 ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/index.tsx
create mode 100644 ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/style.css
create mode 100644 ui/src/components/pages/MonoVertex/style.css
create mode 100644 ui/src/components/pages/Namespace/partials/MonoVertexCard/index.tsx
create mode 100644 ui/src/components/pages/Namespace/partials/MonoVertexCard/style.css
create mode 100644 ui/src/images/monoVertex.svg
create mode 100644 ui/src/images/transformer.svg
create mode 100644 ui/src/utils/fetchWrappers/monoVertexFetch.ts
create mode 100644 ui/src/utils/fetchWrappers/monoVertexUpdateFetch.ts
create mode 100644 ui/src/utils/fetcherHooks/monoVertexViewFetch.ts
diff --git a/ui/src/components/common/Routes/index.tsx b/ui/src/components/common/Routes/index.tsx
index ec6f22be67..d52f24c574 100644
--- a/ui/src/components/common/Routes/index.tsx
+++ b/ui/src/components/common/Routes/index.tsx
@@ -2,6 +2,7 @@ import { useLocation } from "react-router-dom";
import { Cluster } from "../../pages/Cluster";
import { Namespaces } from "../../pages/Namespace";
import { Pipeline } from "../../pages/Pipeline";
+import { MonoVertex } from "../../pages/MonoVertex";
export interface RoutesProps {
managedNamespace?: string;
@@ -12,11 +13,14 @@ export function Routes(props: RoutesProps) {
const query = new URLSearchParams(location.search);
const ns = query.get("namespace") || "";
const pl = query.get("pipeline") || "";
+ const type = query.get("type") || "";
const { managedNamespace } = props;
if (managedNamespace) {
- return pl ? (
+ return type ? (
+
+ ) : pl ? (
) : (
@@ -25,6 +29,8 @@ export function Routes(props: RoutesProps) {
if (ns === "" && pl === "") {
return ;
+ } else if (type !== "") {
+ return ;
} else if (pl !== "") {
return ;
} else {
diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx
index 5d18a79eb3..6113140894 100644
--- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx
+++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx
@@ -12,7 +12,8 @@ import { CloseModal } from "../CloseModal";
import sourceIcon from "../../../../../images/source.png";
import sinkIcon from "../../../../../images/sink.png";
import mapIcon from "../../../../../images/map.png";
-import reducIcon from "../../../../../images/reduce.png";
+import reduceIcon from "../../../../../images/reduce.png";
+import monoVertexIcon from "../../../../../images/monoVertex.svg";
import "./style.css";
@@ -27,6 +28,7 @@ export enum VertexType {
SINK,
MAP,
REDUCE,
+ MONOVERTEX,
}
export interface VertexDetailsProps {
@@ -71,6 +73,8 @@ export function VertexDetails({
setVertexType(VertexType.MAP);
} else if (type === "sink") {
setVertexType(VertexType.SINK);
+ } else if (type === "monoVertex") {
+ setVertexType(VertexType.MONOVERTEX);
}
setVertexSpec(vertexSpecs);
}, [vertexSpecs, type]);
@@ -98,7 +102,7 @@ export function VertexDetails({
return (
@@ -127,6 +131,17 @@ export function VertexDetails({
Sink Vertex
);
+ case VertexType.MONOVERTEX:
+ return (
+
+
+ Mono Vertex
+
+ );
default:
return (
@@ -246,6 +261,7 @@ export function VertexDetails({
namespaceId={namespaceId}
pipelineId={pipelineId}
vertexId={vertexId}
+ type={type}
/>
)}
@@ -261,6 +277,7 @@ export function VertexDetails({
pipelineId={pipelineId}
vertexId={vertexId}
vertexSpec={vertexSpec}
+ type={type}
setModalOnClose={handleUpdateModalClose}
refresh={refresh}
/>
@@ -276,6 +293,7 @@ export function VertexDetails({
)}
@@ -288,8 +306,10 @@ export function VertexDetails({
{tabValue === K8S_EVENTS_TAB_INDEX && (
diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx
index 8bcd790234..87b7c0574f 100644
--- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx
+++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx
@@ -13,12 +13,14 @@ import "./style.css";
export interface ProcessingRatesProps {
vertexId: string;
pipelineId: string;
+ type: string;
vertexMetrics: any;
}
export function ProcessingRates({
vertexMetrics,
pipelineId,
+ type,
vertexId,
}: ProcessingRatesProps) {
const [foundRates, setFoundRates] = useState([]);
@@ -27,13 +29,15 @@ export function ProcessingRates({
if (!vertexMetrics || !pipelineId || !vertexId) {
return;
}
- const vertexData = vertexMetrics[vertexId];
+ const vertexData =
+ type === "monoVertex" ? [vertexMetrics] : vertexMetrics[vertexId];
if (!vertexData) {
return;
}
const rates: PipelineVertexMetric[] = [];
vertexData.forEach((item: any, index: number) => {
- if (item.pipeline !== pipelineId || !item.processingRates) {
+ const key = type === "monoVertex" ? "monoVertex" : "pipeline";
+ if (item?.[key] !== pipelineId || !item.processingRates) {
return; // continue
}
rates.push({
@@ -64,7 +68,7 @@ export function ProcessingRates({
- Partition
+ {type !== "monoVertex" && Partition }
1m
5m
15m
@@ -81,7 +85,9 @@ export function ProcessingRates({
{!!foundRates.length &&
foundRates.map((metric) => (
- {metric.partition}
+ {type !== "monoVertex" && (
+ {metric.partition}
+ )}
{metric.oneM}/sec
{metric.fiveM}/sec
{metric.fifteenM}/sec
diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/VertexUpdate/index.tsx b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/VertexUpdate/index.tsx
index 1163263cbc..a8b836a127 100644
--- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/VertexUpdate/index.tsx
+++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/VertexUpdate/index.tsx
@@ -20,6 +20,7 @@ export interface VertexUpdateProps {
pipelineId: string;
vertexId: string;
vertexSpec: any;
+ type: string;
setModalOnClose?: (props: SpecEditorModalProps | undefined) => void;
refresh: () => void;
}
@@ -30,6 +31,7 @@ export function VertexUpdate({
vertexId,
vertexSpec,
setModalOnClose,
+ type: vertexType,
refresh,
}: VertexUpdateProps) {
const [loading, setLoading] = useState(false);
@@ -225,7 +227,11 @@ export function VertexUpdate({
>
>(
+ new Map()
+);
+
+// TODO add health status + processing rate once implemented
+export function MonoVertex({ namespaceId: nsIdProp }: MonoVertexProps) {
+ const location = useLocation();
+ const query = new URLSearchParams(location.search);
+ const pipelineId = query.get("pipeline") || "";
+ const nsIdParam = query.get("namespace") || "";
+ const namespaceId = nsIdProp || nsIdParam;
+ const { addError, setSidebarProps } = useContext(AppContext);
+ const {
+ data,
+ loading: summaryLoading,
+ error,
+ refresh: summaryRefresh,
+ } = useMonoVertexSummaryFetch({ namespaceId, pipelineId, addError });
+
+ const {
+ pipeline,
+ vertices,
+ pipelineErr,
+ loading,
+ refresh: graphRefresh,
+ } = useMonoVertexViewFetch(namespaceId, pipelineId, addError);
+
+ const refresh = useCallback(() => {
+ graphRefresh();
+ summaryRefresh();
+ }, [graphRefresh, summaryRefresh]);
+
+ const handleK8sEventsClick = useCallback(() => {
+ if (!namespaceId || !pipelineId || !setSidebarProps) {
+ return;
+ }
+ const vertexMap = new Map();
+ setSidebarProps({
+ type: SidebarType.NAMESPACE_K8s,
+ k8sEventsProps: {
+ namespaceId,
+ pipelineId: `${pipelineId} (MonoVertex)`,
+ headerText: "Pipeline K8s Events",
+ vertexFilterOptions: vertexMap,
+ },
+ });
+ }, [namespaceId, pipelineId, setSidebarProps, vertices]);
+
+ const summarySections: SummarySection[] = useMemo(() => {
+ if (summaryLoading) {
+ return [
+ {
+ type: SummarySectionType.CUSTOM,
+ customComponent: (
+
+ ),
+ },
+ ];
+ }
+ if (error) {
+ return [
+ {
+ type: SummarySectionType.CUSTOM,
+ customComponent: (
+
+ ),
+ },
+ ];
+ }
+ if (!data) {
+ return [];
+ }
+ const pipelineData = data?.monoVertexData;
+ const pipelineStatus = pipelineData?.monoVertex?.status?.phase || UNKNOWN;
+ return [
+ // pipeline collection
+ {
+ type: SummarySectionType.CUSTOM,
+ customComponent: (
+
+ ),
+ },
+ {
+ type: SummarySectionType.CUSTOM,
+ customComponent: (
+
+ ),
+ },
+ {
+ type: SummarySectionType.CUSTOM,
+ customComponent: (
+
+ K8s Events
+
+ ),
+ },
+ ];
+ }, [summaryLoading, error, data, pipelineId, refresh]);
+
+ const content = useMemo(() => {
+ if (pipelineErr) {
+ return (
+
+
+
+
+
+ );
+ }
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+ return (
+
+
+
+ );
+ }, [
+ pipelineErr,
+ loading,
+ vertices,
+ pipeline,
+ namespaceId,
+ pipelineId,
+ refresh,
+ ]);
+
+ return (
+
+ {content}
+
+ }
+ />
+ );
+}
diff --git a/ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/index.tsx b/ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/index.tsx
new file mode 100644
index 0000000000..04ebb4621a
--- /dev/null
+++ b/ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/index.tsx
@@ -0,0 +1,58 @@
+import React from "react";
+import Box from "@mui/material/Box";
+import { IconsStatusMap, StatusString } from "../../../../../utils";
+
+import "./style.css";
+
+export interface MonoVertexStatusProps {
+ status: any;
+}
+
+export function MonoVertexStatus({ status }: MonoVertexStatusProps) {
+ return (
+
+
+ STATUS
+
+
+
+
+
+
+
+ {StatusString[status]}
+
+
+ {StatusString["healthy"]}
+
+
+
+
+
+
+ );
+}
diff --git a/ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/style.css b/ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/style.css
new file mode 100644
index 0000000000..ef1f338503
--- /dev/null
+++ b/ui/src/components/pages/MonoVertex/partials/MonoVertexStatus/style.css
@@ -0,0 +1,11 @@
+.pipeline-logo {
+ width: 2.4rem;
+}
+
+.pipeline-logo-text {
+ color: #3C4348;
+ font-size: 1.4rem;
+ font-style: normal;
+ font-weight: 400;
+ margin-top: 0.2rem;
+}
diff --git a/ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/index.tsx b/ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/index.tsx
new file mode 100644
index 0000000000..83b570d188
--- /dev/null
+++ b/ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/index.tsx
@@ -0,0 +1,135 @@
+import React, { useCallback, useContext } from "react";
+import Box from "@mui/material/Box";
+import { useLocation } from "react-router-dom";
+import { SidebarType } from "../../../../common/SlidingSidebar";
+import { AppContextProps } from "../../../../../types/declarations/app";
+import { AppContext } from "../../../../../App";
+import { ViewType } from "../../../../common/SpecEditor";
+
+import "./style.css";
+
+export interface MonoVertexSummaryProps {
+ pipelineId: any;
+ pipeline: any;
+ refresh: () => void;
+}
+
+export function MonoVertexSummaryStatus({
+ pipelineId,
+ pipeline,
+ refresh,
+}: MonoVertexSummaryProps) {
+ const location = useLocation();
+ const query = new URLSearchParams(location.search);
+ const namespaceId = query.get("namespace") || "";
+ const { setSidebarProps } = useContext(AppContext);
+
+ const handleUpdateComplete = useCallback(() => {
+ refresh();
+ if (!setSidebarProps) {
+ return;
+ }
+ // Close sidebar
+ setSidebarProps(undefined);
+ }, [setSidebarProps, refresh]);
+
+ const handleSpecClick = useCallback(() => {
+ if (!namespaceId || !setSidebarProps) {
+ return;
+ }
+ setSidebarProps({
+ type: SidebarType.PIPELINE_UPDATE,
+ specEditorProps: {
+ titleOverride: `View Pipeline: ${pipelineId}`,
+ initialYaml: pipeline,
+ namespaceId,
+ pipelineId,
+ viewType: ViewType.READ_ONLY,
+ // viewType: isReadOnly ? ViewType.READ_ONLY : ViewType.TOGGLE_EDIT,
+ onUpdateComplete: handleUpdateComplete,
+ },
+ });
+ }, [
+ namespaceId,
+ pipelineId,
+ setSidebarProps,
+ pipeline,
+ handleUpdateComplete,
+ ]);
+
+ return (
+
+
+ SUMMARY
+
+
+
+ Created On:
+
+
+
+ Last Updated On:{" "}
+
+
+ {/**/}
+ {/* Last Refresh: */}
+ {/* 2023-12-07T02:02:00Z*/}
+ {/*
*/}
+
+
+
+ {pipeline?.metadata?.creationTimestamp}
+
+
+ {pipeline?.status?.lastUpdated}
+
+ {/**/}
+ {/* 2023-12-07T02:02:00Z*/}
+ {/*
*/}
+
+
+
+
+
+ {`View Specs`}
+
+
+
+
+
+
+
+ );
+}
diff --git a/ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/style.css b/ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/style.css
new file mode 100644
index 0000000000..eeaffa9d73
--- /dev/null
+++ b/ui/src/components/pages/MonoVertex/partials/MonoVertexSummaryStatus/style.css
@@ -0,0 +1,20 @@
+.pipeline-summary-text {
+ color: #393A3D;
+ font-size: 1.4rem;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 2.35rem;
+}
+
+.pipeline-summary-subtitle {
+ font-weight: 600;
+}
+
+.pipeline-onclick-events {
+ color: #393A3D;
+ font-size: 1.4rem;
+ font-style: normal;
+ font-weight: 600;
+ text-decoration: underline;
+ cursor: pointer;
+}
diff --git a/ui/src/components/pages/MonoVertex/style.css b/ui/src/components/pages/MonoVertex/style.css
new file mode 100644
index 0000000000..abd662f23a
--- /dev/null
+++ b/ui/src/components/pages/MonoVertex/style.css
@@ -0,0 +1,14 @@
+.react-flow__edge-textwrapper {
+ cursor: pointer;
+}
+
+.react-flow__edge-textbg {
+ fill: #fafafa;
+}
+
+.pipeline-status-title {
+ font-weight: 600;
+ color: #393A3D;
+ font-size: 1.3rem;
+ font-style: normal;
+}
diff --git a/ui/src/components/pages/Namespace/index.tsx b/ui/src/components/pages/Namespace/index.tsx
index 07d38682f5..a6fe5a1284 100644
--- a/ui/src/components/pages/Namespace/index.tsx
+++ b/ui/src/components/pages/Namespace/index.tsx
@@ -50,12 +50,19 @@ export function Namespaces({ namespaceId: nsIdProp }: NamespaceProps) {
const nsIdParam = query.get("namespace") || "";
const namespaceId = nsIdProp || nsIdParam;
const { setSidebarProps, addError } = useContext(AppContext);
- const { data, pipelineRawData, isbRawData, loading, error, refresh } =
- useNamespaceSummaryFetch({
- namespace: namespaceId || "",
- loadOnRefresh: false,
- addError,
- });
+ const {
+ data,
+ pipelineRawData,
+ isbRawData,
+ monoVertexRawData,
+ loading,
+ error,
+ refresh,
+ } = useNamespaceSummaryFetch({
+ namespace: namespaceId || "",
+ loadOnRefresh: false,
+ addError,
+ });
const handleK8sEventsClick = useCallback(() => {
if (!namespaceId || !setSidebarProps) {
@@ -75,6 +82,11 @@ export function Namespaces({ namespaceId: nsIdProp }: NamespaceProps) {
});
});
}
+ if (monoVertexRawData) {
+ Object.keys(monoVertexRawData).forEach((pipelineId) => {
+ pipelines.push(`${pipelineId} (MonoVertex)`);
+ });
+ }
setSidebarProps({
type: SidebarType.NAMESPACE_K8s,
k8sEventsProps: {
@@ -83,7 +95,7 @@ export function Namespaces({ namespaceId: nsIdProp }: NamespaceProps) {
vertexFilterOptions: vertexMap,
},
});
- }, [namespaceId, setSidebarProps, pipelineRawData]);
+ }, [namespaceId, setSidebarProps, pipelineRawData, monoVertexRawData]);
const defaultPipelinesData = useMemo(() => {
return [
@@ -280,6 +292,7 @@ export function Namespaces({ namespaceId: nsIdProp }: NamespaceProps) {
data={data ? data : defaultNamespaceSummaryData}
pipelineData={pipelineRawData}
isbData={isbRawData}
+ monoVertexData={monoVertexRawData}
refresh={refresh}
/>
);
diff --git a/ui/src/components/pages/Namespace/partials/MonoVertexCard/index.tsx b/ui/src/components/pages/Namespace/partials/MonoVertexCard/index.tsx
new file mode 100644
index 0000000000..80cd548716
--- /dev/null
+++ b/ui/src/components/pages/Namespace/partials/MonoVertexCard/index.tsx
@@ -0,0 +1,580 @@
+import React, { useCallback, useContext, useEffect, useState } from "react";
+import Paper from "@mui/material/Paper";
+import { Link } from "react-router-dom";
+import { PipelineCardProps } from "../../../../../types/declarations/namespace";
+import {
+ Box,
+ Button,
+ CircularProgress,
+ Grid,
+ MenuItem,
+ Select,
+ SelectChangeEvent,
+} from "@mui/material";
+import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
+import { DeleteModal } from "../DeleteModal";
+import {
+ getAPIResponseError,
+ IconsStatusMap,
+ StatusString,
+ timeAgo,
+ UNKNOWN,
+ PAUSED,
+ RUNNING,
+ // PAUSING,
+ DELETING,
+ getBaseHref,
+} from "../../../../../utils";
+import { useMonoVertexUpdateFetch } from "../../../../../utils/fetchWrappers/monoVertexUpdateFetch";
+import { AppContextProps } from "../../../../../types/declarations/app";
+import { AppContext } from "../../../../../App";
+import { SidebarType } from "../../../../common/SlidingSidebar";
+import { ViewType } from "../../../../common/SpecEditor";
+import pipelineIcon from "../../../../../images/pipeline.png";
+
+import "./style.css";
+
+export interface DeleteProps {
+ type: "pipeline";
+ pipelineId?: string;
+}
+
+export function MonoVertexCard({
+ namespace,
+ data,
+ statusData,
+ refresh,
+}: PipelineCardProps) {
+ const { setSidebarProps, host, isReadOnly } =
+ useContext(AppContext);
+ const [viewOption] = useState("view");
+ const [editOption] = useState("edit");
+ const [deleteOption] = useState("delete");
+ const [deleteProps, setDeleteProps] = useState();
+ const [statusPayload, setStatusPayload] = useState(undefined);
+ const [error, setError] = useState(undefined);
+ const [successMessage, setSuccessMessage] = useState(
+ undefined
+ );
+ const [timerDateStamp, setTimerDateStamp] = useState(undefined);
+ const [timer, setTimer] = useState(undefined);
+ const [pipelineAbleToLoad, setPipelineAbleToLoad] = useState(false);
+ const { pipelineAvailable } = useMonoVertexUpdateFetch({
+ namespaceId: namespace,
+ pipelineId: data?.name,
+ active: !pipelineAbleToLoad,
+ refreshInterval: 5000, // 5 seconds
+ });
+
+ useEffect(() => {
+ if (pipelineAvailable) {
+ setPipelineAbleToLoad(true);
+ }
+ }, [pipelineAvailable]);
+
+ const handleUpdateComplete = useCallback(() => {
+ refresh();
+ setPipelineAbleToLoad(false);
+ if (!setSidebarProps) {
+ return;
+ }
+ // Close sidebar
+ setSidebarProps(undefined);
+ }, [setSidebarProps, refresh]);
+
+ const handleViewChange = useCallback(
+ (event: SelectChangeEvent) => {
+ if (event.target.value === "pipeline" && setSidebarProps) {
+ setSidebarProps({
+ type: SidebarType.PIPELINE_UPDATE,
+ specEditorProps: {
+ titleOverride: `View Pipeline: ${data?.name}`,
+ initialYaml: statusData?.monoVertex,
+ namespaceId: namespace,
+ pipelineId: data?.name,
+ viewType: ViewType.READ_ONLY,
+ onUpdateComplete: handleUpdateComplete,
+ },
+ });
+ }
+ },
+ [setSidebarProps, handleUpdateComplete, data]
+ );
+
+ const handleEditChange = useCallback(
+ (event: SelectChangeEvent) => {
+ if (event.target.value === "pipeline" && setSidebarProps) {
+ setSidebarProps({
+ type: SidebarType.PIPELINE_UPDATE,
+ specEditorProps: {
+ initialYaml: statusData?.monoVertex,
+ namespaceId: namespace,
+ pipelineId: data?.name,
+ viewType: ViewType.EDIT,
+ onUpdateComplete: handleUpdateComplete,
+ },
+ });
+ }
+ },
+ [setSidebarProps, handleUpdateComplete, data]
+ );
+
+ const handleDeleteChange = useCallback(
+ (event: SelectChangeEvent) => {
+ if (event.target.value === "pipeline") {
+ setDeleteProps({
+ type: "pipeline",
+ pipelineId: data?.name,
+ });
+ }
+ },
+ [data]
+ );
+
+ const handleDeleteComplete = useCallback(() => {
+ refresh();
+ setDeleteProps(undefined);
+ }, [refresh]);
+
+ const handeDeleteCancel = useCallback(() => {
+ setDeleteProps(undefined);
+ }, []);
+
+ const pipelineStatus = statusData?.monoVertex?.status?.phase || UNKNOWN;
+ const handleTimer = useCallback(() => {
+ const dateString = new Date().toISOString();
+ const time = timeAgo(dateString);
+ setTimerDateStamp(time);
+ const pauseTimer = setInterval(() => {
+ const time = timeAgo(dateString);
+ setTimerDateStamp(time);
+ }, 1000);
+ setTimer(pauseTimer);
+ }, []);
+
+ const handlePlayClick = useCallback(() => {
+ handleTimer();
+ setStatusPayload({
+ spec: {
+ lifecycle: {
+ desiredPhase: RUNNING,
+ },
+ },
+ });
+ }, []);
+
+ const handlePauseClick = useCallback(() => {
+ handleTimer();
+ setStatusPayload({
+ spec: {
+ lifecycle: {
+ desiredPhase: PAUSED,
+ },
+ },
+ });
+ }, []);
+
+ useEffect(() => {
+ const patchStatus = async () => {
+ try {
+ const response = await fetch(
+ `${host}${getBaseHref()}/api/v1/namespaces/${namespace}/mono-vertices/${
+ data?.name
+ }`,
+ {
+ method: "PATCH",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(statusPayload),
+ }
+ );
+ const error = await getAPIResponseError(response);
+ if (error) {
+ setError(error);
+ } else {
+ refresh();
+ setSuccessMessage("Status updated successfully");
+ }
+ } catch (e: any) {
+ setError(e);
+ }
+ };
+ if (statusPayload) {
+ patchStatus();
+ }
+ }, [statusPayload, host]);
+
+ useEffect(() => {
+ if (
+ statusPayload?.spec?.lifecycle?.desiredPhase === PAUSED &&
+ statusData?.monoVertex?.status?.phase === PAUSED
+ ) {
+ clearInterval(timer);
+ setStatusPayload(undefined);
+ }
+ if (
+ statusPayload?.spec?.lifecycle?.desiredPhase === RUNNING &&
+ statusData?.monoVertex?.status?.phase === RUNNING
+ ) {
+ clearInterval(timer);
+ setStatusPayload(undefined);
+ }
+ }, [statusData]);
+
+ return (
+ <>
+
+
+
+
+
+ {data?.name}
+
+
+ {!isReadOnly && (
+
+ {error && statusPayload ? (
+
+ {error}
+
+ ) : successMessage &&
+ statusPayload &&
+ ((statusPayload.spec.lifecycle.desiredPhase === PAUSED &&
+ statusData?.monoVertex?.status?.phase !== PAUSED) ||
+ (statusPayload.spec.lifecycle.desiredPhase === RUNNING &&
+ statusData?.monoVertex?.status?.phase !== RUNNING)) ? (
+
+ {" "}
+
+
+ {statusPayload?.spec?.lifecycle?.desiredPhase === PAUSED
+ ? "Pipeline Pausing..."
+ : "Pipeline Resuming..."}
+
+
+ {timerDateStamp}
+
+
+
+ ) : (
+ ""
+ )}
+
+
+ Resume
+
+
+ Pause
+
+
+ )}
+
+ {pipelineAbleToLoad ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ Status:
+ Health:
+
+
+
+
+
+
+ {StatusString[pipelineStatus]}
+ {StatusString["healthy"]}
+
+
+
+
+ {isReadOnly && (
+
+
+
+ VIEW
+
+
+ Pipeline
+
+
+
+ )}
+ {!isReadOnly && (
+
+
+
+ EDIT
+
+
+ Pipeline
+
+
+
+ )}
+ {!isReadOnly && (
+
+
+
+ DELETE
+
+
+ Pipeline
+
+
+
+ )}
+
+
+ {deleteProps && (
+
+ )}
+
+ >
+ );
+}
diff --git a/ui/src/components/pages/Namespace/partials/MonoVertexCard/style.css b/ui/src/components/pages/Namespace/partials/MonoVertexCard/style.css
new file mode 100644
index 0000000000..c0ad3f86d6
--- /dev/null
+++ b/ui/src/components/pages/Namespace/partials/MonoVertexCard/style.css
@@ -0,0 +1,10 @@
+.pipeline-card-name {
+ font-size: 2rem;
+ font-style: normal;
+ font-weight: 400;
+ color: #000;
+}
+
+.pipeline-card-icon {
+ width: 2.4rem;
+}
\ No newline at end of file
diff --git a/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelineListing/index.tsx b/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelineListing/index.tsx
index 3ea6280f0e..18f48cd3f0 100644
--- a/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelineListing/index.tsx
+++ b/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelineListing/index.tsx
@@ -12,12 +12,14 @@ import {
UNKNOWN,
} from "../../../../../../utils";
import { ListingProps } from "../ISBListing";
-import { PipelineData } from "../PipelinesTypes";
+import { MonoVertexData, PipelineData } from "../PipelinesTypes";
import { PipelineCard } from "../../PipelineCard";
+import { MonoVertexCard } from "../../MonoVertexCard";
interface PipelineListingProps extends ListingProps {
pipelineData: Map | undefined;
isbData: any;
+ monoVertexData: Map | undefined;
totalCount: number;
}
@@ -31,10 +33,11 @@ export function PipelineListing({
totalCount,
search,
isbData,
+ monoVertexData,
}: PipelineListingProps) {
- const [filteredPipelines, setFilteredPipelines] = useState(
- []
- );
+ const [filteredPipelines, setFilteredPipelines] = useState<
+ (PipelineData | MonoVertexData)[]
+ >([]);
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(
Math.ceil(totalCount / MAX_PAGE_SIZE)
@@ -72,18 +75,30 @@ export function PipelineListing({
margin: "0.8rem 0 2.4rem 0",
}}
>
- {filteredPipelines.map((p: PipelineData) => {
- const isbName = pipelineData
- ? pipelineData[p.name]?.pipeline?.spec
- ?.interStepBufferServiceName || DEFAULT_ISB
- : DEFAULT_ISB;
+ {filteredPipelines.map((p: PipelineData | MonoVertexData) => {
+ if (p?.pipeline) {
+ const isbName = pipelineData
+ ? pipelineData[p.name]?.pipeline?.spec
+ ?.interStepBufferServiceName || DEFAULT_ISB
+ : DEFAULT_ISB;
+ return (
+
+
+
+ );
+ }
return (
-
-
+
@@ -91,52 +106,84 @@ export function PipelineListing({
})}
);
- }, [filteredPipelines, namespace, refresh]);
+ }, [
+ filteredPipelines,
+ namespace,
+ pipelineData,
+ isbData,
+ monoVertexData,
+ refresh,
+ ]);
useEffect(() => {
- let filtered: PipelineData[] = Object.values(
+ let filtered: (PipelineData | MonoVertexData)[] = Object.values(
pipelineData ? pipelineData : {}
);
+ filtered = [
+ ...filtered,
+ ...Object.values(monoVertexData ? monoVertexData : {}),
+ ];
if (search) {
// Filter by search
- filtered = filtered.filter((p: PipelineData) => p.name.includes(search));
+ filtered = filtered.filter((p: PipelineData | MonoVertexData) =>
+ p.name.includes(search)
+ );
}
// Sorting
if (orderBy.value === ALPHABETICAL_SORT) {
- filtered?.sort((a: PipelineData, b: PipelineData) => {
- if (orderBy.sortOrder === ASC) {
- return a.name > b.name ? 1 : -1;
- } else {
- return a.name < b.name ? 1 : -1;
+ filtered?.sort(
+ (
+ a: PipelineData | MonoVertexData,
+ b: PipelineData | MonoVertexData
+ ) => {
+ if (orderBy.sortOrder === ASC) {
+ return a.name > b.name ? 1 : -1;
+ } else {
+ return a.name < b.name ? 1 : -1;
+ }
}
- });
+ );
} else if (orderBy.value === LAST_UPDATED_SORT) {
- filtered?.sort((a: PipelineData, b: PipelineData) => {
- if (orderBy.sortOrder === ASC) {
- return a?.pipeline?.status?.lastUpdated >
- b?.pipeline?.status?.lastUpdated
- ? 1
- : -1;
- } else {
- return a?.pipeline?.status?.lastUpdated <
- b?.pipeline?.status?.lastUpdated
- ? 1
- : -1;
+ filtered?.sort(
+ (
+ a: PipelineData | MonoVertexData,
+ b: PipelineData | MonoVertexData
+ ) => {
+ const aType = a?.pipeline ? "pipeline" : "monoVertex";
+ const bType = b?.pipeline ? "pipeline" : "monoVertex";
+ if (orderBy.sortOrder === ASC) {
+ return Date.parse(a?.[aType]?.status?.lastUpdated) >
+ Date.parse(b?.[bType]?.status?.lastUpdated)
+ ? 1
+ : -1;
+ } else {
+ return Date.parse(a?.[aType]?.status?.lastUpdated) <
+ Date.parse(b?.[bType]?.status?.lastUpdated)
+ ? 1
+ : -1;
+ }
}
- });
+ );
} else {
- filtered?.sort((a: PipelineData, b: PipelineData) => {
- if (orderBy.sortOrder === ASC) {
- return Date.parse(a?.pipeline?.metadata?.creationTimestamp) >
- Date.parse(b?.pipeline?.metadata?.creationTimestamp)
- ? 1
- : -1;
- } else {
- return Date.parse(a?.pipeline?.metadata?.creationTimestamp) <
- Date.parse(b?.pipeline?.metadata?.creationTimestamp)
- ? 1
- : -1;
+ filtered?.sort(
+ (
+ a: PipelineData | MonoVertexData,
+ b: PipelineData | MonoVertexData
+ ) => {
+ const aType = a?.pipeline ? "pipeline" : "monoVertex";
+ const bType = b?.pipeline ? "pipeline" : "monoVertex";
+ if (orderBy.sortOrder === ASC) {
+ return Date.parse(a?.[aType]?.metadata?.creationTimestamp) >
+ Date.parse(b?.[bType]?.metadata?.creationTimestamp)
+ ? 1
+ : -1;
+ } else {
+ return Date.parse(a?.[aType]?.metadata?.creationTimestamp) <
+ Date.parse(b?.[bType]?.metadata?.creationTimestamp)
+ ? 1
+ : -1;
+ }
}
- });
+ );
}
//Filter by health
if (healthFilter !== ALL) {
@@ -153,7 +200,8 @@ export function PipelineListing({
//Filter by status
if (statusFilter !== ALL) {
filtered = filtered.filter((p) => {
- const currentStatus = p?.pipeline?.status?.phase || UNKNOWN;
+ const type = p?.pipeline ? "pipeline" : "monoVertex";
+ const currentStatus = p?.[type]?.status?.phase || UNKNOWN;
if (currentStatus.toLowerCase() === statusFilter.toLowerCase()) {
return true;
} else {
@@ -183,6 +231,7 @@ export function PipelineListing({
page,
pipelineData,
isbData,
+ monoVertexData,
orderBy,
healthFilter,
statusFilter,
diff --git a/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelinesTypes.ts b/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelinesTypes.ts
index e793005b9d..35bb2d4b42 100644
--- a/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelinesTypes.ts
+++ b/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/PipelinesTypes.ts
@@ -16,6 +16,20 @@ interface Pipeline {
status: Status;
}
+export interface MonoVertexData {
+ name: string;
+ status: string;
+ monoVertex: MonoVertex;
+}
+
+interface MonoVertex {
+ kind: string;
+ apiVersion: string;
+ metadata: any;
+ spec: any;
+ status: any;
+}
+
interface Status {
conditions: Condition[];
phase: string;
diff --git a/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/index.tsx b/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/index.tsx
index 68aa0f4350..a7889a33c9 100644
--- a/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/index.tsx
+++ b/ui/src/components/pages/Namespace/partials/NamespaceListingWrapper/index.tsx
@@ -111,6 +111,7 @@ export function NamespaceListingWrapper({
data,
pipelineData,
isbData,
+ monoVertexData,
refresh,
}: NamespacePipelineListingProps) {
const { setSidebarProps, isReadOnly } =
@@ -433,6 +434,7 @@ export function NamespaceListingWrapper({
diff --git a/ui/src/components/pages/Pipeline/partials/Graph/index.tsx b/ui/src/components/pages/Pipeline/partials/Graph/index.tsx
index a2ac1e38b2..7c5bd60160 100644
--- a/ui/src/components/pages/Pipeline/partials/Graph/index.tsx
+++ b/ui/src/components/pages/Pipeline/partials/Graph/index.tsx
@@ -64,6 +64,7 @@ import source from "../../../../../images/source.png";
import map from "../../../../../images/map.png";
import reduce from "../../../../../images/reduce.png";
import sink from "../../../../../images/sink.png";
+import monoVertex from "../../../../../images/monoVertex.svg";
import input from "../../../../../images/input0.svg";
import generator from "../../../../../images/generator0.svg";
@@ -161,6 +162,7 @@ const Flow = (props: FlowProps) => {
refresh,
namespaceId,
data,
+ type,
} = props;
const onIsLockedChange = useCallback(
@@ -317,7 +319,11 @@ const Flow = (props: FlowProps) => {
fontSize: "1.4rem",
}}
onClick={handlePlayClick}
- disabled={data?.pipeline?.status?.phase === RUNNING}
+ disabled={
+ type === "monoVertex"
+ ? true
+ : data?.pipeline?.status?.phase === RUNNING
+ }
>
Resume
@@ -333,8 +339,10 @@ const Flow = (props: FlowProps) => {
}}
onClick={handlePauseClick}
disabled={
- data?.pipeline?.status?.phase === PAUSED ||
- data?.pipeline?.status?.phase === PAUSING
+ type === "monoVertex"
+ ? true
+ : data?.pipeline?.status?.phase === PAUSED ||
+ data?.pipeline?.status?.phase === PAUSING
}
>
Pause
@@ -513,10 +521,18 @@ const Flow = (props: FlowProps) => {
Legend
-
-
-
Source
-
+ {type === "monoVertex" && (
+
+
+
MonoVertex
+
+ )}
+ {type === "pipeline" && (
+
+
+
Source
+
+ )}
{isMap && (
@@ -529,10 +545,12 @@ const Flow = (props: FlowProps) => {
Reduce
)}
-
-
-
Sink
-
+ {type === "pipeline" && (
+
+
+
Sink
+
+ )}
{isSideInput && (
@@ -569,7 +587,7 @@ const getHiddenValue = (edges: Edge[]) => {
};
export default function Graph(props: GraphProps) {
- const { data, namespaceId, pipelineId, refresh } = props;
+ const { data, namespaceId, pipelineId, type, refresh } = props;
const { sidebarProps, setSidebarProps } =
useContext
(AppContext);
@@ -833,6 +851,7 @@ export default function Graph(props: GraphProps) {
refresh={refresh}
namespaceId={namespaceId}
data={data}
+ type={type}
/>
diff --git a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx
index 1acd2e61b6..64718e4f12 100644
--- a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx
+++ b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx
@@ -3,6 +3,7 @@
import { FC, memo, useCallback, useContext, useMemo } from "react";
import { Tooltip } from "@mui/material";
import { Handle, NodeProps, Position } from "reactflow";
+import Box from "@mui/material/Box";
import { HighlightContext } from "../../index";
import { GeneratorColorContext } from "../../../../index";
import { HighlightContextProps } from "../../../../../../../types/declarations/graph";
@@ -11,6 +12,8 @@ import source from "../../../../../../../images/source.png";
import map from "../../../../../../../images/map.png";
import reduce from "../../../../../../../images/reduce.png";
import sink from "../../../../../../../images/sink.png";
+import monoVertex from "../../../../../../../images/monoVertex.svg";
+import transformer from "../../../../../../../images/transformer.svg";
import input0 from "../../../../../../../images/input0.svg";
import input1 from "../../../../../../../images/input1.svg";
import input2 from "../../../../../../../images/input2.svg";
@@ -148,17 +151,17 @@ const CustomNode: FC = ({
if (data?.type === "sideInput") {
return (
{data?.name} }
+ title={{data?.name} }
arrow
placement={"left"}
>
-
+
Spec View
}
+ title={Spec View }
arrow
placement={"bottom-start"}
>
- = ({
height={16}
style={{ alignSelf: "center" }}
/>
-
+
Show Edges}
+ title={Show Edges }
arrow
placement={"bottom-start"}
>
- = ({
}}
>
---
-
+
= ({
id="2"
position={Position.Right}
/>
-
+
);
}
if (data?.type === "generator") {
return (
- = ({
onClick={(e) => e.stopPropagation()}
>
Generator
-
+
);
}
@@ -284,25 +287,89 @@ const CustomNode: FC = ({
setHighlightValues({});
}, [setHidden, setHighlightValues]);
+ // arrow for containers in monoVertex
+ const arrowSvg = useMemo(() => {
+ return (
+
+
+
+
+
+ );
+ }, []);
+
return (
-
-
+
- {data?.name}
-
+ {data?.type !== "monoVertex" && (
+ {data?.name}
+ )}
+ {data?.type === "monoVertex" && (
+ <>
+ {data?.name}
+
+ Source Container }
+ arrow
+ placement={"left"}
+ >
+
+
+
+
+ {arrowSvg}
+ {data?.nodeInfo?.source?.transformer && (
+ Transformer Container
+ }
+ arrow
+ placement={"bottom"}
+ >
+
+
+
+
+ )}
+ {data?.nodeInfo?.source?.transformer && arrowSvg}
+
Sink Container}
+ arrow
+ placement={"right"}
+ >
+
+
+
+
+
+ >
+ )}
+
{data?.podnum <= 1 ? "pod" : "pods"}
-
+
}
placement={"top-end"}
arrow
>
-
+
- {/*
+ {/*
- */}
+ */}
- Processing Rates
- 1 min: {data?.vertexMetrics?.ratePerMin}/sec
- 5 min: {data?.vertexMetrics?.ratePerFiveMin}/sec
- 15 min: {data?.vertexMetrics?.ratePerFifteenMin}/sec
-
+
+ Processing Rates
+ 1 min: {data?.vertexMetrics?.ratePerMin}/sec
+ 5 min: {data?.vertexMetrics?.ratePerFiveMin}/sec
+ 15 min: {data?.vertexMetrics?.ratePerFifteenMin}/sec
+
}
arrow
placement={"bottom-end"}
>
-
+
{data?.vertexMetrics?.ratePerMin}/sec
-
+
{(data?.type === "udf" || data?.type === "sink") && (
@@ -407,7 +477,7 @@ const CustomNode: FC = ({
/>
);
})}
-
+
{data?.nodeInfo?.sideInputs?.map((input: any, idx: number) => {
return (
= ({
/>
);
})}
-
+
);
};
export default memo(CustomNode);
diff --git a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css
index f0a4a80594..9e9b094718 100644
--- a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css
+++ b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css
@@ -1,160 +1,193 @@
.node-rate {
- display: flex;
- width: 9rem;
- height: 2.2rem;
- border-radius: 2rem;
- background: #D1DEE9;
- color: #274C77;
- font-family: "IBM Plex Sans", sans-serif;
- font-size: 1.2rem;
- font-style: normal;
- font-weight: 500;
- line-height: normal;
- justify-content: center;
- align-items: center;
- position: absolute;
- bottom: -12.5%;
- right: 8%;
- text-transform: lowercase;
+ display: flex;
+ width: 9rem;
+ height: 2.2rem;
+ border-radius: 2rem;
+ background: #d1dee9;
+ color: #274c77;
+ font-family: "IBM Plex Sans", sans-serif;
+ font-size: 1.2rem;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ bottom: -12.5%;
+ right: 8%;
+ text-transform: lowercase;
}
.node-pods {
- display: flex;
- width: 10rem;
- height: 2.2rem;
- border-radius: 2rem;
- background: #D1DEE9;
- color: #274C77;
- font-family: "IBM Plex Sans", sans-serif;
- font-size: 1.2rem;
- font-style: normal;
- font-weight: 700;
- line-height: normal;
- justify-content: space-evenly;
- align-items: center;
- position: absolute;
- top: -13.5%;
- left: 10%;
- text-transform: lowercase;
+ display: flex;
+ width: 10rem;
+ height: 2.2rem;
+ border-radius: 2rem;
+ background: #d1dee9;
+ color: #274c77;
+ font-family: "IBM Plex Sans", sans-serif;
+ font-size: 1.2rem;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ justify-content: space-evenly;
+ align-items: center;
+ position: absolute;
+ top: -13.5%;
+ left: 10%;
+ text-transform: lowercase;
}
.node-pods > img {
- width: 2rem;
+ width: 2rem;
}
.node-status {
- width: 2.2rem;
- height: 2.2rem;
- border-radius: 2rem;
- background: #D1DEE9;
- display: flex;
- justify-content: center;
- align-items: center;
- position: absolute;
- bottom: -12.5%;
- left: 8%;
+ width: 2.2rem;
+ height: 2.2rem;
+ border-radius: 2rem;
+ background: #d1dee9;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ bottom: -12.5%;
+ left: 8%;
}
.node-tooltip {
- font-size: 1.2rem;
- font-weight: 600;
- font-family: "Avenir", sans-serif;
- cursor: pointer;
+ font-size: 1.2rem;
+ font-weight: 600;
+ font-family: "Avenir", sans-serif;
+ cursor: pointer;
}
.react-flow__node-input {
- width: 25.2rem;
- height: 7.8rem;
- border-radius: 2rem;
- border: 0.01rem solid #009EAC;
- background: var(--boxes, #F8F8FB);
- box-shadow: 0 2.4rem 4.8rem -0.8rem rgba(39, 76, 119, 0.16);
- cursor: pointer;
+ width: 25.2rem;
+ height: 7.8rem;
+ border-radius: 2rem;
+ border: 0.01rem solid #009eac;
+ background: var(--boxes, #f8f8fb);
+ box-shadow: 0 2.4rem 4.8rem -0.8rem rgba(39, 76, 119, 0.16);
+ cursor: pointer;
}
.node-info {
- display: flex;
- width: 100%;
- height: 100%;
- flex-direction: column;
- justify-content: center;
- color: #274C77;
- text-align: center;
- font-family: "IBM Plex Sans", sans-serif;
- font-size: 1.4rem;
- font-style: normal;
- font-weight: 500;
- line-height: normal;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
+ display: flex;
+ width: 100%;
+ height: 100%;
+ flex-direction: column;
+ justify-content: center;
+ color: #274c77;
+ text-align: center;
+ font-family: "IBM Plex Sans", sans-serif;
+ font-size: 1.4rem;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.node-info-mono {
+ display: flex;
+ width: 100%;
+ height: 50%;
+ flex-direction: column;
+ justify-content: center;
+ color: #274c77;
+ text-align: center;
+ font-family: "IBM Plex Sans", sans-serif;
+ font-size: 1.4rem;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.mono-vertex-img-wrapper {
+ height: 2rem;
+ width: 2rem;
+ border-radius: 50%;
+ border: 1px solid #d1dee9;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.mono-vertex-img {
+ height: 1rem;
+ width: 1rem;
}
.node-icon {
- width: 1.144rem;
- height: 1.144rem;
- display: inline-block;
- vertical-align: middle;
- position: relative;
- margin-left: 0.01rem;
- margin-right: 0.01rem;
- padding-bottom: 0.05rem;
+ width: 1.144rem;
+ height: 1.144rem;
+ display: inline-block;
+ vertical-align: middle;
+ position: relative;
+ margin-left: 0.01rem;
+ margin-right: 0.01rem;
+ padding-bottom: 0.05rem;
}
.node-podnum {
- font-weight: bold;
+ font-weight: bold;
}
.react-flow__handle {
- /*background : #8D9096;*/
- z-index: -1;
- margin: -0.32rem;
- height: 1.6rem;
- width: 1.6rem;
+ /*background : #8D9096;*/
+ z-index: -1;
+ margin: -0.32rem;
+ height: 1.6rem;
+ width: 1.6rem;
}
.sideInput_node {
- display: flex;
- cursor: pointer;
+ display: flex;
+ cursor: pointer;
}
.sideInput_node_ele {
- width: 3.6rem;
- height: 3rem;
- background: #E0E0E0;
- display: flex;
- justify-content: center;
- align-items: center;
+ width: 3.6rem;
+ height: 3rem;
+ background: #e0e0e0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
.generator_handle {
- top: 60%;
- left: 82.5%;
- background: none;
+ top: 60%;
+ left: 82.5%;
+ background: none;
}
.generator_node {
- font-size: 1.6rem;
- background: #F8F8FB;
- padding: 1.6rem 2.4rem 1.6rem 2.4rem;
- margin-left: -2.4rem;
- border-radius: 2rem;
- border: 0.1rem solid #DAE3E8;
- color: #6B6C72;
+ font-size: 1.6rem;
+ background: #f8f8fb;
+ padding: 1.6rem 2.4rem 1.6rem 2.4rem;
+ margin-left: -2.4rem;
+ border-radius: 2rem;
+ border: 0.1rem solid #dae3e8;
+ color: #6b6c72;
}
.react-flow__handle-bottom {
- z-index: -1;
- margin: -0.32rem;
- height: 1.6rem;
- width: 1.6rem;
- border: none;
- bottom: -15%;
- background: none !important;
+ z-index: -1;
+ margin: -0.32rem;
+ height: 1.6rem;
+ width: 1.6rem;
+ border: none;
+ bottom: -15%;
+ background: none !important;
}
.sideInput_handle {
- bottom: -26% ;
- position: absolute;
- cursor: pointer;
-}
\ No newline at end of file
+ bottom: -26%;
+ position: absolute;
+ cursor: pointer;
+}
diff --git a/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/index.tsx b/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/index.tsx
index 19ec973709..50182392b2 100644
--- a/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/index.tsx
+++ b/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/index.tsx
@@ -145,6 +145,7 @@ export default function NodeInfo(props: NodeInfoProps) {
namespaceId={namespaceId}
pipelineId={pipelineId}
vertexId={node.id}
+ type={node?.data?.type}
/>
)}
diff --git a/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/index.tsx b/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/index.tsx
index 9d2629fb8a..92cdccd453 100644
--- a/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/index.tsx
+++ b/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/index.tsx
@@ -20,7 +20,7 @@ import {
} from "../../../../../../../../../types/declarations/pods";
export function Pods(props: PodsProps) {
- const { namespaceId, pipelineId, vertexId } = props;
+ const { namespaceId, pipelineId, vertexId, type } = props;
if (!namespaceId || !pipelineId || !vertexId) {
return (
@@ -41,6 +41,7 @@ export function Pods(props: PodsProps) {
pipelineId,
vertexId,
selectedPod,
+ type,
setSelectedPod,
setSelectedContainer
);
diff --git a/ui/src/components/plugin/NumaflowMonitorApp/App.tsx b/ui/src/components/plugin/NumaflowMonitorApp/App.tsx
index 28bc5880e9..9b65030418 100644
--- a/ui/src/components/plugin/NumaflowMonitorApp/App.tsx
+++ b/ui/src/components/plugin/NumaflowMonitorApp/App.tsx
@@ -32,14 +32,6 @@ import "react-toastify/dist/ReactToastify.css";
const MAX_ERRORS = 6;
function App(props: AppProps) {
- // TODO remove, used for testing ns only installation
- // const { systemInfo, error: systemInfoError } = {
- // systemInfo: {
- // namespaced: true,
- // managedNamespace: "test",
- // },
- // error: undefined,
- // };
const { hostUrl = "", namespace = "" } = props;
const pageRef = useRef();
const [pageWidth, setPageWidth] = useState(0);
diff --git a/ui/src/components/plugin/Routes/Routes.tsx b/ui/src/components/plugin/Routes/Routes.tsx
index 3584a366c9..d7a75529cb 100644
--- a/ui/src/components/plugin/Routes/Routes.tsx
+++ b/ui/src/components/plugin/Routes/Routes.tsx
@@ -1,6 +1,7 @@
import { useLocation } from "react-router-dom";
import { Namespaces } from "../../pages/Namespace";
import { Pipeline } from "../../pages/Pipeline";
+import { MonoVertex } from "../../pages/MonoVertex";
export interface RoutesProps {
namespace: string;
@@ -9,9 +10,12 @@ export function Routes(props: RoutesProps) {
const location = useLocation();
const query = new URLSearchParams(location.search);
const pl = query.get("pipeline") || "";
+ const type = query.get("type") || "";
const { namespace } = props;
- return pl ? (
+ return type ? (
+
+ ) : pl ? (
) : (
diff --git a/ui/src/images/monoVertex.svg b/ui/src/images/monoVertex.svg
new file mode 100644
index 0000000000..99c36ab144
--- /dev/null
+++ b/ui/src/images/monoVertex.svg
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ui/src/images/transformer.svg b/ui/src/images/transformer.svg
new file mode 100644
index 0000000000..5f189d9e8a
--- /dev/null
+++ b/ui/src/images/transformer.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ui/src/types/declarations/graph.d.ts b/ui/src/types/declarations/graph.d.ts
index 34a9571daa..bd5abe05d0 100644
--- a/ui/src/types/declarations/graph.d.ts
+++ b/ui/src/types/declarations/graph.d.ts
@@ -6,6 +6,7 @@ export interface GraphProps {
data: GraphData;
namespaceId: string | undefined;
pipelineId: string | undefined;
+ type: "monoVertex" | "pipeline";
refresh: () => void;
}
@@ -37,6 +38,7 @@ export interface FlowProps {
refresh: () => void;
namespaceId: string | undefined;
data: any;
+ type: string;
}
export interface HighlightContextProps {
diff --git a/ui/src/types/declarations/namespace.d.ts b/ui/src/types/declarations/namespace.d.ts
index 3c3582e14d..65473bae4f 100644
--- a/ui/src/types/declarations/namespace.d.ts
+++ b/ui/src/types/declarations/namespace.d.ts
@@ -28,6 +28,7 @@ export interface NamespaceSummaryFetchResult {
data?: NamespaceSummaryData;
pipelineRawData?: any;
isbRawData?: any;
+ monoVertexRawData?: any;
loading: boolean;
error: any;
refresh: () => void;
@@ -50,6 +51,7 @@ export interface NamespacePipelineListingProps {
data: NamespaceSummaryData;
pipelineData?: Map;
isbData?: any;
+ monoVertexData?: any;
refresh: () => void;
}
diff --git a/ui/src/types/declarations/pipeline.d.ts b/ui/src/types/declarations/pipeline.d.ts
index 31aa0de14a..09b8757387 100644
--- a/ui/src/types/declarations/pipeline.d.ts
+++ b/ui/src/types/declarations/pipeline.d.ts
@@ -109,6 +109,45 @@ export interface PipelineSummaryFetchResult {
refresh: () => void;
}
+export interface MonoVertex {
+ spec: any;
+ metadata: any;
+ status?: any;
+}
+
+export interface MonoVertexSpec {
+ replicas: number;
+ source: any;
+ sink: any;
+ scale: any;
+}
+
+export interface MonoVertexMetrics {
+ ratePerMin: string;
+ ratePerFiveMin: string;
+ ratePerFifteenMin: string;
+ podMetrics: any[];
+ error: boolean;
+}
+
+export interface MonoVertexSummary {
+ name: string;
+ status: string;
+ lag?: number;
+ monoVertex: MonoVertex;
+}
+
+export interface MonoVertexMergeSummaryData {
+ monoVertexData: MonoVertexSummary;
+}
+
+export interface MonoVertexSummaryFetchResult {
+ data?: MonoVertexMergeSummaryData;
+ loading: boolean;
+ error: any;
+ refresh: () => void;
+}
+
export interface PipelineUpdateFetchResult {
pipelineAvailable: boolean;
}
diff --git a/ui/src/types/declarations/pods.d.ts b/ui/src/types/declarations/pods.d.ts
index eabc2d8988..31d551da0f 100644
--- a/ui/src/types/declarations/pods.d.ts
+++ b/ui/src/types/declarations/pods.d.ts
@@ -11,6 +11,7 @@ export interface PodsProps {
namespaceId: string;
pipelineId: string;
vertexId: string;
+ type: string;
}
export interface PodContainerSpec {
diff --git a/ui/src/utils/fetchWrappers/clusterSummaryFetch.ts b/ui/src/utils/fetchWrappers/clusterSummaryFetch.ts
index e7de7408a5..ea606f2b02 100644
--- a/ui/src/utils/fetchWrappers/clusterSummaryFetch.ts
+++ b/ui/src/utils/fetchWrappers/clusterSummaryFetch.ts
@@ -33,14 +33,22 @@ const rawDataToClusterSummary = (
rawData.forEach((ns: any) => {
// Pipeline counts
- const nsPipelinesHealthyCount = ns.pipelineSummary?.active?.Healthy || 0;
- const nsPipelinesWarningCount = ns.pipelineSummary?.active?.Warning || 0;
- const nsPipelinesCriticalCount = ns.pipelineSummary?.active?.Critical || 0;
+ const nsPipelinesHealthyCount =
+ (ns.pipelineSummary?.active?.Healthy || 0) +
+ (ns.monoVertexSummary?.active?.Healthy || 0);
+ const nsPipelinesWarningCount =
+ (ns.pipelineSummary?.active?.Warning || 0) +
+ (ns.monoVertexSummary?.active?.Warning || 0);
+ const nsPipelinesCriticalCount =
+ (ns.pipelineSummary?.active?.Critical || 0) +
+ (ns.monoVertexSummary?.active?.Critical || 0);
const nsPipelinesActiveCount =
nsPipelinesHealthyCount +
nsPipelinesWarningCount +
nsPipelinesCriticalCount;
- const nsPipelinesInactiveCount = ns.pipelineSummary?.inactive || 0;
+ const nsPipelinesInactiveCount =
+ (ns.pipelineSummary?.inactive || 0) +
+ (ns.monoVertexSummary?.inactive || 0);
const nsPipelinesCount = nsPipelinesActiveCount + nsPipelinesInactiveCount;
// ISB counts
const nsIsbsHealthyCount = ns.isbServiceSummary?.active?.Healthy || 0;
diff --git a/ui/src/utils/fetchWrappers/monoVertexFetch.ts b/ui/src/utils/fetchWrappers/monoVertexFetch.ts
new file mode 100644
index 0000000000..41c0972c4a
--- /dev/null
+++ b/ui/src/utils/fetchWrappers/monoVertexFetch.ts
@@ -0,0 +1,119 @@
+import { useEffect, useState, useCallback, useContext } from "react";
+import { Options, useFetch } from "./fetch";
+import { MonoVertexSummaryFetchResult } from "../../types/declarations/pipeline";
+import { getBaseHref } from "../index";
+import { AppContextProps } from "../../types/declarations/app";
+import { AppContext } from "../../App";
+
+const DATA_REFRESH_INTERVAL = 15000; // ms
+
+// fetch monoVertex summary
+export const useMonoVertexSummaryFetch = ({
+ namespaceId,
+ pipelineId,
+ addError,
+}: any) => {
+ const [options, setOptions] = useState({
+ skip: false,
+ requestKey: "",
+ });
+
+ const refresh = useCallback(() => {
+ setOptions({
+ skip: false,
+ requestKey: "id" + Math.random().toString(16).slice(2),
+ });
+ }, []);
+
+ const [results, setResults] = useState({
+ data: undefined,
+ loading: true,
+ error: undefined,
+ refresh,
+ });
+
+ const { host } = useContext(AppContext);
+
+ const {
+ data: monoVertexData,
+ loading: monoVertexLoading,
+ error: monoVertexError,
+ } = useFetch(
+ `${host}${getBaseHref()}/api/v1/namespaces/${namespaceId}/mono-vertices/${pipelineId}`,
+ undefined,
+ options
+ );
+
+ useEffect(() => {
+ setInterval(() => {
+ setOptions({
+ skip: false,
+ requestKey: "id" + Math.random().toString(16).slice(2),
+ });
+ }, DATA_REFRESH_INTERVAL);
+ }, []);
+
+ useEffect(() => {
+ if (monoVertexLoading) {
+ if (options?.requestKey === "") {
+ setResults({
+ data: undefined,
+ loading: true,
+ error: undefined,
+ refresh,
+ });
+ }
+ return;
+ }
+ if (monoVertexError) {
+ if (options?.requestKey === "") {
+ // Failed on first load, return error
+ setResults({
+ data: undefined,
+ loading: false,
+ error: monoVertexError,
+ refresh,
+ });
+ } else {
+ // Failed on refresh, add error to app context
+ addError(monoVertexError);
+ }
+ return;
+ }
+ if (monoVertexData?.errMsg) {
+ if (options?.requestKey === "") {
+ // Failed on first load, return error
+ setResults({
+ data: undefined,
+ loading: false,
+ error: monoVertexData?.errMsg,
+ refresh,
+ });
+ } else {
+ // Failed on refresh, add error to app context
+ addError(monoVertexData?.errMsg);
+ }
+ return;
+ }
+ if (monoVertexData) {
+ const monoVertexSummary = {
+ monoVertexData: monoVertexData?.data,
+ };
+ setResults({
+ data: monoVertexSummary,
+ loading: false,
+ error: undefined,
+ refresh,
+ });
+ return;
+ }
+ }, [
+ monoVertexData,
+ monoVertexLoading,
+ monoVertexError,
+ options,
+ refresh,
+ addError,
+ ]);
+ return results;
+};
diff --git a/ui/src/utils/fetchWrappers/monoVertexUpdateFetch.ts b/ui/src/utils/fetchWrappers/monoVertexUpdateFetch.ts
new file mode 100644
index 0000000000..640d718dd3
--- /dev/null
+++ b/ui/src/utils/fetchWrappers/monoVertexUpdateFetch.ts
@@ -0,0 +1,92 @@
+import { useContext, useEffect, useState } from "react";
+import { Options, useFetch } from "./fetch";
+import { getBaseHref } from "../index";
+import { AppContextProps } from "../../types/declarations/app";
+import { AppContext } from "../../App";
+import { PipelineUpdateFetchResult } from "../../types/declarations/pipeline";
+
+const DATA_REFRESH_INTERVAL = 1000; // ms
+
+// fetch monoVertex to check for existence
+export const useMonoVertexUpdateFetch = ({
+ namespaceId,
+ pipelineId,
+ active,
+ refreshInterval = DATA_REFRESH_INTERVAL,
+}: any) => {
+ const [options, setOptions] = useState({
+ skip: !active,
+ requestKey: "",
+ });
+
+ const [results, setResults] = useState({
+ pipelineAvailable: false,
+ });
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [intervalId, setIntervalId] = useState();
+
+ const { host } = useContext(AppContext);
+
+ const { data, loading, error } = useFetch(
+ `${host}${getBaseHref()}/api/v1/namespaces/${namespaceId}/mono-vertices/${pipelineId}`,
+ undefined,
+ options
+ );
+
+ useEffect(() => {
+ if (!active) {
+ // Clear any existing interval running
+ setIntervalId((prev: any) => {
+ if (prev) {
+ clearInterval(prev);
+ }
+ return undefined;
+ });
+ return;
+ }
+ // Set periodic interval to refresh data
+ const id = setInterval(() => {
+ setOptions({
+ skip: false,
+ requestKey: "id" + Math.random().toString(16).slice(2),
+ });
+ }, refreshInterval);
+ // Clear any existing interval running and store new one
+ setIntervalId((prev: any) => {
+ if (prev) {
+ clearInterval(prev);
+ }
+ return id;
+ });
+ return () => {
+ // Clear interval on unmount
+ clearInterval(id);
+ };
+ }, [active, refreshInterval]);
+
+ useEffect(() => {
+ if (loading) {
+ if (options?.requestKey === "") {
+ // Only set false when it's the first load. Keep existing result otherwise.
+ setResults({
+ pipelineAvailable: false,
+ });
+ }
+ return;
+ }
+ if (error || data?.errMsg) {
+ setResults({
+ pipelineAvailable: false,
+ });
+ return;
+ }
+ if (data?.data) {
+ setResults({
+ pipelineAvailable: true,
+ });
+ return;
+ }
+ }, [data, loading, error, options]);
+
+ return results;
+};
diff --git a/ui/src/utils/fetchWrappers/namespaceK8sEventsFetch.ts b/ui/src/utils/fetchWrappers/namespaceK8sEventsFetch.ts
index e3df856db9..99b0ee434b 100644
--- a/ui/src/utils/fetchWrappers/namespaceK8sEventsFetch.ts
+++ b/ui/src/utils/fetchWrappers/namespaceK8sEventsFetch.ts
@@ -70,7 +70,13 @@ export const useNamespaceK8sEventsFetch = ({
if (vertex) {
return `${BASE_URL}?objectType=vertex&objectName=${pipeline}-${vertex}`;
} else if (pipeline) {
- return `${BASE_URL}?objectType=pipeline&objectName=${pipeline}`;
+ const isMonoVertex = pipeline.endsWith("(MonoVertex)");
+ const pipelineName = isMonoVertex
+ ? pipeline.replace(/\s*\(.*?\)\s*/g, "").trim()
+ : pipeline;
+ return `${BASE_URL}?objectType=${
+ isMonoVertex ? "monovertex" : "pipeline"
+ }&objectName=${pipelineName}`;
}
return `${BASE_URL}`;
}, [namespace, pipeline, vertex]);
diff --git a/ui/src/utils/fetchWrappers/namespaceSummaryFetch.ts b/ui/src/utils/fetchWrappers/namespaceSummaryFetch.ts
index 7a051f95a7..5f4a625695 100644
--- a/ui/src/utils/fetchWrappers/namespaceSummaryFetch.ts
+++ b/ui/src/utils/fetchWrappers/namespaceSummaryFetch.ts
@@ -12,11 +12,12 @@ import {
const rawDataToNamespaceSummary = (
rawPipelineData: any[],
- rawIsbData: any[]
+ rawIsbData: any[],
+ rawMonoVertexData: any[]
): NamespaceSummaryData | undefined => {
- const pipelinesCount = Array.isArray(rawPipelineData)
- ? rawPipelineData.length
- : 0;
+ const pipelinesCount =
+ (Array.isArray(rawPipelineData) ? rawPipelineData.length : 0) +
+ (Array.isArray(rawMonoVertexData) ? rawMonoVertexData.length : 0);
let pipelinesActiveCount = 0;
let pipelinesInactiveCount = 0;
let pipelinesHealthyCount = 0;
@@ -56,6 +57,34 @@ const rawDataToNamespaceSummary = (
status: pipeline.status,
});
});
+ // adding MonoVertex count to pipeline count
+ Array.isArray(rawMonoVertexData) &&
+ rawMonoVertexData?.forEach((monoVertex: any) => {
+ switch (monoVertex.status) {
+ case "healthy":
+ pipelinesActiveCount++;
+ pipelinesHealthyCount++;
+ break;
+ case "warning":
+ pipelinesActiveCount++;
+ pipelinesWarningCount++;
+ break;
+ case "critical":
+ pipelinesActiveCount++;
+ pipelinesCriticalCount++;
+ break;
+ case "inactive":
+ pipelinesInactiveCount++;
+ break;
+ default:
+ break;
+ }
+ // Add pipeline summary to array
+ pipelineSummaries.push({
+ name: monoVertex.name,
+ status: monoVertex.status,
+ });
+ });
Array.isArray(rawIsbData) &&
rawIsbData?.forEach((isb: any) => {
switch (isb.status) {
@@ -142,6 +171,15 @@ export const useNamespaceSummaryFetch = ({
undefined,
options
);
+ const {
+ data: monoVertexData,
+ loading: monoVertexLoading,
+ error: monoVertexError,
+ } = useFetch(
+ `${host}${getBaseHref()}/api/v1/namespaces/${namespace}/mono-vertices`,
+ undefined,
+ options
+ );
useEffect(() => {
setInterval(() => {
@@ -153,7 +191,7 @@ export const useNamespaceSummaryFetch = ({
}, []);
useEffect(() => {
- if (pipelineLoading || isbLoading) {
+ if (pipelineLoading || isbLoading || monoVertexLoading) {
if (options?.requestKey === "" || loadOnRefresh) {
// Only set loading true when first load or when loadOnRefresh is true
setResults({
@@ -165,38 +203,41 @@ export const useNamespaceSummaryFetch = ({
}
return;
}
- if (pipelineError || isbError) {
+ if (pipelineError || isbError || monoVertexError) {
if (options?.requestKey === "") {
// Failed on first load, return error
setResults({
data: undefined,
loading: false,
- error: pipelineError || isbError,
+ error: pipelineError || isbError || monoVertexError,
refresh,
});
} else {
// Failed on refresh, add error to app context
- addError(pipelineError || isbError);
+ addError(pipelineError || isbError || monoVertexError);
}
return;
}
- if (pipelineData?.errMsg || isbData?.errMsg) {
+ if (pipelineData?.errMsg || isbData?.errMsg || monoVertexData?.errMsg) {
if (options?.requestKey === "") {
// Failed on first load, return error
setResults({
data: undefined,
loading: false,
- error: pipelineData?.errMsg || isbData?.errMsg,
+ error:
+ pipelineData?.errMsg || isbData?.errMsg || monoVertexData?.errMsg,
refresh,
});
} else {
// Failed on refresh, add error to app context
- addError(pipelineData?.errMsg || isbData?.errMsg);
+ addError(
+ pipelineData?.errMsg || isbData?.errMsg || monoVertexData?.errMsg
+ );
}
return;
}
- if (pipelineData && isbData) {
- const pipeLineMap = pipelineData?.data?.reduce((map: any, obj: any) => {
+ if (pipelineData && isbData && monoVertexData) {
+ const pipelineMap = pipelineData?.data?.reduce((map: any, obj: any) => {
map[obj.name] = obj;
return map;
}, {});
@@ -204,14 +245,23 @@ export const useNamespaceSummaryFetch = ({
map[obj.name] = obj;
return map;
}, {});
+ const monoVertexMap = monoVertexData?.data?.reduce(
+ (map: any, obj: any) => {
+ map[obj.name] = obj;
+ return map;
+ },
+ {}
+ );
const nsSummary = rawDataToNamespaceSummary(
pipelineData?.data,
- isbData?.data
+ isbData?.data,
+ monoVertexData?.data
);
setResults({
data: nsSummary,
- pipelineRawData: pipeLineMap,
+ pipelineRawData: pipelineMap,
isbRawData: isbMap,
+ monoVertexRawData: monoVertexMap,
loading: false,
error: undefined,
refresh,
@@ -221,10 +271,13 @@ export const useNamespaceSummaryFetch = ({
}, [
pipelineData,
isbData,
+ monoVertexData,
pipelineLoading,
isbLoading,
+ monoVertexLoading,
pipelineError,
isbError,
+ monoVertexError,
loadOnRefresh,
options,
refresh,
diff --git a/ui/src/utils/fetcherHooks/monoVertexViewFetch.ts b/ui/src/utils/fetcherHooks/monoVertexViewFetch.ts
new file mode 100644
index 0000000000..9c7cd459d3
--- /dev/null
+++ b/ui/src/utils/fetcherHooks/monoVertexViewFetch.ts
@@ -0,0 +1,278 @@
+import { useCallback, useContext, useEffect, useMemo, useState } from "react";
+import { Node } from "reactflow";
+import { isEqual } from "lodash";
+import { getBaseHref } from "../index";
+import { AppContextProps } from "../../types/declarations/app";
+import { AppContext } from "../../App";
+import {
+ MonoVertex,
+ MonoVertexSpec,
+ MonoVertexMetrics,
+} from "../../types/declarations/pipeline";
+
+export const useMonoVertexViewFetch = (
+ namespaceId: string | undefined,
+ pipelineId: string | undefined,
+ addError: (error: string) => void
+) => {
+ const [requestKey, setRequestKey] = useState("");
+ const [pipeline, setPipeline] = useState(undefined);
+ const [spec, setSpec] = useState(undefined);
+ const [monoVertexPods, setMonoVertexPods] = useState>(
+ new Map()
+ );
+ const [monoVertexMetrics, setMonoVertexMetrics] = useState<
+ Map
+ >(new Map());
+ const [pipelineErr, setPipelineErr] = useState(undefined);
+ const [loading, setLoading] = useState(true);
+ const { host } = useContext(AppContext);
+
+ const BASE_API = `${host}${getBaseHref()}/api/v1/namespaces/${namespaceId}/mono-vertices/${pipelineId}`;
+
+ const refresh = useCallback(() => {
+ setRequestKey(`${Date.now()}`);
+ }, []);
+
+ // Call to get pipeline
+ useEffect(() => {
+ const fetchPipeline = async () => {
+ try {
+ const response = await fetch(`${BASE_API}?refreshKey=${requestKey}`);
+ if (response.ok) {
+ const json = await response.json();
+ if (json?.data) {
+ // Update pipeline state with data from the response
+ setPipeline(json.data?.monoVertex);
+ // Update spec state if it is not equal to the spec from the response
+ if (!isEqual(spec, json.data)) setSpec(json.data?.monoVertex?.spec);
+ setPipelineErr(undefined);
+ } else if (json?.errMsg) {
+ // pipeline API call returns an error message
+ if (requestKey === "") {
+ setPipelineErr(json.errMsg);
+ } else {
+ addError(json.errMsg);
+ }
+ }
+ } else {
+ // Handle the case when the response is not OK
+ if (requestKey === "") {
+ if (response.status === 403) {
+ // Unauthorized user, display given or default error message
+ const data = await response.json();
+ if (data.errMsg) {
+ setPipelineErr(`Error: ${data.errMsg}`);
+ } else {
+ setPipelineErr(
+ `Error: user is not authorized to execute the requested action.`
+ );
+ }
+ } else {
+ setPipelineErr(`Response code: ${response.status}`);
+ }
+ } else {
+ addError(`Failed with code: ${response.status}`);
+ }
+ }
+ } catch (e: any) {
+ // Handle any errors that occur during the fetch request
+ if (requestKey === "") {
+ setPipelineErr(e.message);
+ } else {
+ addError(e.message);
+ }
+ }
+ };
+
+ fetchPipeline();
+ }, [requestKey, addError]);
+
+ // Refresh pipeline every 30 sec
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setRequestKey(`${Date.now()}`);
+ }, 30000);
+ return () => clearInterval(interval);
+ }, []);
+
+ // This useEffect is used to obtain all the pods for a given monoVertex.
+ useEffect(() => {
+ const vertexToPodsMap = new Map();
+ if (spec?.source && spec?.sink) {
+ // Fetch pods count for each vertex in parallel
+ Promise.allSettled([
+ fetch(`${BASE_API}/pods`)
+ .then((response) => {
+ if (response.ok) {
+ return response.json();
+ } else {
+ return Promise.reject({ response, vertex: pipelineId });
+ }
+ })
+ .then((json) => {
+ if (json?.data) {
+ const mvtxPods = json.data.filter(
+ (mvtx: any) => !mvtx?.metadata?.name.includes("-daemon-")
+ );
+ // Update vertexToPodsMap with the number of pods for the current vertex
+ vertexToPodsMap.set(pipelineId, mvtxPods?.length);
+ } else if (json?.errMsg) {
+ // Pods API call returns an error message
+ addError(json.errMsg);
+ }
+ }),
+ ])
+ .then((results) => {
+ results.forEach((result) => {
+ if (result && result?.status === "rejected") {
+ // Handle rejected promises and add error messages to podsErr
+ addError(`Failed to get pods: ${result.reason.response.status}`);
+ }
+ });
+ })
+ .then(() => {
+ if (!isEqual(monoVertexPods, vertexToPodsMap)) {
+ // Update vertexPods state if it is not equal to vertexToPodsMap
+ setMonoVertexPods(vertexToPodsMap);
+ }
+ })
+ .catch((e: any) => {
+ addError(`Error: ${e.message}`);
+ });
+ }
+ }, [spec, requestKey, addError]);
+
+ const getVertexMetrics = useCallback(() => {
+ const vertexToMetricsMap = new Map();
+
+ if (spec?.source && spec?.sink && monoVertexPods.size > 0) {
+ // Fetch metrics for monoVertex
+ Promise.allSettled([
+ fetch(`${BASE_API}/metrics`)
+ .then((response) => {
+ if (response.ok) {
+ return response.json();
+ } else {
+ return Promise.reject(response);
+ }
+ })
+ .then((json) => {
+ if (json?.data) {
+ const mvtx = json.data;
+ const monoVertexName = mvtx.monoVertex;
+ const monoVertexMetrics: MonoVertexMetrics = {
+ ratePerMin: "0.00",
+ ratePerFiveMin: "0.00",
+ ratePerFifteenMin: "0.00",
+ podMetrics: [],
+ error: false,
+ };
+ let ratePerMin = 0.0,
+ ratePerFiveMin = 0.0,
+ ratePerFifteenMin = 0.0;
+ // Calculate processing rates as summation of pod values
+ if ("processingRates" in mvtx) {
+ if ("1m" in mvtx["processingRates"]) {
+ ratePerMin += mvtx["processingRates"]["1m"];
+ }
+ if ("5m" in mvtx["processingRates"]) {
+ ratePerFiveMin += mvtx["processingRates"]["5m"];
+ }
+ if ("15m" in mvtx["processingRates"]) {
+ ratePerFifteenMin += mvtx["processingRates"]["15m"];
+ }
+ } else {
+ if (
+ monoVertexPods.has(monoVertexName) &&
+ monoVertexPods.get(monoVertexName) !== 0
+ ) {
+ // Handle case when processingRates are not available for a vertex
+ monoVertexMetrics.error = true;
+ addError(
+ `Failed to get metrics for ${monoVertexName} monoVertex`
+ );
+ }
+ }
+ monoVertexMetrics.ratePerMin = ratePerMin.toFixed(2);
+ monoVertexMetrics.ratePerFiveMin = ratePerFiveMin.toFixed(2);
+ monoVertexMetrics.ratePerFifteenMin =
+ ratePerFifteenMin.toFixed(2);
+ if (
+ monoVertexPods.has(monoVertexName) &&
+ monoVertexPods.get(monoVertexName) !== 0
+ ) {
+ monoVertexMetrics.podMetrics = json;
+ }
+ vertexToMetricsMap.set(monoVertexName, monoVertexMetrics);
+ } else if (json?.errMsg) {
+ // Metrics API call returns an error message
+ addError(json.errMsg);
+ }
+ }),
+ ])
+ .then((results) => {
+ results.forEach((result) => {
+ if (result && result?.status === "rejected") {
+ // Handle rejected promises and add error messages to metricsErr
+ addError(
+ `Failed to get metrics: ${result.reason.response.status}`
+ );
+ }
+ });
+ })
+ .then(() => setMonoVertexMetrics(vertexToMetricsMap))
+ .catch((e: any) => {
+ addError(`Error: ${e.message}`);
+ });
+ }
+ }, [spec, monoVertexPods, addError]);
+
+ // This useEffect is used to obtain metrics for a given monoVertex and refreshes every 1 minute
+ useEffect(() => {
+ getVertexMetrics();
+ const interval = setInterval(() => {
+ getVertexMetrics();
+ }, 60000);
+ return () => clearInterval(interval);
+ }, [getVertexMetrics]);
+
+ const vertices = useMemo(() => {
+ const newVertices: Node[] = [];
+ // if (spec?.vertices && vertexPods && vertexMetrics) {
+ if (spec?.source && spec?.sink && monoVertexMetrics) {
+ const newNode = {} as Node;
+ const name = pipelineId ?? "";
+ newNode.id = name;
+ newNode.data = { name: name };
+ newNode.data.podnum = spec?.replicas ? spec.replicas : 0;
+ newNode.position = { x: 0, y: 0 };
+ // change this in the future if you would like to make it draggable
+ newNode.draggable = false;
+ newNode.type = "custom";
+ newNode.data.nodeInfo = spec;
+ newNode.data.type = "monoVertex";
+ newNode.data.vertexMetrics = null;
+ newNode.data.vertexMetrics = monoVertexMetrics.has(name)
+ ? monoVertexMetrics.get(name)
+ : null;
+ newVertices.push(newNode);
+ }
+ return newVertices;
+ }, [spec, monoVertexMetrics]);
+
+ //sets loading variable
+ useEffect(() => {
+ if (pipeline && vertices?.length > 0) {
+ setLoading(false);
+ }
+ }, [pipeline, vertices]);
+
+ return {
+ pipeline,
+ vertices,
+ pipelineErr,
+ loading,
+ refresh,
+ };
+};
diff --git a/ui/src/utils/fetcherHooks/podsViewFetch.test.ts b/ui/src/utils/fetcherHooks/podsViewFetch.test.ts
index 0948c1fbc8..b113a7bf93 100644
--- a/ui/src/utils/fetcherHooks/podsViewFetch.test.ts
+++ b/ui/src/utils/fetcherHooks/podsViewFetch.test.ts
@@ -65,6 +65,7 @@ describe("Custom Pods hook", () => {
"simple-pipeline",
"cat",
undefined,
+ "udf",
jest.fn() as Dispatch>,
jest.fn() as Dispatch>
)
@@ -88,6 +89,7 @@ describe("Custom Pods hook", () => {
"simple-pipeline",
"cat",
undefined,
+ "udf",
jest.fn() as Dispatch>,
jest.fn() as Dispatch>
)
@@ -109,6 +111,7 @@ describe("Custom Pods hook", () => {
"simple-pipeline",
"cat",
undefined,
+ "udf",
jest.fn() as Dispatch>,
jest.fn() as Dispatch>
)
diff --git a/ui/src/utils/fetcherHooks/podsViewFetch.ts b/ui/src/utils/fetcherHooks/podsViewFetch.ts
index 9c57592428..e893381cf6 100644
--- a/ui/src/utils/fetcherHooks/podsViewFetch.ts
+++ b/ui/src/utils/fetcherHooks/podsViewFetch.ts
@@ -19,6 +19,7 @@ export const usePodsViewFetch = (
pipelineId: string | undefined,
vertexId: string | undefined,
selectedPod: Pod | undefined,
+ type: string,
setSelectedPod: Dispatch>,
setSelectedContainer: Dispatch>
) => {
@@ -39,12 +40,19 @@ export const usePodsViewFetch = (
const fetchPods = async () => {
try {
const response = await fetch(
- `${host}${getBaseHref()}/api/v1/namespaces/${namespaceId}/pipelines/${pipelineId}/vertices/${vertexId}/pods?refreshKey=${requestKey}`
+ `${host}${getBaseHref()}/api/v1/namespaces/${namespaceId}${
+ type === "monoVertex"
+ ? `/mono-vertices`
+ : `/pipelines/${pipelineId}/vertices`
+ }/${vertexId}/pods?refreshKey=${requestKey}`
);
if (response.ok) {
const json = await response.json();
if (json?.data) {
- const data = json?.data;
+ let data = json?.data;
+ data = data.filter(
+ (pod: any) => !pod?.metadata?.name.includes("-daemon-")
+ );
const pList = data?.map((pod: any) => {
const containers: string[] = [];
const containerSpecMap = new Map();
From 0489eae34a5ee998c26429c2664d14a59d71c169 Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Fri, 16 Aug 2024 14:21:46 -0400
Subject: [PATCH 005/188] chore: add missed tests for java (#1953)
Signed-off-by: Keran Yang
---
test/sdks-e2e/sdks_test.go | 12 +++---
test/sdks-e2e/testdata/flatmap-stream.yaml | 49 +++++++++++-----------
2 files changed, 29 insertions(+), 32 deletions(-)
diff --git a/test/sdks-e2e/sdks_test.go b/test/sdks-e2e/sdks_test.go
index a37785ba68..a1bf013f41 100644
--- a/test/sdks-e2e/sdks_test.go
+++ b/test/sdks-e2e/sdks_test.go
@@ -77,9 +77,9 @@ func (s *SDKsSuite) TestMapStreamUDFunctionAndSink() {
VertexPodLogContains("go-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("go-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
- //VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
- // VertexPodLogContains("java-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
+ VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ VertexPodLogContains("java-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello,hello,hello"))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello")))
@@ -90,10 +90,8 @@ func (s *SDKsSuite) TestMapStreamUDFunctionAndSink() {
VertexPodLogContains("go-udsink-2", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
w.Expect().
VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
-
- // FIXME(map-batch): enable Java
- //w.Expect().
- // VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
+ w.Expect().
+ VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
}
func (s *SDKsSuite) TestBatchMapUDFunctionAndSink() {
diff --git a/test/sdks-e2e/testdata/flatmap-stream.yaml b/test/sdks-e2e/testdata/flatmap-stream.yaml
index c8ba177696..503ffcaf3a 100644
--- a/test/sdks-e2e/testdata/flatmap-stream.yaml
+++ b/test/sdks-e2e/testdata/flatmap-stream.yaml
@@ -62,27 +62,26 @@ spec:
# https://github.com/numaproj/numaflow-python/tree/main/examples/sink/log
image: quay.io/numaio/numaflow-python/sink-log:stable
imagePullPolicy: Always
-## FIXME(map-batch): enable Java
-# - name: java-split
-# partitions: 3
-# limits:
-# readBatchSize: 1
-# scale:
-# min: 1
-# udf:
-# container:
-# # Split input message into an array with comma, see https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/mapstream/flatmapstream
-# image: quay.io/numaio/numaflow-java/flat-map-stream:stable
-# imagePullPolicy: Always
-# - name: java-udsink
-# scale:
-# min: 1
-# sink:
-# udsink:
-# container:
-# # https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/sink/simple
-# image: quay.io/numaio/numaflow-java/simple-sink:stable
-# imagePullPolicy: Always
+ - name: java-split
+ partitions: 3
+ limits:
+ readBatchSize: 1
+ scale:
+ min: 1
+ udf:
+ container:
+ # Split input message into an array with comma, see https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/mapstream/flatmapstream
+ image: quay.io/numaio/numaflow-java/flat-map-stream:stable
+ imagePullPolicy: Always
+ - name: java-udsink
+ scale:
+ min: 1
+ sink:
+ udsink:
+ container:
+ # https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/sink/simple
+ image: quay.io/numaio/numaflow-java/simple-sink:stable
+ imagePullPolicy: Always
edges:
- from: in
to: go-split
@@ -94,7 +93,7 @@ spec:
to: python-split
- from: python-split
to: python-udsink
-# - from: in
-# to: java-split
-# - from: java-split
-# to: java-udsink
+ - from: in
+ to: java-split
+ - from: java-split
+ to: java-udsink
From a52102d1367215fd202c82791891d50d7ae52227 Mon Sep 17 00:00:00 2001
From: Sreekanth
Date: Sat, 17 Aug 2024 23:58:16 +0530
Subject: [PATCH 006/188] chore: switch to scratch base image (#1955)
---
.dockerignore | 1 +
Dockerfile | 50 ++--
Makefile | 2 +-
rust/Cargo.lock | 273 ++++++------------
rust/numaflow-models/Cargo.toml | 3 +-
rust/numaflow-models/templates/Cargo.mustache | 6 +-
rust/servesink/Cargo.toml | 6 +-
7 files changed, 127 insertions(+), 214 deletions(-)
create mode 100644 .dockerignore
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000..7a8cf80175
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+rust/target
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 7796a35b81..c5ac53cbb1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,13 @@
-ARG BASE_IMAGE=gcr.io/distroless/cc-debian12
+ARG BASE_IMAGE=scratch
ARG ARCH=$TARGETARCH
####################################################################################################
# base
####################################################################################################
-FROM debian:bullseye as base
+FROM alpine:3.17 AS base
+ARG ARCH
+RUN apk update && apk upgrade && \
+ apk add ca-certificates && \
+ apk --no-cache add tzdata
ARG ARCH
COPY dist/numaflow-linux-${ARCH} /bin/numaflow
@@ -13,12 +17,10 @@ RUN chmod +x /bin/numaflow
####################################################################################################
# extension base
####################################################################################################
-FROM rust:1.79-bookworm as extension-base
-
-RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
+FROM rust:1.80-bookworm AS extension-base
+ARG TARGETPLATFORM
-RUN apt-get update
-RUN apt-get install protobuf-compiler -y
+RUN apt-get update && apt-get install protobuf-compiler -y
RUN cargo new numaflow
# Create a new empty shell project
@@ -43,8 +45,15 @@ COPY ./rust/serving/Cargo.toml ./serving/Cargo.toml
COPY ./rust/Cargo.toml ./rust/Cargo.lock ./
# Build to cache dependencies
-RUN mkdir -p src/bin && echo "fn main() {}" > src/bin/main.rs && \
- cargo build --workspace --all --release
+RUN --mount=type=cache,target=/usr/local/cargo/registry \
+ --mount=type=cache,target=/usr/local/cargo/git \
+ case ${TARGETPLATFORM} in \
+ "linux/amd64") TARGET="x86_64-unknown-linux-gnu" ;; \
+ "linux/arm64") TARGET="aarch64-unknown-linux-gnu" ;; \
+ *) echo "Unsupported platform: ${TARGETPLATFORM}" && exit 1 ;; \
+ esac && \
+ mkdir -p src/bin && echo "fn main() {}" > src/bin/main.rs && \
+ RUSTFLAGS='-C target-feature=+crt-static' cargo build --workspace --all --release --target ${TARGET}
# Copy the actual source code files of the main project and the subprojects
COPY ./rust/src ./src
@@ -57,22 +66,29 @@ COPY ./rust/monovertex/build.rs ./monovertex/build.rs
COPY ./rust/monovertex/proto ./monovertex/proto
# Build the real binaries
-RUN touch src/bin/main.rs && \
- cargo build --workspace --all --release
+RUN --mount=type=cache,target=/usr/local/cargo/registry \
+ --mount=type=cache,target=/usr/local/cargo/git \
+ case ${TARGETPLATFORM} in \
+ "linux/amd64") TARGET="x86_64-unknown-linux-gnu" ;; \
+ "linux/arm64") TARGET="aarch64-unknown-linux-gnu" ;; \
+ *) echo "Unsupported platform: ${TARGETPLATFORM}" && exit 1 ;; \
+ esac && \
+ touch src/bin/main.rs && \
+ RUSTFLAGS='-C target-feature=+crt-static' cargo build --workspace --all --release --target ${TARGET} && \
+ cp -pv target/${TARGET}/release/numaflow /root/numaflow
####################################################################################################
# numaflow
####################################################################################################
ARG BASE_IMAGE
-FROM debian:bookworm as numaflow
-
-# Install necessary libraries
-RUN apt-get update && apt-get install -y libssl3
+FROM ${BASE_IMAGE} AS numaflow
+COPY --from=base /usr/share/zoneinfo /usr/share/zoneinfo
+COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=base /bin/numaflow /bin/numaflow
COPY ui/build /ui/build
-COPY --from=extension-base /numaflow/target/release/numaflow /bin/numaflow-rs
+COPY --from=extension-base /root/numaflow /bin/numaflow-rs
COPY ./rust/serving/config config
ENTRYPOINT [ "/bin/numaflow" ]
@@ -80,7 +96,7 @@ ENTRYPOINT [ "/bin/numaflow" ]
####################################################################################################
# testbase
####################################################################################################
-FROM alpine:3.17 as testbase
+FROM alpine:3.17 AS testbase
RUN apk update && apk upgrade && \
apk add ca-certificates && \
apk --no-cache add tzdata
diff --git a/Makefile b/Makefile
index 2c6e30d68c..f437836f48 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ DIST_DIR=${CURRENT_DIR}/dist
BINARY_NAME:=numaflow
DOCKERFILE:=Dockerfile
DEV_BASE_IMAGE:=debian:bookworm
-RELEASE_BASE_IMAGE:=debian:bookworm
+RELEASE_BASE_IMAGE:=scratch
BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
GIT_COMMIT=$(shell git rev-parse HEAD)
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 3b4bfaa19b..94bce16b9a 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -84,7 +84,7 @@ dependencies = [
"ring",
"rustls-native-certs",
"rustls-pemfile 2.1.3",
- "rustls-webpki",
+ "rustls-webpki 0.102.6",
"serde",
"serde_json",
"serde_nanos",
@@ -92,7 +92,7 @@ dependencies = [
"thiserror",
"time",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.26.0",
"tracing",
"tryhard",
"url",
@@ -252,11 +252,11 @@ dependencies = [
"hyper 1.4.1",
"hyper-util",
"pin-project-lite",
- "rustls",
+ "rustls 0.23.12",
"rustls-pemfile 2.1.3",
"rustls-pki-types",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.26.0",
"tower",
"tower-service",
]
@@ -716,21 +716,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-[[package]]
-name = "foreign-types"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
-dependencies = [
- "foreign-types-shared",
-]
-
-[[package]]
-name = "foreign-types-shared"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@@ -1102,15 +1087,29 @@ dependencies = [
"headers",
"http 1.1.0",
"hyper 1.4.1",
- "hyper-rustls",
+ "hyper-rustls 0.27.2",
"hyper-util",
"pin-project-lite",
"rustls-native-certs",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.26.0",
"tower-service",
]
+[[package]]
+name = "hyper-rustls"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
+dependencies = [
+ "futures-util",
+ "http 0.2.12",
+ "hyper 0.14.30",
+ "rustls 0.21.12",
+ "tokio",
+ "tokio-rustls 0.24.1",
+]
+
[[package]]
name = "hyper-rustls"
version = "0.27.2"
@@ -1122,11 +1121,11 @@ dependencies = [
"hyper 1.4.1",
"hyper-util",
"log",
- "rustls",
+ "rustls 0.23.12",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.26.0",
"tower-service",
]
@@ -1143,35 +1142,6 @@ dependencies = [
"tower-service",
]
-[[package]]
-name = "hyper-tls"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
-dependencies = [
- "bytes",
- "hyper 0.14.30",
- "native-tls",
- "tokio",
- "tokio-native-tls",
-]
-
-[[package]]
-name = "hyper-tls"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
-dependencies = [
- "bytes",
- "http-body-util",
- "hyper 1.4.1",
- "hyper-util",
- "native-tls",
- "tokio",
- "tokio-native-tls",
- "tower-service",
-]
-
[[package]]
name = "hyper-util"
version = "0.1.6"
@@ -1360,14 +1330,14 @@ dependencies = [
"http-body-util",
"hyper 1.4.1",
"hyper-http-proxy",
- "hyper-rustls",
+ "hyper-rustls 0.27.2",
"hyper-timeout",
"hyper-util",
"jsonpath-rust",
"k8s-openapi",
"kube-core",
"pem",
- "rustls",
+ "rustls 0.23.12",
"rustls-pemfile 2.1.3",
"secrecy",
"serde",
@@ -1581,7 +1551,7 @@ dependencies = [
"prost",
"prost-types",
"rcgen",
- "rustls",
+ "rustls 0.23.12",
"semver",
"serde",
"serde_json",
@@ -1605,23 +1575,6 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
-[[package]]
-name = "native-tls"
-version = "0.2.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
-dependencies = [
- "libc",
- "log",
- "openssl",
- "openssl-probe",
- "openssl-sys",
- "schannel",
- "security-framework",
- "security-framework-sys",
- "tempfile",
-]
-
[[package]]
name = "nkeys"
version = "0.4.3"
@@ -1750,7 +1703,7 @@ version = "0.0.0-pre"
dependencies = [
"k8s-openapi",
"kube",
- "reqwest 0.11.27",
+ "reqwest",
"serde",
"serde_derive",
"serde_json",
@@ -1773,50 +1726,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-[[package]]
-name = "openssl"
-version = "0.10.66"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
-dependencies = [
- "bitflags 2.6.0",
- "cfg-if",
- "foreign-types",
- "libc",
- "once_cell",
- "openssl-macros",
- "openssl-sys",
-]
-
-[[package]]
-name = "openssl-macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
-[[package]]
-name = "openssl-sys"
-version = "0.9.103"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
-dependencies = [
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
-]
-
[[package]]
name = "ordered-float"
version = "2.10.1"
@@ -2010,12 +1925,6 @@ dependencies = [
"spki",
]
-[[package]]
-name = "pkg-config"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
-
[[package]]
name = "portable-atomic"
version = "1.7.0"
@@ -2301,16 +2210,16 @@ dependencies = [
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.30",
- "hyper-tls 0.5.0",
+ "hyper-rustls 0.24.2",
"ipnet",
"js-sys",
"log",
"mime",
"mime_guess",
- "native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
+ "rustls 0.21.12",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
@@ -2318,56 +2227,14 @@ dependencies = [
"sync_wrapper 0.1.2",
"system-configuration",
"tokio",
- "tokio-native-tls",
+ "tokio-rustls 0.24.1",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "winreg 0.50.0",
-]
-
-[[package]]
-name = "reqwest"
-version = "0.12.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
-dependencies = [
- "base64 0.22.1",
- "bytes",
- "encoding_rs",
- "futures-core",
- "futures-util",
- "h2 0.4.5",
- "http 1.1.0",
- "http-body 1.0.1",
- "http-body-util",
- "hyper 1.4.1",
- "hyper-rustls",
- "hyper-tls 0.6.0",
- "hyper-util",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "native-tls",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "rustls-pemfile 2.1.3",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper 1.0.1",
- "system-configuration",
- "tokio",
- "tokio-native-tls",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
- "winreg 0.52.0",
+ "webpki-roots",
+ "winreg",
]
[[package]]
@@ -2441,6 +2308,18 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "rustls"
+version = "0.21.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki 0.101.7",
+ "sct",
+]
+
[[package]]
name = "rustls"
version = "0.23.12"
@@ -2452,7 +2331,7 @@ dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
- "rustls-webpki",
+ "rustls-webpki 0.102.6",
"subtle",
"zeroize",
]
@@ -2495,6 +2374,16 @@ version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
[[package]]
name = "rustls-webpki"
version = "0.102.6"
@@ -2534,6 +2423,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
[[package]]
name = "secrecy"
version = "0.8.0"
@@ -2684,7 +2583,7 @@ name = "servesink"
version = "0.1.0"
dependencies = [
"numaflow 0.1.0 (git+https://github.com/numaproj/numaflow-rs.git?branch=main)",
- "reqwest 0.12.5",
+ "reqwest",
"tokio",
"tonic",
"tracing",
@@ -3017,23 +2916,23 @@ dependencies = [
]
[[package]]
-name = "tokio-native-tls"
-version = "0.3.1"
+name = "tokio-retry"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
dependencies = [
- "native-tls",
+ "pin-project",
+ "rand",
"tokio",
]
[[package]]
-name = "tokio-retry"
-version = "0.3.0"
+name = "tokio-rustls"
+version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
- "pin-project",
- "rand",
+ "rustls 0.21.12",
"tokio",
]
@@ -3043,7 +2942,7 @@ version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
- "rustls",
+ "rustls 0.23.12",
"rustls-pki-types",
"tokio",
]
@@ -3390,12 +3289,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-
[[package]]
name = "version_check"
version = "0.9.5"
@@ -3493,6 +3386,12 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "webpki-roots"
+version = "0.25.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+
[[package]]
name = "which"
version = "4.4.2"
@@ -3694,16 +3593,6 @@ dependencies = [
"windows-sys 0.48.0",
]
-[[package]]
-name = "winreg"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
-dependencies = [
- "cfg-if",
- "windows-sys 0.48.0",
-]
-
[[package]]
name = "yaml-rust"
version = "0.4.5"
diff --git a/rust/numaflow-models/Cargo.toml b/rust/numaflow-models/Cargo.toml
index 6a38e0cc01..1e133e3b08 100644
--- a/rust/numaflow-models/Cargo.toml
+++ b/rust/numaflow-models/Cargo.toml
@@ -16,4 +16,5 @@ url = "^2.2"
uuid = { version = "^1.0", features = ["serde", "v4"] }
[dependencies.reqwest]
version = "^0.11"
-features = ["json", "multipart"]
+default-features = false
+features = ["json", "multipart", "rustls-tls"]
diff --git a/rust/numaflow-models/templates/Cargo.mustache b/rust/numaflow-models/templates/Cargo.mustache
index a4bbdfeae2..f7d1cdeb7c 100644
--- a/rust/numaflow-models/templates/Cargo.mustache
+++ b/rust/numaflow-models/templates/Cargo.mustache
@@ -50,7 +50,8 @@ secrecy = "0.8.0"
{{^supportAsync}}
[dependencies.reqwest]
version = "^0.11"
-features = ["json", "blocking", "multipart"]
+default-features = false
+features = ["json", "blocking", "multipart", "rustls-tls"]
{{/supportAsync}}
{{#supportAsync}}
{{#supportMiddleware}}
@@ -58,6 +59,7 @@ reqwest-middleware = "0.2.0"
{{/supportMiddleware}}
[dependencies.reqwest]
version = "^0.11"
-features = ["json", "multipart"]
+default-features = false
+features = ["json", "multipart", "rustls-tls"]
{{/supportAsync}}
{{/reqwest}}
diff --git a/rust/servesink/Cargo.toml b/rust/servesink/Cargo.toml
index 70fa8e55f5..e820030494 100644
--- a/rust/servesink/Cargo.toml
+++ b/rust/servesink/Cargo.toml
@@ -7,6 +7,10 @@ edition = "2021"
tonic = "0.12.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
-reqwest = "0.12.4"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
+
+[dependencies.reqwest]
+version = "^0.11"
+default-features = false
+features = ["rustls-tls"]
\ No newline at end of file
From cbad6996f063acf1f4a3d2d8fc2ec1acff6ee912 Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Mon, 19 Aug 2024 23:02:23 +0530
Subject: [PATCH 007/188] feat: enable fallback sink for mvtx (#1957)
Signed-off-by: Yashash H L
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
.codecov.yml | 3 +-
.../numaflow/v1alpha1/mono_vertex_types.go | 3 +
rust/monovertex/src/config.rs | 9 +
rust/monovertex/src/forwarder.rs | 614 ++++++++++++++----
rust/monovertex/src/lib.rs | 71 +-
rust/monovertex/src/metrics.rs | 22 +-
rust/monovertex/src/sink.rs | 10 +-
7 files changed, 579 insertions(+), 153 deletions(-)
diff --git a/.codecov.yml b/.codecov.yml
index 15eaa610e7..dd3d8fd073 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -7,7 +7,8 @@ ignore:
- "pkg/client/.*"
- "vendor/.*"
- "test/.*"
-- "serving/src/error.rs"
+- "rust/**/error.rs"
+- "rust/numaflow-models/**" # ignore generated files
coverage:
status:
patch: off
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index ac64c7e2a6..6f02509563 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -425,6 +425,9 @@ func (mvspec MonoVertexSpec) buildContainers(req getContainerReq) []corev1.Conta
if mvspec.Sink.UDSink != nil { // Only support UDSink for now.
containers = append(containers, mvspec.Sink.getUDSinkContainer(req))
}
+ if mvspec.Sink.Fallback != nil {
+ containers = append(containers, mvspec.Sink.getFallbackUDSinkContainer(req))
+ }
// Fallback sink is not supported.
containers = append(containers, mvspec.Sidecars...)
return containers
diff --git a/rust/monovertex/src/config.rs b/rust/monovertex/src/config.rs
index 7e102e06b8..5a3121e862 100644
--- a/rust/monovertex/src/config.rs
+++ b/rust/monovertex/src/config.rs
@@ -41,6 +41,7 @@ pub struct Settings {
pub log_level: String,
pub grpc_max_message_size: usize,
pub is_transformer_enabled: bool,
+ pub is_fallback_enabled: bool,
pub lag_check_interval_in_secs: u16,
pub lag_refresh_interval_in_secs: u16,
pub sink_max_retry_attempts: u16,
@@ -58,6 +59,7 @@ impl Default for Settings {
log_level: LevelFilter::INFO.to_string(),
grpc_max_message_size: DEFAULT_GRPC_MAX_MESSAGE_SIZE,
is_transformer_enabled: false,
+ is_fallback_enabled: false,
lag_check_interval_in_secs: DEFAULT_LAG_CHECK_INTERVAL_IN_SECS,
lag_refresh_interval_in_secs: DEFAULT_LAG_REFRESH_INTERVAL_IN_SECS,
sink_max_retry_attempts: DEFAULT_MAX_SINK_RETRY_ATTEMPTS,
@@ -111,6 +113,13 @@ impl Settings {
.ok_or(Error::ConfigError("Source not found".to_string()))?
.transformer
.is_some();
+
+ settings.is_fallback_enabled = mono_vertex_obj
+ .spec
+ .sink
+ .ok_or(Error::ConfigError("Sink not found".to_string()))?
+ .fallback
+ .is_some();
}
settings.log_level =
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index f774cc80b0..5862e753e9 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -1,6 +1,6 @@
use crate::config::config;
use crate::error::{Error, Result};
-use crate::message::Offset;
+use crate::message::{Message, Offset};
use crate::metrics;
use crate::metrics::forward_metrics;
use crate::sink::{proto, SinkClient};
@@ -11,8 +11,8 @@ use std::collections::HashMap;
use tokio::task::JoinSet;
use tokio::time::sleep;
use tokio_util::sync::CancellationToken;
-use tracing::info;
use tracing::log::warn;
+use tracing::{debug, info};
/// Forwarder is responsible for reading messages from the source, applying transformation if
/// transformer is present, writing the messages to the sink, and then acknowledging the messages
@@ -21,147 +21,89 @@ pub(crate) struct Forwarder {
source_client: SourceClient,
sink_client: SinkClient,
transformer_client: Option,
+ fallback_client: Option,
cln_token: CancellationToken,
common_labels: Vec<(String, String)>,
}
-impl Forwarder {
- #[allow(clippy::too_many_arguments)]
- pub(crate) async fn new(
+/// ForwarderBuilder is used to build a Forwarder instance with optional fields.
+pub(crate) struct ForwarderBuilder {
+ source_client: SourceClient,
+ sink_client: SinkClient,
+ cln_token: CancellationToken,
+ transformer_client: Option,
+ fb_sink_client: Option,
+}
+
+impl ForwarderBuilder {
+ /// Create a new builder with mandatory fields
+ pub(crate) fn new(
source_client: SourceClient,
sink_client: SinkClient,
- transformer_client: Option,
cln_token: CancellationToken,
- ) -> Result {
- let common_labels = metrics::forward_metrics_labels().clone();
-
- Ok(Self {
+ ) -> Self {
+ Self {
source_client,
sink_client,
- transformer_client,
- common_labels,
cln_token,
- })
+ transformer_client: None,
+ fb_sink_client: None,
+ }
+ }
+
+ /// Set the optional transformer client
+ pub(crate) fn transformer_client(mut self, transformer_client: TransformerClient) -> Self {
+ self.transformer_client = Some(transformer_client);
+ self
+ }
+
+ /// Set the optional fallback client
+ pub(crate) fn fb_sink_client(mut self, fallback_client: SinkClient) -> Self {
+ self.fb_sink_client = Some(fallback_client);
+ self
}
- /// run starts the forward-a-chunk loop and exits only after a chunk has been forwarded and ack'ed.
+ /// Build the Forwarder instance
+ #[must_use]
+ pub(crate) fn build(self) -> Forwarder {
+ let common_labels = metrics::forward_metrics_labels().clone();
+ Forwarder {
+ source_client: self.source_client,
+ sink_client: self.sink_client,
+ transformer_client: self.transformer_client,
+ fallback_client: self.fb_sink_client,
+ cln_token: self.cln_token,
+ common_labels,
+ }
+ }
+}
+
+impl Forwarder {
+ /// start starts the forward-a-chunk loop and exits only after a chunk has been forwarded and ack'ed.
/// this means that, in the happy path scenario a block is always completely processed.
/// this function will return on any error and will cause end up in a non-0 exit code.
- pub(crate) async fn run(&mut self) -> Result<()> {
- let mut messages_count: u64 = 0;
+ pub(crate) async fn start(&mut self) -> Result<()> {
+ let mut processed_msgs_count: usize = 0;
let mut last_forwarded_at = std::time::Instant::now();
loop {
- // TODO: emit latency metrics, metrics-rs histograms has memory leak issues.
let start_time = tokio::time::Instant::now();
- // two arms, either shutdown or forward-a-chunk
- tokio::select! {
- _ = self.cln_token.cancelled() => {
- info!("Shutdown signal received, stopping forwarder...");
- break;
- }
- result = self.source_client.read_fn(config().batch_size, config().timeout_in_ms) => {
- // Read messages from the source
- let messages = result?;
- info!("Read batch size: {} and latency - {}ms", messages.len(), start_time.elapsed().as_millis());
- // emit metrics
- let msg_count = messages.len() as u64;
- // collect all the offsets as the transformer can drop (via filter) messages
- let offsets = messages.iter().map(|msg| msg.offset.clone()).collect::>();
- messages_count += messages.len() as u64;
- let bytes_count = messages.iter().map(|msg| msg.value.len() as u64).sum::();
- forward_metrics().monovtx_read_total.get_or_create(&self.common_labels).inc_by(msg_count);
- forward_metrics().monovtx_read_bytes_total.get_or_create(&self.common_labels).inc_by(bytes_count);
-
- // Apply transformation if transformer is present
- let transformed_messages = if let Some(transformer_client) = &self.transformer_client {
- let start_time = tokio::time::Instant::now();
- let mut jh = JoinSet::new();
- for message in messages {
- let mut transformer_client = transformer_client.clone();
- jh.spawn(async move { transformer_client.transform_fn(message).await });
- }
+ if self.cln_token.is_cancelled() {
+ break;
+ }
- let mut results = Vec::new();
- while let Some(task) = jh.join_next().await {
- let result = task.map_err(|e| Error::TransformerError(format!("{:?}", e)))?;
- if let Some(result) = result? {
- results.extend(result);
- }
- }
- info!("Transformer latency - {}ms", start_time.elapsed().as_millis());
- results
- } else {
- messages
- };
-
- let transformed_msg_count = transformed_messages.len() as u64;
- forward_metrics().monovtx_sink_write_total.get_or_create(&self.common_labels).inc_by(transformed_msg_count);
-
- // Write messages to the sink
- // TODO: should we retry writing? what if the error is transient?
- // we could rely on gRPC retries and say that any error that is bubbled up is worthy of non-0 exit.
- // we need to confirm this via FMEA tests.
-
- let mut retry_messages = transformed_messages;
- let mut attempts = 0;
- let mut error_map = HashMap::new();
-
- while attempts <= config().sink_max_retry_attempts {
- let start_time = tokio::time::Instant::now();
- match self.sink_client.sink_fn(retry_messages.clone()).await {
- Ok(response) => {
- info!("Sink latency - {}ms", start_time.elapsed().as_millis());
-
- let failed_ids: Vec = response.results.iter()
- .filter(|result| result.status != proto::Status::Success as i32)
- .map(|result| result.id.clone())
- .collect();
- attempts += 1;
-
- if failed_ids.is_empty() {
- break;
- } else {
- // Collect error messages and their counts
- retry_messages.retain(|msg| failed_ids.contains(&msg.id));
- error_map.clear();
- for result in response.results {
- if result.status != proto::Status::Success as i32 {
- *error_map.entry(result.err_msg).or_insert(0) += 1;
- }
- }
-
- warn!("Retry attempt {} due to retryable error. Errors: {:?}", attempts, error_map);
- sleep(tokio::time::Duration::from_millis(config().sink_retry_interval_in_ms as u64)).await;
- }
- }
- Err(e) => return Err(e),
- }
- }
+ processed_msgs_count += self.read_and_process_messages().await?;
- if !error_map.is_empty() {
- return Err(Error::SinkError(format!(
- "Failed to sink messages after {} attempts. Errors: {:?}",
- attempts, error_map
- )));
- }
- // Acknowledge the messages back to the source
- let start_time = tokio::time::Instant::now();
- self.source_client.ack_fn(offsets).await?;
- info!("Ack latency - {}ms", start_time.elapsed().as_millis());
- // increment the acked messages count metric
- forward_metrics().monovtx_ack_total.get_or_create(&self.common_labels).inc_by(msg_count);
- }
- }
// if the last forward was more than 1 second ago, forward a chunk print the number of messages forwarded
if last_forwarded_at.elapsed().as_millis() >= 1000 {
info!(
"Forwarded {} messages at time {}",
- messages_count,
+ processed_msgs_count,
Utc::now()
);
- messages_count = 0;
+ processed_msgs_count = 0;
last_forwarded_at = std::time::Instant::now();
}
+
forward_metrics()
.monovtx_processing_time
.get_or_create(&self.common_labels)
@@ -169,14 +111,281 @@ impl Forwarder {
}
Ok(())
}
+
+ /*
+ Read messages from the source, apply transformation if transformer is present,
+ write the messages to the sink, if fallback messages are present write them to the fallback sink,
+ and then acknowledge the messages back to the source.
+ */
+ async fn read_and_process_messages(&mut self) -> Result {
+ let start_time = tokio::time::Instant::now();
+ let messages = self
+ .source_client
+ .read_fn(config().batch_size, config().timeout_in_ms)
+ .await?;
+ debug!(
+ "Read batch size: {} and latency - {}ms",
+ messages.len(),
+ start_time.elapsed().as_millis()
+ );
+
+ // nothing more to be done.
+ if messages.is_empty() {
+ return Ok(0);
+ }
+
+ let msg_count = messages.len() as u64;
+ let bytes_count = messages
+ .iter()
+ .map(|msg| msg.value.len() as u64)
+ .sum::();
+ forward_metrics()
+ .monovtx_read_total
+ .get_or_create(&self.common_labels)
+ .inc_by(msg_count);
+ forward_metrics()
+ .monovtx_read_bytes_total
+ .get_or_create(&self.common_labels)
+ .inc_by(bytes_count);
+
+ // collect all the offsets as the transformer can drop (via filter) messages
+ let offsets = messages
+ .iter()
+ .map(|msg| msg.offset.clone())
+ .collect::>();
+
+ // Apply transformation if transformer is present
+ let transformed_messages = self.apply_transformer(messages).await?;
+
+ // Write the messages to the sink
+ self.write_to_sink(transformed_messages).await?;
+
+ // Acknowledge the messages back to the source
+ self.acknowledge_messages(offsets).await?;
+
+ Ok(msg_count as usize)
+ }
+
+ // Applies transformation to the messages if transformer is present
+ // we concurrently apply transformation to all the messages.
+ async fn apply_transformer(&self, messages: Vec) -> Result> {
+ if let Some(transformer_client) = &self.transformer_client {
+ let start_time = tokio::time::Instant::now();
+ let mut jh = JoinSet::new();
+ for message in messages {
+ let mut transformer_client = transformer_client.clone();
+ jh.spawn(async move { transformer_client.transform_fn(message).await });
+ }
+
+ let mut results = Vec::new();
+ while let Some(task) = jh.join_next().await {
+ let result = task.map_err(|e| Error::TransformerError(format!("{:?}", e)))?;
+ if let Some(result) = result? {
+ results.extend(result);
+ }
+ }
+ debug!(
+ "Transformer latency - {}ms",
+ start_time.elapsed().as_millis()
+ );
+ Ok(results)
+ } else {
+ Ok(messages)
+ }
+ }
+
+ // Writes the messages to the sink and handles fallback messages if present
+ async fn write_to_sink(&mut self, mut messages: Vec) -> Result<()> {
+ let msg_count = messages.len() as u64;
+
+ if messages.is_empty() {
+ return Ok(());
+ }
+
+ let mut attempts = 0;
+ let mut error_map = HashMap::new();
+ let mut fallback_msgs = Vec::new();
+
+ while attempts <= config().sink_max_retry_attempts {
+ let start_time = tokio::time::Instant::now();
+ match self.sink_client.sink_fn(messages.clone()).await {
+ Ok(response) => {
+ debug!("Sink latency - {}ms", start_time.elapsed().as_millis());
+ attempts += 1;
+
+ fallback_msgs.extend(
+ response
+ .results
+ .iter()
+ .filter(|result| result.status == proto::Status::Fallback as i32)
+ .map(|result| {
+ messages
+ .iter()
+ .find(|msg| msg.id == result.id)
+ .unwrap()
+ .clone()
+ })
+ .collect::>(),
+ );
+
+ messages = response
+ .results
+ .iter()
+ .filter(|result| result.status == proto::Status::Failure as i32)
+ .map(|result| {
+ messages
+ .iter()
+ .find(|msg| msg.id == result.id)
+ .unwrap()
+ .clone()
+ })
+ .collect::>();
+
+ if messages.is_empty() {
+ break;
+ } else {
+ error_map.clear();
+ for result in response.results {
+ if result.status == proto::Status::Failure as i32 {
+ *error_map.entry(result.err_msg).or_insert(0) += 1;
+ }
+ }
+
+ warn!(
+ "Retry attempt {} due to retryable error. Errors: {:?}",
+ attempts, error_map
+ );
+ sleep(tokio::time::Duration::from_millis(
+ config().sink_retry_interval_in_ms as u64,
+ ))
+ .await;
+ }
+ }
+ Err(e) => return Err(e),
+ }
+ }
+
+ if !messages.is_empty() {
+ return Err(Error::SinkError(format!(
+ "Failed to sink messages after {} attempts. Errors: {:?}",
+ attempts, error_map
+ )));
+ }
+
+ // If there are fallback messages, write them to the fallback sink
+ if !fallback_msgs.is_empty() {
+ self.handle_fallback_messages(fallback_msgs).await?;
+ }
+
+ forward_metrics()
+ .monovtx_sink_write_total
+ .get_or_create(&self.common_labels)
+ .inc_by(msg_count);
+ Ok(())
+ }
+
+ // Writes the fallback messages to the fallback sink
+ async fn handle_fallback_messages(&mut self, mut fallback_msgs: Vec) -> Result<()> {
+ if self.fallback_client.is_none() {
+ return Err(Error::SinkError(
+ "Response contains fallback messages but no fallback sink is configured"
+ .to_string(),
+ ));
+ }
+
+ let fallback_client = self.fallback_client.as_mut().unwrap();
+ let mut attempts = 0;
+ let mut fallback_error_map = HashMap::new();
+
+ while attempts <= config().sink_max_retry_attempts {
+ let start_time = tokio::time::Instant::now();
+ match fallback_client.sink_fn(fallback_msgs.clone()).await {
+ Ok(fb_response) => {
+ debug!(
+ "Fallback sink latency - {}ms",
+ start_time.elapsed().as_millis()
+ );
+
+ fallback_msgs = fb_response
+ .results
+ .iter()
+ .filter(|result| result.status == proto::Status::Failure as i32)
+ .map(|result| {
+ fallback_msgs
+ .iter()
+ .find(|msg| msg.id == result.id)
+ .unwrap()
+ .clone()
+ })
+ .collect::>();
+
+ // we can't specify fallback response inside fallback sink
+ if fb_response
+ .results
+ .iter()
+ .any(|result| result.status == proto::Status::Fallback as i32)
+ {
+ return Err(Error::SinkError(
+ "Fallback sink can't specify status fallback".to_string(),
+ ));
+ }
+
+ attempts += 1;
+
+ if fallback_msgs.is_empty() {
+ break;
+ } else {
+ fallback_error_map.clear();
+ for result in fb_response.results {
+ if result.status != proto::Status::Success as i32 {
+ *fallback_error_map.entry(result.err_msg).or_insert(0) += 1;
+ }
+ }
+
+ warn!(
+ "Fallback sink retry attempt {} due to retryable error. Errors: {:?}",
+ attempts, fallback_error_map
+ );
+ sleep(tokio::time::Duration::from_millis(
+ config().sink_retry_interval_in_ms as u64,
+ ))
+ .await;
+ }
+ }
+ Err(e) => return Err(e),
+ }
+ }
+
+ if !fallback_msgs.is_empty() {
+ return Err(Error::SinkError(format!(
+ "Failed to write messages to fallback sink after {} attempts. Errors: {:?}",
+ attempts, fallback_error_map
+ )));
+ }
+
+ Ok(())
+ }
+
+ // Acknowledge the messages back to the source
+ async fn acknowledge_messages(&mut self, offsets: Vec) -> Result<()> {
+ let n = offsets.len();
+ let start_time = tokio::time::Instant::now();
+ self.source_client.ack_fn(offsets).await?;
+ debug!("Ack latency - {}ms", start_time.elapsed().as_millis());
+ forward_metrics()
+ .monovtx_ack_total
+ .get_or_create(&self.common_labels)
+ .inc_by(n as u64);
+ Ok(())
+ }
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
- use crate::error::Error;
- use crate::forwarder::Forwarder;
+ use crate::error::Result;
+ use crate::forwarder::ForwarderBuilder;
use crate::sink::{SinkClient, SinkConfig};
use crate::source::{SourceClient, SourceConfig};
use crate::transformer::{TransformerClient, TransformerConfig};
@@ -310,7 +519,6 @@ mod tests {
#[tokio::test]
async fn test_forwarder_source_sink() {
- // Create channels for communication
let (sink_tx, mut sink_rx) = tokio::sync::mpsc::channel(10);
// Start the source server
@@ -396,17 +604,12 @@ mod tests {
.await
.expect("failed to connect to transformer server");
- let mut forwarder = Forwarder::new(
- source_client,
- sink_client,
- Some(transformer_client),
- cln_token.clone(),
- )
- .await
- .expect("failed to create forwarder");
+ let mut forwarder = ForwarderBuilder::new(source_client, sink_client, cln_token.clone())
+ .transformer_client(transformer_client)
+ .build();
let forwarder_handle = tokio::spawn(async move {
- forwarder.run().await.unwrap();
+ forwarder.start().await.unwrap();
});
// Receive messages from the sink
@@ -524,19 +727,18 @@ mod tests {
.await
.expect("failed to connect to sink server");
- let mut forwarder = Forwarder::new(source_client, sink_client, None, cln_token.clone())
- .await
- .expect("failed to create forwarder");
+ let mut forwarder =
+ ForwarderBuilder::new(source_client, sink_client, cln_token.clone()).build();
let forwarder_handle = tokio::spawn(async move {
- forwarder.run().await?;
+ forwarder.start().await?;
Ok(())
});
// Set a timeout for the forwarder
let timeout_duration = tokio::time::Duration::from_secs(1);
let result = tokio::time::timeout(timeout_duration, forwarder_handle).await;
- let result: Result<(), Error> = result.expect("forwarder_handle timed out").unwrap();
+ let result: Result<()> = result.expect("forwarder_handle timed out").unwrap();
assert!(result.is_err());
// stop the servers
@@ -554,4 +756,150 @@ mod tests {
.await
.expect("failed to join sink server task");
}
+
+ // Sink that returns status fallback
+ struct FallbackSender {}
+
+ #[tonic::async_trait]
+ impl sink::Sinker for FallbackSender {
+ async fn sink(
+ &self,
+ mut input: tokio::sync::mpsc::Receiver,
+ ) -> Vec {
+ let mut responses = vec![];
+ while let Some(datum) = input.recv().await {
+ responses.append(&mut vec![sink::Response::fallback(datum.id)]);
+ }
+ responses
+ }
+ }
+
+ #[tokio::test]
+ async fn test_fb_sink() {
+ let (sink_tx, mut sink_rx) = tokio::sync::mpsc::channel(10);
+
+ // Start the source server
+ let (source_shutdown_tx, source_shutdown_rx) = tokio::sync::oneshot::channel();
+ let tmp_dir = tempfile::TempDir::new().unwrap();
+ let source_sock_file = tmp_dir.path().join("source.sock");
+ let server_info_file = tmp_dir.path().join("source-server-info");
+
+ let server_info = server_info_file.clone();
+ let source_socket = source_sock_file.clone();
+ let source_server_handle = tokio::spawn(async move {
+ source::Server::new(SimpleSource::new())
+ .with_socket_file(source_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(source_shutdown_rx)
+ .await
+ .unwrap();
+ });
+ let source_config = SourceConfig {
+ socket_path: source_sock_file.to_str().unwrap().to_string(),
+ server_info_file: server_info_file.to_str().unwrap().to_string(),
+ max_message_size: 4 * 1024 * 1024,
+ };
+
+ // Start the primary sink server (which returns status fallback)
+ let (sink_shutdown_tx, sink_shutdown_rx) = tokio::sync::oneshot::channel();
+ let sink_tmp_dir = tempfile::TempDir::new().unwrap();
+ let sink_sock_file = sink_tmp_dir.path().join("sink.sock");
+ let server_info_file = sink_tmp_dir.path().join("sink-server-info");
+
+ let server_info = server_info_file.clone();
+ let sink_socket = sink_sock_file.clone();
+ let sink_server_handle = tokio::spawn(async move {
+ sink::Server::new(FallbackSender {})
+ .with_socket_file(sink_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(sink_shutdown_rx)
+ .await
+ .unwrap();
+ });
+ let sink_config = SinkConfig {
+ socket_path: sink_sock_file.to_str().unwrap().to_string(),
+ server_info_file: server_info_file.to_str().unwrap().to_string(),
+ max_message_size: 4 * 1024 * 1024,
+ };
+
+ // Start the fb sink server
+ let (fb_sink_shutdown_tx, fb_sink_shutdown_rx) = tokio::sync::oneshot::channel();
+ let fb_sink_tmp_dir = tempfile::TempDir::new().unwrap();
+ let fb_sink_sock_file = fb_sink_tmp_dir.path().join("fb-sink.sock");
+ let server_info_file = fb_sink_tmp_dir.path().join("fb-sinker-server-info");
+
+ let server_info = server_info_file.clone();
+ let fb_sink_socket = fb_sink_sock_file.clone();
+ let fb_sink_server_handle = tokio::spawn(async move {
+ sink::Server::new(InMemorySink::new(sink_tx))
+ .with_socket_file(fb_sink_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(fb_sink_shutdown_rx)
+ .await
+ .unwrap();
+ });
+ let fb_sink_config = SinkConfig {
+ socket_path: fb_sink_sock_file.to_str().unwrap().to_string(),
+ server_info_file: server_info_file.to_str().unwrap().to_string(),
+ max_message_size: 4 * 1024 * 1024,
+ };
+
+ // Wait for the servers to start
+ tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
+
+ let cln_token = CancellationToken::new();
+
+ let source_client = SourceClient::connect(source_config)
+ .await
+ .expect("failed to connect to source server");
+
+ let sink_client = SinkClient::connect(sink_config)
+ .await
+ .expect("failed to connect to sink server");
+
+ let fb_sink_client = SinkClient::connect(fb_sink_config)
+ .await
+ .expect("failed to connect to fb sink server");
+
+ let mut forwarder = ForwarderBuilder::new(source_client, sink_client, cln_token.clone())
+ .fb_sink_client(fb_sink_client)
+ .build();
+
+ let forwarder_handle = tokio::spawn(async move {
+ forwarder.start().await.unwrap();
+ });
+
+ // We should receive the message in the fallback sink, since the primary sink returns status fallback
+ let received_message = sink_rx.recv().await.unwrap();
+ assert_eq!(received_message.value, "test-message".as_bytes());
+ assert_eq!(received_message.keys, vec!["test-key".to_string()]);
+
+ // stop the forwarder
+ cln_token.cancel();
+ forwarder_handle
+ .await
+ .expect("failed to join forwarder task");
+
+ // stop the servers
+ source_shutdown_tx
+ .send(())
+ .expect("failed to send shutdown signal");
+ source_server_handle
+ .await
+ .expect("failed to join source server task");
+
+ sink_shutdown_tx
+ .send(())
+ .expect("failed to send shutdown signal");
+ sink_server_handle
+ .await
+ .expect("failed to join sink server task");
+
+ fb_sink_shutdown_tx
+ .send(())
+ .expect("failed to send shutdown signal");
+ fb_sink_server_handle
+ .await
+ .expect("failed to join fb sink server task");
+ }
}
diff --git a/rust/monovertex/src/lib.rs b/rust/monovertex/src/lib.rs
index f3864f7531..614e7279aa 100644
--- a/rust/monovertex/src/lib.rs
+++ b/rust/monovertex/src/lib.rs
@@ -1,9 +1,9 @@
pub(crate) use self::error::Result;
use crate::config::config;
pub(crate) use crate::error::Error;
-use crate::forwarder::Forwarder;
+use crate::forwarder::ForwarderBuilder;
use crate::metrics::{start_metrics_https_server, LagReaderBuilder, MetricsState};
-use crate::sink::{SinkClient, SinkConfig};
+use crate::sink::{SinkClient, SinkConfig, FB_SINK_SERVER_INFO_FILE, FB_SINK_SOCKET};
use crate::source::{SourceClient, SourceConfig};
use crate::transformer::{TransformerClient, TransformerConfig};
use std::net::SocketAddr;
@@ -72,6 +72,16 @@ pub async fn mono_vertex() {
None
};
+ let fb_sink_config = if config().is_fallback_enabled {
+ Some(SinkConfig {
+ max_message_size: config().grpc_max_message_size,
+ socket_path: FB_SINK_SOCKET.to_string(),
+ server_info_file: FB_SINK_SERVER_INFO_FILE.to_string(),
+ })
+ } else {
+ None
+ };
+
let cln_token = CancellationToken::new();
let shutdown_cln_token = cln_token.clone();
// wait for SIG{INT,TERM} and invoke cancellation token.
@@ -82,7 +92,15 @@ pub async fn mono_vertex() {
});
// Run the forwarder with cancellation token.
- if let Err(e) = init(source_config, sink_config, transformer_config, cln_token).await {
+ if let Err(e) = init(
+ source_config,
+ sink_config,
+ transformer_config,
+ fb_sink_config,
+ cln_token,
+ )
+ .await
+ {
error!("Application error: {:?}", e);
// abort the task since we have an error
@@ -122,6 +140,7 @@ pub async fn init(
source_config: SourceConfig,
sink_config: SinkConfig,
transformer_config: Option,
+ fb_sink_config: Option,
cln_token: CancellationToken,
) -> Result<()> {
server_info::check_for_server_compatibility(&source_config.server_info_file, cln_token.clone())
@@ -153,11 +172,24 @@ pub async fn init(
None
};
+ let mut fb_sink_client = if let Some(config) = fb_sink_config {
+ server_info::check_for_server_compatibility(&config.server_info_file, cln_token.clone())
+ .await
+ .map_err(|e| {
+ warn!("Error waiting for fallback sink server info file: {:?}", e);
+ Error::ForwarderError("Error waiting for server info file".to_string())
+ })?;
+ Some(SinkClient::connect(config).await?)
+ } else {
+ None
+ };
+
// readiness check for all the ud containers
wait_until_ready(
&mut source_client,
&mut sink_client,
&mut transformer_client,
+ &mut fb_sink_client,
)
.await?;
@@ -173,6 +205,7 @@ pub async fn init(
source_client: source_client.clone(),
sink_client: sink_client.clone(),
transformer_client: transformer_client.clone(),
+ fb_sink_client: fb_sink_client.clone(),
};
tokio::spawn(async move {
if let Err(e) = start_metrics_https_server(metrics_addr, metrics_state).await {
@@ -191,10 +224,20 @@ pub async fn init(
.build();
lag_reader.start().await;
- let mut forwarder =
- Forwarder::new(source_client, sink_client, transformer_client, cln_token).await?;
+ // build the forwarder
+ let mut forwarder_builder = ForwarderBuilder::new(source_client, sink_client, cln_token);
+ if let Some(transformer_client) = transformer_client {
+ forwarder_builder = forwarder_builder.transformer_client(transformer_client);
+ }
- forwarder.run().await?;
+ if let Some(fb_sink_client) = fb_sink_client {
+ forwarder_builder = forwarder_builder.fb_sink_client(fb_sink_client);
+ }
+
+ let mut forwarder = forwarder_builder.build();
+
+ // start the forwarder
+ forwarder.start().await?;
info!("Forwarder stopped gracefully");
Ok(())
@@ -204,6 +247,7 @@ async fn wait_until_ready(
source_client: &mut SourceClient,
sink_client: &mut SinkClient,
transformer_client: &mut Option,
+ fb_sink_client: &mut Option,
) -> Result<()> {
loop {
let source_ready = source_client.is_ready().await;
@@ -226,7 +270,17 @@ async fn wait_until_ready(
true
};
- if source_ready && sink_ready && transformer_ready {
+ let fb_sink_ready = if let Some(client) = fb_sink_client {
+ let ready = client.is_ready().await;
+ if !ready {
+ info!("Fallback Sink is not ready, waiting...");
+ }
+ ready
+ } else {
+ true
+ };
+
+ if source_ready && sink_ready && transformer_ready && fb_sink_ready {
break;
}
@@ -330,7 +384,8 @@ mod tests {
let forwarder_cln_token = cln_token.clone();
let forwarder_handle = tokio::spawn(async move {
- let result = super::init(source_config, sink_config, None, forwarder_cln_token).await;
+ let result =
+ super::init(source_config, sink_config, None, None, forwarder_cln_token).await;
assert!(result.is_ok());
});
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index cd792d9693..789ba47a3b 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -51,6 +51,7 @@ pub(crate) struct MetricsState {
pub source_client: SourceClient,
pub sink_client: SinkClient,
pub transformer_client: Option,
+ pub fb_sink_client: Option,
}
/// The global register of all metrics.
@@ -190,7 +191,7 @@ pub(crate) fn forward_metrics_labels() -> &'static Vec<(String, String)> {
pub async fn metrics_handler() -> impl IntoResponse {
let state = global_registry().registry.lock();
let mut buffer = String::new();
- encode(&mut buffer, &*state).unwrap();
+ encode(&mut buffer, &state).unwrap();
debug!("Exposing Metrics: {:?}", buffer);
Response::builder()
.status(StatusCode::OK)
@@ -204,18 +205,12 @@ pub async fn metrics_handler() -> impl IntoResponse {
#[allow(dead_code)]
pub(crate) async fn start_metrics_http_server(
addr: A,
- source_client: SourceClient,
- sink_client: SinkClient,
- transformer_client: Option,
+ metrics_state: MetricsState,
) -> crate::Result<()>
where
A: ToSocketAddrs + std::fmt::Debug,
{
- let metrics_app = metrics_router(MetricsState {
- source_client,
- sink_client,
- transformer_client,
- });
+ let metrics_app = metrics_router(metrics_state);
let listener = TcpListener::bind(&addr)
.await
@@ -273,13 +268,22 @@ async fn readyz() -> impl IntoResponse {
async fn sidecar_livez(State(mut state): State) -> impl IntoResponse {
if !state.source_client.is_ready().await {
+ error!("Source client is not available");
return StatusCode::SERVICE_UNAVAILABLE;
}
if !state.sink_client.is_ready().await {
+ error!("Sink client is not available");
return StatusCode::SERVICE_UNAVAILABLE;
}
if let Some(mut transformer_client) = state.transformer_client {
if !transformer_client.is_ready().await {
+ error!("Transformer client is not available");
+ return StatusCode::SERVICE_UNAVAILABLE;
+ }
+ }
+ if let Some(mut fb_sink_client) = state.fb_sink_client {
+ if !fb_sink_client.is_ready().await {
+ error!("Fallback sink client is not available");
return StatusCode::SERVICE_UNAVAILABLE;
}
}
diff --git a/rust/monovertex/src/sink.rs b/rust/monovertex/src/sink.rs
index 5f2b8a74be..4cedaef49e 100644
--- a/rust/monovertex/src/sink.rs
+++ b/rust/monovertex/src/sink.rs
@@ -13,7 +13,9 @@ pub mod proto {
const RECONNECT_INTERVAL: u64 = 1000;
const MAX_RECONNECT_ATTEMPTS: usize = 5;
const SINK_SOCKET: &str = "/var/run/numaflow/sink.sock";
+pub(crate) const FB_SINK_SOCKET: &str = "/var/run/numaflow/fb-sink.sock";
const SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/sinker-server-info";
+pub(crate) const FB_SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/fb-sinker-server-info";
/// SinkConfig is the configuration for the sink server.
#[derive(Debug, Clone)]
@@ -58,11 +60,15 @@ impl SinkClient {
}
pub(crate) async fn sink_fn(&mut self, messages: Vec) -> Result {
+ let (tx, rx) = tokio::sync::mpsc::channel(if messages.is_empty() {
+ 1
+ } else {
+ messages.len()
+ });
+
let requests: Vec =
messages.into_iter().map(|message| message.into()).collect();
- let (tx, rx) = tokio::sync::mpsc::channel(1);
-
tokio::spawn(async move {
for request in requests {
if tx.send(request).await.is_err() {
From b54a4cd3e555ee3e29c603f7f2ea1c15ccd88f7a Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Mon, 19 Aug 2024 14:23:31 -0400
Subject: [PATCH 008/188] feat: add health for monovertex (#1954)
Signed-off-by: Sidhant Kohli
---
pkg/apis/numaflow/v1alpha1/const.go | 6 +-
.../numaflow/v1alpha1/mono_vertex_types.go | 7 +
.../v1alpha1/mono_vertex_types_test.go | 85 +++++++
pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go | 222 ++++++++++++++---
pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go | 69 ++++++
pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto | 15 ++
.../proto/mvtxdaemon/mvtxdaemon_grpc.pb.go | 37 +++
pkg/mvtxdaemon/client/grpc_client.go | 8 +
pkg/mvtxdaemon/client/interface.go | 1 +
pkg/mvtxdaemon/client/restful_client.go | 14 ++
pkg/mvtxdaemon/server/daemon_server.go | 12 +-
.../server/service/health_status.go | 226 ++++++++++++++++++
.../server/service/health_status_test.go | 145 +++++++++++
pkg/mvtxdaemon/server/service/mvtx_service.go | 83 ++++++-
.../health-status-code/mono_vtx_code_map.go | 71 ++++++
.../mono_vtx_code_map_test.go | 50 ++++
.../{code_map.go => pipeline_code_map.go} | 14 --
..._map_test.go => pipeline_code_map_test.go} | 0
pkg/shared/health-status-code/utils.go | 31 +++
server/apis/v1/handler.go | 37 ++-
server/apis/v1/health.go | 95 +++++++-
server/cmd/server/start.go | 1 +
server/cmd/server/start_test.go | 4 +-
server/routes/routes.go | 2 +
24 files changed, 1161 insertions(+), 74 deletions(-)
create mode 100644 pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
create mode 100644 pkg/mvtxdaemon/server/service/health_status.go
create mode 100644 pkg/mvtxdaemon/server/service/health_status_test.go
create mode 100644 pkg/shared/health-status-code/mono_vtx_code_map.go
create mode 100644 pkg/shared/health-status-code/mono_vtx_code_map_test.go
rename pkg/shared/health-status-code/{code_map.go => pipeline_code_map.go} (89%)
rename pkg/shared/health-status-code/{code_map_test.go => pipeline_code_map_test.go} (100%)
create mode 100644 pkg/shared/health-status-code/utils.go
diff --git a/pkg/apis/numaflow/v1alpha1/const.go b/pkg/apis/numaflow/v1alpha1/const.go
index a1a8e518fa..177e6f73a1 100644
--- a/pkg/apis/numaflow/v1alpha1/const.go
+++ b/pkg/apis/numaflow/v1alpha1/const.go
@@ -230,7 +230,11 @@ const (
// MonoVertex health status
// TODO - more statuses to be added
- MonoVertexStatusHealthy = "healthy"
+ MonoVertexStatusHealthy = "healthy"
+ MonoVertexStatusUnhealthy = "unhealthy"
+ MonoVertexStatusUnknown = "unknown"
+ MonoVertexStatusCritical = "critical"
+ MonoVertexStatusWarning = "warning"
// Callback annotation keys
CallbackEnabledKey = "numaflow.numaproj.io/callback"
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index 6f02509563..4d835e9c2c 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -532,10 +532,17 @@ func (mvs *MonoVertexStatus) MarkPhaseRunning() {
}
// IsHealthy indicates whether the MonoVertex is in healthy status
+// It returns false if any issues exists
+// True indicates that the MonoVertex is healthy
+// TODO: Add support for paused whenever added in MonoVtx?
func (mvs *MonoVertexStatus) IsHealthy() bool {
+ // check for the phase field first
switch mvs.Phase {
+ // Directly return an error if the phase is failed
case MonoVertexPhaseFailed:
return false
+ // Check if the MonoVertex is ready if the phase is running,
+ // We check if all the required conditions are true for it to be healthy
case MonoVertexPhaseRunning:
return mvs.IsReady()
default:
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
new file mode 100644
index 0000000000..2f2fee9a39
--- /dev/null
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
@@ -0,0 +1,85 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "testing"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestMonoVertex_GetDaemonServiceObj(t *testing.T) {
+ mv := MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "default",
+ },
+ }
+
+ svc := mv.GetDaemonServiceObj()
+ if svc.Name != "test-mv-daemon-svc" {
+ t.Error("GetDaemonServiceObj generated incorrect service name")
+ }
+ if svc.Namespace != "default" {
+ t.Error("GetDaemonServiceObj generated incorrect namespace")
+ }
+}
+
+func TestMonoVertex_MarkPhaseRunning(t *testing.T) {
+ mvs := MonoVertexStatus{}
+ mvs.MarkPhaseRunning()
+
+ if mvs.Phase != MonoVertexPhaseRunning {
+ t.Errorf("MarkPhaseRunning did not set the Phase to Running, got %v", mvs.Phase)
+ }
+}
+
+func TestMonoVertex_IsHealthy(t *testing.T) {
+ mvs := MonoVertexStatus{}
+
+ mvs.InitConditions()
+ mvs.MarkPhaseRunning()
+ mvs.MarkDeployed()
+ mvs.MarkDaemonHealthy()
+ mvs.MarkPodHealthy("AllGood", "All pod are up and running")
+
+ isHealthy := mvs.IsHealthy()
+ if !isHealthy {
+ t.Error("IsHealthy should return true when everything is healthy")
+ }
+
+ mvs.MarkPodNotHealthy("PodIssue", "One of the pods is down")
+ isHealthy = mvs.IsHealthy()
+ if isHealthy {
+ t.Error("IsHealthy should return false when pod condition is not healthy")
+ }
+}
+
+func TestMonoVertexStatus_MarkDeployFailed(t *testing.T) {
+ mvs := MonoVertexStatus{}
+ mvs.MarkDeployFailed("DeployError", "Deployment failed due to resource constraints")
+
+ if mvs.Phase != MonoVertexPhaseFailed {
+ t.Errorf("MarkDeployFailed should set the Phase to Failed, got %v", mvs.Phase)
+ }
+ if mvs.Reason != "DeployError" {
+ t.Errorf("MarkDeployFailed should set the Reason to 'DeployError', got %s", mvs.Reason)
+ }
+ if mvs.Message != "Deployment failed due to resource constraints" {
+ t.Errorf("MarkDeployFailed should set the Message correctly, got %s", mvs.Message)
+ }
+}
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go
index 10d3e2350c..e33c93aa7a 100644
--- a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.go
@@ -151,6 +151,117 @@ func (x *GetMonoVertexMetricsResponse) GetMetrics() *MonoVertexMetrics {
return nil
}
+// MonoVertexStatus is used to provide information about the mono vertex status.
+type MonoVertexStatus struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
+ Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
+ Code string `protobuf:"bytes,3,opt,name=code,proto3" json:"code,omitempty"`
+}
+
+func (x *MonoVertexStatus) Reset() {
+ *x = MonoVertexStatus{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *MonoVertexStatus) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MonoVertexStatus) ProtoMessage() {}
+
+func (x *MonoVertexStatus) ProtoReflect() protoreflect.Message {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use MonoVertexStatus.ProtoReflect.Descriptor instead.
+func (*MonoVertexStatus) Descriptor() ([]byte, []int) {
+ return file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *MonoVertexStatus) GetStatus() string {
+ if x != nil {
+ return x.Status
+ }
+ return ""
+}
+
+func (x *MonoVertexStatus) GetMessage() string {
+ if x != nil {
+ return x.Message
+ }
+ return ""
+}
+
+func (x *MonoVertexStatus) GetCode() string {
+ if x != nil {
+ return x.Code
+ }
+ return ""
+}
+
+type GetMonoVertexStatusResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Status *MonoVertexStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
+}
+
+func (x *GetMonoVertexStatusResponse) Reset() {
+ *x = GetMonoVertexStatusResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *GetMonoVertexStatusResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetMonoVertexStatusResponse) ProtoMessage() {}
+
+func (x *GetMonoVertexStatusResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetMonoVertexStatusResponse.ProtoReflect.Descriptor instead.
+func (*GetMonoVertexStatusResponse) Descriptor() ([]byte, []int) {
+ return file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *GetMonoVertexStatusResponse) GetStatus() *MonoVertexStatus {
+ if x != nil {
+ return x.Status
+ }
+ return nil
+}
+
var File_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto protoreflect.FileDescriptor
var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDesc = []byte{
@@ -194,20 +305,38 @@ var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDesc = []byte{
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x6f, 0x6e, 0x6f, 0x56,
0x65, 0x72, 0x74, 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07, 0x6d, 0x65,
- 0x74, 0x72, 0x69, 0x63, 0x73, 0x32, 0x8c, 0x01, 0x0a, 0x17, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65,
- 0x72, 0x74, 0x65, 0x78, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
- 0x65, 0x12, 0x71, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74,
- 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
- 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
- 0x79, 0x1a, 0x28, 0x2e, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47,
- 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72,
- 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4,
- 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74,
- 0x72, 0x69, 0x63, 0x73, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
- 0x6f, 0x6d, 0x2f, 0x6e, 0x75, 0x6d, 0x61, 0x70, 0x72, 0x6f, 0x6a, 0x2f, 0x6e, 0x75, 0x6d, 0x61,
- 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x58, 0x0a, 0x10, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72,
+ 0x74, 0x65, 0x78, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63,
+ 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22,
+ 0x53, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
+ 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34,
+ 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
+ 0x2e, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x6f, 0x6e, 0x6f,
+ 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x32, 0xfc, 0x01, 0x0a, 0x17, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72,
+ 0x74, 0x65, 0x78, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x12, 0x71, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65,
+ 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
+ 0x1a, 0x28, 0x2e, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65,
+ 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x4d, 0x65, 0x74, 0x72, 0x69,
+ 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93,
+ 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x72,
+ 0x69, 0x63, 0x73, 0x12, 0x6e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65,
+ 0x72, 0x74, 0x65, 0x78, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
+ 0x74, 0x79, 0x1a, 0x27, 0x2e, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
+ 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x53, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4,
+ 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x6e, 0x75, 0x6d, 0x61, 0x70, 0x72, 0x6f, 0x6a, 0x2f, 0x6e, 0x75, 0x6d, 0x61, 0x66,
+ 0x6c, 0x6f, 0x77, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x2f, 0x6d, 0x76, 0x74, 0x78, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -222,29 +351,34 @@ func file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescGZIP() []byte {
return file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDescData
}
-var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_goTypes = []any{
(*MonoVertexMetrics)(nil), // 0: mvtxdaemon.MonoVertexMetrics
(*GetMonoVertexMetricsResponse)(nil), // 1: mvtxdaemon.GetMonoVertexMetricsResponse
- nil, // 2: mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry
- nil, // 3: mvtxdaemon.MonoVertexMetrics.PendingsEntry
- (*wrapperspb.DoubleValue)(nil), // 4: google.protobuf.DoubleValue
- (*wrapperspb.Int64Value)(nil), // 5: google.protobuf.Int64Value
- (*emptypb.Empty)(nil), // 6: google.protobuf.Empty
+ (*MonoVertexStatus)(nil), // 2: mvtxdaemon.MonoVertexStatus
+ (*GetMonoVertexStatusResponse)(nil), // 3: mvtxdaemon.GetMonoVertexStatusResponse
+ nil, // 4: mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry
+ nil, // 5: mvtxdaemon.MonoVertexMetrics.PendingsEntry
+ (*wrapperspb.DoubleValue)(nil), // 6: google.protobuf.DoubleValue
+ (*wrapperspb.Int64Value)(nil), // 7: google.protobuf.Int64Value
+ (*emptypb.Empty)(nil), // 8: google.protobuf.Empty
}
var file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_depIdxs = []int32{
- 2, // 0: mvtxdaemon.MonoVertexMetrics.processingRates:type_name -> mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry
- 3, // 1: mvtxdaemon.MonoVertexMetrics.pendings:type_name -> mvtxdaemon.MonoVertexMetrics.PendingsEntry
+ 4, // 0: mvtxdaemon.MonoVertexMetrics.processingRates:type_name -> mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry
+ 5, // 1: mvtxdaemon.MonoVertexMetrics.pendings:type_name -> mvtxdaemon.MonoVertexMetrics.PendingsEntry
0, // 2: mvtxdaemon.GetMonoVertexMetricsResponse.metrics:type_name -> mvtxdaemon.MonoVertexMetrics
- 4, // 3: mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry.value:type_name -> google.protobuf.DoubleValue
- 5, // 4: mvtxdaemon.MonoVertexMetrics.PendingsEntry.value:type_name -> google.protobuf.Int64Value
- 6, // 5: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexMetrics:input_type -> google.protobuf.Empty
- 1, // 6: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexMetrics:output_type -> mvtxdaemon.GetMonoVertexMetricsResponse
- 6, // [6:7] is the sub-list for method output_type
- 5, // [5:6] is the sub-list for method input_type
- 5, // [5:5] is the sub-list for extension type_name
- 5, // [5:5] is the sub-list for extension extendee
- 0, // [0:5] is the sub-list for field type_name
+ 2, // 3: mvtxdaemon.GetMonoVertexStatusResponse.status:type_name -> mvtxdaemon.MonoVertexStatus
+ 6, // 4: mvtxdaemon.MonoVertexMetrics.ProcessingRatesEntry.value:type_name -> google.protobuf.DoubleValue
+ 7, // 5: mvtxdaemon.MonoVertexMetrics.PendingsEntry.value:type_name -> google.protobuf.Int64Value
+ 8, // 6: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexMetrics:input_type -> google.protobuf.Empty
+ 8, // 7: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexStatus:input_type -> google.protobuf.Empty
+ 1, // 8: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexMetrics:output_type -> mvtxdaemon.GetMonoVertexMetricsResponse
+ 3, // 9: mvtxdaemon.MonoVertexDaemonService.GetMonoVertexStatus:output_type -> mvtxdaemon.GetMonoVertexStatusResponse
+ 8, // [8:10] is the sub-list for method output_type
+ 6, // [6:8] is the sub-list for method input_type
+ 6, // [6:6] is the sub-list for extension type_name
+ 6, // [6:6] is the sub-list for extension extendee
+ 0, // [0:6] is the sub-list for field type_name
}
func init() { file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_init() }
@@ -277,6 +411,30 @@ func file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_init() {
return nil
}
}
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[2].Exporter = func(v any, i int) any {
+ switch v := v.(*MonoVertexStatus); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_msgTypes[3].Exporter = func(v any, i int) any {
+ switch v := v.(*GetMonoVertexStatusResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -284,7 +442,7 @@ func file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pkg_apis_proto_mvtxdaemon_mvtxdaemon_proto_rawDesc,
NumEnums: 0,
- NumMessages: 4,
+ NumMessages: 6,
NumExtensions: 0,
NumServices: 1,
},
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go
index 97c8075676..80741d34f6 100644
--- a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.pb.gw.go
@@ -50,6 +50,24 @@ func local_request_MonoVertexDaemonService_GetMonoVertexMetrics_0(ctx context.Co
}
+func request_MonoVertexDaemonService_GetMonoVertexStatus_0(ctx context.Context, marshaler runtime.Marshaler, client MonoVertexDaemonServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq emptypb.Empty
+ var metadata runtime.ServerMetadata
+
+ msg, err := client.GetMonoVertexStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_MonoVertexDaemonService_GetMonoVertexStatus_0(ctx context.Context, marshaler runtime.Marshaler, server MonoVertexDaemonServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq emptypb.Empty
+ var metadata runtime.ServerMetadata
+
+ msg, err := server.GetMonoVertexStatus(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
// RegisterMonoVertexDaemonServiceHandlerServer registers the http handlers for service MonoVertexDaemonService to "mux".
// UnaryRPC :call MonoVertexDaemonServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -81,6 +99,31 @@ func RegisterMonoVertexDaemonServiceHandlerServer(ctx context.Context, mux *runt
})
+ mux.Handle("GET", pattern_MonoVertexDaemonService_GetMonoVertexStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/mvtxdaemon.MonoVertexDaemonService/GetMonoVertexStatus", runtime.WithHTTPPathPattern("/api/v1/status"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_MonoVertexDaemonService_GetMonoVertexStatus_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_MonoVertexDaemonService_GetMonoVertexStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
return nil
}
@@ -144,13 +187,39 @@ func RegisterMonoVertexDaemonServiceHandlerClient(ctx context.Context, mux *runt
})
+ mux.Handle("GET", pattern_MonoVertexDaemonService_GetMonoVertexStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/mvtxdaemon.MonoVertexDaemonService/GetMonoVertexStatus", runtime.WithHTTPPathPattern("/api/v1/status"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_MonoVertexDaemonService_GetMonoVertexStatus_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_MonoVertexDaemonService_GetMonoVertexStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
return nil
}
var (
pattern_MonoVertexDaemonService_GetMonoVertexMetrics_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "metrics"}, ""))
+
+ pattern_MonoVertexDaemonService_GetMonoVertexStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "status"}, ""))
)
var (
forward_MonoVertexDaemonService_GetMonoVertexMetrics_0 = runtime.ForwardResponseMessage
+
+ forward_MonoVertexDaemonService_GetMonoVertexStatus_0 = runtime.ForwardResponseMessage
)
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
index 512b5bf515..21bc215c50 100644
--- a/pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto
@@ -37,6 +37,17 @@ message GetMonoVertexMetricsResponse {
MonoVertexMetrics metrics = 1;
}
+// MonoVertexStatus is used to provide information about the mono vertex status.
+message MonoVertexStatus {
+ string status = 1;
+ string message = 2;
+ string code = 3;
+}
+
+message GetMonoVertexStatusResponse {
+ MonoVertexStatus status = 1;
+}
+
// MonoVertexDaemonService is a grpc service that is used to provide APIs for giving any MonoVertex information.
service MonoVertexDaemonService {
@@ -44,4 +55,8 @@ service MonoVertexDaemonService {
option (google.api.http).get = "/api/v1/metrics";
};
+ rpc GetMonoVertexStatus (google.protobuf.Empty) returns (GetMonoVertexStatusResponse) {
+ option (google.api.http).get = "/api/v1/status";
+ };
+
}
\ No newline at end of file
diff --git a/pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go b/pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go
index 1dd50188d7..33f0b26d6b 100644
--- a/pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go
+++ b/pkg/apis/proto/mvtxdaemon/mvtxdaemon_grpc.pb.go
@@ -36,6 +36,7 @@ const _ = grpc.SupportPackageIsVersion7
const (
MonoVertexDaemonService_GetMonoVertexMetrics_FullMethodName = "/mvtxdaemon.MonoVertexDaemonService/GetMonoVertexMetrics"
+ MonoVertexDaemonService_GetMonoVertexStatus_FullMethodName = "/mvtxdaemon.MonoVertexDaemonService/GetMonoVertexStatus"
)
// MonoVertexDaemonServiceClient is the client API for MonoVertexDaemonService service.
@@ -43,6 +44,7 @@ const (
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MonoVertexDaemonServiceClient interface {
GetMonoVertexMetrics(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetMonoVertexMetricsResponse, error)
+ GetMonoVertexStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetMonoVertexStatusResponse, error)
}
type monoVertexDaemonServiceClient struct {
@@ -62,11 +64,21 @@ func (c *monoVertexDaemonServiceClient) GetMonoVertexMetrics(ctx context.Context
return out, nil
}
+func (c *monoVertexDaemonServiceClient) GetMonoVertexStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetMonoVertexStatusResponse, error) {
+ out := new(GetMonoVertexStatusResponse)
+ err := c.cc.Invoke(ctx, MonoVertexDaemonService_GetMonoVertexStatus_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
// MonoVertexDaemonServiceServer is the server API for MonoVertexDaemonService service.
// All implementations must embed UnimplementedMonoVertexDaemonServiceServer
// for forward compatibility
type MonoVertexDaemonServiceServer interface {
GetMonoVertexMetrics(context.Context, *emptypb.Empty) (*GetMonoVertexMetricsResponse, error)
+ GetMonoVertexStatus(context.Context, *emptypb.Empty) (*GetMonoVertexStatusResponse, error)
mustEmbedUnimplementedMonoVertexDaemonServiceServer()
}
@@ -77,6 +89,9 @@ type UnimplementedMonoVertexDaemonServiceServer struct {
func (UnimplementedMonoVertexDaemonServiceServer) GetMonoVertexMetrics(context.Context, *emptypb.Empty) (*GetMonoVertexMetricsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMonoVertexMetrics not implemented")
}
+func (UnimplementedMonoVertexDaemonServiceServer) GetMonoVertexStatus(context.Context, *emptypb.Empty) (*GetMonoVertexStatusResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GetMonoVertexStatus not implemented")
+}
func (UnimplementedMonoVertexDaemonServiceServer) mustEmbedUnimplementedMonoVertexDaemonServiceServer() {
}
@@ -109,6 +124,24 @@ func _MonoVertexDaemonService_GetMonoVertexMetrics_Handler(srv interface{}, ctx
return interceptor(ctx, in, info, handler)
}
+func _MonoVertexDaemonService_GetMonoVertexStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(emptypb.Empty)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MonoVertexDaemonServiceServer).GetMonoVertexStatus(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: MonoVertexDaemonService_GetMonoVertexStatus_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MonoVertexDaemonServiceServer).GetMonoVertexStatus(ctx, req.(*emptypb.Empty))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
// MonoVertexDaemonService_ServiceDesc is the grpc.ServiceDesc for MonoVertexDaemonService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -120,6 +153,10 @@ var MonoVertexDaemonService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetMonoVertexMetrics",
Handler: _MonoVertexDaemonService_GetMonoVertexMetrics_Handler,
},
+ {
+ MethodName: "GetMonoVertexStatus",
+ Handler: _MonoVertexDaemonService_GetMonoVertexStatus_Handler,
+ },
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/apis/proto/mvtxdaemon/mvtxdaemon.proto",
diff --git a/pkg/mvtxdaemon/client/grpc_client.go b/pkg/mvtxdaemon/client/grpc_client.go
index c7fb80d6fc..cd6fdbb455 100644
--- a/pkg/mvtxdaemon/client/grpc_client.go
+++ b/pkg/mvtxdaemon/client/grpc_client.go
@@ -54,6 +54,14 @@ func (dc *grpcClient) GetMonoVertexMetrics(ctx context.Context) (*mvtxdaemon.Mon
}
}
+func (dc *grpcClient) GetMonoVertexStatus(ctx context.Context) (*mvtxdaemon.MonoVertexStatus, error) {
+ if rspn, err := dc.client.GetMonoVertexStatus(ctx, &emptypb.Empty{}); err != nil {
+ return nil, err
+ } else {
+ return rspn.Status, nil
+ }
+}
+
// Close function closes the gRPC connection, it has to be called after a daemon client has finished all its jobs.
func (dc *grpcClient) Close() error {
if dc.conn != nil {
diff --git a/pkg/mvtxdaemon/client/interface.go b/pkg/mvtxdaemon/client/interface.go
index 71a1a4aeaf..25a7ca1d05 100644
--- a/pkg/mvtxdaemon/client/interface.go
+++ b/pkg/mvtxdaemon/client/interface.go
@@ -26,4 +26,5 @@ import (
type MonoVertexDaemonClient interface {
io.Closer
GetMonoVertexMetrics(ctx context.Context) (*mvtxdaemon.MonoVertexMetrics, error)
+ GetMonoVertexStatus(ctx context.Context) (*mvtxdaemon.MonoVertexStatus, error)
}
diff --git a/pkg/mvtxdaemon/client/restful_client.go b/pkg/mvtxdaemon/client/restful_client.go
index 7409e19734..0e08e6f8a8 100644
--- a/pkg/mvtxdaemon/client/restful_client.go
+++ b/pkg/mvtxdaemon/client/restful_client.go
@@ -89,3 +89,17 @@ func (rc *restfulClient) GetMonoVertexMetrics(ctx context.Context) (*mvtxdaemon.
return res.Metrics, nil
}
}
+
+func (rc *restfulClient) GetMonoVertexStatus(ctx context.Context) (*mvtxdaemon.MonoVertexStatus, error) {
+ resp, err := rc.httpClient.Get(fmt.Sprintf("%s/api/v1/status", rc.hostURL))
+ if err != nil {
+ return nil, fmt.Errorf("failed to call get mono vertex status RESTful API, %w", err)
+ }
+ defer func() { _ = resp.Body.Close() }()
+ if res, err := unmarshalResponse[mvtxdaemon.GetMonoVertexStatusResponse](resp); err != nil {
+ return nil, err
+ } else {
+ return res.Status, nil
+ }
+
+}
diff --git a/pkg/mvtxdaemon/server/daemon_server.go b/pkg/mvtxdaemon/server/daemon_server.go
index f6ce1c9ec7..a8f05f64a7 100644
--- a/pkg/mvtxdaemon/server/daemon_server.go
+++ b/pkg/mvtxdaemon/server/daemon_server.go
@@ -45,12 +45,14 @@ import (
)
type daemonServer struct {
- monoVtx *v1alpha1.MonoVertex
+ monoVtx *v1alpha1.MonoVertex
+ mvtxService *service.MonoVertexService
}
func NewDaemonServer(monoVtx *v1alpha1.MonoVertex) *daemonServer {
return &daemonServer{
- monoVtx: monoVtx,
+ monoVtx: monoVtx,
+ mvtxService: nil,
}
}
@@ -93,6 +95,11 @@ func (ds *daemonServer) Run(ctx context.Context) error {
go func() { _ = httpServer.Serve(httpL) }()
go func() { _ = tcpm.Serve() }()
+ // Start the Data flow health status updater
+ go func() {
+ ds.mvtxService.StartHealthCheck(ctx)
+ }()
+
// Start the rater
go func() {
if err := rater.Start(ctx); err != nil {
@@ -127,6 +134,7 @@ func (ds *daemonServer) newGRPCServer(rater rateServer.MonoVtxRatable) (*grpc.Se
return nil, err
}
mvtxdaemon.RegisterMonoVertexDaemonServiceServer(grpcServer, mvtxService)
+ ds.mvtxService = mvtxService
return grpcServer, nil
}
diff --git a/pkg/mvtxdaemon/server/service/health_status.go b/pkg/mvtxdaemon/server/service/health_status.go
new file mode 100644
index 0000000000..9942f7885c
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/health_status.go
@@ -0,0 +1,226 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package service
+
+import (
+ "context"
+ "fmt"
+ "math"
+ "sync"
+ "time"
+
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon"
+ "github.com/numaproj/numaflow/pkg/isb"
+)
+
+const (
+ // healthTimeStep is the frequency at which the health of a MonoVertex is computed
+ healthTimeStep = 30 * time.Second
+)
+
+// dataHealthResponse is the response returned by the data health check API
+type dataHealthResponse struct {
+ // Status is the overall data status of the pipeline
+ Status string `json:"status"`
+ // Message is the error message if any
+ Message string `json:"message"`
+ // Code is the status code for the data health
+ Code string `json:"code"`
+}
+
+// newDataHealthResponse is used to create a new dataHealthResponse object
+func newDataHealthResponse(status string, message string, code string) *dataHealthResponse {
+ return &dataHealthResponse{
+ Status: status,
+ Message: message,
+ Code: code,
+ }
+}
+
+// defaultDataHealthResponse is the default response returned by the data health check API
+var defaultDataHealthResponse = newDataHealthResponse(
+ v1alpha1.MonoVertexStatusUnknown,
+ "MonoVertex data flow is in an unknown state",
+ "D4")
+
+// HealthChecker is the struct type for health checker.
+type HealthChecker struct {
+ // field for the health status.
+ currentDataStatus *dataHealthResponse
+ // Spec of the monoVertex getting processed
+ monoVertex *v1alpha1.MonoVertex
+ // Mutex for state information
+ statusLock *sync.RWMutex
+}
+
+// NewHealthChecker creates a new object HealthChecker struct type.
+func NewHealthChecker(monoVertex *v1alpha1.MonoVertex) *HealthChecker {
+ // Return a new HealthChecker struct instance.
+ return &HealthChecker{
+ currentDataStatus: defaultDataHealthResponse,
+ monoVertex: monoVertex,
+ statusLock: &sync.RWMutex{},
+ }
+}
+
+// monoVtxState is a struct which contains the name and data health state
+// of a MonoVertex
+type monoVtxState struct {
+ // Name is the name of the vertex
+ Name string `json:"name"`
+ // State is the state of the vertex
+ State string `json:"state"`
+}
+
+// newMonoVtxState is used to create a new monoVtxState object
+func newMonoVtxState(name string, state string) *monoVtxState {
+ return &monoVtxState{
+ Name: name,
+ State: state,
+ }
+}
+
+// getCurrentHealth returns the current health status of the MonoVertex.
+// It is thread safe to ensure concurrent access.
+func (hc *HealthChecker) getCurrentHealth() *dataHealthResponse {
+ // Lock the statusLock to ensure thread safety.
+ hc.statusLock.RLock()
+ defer hc.statusLock.RUnlock()
+ // Return the health status.
+ return hc.currentDataStatus
+}
+
+// setCurrentHealth sets the current health status of the MonoVertex.
+// It is thread safe to ensure concurrent access.
+func (hc *HealthChecker) setCurrentHealth(status *dataHealthResponse) {
+ // Lock the statusLock to ensure thread safety.
+ hc.statusLock.Lock()
+ defer hc.statusLock.Unlock()
+ // Set the health status.
+ hc.currentDataStatus = status
+}
+
+// getMonoVertexDataCriticality is used to provide the data criticality of the MonoVertex
+// They can be of the following types:
+// 1. Healthy: The MonoVertex is working as expected
+// 2. Warning: The MonoVertex is working but there could be a lag in the data movement
+// 3. Critical: The MonoVertex is not working as expected
+// We need to check the following things to determine the data criticality of the MonoVertex:
+// At any given instant of time what is the desired number of replicas required by the MonoVertex
+// to clear out the backlog in the target state time.
+// This logic is similar to our scaling logic.
+// Based on the desired replicas, we decide the data criticality.
+//
+// - If the current replicas are equal to the max replicas, and the desired replicas are more than the max replicas,
+// the data criticality is Critical. This means that the MonoVertex is not able to process the data at the rate
+// it is coming in, and the due to the provided specified scale we cannot add more replicas as well.
+// Else we consider the data criticality as healthy.
+//
+// TODO(MonoVertex): Add the logic to determine the warning state based on more conditions.
+func (hc *HealthChecker) getMonoVertexDataCriticality(ctx context.Context, mvtxMetrics *mvtxdaemon.MonoVertexMetrics) (*monoVtxState, error) {
+ // Get the desired replicas for the MonoVertex based on the metrics
+ desiredReplicas, err := hc.getDesiredReplica(mvtxMetrics)
+ if err != nil {
+ return nil, err
+ }
+ // Get the current state of the MonoVertex replicas
+ currentReplicas := hc.monoVertex.GetReplicas()
+ maxReplicas := int(hc.monoVertex.Spec.Scale.GetMaxReplicas())
+ // default status is healthy
+ status := v1alpha1.MonoVertexStatusHealthy
+ // If the current replicas are equal to the max replicas, and the desired replicas are more than the max replicas,
+ // the data criticality is Critical.
+ if currentReplicas == maxReplicas && desiredReplicas > maxReplicas {
+ status = v1alpha1.MonoVertexStatusCritical
+ }
+ return newMonoVtxState(mvtxMetrics.MonoVertex, status), nil
+}
+
+// getDesiredReplica calculates the desired replicas based on the processing rate and pending information
+// of the MonoVertex. This logic is similar to our scaling logic.
+// But unlike the scaling where we change the desired replicas based on the provided scale,
+// here we just calculate the desired replicas and return it.
+func (hc *HealthChecker) getDesiredReplica(mvtxMetrics *mvtxdaemon.MonoVertexMetrics) (int, error) {
+ totalRate := float64(0)
+ totalPending := int64(0)
+ // Extract the processing rate from the metrics for the default lookback period
+ rate, existing := mvtxMetrics.ProcessingRates["default"]
+ // Rate not available
+ // send back error that we calculate the health status right now
+ if !existing || rate.GetValue() < 0 {
+ return 0, fmt.Errorf("cannot check data health, MonoVertex %s has no rate information", mvtxMetrics.MonoVertex)
+ } else {
+ totalRate = rate.GetValue()
+ }
+
+ // Extract the pending information from the metrics for the default lookback period
+ pending, existing := mvtxMetrics.Pendings["default"]
+ if !existing || pending.GetValue() < 0 || pending.GetValue() == isb.PendingNotAvailable {
+ // Pending not available, we don't do anything
+ // send back error that we calculate the health status right now
+ return 0, fmt.Errorf("cannot check data health, MonoVertex %s has no pending information", mvtxMetrics.MonoVertex)
+ } else {
+ totalPending = pending.GetValue()
+ }
+
+ // If both totalRate and totalPending are 0, we don't need any processing replicas
+ if totalPending == 0 && totalRate == 0 {
+ return 0, nil
+ }
+
+ //TODO(MonoVertex): Something is wrong
+ // MonoVertex is not processing any data even though the pending is still around.
+ // It could be a slow processor, but zero rate isn't ideal
+ // we should mark this up as warning maybe?
+ if totalRate == 0 {
+ return int(hc.monoVertex.Status.Replicas), nil
+ }
+
+ // We calculate the time of finishing processing the pending messages,
+ // and then we know how many replicas are needed to get them done in target seconds.
+ desired := int32(math.Round(((float64(totalPending) / totalRate) / float64(hc.monoVertex.Spec.Scale.GetTargetProcessingSeconds())) * float64(hc.monoVertex.Status.Replicas)))
+ return int(desired), nil
+}
+
+// convertMonoVtxStateToHealthResp is used to generate the data health response from a MonoVtx State
+func (hc *HealthChecker) convertMonoVtxStateToHealthResp(vertexState *monoVtxState) *dataHealthResponse {
+ switch vertexState.State {
+ case v1alpha1.MonoVertexStatusHealthy:
+ return newDataHealthResponse(
+ v1alpha1.MonoVertexStatusHealthy,
+ "MonoVertex data flow is healthy",
+ "D1")
+ case v1alpha1.MonoVertexStatusWarning:
+ return newDataHealthResponse(
+ v1alpha1.MonoVertexStatusWarning,
+ fmt.Sprintf("MonoVertex data flow is in a warning state for %s", vertexState.Name),
+ "D2")
+ case v1alpha1.MonoVertexStatusCritical:
+ return newDataHealthResponse(
+ v1alpha1.MonoVertexStatusCritical,
+ fmt.Sprintf("MonoVertex data flow is in a critical state for %s", vertexState.Name),
+ "D3")
+ case v1alpha1.MonoVertexStatusUnknown:
+ return newDataHealthResponse(
+ v1alpha1.MonoVertexStatusUnknown,
+ fmt.Sprintf("MonoVertex data flow is in an unknown state due to %s", vertexState.Name),
+ "D4")
+ default:
+ return defaultDataHealthResponse
+ }
+}
diff --git a/pkg/mvtxdaemon/server/service/health_status_test.go b/pkg/mvtxdaemon/server/service/health_status_test.go
new file mode 100644
index 0000000000..ecd3dedc44
--- /dev/null
+++ b/pkg/mvtxdaemon/server/service/health_status_test.go
@@ -0,0 +1,145 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package service
+
+import (
+ "testing"
+
+ "google.golang.org/protobuf/types/known/wrapperspb"
+
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ "github.com/numaproj/numaflow/pkg/apis/proto/mvtxdaemon"
+)
+
+func TestGetCurrentHealth(t *testing.T) {
+ monoVertex := &v1alpha1.MonoVertex{} // Simplified for testing
+ hc := NewHealthChecker(monoVertex)
+ expected := defaultDataHealthResponse
+
+ if result := hc.getCurrentHealth(); result != expected {
+ t.Errorf("Expected %v, got %v", expected, result)
+ }
+}
+
+func TestSetCurrentHealth(t *testing.T) {
+ monoVertex := &v1alpha1.MonoVertex{} // Simplified
+ hc := NewHealthChecker(monoVertex)
+ newStatus := newDataHealthResponse("Healthy", "All systems green", "D1")
+
+ hc.setCurrentHealth(newStatus)
+
+ if result := hc.getCurrentHealth(); result != newStatus {
+ t.Errorf("Expected %v, got %v", newStatus, result)
+ }
+}
+
+func TestConvertMonoVtxStateToHealthResp(t *testing.T) {
+ monoVertex := &v1alpha1.MonoVertex{} // Simplified
+ hc := NewHealthChecker(monoVertex)
+
+ tests := []struct {
+ name string
+ state *monoVtxState
+ expected *dataHealthResponse
+ }{
+ {
+ name: "Healthy State",
+ state: newMonoVtxState("vertex1", v1alpha1.MonoVertexStatusHealthy),
+ expected: newDataHealthResponse(v1alpha1.MonoVertexStatusHealthy, "MonoVertex data flow is healthy", "D1"),
+ },
+ {
+ name: "Critical State",
+ state: newMonoVtxState("vertex1", v1alpha1.MonoVertexStatusCritical),
+ expected: newDataHealthResponse(v1alpha1.MonoVertexStatusCritical, "MonoVertex data flow is in a critical state for vertex1", "D3"),
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ result := hc.convertMonoVtxStateToHealthResp(test.state)
+ if result.Status != test.expected.Status || result.Message != test.expected.Message || result.Code != test.expected.Code {
+ t.Errorf("Expected %+v, got %+v", test.expected, result)
+ }
+ })
+ }
+}
+
+func TestGetDesiredReplica(t *testing.T) {
+ targetProcessingSeconds := uint32(5)
+ monoVertex := &v1alpha1.MonoVertex{
+ Spec: v1alpha1.MonoVertexSpec{
+ Scale: v1alpha1.Scale{TargetProcessingSeconds: &targetProcessingSeconds},
+ },
+ Status: v1alpha1.MonoVertexStatus{Replicas: 4},
+ }
+ hc := NewHealthChecker(monoVertex)
+
+ metrics := &mvtxdaemon.MonoVertexMetrics{
+ MonoVertex: "vertex",
+ ProcessingRates: map[string]*wrapperspb.DoubleValue{
+ "default": {Value: 100},
+ },
+ Pendings: map[string]*wrapperspb.Int64Value{
+ "default": {Value: 500},
+ },
+ }
+
+ expected := int(4)
+ result, err := hc.getDesiredReplica(metrics)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if result != expected {
+ t.Errorf("Expected %d, got %d", expected, result)
+ }
+}
+
+func TestGetDesiredReplicaNoRateAvailable(t *testing.T) {
+ monoVertex := &v1alpha1.MonoVertex{
+ Status: v1alpha1.MonoVertexStatus{Replicas: 4},
+ }
+ hc := NewHealthChecker(monoVertex)
+
+ metrics := &mvtxdaemon.MonoVertexMetrics{
+ MonoVertex: "vertex",
+ Pendings: map[string]*wrapperspb.Int64Value{
+ "default": {Value: 100},
+ },
+ }
+
+ _, err := hc.getDesiredReplica(metrics)
+ if err == nil {
+ t.Errorf("Expected error for no rate information, got nil")
+ }
+}
+
+func TestGetDesiredReplicaPendingNotAvailable(t *testing.T) {
+ monoVertex := &v1alpha1.MonoVertex{}
+ hc := NewHealthChecker(monoVertex)
+
+ metrics := &mvtxdaemon.MonoVertexMetrics{
+ MonoVertex: "vertex",
+ ProcessingRates: map[string]*wrapperspb.DoubleValue{
+ "default": {Value: 100},
+ },
+ }
+
+ _, err := hc.getDesiredReplica(metrics)
+ if err == nil {
+ t.Errorf("Expected error for no pending information, got nil")
+ }
+}
diff --git a/pkg/mvtxdaemon/server/service/mvtx_service.go b/pkg/mvtxdaemon/server/service/mvtx_service.go
index 40a2b2972c..c52c590ac6 100644
--- a/pkg/mvtxdaemon/server/service/mvtx_service.go
+++ b/pkg/mvtxdaemon/server/service/mvtx_service.go
@@ -40,21 +40,22 @@ import (
// Note: Please keep consistent with the definitions in rust/monovertex/sc/metrics.rs
const MonoVtxPendingMetric = "monovtx_pending"
-type MoveVertexService struct {
+type MonoVertexService struct {
mvtxdaemon.UnimplementedMonoVertexDaemonServiceServer
- monoVtx *v1alpha1.MonoVertex
- httpClient *http.Client
- rater raterPkg.MonoVtxRatable
+ monoVtx *v1alpha1.MonoVertex
+ httpClient *http.Client
+ rater raterPkg.MonoVtxRatable
+ healthChecker *HealthChecker
}
-var _ mvtxdaemon.MonoVertexDaemonServiceServer = (*MoveVertexService)(nil)
+var _ mvtxdaemon.MonoVertexDaemonServiceServer = (*MonoVertexService)(nil)
-// NewMoveVertexService returns a new instance of MoveVertexService
+// NewMoveVertexService returns a new instance of MonoVertexService
func NewMoveVertexService(
monoVtx *v1alpha1.MonoVertex,
rater raterPkg.MonoVtxRatable,
-) (*MoveVertexService, error) {
- mv := MoveVertexService{
+) (*MonoVertexService, error) {
+ mv := MonoVertexService{
monoVtx: monoVtx,
httpClient: &http.Client{
Transport: &http.Transport{
@@ -62,12 +63,18 @@ func NewMoveVertexService(
},
Timeout: time.Second * 3,
},
- rater: rater,
+ rater: rater,
+ healthChecker: NewHealthChecker(monoVtx),
}
return &mv, nil
}
-func (mvs *MoveVertexService) GetMonoVertexMetrics(ctx context.Context, empty *emptypb.Empty) (*mvtxdaemon.GetMonoVertexMetricsResponse, error) {
+func (mvs *MonoVertexService) GetMonoVertexMetrics(ctx context.Context, empty *emptypb.Empty) (*mvtxdaemon.GetMonoVertexMetricsResponse, error) {
+ return mvs.fetchMonoVertexMetrics(ctx)
+}
+
+// fetchMonoVertexMetrics is a helper function to derive the MonoVertex metrics
+func (mvs *MonoVertexService) fetchMonoVertexMetrics(ctx context.Context) (*mvtxdaemon.GetMonoVertexMetricsResponse, error) {
resp := new(mvtxdaemon.GetMonoVertexMetricsResponse)
collectedMetrics := new(mvtxdaemon.MonoVertexMetrics)
collectedMetrics.MonoVertex = mvs.monoVtx.Name
@@ -77,8 +84,19 @@ func (mvs *MoveVertexService) GetMonoVertexMetrics(ctx context.Context, empty *e
return resp, nil
}
+func (mvs *MonoVertexService) GetMonoVertexStatus(ctx context.Context, empty *emptypb.Empty) (*mvtxdaemon.GetMonoVertexStatusResponse, error) {
+ resp := new(mvtxdaemon.GetMonoVertexStatusResponse)
+ collectedStatus := new(mvtxdaemon.MonoVertexStatus)
+ dataHealth := mvs.healthChecker.getCurrentHealth()
+ collectedStatus.Status = dataHealth.Status
+ collectedStatus.Message = dataHealth.Message
+ collectedStatus.Code = dataHealth.Code
+ resp.Status = collectedStatus
+ return resp, nil
+}
+
// getPending returns the pending count for the mono vertex
-func (mvs *MoveVertexService) getPending(ctx context.Context) map[string]*wrapperspb.Int64Value {
+func (mvs *MonoVertexService) getPending(ctx context.Context) map[string]*wrapperspb.Int64Value {
log := logging.FromContext(ctx)
headlessServiceName := mvs.monoVtx.GetHeadlessServiceName()
pendingMap := make(map[string]*wrapperspb.Int64Value)
@@ -117,3 +135,46 @@ func (mvs *MoveVertexService) getPending(ctx context.Context) map[string]*wrappe
}
return pendingMap
}
+
+// StartHealthCheck starts the health check for the MonoVertex using the health checker
+func (mvs *MonoVertexService) StartHealthCheck(ctx context.Context) {
+ mvs.startHealthCheck(ctx)
+}
+
+// startHealthCheck starts the health check for the pipeline.
+// The ticks are generated at the interval of healthTimeStep.
+func (mvs *MonoVertexService) startHealthCheck(ctx context.Context) {
+ logger := logging.FromContext(ctx)
+ // Goroutine to listen for ticks
+ // At every tick, check and update the health status of the MonoVertex.
+ // If the context is done, return.
+ // Create a ticker to generate ticks at the interval of healthTimeStep.
+ ticker := time.NewTicker(healthTimeStep)
+ defer ticker.Stop()
+ for {
+ select {
+ // Get the current health status of the MonoVertex.
+ case <-ticker.C:
+ // Fetch the MonoVertex metrics, these are required for deriving the
+ // health status
+ mvtxMetrics, _ := mvs.fetchMonoVertexMetrics(ctx)
+ // Calculate the data criticality
+ criticality, err := mvs.healthChecker.getMonoVertexDataCriticality(ctx, mvtxMetrics.Metrics)
+ logger.Debugw("MonoVertex Health check", zap.Any("criticality", criticality))
+ if err != nil {
+ // If there is an error, set the current health status to unknown.
+ // as we are not able to determine the health of the pipeline.
+ logger.Errorw("Failed to MonoVertex data criticality", zap.Error(err))
+ mvs.healthChecker.setCurrentHealth(defaultDataHealthResponse)
+ } else {
+ // convert the MonoVertex health state to API response
+ monoVertexState := mvs.healthChecker.convertMonoVtxStateToHealthResp(criticality)
+ // update the current health status of the MonoVertex to cache
+ mvs.healthChecker.setCurrentHealth(monoVertexState)
+ }
+ // If the context is done, return.
+ case <-ctx.Done():
+ return
+ }
+ }
+}
diff --git a/pkg/shared/health-status-code/mono_vtx_code_map.go b/pkg/shared/health-status-code/mono_vtx_code_map.go
new file mode 100644
index 0000000000..ef2e3c5ab4
--- /dev/null
+++ b/pkg/shared/health-status-code/mono_vtx_code_map.go
@@ -0,0 +1,71 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package health_status_code
+
+var monoVtxResourceMap = map[string]*HealthCodeInfo{
+ "M1": newHealthCodeInfo(
+ "Mono Vertex is healthy",
+ "Healthy",
+ ),
+ "M2": newHealthCodeInfo(
+ "Mono Vertex is in a critical state",
+ "Critical",
+ ),
+ "M3": newHealthCodeInfo(
+ "Mono Vertex is in a warning state",
+ "Warning",
+ ),
+ "M4": newHealthCodeInfo(
+ "Mono Vertex is in an unknown state",
+ "Critical",
+ ),
+ "M5": newHealthCodeInfo(
+ "Mono Vertex is in a paused state",
+ "Warning",
+ ),
+}
+
+// monoVtxDataflowHealthMap is used to maintain status codes for dataflow level health
+// Each map entry is a map of status code as the key to the status message and the criticality of the status.
+// Status codes are in incremental like
+// 1. D1
+// 2. D2
+// 3. D3
+// The criticality is used to determine the overall status of the MonoVertex
+// Criticality can be one of the following:
+// 1. Critical: The MonoVertex is in a critical state
+// 2. Warning: The MonoVertex is in a warning state
+// 3. Healthy: The MonoVertex is healthy
+
+var _ = map[string]*HealthCodeInfo{
+ "D1": newHealthCodeInfo(
+ "Dataflow is healthy",
+ "Healthy",
+ ),
+ "D2": newHealthCodeInfo(
+ "Dataflow in warning state",
+ "Warning",
+ ),
+ "D3": newHealthCodeInfo(
+ "Dataflow in critical state",
+ "Critical",
+ ),
+ "D4": newHealthCodeInfo(
+ "Dataflow in unknown state",
+ "Critical",
+ ),
+}
diff --git a/pkg/shared/health-status-code/mono_vtx_code_map_test.go b/pkg/shared/health-status-code/mono_vtx_code_map_test.go
new file mode 100644
index 0000000000..6bb643b0b9
--- /dev/null
+++ b/pkg/shared/health-status-code/mono_vtx_code_map_test.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package health_status_code
+
+import "testing"
+
+func TestHealthCodeInformation(t *testing.T) {
+ tests := []struct {
+ code string
+ expectedCriticality string
+ expectedStatus string
+ }{
+ {"M1", "Healthy", "Mono Vertex is healthy"},
+ {"M2", "Critical", "Mono Vertex is in a critical state"},
+ {"M3", "Warning", "Mono Vertex is in a warning state"},
+ {"M4", "Critical", "Mono Vertex is in an unknown state"},
+ {"M5", "Warning", "Mono Vertex is in a paused state"},
+ }
+
+ for _, test := range tests {
+ t.Run(test.code, func(t *testing.T) {
+ info, exists := monoVtxResourceMap[test.code]
+ if !exists {
+ t.Errorf("Health code %s does not exist in map", test.code)
+ }
+
+ if info.Status != test.expectedStatus {
+ t.Errorf("Wrong state for %s: got %s, want %s", test.code, info.Status, test.expectedStatus)
+ }
+
+ if info.Criticality != test.expectedCriticality {
+ t.Errorf("Wrong description for %s: got %s, want %s", test.code, info.Criticality, test.expectedCriticality)
+ }
+ })
+ }
+}
diff --git a/pkg/shared/health-status-code/code_map.go b/pkg/shared/health-status-code/pipeline_code_map.go
similarity index 89%
rename from pkg/shared/health-status-code/code_map.go
rename to pkg/shared/health-status-code/pipeline_code_map.go
index 8d20618304..08da32c0cd 100644
--- a/pkg/shared/health-status-code/code_map.go
+++ b/pkg/shared/health-status-code/pipeline_code_map.go
@@ -16,20 +16,6 @@ limitations under the License.
package health_status_code
-// HealthCodeInfo is used to maintain status codes for vertex level health
-type HealthCodeInfo struct {
- Status string
- Criticality string
-}
-
-// newHealthCodeInfo is used to create a new HealthCodeInfo object
-func newHealthCodeInfo(status string, criticality string) *HealthCodeInfo {
- return &HealthCodeInfo{
- Status: status,
- Criticality: criticality,
- }
-}
-
// VertexHealthMap is used to maintain status codes for vertex level health
// Each map entry is a map of status code as the key to the status message and the criticality of the status.
// Status codes are in incremental like
diff --git a/pkg/shared/health-status-code/code_map_test.go b/pkg/shared/health-status-code/pipeline_code_map_test.go
similarity index 100%
rename from pkg/shared/health-status-code/code_map_test.go
rename to pkg/shared/health-status-code/pipeline_code_map_test.go
diff --git a/pkg/shared/health-status-code/utils.go b/pkg/shared/health-status-code/utils.go
new file mode 100644
index 0000000000..e83ca20de0
--- /dev/null
+++ b/pkg/shared/health-status-code/utils.go
@@ -0,0 +1,31 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package health_status_code
+
+// HealthCodeInfo is used to maintain status codes for vertex level health
+type HealthCodeInfo struct {
+ Status string
+ Criticality string
+}
+
+// newHealthCodeInfo is used to create a new HealthCodeInfo object
+func newHealthCodeInfo(status string, criticality string) *HealthCodeInfo {
+ return &HealthCodeInfo{
+ Status: status,
+ Criticality: criticality,
+ }
+}
diff --git a/server/apis/v1/handler.go b/server/apis/v1/handler.go
index fb53cfbe42..460d863435 100644
--- a/server/apis/v1/handler.go
+++ b/server/apis/v1/handler.go
@@ -1016,7 +1016,7 @@ func (h *handler) GetPipelineStatus(c *gin.Context) {
// Get the vertex level health of the pipeline
resourceHealth, err := h.healthChecker.getPipelineResourceHealth(h, ns, pipeline)
if err != nil {
- h.respondWithError(c, fmt.Sprintf("Failed to get the dataStatus for pipeline %q: %s", pipeline, err.Error()))
+ h.respondWithError(c, fmt.Sprintf("Failed to get the resourceHealth for pipeline %q: %s", pipeline, err.Error()))
return
}
@@ -1145,6 +1145,41 @@ func (h *handler) GetMonoVertexMetrics(c *gin.Context) {
c.JSON(http.StatusOK, NewNumaflowAPIResponse(nil, metrics))
}
+// GetMonoVertexHealth is used to the health information about a mono vertex
+// We use two checks to determine the health of the mono vertex:
+// 1. Resource Health: It is based on the health of the mono vertex deployment and pods.
+// 2. Data Criticality: It is based on the data movement of the mono vertex
+func (h *handler) GetMonoVertexHealth(c *gin.Context) {
+ ns, monoVertex := c.Param("namespace"), c.Param("mono-vertex")
+
+ // Resource level health
+ resourceHealth, err := h.healthChecker.getMonoVtxResourceHealth(h, ns, monoVertex)
+ if err != nil {
+ h.respondWithError(c, fmt.Sprintf("Failed to get the resourceHealth for MonoVertex %q: %s", monoVertex, err.Error()))
+ return
+ }
+
+ // Create a new daemon client to get the data status
+ client, err := h.getMonoVertexDaemonClient(ns, monoVertex)
+ if err != nil || client == nil {
+ h.respondWithError(c, fmt.Sprintf("failed to get daemon service client for mono vertex %q, %s", monoVertex, err.Error()))
+ return
+ }
+ // Data level health status
+ dataHealth, err := client.GetMonoVertexStatus(c)
+ if err != nil {
+ h.respondWithError(c, fmt.Sprintf("Failed to get the mono vertex dataStatus: namespace %q mono vertex %q: %s", ns, monoVertex, err.Error()))
+ return
+ }
+
+ // Create a response string based on the vertex health and data criticality
+ // We combine both the states to get the final dataStatus of the pipeline
+ response := NewHealthResponse(resourceHealth.Status, dataHealth.GetStatus(),
+ resourceHealth.Message, dataHealth.GetMessage(), resourceHealth.Code, dataHealth.GetCode())
+
+ c.JSON(http.StatusOK, NewNumaflowAPIResponse(nil, response))
+}
+
// getAllNamespaces is a utility used to fetch all the namespaces in the cluster
// except the kube system namespaces
func getAllNamespaces(h *handler) ([]string, error) {
diff --git a/server/apis/v1/health.go b/server/apis/v1/health.go
index 690631b80f..5973a8865d 100644
--- a/server/apis/v1/health.go
+++ b/server/apis/v1/health.go
@@ -14,32 +14,38 @@ import (
)
const (
- // resourceCacheRefreshDuration is the duration after which the vertex status cache is refreshed
+ // resourceCacheRefreshDuration is the duration after which the resource status cache is refreshed
resourceCacheRefreshDuration = 30 * time.Second
)
-// resourceHealthResponse is the response returned by the vertex health check API
+// resourceHealthResponse is the response returned by the health check API
type resourceHealthResponse struct {
- // Status is the overall vertex status of the pipeline
+ // Status is the overall resource status of the corresponding resource
Status string `json:"status"`
// Message is the error message if any
Message string `json:"message"`
- // Code is the status code for the vertex health
+ // Code is the status code for the resource health
Code string `json:"code"`
}
-// HealthChecker is the struct to hold the resource status cache for the pipeline
+// HealthChecker is the struct to hold the resource status cache for the
+// pipeline and Mono Vertex
type HealthChecker struct {
- resourceStatusCache *evictCache.LRU[string, *resourceHealthResponse]
- log *zap.SugaredLogger
+ // pipelineResourceStatusCache is a cache to store the pipeline resource status
+ pipelineResourceStatusCache *evictCache.LRU[string, *resourceHealthResponse]
+ // monoVtxResourceStatusCache is a cache to store the Mono Vertex resource status
+ monoVtxResourceStatusCache *evictCache.LRU[string, *resourceHealthResponse]
+ log *zap.SugaredLogger
}
// NewHealthChecker is used to create a new health checker
func NewHealthChecker(ctx context.Context) *HealthChecker {
c := evictCache.NewLRU[string, *resourceHealthResponse](500, nil, resourceCacheRefreshDuration)
+ mvCache := evictCache.NewLRU[string, *resourceHealthResponse](500, nil, resourceCacheRefreshDuration)
return &HealthChecker{
- resourceStatusCache: c,
- log: logging.FromContext(ctx),
+ pipelineResourceStatusCache: c,
+ monoVtxResourceStatusCache: mvCache,
+ log: logging.FromContext(ctx),
}
}
@@ -55,7 +61,7 @@ func (hc *HealthChecker) getPipelineResourceHealth(h *handler, ns string,
cacheKey := fmt.Sprintf("%s-%s", ns, pipeline)
// check if the pipeline status is cached
- if status, ok := hc.resourceStatusCache.Get(cacheKey); ok {
+ if status, ok := hc.pipelineResourceStatusCache.Get(cacheKey); ok {
hc.log.Info("Pipeline status from cache: ", status)
return status, nil
}
@@ -65,7 +71,7 @@ func (hc *HealthChecker) getPipelineResourceHealth(h *handler, ns string,
return status, err
}
// update cache with the new pipeline status
- hc.resourceStatusCache.Add(cacheKey, status)
+ hc.pipelineResourceStatusCache.Add(cacheKey, status)
return status, nil
}
@@ -207,3 +213,70 @@ func isVertexHealthy(h *handler, ns string, pipeline string, vertex *dfv1.Vertex
Code: "V1",
}, nil
}
+
+// getMonoVtxResourceHealth is used to provide the overall resouce health and status of the Mono Vertex
+// This first check if the Mono Vertex status is cached, if not,
+// it checks for the current Mono Vertex status
+func (hc *HealthChecker) getMonoVtxResourceHealth(h *handler, ns string,
+ monoVtx string) (*resourceHealthResponse, error) {
+
+ // create a cache key for the Mono Vertex
+ // It is a combination of namespace and Mono Vertex name
+ // In the form of -
+ cacheKey := fmt.Sprintf("%s-%s", ns, monoVtx)
+
+ // check if the Mono Vertex status is cached
+ if status, ok := hc.monoVtxResourceStatusCache.Get(cacheKey); ok {
+ hc.log.Info("Mono Vertex status from cache: ", status)
+ return status, nil
+ }
+ // if not present in cache, check for the current Mono Vertex status
+ status, err := checkMonoVtxHealth(h, ns, monoVtx)
+ if err != nil {
+ return nil, err
+ }
+ // update cache with the new Mono Vertex status
+ hc.monoVtxResourceStatusCache.Add(cacheKey, status)
+
+ return status, nil
+}
+
+// checkMonoVtxHealth is used to provide the overall Mono Vertex health and status of the Mono Vertex
+// They can be of the following types:
+// 1. Healthy: The Mono Vertex is healthy
+// 2. Unhealthy: The Mono Vertex is unhealthy
+// 3. Paused: The Mono Vertex is paused (Not supported right now)
+// 4. Unknown: The Mono Vertex is in an unknown state
+// We use the kubernetes client to get the spec of the MonoVertex and
+// then check its status to derive the resource health status
+
+// We perform the following checks:
+// 1) Check the `phase“ in the Status field of the spec, it should be “Running”
+// 2) Check if the `conditions` field of the spec, all of them shoudl be true
+func checkMonoVtxHealth(h *handler, ns string, monoVtx string) (*resourceHealthResponse, error) {
+ // fetch the current spec of the Mono Vertex
+ v, err := h.numaflowClient.MonoVertices(ns).Get(context.Background(), monoVtx, metav1.GetOptions{})
+ // if there is an error fetching the spec, return an error
+ // with status unknown
+ if err != nil {
+ return &resourceHealthResponse{
+ Status: dfv1.MonoVertexStatusUnknown,
+ Message: fmt.Sprintf("error in getting Mono Vertex %q status: %v", monoVtx, err),
+ Code: "M4",
+ }, err
+ }
+ // check if the Mono vertex is healthy or not through the status field of the spec
+ isMvtxHealthy := v.Status.IsHealthy()
+ if !isMvtxHealthy {
+ return &resourceHealthResponse{
+ Status: dfv1.MonoVertexStatusUnhealthy,
+ Message: fmt.Sprintf("mono vertex %q is unhealthy: %s:%s", monoVtx, v.Status.Message, v.Status.Reason),
+ Code: "M2",
+ }, nil
+ }
+ return &resourceHealthResponse{
+ Status: dfv1.MonoVertexStatusHealthy,
+ Message: fmt.Sprintf("mono vertex %q is healthy", monoVtx),
+ Code: "M1",
+ }, nil
+}
diff --git a/server/cmd/server/start.go b/server/cmd/server/start.go
index 16e52eaf62..aa4e3403b8 100644
--- a/server/cmd/server/start.go
+++ b/server/cmd/server/start.go
@@ -205,5 +205,6 @@ func CreateAuthRouteMap(baseHref string) authz.RouteMap {
"GET:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices/:mono-vertex/pods": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
"GET:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices/:mono-vertex/metrics": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
"POST:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
+ "GET:" + baseHref + "api/v1/namespaces/:namespace/mono-vertices/:mono-vertex/health": authz.NewRouteInfo(authz.ObjectMonoVertex, true),
}
}
diff --git a/server/cmd/server/start_test.go b/server/cmd/server/start_test.go
index b8abd6291e..13518ee163 100644
--- a/server/cmd/server/start_test.go
+++ b/server/cmd/server/start_test.go
@@ -25,12 +25,12 @@ import (
func TestCreateAuthRouteMap(t *testing.T) {
t.Run("empty base", func(t *testing.T) {
got := CreateAuthRouteMap("")
- assert.Equal(t, 29, len(got))
+ assert.Equal(t, 30, len(got))
})
t.Run("customize base", func(t *testing.T) {
got := CreateAuthRouteMap("abcdefg")
- assert.Equal(t, 29, len(got))
+ assert.Equal(t, 30, len(got))
for k := range got {
assert.Contains(t, k, "abcdefg")
}
diff --git a/server/routes/routes.go b/server/routes/routes.go
index 68064e8c52..c80181d789 100644
--- a/server/routes/routes.go
+++ b/server/routes/routes.go
@@ -163,6 +163,8 @@ func v1Routes(ctx context.Context, r gin.IRouter, dexObj *v1.DexObject, localUse
r.POST("/namespaces/:namespace/mono-vertices", handler.CreateMonoVertex)
// Get the metrics of a mono vertex.
r.GET("/namespaces/:namespace/mono-vertices/:mono-vertex/metrics", handler.GetMonoVertexMetrics)
+ // Get the health information of a mono vertex.
+ r.GET("/namespaces/:namespace/mono-vertices/:mono-vertex/health", handler.GetMonoVertexHealth)
}
// authMiddleware is the middleware for AuthN/AuthZ.
From 42671138250d67f6eacddf33d4b5d5e069e5674f Mon Sep 17 00:00:00 2001
From: Vedant Gupta <49195734+veds-g@users.noreply.github.com>
Date: Tue, 20 Aug 2024 00:40:17 +0530
Subject: [PATCH 009/188] fix: replicas derived in UI from mvtx status instead
of spec (#1965)
Signed-off-by: veds-g
---
ui/src/types/declarations/pipeline.d.ts | 1 -
ui/src/utils/fetcherHooks/monoVertexViewFetch.ts | 12 +++++++++---
ui/src/utils/fetcherHooks/pipelineViewFetch.ts | 4 +++-
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/ui/src/types/declarations/pipeline.d.ts b/ui/src/types/declarations/pipeline.d.ts
index 09b8757387..dbc2811f3f 100644
--- a/ui/src/types/declarations/pipeline.d.ts
+++ b/ui/src/types/declarations/pipeline.d.ts
@@ -116,7 +116,6 @@ export interface MonoVertex {
}
export interface MonoVertexSpec {
- replicas: number;
source: any;
sink: any;
scale: any;
diff --git a/ui/src/utils/fetcherHooks/monoVertexViewFetch.ts b/ui/src/utils/fetcherHooks/monoVertexViewFetch.ts
index 9c7cd459d3..68d7524950 100644
--- a/ui/src/utils/fetcherHooks/monoVertexViewFetch.ts
+++ b/ui/src/utils/fetcherHooks/monoVertexViewFetch.ts
@@ -18,6 +18,7 @@ export const useMonoVertexViewFetch = (
const [requestKey, setRequestKey] = useState("");
const [pipeline, setPipeline] = useState(undefined);
const [spec, setSpec] = useState(undefined);
+ const [replicas, setReplicas] = useState(undefined);
const [monoVertexPods, setMonoVertexPods] = useState>(
new Map()
);
@@ -45,7 +46,12 @@ export const useMonoVertexViewFetch = (
// Update pipeline state with data from the response
setPipeline(json.data?.monoVertex);
// Update spec state if it is not equal to the spec from the response
- if (!isEqual(spec, json.data)) setSpec(json.data?.monoVertex?.spec);
+ if (!isEqual(spec, json.data?.monoVertex?.spec)) {
+ setSpec(json.data.monoVertex.spec);
+ }
+ if (replicas !== json.data?.monoVertex?.status?.replicas) {
+ setReplicas(json.data.monoVertex.status.replicas);
+ }
setPipelineErr(undefined);
} else if (json?.errMsg) {
// pipeline API call returns an error message
@@ -245,7 +251,7 @@ export const useMonoVertexViewFetch = (
const name = pipelineId ?? "";
newNode.id = name;
newNode.data = { name: name };
- newNode.data.podnum = spec?.replicas ? spec.replicas : 0;
+ newNode.data.podnum = replicas ? replicas : 0;
newNode.position = { x: 0, y: 0 };
// change this in the future if you would like to make it draggable
newNode.draggable = false;
@@ -266,7 +272,7 @@ export const useMonoVertexViewFetch = (
if (pipeline && vertices?.length > 0) {
setLoading(false);
}
- }, [pipeline, vertices]);
+ }, [pipeline, vertices, replicas]);
return {
pipeline,
diff --git a/ui/src/utils/fetcherHooks/pipelineViewFetch.ts b/ui/src/utils/fetcherHooks/pipelineViewFetch.ts
index 583d0859fc..abfab42443 100644
--- a/ui/src/utils/fetcherHooks/pipelineViewFetch.ts
+++ b/ui/src/utils/fetcherHooks/pipelineViewFetch.ts
@@ -70,7 +70,9 @@ export const usePipelineViewFetch = (
`${json.data?.pipeline?.metadata?.namespace}-${json.data.pipeline?.metadata?.name}-`
);
// Update spec state if it is not equal to the spec from the response
- if (!isEqual(spec, json.data)) setSpec(json.data?.pipeline?.spec);
+ if (!isEqual(spec, json.data?.pipeline?.spec)) {
+ setSpec(json.data.pipeline.spec);
+ }
setPipelineErr(undefined);
} else if (json?.errMsg) {
// pipeline API call returns an error message
From d2fc8d7e95f8f85a12ef338e84859e2b5d87d191 Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Mon, 19 Aug 2024 17:34:57 -0400
Subject: [PATCH 010/188] chore: send negative value rateNotAvailable (#1966)
Signed-off-by: Sidhant Kohli
---
pkg/daemon/server/service/rater/helper.go | 10 +++-
.../server/service/rater/helper_test.go | 60 +++++++++----------
pkg/mvtxdaemon/server/service/rater/helper.go | 10 +++-
.../server/service/rater/helper_test.go | 36 +++++------
4 files changed, 62 insertions(+), 54 deletions(-)
diff --git a/pkg/daemon/server/service/rater/helper.go b/pkg/daemon/server/service/rater/helper.go
index 929ac99d50..73683c06af 100644
--- a/pkg/daemon/server/service/rater/helper.go
+++ b/pkg/daemon/server/service/rater/helper.go
@@ -17,6 +17,7 @@ limitations under the License.
package rater
import (
+ "math"
"time"
sharedqueue "github.com/numaproj/numaflow/pkg/shared/queue"
@@ -24,6 +25,9 @@ import (
const (
indexNotFound = -1
+ // rateNotAvailable is returned when the processing rate cannot be derived from the currently
+ // available pod data, a negative min is returned to indicate this.
+ rateNotAvailable = float64(math.MinInt)
)
// UpdateCount updates the count of processed messages for a pod at a given time
@@ -48,14 +52,14 @@ func UpdateCount(q *sharedqueue.OverflowQueue[*TimestampedCounts], time int64, p
func CalculateRate(q *sharedqueue.OverflowQueue[*TimestampedCounts], lookbackSeconds int64, partitionName string) float64 {
counts := q.Items()
if len(counts) <= 1 {
- return 0
+ return rateNotAvailable
}
startIndex := findStartIndex(lookbackSeconds, counts)
// we consider the last but one element as the end index because the last element might be incomplete
// we can be sure that the last but one element in the queue is complete.
endIndex := len(counts) - 2
if startIndex == indexNotFound {
- return 0
+ return rateNotAvailable
}
// time diff in seconds.
@@ -63,7 +67,7 @@ func CalculateRate(q *sharedqueue.OverflowQueue[*TimestampedCounts], lookbackSec
if timeDiff == 0 {
// if the time difference is 0, we return 0 to avoid division by 0
// this should not happen in practice because we are using a 10s interval
- return 0
+ return rateNotAvailable
}
delta := float64(0)
diff --git a/pkg/daemon/server/service/rater/helper_test.go b/pkg/daemon/server/service/rater/helper_test.go
index e420bcc0dc..8873f99802 100644
--- a/pkg/daemon/server/service/rater/helper_test.go
+++ b/pkg/daemon/server/service/rater/helper_test.go
@@ -119,17 +119,17 @@ func TestUpdateCount(t *testing.T) {
}
func TestCalculateRate(t *testing.T) {
- t.Run("givenCollectedTimeLessThanTwo_whenCalculateRate_thenReturnZero", func(t *testing.T) {
+ t.Run("givenCollectedTimeLessThanTwo_whenCalculateRate_thenReturnRateNotAvailable", func(t *testing.T) {
q := sharedqueue.New[*TimestampedCounts](1800)
// no data
- assert.Equal(t, 0.0, CalculateRate(q, 10, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 10, "partition1"))
// only one data
now := time.Now()
tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
tc1.Update(&PodReadCount{"pod1", map[string]float64{"partition1": 5.0}})
q.Append(tc1)
- assert.Equal(t, 0.0, CalculateRate(q, 10, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 10, "partition1"))
})
t.Run("singlePod_givenCountIncreases_whenCalculateRate_thenReturnRate", func(t *testing.T) {
@@ -147,9 +147,9 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition1"))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 0.5, CalculateRate(q, 25, "partition1"))
// tc1 and tc2 are used to calculate the rate
@@ -174,9 +174,9 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc4)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition1"))
// tc2 and tc3 are used to calculate the rate
assert.Equal(t, 5.0, CalculateRate(q, 25, "partition1"))
// tc1, 2 and 3 are used to calculate the rate
@@ -203,11 +203,11 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 25, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 25, "partition1"))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 15.0, CalculateRate(q, 35, "partition1"))
})
@@ -230,11 +230,11 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 25, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 25, "partition1"))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 30.0, CalculateRate(q, 35, "partition1"))
})
@@ -257,11 +257,11 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 25, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 25, "partition1"))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 25.0, CalculateRate(q, 35, "partition1"))
})
@@ -292,9 +292,9 @@ func TestCalculateRate(t *testing.T) {
// partition1 rate
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition1"))
// tc2 and tc3 are used to calculate the rate
assert.Equal(t, 5.0, CalculateRate(q, 25, "partition1"))
// tc1, 2 and 3 are used to calculate the rate
@@ -303,29 +303,29 @@ func TestCalculateRate(t *testing.T) {
assert.Equal(t, 7.5, CalculateRate(q, 100, "partition1"))
// partition2 rate
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition2"))
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition2"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition2"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition2"))
assert.Equal(t, 10.0, CalculateRate(q, 25, "partition2"))
assert.Equal(t, 10.5, CalculateRate(q, 35, "partition2"))
assert.Equal(t, 10.5, CalculateRate(q, 100, "partition2"))
// partition3 rate
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition3"))
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition3"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition3"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition3"))
assert.Equal(t, 20.0, CalculateRate(q, 25, "partition3"))
assert.Equal(t, 10.0, CalculateRate(q, 35, "partition3"))
assert.Equal(t, 10.0, CalculateRate(q, 100, "partition3"))
// partition4 rate
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition4"))
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition4"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition4"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition4"))
assert.Equal(t, 10.0, CalculateRate(q, 25, "partition4"))
assert.Equal(t, 5.0, CalculateRate(q, 35, "partition4"))
assert.Equal(t, 5.0, CalculateRate(q, 100, "partition4"))
// partition100 rate
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition100"))
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition100"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition100"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition100"))
assert.Equal(t, 0.0, CalculateRate(q, 25, "partition100"))
assert.Equal(t, 0.0, CalculateRate(q, 35, "partition100"))
assert.Equal(t, 0.0, CalculateRate(q, 100, "partition100"))
@@ -359,9 +359,9 @@ func TestCalculateRate(t *testing.T) {
// partition1 rate
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition1"))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition1"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition1"))
// tc2 and tc3 are used to calculate the rate
assert.Equal(t, 111.0, CalculateRate(q, 25, "partition1"))
// tc1, 2 and 3 are used to calculate the rate
@@ -370,8 +370,8 @@ func TestCalculateRate(t *testing.T) {
assert.Equal(t, 111.0, CalculateRate(q, 100, "partition1"))
// partition2 rate
- assert.Equal(t, 0.0, CalculateRate(q, 5, "partition2"))
- assert.Equal(t, 0.0, CalculateRate(q, 15, "partition2"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5, "partition2"))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15, "partition2"))
assert.Equal(t, 111.0, CalculateRate(q, 25, "partition2"))
assert.Equal(t, 111.0, CalculateRate(q, 35, "partition2"))
assert.Equal(t, 111.0, CalculateRate(q, 100, "partition2"))
diff --git a/pkg/mvtxdaemon/server/service/rater/helper.go b/pkg/mvtxdaemon/server/service/rater/helper.go
index 0973b3c5cb..ce9aac1ed4 100644
--- a/pkg/mvtxdaemon/server/service/rater/helper.go
+++ b/pkg/mvtxdaemon/server/service/rater/helper.go
@@ -17,6 +17,7 @@ limitations under the License.
package rater
import (
+ "math"
"time"
sharedqueue "github.com/numaproj/numaflow/pkg/shared/queue"
@@ -25,6 +26,9 @@ import (
const (
// indexNotFound is returned when the start index cannot be found in the queue.
indexNotFound = -1
+ // rateNotAvailable is returned when the processing rate cannot be derived from the currently
+ // available pod data, a negative min is returned to indicate this.
+ rateNotAvailable = float64(math.MinInt)
)
// UpdateCount updates the count for a given timestamp in the queue.
@@ -49,14 +53,14 @@ func UpdateCount(q *sharedqueue.OverflowQueue[*TimestampedCounts], time int64, p
func CalculateRate(q *sharedqueue.OverflowQueue[*TimestampedCounts], lookbackSeconds int64) float64 {
counts := q.Items()
if len(counts) <= 1 {
- return 0
+ return rateNotAvailable
}
startIndex := findStartIndex(lookbackSeconds, counts)
// we consider the last but one element as the end index because the last element might be incomplete
// we can be sure that the last but one element in the queue is complete.
endIndex := len(counts) - 2
if startIndex == indexNotFound {
- return 0
+ return rateNotAvailable
}
// time diff in seconds.
@@ -64,7 +68,7 @@ func CalculateRate(q *sharedqueue.OverflowQueue[*TimestampedCounts], lookbackSec
if timeDiff == 0 {
// if the time difference is 0, we return 0 to avoid division by 0
// this should not happen in practice because we are using a 10s interval
- return 0
+ return rateNotAvailable
}
delta := float64(0)
diff --git a/pkg/mvtxdaemon/server/service/rater/helper_test.go b/pkg/mvtxdaemon/server/service/rater/helper_test.go
index 6ac878244c..f3e61e3390 100644
--- a/pkg/mvtxdaemon/server/service/rater/helper_test.go
+++ b/pkg/mvtxdaemon/server/service/rater/helper_test.go
@@ -106,17 +106,17 @@ func TestUpdateCount(t *testing.T) {
}
func TestCalculateRate(t *testing.T) {
- t.Run("givenCollectedTimeLessThanTwo_whenCalculateRate_thenReturnZero", func(t *testing.T) {
+ t.Run("givenCollectedTimeLessThanTwo_whenCalculateRate_thenReturnRateNotAvailable", func(t *testing.T) {
q := sharedqueue.New[*TimestampedCounts](1800)
// no data
- assert.Equal(t, 0.0, CalculateRate(q, 10))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 10))
// only one data
now := time.Now()
tc1 := NewTimestampedCounts(now.Truncate(CountWindow).Unix() - 20)
tc1.Update(&PodReadCount{"pod1", 5.0})
q.Append(tc1)
- assert.Equal(t, 0.0, CalculateRate(q, 10))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 10))
})
t.Run("singlePod_givenCountIncreases_whenCalculateRate_thenReturnRate", func(t *testing.T) {
@@ -134,9 +134,9 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 0.5, CalculateRate(q, 25))
// tc1 and tc2 are used to calculate the rate
@@ -161,9 +161,9 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc4)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15))
// tc2 and tc3 are used to calculate the rate
assert.Equal(t, 5.0, CalculateRate(q, 25))
// tc1, 2 and 3 are used to calculate the rate
@@ -190,11 +190,11 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 25))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 25))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 15.0, CalculateRate(q, 35))
})
@@ -217,11 +217,11 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 25))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 25))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 30.0, CalculateRate(q, 35))
})
@@ -244,11 +244,11 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc3)
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 5))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 15))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15))
// no enough data collected within lookback seconds, expect rate 0
- assert.Equal(t, 0.0, CalculateRate(q, 25))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 25))
// tc1 and tc2 are used to calculate the rate
assert.Equal(t, 25.0, CalculateRate(q, 35))
})
@@ -279,8 +279,8 @@ func TestCalculateRate(t *testing.T) {
q.Append(tc4)
// vertex rate
- assert.Equal(t, 0.0, CalculateRate(q, 5))
- assert.Equal(t, 0.0, CalculateRate(q, 15))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 5))
+ assert.Equal(t, rateNotAvailable, CalculateRate(q, 15))
assert.Equal(t, 25.0, CalculateRate(q, 25))
assert.Equal(t, 23.0, CalculateRate(q, 35))
assert.Equal(t, 23.0, CalculateRate(q, 100))
From e1bfd1b2d016d64bfb9d6ac546cc3489c96b806d Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Mon, 19 Aug 2024 18:18:24 -0400
Subject: [PATCH 011/188] refactor: re-arrange e2e tests (#1961)
Signed-off-by: Keran Yang
---
.github/workflows/ci.yaml | 2 +-
Makefile | 8 +-
test/api-e2e/api_test.go | 15 ++
test/api-e2e/testdata.go | 15 ++
.../builtin_source_test.go} | 54 +++++-
.../testdata/http-auth-fake-secret.yaml | 0
.../testdata/http-source-with-auth.yaml | 0
.../testdata/http-source.yaml | 0
.../testdata/jetstream-source-pipeline.yaml | 0
.../testdata/nats-source-pipeline.yaml | 0
test/e2e/functional_test.go | 35 +---
test/e2e/testdata/even-odd.yaml | 75 --------
test/idle-source-e2e/idle_source_test.go | 3 -
test/jetstream-e2e/jetstream_test.go | 56 ------
test/kafka-e2e/kafka_test.go | 63 +------
.../sdks_test.go => map-e2e/map_test.go} | 176 +++---------------
.../testdata/flatmap-batch.yaml | 0
.../testdata/flatmap-stream.yaml | 0
.../testdata/flatmap.yaml | 0
test/monovertex-e2e/monovertex_test.go | 18 ++
test/nats-e2e/nats_test.go | 53 ------
test/reduce-one-e2e/reduce_one_test.go | 9 +-
.../simple-keyed-reduce-pipeline.yaml | 5 +-
test/reduce-two-e2e/reduce_two_test.go | 55 ------
.../simple-session-sum-pipeline.yaml | 47 -----
test/sdks-e2e/README.md | 7 -
.../simple-keyed-reduce-pipeline.yaml | 47 -----
..._test.go => sideinput_sink_source_test.go} | 2 -
test/sideinputs-e2e/sideinput_test.go | 2 -
.../testdata}/event-time-filter-go.yaml | 0
.../testdata}/event-time-filter-java.yaml | 0
.../testdata}/event-time-filter-python.yaml | 0
.../testdata}/event-time-filter-rust.yaml | 0
test/transformer-e2e/transformer_test.go | 70 ++++++-
34 files changed, 205 insertions(+), 612 deletions(-)
rename test/{http-e2e/http_test.go => builtin-source-e2e/builtin_source_test.go} (62%)
rename test/{http-e2e => builtin-source-e2e}/testdata/http-auth-fake-secret.yaml (100%)
rename test/{http-e2e => builtin-source-e2e}/testdata/http-source-with-auth.yaml (100%)
rename test/{http-e2e => builtin-source-e2e}/testdata/http-source.yaml (100%)
rename test/{jetstream-e2e => builtin-source-e2e}/testdata/jetstream-source-pipeline.yaml (100%)
rename test/{nats-e2e => builtin-source-e2e}/testdata/nats-source-pipeline.yaml (100%)
delete mode 100644 test/e2e/testdata/even-odd.yaml
delete mode 100644 test/jetstream-e2e/jetstream_test.go
rename test/{sdks-e2e/sdks_test.go => map-e2e/map_test.go} (50%)
rename test/{sdks-e2e => map-e2e}/testdata/flatmap-batch.yaml (100%)
rename test/{sdks-e2e => map-e2e}/testdata/flatmap-stream.yaml (100%)
rename test/{sdks-e2e => map-e2e}/testdata/flatmap.yaml (100%)
delete mode 100644 test/nats-e2e/nats_test.go
delete mode 100644 test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml
delete mode 100644 test/sdks-e2e/README.md
delete mode 100644 test/sdks-e2e/testdata/simple-keyed-reduce-pipeline.yaml
rename test/sideinputs-e2e/{sideinput-e2e_sink_source_test.go => sideinput_sink_source_test.go} (99%)
rename test/{sdks-e2e/testdata/transformer => transformer-e2e/testdata}/event-time-filter-go.yaml (100%)
rename test/{sdks-e2e/testdata/transformer => transformer-e2e/testdata}/event-time-filter-java.yaml (100%)
rename test/{sdks-e2e/testdata/transformer => transformer-e2e/testdata}/event-time-filter-python.yaml (100%)
rename test/{sdks-e2e/testdata/transformer => transformer-e2e/testdata}/event-time-filter-rust.yaml (100%)
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index ba128ef042..e27feac027 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -151,7 +151,7 @@ jobs:
fail-fast: false
matrix:
driver: [jetstream]
- case: [e2e, diamond-e2e, transformer-e2e, kafka-e2e, http-e2e, nats-e2e, jetstream-e2e, sdks-e2e, reduce-one-e2e, reduce-two-e2e, udsource-e2e, api-e2e, sideinputs-e2e, idle-source-e2e, monovertex-e2e]
+ case: [e2e, diamond-e2e, transformer-e2e, kafka-e2e, map-e2e, reduce-one-e2e, reduce-two-e2e, udsource-e2e, api-e2e, sideinputs-e2e, idle-source-e2e, monovertex-e2e, builtin-source-e2e]
include:
- driver: redis
case: e2e
diff --git a/Makefile b/Makefile
index f437836f48..696b63f87b 100644
--- a/Makefile
+++ b/Makefile
@@ -104,10 +104,7 @@ test-code:
test-e2e:
test-kafka-e2e:
-test-http-e2e:
-test-nats-e2e:
-test-jetstream-e2e:
-test-sdks-e2e:
+test-map-e2e:
test-reduce-one-e2e:
test-reduce-two-e2e:
test-api-e2e:
@@ -115,6 +112,9 @@ test-udsource-e2e:
test-transformer-e2e:
test-diamond-e2e:
test-sideinputs-e2e:
+test-monovertex-e2e:
+test-idle-source-e2e:
+test-builtin-source-e2e:
test-%:
$(MAKE) cleanup-e2e
$(MAKE) image e2eapi-image
diff --git a/test/api-e2e/api_test.go b/test/api-e2e/api_test.go
index ae927e51d4..b23735e699 100644
--- a/test/api-e2e/api_test.go
+++ b/test/api-e2e/api_test.go
@@ -1,3 +1,18 @@
+/*
+Copyright 2022 The Numaproj Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
package api_e2e
import (
diff --git a/test/api-e2e/testdata.go b/test/api-e2e/testdata.go
index 5322b38ce8..411a7b586a 100644
--- a/test/api-e2e/testdata.go
+++ b/test/api-e2e/testdata.go
@@ -1,3 +1,18 @@
+/*
+Copyright 2022 The Numaproj Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
package api_e2e
var (
diff --git a/test/http-e2e/http_test.go b/test/builtin-source-e2e/builtin_source_test.go
similarity index 62%
rename from test/http-e2e/http_test.go
rename to test/builtin-source-e2e/builtin_source_test.go
index a5e7058dee..da37c4c3ad 100644
--- a/test/http-e2e/http_test.go
+++ b/test/builtin-source-e2e/builtin_source_test.go
@@ -1,3 +1,5 @@
+//go:build test
+
/*
Copyright 2022 The Numaproj Authors.
@@ -14,24 +16,41 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package http_e2e
+package builtin_source_e2e
import (
"fmt"
"testing"
+ "time"
"github.com/stretchr/testify/suite"
. "github.com/numaproj/numaflow/test/fixtures"
)
+//go:generate kubectl -n numaflow-system delete statefulset nats --ignore-not-found=true
//go:generate kubectl apply -f testdata/http-auth-fake-secret.yaml -n numaflow-system
-type HTTPSuite struct {
+//go:generate kubectl apply -k ../../config/apps/nats -n numaflow-system
+type BuiltinSourceSuite struct {
E2ESuite
}
-func (s *HTTPSuite) TestHTTPSourcePipeline() {
- w := s.Given().Pipeline("@testdata/http-source.yaml").
+func (bss *BuiltinSourceSuite) TestNatsSource() {
+ subject := "test-subject"
+ w := bss.Given().Pipeline("@testdata/nats-source-pipeline.yaml").
+ When().
+ CreatePipelineAndWait()
+ defer w.DeletePipelineAndWait()
+
+ // wait for all the pods to come up
+ w.Expect().VertexPodsRunning()
+
+ PumpNatsSubject(subject, 100, 20*time.Millisecond, 10, "test-message")
+ w.Expect().RedisSinkContains("nats-source-e2e-out", "test-message", SinkCheckWithContainCount(100))
+}
+
+func (bss *BuiltinSourceSuite) TestHTTPSourcePipeline() {
+ w := bss.Given().Pipeline("@testdata/http-source.yaml").
When().
CreatePipelineAndWait()
defer w.DeletePipelineAndWait()
@@ -59,8 +78,8 @@ func (s *HTTPSuite) TestHTTPSourcePipeline() {
w.Expect().RedisSinkContains("http-source-out", "with-id", SinkCheckWithContainCount(2))
}
-func (s *HTTPSuite) TestHTTPSourceAuthPipeline() {
- w := s.Given().Pipeline("@testdata/http-source-with-auth.yaml").
+func (bss *BuiltinSourceSuite) TestHTTPSourceAuthPipeline() {
+ w := bss.Given().Pipeline("@testdata/http-source-with-auth.yaml").
When().
CreatePipelineAndWait()
defer w.DeletePipelineAndWait()
@@ -75,6 +94,25 @@ func (s *HTTPSuite) TestHTTPSourceAuthPipeline() {
w.Expect().RedisSinkNotContains("http-auth-source-out", "no-auth")
}
-func TestHTTPSuite(t *testing.T) {
- suite.Run(t, new(HTTPSuite))
+func (bss *BuiltinSourceSuite) TestJetstreamSource() {
+ const streamName = "test-stream"
+ const msgPayload = "jetstream-test-message"
+ const msgCount = 100
+
+ // The source pods expect stream to exist
+ PumpJetstream(streamName, msgPayload, msgCount)
+
+ w := bss.Given().Pipeline("@testdata/jetstream-source-pipeline.yaml").
+ When().
+ CreatePipelineAndWait()
+ defer w.DeletePipelineAndWait()
+
+ // wait for all the pods to come up
+ w.Expect().VertexPodsRunning()
+
+ w.Expect().RedisSinkContains("jetstream-source-e2e-out", msgPayload, SinkCheckWithContainCount(msgCount))
+}
+
+func TestBuiltinSourceSuite(t *testing.T) {
+ suite.Run(t, new(BuiltinSourceSuite))
}
diff --git a/test/http-e2e/testdata/http-auth-fake-secret.yaml b/test/builtin-source-e2e/testdata/http-auth-fake-secret.yaml
similarity index 100%
rename from test/http-e2e/testdata/http-auth-fake-secret.yaml
rename to test/builtin-source-e2e/testdata/http-auth-fake-secret.yaml
diff --git a/test/http-e2e/testdata/http-source-with-auth.yaml b/test/builtin-source-e2e/testdata/http-source-with-auth.yaml
similarity index 100%
rename from test/http-e2e/testdata/http-source-with-auth.yaml
rename to test/builtin-source-e2e/testdata/http-source-with-auth.yaml
diff --git a/test/http-e2e/testdata/http-source.yaml b/test/builtin-source-e2e/testdata/http-source.yaml
similarity index 100%
rename from test/http-e2e/testdata/http-source.yaml
rename to test/builtin-source-e2e/testdata/http-source.yaml
diff --git a/test/jetstream-e2e/testdata/jetstream-source-pipeline.yaml b/test/builtin-source-e2e/testdata/jetstream-source-pipeline.yaml
similarity index 100%
rename from test/jetstream-e2e/testdata/jetstream-source-pipeline.yaml
rename to test/builtin-source-e2e/testdata/jetstream-source-pipeline.yaml
diff --git a/test/nats-e2e/testdata/nats-source-pipeline.yaml b/test/builtin-source-e2e/testdata/nats-source-pipeline.yaml
similarity index 100%
rename from test/nats-e2e/testdata/nats-source-pipeline.yaml
rename to test/builtin-source-e2e/testdata/nats-source-pipeline.yaml
diff --git a/test/e2e/functional_test.go b/test/e2e/functional_test.go
index 3e9c1d70dd..2d0989ac7e 100644
--- a/test/e2e/functional_test.go
+++ b/test/e2e/functional_test.go
@@ -1,6 +1,7 @@
+//go:build test
+
/*
Copyright 2022 The Numaproj Authors.
-
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -176,38 +177,6 @@ func (s *FunctionalSuite) TestUDFFiltering() {
w.Expect().RedisSinkNotContains("udf-filtering-out", expect2)
}
-func (s *FunctionalSuite) TestConditionalForwarding() {
- // FIXME: flaky when redis is used as isb
- if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
- s.T().SkipNow()
- }
-
- w := s.Given().Pipeline("@testdata/even-odd.yaml").
- When().
- CreatePipelineAndWait()
- defer w.DeletePipelineAndWait()
- pipelineName := "even-odd"
-
- // wait for all the pods to come up
- w.Expect().VertexPodsRunning()
-
- w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("888888"))).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("888889"))).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("not an integer")))
-
- w.Expect().RedisSinkContains("even-odd-even-sink", "888888")
- w.Expect().RedisSinkNotContains("even-odd-even-sink", "888889")
- w.Expect().RedisSinkNotContains("even-odd-even-sink", "not an integer")
-
- w.Expect().RedisSinkContains("even-odd-odd-sink", "888889")
- w.Expect().RedisSinkNotContains("even-odd-odd-sink", "888888")
- w.Expect().RedisSinkNotContains("even-odd-odd-sink", "not an integer")
-
- w.Expect().RedisSinkContains("even-odd-number-sink", "888888")
- w.Expect().RedisSinkContains("even-odd-number-sink", "888889")
- w.Expect().RedisSinkNotContains("even-odd-number-sink", "not an integer")
-}
-
func (s *FunctionalSuite) TestDropOnFull() {
// the drop on full feature is not supported with redis ISBSVC
diff --git a/test/e2e/testdata/even-odd.yaml b/test/e2e/testdata/even-odd.yaml
deleted file mode 100644
index 7cc6822243..0000000000
--- a/test/e2e/testdata/even-odd.yaml
+++ /dev/null
@@ -1,75 +0,0 @@
-apiVersion: numaflow.numaproj.io/v1alpha1
-kind: Pipeline
-metadata:
- name: even-odd
-spec:
- vertices:
- - name: in
- source:
- http: {}
- - name: even-or-odd
- partitions: 2
- udf:
- container:
- # Tell the input number is even or odd, see https://github.com/numaproj/numaflow-go/tree/main/pkg/mapper/examples/even_odd
- image: quay.io/numaio/numaflow-go/map-even-odd:stable
- imagePullPolicy: Always
- - name: even-sink
- partitions: 2
- sink:
- udsink:
- container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
- image: quay.io/numaio/numaflow-go/redis-sink:stable
- imagePullPolicy: Always
- env:
- - name: SINK_HASH_KEY
- # The key is set in the format of "pipeline_name-vertex_name"
- value: "even-odd-even-sink"
- - name: odd-sink
- sink:
- udsink:
- container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
- image: quay.io/numaio/numaflow-go/redis-sink:stable
- imagePullPolicy: Always
- env:
- - name: SINK_HASH_KEY
- # The key is set in the format of "pipeline_name-vertex_name"
- value: "even-odd-odd-sink"
- - name: number-sink
- sink:
- udsink:
- container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
- image: quay.io/numaio/numaflow-go/redis-sink:stable
- imagePullPolicy: Always
- env:
- - name: SINK_HASH_KEY
- # The key is set in the format of "pipeline_name-vertex_name"
- value: "even-odd-number-sink"
- edges:
- - from: in
- to: even-or-odd
- - from: even-or-odd
- to: even-sink
- conditions:
- tags:
- operator: or
- values:
- - even-tag
- - from: even-or-odd
- to: odd-sink
- conditions:
- tags:
- operator: or
- values:
- - odd-tag
- - from: even-or-odd
- to: number-sink
- conditions:
- tags:
- operator: or
- values:
- - odd-tag
- - even-tag
diff --git a/test/idle-source-e2e/idle_source_test.go b/test/idle-source-e2e/idle_source_test.go
index ef5e3eff98..cf9f6d8929 100644
--- a/test/idle-source-e2e/idle_source_test.go
+++ b/test/idle-source-e2e/idle_source_test.go
@@ -62,8 +62,6 @@ func (is *IdleSourceSuite) TestIdleKeyedReducePipelineWithHttpSource() {
// wait for all the pods to come up
w.Expect().VertexPodsRunning()
- defer w.StreamVertexPodlogs("sink", "udsink").TerminateAllPodLogs()
-
done := make(chan struct{})
go func() {
// publish messages to source vertex, with event time starting from 0
@@ -111,7 +109,6 @@ func (is *IdleSourceSuite) TestIdleKeyedReducePipelineWithKafkaSource() {
// wait for all the pods to come up
w.Expect().VertexPodsRunning()
- defer w.StreamVertexPodlogs("sink", "udsink").TerminateAllPodLogs()
defer DeleteKafkaTopic(topic)
diff --git a/test/jetstream-e2e/jetstream_test.go b/test/jetstream-e2e/jetstream_test.go
deleted file mode 100644
index 06aa24d05a..0000000000
--- a/test/jetstream-e2e/jetstream_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-//go:build test
-
-/*
-Copyright 2022 The Numaproj Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package jetstream_e2e
-
-import (
- "testing"
-
- "github.com/stretchr/testify/suite"
-
- "github.com/numaproj/numaflow/test/fixtures"
-)
-
-//go:generate kubectl -n numaflow-system delete statefulset nats --ignore-not-found=true
-//go:generate kubectl apply -k ../../config/apps/nats -n numaflow-system
-
-type JetstreamSuite struct {
- fixtures.E2ESuite
-}
-
-func (ns *JetstreamSuite) TestJetstreamSource() {
- const streamName = "test-stream"
- const msgPayload = "jetstream-test-message"
-
- // The source pods expects stream to exist
- fixtures.PumpJetstream(streamName, msgPayload, 100)
-
- w := ns.Given().Pipeline("@testdata/jetstream-source-pipeline.yaml").
- When().
- CreatePipelineAndWait()
- defer w.DeletePipelineAndWait()
-
- // wait for all the pods to come up
- w.Expect().VertexPodsRunning()
-
- w.Expect().RedisSinkContains("jetstream-source-e2e-out", msgPayload, fixtures.SinkCheckWithContainCount(100))
-}
-
-func TestJetstreamSuite(t *testing.T) {
- suite.Run(t, new(JetstreamSuite))
-}
diff --git a/test/kafka-e2e/kafka_test.go b/test/kafka-e2e/kafka_test.go
index c7c10a4df9..74d7fbd53c 100644
--- a/test/kafka-e2e/kafka_test.go
+++ b/test/kafka-e2e/kafka_test.go
@@ -24,7 +24,6 @@ import (
"github.com/stretchr/testify/suite"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/utils/ptr"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/test/fixtures"
@@ -39,64 +38,6 @@ type KafkaSuite struct {
fixtures.E2ESuite
}
-func (ks *KafkaSuite) TestKafkaSink() {
- topicName := fixtures.GenerateKafkaTopicName()
- fixtures.CreateKafkaTopic(topicName, 1)
- defer fixtures.DeleteKafkaTopic(topicName)
-
- pipeline := &dfv1.Pipeline{
- ObjectMeta: metav1.ObjectMeta{
- Name: "kafka-sink-e2e",
- },
- Spec: dfv1.PipelineSpec{
- Vertices: []dfv1.AbstractVertex{
- {
- Name: "input",
- Source: &dfv1.Source{
- Generator: &dfv1.GeneratorSource{
- RPU: ptr.To[int64](5),
- Duration: &metav1.Duration{Duration: 2 * time.Second},
- },
- },
- },
- {
- Name: "p1",
- UDF: &dfv1.UDF{
- Builtin: &dfv1.Function{Name: "cat"},
- },
- },
-
- {
- Name: "output",
- Sink: &dfv1.Sink{
- AbstractSink: dfv1.AbstractSink{
- Kafka: &dfv1.KafkaSink{
- Brokers: []string{"kafka-broker:9092"},
- Topic: topicName,
- },
- },
- },
- },
- },
- Edges: []dfv1.Edge{
- {
- From: "input",
- To: "p1",
- },
- {
- From: "p1",
- To: "output",
- },
- },
- },
- }
- w := ks.Given().WithPipeline(pipeline).
- When().
- CreatePipelineAndWait()
- defer w.DeletePipelineAndWait()
- fixtures.ExpectKafkaTopicCount(topicName, 15, 3*time.Second)
-}
-
func (ks *KafkaSuite) TestKafkaSourceSink() {
inputTopic := fixtures.GenerateKafkaTopicName()
fixtures.CreateKafkaTopic(inputTopic, 1)
@@ -105,7 +46,7 @@ func (ks *KafkaSuite) TestKafkaSourceSink() {
fixtures.CreateKafkaTopic(outputTopic, 1)
pipeline := &dfv1.Pipeline{
ObjectMeta: metav1.ObjectMeta{
- Name: "kafka-sink-e2e",
+ Name: "kafka-source-sink-e2e",
},
Spec: dfv1.PipelineSpec{
Vertices: []dfv1.AbstractVertex{
@@ -125,7 +66,6 @@ func (ks *KafkaSuite) TestKafkaSourceSink() {
Builtin: &dfv1.Function{Name: "cat"},
},
},
-
{
Name: "output",
Sink: &dfv1.Sink{
@@ -134,7 +74,6 @@ func (ks *KafkaSuite) TestKafkaSourceSink() {
Brokers: []string{"kafka-broker:9092"},
Topic: outputTopic,
},
- //Log: &dfv1.Log{},
},
},
},
diff --git a/test/sdks-e2e/sdks_test.go b/test/map-e2e/map_test.go
similarity index 50%
rename from test/sdks-e2e/sdks_test.go
rename to test/map-e2e/map_test.go
index a1bf013f41..a1d171a253 100644
--- a/test/sdks-e2e/sdks_test.go
+++ b/test/map-e2e/map_test.go
@@ -19,30 +19,23 @@ limitations under the License.
package sdks_e2e
import (
- "context"
- "fmt"
- "os"
- "strconv"
- "strings"
- "sync"
"testing"
- "time"
"github.com/stretchr/testify/suite"
. "github.com/numaproj/numaflow/test/fixtures"
)
-type SDKsSuite struct {
+type MapSuite struct {
E2ESuite
}
-func (s *SDKsSuite) TestUDFunctionAndSink() {
- w := s.Given().Pipeline("@testdata/flatmap.yaml").
+func (s *MapSuite) TestBatchMapUDFunctionAndSink() {
+ w := s.Given().Pipeline("@testdata/flatmap-batch.yaml").
When().
CreatePipelineAndWait()
defer w.DeletePipelineAndWait()
- pipelineName := "flatmap"
+ pipelineName := "flatmap-batch"
w.Expect().
VertexPodsRunning().
@@ -51,6 +44,8 @@ func (s *SDKsSuite) TestUDFunctionAndSink() {
VertexPodLogContains("go-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ VertexPodLogContains("rust-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ VertexPodLogContains("rust-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
@@ -59,17 +54,17 @@ func (s *SDKsSuite) TestUDFunctionAndSink() {
w.Expect().
VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
+ VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
+ VertexPodLogContains("rust-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
+ VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
}
-func (s *SDKsSuite) TestMapStreamUDFunctionAndSink() {
- w := s.Given().Pipeline("@testdata/flatmap-stream.yaml").
+func (s *MapSuite) TestUDFunctionAndSink() {
+ w := s.Given().Pipeline("@testdata/flatmap.yaml").
When().
CreatePipelineAndWait()
defer w.DeletePipelineAndWait()
-
- pipelineName := "flatmap-stream"
+ pipelineName := "flatmap"
w.Expect().
VertexPodsRunning().
@@ -81,25 +76,22 @@ func (s *SDKsSuite) TestMapStreamUDFunctionAndSink() {
VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
- w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello,hello,hello"))).
+ w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello,hello"))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello")))
w.Expect().
- VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
- w.Expect().
- VertexPodLogContains("go-udsink-2", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
- w.Expect().
- VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
- w.Expect().
- VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
+ VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
+ VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
+ VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
}
-func (s *SDKsSuite) TestBatchMapUDFunctionAndSink() {
- w := s.Given().Pipeline("@testdata/flatmap-batch.yaml").
+func (s *MapSuite) TestMapStreamUDFunctionAndSink() {
+ w := s.Given().Pipeline("@testdata/flatmap-stream.yaml").
When().
CreatePipelineAndWait()
defer w.DeletePipelineAndWait()
- pipelineName := "flatmap-batch"
+
+ pipelineName := "flatmap-stream"
w.Expect().
VertexPodsRunning().
@@ -108,132 +100,22 @@ func (s *SDKsSuite) TestBatchMapUDFunctionAndSink() {
VertexPodLogContains("go-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("rust-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("rust-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
- w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello,hello"))).
+ w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello,hello,hello"))).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("hello")))
w.Expect().
- VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("rust-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
-}
-
-func (s *SDKsSuite) TestReduceSDK() {
-
- // the reduce feature is not supported with redis ISBSVC
- if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
- s.T().SkipNow()
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
- defer cancel()
- w := s.Given().Pipeline("@testdata/simple-keyed-reduce-pipeline.yaml").
- When().
- CreatePipelineAndWait()
- defer w.DeletePipelineAndWait()
- pipelineName := "even-odd-sum"
-
- // wait for all the pods to come up
- w.Expect().VertexPodsRunning()
-
- done := make(chan struct{})
- go func() {
- // publish messages to source vertex, with event time starting from 60000
- startTime := 60000
- for i := 0; true; i++ {
- select {
- case <-ctx.Done():
- return
- case <-done:
- return
- default:
- eventTime := strconv.Itoa(startTime + i*1000)
- w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("1")).WithHeader("X-Numaflow-Event-Time", eventTime)).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("2")).WithHeader("X-Numaflow-Event-Time", eventTime)).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("3")).WithHeader("X-Numaflow-Event-Time", eventTime))
- }
- }
- }()
-
+ VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
w.Expect().
- VertexPodLogContains("java-udsink", "120", PodLogCheckOptionWithContainer("udsink")).
- VertexPodLogContains("java-udsink", "240", PodLogCheckOptionWithContainer("udsink"))
- done <- struct{}{}
-}
-
-func (s *SDKsSuite) TestSourceTransformer() {
-
- // the transformer feature is not supported with redis ISBSVC
- if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
- s.T().SkipNow()
- }
-
- var wg sync.WaitGroup
- wg.Add(4)
- go func() {
- defer wg.Done()
- s.testSourceTransformer("python")
- }()
- go func() {
- defer wg.Done()
- s.testSourceTransformer("java")
- }()
- go func() {
- defer wg.Done()
- s.testSourceTransformer("go")
- }()
- go func() {
- defer wg.Done()
- s.testSourceTransformer("rust")
- }()
- wg.Wait()
-}
-
-func (s *SDKsSuite) testSourceTransformer(lang string) {
- w := s.Given().Pipeline(fmt.Sprintf("@testdata/transformer/event-time-filter-%s.yaml", lang)).
- When().
- CreatePipelineAndWait()
- defer w.DeletePipelineAndWait()
- pipelineName := fmt.Sprintf("event-time-filter-%s", lang)
-
- // wait for all the pods to come up
- w.Expect().VertexPodsRunning()
-
- eventTimeBefore2022_1 := strconv.FormatInt(time.Date(2021, 4, 2, 7, 4, 5, 2, time.UTC).UnixMilli(), 10)
- eventTimeBefore2022_2 := strconv.FormatInt(time.Date(1998, 4, 2, 8, 4, 5, 2, time.UTC).UnixMilli(), 10)
- eventTimeBefore2022_3 := strconv.FormatInt(time.Date(2013, 4, 4, 7, 4, 5, 2, time.UTC).UnixMilli(), 10)
-
- eventTimeAfter2022_1 := strconv.FormatInt(time.Date(2023, 4, 2, 7, 4, 5, 2, time.UTC).UnixMilli(), 10)
- eventTimeAfter2022_2 := strconv.FormatInt(time.Date(2026, 4, 2, 3, 4, 5, 2, time.UTC).UnixMilli(), 10)
-
- eventTimeWithin2022_1 := strconv.FormatInt(time.Date(2022, 4, 2, 3, 4, 5, 2, time.UTC).UnixMilli(), 10)
-
- w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Before2022")).WithHeader("X-Numaflow-Event-Time", eventTimeBefore2022_1)).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Before2022")).WithHeader("X-Numaflow-Event-Time", eventTimeBefore2022_2)).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Before2022")).WithHeader("X-Numaflow-Event-Time", eventTimeBefore2022_3)).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("After2022")).WithHeader("X-Numaflow-Event-Time", eventTimeAfter2022_1)).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("After2022")).WithHeader("X-Numaflow-Event-Time", eventTimeAfter2022_2)).
- SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Within2022")).WithHeader("X-Numaflow-Event-Time", eventTimeWithin2022_1))
-
- janFirst2022 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
- janFirst2023 := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
-
- w.Expect().VertexPodLogContains("sink-within-2022", fmt.Sprintf("EventTime - %d", janFirst2022.UnixMilli()), PodLogCheckOptionWithCount(1)).
- VertexPodLogContains("sink-after-2022", fmt.Sprintf("EventTime - %d", janFirst2023.UnixMilli()), PodLogCheckOptionWithCount(2)).
- VertexPodLogContains("sink-all", fmt.Sprintf("EventTime - %d", janFirst2022.UnixMilli()), PodLogCheckOptionWithCount(1)).
- VertexPodLogContains("sink-all", fmt.Sprintf("EventTime - %d", janFirst2023.UnixMilli()), PodLogCheckOptionWithCount(2)).
- VertexPodLogNotContains("sink-within-2022", fmt.Sprintf("EventTime - %d", janFirst2023.UnixMilli()), PodLogCheckOptionWithTimeout(1*time.Second)).
- VertexPodLogNotContains("sink-after-2022", fmt.Sprintf("EventTime - %d", janFirst2022.UnixMilli()), PodLogCheckOptionWithTimeout(1*time.Second)).
- VertexPodLogNotContains("sink-all", "Before2022", PodLogCheckOptionWithTimeout(1*time.Second)).
- VertexPodLogNotContains("sink-within-2022", "Before2022", PodLogCheckOptionWithTimeout(1*time.Second)).
- VertexPodLogNotContains("sink-after-2022", "Before2022", PodLogCheckOptionWithTimeout(1*time.Second))
+ VertexPodLogContains("go-udsink-2", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
+ w.Expect().
+ VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
+ w.Expect().
+ VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
}
-func TestHTTPSuite(t *testing.T) {
- suite.Run(t, new(SDKsSuite))
+func TestMapSuite(t *testing.T) {
+ suite.Run(t, new(MapSuite))
}
diff --git a/test/sdks-e2e/testdata/flatmap-batch.yaml b/test/map-e2e/testdata/flatmap-batch.yaml
similarity index 100%
rename from test/sdks-e2e/testdata/flatmap-batch.yaml
rename to test/map-e2e/testdata/flatmap-batch.yaml
diff --git a/test/sdks-e2e/testdata/flatmap-stream.yaml b/test/map-e2e/testdata/flatmap-stream.yaml
similarity index 100%
rename from test/sdks-e2e/testdata/flatmap-stream.yaml
rename to test/map-e2e/testdata/flatmap-stream.yaml
diff --git a/test/sdks-e2e/testdata/flatmap.yaml b/test/map-e2e/testdata/flatmap.yaml
similarity index 100%
rename from test/sdks-e2e/testdata/flatmap.yaml
rename to test/map-e2e/testdata/flatmap.yaml
diff --git a/test/monovertex-e2e/monovertex_test.go b/test/monovertex-e2e/monovertex_test.go
index 51d9135c56..3fd72d6554 100644
--- a/test/monovertex-e2e/monovertex_test.go
+++ b/test/monovertex-e2e/monovertex_test.go
@@ -1,3 +1,21 @@
+//go:build test
+
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
package monovertex_e2e
import (
diff --git a/test/nats-e2e/nats_test.go b/test/nats-e2e/nats_test.go
deleted file mode 100644
index 72d2c70e99..0000000000
--- a/test/nats-e2e/nats_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-//go:build test
-
-/*
-Copyright 2022 The Numaproj Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package nats_e2e
-
-import (
- "testing"
- "time"
-
- "github.com/stretchr/testify/suite"
-
- . "github.com/numaproj/numaflow/test/fixtures"
-)
-
-//go:generate kubectl -n numaflow-system delete statefulset nats --ignore-not-found=true
-//go:generate kubectl apply -k ../../config/apps/nats -n numaflow-system
-
-type NatsSuite struct {
- E2ESuite
-}
-
-func (ns *NatsSuite) TestNatsSource() {
- subject := "test-subject"
- w := ns.Given().Pipeline("@testdata/nats-source-pipeline.yaml").
- When().
- CreatePipelineAndWait()
- defer w.DeletePipelineAndWait()
-
- // wait for all the pods to come up
- w.Expect().VertexPodsRunning()
-
- PumpNatsSubject(subject, 100, 20*time.Millisecond, 10, "test-message")
- w.Expect().RedisSinkContains("nats-source-e2e-out", "test-message", SinkCheckWithContainCount(100))
-}
-
-func TestNatsSuite(t *testing.T) {
- suite.Run(t, new(NatsSuite))
-}
diff --git a/test/reduce-one-e2e/reduce_one_test.go b/test/reduce-one-e2e/reduce_one_test.go
index 4cf592e1cf..618913a829 100644
--- a/test/reduce-one-e2e/reduce_one_test.go
+++ b/test/reduce-one-e2e/reduce_one_test.go
@@ -1,3 +1,5 @@
+//go:build test
+
/*
Copyright 2022 The Numaproj Authors.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,7 +36,6 @@ type ReduceSuite struct {
// one reduce vertex (keyed)
func (r *ReduceSuite) TestSimpleKeyedReducePipeline() {
-
// the reduce feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
r.T().SkipNow()
@@ -79,7 +80,6 @@ func (r *ReduceSuite) TestSimpleKeyedReducePipeline() {
// one reduce vertex(non keyed)
func (r *ReduceSuite) TestSimpleNonKeyedReducePipeline() {
-
// the reduce feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
r.T().SkipNow()
@@ -123,7 +123,6 @@ func (r *ReduceSuite) TestSimpleNonKeyedReducePipeline() {
// two reduce vertex(keyed and non keyed)
func (r *ReduceSuite) TestComplexReducePipelineKeyedNonKeyed() {
-
// the reduce feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
r.T().SkipNow()
@@ -166,7 +165,6 @@ func (r *ReduceSuite) TestComplexReducePipelineKeyedNonKeyed() {
}
func (r *ReduceSuite) TestSimpleReducePipelineFailOverUsingWAL() {
-
// the reduce feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
r.T().SkipNow()
@@ -193,7 +191,6 @@ func (r *ReduceSuite) TestSimpleReducePipelineFailOverUsingWAL() {
w.Expect().VertexPodsRunning()
- defer w.StreamVertexPodlogs("compute-sum", "numa").TerminateAllPodLogs()
go func() {
startTime := int(time.Unix(1000, 0).UnixMilli())
for i := 1; true; i++ {
@@ -209,7 +206,6 @@ func (r *ReduceSuite) TestSimpleReducePipelineFailOverUsingWAL() {
w.Expect().VertexPodsRunning()
w.Exec("/bin/sh", []string{"-c", args}, CheckPodKillSucceeded)
w.Expect().VertexPodsRunning()
- w.StreamVertexPodlogs("compute-sum", "numa")
}
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("1")).WithHeader("X-Numaflow-Event-Time", eventTime)).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("2")).WithHeader("X-Numaflow-Event-Time", eventTime)).
@@ -229,7 +225,6 @@ func (r *ReduceSuite) TestSimpleReducePipelineFailOverUsingWAL() {
// two reduce vertices (keyed and non-keyed) followed by a sliding window vertex
func (r *ReduceSuite) TestComplexSlidingWindowPipeline() {
-
// the reduce feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
r.T().SkipNow()
diff --git a/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml b/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
index 25e1976331..c770092617 100644
--- a/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
@@ -25,8 +25,9 @@ spec:
partitions: 1
udf:
container:
- # Compute the sum, see https://github.com/numaproj/numaflow-go/tree/main/pkg/reducer/examples/sum
- image: quay.io/numaio/numaflow-go/reduce-sum:stable
+ # compute the sum, see https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/reduce/sum
+ # this also serves as a Java reduce sdk test case
+ image: quay.io/numaio/numaflow-java/reduce-sum:stable
imagePullPolicy: Always
groupBy:
window:
diff --git a/test/reduce-two-e2e/reduce_two_test.go b/test/reduce-two-e2e/reduce_two_test.go
index 7a1815f2c2..c4749569a0 100644
--- a/test/reduce-two-e2e/reduce_two_test.go
+++ b/test/reduce-two-e2e/reduce_two_test.go
@@ -61,8 +61,6 @@ func (r *ReduceSuite) testReduceStream(lang string) {
// wait for all the pods to come up
w.Expect().VertexPodsRunning()
- defer w.StreamVertexPodlogs("sink", "udsink").TerminateAllPodLogs()
-
done := make(chan struct{})
go func() {
// publish messages to source vertex, with event time starting from 60000
@@ -89,53 +87,6 @@ func (r *ReduceSuite) testReduceStream(lang string) {
done <- struct{}{}
}
-func (r *ReduceSuite) TestSimpleSessionPipeline() {
-
- // the reduce feature is not supported with redis ISBSVC
- if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
- r.T().SkipNow()
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
- defer cancel()
- w := r.Given().Pipeline("@testdata/session-reduce/simple-session-sum-pipeline.yaml").
- When().
- CreatePipelineAndWait()
- defer w.DeletePipelineAndWait()
- pipelineName := "simple-session-sum"
-
- // wait for all the pods to come up
- w.Expect().VertexPodsRunning()
-
- count := 0
- done := make(chan struct{})
- go func() {
- // publish messages to source vertex, with event time starting from 60000
- startTime := 60000
- for i := 0; true; i++ {
- select {
- case <-ctx.Done():
- return
- case <-done:
- return
- default:
- if count == 10 {
- startTime = startTime + (10 * 1000)
- count = 0
- } else {
- startTime = startTime + 1000
- }
- eventTime := strconv.Itoa(startTime)
- w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("100")).WithHeader("X-Numaflow-Event-Time", eventTime))
- count += 1
- }
- }
- }()
-
- w.Expect().RedisSinkContains("simple-session-sum-sink", "1000")
- done <- struct{}{}
-}
-
func (r *ReduceSuite) TestSimpleSessionKeyedPipelineGo() {
r.testSimpleSessionKeyedPipeline("go")
}
@@ -145,7 +96,6 @@ func (r *ReduceSuite) TestSimpleSessionKeyedPipelineJava() {
}
func (r *ReduceSuite) testSimpleSessionKeyedPipeline(lang string) {
-
// the reduce feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
r.T().SkipNow()
@@ -162,8 +112,6 @@ func (r *ReduceSuite) testSimpleSessionKeyedPipeline(lang string) {
// wait for all the pods to come up
w.Expect().VertexPodsRunning()
- defer w.StreamVertexPodlogs("sink", "udsink").TerminateAllPodLogs()
-
count := 0
done := make(chan struct{})
go func() {
@@ -199,7 +147,6 @@ func (r *ReduceSuite) testSimpleSessionKeyedPipeline(lang string) {
}
func (r *ReduceSuite) TestSimpleSessionPipelineFailOverUsingWAL() {
-
// the reduce feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
r.T().SkipNow()
@@ -219,7 +166,6 @@ func (r *ReduceSuite) TestSimpleSessionPipelineFailOverUsingWAL() {
args := "kubectl delete po -n numaflow-system -l " +
"numaflow.numaproj.io/pipeline-name=simple-session-counter-go,numaflow.numaproj.io/vertex-name=compute-count"
- defer w.StreamVertexPodlogs("compute-count", "numa").TerminateAllPodLogs()
// Kill the reducer pods before processing to trigger failover.
w.Exec("/bin/sh", []string{"-c", args}, CheckPodKillSucceeded)
done := make(chan struct{})
@@ -245,7 +191,6 @@ func (r *ReduceSuite) TestSimpleSessionPipelineFailOverUsingWAL() {
w.Expect().VertexPodsRunning()
w.Exec("/bin/sh", []string{"-c", args}, CheckPodKillSucceeded)
w.Expect().VertexPodsRunning()
- w.StreamVertexPodlogs("compute-sum", "numa")
}
w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("1")).WithHeader("X-Numaflow-Event-Time", eventTime)).
SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("2")).WithHeader("X-Numaflow-Event-Time", eventTime))
diff --git a/test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml b/test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml
deleted file mode 100644
index 6f1259e085..0000000000
--- a/test/reduce-two-e2e/testdata/session-reduce/simple-session-sum-pipeline.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-apiVersion: numaflow.numaproj.io/v1alpha1
-kind: Pipeline
-metadata:
- name: simple-session-sum
-spec:
- watermark:
- maxDelay: 30s
- vertices:
- - name: in
- scale:
- min: 1
- source:
- http: {}
- - name: compute-sum
- partitions: 1
- udf:
- container:
- # https://github.com/numaproj/numaflow-go/tree/main/pkg/sessionreducer/examples/sum
- image: quay.io/numaio/numaflow-go/session-sum:stable
- imagePullPolicy: Always
- groupBy:
- window:
- session:
- timeout: 10s
- keyed: true
- storage:
- persistentVolumeClaim:
- volumeSize: 2Gi
- accessMode: ReadWriteOnce
- - name: sink
- scale:
- min: 1
- sink:
- udsink:
- container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
- image: quay.io/numaio/numaflow-go/redis-sink:stable
- imagePullPolicy: Always
- env:
- - name: SINK_HASH_KEY
- # The key is set in the format of "pipeline_name-vertex_name"
- value: "simple-session-sum-sink"
- edges:
- - from: in
- to: compute-sum
- - from: compute-sum
- to: sink
diff --git a/test/sdks-e2e/README.md b/test/sdks-e2e/README.md
deleted file mode 100644
index 24451e9c61..0000000000
--- a/test/sdks-e2e/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# SDKs Test
-
-Each SDK is supposed to implement:
-
-1. A `flatmap` UDF example, which splits the input message with `,`, and return a list. Build a docker image `quay.io/numaio/numaflow-${language}/map-flatmap:latest` and push to `quay.io`;
-
-2. A `log` UDSink example, which prints out original message in pod logs. Build a docker image `quay.io/numaio/numaflow-${language}/sink-log:latest` and push to `quay.io`.
diff --git a/test/sdks-e2e/testdata/simple-keyed-reduce-pipeline.yaml b/test/sdks-e2e/testdata/simple-keyed-reduce-pipeline.yaml
deleted file mode 100644
index 24f536d75d..0000000000
--- a/test/sdks-e2e/testdata/simple-keyed-reduce-pipeline.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-apiVersion: numaflow.numaproj.io/v1alpha1
-kind: Pipeline
-metadata:
- name: even-odd-sum
-spec:
- vertices:
- - name: in
- source:
- http: {}
- - name: atoi
- scale:
- min: 1
- udf:
- container:
- # Tell the input number is even or odd, see https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/map/evenodd
- image: quay.io/numaio/numaflow-java/even-odd:stable
- imagePullPolicy: Always
- - name: compute-sum
- partitions: 2
- udf:
- container:
- # compute the sum, see https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/reduce/sum
- image: quay.io/numaio/numaflow-java/reduce-sum:stable
- imagePullPolicy: Always
- groupBy:
- window:
- fixed:
- length: 60s
- keyed: true
- storage:
- emptyDir: {}
- - name: java-udsink
- scale:
- min: 1
- sink:
- udsink:
- container:
- # https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/sink/simple
- image: quay.io/numaio/numaflow-java/simple-sink:stable
- imagePullPolicy: Always
- edges:
- - from: in
- to: atoi
- - from: atoi
- to: compute-sum
- - from: compute-sum
- to: java-udsink
diff --git a/test/sideinputs-e2e/sideinput-e2e_sink_source_test.go b/test/sideinputs-e2e/sideinput_sink_source_test.go
similarity index 99%
rename from test/sideinputs-e2e/sideinput-e2e_sink_source_test.go
rename to test/sideinputs-e2e/sideinput_sink_source_test.go
index be95e4c3f5..9278214ef8 100644
--- a/test/sideinputs-e2e/sideinput-e2e_sink_source_test.go
+++ b/test/sideinputs-e2e/sideinput_sink_source_test.go
@@ -51,7 +51,6 @@ func (s *SideInputUDSSuite) TestSinkWithSideInput() {
}
func (s *SideInputUDSSuite) TestSourceWithSideInput() {
-
// the side inputs feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
s.T().SkipNow()
@@ -60,7 +59,6 @@ func (s *SideInputUDSSuite) TestSourceWithSideInput() {
w := s.setUpTests("@testdata/sideinput-source.yaml")
defer w.DeletePipelineAndWait()
w.Expect().RedisSinkContains("sideinput-source-test-redis-uds", "e2e-even", SinkCheckWithTimeout(2*time.Minute))
-
}
func TestSideInputUDSSuite(t *testing.T) {
diff --git a/test/sideinputs-e2e/sideinput_test.go b/test/sideinputs-e2e/sideinput_test.go
index 8f1e156926..c67e1b2cbe 100644
--- a/test/sideinputs-e2e/sideinput_test.go
+++ b/test/sideinputs-e2e/sideinput_test.go
@@ -35,7 +35,6 @@ type SideInputSuite struct {
}
func (s *SideInputSuite) TestSimpleMapSideInputPipeline() {
-
// the side inputs feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
s.T().SkipNow()
@@ -77,7 +76,6 @@ func (s *SideInputSuite) TestSimpleMapSideInputPipeline() {
}
func (s *SideInputSuite) TestSimpleReduceSideInputPipeline() {
-
// the side inputs feature is not supported with redis ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
s.T().SkipNow()
diff --git a/test/sdks-e2e/testdata/transformer/event-time-filter-go.yaml b/test/transformer-e2e/testdata/event-time-filter-go.yaml
similarity index 100%
rename from test/sdks-e2e/testdata/transformer/event-time-filter-go.yaml
rename to test/transformer-e2e/testdata/event-time-filter-go.yaml
diff --git a/test/sdks-e2e/testdata/transformer/event-time-filter-java.yaml b/test/transformer-e2e/testdata/event-time-filter-java.yaml
similarity index 100%
rename from test/sdks-e2e/testdata/transformer/event-time-filter-java.yaml
rename to test/transformer-e2e/testdata/event-time-filter-java.yaml
diff --git a/test/sdks-e2e/testdata/transformer/event-time-filter-python.yaml b/test/transformer-e2e/testdata/event-time-filter-python.yaml
similarity index 100%
rename from test/sdks-e2e/testdata/transformer/event-time-filter-python.yaml
rename to test/transformer-e2e/testdata/event-time-filter-python.yaml
diff --git a/test/sdks-e2e/testdata/transformer/event-time-filter-rust.yaml b/test/transformer-e2e/testdata/event-time-filter-rust.yaml
similarity index 100%
rename from test/sdks-e2e/testdata/transformer/event-time-filter-rust.yaml
rename to test/transformer-e2e/testdata/event-time-filter-rust.yaml
diff --git a/test/transformer-e2e/transformer_test.go b/test/transformer-e2e/transformer_test.go
index 888b3ee3fb..55b88f3683 100644
--- a/test/transformer-e2e/transformer_test.go
+++ b/test/transformer-e2e/transformer_test.go
@@ -23,7 +23,9 @@ import (
"encoding/json"
"fmt"
"os"
+ "strconv"
"strings"
+ "sync"
"testing"
"time"
@@ -88,7 +90,6 @@ func (s *TransformerSuite) TestTimeExtractionFilter() {
}
func (s *TransformerSuite) TestBuiltinEventTimeExtractor() {
-
// this test is skipped for redis as watermark is not supported with this ISBSVC
if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
s.T().SkipNow()
@@ -165,6 +166,73 @@ wmLoop:
done <- struct{}{}
}
+func (s *TransformerSuite) TestSourceTransformer() {
+ // the transformer feature is not supported with redis ISBSVC
+ if strings.ToUpper(os.Getenv("ISBSVC")) == "REDIS" {
+ s.T().SkipNow()
+ }
+
+ var wg sync.WaitGroup
+ wg.Add(4)
+ go func() {
+ defer wg.Done()
+ s.testSourceTransformer("python")
+ }()
+ go func() {
+ defer wg.Done()
+ s.testSourceTransformer("java")
+ }()
+ go func() {
+ defer wg.Done()
+ s.testSourceTransformer("go")
+ }()
+ go func() {
+ defer wg.Done()
+ s.testSourceTransformer("rust")
+ }()
+ wg.Wait()
+}
+
+func (s *TransformerSuite) testSourceTransformer(lang string) {
+ w := s.Given().Pipeline(fmt.Sprintf("@testdata/event-time-filter-%s.yaml", lang)).
+ When().
+ CreatePipelineAndWait()
+ defer w.DeletePipelineAndWait()
+ pipelineName := fmt.Sprintf("event-time-filter-%s", lang)
+
+ // wait for all the pods to come up
+ w.Expect().VertexPodsRunning()
+
+ eventTimeBefore2022_1 := strconv.FormatInt(time.Date(2021, 4, 2, 7, 4, 5, 2, time.UTC).UnixMilli(), 10)
+ eventTimeBefore2022_2 := strconv.FormatInt(time.Date(1998, 4, 2, 8, 4, 5, 2, time.UTC).UnixMilli(), 10)
+ eventTimeBefore2022_3 := strconv.FormatInt(time.Date(2013, 4, 4, 7, 4, 5, 2, time.UTC).UnixMilli(), 10)
+
+ eventTimeAfter2022_1 := strconv.FormatInt(time.Date(2023, 4, 2, 7, 4, 5, 2, time.UTC).UnixMilli(), 10)
+ eventTimeAfter2022_2 := strconv.FormatInt(time.Date(2026, 4, 2, 3, 4, 5, 2, time.UTC).UnixMilli(), 10)
+
+ eventTimeWithin2022_1 := strconv.FormatInt(time.Date(2022, 4, 2, 3, 4, 5, 2, time.UTC).UnixMilli(), 10)
+
+ w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Before2022")).WithHeader("X-Numaflow-Event-Time", eventTimeBefore2022_1)).
+ SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Before2022")).WithHeader("X-Numaflow-Event-Time", eventTimeBefore2022_2)).
+ SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Before2022")).WithHeader("X-Numaflow-Event-Time", eventTimeBefore2022_3)).
+ SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("After2022")).WithHeader("X-Numaflow-Event-Time", eventTimeAfter2022_1)).
+ SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("After2022")).WithHeader("X-Numaflow-Event-Time", eventTimeAfter2022_2)).
+ SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("Within2022")).WithHeader("X-Numaflow-Event-Time", eventTimeWithin2022_1))
+
+ janFirst2022 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
+ janFirst2023 := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
+
+ w.Expect().VertexPodLogContains("sink-within-2022", fmt.Sprintf("EventTime - %d", janFirst2022.UnixMilli()), PodLogCheckOptionWithCount(1)).
+ VertexPodLogContains("sink-after-2022", fmt.Sprintf("EventTime - %d", janFirst2023.UnixMilli()), PodLogCheckOptionWithCount(2)).
+ VertexPodLogContains("sink-all", fmt.Sprintf("EventTime - %d", janFirst2022.UnixMilli()), PodLogCheckOptionWithCount(1)).
+ VertexPodLogContains("sink-all", fmt.Sprintf("EventTime - %d", janFirst2023.UnixMilli()), PodLogCheckOptionWithCount(2)).
+ VertexPodLogNotContains("sink-within-2022", fmt.Sprintf("EventTime - %d", janFirst2023.UnixMilli()), PodLogCheckOptionWithTimeout(1*time.Second)).
+ VertexPodLogNotContains("sink-after-2022", fmt.Sprintf("EventTime - %d", janFirst2022.UnixMilli()), PodLogCheckOptionWithTimeout(1*time.Second)).
+ VertexPodLogNotContains("sink-all", "Before2022", PodLogCheckOptionWithTimeout(1*time.Second)).
+ VertexPodLogNotContains("sink-within-2022", "Before2022", PodLogCheckOptionWithTimeout(1*time.Second)).
+ VertexPodLogNotContains("sink-after-2022", "Before2022", PodLogCheckOptionWithTimeout(1*time.Second))
+}
+
func TestTransformerSuite(t *testing.T) {
suite.Run(t, new(TransformerSuite))
}
From ef18e092b6bec602080a4c27301e8c24e998691c Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Mon, 19 Aug 2024 18:19:36 -0400
Subject: [PATCH 012/188] chore: version update for releasing (#1944)
Signed-off-by: Keran Yang
---
go.mod | 2 +-
go.sum | 4 ++--
pkg/sdkclient/serverinfo/versions.go | 6 +++---
test/sideinputs-e2e/testdata/sideinput-sink.yaml | 2 +-
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/go.mod b/go.mod
index 2560e8dc81..f0dd236bd7 100644
--- a/go.mod
+++ b/go.mod
@@ -32,7 +32,7 @@ require (
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe
github.com/nats-io/nats-server/v2 v2.10.17
github.com/nats-io/nats.go v1.36.0
- github.com/numaproj/numaflow-go v0.7.1-0.20240711051731-15e45210b784
+ github.com/numaproj/numaflow-go v0.8.0
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_model v0.5.0
github.com/prometheus/common v0.45.0
diff --git a/go.sum b/go.sum
index eb4d23b5cf..1e46c54600 100644
--- a/go.sum
+++ b/go.sum
@@ -485,8 +485,8 @@ github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDm
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/numaproj/numaflow-go v0.7.1-0.20240711051731-15e45210b784 h1:JnpaG557KqDrzIi1c5YeffeLXGmLd8F2lzQEBC+wFWQ=
-github.com/numaproj/numaflow-go v0.7.1-0.20240711051731-15e45210b784/go.mod h1:WoMt31+h3up202zTRI8c/qe42B8UbvwLe2mJH0MAlhI=
+github.com/numaproj/numaflow-go v0.8.0 h1:1Pp0AMLXkmUPlvFjKeY3a9X+OLU8oN1OQWxD9jLg8Uo=
+github.com/numaproj/numaflow-go v0.8.0/go.mod h1:WoMt31+h3up202zTRI8c/qe42B8UbvwLe2mJH0MAlhI=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
diff --git a/pkg/sdkclient/serverinfo/versions.go b/pkg/sdkclient/serverinfo/versions.go
index 9a9c1cb02f..aded1c9bd3 100644
--- a/pkg/sdkclient/serverinfo/versions.go
+++ b/pkg/sdkclient/serverinfo/versions.go
@@ -23,7 +23,7 @@ import (
type sdkConstraints map[info.Language]string
var minimumSupportedSDKVersions = sdkConstraints{
- info.Go: "0.7.0-rc2",
- info.Python: "0.7.0a1",
- info.Java: "0.7.2-0",
+ info.Go: "0.8.0",
+ info.Python: "0.8.0",
+ info.Java: "0.8.0",
}
diff --git a/test/sideinputs-e2e/testdata/sideinput-sink.yaml b/test/sideinputs-e2e/testdata/sideinput-sink.yaml
index f172b4ec31..086254a3e7 100644
--- a/test/sideinputs-e2e/testdata/sideinput-sink.yaml
+++ b/test/sideinputs-e2e/testdata/sideinput-sink.yaml
@@ -26,7 +26,7 @@ spec:
udsink:
container:
# see https://github.com/numaproj/numaflow-go/tree/main/pkg/sideinput/examples/sink_sideinput
- image: quay.io/numaio/numaflow-go/redis-sink-with-sideinput:hash
+ image: quay.io/numaio/numaflow-go/redis-sink-with-sideinput:stable
imagePullPolicy: Always
env:
- name: SINK_HASH_KEY
From 216cbd20d5917ecdb655a93295b44c6181944eb0 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 19 Aug 2024 16:43:42 -0700
Subject: [PATCH 013/188] docs: updated CHANGELOG.md (#1968)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
CHANGELOG.md | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b0b83f0f4..0b2f607fbc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
# Changelog
+## v1.3.0 (2024-08-19)
+
+ * [4de121c2](https://github.com/numaproj/numaflow/commit/4de121c2c3b436ac51fba97c8ce5153afc5364c9) Update manifests to v1.3.0
+ * [a566548b](https://github.com/numaproj/numaflow/commit/a566548b8d1be71367dc01049086b5a685b610eb) refactor: re-arrange e2e tests (#1961)
+ * [a30649de](https://github.com/numaproj/numaflow/commit/a30649decdf381950e54187c1633b0e27fe85cff) fix: replicas derived in UI from mvtx status instead of spec (#1965)
+ * [5a22c321](https://github.com/numaproj/numaflow/commit/5a22c321912df0a5e4d59d8027a1173acfe0079c) feat: add health for monovertex (#1954)
+ * [1087e860](https://github.com/numaproj/numaflow/commit/1087e860c2896a861fb5068f9416dac39a948b30) feat: enable fallback sink for mvtx (#1957)
+ * [b5aa6ffb](https://github.com/numaproj/numaflow/commit/b5aa6ffba0466f87727354d6a069e8d6fb8e07ba) feat: Mono vertex UI (#1941)
+ * [447cd3f4](https://github.com/numaproj/numaflow/commit/447cd3f47cfaf7856c191ee27815fc338cea1cf3) fix: default resources mutated when applying templates (#1948)
+ * [5a531620](https://github.com/numaproj/numaflow/commit/5a53162077305cea43e6bf9d23dd19805c8c8bb4) feat: autoscaling for MonoVertex (#1927)
+ * [78468019](https://github.com/numaproj/numaflow/commit/78468019b0a292749ab688b4a74af1149b43d540) fix: retry failed messages for MonoVertex sink (#1933)
+ * [206a535f](https://github.com/numaproj/numaflow/commit/206a535f7e86f860ed5597616b0e3b1d9ab93ec0) Add Lockheed to Users.md (#1934)
+ * [c1d25acd](https://github.com/numaproj/numaflow/commit/c1d25acd0d1722c6011ada6ccff06cd5dc8812be) feat: add server-info support and versioning to MonoVertex (#1918)
+ * [292e3eae](https://github.com/numaproj/numaflow/commit/292e3eae4537c6d497f1eb2de5f72d3f657b4360) feat: source to sink with an optional transformer without ISB (#1904)
+
+### Contributors
+
+ * Derek Wang
+ * Keran Yang
+ * Sidhant Kohli
+ * Vedant Gupta
+ * Vigith Maurice
+ * Yashash H L
+ * mdwarne1
+
## v1.3.0-rc1 (2024-08-08)
* [179f5967](https://github.com/numaproj/numaflow/commit/179f59674a0a61eb7ae7cd7a83612f0eb7b3be7f) Update manifests to v1.3.0-rc1
From a7074aa80345e41c39770e7d069e14c29eaff9e0 Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Mon, 19 Aug 2024 20:29:19 -0700
Subject: [PATCH 014/188] doc: update roadmap (#1970)
Signed-off-by: Vigith Maurice
---
README.md | 2 +-
docs/README.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 96f7fb5cb1..be45ad6409 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ Numaflow, created by the Intuit Argo team to address community needs for continu
## Roadmap
-- Map Streaming (1.3)
+- Mono Vertex to bypass ISB for simple use cases (1.4)
## Demo
diff --git a/docs/README.md b/docs/README.md
index 577a2afc5e..6388ec5326 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -26,7 +26,7 @@ Welcome to Numaflow! A Kubernetes-native, serverless platform for running scalab
## Roadmap
-- Map Streaming (1.3)
+- Mono Vertex to bypass ISB for simple use cases (1.4)
## Demo
From b36e7d3df8453cbedf2080f435f09d8133410147 Mon Sep 17 00:00:00 2001
From: Abdullah Yildirim
Date: Tue, 20 Aug 2024 18:27:41 +0000
Subject: [PATCH 015/188] chore: fix broken links (#1972)
Signed-off-by: a3hadi
---
test/builtin-source-e2e/testdata/http-source-with-auth.yaml | 2 +-
test/builtin-source-e2e/testdata/http-source.yaml | 2 +-
test/builtin-source-e2e/testdata/jetstream-source-pipeline.yaml | 2 +-
test/builtin-source-e2e/testdata/nats-source-pipeline.yaml | 2 +-
test/diamond-e2e/testdata/cycle-backward.yaml | 2 +-
test/diamond-e2e/testdata/cycle-to-self.yaml | 2 +-
test/diamond-e2e/testdata/join-on-map.yaml | 2 +-
test/diamond-e2e/testdata/join-on-reduce.yaml | 2 +-
test/diamond-e2e/testdata/join-on-sink.yaml | 2 +-
test/e2e/testdata/simple-fallback.yaml | 2 +-
test/e2e/testdata/udf-filtering.yaml | 2 +-
test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml | 2 +-
test/idle-source-e2e/testdata/kafka-pipeline.yaml | 2 +-
test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml | 2 +-
test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml | 2 +-
.../testdata/complex-sliding-window-pipeline.yaml | 2 +-
test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml | 2 +-
.../testdata/simple-non-keyed-reduce-pipeline.yaml | 2 +-
test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml | 2 +-
.../reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml | 2 +-
.../testdata/reduce-stream/reduce-stream-java.yaml | 2 +-
.../simple-session-keyed-counter-pipeline-go.yaml | 2 +-
.../simple-session-keyed-counter-pipeline-java.yaml | 2 +-
test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml | 2 +-
test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml | 2 +-
test/sideinputs-e2e/testdata/sideinput-source.yaml | 2 +-
test/transformer-e2e/testdata/source-filtering.yaml | 2 +-
27 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/test/builtin-source-e2e/testdata/http-source-with-auth.yaml b/test/builtin-source-e2e/testdata/http-source-with-auth.yaml
index 4b27a58cee..08fb2bdf25 100644
--- a/test/builtin-source-e2e/testdata/http-source-with-auth.yaml
+++ b/test/builtin-source-e2e/testdata/http-source-with-auth.yaml
@@ -15,7 +15,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/builtin-source-e2e/testdata/http-source.yaml b/test/builtin-source-e2e/testdata/http-source.yaml
index c67d366ddb..53f32afb07 100644
--- a/test/builtin-source-e2e/testdata/http-source.yaml
+++ b/test/builtin-source-e2e/testdata/http-source.yaml
@@ -14,7 +14,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/builtin-source-e2e/testdata/jetstream-source-pipeline.yaml b/test/builtin-source-e2e/testdata/jetstream-source-pipeline.yaml
index c1a3766530..0ef6065413 100644
--- a/test/builtin-source-e2e/testdata/jetstream-source-pipeline.yaml
+++ b/test/builtin-source-e2e/testdata/jetstream-source-pipeline.yaml
@@ -23,7 +23,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/builtin-source-e2e/testdata/nats-source-pipeline.yaml b/test/builtin-source-e2e/testdata/nats-source-pipeline.yaml
index 3dd0d965df..88630fd11d 100644
--- a/test/builtin-source-e2e/testdata/nats-source-pipeline.yaml
+++ b/test/builtin-source-e2e/testdata/nats-source-pipeline.yaml
@@ -24,7 +24,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/diamond-e2e/testdata/cycle-backward.yaml b/test/diamond-e2e/testdata/cycle-backward.yaml
index 7312fc6cce..5ede83e3b0 100644
--- a/test/diamond-e2e/testdata/cycle-backward.yaml
+++ b/test/diamond-e2e/testdata/cycle-backward.yaml
@@ -23,7 +23,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/diamond-e2e/testdata/cycle-to-self.yaml b/test/diamond-e2e/testdata/cycle-to-self.yaml
index eaa7bf9cfa..036d2a4ecb 100644
--- a/test/diamond-e2e/testdata/cycle-to-self.yaml
+++ b/test/diamond-e2e/testdata/cycle-to-self.yaml
@@ -19,7 +19,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/diamond-e2e/testdata/join-on-map.yaml b/test/diamond-e2e/testdata/join-on-map.yaml
index 6ca69cda24..b2bd667da7 100644
--- a/test/diamond-e2e/testdata/join-on-map.yaml
+++ b/test/diamond-e2e/testdata/join-on-map.yaml
@@ -29,7 +29,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/diamond-e2e/testdata/join-on-reduce.yaml b/test/diamond-e2e/testdata/join-on-reduce.yaml
index f0ec43e19f..e1562dd133 100644
--- a/test/diamond-e2e/testdata/join-on-reduce.yaml
+++ b/test/diamond-e2e/testdata/join-on-reduce.yaml
@@ -50,7 +50,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/diamond-e2e/testdata/join-on-sink.yaml b/test/diamond-e2e/testdata/join-on-sink.yaml
index 1f1a442b24..2cf19c9f31 100644
--- a/test/diamond-e2e/testdata/join-on-sink.yaml
+++ b/test/diamond-e2e/testdata/join-on-sink.yaml
@@ -25,7 +25,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/e2e/testdata/simple-fallback.yaml b/test/e2e/testdata/simple-fallback.yaml
index 0e39df2ddf..8637a7b100 100644
--- a/test/e2e/testdata/simple-fallback.yaml
+++ b/test/e2e/testdata/simple-fallback.yaml
@@ -24,7 +24,7 @@ spec:
fallback:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/e2e/testdata/udf-filtering.yaml b/test/e2e/testdata/udf-filtering.yaml
index 1c8c79057c..507c7a9b59 100644
--- a/test/e2e/testdata/udf-filtering.yaml
+++ b/test/e2e/testdata/udf-filtering.yaml
@@ -17,7 +17,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml b/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml
index 2fc2a0a89e..28964b3fda 100644
--- a/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml
+++ b/test/idle-source-e2e/testdata/idle-source-reduce-pipeline.yaml
@@ -47,7 +47,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/idle-source-e2e/testdata/kafka-pipeline.yaml b/test/idle-source-e2e/testdata/kafka-pipeline.yaml
index 3af968f58d..8b397f5738 100644
--- a/test/idle-source-e2e/testdata/kafka-pipeline.yaml
+++ b/test/idle-source-e2e/testdata/kafka-pipeline.yaml
@@ -50,7 +50,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml b/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
index 66f1bb34bd..acdb0b29f6 100644
--- a/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
+++ b/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
@@ -15,7 +15,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml b/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml
index d7a56c5f0e..c57c9f580c 100644
--- a/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/complex-reduce-pipeline.yaml
@@ -54,7 +54,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml b/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml
index 8c19e22d72..3ad9d1b552 100644
--- a/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/complex-sliding-window-pipeline.yaml
@@ -86,7 +86,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml b/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
index c770092617..1b0d7f713c 100644
--- a/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/simple-keyed-reduce-pipeline.yaml
@@ -44,7 +44,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml b/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml
index d5a932a7c5..918b936650 100644
--- a/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml
+++ b/test/reduce-one-e2e/testdata/simple-non-keyed-reduce-pipeline.yaml
@@ -42,7 +42,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml b/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml
index 25a6770a5d..15440d9c39 100644
--- a/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml
+++ b/test/reduce-one-e2e/testdata/simple-reduce-pipeline-wal.yaml
@@ -41,7 +41,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml
index d80138366e..d859384b89 100644
--- a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml
+++ b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-go.yaml
@@ -39,7 +39,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml
index b94e7fd649..9b8253d13c 100644
--- a/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml
+++ b/test/reduce-two-e2e/testdata/reduce-stream/reduce-stream-java.yaml
@@ -39,7 +39,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml
index a2d88efeb6..1e8e12ebc5 100644
--- a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml
+++ b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-go.yaml
@@ -40,7 +40,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml
index f468710f03..64c9ee2376 100644
--- a/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml
+++ b/test/reduce-two-e2e/testdata/session-reduce/simple-session-keyed-counter-pipeline-java.yaml
@@ -45,7 +45,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml b/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml
index cb3ea152ed..7ecff1e3e6 100644
--- a/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml
+++ b/test/sideinputs-e2e/testdata/map-sideinput-pipeline.yaml
@@ -29,7 +29,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml b/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml
index 028a20125e..4497278f68 100644
--- a/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml
+++ b/test/sideinputs-e2e/testdata/reduce-sideinput-pipeline.yaml
@@ -46,7 +46,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/sideinputs-e2e/testdata/sideinput-source.yaml b/test/sideinputs-e2e/testdata/sideinput-source.yaml
index 6c21edd717..60a0b74611 100644
--- a/test/sideinputs-e2e/testdata/sideinput-source.yaml
+++ b/test/sideinputs-e2e/testdata/sideinput-source.yaml
@@ -28,7 +28,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
diff --git a/test/transformer-e2e/testdata/source-filtering.yaml b/test/transformer-e2e/testdata/source-filtering.yaml
index 549fddec63..e517ca756b 100644
--- a/test/transformer-e2e/testdata/source-filtering.yaml
+++ b/test/transformer-e2e/testdata/source-filtering.yaml
@@ -16,7 +16,7 @@ spec:
sink:
udsink:
container:
- # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis-sink
+ # A redis sink for e2e testing, see https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/redis_sink
image: quay.io/numaio/numaflow-go/redis-sink:stable
imagePullPolicy: Always
env:
From 5ff8ac1d3fea62b2046482bb445b60240f20ef21 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Tue, 20 Aug 2024 16:36:41 -0700
Subject: [PATCH 016/188] chore: kustomize transformation config for MonoVertex
(#1977)
Signed-off-by: Derek Wang
---
Makefile | 4 +-
.../numaflow-transformer-config.yaml | 711 ++++++++++--------
2 files changed, 395 insertions(+), 320 deletions(-)
diff --git a/Makefile b/Makefile
index 696b63f87b..b616d50621 100644
--- a/Makefile
+++ b/Makefile
@@ -288,12 +288,12 @@ ifneq ($(findstring release,$(GIT_BRANCH)),)
.PHONY: prepare-release
prepare-release: check-version-warning clean update-manifests-version codegen
git status
- @git diff --quiet || echo "\n\nPlease run 'git diff' to confirm the file changes are correct.\n"
+ @git diff --quiet || printf "\n\nPlease run 'git diff' to confirm the file changes are correct.\n\n"
.PHONY: release
release: check-version-warning
@echo
- @echo "1. Make sure you have run 'VERSION=$(VERSION) make prepare-release', and confirmed all the changes are expected."
+ @echo "1. Make sure you have run 'make prepare-release VERSION=$(VERSION)', and confirmed all the changes are expected."
@echo
@echo "2. Run following commands to commit the changes to the release branch, add give a tag."
@echo
diff --git a/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml b/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml
index 6602151708..39439f10da 100644
--- a/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml
+++ b/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml
@@ -1,383 +1,458 @@
# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/images.go
images:
-- path: spec/vertices/sidecars/image
- kind: Pipeline
-- path: spec/vertices/udf/container/image
- kind: Pipeline
-- path: spec/vertices/sink/udsink/container/image
- kind: Pipeline
-- path: spec/vertices/sink/fallback/udsink/container/image
- kind: Pipeline
-- path: spec/vertices/source/transformer/container/image
- kind: Pipeline
-- path: spec/vertices/source/udsource/container/image
- kind: Pipeline
-- path: spec/sideInputs/container/image
- kind: Pipeline
-
-# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/namereference.go
-nameReference:
-- kind: ConfigMap
- version: v1
- fieldSpecs:
- - path: spec/vertices/volumes/configMap/name
- kind: Pipeline
- - path: spec/vertices/volumes/projected/sources/configMap/name
- kind: Pipeline
- - path: spec/vertices/containerTemplate/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/vertices/containerTemplate/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/vertices/initContainerTemplate/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/vertices/initContainerTemplate/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/vertices/sideInputsContainerTemplate/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/vertices/sideInputsContainerTemplate/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/vertices/initContainers/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/vertices/initContainers/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/vertices/sidecars/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/vertices/sidecars/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/vertices/udf/container/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/vertices/udf/container/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/vertices/sink/udsink/container/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/vertices/sink/udsink/container/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/vertices/sink/fallback/udsink/container/env/valueFrom/configMapKeyRef/name
+ - path: spec/vertices/sidecars/image
kind: Pipeline
- - path: spec/vertices/sink/fallback/udsink/container/envFrom/configMapRef/name
+ - path: spec/vertices/udf/container/image
kind: Pipeline
- - path: spec/vertices/source/transformer/container/env/valueFrom/configMapKeyRef/name
+ - path: spec/vertices/sink/udsink/container/image
kind: Pipeline
- - path: spec/vertices/source/transformer/container/envFrom/configMapRef/name
+ - path: spec/vertices/sink/fallback/udsink/container/image
kind: Pipeline
- - path: spec/vertices/source/udsource/container/env/valueFrom/configMapKeyRef/name
+ - path: spec/vertices/source/transformer/container/image
kind: Pipeline
- - path: spec/vertices/source/udsource/container/envFrom/configMapRef/name
+ - path: spec/vertices/source/udsource/container/image
kind: Pipeline
- - path: spec/sideInputs/container/env/valueFrom/configMapKeyRef/name
+ - path: spec/sideInputs/container/image
kind: Pipeline
- - path: spec/sideInputs/container/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/templates/daemon/containerTemplate/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/templates/daemon/containerTemplate/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/templates/daemon/initContainerTemplate/env/valueFrom/configMapKeyRef/name
- kind: Pipeline
- - path: spec/templates/daemon/initContainerTemplate/envFrom/configMapRef/name
- kind: Pipeline
- - path: spec/templates/job/containerTemplate/env/valueFrom/configMapKeyRef/name
+ - path: spec/source/udsource/container/image
+ kind: MonoVertex
+ - path: spec/source/transformer/container/image
+ kind: MonoVertex
+ - path: spec/sink/udsink/container/image
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/image
+ kind: MonoVertex
+
+# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/namereference.go
+nameReference:
+ - kind: ConfigMap
+ version: v1
+ fieldSpecs:
+ - path: spec/vertices/volumes/configMap/name
+ kind: Pipeline
+ - path: spec/vertices/volumes/projected/sources/configMap/name
+ kind: Pipeline
+ - path: spec/vertices/containerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/containerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/sideInputsContainerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sideInputsContainerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainers/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainers/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/sidecars/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sidecars/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/udf/container/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/udf/container/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/udsink/container/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/udsink/container/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/fallback/udsink/container/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/fallback/udsink/container/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/transformer/container/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/transformer/container/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/udsource/container/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/udsource/container/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/sideInputs/container/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/sideInputs/container/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/containerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/containerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/initContainerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/initContainerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/templates/job/containerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/job/containerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/containerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/containerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/initContainerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/initContainerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/containerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/containerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/initContainerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/initContainerTemplate/envFrom/configMapRef/name
+ kind: Pipeline
+ - path: spec/jetstream/containerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/containerTemplate/envFrom/configMapRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/reloaderContainerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/reloaderContainerTemplate/envFrom/configMapRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/metricsContainerTemplate/env/valueFrom/configMapKeyRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/metricsContainerTemplate/envFrom/configMapRef/name
+ kind: InterStepBufferService
+ - path: spec/source/transformer/container/env/valueFrom/configMapKeyRef/name
+ kind: MonoVertex
+ - path: spec/source/transformer/container/envFrom/configMapRef/name
+ kind: MonoVertex
+ - path: spec/source/udsource/container/env/valueFrom/configMapKeyRef/name
+ kind: MonoVertex
+ - path: spec/source/udsource/container/envFrom/configMapRef/name
+ kind: MonoVertex
+ - path: spec/sink/udsink/container/env/valueFrom/configMapKeyRef/name
+ kind: MonoVertex
+ - path: spec/sink/udsink/container/envFrom/configMapRef/name
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/env/valueFrom/configMapKeyRef/name
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/envFrom/configMapRef/name
+ kind: MonoVertex
+ - kind: Secret
+ version: v1
+ fieldSpecs:
+ - path: spec/vertices/volumes/secret/secretName
+ kind: Pipeline
+ - path: spec/vertices/volumes/projected/sources/secret/name
+ kind: Pipeline
+ - path: spec/vertices/containerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/containerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/sideInputsContainerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sideInputsContainerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainers/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/initContainers/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/sidecars/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sidecars/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/udf/container/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/udf/container/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/udsink/container/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/udsink/container/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/fallback/udsink/container/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/sink/fallback/udsink/container/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/transformer/container/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/transformer/container/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/udsource/container/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/vertices/source/udousrce/container/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/sideInputs/container/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/sideInputs/container/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/containerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/containerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/daemon/initContainerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/templates/job/containerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/job/containerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/containerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/containerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/initContainerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/containerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/containerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ kind: Pipeline
+ - path: spec/templates/vertex/initContainerTemplate/envFrom/secretRef/name
+ kind: Pipeline
+ - path: spec/vertices/imagePullSecrets/name
+ kind: Pipeline
+ - path: spec/templates/daemon/imagePullSecrets/name
+ kind: Pipeline
+ - path: spec/templates/job/imagePullSecrets/name
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/imagePullSecrets/name
+ kind: Pipeline
+ - path: spec/templates/vertex/imagePullSecrets/name
+ kind: Pipeline
+ - path: spec/jetstream/containerTemplate/env/valueFrom/secretKeyRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/containerTemplate/envFrom/secretRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/reloaderContainerTemplate/env/valueFrom/secretKeyRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/reloaderContainerTemplate/envFrom/secretRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/metricsContainerTemplate/env/valueFrom/secretKeyRef/name
+ kind: InterStepBufferService
+ - path: spec/jetstream/metricsContainerTemplate/envFrom/secretRef/name
+ kind: InterStepBufferService
+ - path: spec/source/transformer/container/env/valueFrom/secretKeyRef/name
+ kind: MonoVertex
+ - path: spec/source/transformer/container/envFrom/secretRef/name
+ kind: MonoVertex
+ - path: spec/source/udsource/container/env/valueFrom/secretKeyRef/name
+ kind: MonoVertex
+ - path: spec/source/udousrce/container/envFrom/secretRef/name
+ kind: MonoVertex
+ - path: spec/sink/udsink/container/env/valueFrom/secretKeyRef/name
+ kind: MonoVertex
+ - path: spec/sink/udsink/container/envFrom/secretRef/name
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/env/valueFrom/secretKeyRef/name
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/envFrom/secretRef/name
+ kind: MonoVertex
+ - kind: ServiceAccount
+ version: v1
+ fieldSpecs:
+ - path: spec/vertices/serviceAccountName
+ kind: Pipeline
+ - path: spec/templates/daemon/serviceAccountName
+ kind: Pipeline
+ - path: spec/templates/job/serviceAccountName
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/serviceAccountName
+ kind: Pipeline
+ - path: spec/templates/vertex/serviceAccountName
+ kind: Pipeline
+ - path: spec/jetstream/serviceAccountName
+ kind: InterStepBufferService
+ - path: spec/serviceAccountName
+ kind: MonoVertex
+ - kind: PersistentVolumeClaim
+ version: v1
+ fieldSpecs:
+ - path: spec/vertices/volumes/persistentVolumeClaim/claimName
+ kind: Pipeline
+ - kind: PriorityClass
+ version: v1
+ group: scheduling.k8s.io
+ fieldSpecs:
+ - path: spec/vertices/priorityClassName
+ kind: Pipeline
+ - path: spec/templates/daemon/priorityClassName
+ kind: Pipeline
+ - path: spec/templates/job/priorityClassName
+ kind: Pipeline
+ - path: spec/templates/sideInputsManager/priorityClassName
+ kind: Pipeline
+ - path: spec/templates/vertex/priorityClassName
+ kind: Pipeline
+ - path: spec/jetstream/priorityClassName
+ kind: InterStepBufferService
+ - path: spec/priorityClassName
+ kind: MonoVertex
+
+# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/commonlabels.go
+commonLabels:
+ - path: spec/vertices/metadata/labels
+ create: true
kind: Pipeline
- - path: spec/templates/job/containerTemplate/envFrom/configMapRef/name
+ - path: spec/templates/daemon/metadata/labels
+ create: true
kind: Pipeline
- - path: spec/templates/sideInputsManager/containerTemplate/env/valueFrom/configMapKeyRef/name
+ - path: spec/templates/job/metadata/labels
+ create: true
kind: Pipeline
- - path: spec/templates/sideInputsManager/containerTemplate/envFrom/configMapRef/name
+ - path: spec/templates/sideInputsManager/metadata/labels
+ create: true
kind: Pipeline
- - path: spec/templates/sideInputsManager/initContainerTemplate/env/valueFrom/configMapKeyRef/name
+ - path: spec/templates/vertex/metadata/labels
+ create: true
kind: Pipeline
- - path: spec/templates/sideInputsManager/initContainerTemplate/envFrom/configMapRef/name
+ - path: spec/jetstream/metadata/labels
+ create: true
+ kind: InterStepBufferService
+ - path: spec/metadata/labels
+ create: true
+ kind: MonoVertex
+
+# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/commonannotations.go
+commonAnnotations:
+ - path: spec/vertices/metadata/annotations
+ create: true
kind: Pipeline
- - path: spec/templates/vertex/containerTemplate/env/valueFrom/configMapKeyRef/name
+ - path: spec/templates/daemon/metadata/annotations
+ create: true
kind: Pipeline
- - path: spec/templates/vertex/containerTemplate/envFrom/configMapRef/name
+ - path: spec/templates/job/metadata/annotations
+ create: true
kind: Pipeline
- - path: spec/templates/vertex/initContainerTemplate/env/valueFrom/configMapKeyRef/name
+ - path: spec/templates/sideInputsManager/metadata/annotations
+ create: true
kind: Pipeline
- - path: spec/templates/vertex/initContainerTemplate/envFrom/configMapRef/name
+ - path: spec/templates/vertex/metadata/annotations
+ create: true
kind: Pipeline
- - path: spec/jetstream/containerTemplate/env/valueFrom/configMapKeyRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/containerTemplate/envFrom/configMapRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/reloaderContainerTemplate/env/valueFrom/configMapKeyRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/reloaderContainerTemplate/envFrom/configMapRef/name
+ - path: spec/jetstream/metadata/annotations
+ create: true
kind: InterStepBufferService
- - path: spec/jetstream/metricsContainerTemplate/env/valueFrom/configMapKeyRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/metricsContainerTemplate/envFrom/configMapRef/name
- kind: InterStepBufferService
-- kind: Secret
- version: v1
- fieldSpecs:
- - path: spec/vertices/volumes/secret/secretName
- kind: Pipeline
- - path: spec/vertices/volumes/projected/sources/secret/name
- kind: Pipeline
- - path: spec/vertices/containerTemplate/env/valueFrom/secretKeyRef/name
- kind: Pipeline
- - path: spec/vertices/containerTemplate/envFrom/secretRef/name
- kind: Pipeline
- - path: spec/vertices/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/metadata/annotations
+ create: true
+ kind: MonoVertex
+
+# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/varreference.go
+varReference:
+ - path: spec/vertices/sidecars/args
kind: Pipeline
- - path: spec/vertices/initContainerTemplate/envFrom/secretRef/name
+ - path: spec/vertices/sidecars/command
kind: Pipeline
- - path: spec/vertices/sideInputsContainerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/sidecars/env/value
kind: Pipeline
- - path: spec/vertices/sideInputsContainerTemplate/envFrom/secretRef/name
+ - path: spec/vertices/udf/container/args
kind: Pipeline
- - path: spec/vertices/initContainers/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/udf/container/command
kind: Pipeline
- - path: spec/vertices/initContainers/envFrom/secretRef/name
+ - path: spec/vertices/udf/container/env/value
kind: Pipeline
- - path: spec/vertices/sidecars/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/sink/udsink/container/args
kind: Pipeline
- - path: spec/vertices/sidecars/envFrom/secretRef/name
+ - path: spec/vertices/sink/udsink/container/command
kind: Pipeline
- - path: spec/vertices/udf/container/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/sink/udsink/container/env/value
kind: Pipeline
- - path: spec/vertices/udf/container/envFrom/secretRef/name
+ - path: spec/vertices/sink/fallback/udsink/container/args
kind: Pipeline
- - path: spec/vertices/sink/udsink/container/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/sink/fallback/udsink/container/command
kind: Pipeline
- - path: spec/vertices/sink/udsink/container/envFrom/secretRef/name
+ - path: spec/vertices/sink/fallback/udsink/container/env/value
kind: Pipeline
- - path: spec/vertices/sink/fallback/udsink/container/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/source/transformer/container/args
kind: Pipeline
- - path: spec/vertices/sink/fallback/udsink/container/envFrom/secretRef/name
+ - path: spec/vertices/source/transformer/container/command
kind: Pipeline
- - path: spec/vertices/source/transformer/container/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/source/transformer/container/env/value
kind: Pipeline
- - path: spec/vertices/source/transformer/container/envFrom/secretRef/name
+ - path: spec/vertices/source/udsource/container/args
kind: Pipeline
- - path: spec/vertices/source/udsource/container/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/source/udsource/container/command
kind: Pipeline
- - path: spec/vertices/source/udousrce/container/envFrom/secretRef/name
+ - path: spec/vertices/source/udsource/container/env/value
kind: Pipeline
- - path: spec/sideInputs/container/env/valueFrom/secretKeyRef/name
+ - path: spec/sideInputs/container/args
kind: Pipeline
- - path: spec/sideInputs/container/envFrom/secretRef/name
+ - path: spec/sideInputs/container/command
kind: Pipeline
- - path: spec/templates/daemon/containerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/sideInputs/container/env/value
kind: Pipeline
- - path: spec/templates/daemon/containerTemplate/envFrom/secretRef/name
+ - path: spec/vertices/containerTemplate/env/value
kind: Pipeline
- - path: spec/templates/daemon/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/initContainerTemplate/env/value
kind: Pipeline
- - path: spec/templates/daemon/initContainerTemplate/envFrom/secretRef/name
+ - path: spec/vertices/sideInputsContainerTemplate/env/value
kind: Pipeline
- - path: spec/templates/job/containerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/initContainers/env/value
kind: Pipeline
- - path: spec/templates/job/containerTemplate/envFrom/secretRef/name
+ - path: spec/templates/daemon/containerTemplate/env/value
kind: Pipeline
- - path: spec/templates/sideInputsManager/containerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/templates/daemon/initContainerTemplate/env/value
kind: Pipeline
- - path: spec/templates/sideInputsManager/containerTemplate/envFrom/secretRef/name
+ - path: spec/templates/job/containerTemplate/env/value
kind: Pipeline
- - path: spec/templates/sideInputsManager/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/templates/sideInputsManager/containerTemplate/env/value
kind: Pipeline
- - path: spec/templates/sideInputsManager/initContainerTemplate/envFrom/secretRef/name
+ - path: spec/templates/sideInputsManager/initContainerTemplate/env/value
kind: Pipeline
- - path: spec/templates/vertex/containerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/templates/vertex/containerTemplate/env/value
kind: Pipeline
- - path: spec/templates/vertex/containerTemplate/envFrom/secretRef/name
+ - path: spec/templates/vertex/initContainerTemplate/env/value
kind: Pipeline
- - path: spec/templates/vertex/initContainerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/vertices/sidecars/volumeMounts/mountPath
kind: Pipeline
- - path: spec/templates/vertex/initContainerTemplate/envFrom/secretRef/name
+ - path: spec/vertices/udf/container/volumeMounts/mountPath
kind: Pipeline
- - path: spec/vertices/imagePullSecrets/name
+ - path: spec/vertices/sink/udsink/container/volumeMounts/mountPath
kind: Pipeline
- - path: spec/templates/daemon/imagePullSecrets/name
+ - path: spec/vertices/sink/fallback/udsink/container/volumeMounts/mountPath
kind: Pipeline
- - path: spec/templates/job/imagePullSecrets/name
+ - path: spec/vertices/source/transformer/container/volumeMounts/mountPath
kind: Pipeline
- - path: spec/templates/sideInputsManager/imagePullSecrets/name
+ - path: spec/vertices/source/udsource/container/volumeMounts/mountPath
kind: Pipeline
- - path: spec/templates/vertex/imagePullSecrets/name
+ - path: spec/sideInputs/container/volumeMounts/mountPath
kind: Pipeline
- - path: spec/jetstream/containerTemplate/env/valueFrom/secretKeyRef/name
+ - path: spec/jetstream/containerTemplate/env/value
kind: InterStepBufferService
- - path: spec/jetstream/containerTemplate/envFrom/secretRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/reloaderContainerTemplate/env/valueFrom/secretKeyRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/reloaderContainerTemplate/envFrom/secretRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/metricsContainerTemplate/env/valueFrom/secretKeyRef/name
- kind: InterStepBufferService
- - path: spec/jetstream/metricsContainerTemplate/envFrom/secretRef/name
- kind: InterStepBufferService
-- kind: ServiceAccount
- version: v1
- fieldSpecs:
- - path: spec/vertices/serviceAccountName
- kind: Pipeline
- - path: spec/templates/daemon/serviceAccountName
- kind: Pipeline
- - path: spec/templates/job/serviceAccountName
- kind: Pipeline
- - path: spec/templates/sideInputsManager/serviceAccountName
- kind: Pipeline
- - path: spec/templates/vertex/serviceAccountName
- kind: Pipeline
- - path: spec/jetstream/serviceAccountName
+ - path: spec/jetstream/reloaderContainerTemplate/env/value
kind: InterStepBufferService
-- kind: PersistentVolumeClaim
- version: v1
- fieldSpecs:
- - path: spec/vertices/volumes/persistentVolumeClaim/claimName
- kind: Pipeline
-- kind: PriorityClass
- version: v1
- group: scheduling.k8s.io
- fieldSpecs:
- - path: spec/vertices/priorityClassName
- kind: Pipeline
- - path: spec/templates/daemon/priorityClassName
- kind: Pipeline
- - path: spec/templates/job/priorityClassName
- kind: Pipeline
- - path: spec/templates/sideInputsManager/priorityClassName
- kind: Pipeline
- - path: spec/templates/vertex/priorityClassName
- kind: Pipeline
- - path: spec/jetstream/priorityClassName
+ - path: spec/jetstream/metricsContainerTemplate/env/value
kind: InterStepBufferService
-
-# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/commonlabels.go
-commonLabels:
-- path: spec/vertices/metadata/labels
- create: true
- kind: Pipeline
-- path: spec/templates/daemon/metadata/labels
- create: true
- kind: Pipeline
-- path: spec/templates/job/metadata/labels
- create: true
- kind: Pipeline
-- path: spec/templates/sideInputsManager/metadata/labels
- create: true
- kind: Pipeline
-- path: spec/templates/vertex/metadata/labels
- create: true
- kind: Pipeline
-- path: spec/jetstream/metadata/labels
- create: true
- kind: InterStepBufferService
-
-# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/commonannotations.go
-commonAnnotations:
-- path: spec/vertices/metadata/annotations
- create: true
- kind: Pipeline
-- path: spec/templates/daemon/metadata/annotations
- create: true
- kind: Pipeline
-- path: spec/templates/job/metadata/annotations
- create: true
- kind: Pipeline
-- path: spec/templates/sideInputsManager/metadata/annotations
- create: true
- kind: Pipeline
-- path: spec/templates/vertex/metadata/annotations
- create: true
- kind: Pipeline
-- path: spec/jetstream/metadata/annotations
- create: true
- kind: InterStepBufferService
-
-# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/varreference.go
-varReference:
-- path: spec/vertices/sidecars/args
- kind: Pipeline
-- path: spec/vertices/sidecars/command
- kind: Pipeline
-- path: spec/vertices/sidecars/env/value
- kind: Pipeline
-- path: spec/vertices/udf/container/args
- kind: Pipeline
-- path: spec/vertices/udf/container/command
- kind: Pipeline
-- path: spec/vertices/udf/container/env/value
- kind: Pipeline
-- path: spec/vertices/sink/udsink/container/args
- kind: Pipeline
-- path: spec/vertices/sink/udsink/container/command
- kind: Pipeline
-- path: spec/vertices/sink/udsink/container/env/value
- kind: Pipeline
-- path: spec/vertices/sink/fallback/udsink/container/args
- kind: Pipeline
-- path: spec/vertices/sink/fallback/udsink/container/command
- kind: Pipeline
-- path: spec/vertices/sink/fallback/udsink/container/env/value
- kind: Pipeline
-- path: spec/vertices/source/transformer/container/args
- kind: Pipeline
-- path: spec/vertices/source/transformer/container/command
- kind: Pipeline
-- path: spec/vertices/source/transformer/container/env/value
- kind: Pipeline
-- path: spec/vertices/source/udsource/container/args
- kind: Pipeline
-- path: spec/vertices/source/udsource/container/command
- kind: Pipeline
-- path: spec/vertices/source/udsource/container/env/value
- kind: Pipeline
-- path: spec/sideInputs/container/args
- kind: Pipeline
-- path: spec/sideInputs/container/command
- kind: Pipeline
-- path: spec/sideInputs/container/env/value
- kind: Pipeline
-- path: spec/vertices/containerTemplate/env/value
- kind: Pipeline
-- path: spec/vertices/initContainerTemplate/env/value
- kind: Pipeline
-- path: spec/vertices/sideInputsContainerTemplate/env/value
- kind: Pipeline
-- path: spec/vertices/initContainers/env/value
- kind: Pipeline
-- path: spec/templates/daemon/containerTemplate/env/value
- kind: Pipeline
-- path: spec/templates/daemon/initContainerTemplate/env/value
- kind: Pipeline
-- path: spec/templates/job/containerTemplate/env/value
- kind: Pipeline
-- path: spec/templates/sideInputsManager/containerTemplate/env/value
- kind: Pipeline
-- path: spec/templates/sideInputsManager/initContainerTemplate/env/value
- kind: Pipeline
-- path: spec/templates/vertex/containerTemplate/env/value
- kind: Pipeline
-- path: spec/templates/vertex/initContainerTemplate/env/value
- kind: Pipeline
-- path: spec/vertices/sidecars/volumeMounts/mountPath
- kind: Pipeline
-- path: spec/vertices/udf/container/volumeMounts/mountPath
- kind: Pipeline
-- path: spec/vertices/sink/udsink/container/volumeMounts/mountPath
- kind: Pipeline
-- path: spec/vertices/sink/fallback/udsink/container/volumeMounts/mountPath
- kind: Pipeline
-- path: spec/vertices/source/transformer/container/volumeMounts/mountPath
- kind: Pipeline
-- path: spec/vertices/source/udsource/container/volumeMounts/mountPath
- kind: Pipeline
-- path: spec/sideInputs/container/volumeMounts/mountPath
- kind: Pipeline
-- path: spec/jetstream/containerTemplate/env/value
- kind: InterStepBufferService
-- path: spec/jetstream/reloaderContainerTemplate/env/value
- kind: InterStepBufferService
-- path: spec/jetstream/metricsContainerTemplate/env/value
- kind: InterStepBufferService
+ - path: spec/source/transformer/container/args
+ kind: MonoVertex
+ - path: spec/source/transformer/container/command
+ kind: MonoVertex
+ - path: spec/source/transformer/container/env/value
+ kind: MonoVertex
+ - path: spec/source/udsource/container/args
+ kind: MonoVertex
+ - path: spec/source/udsource/container/command
+ kind: MonoVertex
+ - path: spec/source/udsource/container/env/value
+ - path: spec/sink/udsink/container/args
+ kind: MonoVertex
+ - path: spec/sink/udsink/container/command
+ kind: MonoVertex
+ - path: spec/sink/udsink/container/env/value
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/args
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/command
+ kind: MonoVertex
+ - path: spec/sink/fallback/udsink/container/env/value
+ kind: MonoVertex
# https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/konfig/builtinpluginconsts/replicas.go
replicas:
-- path: spec/jetstream/replicas
- create: true
- kind: InterStepBufferService
-
+ - path: spec/jetstream/replicas
+ create: true
+ kind: InterStepBufferService
+ - path: spec/replicas
+ create: true
+ kind: MonoVertex
From af2f65220afa80fc8f4bf684cc9ce58234c2bb80 Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Tue, 20 Aug 2024 17:46:39 -0700
Subject: [PATCH 017/188] fix: remove coloring in logs (#1975)
---
rust/monovertex/src/lib.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/rust/monovertex/src/lib.rs b/rust/monovertex/src/lib.rs
index 614e7279aa..515975c16f 100644
--- a/rust/monovertex/src/lib.rs
+++ b/rust/monovertex/src/lib.rs
@@ -49,6 +49,7 @@ pub async fn mono_vertex() {
.parse_lossy(&config().log_level),
)
.with_target(false)
+ .with_ansi(false)
.init();
// Initialize the source, sink and transformer configurations
From 33bbbad4d7b16f9494d4164993b1cb9d32acc18b Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Tue, 20 Aug 2024 21:48:56 -0700
Subject: [PATCH 018/188] fix: minor perf improvements of mvtx fallback sink
(#1967)
Signed-off-by: Vigith Maurice
Signed-off-by: Yashash H L
Co-authored-by: Yashash H L
---
rust/monovertex/src/forwarder.rs | 250 ++++++++++++++++---------------
rust/monovertex/src/lib.rs | 41 ++---
rust/monovertex/src/sink.rs | 20 ++-
3 files changed, 169 insertions(+), 142 deletions(-)
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index 5862e753e9..7be4824b6e 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -94,6 +94,7 @@ impl Forwarder {
processed_msgs_count += self.read_and_process_messages().await?;
// if the last forward was more than 1 second ago, forward a chunk print the number of messages forwarded
+ // TODO: add histogram details (p99, etc.)
if last_forwarded_at.elapsed().as_millis() >= 1000 {
info!(
"Forwarded {} messages at time {}",
@@ -112,11 +113,9 @@ impl Forwarder {
Ok(())
}
- /*
- Read messages from the source, apply transformation if transformer is present,
- write the messages to the sink, if fallback messages are present write them to the fallback sink,
- and then acknowledge the messages back to the source.
- */
+ /// Read messages from the source, apply transformation if transformer is present,
+ /// write the messages to the sink, if fallback messages are present write them to the fallback sink,
+ /// and then acknowledge the messages back to the source.
async fn read_and_process_messages(&mut self) -> Result {
let start_time = tokio::time::Instant::now();
let messages = self
@@ -129,31 +128,31 @@ impl Forwarder {
start_time.elapsed().as_millis()
);
- // nothing more to be done.
+ // read returned 0 messages, nothing more to be done.
if messages.is_empty() {
return Ok(0);
}
let msg_count = messages.len() as u64;
- let bytes_count = messages
- .iter()
- .map(|msg| msg.value.len() as u64)
- .sum::();
forward_metrics()
.monovtx_read_total
.get_or_create(&self.common_labels)
.inc_by(msg_count);
+
+ let (offsets, bytes_count): (Vec, u64) = messages.iter().fold(
+ (Vec::with_capacity(messages.len()), 0),
+ |(mut offsets, mut bytes_count), msg| {
+ offsets.push(msg.offset.clone());
+ bytes_count += msg.value.len() as u64;
+ (offsets, bytes_count)
+ },
+ );
+
forward_metrics()
.monovtx_read_bytes_total
.get_or_create(&self.common_labels)
.inc_by(bytes_count);
- // collect all the offsets as the transformer can drop (via filter) messages
- let offsets = messages
- .iter()
- .map(|msg| msg.offset.clone())
- .collect::>();
-
// Apply transformation if transformer is present
let transformed_messages = self.apply_transformer(messages).await?;
@@ -169,33 +168,39 @@ impl Forwarder {
// Applies transformation to the messages if transformer is present
// we concurrently apply transformation to all the messages.
async fn apply_transformer(&self, messages: Vec) -> Result> {
- if let Some(transformer_client) = &self.transformer_client {
- let start_time = tokio::time::Instant::now();
- let mut jh = JoinSet::new();
- for message in messages {
- let mut transformer_client = transformer_client.clone();
- jh.spawn(async move { transformer_client.transform_fn(message).await });
- }
+ let transformer_client;
+ if let Some(trf_client) = &self.transformer_client {
+ transformer_client = trf_client;
+ } else {
+ // return early if there is no transformer
+ return Ok(messages);
+ }
- let mut results = Vec::new();
- while let Some(task) = jh.join_next().await {
- let result = task.map_err(|e| Error::TransformerError(format!("{:?}", e)))?;
- if let Some(result) = result? {
- results.extend(result);
- }
+ let start_time = tokio::time::Instant::now();
+ let mut jh = JoinSet::new();
+ for message in messages {
+ let mut transformer_client = transformer_client.clone();
+ jh.spawn(async move { transformer_client.transform_fn(message).await });
+ }
+
+ let mut results = Vec::new();
+ while let Some(task) = jh.join_next().await {
+ let result = task.map_err(|e| Error::TransformerError(format!("{:?}", e)))?;
+ if let Some(result) = result? {
+ results.extend(result);
}
- debug!(
- "Transformer latency - {}ms",
- start_time.elapsed().as_millis()
- );
- Ok(results)
- } else {
- Ok(messages)
}
+
+ debug!(
+ "Transformer latency - {}ms",
+ start_time.elapsed().as_millis()
+ );
+
+ Ok(results)
}
// Writes the messages to the sink and handles fallback messages if present
- async fn write_to_sink(&mut self, mut messages: Vec) -> Result<()> {
+ async fn write_to_sink(&mut self, messages: Vec) -> Result<()> {
let msg_count = messages.len() as u64;
if messages.is_empty() {
@@ -205,67 +210,63 @@ impl Forwarder {
let mut attempts = 0;
let mut error_map = HashMap::new();
let mut fallback_msgs = Vec::new();
+ // start with the original set of message to be sent.
+ // we will overwrite this vec with failed messages and will keep retrying.
+ let mut messages_to_send = messages;
while attempts <= config().sink_max_retry_attempts {
let start_time = tokio::time::Instant::now();
- match self.sink_client.sink_fn(messages.clone()).await {
+ match self.sink_client.sink_fn(messages_to_send.clone()).await {
Ok(response) => {
debug!("Sink latency - {}ms", start_time.elapsed().as_millis());
attempts += 1;
- fallback_msgs.extend(
- response
- .results
- .iter()
- .filter(|result| result.status == proto::Status::Fallback as i32)
- .map(|result| {
- messages
- .iter()
- .find(|msg| msg.id == result.id)
- .unwrap()
- .clone()
- })
- .collect::>(),
- );
-
- messages = response
+ // create a map of id to result, since there is no strict requirement
+ // for the udsink to return the results in the same order as the requests
+ let result_map: HashMap<_, _> = response
.results
.iter()
- .filter(|result| result.status == proto::Status::Failure as i32)
- .map(|result| {
- messages
- .iter()
- .find(|msg| msg.id == result.id)
- .unwrap()
- .clone()
- })
- .collect::>();
-
- if messages.is_empty() {
- break;
- } else {
- error_map.clear();
- for result in response.results {
- if result.status == proto::Status::Failure as i32 {
- *error_map.entry(result.err_msg).or_insert(0) += 1;
- }
+ .map(|result| (result.id.clone(), result))
+ .collect();
+
+ error_map.clear();
+ // drain all the messages that were successfully written
+ // and keep only the failed messages to send again
+ // construct the error map for the failed messages
+ messages_to_send.retain(|msg| {
+ if let Some(result) = result_map.get(&msg.id) {
+ return if result.status == proto::Status::Success as i32 {
+ false
+ } else if result.status == proto::Status::Fallback as i32 {
+ fallback_msgs.push(msg.clone()); // add to fallback messages
+ false
+ } else {
+ *error_map.entry(result.err_msg.clone()).or_insert(0) += 1;
+ true
+ };
}
+ false
+ });
- warn!(
- "Retry attempt {} due to retryable error. Errors: {:?}",
- attempts, error_map
- );
- sleep(tokio::time::Duration::from_millis(
- config().sink_retry_interval_in_ms as u64,
- ))
- .await;
+ // if all messages are successfully written, break the loop
+ if messages_to_send.is_empty() {
+ break;
}
+
+ warn!(
+ "Retry attempt {} due to retryable error. Errors: {:?}",
+ attempts, error_map
+ );
+ sleep(tokio::time::Duration::from_millis(
+ config().sink_retry_interval_in_ms as u64,
+ ))
+ .await;
}
Err(e) => return Err(e),
}
}
- if !messages.is_empty() {
+ if !messages_to_send.is_empty() {
return Err(Error::SinkError(format!(
"Failed to sink messages after {} attempts. Errors: {:?}",
attempts, error_map
@@ -285,7 +286,7 @@ impl Forwarder {
}
// Writes the fallback messages to the fallback sink
- async fn handle_fallback_messages(&mut self, mut fallback_msgs: Vec) -> Result<()> {
+ async fn handle_fallback_messages(&mut self, fallback_msgs: Vec) -> Result<()> {
if self.fallback_client.is_none() {
return Err(Error::SinkError(
"Response contains fallback messages but no fallback sink is configured"
@@ -296,67 +297,78 @@ impl Forwarder {
let fallback_client = self.fallback_client.as_mut().unwrap();
let mut attempts = 0;
let mut fallback_error_map = HashMap::new();
+ // start with the original set of message to be sent.
+ // we will overwrite this vec with failed messages and will keep retrying.
+ let mut messages_to_send = fallback_msgs;
while attempts <= config().sink_max_retry_attempts {
let start_time = tokio::time::Instant::now();
- match fallback_client.sink_fn(fallback_msgs.clone()).await {
+ match fallback_client.sink_fn(messages_to_send.clone()).await {
Ok(fb_response) => {
debug!(
"Fallback sink latency - {}ms",
start_time.elapsed().as_millis()
);
- fallback_msgs = fb_response
- .results
- .iter()
- .filter(|result| result.status == proto::Status::Failure as i32)
- .map(|result| {
- fallback_msgs
- .iter()
- .find(|msg| msg.id == result.id)
- .unwrap()
- .clone()
- })
- .collect::>();
-
- // we can't specify fallback response inside fallback sink
- if fb_response
+ // create a map of id to result, since there is no strict requirement
+ // for the udsink to return the results in the same order as the requests
+ let result_map: HashMap<_, _> = fb_response
.results
.iter()
- .any(|result| result.status == proto::Status::Fallback as i32)
- {
+ .map(|result| (result.id.clone(), result))
+ .collect();
+
+ let mut contains_fallback_status = false;
+
+ fallback_error_map.clear();
+ // drain all the messages that were successfully written
+ // and keep only the failed messages to send again
+ // construct the error map for the failed messages
+ messages_to_send.retain(|msg| {
+ if let Some(result) = result_map.get(&msg.id) {
+ if result.status == proto::Status::Failure as i32 {
+ *fallback_error_map
+ .entry(result.err_msg.clone())
+ .or_insert(0) += 1;
+ true
+ } else if result.status == proto::Status::Fallback as i32 {
+ contains_fallback_status = true;
+ false
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ });
+
+ // specifying fallback status in fallback response is not allowed
+ if contains_fallback_status {
return Err(Error::SinkError(
- "Fallback sink can't specify status fallback".to_string(),
+ "Fallback response contains fallback status".to_string(),
));
}
attempts += 1;
- if fallback_msgs.is_empty() {
+ if messages_to_send.is_empty() {
break;
- } else {
- fallback_error_map.clear();
- for result in fb_response.results {
- if result.status != proto::Status::Success as i32 {
- *fallback_error_map.entry(result.err_msg).or_insert(0) += 1;
- }
- }
-
- warn!(
- "Fallback sink retry attempt {} due to retryable error. Errors: {:?}",
- attempts, fallback_error_map
- );
- sleep(tokio::time::Duration::from_millis(
- config().sink_retry_interval_in_ms as u64,
- ))
- .await;
}
+
+ warn!(
+ "Retry attempt {} due to retryable error. Errors: {:?}",
+ attempts, fallback_error_map
+ );
+ sleep(tokio::time::Duration::from_millis(
+ config().sink_retry_interval_in_ms as u64,
+ ))
+ .await;
}
Err(e) => return Err(e),
}
}
- if !fallback_msgs.is_empty() {
+ if !messages_to_send.is_empty() {
return Err(Error::SinkError(format!(
"Failed to write messages to fallback sink after {} attempts. Errors: {:?}",
attempts, fallback_error_map
diff --git a/rust/monovertex/src/lib.rs b/rust/monovertex/src/lib.rs
index 515975c16f..9acabc95ea 100644
--- a/rust/monovertex/src/lib.rs
+++ b/rust/monovertex/src/lib.rs
@@ -3,7 +3,7 @@ use crate::config::config;
pub(crate) use crate::error::Error;
use crate::forwarder::ForwarderBuilder;
use crate::metrics::{start_metrics_https_server, LagReaderBuilder, MetricsState};
-use crate::sink::{SinkClient, SinkConfig, FB_SINK_SERVER_INFO_FILE, FB_SINK_SOCKET};
+use crate::sink::{SinkClient, SinkConfig};
use crate::source::{SourceClient, SourceConfig};
use crate::transformer::{TransformerClient, TransformerConfig};
use std::net::SocketAddr;
@@ -24,22 +24,24 @@ use tracing_subscriber::EnvFilter;
/// - Send Acknowledgement back to the Source
pub mod error;
-mod metrics;
+pub(crate) mod source;
-pub mod source;
+pub(crate) mod sink;
-pub mod sink;
+pub(crate) mod transformer;
-pub mod transformer;
+pub(crate) mod forwarder;
-pub mod forwarder;
+pub(crate) mod config;
-pub mod config;
+pub(crate) mod message;
-pub mod message;
-mod server_info;
pub(crate) mod shared;
+mod server_info;
+
+mod metrics;
+
pub async fn mono_vertex() {
// Initialize the logger
tracing_subscriber::fmt()
@@ -74,11 +76,7 @@ pub async fn mono_vertex() {
};
let fb_sink_config = if config().is_fallback_enabled {
- Some(SinkConfig {
- max_message_size: config().grpc_max_message_size,
- socket_path: FB_SINK_SOCKET.to_string(),
- server_info_file: FB_SINK_SERVER_INFO_FILE.to_string(),
- })
+ Some(SinkConfig::fallback_default())
} else {
None
};
@@ -104,7 +102,7 @@ pub async fn mono_vertex() {
{
error!("Application error: {:?}", e);
- // abort the task since we have an error
+ // abort the signal handler task since we have an error and we are shutting down
if !shutdown_handle.is_finished() {
shutdown_handle.abort();
}
@@ -227,17 +225,18 @@ pub async fn init(
// build the forwarder
let mut forwarder_builder = ForwarderBuilder::new(source_client, sink_client, cln_token);
+ // add transformer if exists
if let Some(transformer_client) = transformer_client {
forwarder_builder = forwarder_builder.transformer_client(transformer_client);
}
-
+ // add fallback sink if exists
if let Some(fb_sink_client) = fb_sink_client {
forwarder_builder = forwarder_builder.fb_sink_client(fb_sink_client);
}
-
+ // build the final forwarder
let mut forwarder = forwarder_builder.build();
- // start the forwarder
+ // start the forwarder, it will return only on Signal
forwarder.start().await?;
info!("Forwarder stopped gracefully");
@@ -378,8 +377,10 @@ mod tests {
// FIXME: we need to have a better way, this is flaky
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
- env::set_var("SOURCE_SOCKET", src_sock_file.to_str().unwrap());
- env::set_var("SINK_SOCKET", sink_sock_file.to_str().unwrap());
+ unsafe {
+ env::set_var("SOURCE_SOCKET", src_sock_file.to_str().unwrap());
+ env::set_var("SINK_SOCKET", sink_sock_file.to_str().unwrap());
+ }
let cln_token = CancellationToken::new();
diff --git a/rust/monovertex/src/sink.rs b/rust/monovertex/src/sink.rs
index 4cedaef49e..fb82273fb6 100644
--- a/rust/monovertex/src/sink.rs
+++ b/rust/monovertex/src/sink.rs
@@ -1,3 +1,4 @@
+use crate::config::config;
use crate::error::{Error, Result};
use crate::message::Message;
use crate::shared::connect_with_uds;
@@ -13,9 +14,10 @@ pub mod proto {
const RECONNECT_INTERVAL: u64 = 1000;
const MAX_RECONNECT_ATTEMPTS: usize = 5;
const SINK_SOCKET: &str = "/var/run/numaflow/sink.sock";
-pub(crate) const FB_SINK_SOCKET: &str = "/var/run/numaflow/fb-sink.sock";
+const FB_SINK_SOCKET: &str = "/var/run/numaflow/fb-sink.sock";
+
const SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/sinker-server-info";
-pub(crate) const FB_SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/fb-sinker-server-info";
+const FB_SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/fb-sinker-server-info";
/// SinkConfig is the configuration for the sink server.
#[derive(Debug, Clone)]
@@ -30,7 +32,18 @@ impl Default for SinkConfig {
SinkConfig {
socket_path: SINK_SOCKET.to_string(),
server_info_file: SINK_SERVER_INFO_FILE.to_string(),
- max_message_size: 64 * 1024 * 1024, // 64 MB
+ max_message_size: config().grpc_max_message_size,
+ }
+ }
+}
+
+impl SinkConfig {
+ /// default config for fallback sink
+ pub(crate) fn fallback_default() -> Self {
+ SinkConfig {
+ max_message_size: config().grpc_max_message_size,
+ socket_path: FB_SINK_SOCKET.to_string(),
+ server_info_file: FB_SINK_SERVER_INFO_FILE.to_string(),
}
}
}
@@ -60,6 +73,7 @@ impl SinkClient {
}
pub(crate) async fn sink_fn(&mut self, messages: Vec) -> Result {
+ // create a channel with at least size
let (tx, rx) = tokio::sync::mpsc::channel(if messages.is_empty() {
1
} else {
From b21e0bf5b2c288a76ad12fc323721f5d99bc14d4 Mon Sep 17 00:00:00 2001
From: Sreekanth
Date: Wed, 21 Aug 2024 21:59:38 +0530
Subject: [PATCH 019/188] chore: make nightly build pipeline faster (#1958)
Signed-off-by: Sreekanth
Signed-off-by: Derek Wang
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
Co-authored-by: Derek Wang
---
.github/workflows/ci.yaml | 37 ++++++++++++++
.github/workflows/nightly-build.yml | 79 +++++++++++++++++++++++++++--
.github/workflows/release.yml | 67 ++++++++++++++++++++++--
Dockerfile | 51 ++++++-------------
Makefile | 32 ++++++++++--
rust/rust-toolchain.toml | 3 ++
6 files changed, 224 insertions(+), 45 deletions(-)
create mode 100644 rust/rust-toolchain.toml
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index e27feac027..eb48e60d61 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -143,9 +143,42 @@ jobs:
- run: make lint
- run: git diff --exit-code
+ build-rust-amd64:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: ./rust
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Rust toolchain
+ uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
+ with:
+ rustflags: ''
+ - name: Configure sccache
+ run: |
+ echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
+ echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
+ - name: Run sccache-cache
+ uses: mozilla-actions/sccache-action@v0.0.5
+ - name: Install dependencies
+ run: sudo apt-get install -y protobuf-compiler
+ - name: Build binary
+ run: RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-gnu
+ - name: Rename binary
+ run: cp -pv target/x86_64-unknown-linux-gnu/release/numaflow ./numaflow-rs-linux-amd64
+ - name: List files
+ run: pwd && ls -al && file ./numaflow-rs-linux-amd64
+ - name: Upload numaflow binary
+ uses: actions/upload-artifact@v4
+ with:
+ name: numaflow-rs-linux-amd64
+ path: rust/numaflow-rs-linux-amd64
+ if-no-files-found: error
+
e2e-tests:
name: E2E Tests
runs-on: ubuntu-latest
+ needs: [ build-rust-amd64 ]
timeout-minutes: 20
strategy:
fail-fast: false
@@ -185,6 +218,10 @@ jobs:
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v1-${{ hashFiles('**/yarn.lock') }}
+ - name: Download Rust amd64 binaries
+ uses: actions/download-artifact@v4
+ with:
+ name: numaflow-rs-linux-amd64
- name: Install k3d
run: curl -sfL https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash &
- name: Create a cluster
diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml
index 0b1a6353d1..3a671b2275 100644
--- a/.github/workflows/nightly-build.yml
+++ b/.github/workflows/nightly-build.yml
@@ -15,7 +15,7 @@ defaults:
shell: bash
jobs:
- build-binaries:
+ build-go-binaries:
runs-on: ubuntu-20.04
if: github.repository == 'numaproj/numaflow'
name: Build binaries
@@ -40,9 +40,70 @@ jobs:
name: binaries
path: dist
+ build-rust-amd64:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: ./rust
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Rust toolchain
+ uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
+ with:
+ rustflags: ''
+ - name: Configure sccache
+ run: |
+ echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
+ echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
+ - name: Run sccache-cache
+ uses: mozilla-actions/sccache-action@v0.0.5
+ - name: Install dependencies
+ run: sudo apt-get install -y protobuf-compiler
+ - name: Build binary
+ run: RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-gnu
+ - name: Rename binary
+ run: cp -pv target/x86_64-unknown-linux-gnu/release/numaflow numaflow-rs-linux-amd64
+ - name: Upload numaflow binary
+ uses: actions/upload-artifact@v3
+ with:
+ name: numaflow-rs-linux-amd64
+ path: rust/numaflow-rs-linux-amd64
+
+ build-rust-arm64:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: ./rust
+ steps:
+ - uses: actions/checkout@v4
+ - name: Update Rust Toolchain Target
+ run: |
+ echo "targets = ['aarch64-unknown-linux-gnu']" >> rust-toolchain.toml
+ - name: Setup Rust toolchain
+ uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
+ with:
+ rustflags: ''
+ - name: Configure sccache
+ run: |
+ echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
+ echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
+ - name: Run sccache-cache
+ uses: mozilla-actions/sccache-action@v0.0.5
+ - name: Install dependenices
+ run: sudo apt-get install -y gcc-aarch64-linux-gnu protobuf-compiler
+ - name: Build binary
+ run: RUSTFLAGS='-C target-feature=+crt-static -C linker=aarch64-linux-gnu-gcc' cargo build --release --target aarch64-unknown-linux-gnu
+ - name: Rename binary
+ run: cp -pv target/aarch64-unknown-linux-gnu/release/numaflow numaflow-rs-linux-arm64
+ - name: Upload numaflow binary
+ uses: actions/upload-artifact@v3
+ with:
+ name: numaflow-rs-linux-arm64
+ path: rust/numaflow-rs-linux-arm64
+
build-push-linux-multi:
name: Build & push linux/amd64 and linux/arm64
- needs: [ build-binaries ]
+ needs: [ build-go-binaries, build-rust-amd64, build-rust-arm64]
runs-on: ubuntu-20.04
if: github.repository == 'numaproj/numaflow'
strategy:
@@ -63,12 +124,24 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- - name: Download binaries
+ - name: Download Go binaries
uses: actions/download-artifact@v3
with:
name: binaries
path: dist/
+ - name: Download Rust amd64 binaries
+ uses: actions/download-artifact@v3
+ with:
+ name: numaflow-rs-linux-amd64
+ path: dist/numaflow-rs-linux-amd64
+
+ - name: Download Rust arm64 binaries
+ uses: actions/download-artifact@v3
+ with:
+ name: numaflow-rs-linux-arm64
+ path: dist/numaflow-rs-linux-arm64
+
- name: Registry Login
uses: docker/login-action@v2
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e5b597014d..5c17591fe8 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,7 +13,7 @@ defaults:
shell: bash
jobs:
- build-binaries:
+ build-go-binaries:
runs-on: ubuntu-20.04
if: github.repository == 'numaproj/numaflow'
name: Build binaries
@@ -38,9 +38,58 @@ jobs:
name: binaries
path: dist
+ build-rust-amd64:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: ./rust
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Rust toolchain
+ uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
+ with:
+ rustflags: ''
+ - name: Install dependencies
+ run: sudo apt-get install -y protobuf-compiler
+ - name: Build binary
+ run: RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-gnu
+ - name: Rename binary
+ run: cp -pv target/x86_64-unknown-linux-gnu/release/numaflow numaflow-rs-linux-amd64
+ - name: Upload numaflow binary
+ uses: actions/upload-artifact@v3
+ with:
+ name: numaflow-rs-linux-amd64
+ path: rust/numaflow-rs-linux-amd64
+
+ build-rust-arm64:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: ./rust
+ steps:
+ - uses: actions/checkout@v4
+ - name: Update Rust Toolchain Target
+ run: |
+ echo "targets = ['aarch64-unknown-linux-gnu']" >> rust-toolchain.toml
+ - name: Setup Rust toolchain
+ uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
+ with:
+ rustflags: ''
+ - name: Install dependenices
+ run: sudo apt-get install -y gcc-aarch64-linux-gnu protobuf-compiler
+ - name: Build binary
+ run: RUSTFLAGS='-C target-feature=+crt-static -C linker=aarch64-linux-gnu-gcc' cargo build --release --target aarch64-unknown-linux-gnu
+ - name: Rename binary
+ run: cp -pv target/aarch64-unknown-linux-gnu/release/numaflow numaflow-rs-linux-arm64
+ - name: Upload numaflow binary
+ uses: actions/upload-artifact@v3
+ with:
+ name: numaflow-rs-linux-arm64
+ path: rust/numaflow-rs-linux-arm64
+
build-push-linux-multi:
name: Build & push linux/amd64 and linux/arm64
- needs: [ build-binaries ]
+ needs: [ build-go-binaries, build-rust-amd64, build-rust-arm64]
runs-on: ubuntu-20.04
if: github.repository == 'numaproj/numaflow'
strategy:
@@ -61,12 +110,24 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- - name: Download binaries
+ - name: Download Go binaries
uses: actions/download-artifact@v3
with:
name: binaries
path: dist/
+ - name: Download Rust amd64 binaries
+ uses: actions/download-artifact@v3
+ with:
+ name: numaflow-rs-linux-amd64
+ path: dist/numaflow-rs-linux-amd64
+
+ - name: Download Rust arm64 binaries
+ uses: actions/download-artifact@v3
+ with:
+ name: numaflow-rs-linux-arm64
+ path: dist/numaflow-rs-linux-arm64
+
- name: Registry Login
uses: docker/login-action@v2
with:
diff --git a/Dockerfile b/Dockerfile
index c5ac53cbb1..efee5a8fd2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,41 +8,30 @@ ARG ARCH
RUN apk update && apk upgrade && \
apk add ca-certificates && \
apk --no-cache add tzdata
-ARG ARCH
COPY dist/numaflow-linux-${ARCH} /bin/numaflow
+COPY dist/numaflow-rs-linux-${ARCH} /bin/numaflow-rs
RUN chmod +x /bin/numaflow
+RUN chmod +x /bin/numaflow-rs
####################################################################################################
-# extension base
+# Rust binary
####################################################################################################
-FROM rust:1.80-bookworm AS extension-base
+FROM lukemathwalker/cargo-chef:latest-rust-1.80 AS chef
ARG TARGETPLATFORM
-
-RUN apt-get update && apt-get install protobuf-compiler -y
-
-RUN cargo new numaflow
-# Create a new empty shell project
WORKDIR /numaflow
+RUN apt-get update && apt-get install -y protobuf-compiler
-RUN cargo new servesink
-COPY ./rust/servesink/Cargo.toml ./servesink/
-
-RUN cargo new backoff
-COPY ./rust/backoff/Cargo.toml ./backoff/
-
-RUN cargo new numaflow-models
-COPY ./rust/numaflow-models/Cargo.toml ./numaflow-models/
-RUN cargo new monovertex
-COPY ./rust/monovertex/Cargo.toml ./monovertex/
+FROM chef AS planner
+COPY ./rust/ .
+RUN cargo chef prepare --recipe-path recipe.json
-RUN cargo new serving
-COPY ./rust/serving/Cargo.toml ./serving/Cargo.toml
-
-# Copy all Cargo.toml and Cargo.lock files for caching dependencies
-COPY ./rust/Cargo.toml ./rust/Cargo.lock ./
+FROM chef AS rust-builder
+ARG TARGETPLATFORM
+ARG ARCH
+COPY --from=planner /numaflow/recipe.json recipe.json
# Build to cache dependencies
RUN --mount=type=cache,target=/usr/local/cargo/registry \
@@ -52,18 +41,10 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
"linux/arm64") TARGET="aarch64-unknown-linux-gnu" ;; \
*) echo "Unsupported platform: ${TARGETPLATFORM}" && exit 1 ;; \
esac && \
- mkdir -p src/bin && echo "fn main() {}" > src/bin/main.rs && \
- RUSTFLAGS='-C target-feature=+crt-static' cargo build --workspace --all --release --target ${TARGET}
+ RUSTFLAGS='-C target-feature=+crt-static' cargo chef cook --workspace --release --target ${TARGET} --recipe-path recipe.json
# Copy the actual source code files of the main project and the subprojects
-COPY ./rust/src ./src
-COPY ./rust/servesink/src ./servesink/src
-COPY ./rust/backoff/src ./backoff/src
-COPY ./rust/numaflow-models/src ./numaflow-models/src
-COPY ./rust/serving/src ./serving/src
-COPY ./rust/monovertex/src ./monovertex/src
-COPY ./rust/monovertex/build.rs ./monovertex/build.rs
-COPY ./rust/monovertex/proto ./monovertex/proto
+COPY ./rust/ .
# Build the real binaries
RUN --mount=type=cache,target=/usr/local/cargo/registry \
@@ -73,7 +54,6 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
"linux/arm64") TARGET="aarch64-unknown-linux-gnu" ;; \
*) echo "Unsupported platform: ${TARGETPLATFORM}" && exit 1 ;; \
esac && \
- touch src/bin/main.rs && \
RUSTFLAGS='-C target-feature=+crt-static' cargo build --workspace --all --release --target ${TARGET} && \
cp -pv target/${TARGET}/release/numaflow /root/numaflow
@@ -82,13 +62,14 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
####################################################################################################
ARG BASE_IMAGE
FROM ${BASE_IMAGE} AS numaflow
+ARG ARCH
COPY --from=base /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=base /bin/numaflow /bin/numaflow
+COPY --from=base /bin/numaflow-rs /bin/numaflow-rs
COPY ui/build /ui/build
-COPY --from=extension-base /root/numaflow /bin/numaflow-rs
COPY ./rust/serving/config config
ENTRYPOINT [ "/bin/numaflow" ]
diff --git a/Makefile b/Makefile
index b616d50621..d31c9095e6 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,16 @@ SHELL:=/bin/bash
PACKAGE=github.com/numaproj/numaflow
CURRENT_DIR=$(shell pwd)
+
+HOST_ARCH=$(shell uname -m)
+# Github actions instances are x86_64
+ifeq ($(HOST_ARCH),x86_64)
+ HOST_ARCH=amd64
+endif
+ifeq ($(HOST_ARCH),aarch64)
+ HOST_ARCH=arm64
+endif
+
DIST_DIR=${CURRENT_DIR}/dist
BINARY_NAME:=numaflow
DOCKERFILE:=Dockerfile
@@ -78,7 +88,7 @@ dist/$(BINARY_NAME):
go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/$(BINARY_NAME) ./cmd
dist/e2eapi:
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/e2eapi ./test/e2e-api
+ CGO_ENABLED=0 GOOS=linux go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/e2eapi ./test/e2e-api
dist/$(BINARY_NAME)-%:
CGO_ENABLED=0 $(GOARGS) go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/$(BINARY_NAME)-$* ./cmd
@@ -162,13 +172,27 @@ ui-test: ui-build
./hack/test-ui.sh
.PHONY: image
-image: clean ui-build dist/$(BINARY_NAME)-linux-amd64
- DOCKER_BUILDKIT=1 $(DOCKER) build --build-arg "ARCH=amd64" --build-arg "BASE_IMAGE=$(DEV_BASE_IMAGE)" $(DOCKER_BUILD_ARGS) -t $(IMAGE_NAMESPACE)/$(BINARY_NAME):$(VERSION) --target $(BINARY_NAME) -f $(DOCKERFILE) .
+image: clean ui-build dist/$(BINARY_NAME)-linux-$(HOST_ARCH)
+ifdef GITHUB_ACTIONS
+ # The binary will be built in a separate Github Actions job
+ cp -pv numaflow-rs-linux-amd64 dist/numaflow-rs-linux-amd64
+else
+ $(MAKE) build-rust-in-docker
+endif
+ DOCKER_BUILDKIT=1 $(DOCKER) build --build-arg "BASE_IMAGE=$(DEV_BASE_IMAGE)" $(DOCKER_BUILD_ARGS) -t $(IMAGE_NAMESPACE)/$(BINARY_NAME):$(VERSION) --target $(BINARY_NAME) -f $(DOCKERFILE) .
@if [[ "$(DOCKER_PUSH)" = "true" ]]; then $(DOCKER) push $(IMAGE_NAMESPACE)/$(BINARY_NAME):$(VERSION); fi
ifdef IMAGE_IMPORT_CMD
$(IMAGE_IMPORT_CMD) $(IMAGE_NAMESPACE)/$(BINARY_NAME):$(VERSION)
endif
+.PHONY: build-rust-in-docker
+build-rust-in-docker:
+ mkdir -p dist
+ -$(DOCKER) container ls --all --filter=ancestor='$(IMAGE_NAMESPACE)/$(BINARY_NAME)-rust-builder:$(VERSION)' --format "{{.ID}}" | xargs docker rm
+ -$(DOCKER) image rm $(IMAGE_NAMESPACE)/$(BINARY_NAME)-rust-builder:$(VERSION)
+ DOCKER_BUILDKIT=1 $(DOCKER) build --build-arg "BASE_IMAGE=$(DEV_BASE_IMAGE)" $(DOCKER_BUILD_ARGS) -t $(IMAGE_NAMESPACE)/$(BINARY_NAME)-rust-builder:$(VERSION) --target rust-builder -f $(DOCKERFILE) .
+ export CTR=$$(docker create $(IMAGE_NAMESPACE)/$(BINARY_NAME)-rust-builder:$(VERSION)) && $(DOCKER) cp $$CTR:/root/numaflow dist/numaflow-rs-linux-$(HOST_ARCH) && $(DOCKER) rm $$CTR && $(DOCKER) image rm $(IMAGE_NAMESPACE)/$(BINARY_NAME)-rust-builder:$(VERSION)
+
image-multi: ui-build set-qemu dist/$(BINARY_NAME)-linux-arm64.gz dist/$(BINARY_NAME)-linux-amd64.gz
$(DOCKER) buildx build --sbom=false --provenance=false --build-arg "BASE_IMAGE=$(RELEASE_BASE_IMAGE)" $(DOCKER_BUILD_ARGS) -t $(IMAGE_NAMESPACE)/$(BINARY_NAME):$(VERSION) --target $(BINARY_NAME) --platform linux/amd64,linux/arm64 --file $(DOCKERFILE) ${PUSH_OPTION} .
@@ -232,7 +256,7 @@ start: image
.PHONY: e2eapi-image
e2eapi-image: clean dist/e2eapi
- DOCKER_BUILDKIT=1 $(DOCKER) build . --build-arg "ARCH=amd64" --target e2eapi --tag $(IMAGE_NAMESPACE)/e2eapi:$(VERSION) --build-arg VERSION="$(VERSION)"
+ DOCKER_BUILDKIT=1 $(DOCKER) build . --target e2eapi --tag $(IMAGE_NAMESPACE)/e2eapi:$(VERSION) --build-arg VERSION="$(VERSION)"
@if [[ "$(DOCKER_PUSH)" = "true" ]]; then $(DOCKER) push $(IMAGE_NAMESPACE)/e2eapi:$(VERSION); fi
ifdef IMAGE_IMPORT_CMD
$(IMAGE_IMPORT_CMD) $(IMAGE_NAMESPACE)/e2eapi:$(VERSION)
diff --git a/rust/rust-toolchain.toml b/rust/rust-toolchain.toml
new file mode 100644
index 0000000000..a5b1f06904
--- /dev/null
+++ b/rust/rust-toolchain.toml
@@ -0,0 +1,3 @@
+[toolchain]
+profile = "default"
+channel = "1.80"
From 3f735f76425a15d8670f145e69e3caa044037a2c Mon Sep 17 00:00:00 2001
From: Vedant Gupta <49195734+veds-g@users.noreply.github.com>
Date: Thu, 22 Aug 2024 00:22:53 +0530
Subject: [PATCH 020/188] fix: adding not available for negative processing
rates (#1983)
Signed-off-by: veds-g
---
.../partials/ProcessingRates/index.tsx | 10 +++++++---
.../partials/Graph/partials/CustomNode/index.tsx | 16 ++++++++++++----
.../partials/Graph/partials/CustomNode/style.css | 3 +--
3 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx
index 87b7c0574f..dd8f4bfd84 100644
--- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx
+++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/ProcessingRates/index.tsx
@@ -56,6 +56,10 @@ export function ProcessingRates({
setFoundRates(rates);
}, [vertexMetrics, pipelineId, vertexId]);
+ const formatRate = (rate?: number): string => {
+ return rate !== undefined && rate >= 0 ? `${rate}/sec` : "Not Available";
+ };
+
return (
{metric.partition}
)}
- {metric.oneM}/sec
- {metric.fiveM}/sec
- {metric.fifteenM}/sec
+ {formatRate(metric.oneM)}
+ {formatRate(metric.fiveM)}
+ {formatRate(metric.fifteenM)}
))}
diff --git a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx
index 64718e4f12..a604a36e14 100644
--- a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx
+++ b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/index.tsx
@@ -298,6 +298,10 @@ const CustomNode: FC = ({
);
}, []);
+ const formatRate = (rate?: number): string => {
+ return rate !== undefined && rate >= 0 ? `${rate}/sec` : "Not Available";
+ };
+
return (
= ({
title={
Processing Rates
- 1 min: {data?.vertexMetrics?.ratePerMin}/sec
- 5 min: {data?.vertexMetrics?.ratePerFiveMin}/sec
- 15 min: {data?.vertexMetrics?.ratePerFifteenMin}/sec
+ 1 min: {formatRate(data?.vertexMetrics?.ratePerMin)}
+
+ 5 min: {formatRate(data?.vertexMetrics?.ratePerFiveMin)}
+
+
+ 15 min: {formatRate(data?.vertexMetrics?.ratePerFifteenMin)}
+
}
arrow
placement={"bottom-end"}
>
- {data?.vertexMetrics?.ratePerMin}/sec
+ {formatRate(data?.vertexMetrics?.ratePerMin)}
diff --git a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css
index 9e9b094718..649d647f8e 100644
--- a/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css
+++ b/ui/src/components/pages/Pipeline/partials/Graph/partials/CustomNode/style.css
@@ -1,6 +1,6 @@
.node-rate {
display: flex;
- width: 9rem;
+ min-width: 9rem;
height: 2.2rem;
border-radius: 2rem;
background: #d1dee9;
@@ -15,7 +15,6 @@
position: absolute;
bottom: -12.5%;
right: 8%;
- text-transform: lowercase;
}
.node-pods {
From b72be93b72e7d5aaacc3b5445f0c0df03d984fd4 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Wed, 21 Aug 2024 14:47:23 -0700
Subject: [PATCH 021/188] chore: enable current/desired replicas (#1987)
Signed-off-by: Derek Wang
---
config/advanced-install/minimal-crds.yaml | 6 ++++++
.../base/crds/full/numaflow.numaproj.io_monovertices.yaml | 6 ++++++
.../crds/minimal/numaflow.numaproj.io_monovertices.yaml | 6 ++++++
config/install.yaml | 6 ++++++
config/namespace-install.yaml | 6 ++++++
pkg/apis/numaflow/v1alpha1/generated.proto | 2 ++
pkg/apis/numaflow/v1alpha1/mono_vertex_types.go | 2 ++
7 files changed, 34 insertions(+)
diff --git a/config/advanced-install/minimal-crds.yaml b/config/advanced-install/minimal-crds.yaml
index f06a75b3f4..9d27719000 100644
--- a/config/advanced-install/minimal-crds.yaml
+++ b/config/advanced-install/minimal-crds.yaml
@@ -69,6 +69,12 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
+ - jsonPath: .spec.replicas
+ name: Desired
+ type: string
+ - jsonPath: .status.replicas
+ name: Current
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index 15d016ba47..ece346f242 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -21,6 +21,12 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
+ - jsonPath: .spec.replicas
+ name: Desired
+ type: string
+ - jsonPath: .status.replicas
+ name: Current
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
index 10c877b13b..ac33f527a9 100644
--- a/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
@@ -17,6 +17,12 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
+ - jsonPath: .spec.replicas
+ name: Desired
+ type: string
+ - jsonPath: .status.replicas
+ name: Current
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/install.yaml b/config/install.yaml
index 7905eb6e56..b8778fdb89 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -2633,6 +2633,12 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
+ - jsonPath: .spec.replicas
+ name: Desired
+ type: string
+ - jsonPath: .status.replicas
+ name: Current
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 5e5b823e35..c265de29f4 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -2633,6 +2633,12 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
+ - jsonPath: .spec.replicas
+ name: Desired
+ type: string
+ - jsonPath: .status.replicas
+ name: Current
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 09b1af32c1..8f12a27eca 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -844,6 +844,8 @@ message Metadata {
// +kubebuilder:subresource:status
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
+// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
+// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason`
// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.message`
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index 4d835e9c2c..5ba6a78c12 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -54,6 +54,8 @@ const (
// +kubebuilder:subresource:status
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
+// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
+// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason`
// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.message`
From 6918e6f47e9309173dd67e6fc0c105d2cd9814f2 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Wed, 21 Aug 2024 19:26:48 -0700
Subject: [PATCH 022/188] fix: do not pass scale info to MonoVertex (#1990)
---
pkg/apis/numaflow/v1alpha1/generated.pb.go | 957 +++++++++---------
.../numaflow/v1alpha1/mono_vertex_types.go | 4 +-
.../v1alpha1/mono_vertex_types_test.go | 88 ++
.../numaflow/v1alpha1/openapi_generated.go | 3 +-
pkg/apis/numaflow/v1alpha1/sink_test.go | 6 +-
.../numaflow/v1alpha1/user_defined_sink.go | 2 +-
.../numaflow/v1alpha1/vertex_types_test.go | 2 +-
.../v1alpha1/zz_generated.deepcopy.go | 6 +-
pkg/reconciler/vertex/controller_test.go | 2 +-
9 files changed, 585 insertions(+), 485 deletions(-)
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index bd85ab3af0..0b8823d5d0 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -2704,471 +2704,471 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7423 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0xc7,
- 0x75, 0xae, 0xe6, 0x8f, 0x33, 0x73, 0x86, 0xe4, 0xee, 0xd6, 0x4a, 0x2b, 0xee, 0x6a, 0xb5, 0x5c,
- 0xb7, 0xae, 0x74, 0xd7, 0xd7, 0x36, 0x79, 0xc5, 0xab, 0x3f, 0xfb, 0xda, 0x96, 0x38, 0xe4, 0x92,
- 0x4b, 0x2d, 0xb9, 0x4b, 0x9f, 0x21, 0x57, 0xb2, 0x75, 0x6d, 0xdd, 0x66, 0x77, 0x71, 0xd8, 0x62,
- 0x4f, 0xf7, 0xa8, 0xbb, 0x87, 0xbb, 0x94, 0xaf, 0xe1, 0xbf, 0x07, 0xe9, 0x22, 0x09, 0x12, 0xf8,
- 0xc9, 0x40, 0xe0, 0x04, 0x09, 0x02, 0xf8, 0xc1, 0x70, 0x1e, 0x02, 0x28, 0x0f, 0x01, 0xf2, 0x07,
- 0x04, 0x89, 0x13, 0xe4, 0xc7, 0x0f, 0x01, 0xa2, 0x20, 0x00, 0x11, 0x33, 0xc8, 0x43, 0x12, 0xc4,
- 0x30, 0x62, 0x20, 0xb6, 0x17, 0x06, 0x1c, 0xd4, 0x5f, 0xff, 0x4d, 0xcf, 0x2e, 0x39, 0x4d, 0xae,
- 0x56, 0x89, 0xde, 0xba, 0xab, 0x4e, 0x7d, 0xa7, 0xfa, 0xd4, 0xcf, 0x39, 0x75, 0xea, 0x54, 0x35,
- 0x2c, 0xb6, 0xad, 0x60, 0xab, 0xb7, 0x31, 0x65, 0xb8, 0x9d, 0x69, 0xa7, 0xd7, 0xd1, 0xbb, 0x9e,
- 0xfb, 0x1a, 0x7f, 0xd8, 0xb4, 0xdd, 0x9b, 0xd3, 0xdd, 0xed, 0xf6, 0xb4, 0xde, 0xb5, 0xfc, 0x28,
- 0x65, 0xe7, 0x49, 0xdd, 0xee, 0x6e, 0xe9, 0x4f, 0x4e, 0xb7, 0xa9, 0x43, 0x3d, 0x3d, 0xa0, 0xe6,
- 0x54, 0xd7, 0x73, 0x03, 0x97, 0x3c, 0x1b, 0x01, 0x4d, 0x29, 0xa0, 0x29, 0x55, 0x6c, 0xaa, 0xbb,
- 0xdd, 0x9e, 0x62, 0x40, 0x51, 0x8a, 0x02, 0x3a, 0xf7, 0x91, 0x58, 0x0d, 0xda, 0x6e, 0xdb, 0x9d,
- 0xe6, 0x78, 0x1b, 0xbd, 0x4d, 0xfe, 0xc6, 0x5f, 0xf8, 0x93, 0xe0, 0x73, 0x4e, 0xdb, 0x7e, 0xce,
- 0x9f, 0xb2, 0x5c, 0x56, 0xad, 0x69, 0xc3, 0xf5, 0xe8, 0xf4, 0x4e, 0x5f, 0x5d, 0xce, 0x3d, 0x15,
- 0xd1, 0x74, 0x74, 0x63, 0xcb, 0x72, 0xa8, 0xb7, 0xab, 0xbe, 0x65, 0xda, 0xa3, 0xbe, 0xdb, 0xf3,
- 0x0c, 0x7a, 0xa8, 0x52, 0xfe, 0x74, 0x87, 0x06, 0x7a, 0x16, 0xaf, 0xe9, 0x41, 0xa5, 0xbc, 0x9e,
- 0x13, 0x58, 0x9d, 0x7e, 0x36, 0xcf, 0xdc, 0xad, 0x80, 0x6f, 0x6c, 0xd1, 0x8e, 0x9e, 0x2e, 0xa7,
- 0xfd, 0x5d, 0x1d, 0x4e, 0xcf, 0x6e, 0xf8, 0x81, 0xa7, 0x1b, 0xc1, 0xaa, 0x6b, 0xae, 0xd1, 0x4e,
- 0xd7, 0xd6, 0x03, 0x4a, 0xb6, 0xa1, 0xc6, 0xea, 0x66, 0xea, 0x81, 0x3e, 0x51, 0xb8, 0x58, 0xb8,
- 0xd4, 0x98, 0x99, 0x9d, 0x1a, 0xb2, 0x2d, 0xa6, 0x56, 0x24, 0x50, 0x73, 0x74, 0x7f, 0x6f, 0xb2,
- 0xa6, 0xde, 0x30, 0x64, 0x40, 0xbe, 0x5e, 0x80, 0x51, 0xc7, 0x35, 0x69, 0x8b, 0xda, 0xd4, 0x08,
- 0x5c, 0x6f, 0xa2, 0x78, 0xb1, 0x74, 0xa9, 0x31, 0xf3, 0xb9, 0xa1, 0x39, 0x66, 0x7c, 0xd1, 0xd4,
- 0xb5, 0x18, 0x83, 0xcb, 0x4e, 0xe0, 0xed, 0x36, 0x1f, 0xfc, 0xce, 0xde, 0xe4, 0x03, 0xfb, 0x7b,
- 0x93, 0xa3, 0xf1, 0x2c, 0x4c, 0xd4, 0x84, 0xac, 0x43, 0x23, 0x70, 0x6d, 0x26, 0x32, 0xcb, 0x75,
- 0xfc, 0x89, 0x12, 0xaf, 0xd8, 0x85, 0x29, 0x21, 0x6d, 0xc6, 0x7e, 0x8a, 0x75, 0x97, 0xa9, 0x9d,
- 0x27, 0xa7, 0xd6, 0x42, 0xb2, 0xe6, 0x69, 0x09, 0xdc, 0x88, 0xd2, 0x7c, 0x8c, 0xe3, 0x10, 0x0a,
- 0x27, 0x7c, 0x6a, 0xf4, 0x3c, 0x2b, 0xd8, 0x9d, 0x73, 0x9d, 0x80, 0xde, 0x0a, 0x26, 0xca, 0x5c,
- 0xca, 0x4f, 0x64, 0x41, 0xaf, 0xba, 0x66, 0x2b, 0x49, 0xdd, 0x3c, 0xbd, 0xbf, 0x37, 0x79, 0x22,
- 0x95, 0x88, 0x69, 0x4c, 0xe2, 0xc0, 0x49, 0xab, 0xa3, 0xb7, 0xe9, 0x6a, 0xcf, 0xb6, 0x5b, 0xd4,
- 0xf0, 0x68, 0xe0, 0x4f, 0x54, 0xf8, 0x27, 0x5c, 0xca, 0xe2, 0xb3, 0xec, 0x1a, 0xba, 0x7d, 0x7d,
- 0xe3, 0x35, 0x6a, 0x04, 0x48, 0x37, 0xa9, 0x47, 0x1d, 0x83, 0x36, 0x27, 0xe4, 0xc7, 0x9c, 0x5c,
- 0x4a, 0x21, 0x61, 0x1f, 0x36, 0x59, 0x84, 0x53, 0x5d, 0xcf, 0x72, 0x79, 0x15, 0x6c, 0xdd, 0xf7,
- 0xaf, 0xe9, 0x1d, 0x3a, 0x31, 0x72, 0xb1, 0x70, 0xa9, 0xde, 0x3c, 0x2b, 0x61, 0x4e, 0xad, 0xa6,
- 0x09, 0xb0, 0xbf, 0x0c, 0xb9, 0x04, 0x35, 0x95, 0x38, 0x51, 0xbd, 0x58, 0xb8, 0x54, 0x11, 0x7d,
- 0x47, 0x95, 0xc5, 0x30, 0x97, 0x2c, 0x40, 0x4d, 0xdf, 0xdc, 0xb4, 0x1c, 0x46, 0x59, 0xe3, 0x22,
- 0x3c, 0x9f, 0xf5, 0x69, 0xb3, 0x92, 0x46, 0xe0, 0xa8, 0x37, 0x0c, 0xcb, 0x92, 0x17, 0x81, 0xf8,
- 0xd4, 0xdb, 0xb1, 0x0c, 0x3a, 0x6b, 0x18, 0x6e, 0xcf, 0x09, 0x78, 0xdd, 0xeb, 0xbc, 0xee, 0xe7,
- 0x64, 0xdd, 0x49, 0xab, 0x8f, 0x02, 0x33, 0x4a, 0x91, 0x17, 0xe0, 0xa4, 0x1c, 0x76, 0x91, 0x14,
- 0x80, 0x23, 0x3d, 0xc8, 0x04, 0x89, 0xa9, 0x3c, 0xec, 0xa3, 0x26, 0x26, 0x9c, 0xd7, 0x7b, 0x81,
- 0xdb, 0x61, 0x90, 0x49, 0xa6, 0x6b, 0xee, 0x36, 0x75, 0x26, 0x1a, 0x17, 0x0b, 0x97, 0x6a, 0xcd,
- 0x8b, 0xfb, 0x7b, 0x93, 0xe7, 0x67, 0xef, 0x40, 0x87, 0x77, 0x44, 0x21, 0xd7, 0xa1, 0x6e, 0x3a,
- 0xfe, 0xaa, 0x6b, 0x5b, 0xc6, 0xee, 0xc4, 0x28, 0xaf, 0xe0, 0x93, 0xf2, 0x53, 0xeb, 0xf3, 0xd7,
- 0x5a, 0x22, 0xe3, 0xf6, 0xde, 0xe4, 0xf9, 0xfe, 0xd9, 0x71, 0x2a, 0xcc, 0xc7, 0x08, 0x83, 0xac,
- 0x70, 0xc0, 0x39, 0xd7, 0xd9, 0xb4, 0xda, 0x13, 0x63, 0xbc, 0x35, 0x2e, 0x0e, 0xe8, 0xd0, 0xf3,
- 0xd7, 0x5a, 0x82, 0xae, 0x39, 0x26, 0xd9, 0x89, 0x57, 0x8c, 0x10, 0xce, 0x3d, 0x0f, 0xa7, 0xfa,
- 0x46, 0x2d, 0x39, 0x09, 0xa5, 0x6d, 0xba, 0xcb, 0x27, 0xa5, 0x3a, 0xb2, 0x47, 0xf2, 0x20, 0x54,
- 0x76, 0x74, 0xbb, 0x47, 0x27, 0x8a, 0x3c, 0x4d, 0xbc, 0x7c, 0xac, 0xf8, 0x5c, 0x41, 0xfb, 0xf5,
- 0x12, 0x8c, 0xaa, 0xb9, 0xa0, 0x65, 0x39, 0xdb, 0xe4, 0x25, 0x28, 0xd9, 0x6e, 0x5b, 0xce, 0x68,
- 0x1f, 0x1f, 0x7a, 0x7e, 0x59, 0x76, 0xdb, 0xcd, 0xea, 0xfe, 0xde, 0x64, 0x69, 0xd9, 0x6d, 0x23,
- 0x43, 0x24, 0x06, 0x54, 0xb6, 0xf5, 0xcd, 0x6d, 0x9d, 0xd7, 0xa1, 0x31, 0xd3, 0x1c, 0x1a, 0xfa,
- 0x2a, 0x43, 0x61, 0x75, 0x6d, 0xd6, 0xf7, 0xf7, 0x26, 0x2b, 0xfc, 0x15, 0x05, 0x36, 0x71, 0xa1,
- 0xbe, 0x61, 0xeb, 0xc6, 0xf6, 0x96, 0x6b, 0xd3, 0x89, 0x52, 0x4e, 0x46, 0x4d, 0x85, 0x24, 0x1a,
- 0x20, 0x7c, 0xc5, 0x88, 0x07, 0x31, 0x60, 0xa4, 0x67, 0xfa, 0x96, 0xb3, 0x2d, 0x67, 0xa7, 0xe7,
- 0x87, 0xe6, 0xb6, 0x3e, 0xcf, 0xbf, 0x09, 0xf6, 0xf7, 0x26, 0x47, 0xc4, 0x33, 0x4a, 0x68, 0xed,
- 0xfb, 0x0d, 0x18, 0x57, 0x8d, 0x74, 0x83, 0x7a, 0x01, 0xbd, 0x45, 0x2e, 0x42, 0xd9, 0x61, 0x83,
- 0x86, 0x37, 0x72, 0x73, 0x54, 0xf6, 0xc9, 0x32, 0x1f, 0x2c, 0x3c, 0x87, 0xd5, 0x4c, 0x28, 0x5c,
- 0x29, 0xf0, 0xe1, 0x6b, 0xd6, 0xe2, 0x30, 0xa2, 0x66, 0xe2, 0x19, 0x25, 0x34, 0x79, 0x05, 0xca,
- 0xfc, 0xe3, 0x85, 0xa8, 0x3f, 0x31, 0x3c, 0x0b, 0xf6, 0xe9, 0x35, 0xf6, 0x05, 0xfc, 0xc3, 0x39,
- 0x28, 0xeb, 0x8a, 0x3d, 0x73, 0x53, 0x0a, 0xf6, 0xe3, 0x39, 0x04, 0xbb, 0x20, 0xba, 0xe2, 0xfa,
- 0xfc, 0x02, 0x32, 0x44, 0xf2, 0x8b, 0x05, 0x38, 0x65, 0xb8, 0x4e, 0xa0, 0x33, 0x23, 0x40, 0xa9,
- 0xbf, 0x89, 0x0a, 0xe7, 0xf3, 0xe2, 0xd0, 0x7c, 0xe6, 0xd2, 0x88, 0xcd, 0x87, 0xd8, 0x6c, 0xde,
- 0x97, 0x8c, 0xfd, 0xbc, 0xc9, 0x2f, 0x17, 0xe0, 0x21, 0x36, 0xcb, 0xf6, 0x11, 0x73, 0xdd, 0x70,
- 0xb4, 0xb5, 0x3a, 0xbb, 0xbf, 0x37, 0xf9, 0xd0, 0x52, 0x16, 0x33, 0xcc, 0xae, 0x03, 0xab, 0xdd,
- 0x69, 0xbd, 0xdf, 0x60, 0xe0, 0x7a, 0xa7, 0x31, 0xb3, 0x7c, 0x94, 0x46, 0x48, 0xf3, 0x11, 0xd9,
- 0x95, 0xb3, 0x6c, 0x2e, 0xcc, 0xaa, 0x05, 0xb9, 0x0c, 0xd5, 0x1d, 0xd7, 0xee, 0x75, 0xa8, 0x3f,
- 0x51, 0xe3, 0x9a, 0xfb, 0x5c, 0xd6, 0x84, 0x7a, 0x83, 0x93, 0x34, 0x4f, 0x48, 0xf8, 0xaa, 0x78,
- 0xf7, 0x51, 0x95, 0x25, 0x16, 0x8c, 0xd8, 0x56, 0xc7, 0x0a, 0x7c, 0xae, 0xd2, 0x1a, 0x33, 0x97,
- 0x87, 0xfe, 0x2c, 0x31, 0x44, 0x97, 0x39, 0x98, 0x18, 0x35, 0xe2, 0x19, 0x25, 0x03, 0x36, 0x15,
- 0xfa, 0x86, 0x6e, 0x0b, 0x95, 0xd7, 0x98, 0xf9, 0xe4, 0xf0, 0xc3, 0x86, 0xa1, 0x34, 0xc7, 0xe4,
- 0x37, 0x55, 0xf8, 0x2b, 0x0a, 0x6c, 0xf2, 0x59, 0x18, 0x4f, 0xb4, 0xa6, 0x3f, 0xd1, 0xe0, 0xd2,
- 0x79, 0x34, 0x4b, 0x3a, 0x21, 0x55, 0xf3, 0x8c, 0x04, 0x1b, 0x4f, 0xf4, 0x10, 0x1f, 0x53, 0x60,
- 0xe4, 0x2a, 0xd4, 0x7c, 0xcb, 0xa4, 0x86, 0xee, 0xf9, 0x13, 0xa3, 0x07, 0x01, 0x3e, 0x29, 0x81,
- 0x6b, 0x2d, 0x59, 0x0c, 0x43, 0x00, 0x32, 0x05, 0xd0, 0xd5, 0xbd, 0xc0, 0x12, 0x26, 0xe4, 0x18,
- 0x37, 0x67, 0xc6, 0xf7, 0xf7, 0x26, 0x61, 0x35, 0x4c, 0xc5, 0x18, 0x05, 0xa3, 0x67, 0x65, 0x97,
- 0x9c, 0x6e, 0x2f, 0xf0, 0x27, 0xc6, 0x2f, 0x96, 0x2e, 0xd5, 0x05, 0x7d, 0x2b, 0x4c, 0xc5, 0x18,
- 0x05, 0xf9, 0x76, 0x01, 0x1e, 0x89, 0x5e, 0xfb, 0x07, 0xd9, 0x89, 0x23, 0x1f, 0x64, 0x93, 0xfb,
- 0x7b, 0x93, 0x8f, 0xb4, 0x06, 0xb3, 0xc4, 0x3b, 0xd5, 0x47, 0x7b, 0x09, 0xc6, 0x66, 0x7b, 0xc1,
- 0x96, 0xeb, 0x59, 0x6f, 0x70, 0x73, 0x98, 0x2c, 0x40, 0x25, 0xe0, 0x66, 0x8d, 0xd0, 0xcb, 0x8f,
- 0x67, 0x89, 0x5a, 0x98, 0x98, 0x57, 0xe9, 0xae, 0xb2, 0x06, 0x84, 0x7e, 0x14, 0x66, 0x8e, 0x28,
- 0xae, 0xfd, 0x5a, 0x01, 0xea, 0x4d, 0xdd, 0xb7, 0x0c, 0x06, 0x4f, 0xe6, 0xa0, 0xdc, 0xf3, 0xa9,
- 0x77, 0x38, 0x50, 0x3e, 0x4b, 0xaf, 0xfb, 0xd4, 0x43, 0x5e, 0x98, 0x5c, 0x87, 0x5a, 0x57, 0xf7,
- 0xfd, 0x9b, 0xae, 0x67, 0x4a, 0x4d, 0x73, 0x40, 0x20, 0x61, 0xaf, 0xca, 0xa2, 0x18, 0x82, 0x68,
- 0x0d, 0x88, 0x54, 0xad, 0xf6, 0xc3, 0x02, 0x9c, 0x6e, 0xf6, 0x36, 0x37, 0xa9, 0x27, 0xcd, 0x33,
- 0x61, 0xf8, 0x10, 0x0a, 0x15, 0x8f, 0x9a, 0x96, 0x2f, 0xeb, 0x3e, 0x3f, 0x74, 0xd3, 0x21, 0x43,
- 0x91, 0x76, 0x16, 0x97, 0x17, 0x4f, 0x40, 0x81, 0x4e, 0x7a, 0x50, 0x7f, 0x8d, 0x06, 0x7e, 0xe0,
- 0x51, 0xbd, 0x23, 0xbf, 0xee, 0xca, 0xd0, 0xac, 0x5e, 0xa4, 0x41, 0x8b, 0x23, 0xc5, 0xcd, 0xba,
- 0x30, 0x11, 0x23, 0x4e, 0xda, 0x1f, 0x56, 0x60, 0x74, 0xce, 0xed, 0x6c, 0x58, 0x0e, 0x35, 0x2f,
- 0x9b, 0x6d, 0x4a, 0x5e, 0x85, 0x32, 0x35, 0xdb, 0x54, 0x7e, 0xed, 0xf0, 0x7a, 0x96, 0x81, 0x45,
- 0xd6, 0x02, 0x7b, 0x43, 0x0e, 0x4c, 0x96, 0x61, 0x7c, 0xd3, 0x73, 0x3b, 0x62, 0xea, 0x5a, 0xdb,
- 0xed, 0x4a, 0x53, 0xb1, 0xf9, 0xdf, 0xd4, 0x74, 0xb0, 0x90, 0xc8, 0xbd, 0xbd, 0x37, 0x09, 0xd1,
- 0x1b, 0xa6, 0xca, 0x92, 0x97, 0x61, 0x22, 0x4a, 0x09, 0xc7, 0xf0, 0x1c, 0xb3, 0xab, 0xb9, 0xa9,
- 0x50, 0x69, 0x9e, 0xdf, 0xdf, 0x9b, 0x9c, 0x58, 0x18, 0x40, 0x83, 0x03, 0x4b, 0x93, 0x37, 0x0b,
- 0x70, 0x32, 0xca, 0x14, 0xf3, 0xaa, 0xb4, 0x10, 0x8e, 0x68, 0xc2, 0xe6, 0x0b, 0x90, 0x85, 0x14,
- 0x0b, 0xec, 0x63, 0x4a, 0x16, 0x60, 0x34, 0x70, 0x63, 0xf2, 0xaa, 0x70, 0x79, 0x69, 0x6a, 0xc5,
- 0xbc, 0xe6, 0x0e, 0x94, 0x56, 0xa2, 0x1c, 0x41, 0x38, 0xa3, 0xde, 0x53, 0x92, 0x1a, 0xe1, 0x92,
- 0x3a, 0xb7, 0xbf, 0x37, 0x79, 0x66, 0x2d, 0x93, 0x02, 0x07, 0x94, 0x24, 0x5f, 0x2e, 0xc0, 0xb8,
- 0xca, 0x92, 0x32, 0xaa, 0x1e, 0xa5, 0x8c, 0x08, 0xeb, 0x11, 0x6b, 0x09, 0x06, 0x98, 0x62, 0xa8,
- 0xfd, 0xb8, 0x0c, 0xf5, 0x70, 0x66, 0x23, 0x8f, 0x41, 0x85, 0xaf, 0x85, 0xa5, 0xc1, 0x1a, 0xaa,
- 0x2c, 0xbe, 0x64, 0x46, 0x91, 0x47, 0x1e, 0x87, 0xaa, 0xe1, 0x76, 0x3a, 0xba, 0x63, 0x72, 0xff,
- 0x46, 0xbd, 0xd9, 0x60, 0x9a, 0x7a, 0x4e, 0x24, 0xa1, 0xca, 0x23, 0xe7, 0xa1, 0xac, 0x7b, 0x6d,
- 0xe1, 0x6a, 0xa8, 0x8b, 0xf9, 0x68, 0xd6, 0x6b, 0xfb, 0xc8, 0x53, 0xc9, 0x47, 0xa1, 0x44, 0x9d,
- 0x9d, 0x89, 0xf2, 0x60, 0x53, 0xe0, 0xb2, 0xb3, 0x73, 0x43, 0xf7, 0x9a, 0x0d, 0x59, 0x87, 0xd2,
- 0x65, 0x67, 0x07, 0x59, 0x19, 0xb2, 0x0c, 0x55, 0xea, 0xec, 0xb0, 0xb6, 0x97, 0x3e, 0x80, 0x0f,
- 0x0c, 0x28, 0xce, 0x48, 0xa4, 0x55, 0x1c, 0x1a, 0x14, 0x32, 0x19, 0x15, 0x04, 0xf9, 0x34, 0x8c,
- 0x0a, 0xdb, 0x62, 0x85, 0xb5, 0x89, 0x3f, 0x31, 0xc2, 0x21, 0x27, 0x07, 0x1b, 0x27, 0x9c, 0x2e,
- 0xf2, 0xb9, 0xc4, 0x12, 0x7d, 0x4c, 0x40, 0x91, 0x4f, 0x43, 0x5d, 0xb9, 0xd3, 0x54, 0xcb, 0x66,
- 0xba, 0x2b, 0x50, 0x12, 0x21, 0x7d, 0xbd, 0x67, 0x79, 0xb4, 0x43, 0x9d, 0xc0, 0x6f, 0x9e, 0x52,
- 0x0b, 0x58, 0x95, 0xeb, 0x63, 0x84, 0x46, 0x36, 0xfa, 0xfd, 0x2e, 0xc2, 0x69, 0xf0, 0xd8, 0x80,
- 0x59, 0x7d, 0x08, 0xa7, 0xcb, 0xe7, 0xe0, 0x44, 0xe8, 0x18, 0x91, 0x6b, 0x6b, 0xe1, 0x46, 0x78,
- 0x8a, 0x15, 0x5f, 0x4a, 0x66, 0xdd, 0xde, 0x9b, 0x7c, 0x34, 0x63, 0x75, 0x1d, 0x11, 0x60, 0x1a,
- 0x4c, 0xfb, 0xfd, 0x12, 0xf4, 0x9b, 0xdd, 0x49, 0xa1, 0x15, 0x8e, 0x5a, 0x68, 0xe9, 0x0f, 0x12,
- 0xd3, 0xe7, 0x73, 0xb2, 0x58, 0xfe, 0x8f, 0xca, 0x6a, 0x98, 0xd2, 0x51, 0x37, 0xcc, 0xfd, 0x32,
- 0x76, 0xb4, 0xb7, 0xca, 0x30, 0x3e, 0xaf, 0xd3, 0x8e, 0xeb, 0xdc, 0x75, 0x11, 0x52, 0xb8, 0x2f,
- 0x16, 0x21, 0x97, 0xa0, 0xe6, 0xd1, 0xae, 0x6d, 0x19, 0xba, 0xcf, 0x9b, 0x5e, 0xba, 0xe3, 0x50,
- 0xa6, 0x61, 0x98, 0x3b, 0x60, 0xf1, 0x59, 0xba, 0x2f, 0x17, 0x9f, 0xe5, 0x77, 0x7f, 0xf1, 0xa9,
- 0x7d, 0xb9, 0x08, 0xdc, 0x50, 0x21, 0x17, 0xa1, 0xcc, 0x94, 0x70, 0xda, 0xe5, 0xc1, 0x3b, 0x0e,
- 0xcf, 0x21, 0xe7, 0xa0, 0x18, 0xb8, 0x72, 0xe4, 0x81, 0xcc, 0x2f, 0xae, 0xb9, 0x58, 0x0c, 0x5c,
- 0xf2, 0x06, 0x80, 0xe1, 0x3a, 0xa6, 0xa5, 0xbc, 0xd4, 0xf9, 0x3e, 0x6c, 0xc1, 0xf5, 0x6e, 0xea,
- 0x9e, 0x39, 0x17, 0x22, 0x8a, 0xe5, 0x47, 0xf4, 0x8e, 0x31, 0x6e, 0xe4, 0x79, 0x18, 0x71, 0x9d,
- 0x85, 0x9e, 0x6d, 0x73, 0x81, 0xd6, 0x9b, 0xff, 0x9d, 0xad, 0x09, 0xaf, 0xf3, 0x94, 0xdb, 0x7b,
- 0x93, 0x67, 0x85, 0x7d, 0xcb, 0xde, 0x5e, 0xf2, 0xac, 0xc0, 0x72, 0xda, 0xad, 0xc0, 0xd3, 0x03,
- 0xda, 0xde, 0x45, 0x59, 0x4c, 0xfb, 0x5a, 0x01, 0x1a, 0x0b, 0xd6, 0x2d, 0x6a, 0xbe, 0x64, 0x39,
- 0xa6, 0x7b, 0x93, 0x20, 0x8c, 0xd8, 0xd4, 0x69, 0x07, 0x5b, 0xb2, 0xf7, 0x4f, 0xc5, 0xc6, 0x5a,
- 0xb8, 0xb9, 0x11, 0xd5, 0xbf, 0x43, 0x03, 0x9d, 0x8d, 0xbe, 0xf9, 0x9e, 0x74, 0xbf, 0x8b, 0x45,
- 0x29, 0x47, 0x40, 0x89, 0x44, 0xa6, 0xa1, 0x2e, 0xac, 0x4f, 0xcb, 0x69, 0x73, 0x19, 0xd6, 0xa2,
- 0x49, 0xaf, 0xa5, 0x32, 0x30, 0xa2, 0xd1, 0x76, 0xe1, 0x54, 0x9f, 0x18, 0x88, 0x09, 0xe5, 0x40,
- 0x6f, 0xab, 0xf9, 0x75, 0x61, 0x68, 0x01, 0xaf, 0xe9, 0xed, 0x98, 0x70, 0xb9, 0x8e, 0x5f, 0xd3,
- 0x99, 0x8e, 0x67, 0xe8, 0xda, 0x4f, 0x0b, 0x50, 0x5b, 0xe8, 0x39, 0x06, 0x5f, 0x1b, 0xdd, 0xdd,
- 0x15, 0xa6, 0x0c, 0x86, 0x62, 0xa6, 0xc1, 0xd0, 0x83, 0x91, 0xed, 0x9b, 0xa1, 0x41, 0xd1, 0x98,
- 0x59, 0x19, 0xbe, 0x57, 0xc8, 0x2a, 0x4d, 0x5d, 0xe5, 0x78, 0x62, 0x0f, 0x65, 0x5c, 0x56, 0x68,
- 0xe4, 0xea, 0x4b, 0x9c, 0xa9, 0x64, 0x76, 0xee, 0xa3, 0xd0, 0x88, 0x91, 0x1d, 0xca, 0x69, 0xfb,
- 0xdb, 0x65, 0x18, 0x59, 0x6c, 0xb5, 0x66, 0x57, 0x97, 0xc8, 0xd3, 0xd0, 0x90, 0xee, 0xf5, 0x6b,
- 0x91, 0x0c, 0xc2, 0xdd, 0x95, 0x56, 0x94, 0x85, 0x71, 0x3a, 0x66, 0x8e, 0x79, 0x54, 0xb7, 0x3b,
- 0x72, 0xb0, 0x84, 0xe6, 0x18, 0xb2, 0x44, 0x14, 0x79, 0x44, 0x87, 0x71, 0xb6, 0xc2, 0x63, 0x22,
- 0x14, 0xab, 0x37, 0x39, 0x6c, 0x0e, 0xb8, 0xbe, 0xe3, 0x46, 0xe2, 0x7a, 0x02, 0x00, 0x53, 0x80,
- 0xe4, 0x39, 0xa8, 0xe9, 0xbd, 0x60, 0x8b, 0x1b, 0xd0, 0x62, 0x6c, 0x9c, 0xe7, 0xbb, 0x0f, 0x32,
- 0xed, 0xf6, 0xde, 0xe4, 0xe8, 0x55, 0x6c, 0x3e, 0xad, 0xde, 0x31, 0xa4, 0x66, 0x95, 0x53, 0x2b,
- 0x46, 0x59, 0xb9, 0xca, 0xa1, 0x2b, 0xb7, 0x9a, 0x00, 0xc0, 0x14, 0x20, 0x79, 0x05, 0x46, 0xb7,
- 0xe9, 0x6e, 0xa0, 0x6f, 0x48, 0x06, 0x23, 0x87, 0x61, 0x70, 0x92, 0x99, 0x70, 0x57, 0x63, 0xc5,
- 0x31, 0x01, 0x46, 0x7c, 0x78, 0x70, 0x9b, 0x7a, 0x1b, 0xd4, 0x73, 0xe5, 0xea, 0x53, 0x32, 0xa9,
- 0x1e, 0x86, 0xc9, 0xc4, 0xfe, 0xde, 0xe4, 0x83, 0x57, 0x33, 0x60, 0x30, 0x13, 0x5c, 0xfb, 0x49,
- 0x11, 0x4e, 0x2c, 0x8a, 0xfd, 0x4d, 0xd7, 0x13, 0x4a, 0x98, 0x9c, 0x85, 0x92, 0xd7, 0xed, 0xf1,
- 0x9e, 0x53, 0x12, 0x7e, 0x52, 0x5c, 0x5d, 0x47, 0x96, 0x46, 0x5e, 0x86, 0x9a, 0x29, 0xa7, 0x0c,
- 0xb9, 0xf8, 0x3d, 0xec, 0x44, 0xc3, 0x95, 0xa0, 0x7a, 0xc3, 0x10, 0x8d, 0x59, 0xfa, 0x1d, 0xbf,
- 0xdd, 0xb2, 0xde, 0xa0, 0x72, 0x3d, 0xc8, 0x2d, 0xfd, 0x15, 0x91, 0x84, 0x2a, 0x8f, 0x69, 0xd5,
- 0x6d, 0xba, 0x2b, 0x56, 0x43, 0xe5, 0x48, 0xab, 0x5e, 0x95, 0x69, 0x18, 0xe6, 0x92, 0x49, 0x35,
- 0x58, 0x58, 0x2f, 0x28, 0x8b, 0x95, 0xfc, 0x0d, 0x96, 0x20, 0xc7, 0x0d, 0x9b, 0x32, 0x5f, 0xb3,
- 0x82, 0x80, 0x7a, 0xb2, 0x19, 0x87, 0x9a, 0x32, 0x5f, 0xe4, 0x08, 0x28, 0x91, 0xc8, 0x87, 0xa0,
- 0xce, 0xc1, 0x9b, 0xb6, 0xbb, 0xc1, 0x1b, 0xae, 0x2e, 0xd6, 0xf4, 0x37, 0x54, 0x22, 0x46, 0xf9,
- 0xda, 0xcf, 0x8a, 0x70, 0x66, 0x91, 0x06, 0xc2, 0xaa, 0x99, 0xa7, 0x5d, 0xdb, 0xdd, 0x65, 0xa6,
- 0x25, 0xd2, 0xd7, 0xc9, 0x0b, 0x00, 0x96, 0xbf, 0xd1, 0xda, 0x31, 0xf8, 0x38, 0x10, 0x63, 0xf8,
- 0xa2, 0x1c, 0x92, 0xb0, 0xd4, 0x6a, 0xca, 0x9c, 0xdb, 0x89, 0x37, 0x8c, 0x95, 0x89, 0x96, 0x57,
- 0xc5, 0x3b, 0x2c, 0xaf, 0x5a, 0x00, 0xdd, 0xc8, 0x40, 0x2d, 0x71, 0xca, 0xff, 0xa5, 0xd8, 0x1c,
- 0xc6, 0x36, 0x8d, 0xc1, 0xe4, 0x31, 0x19, 0x1d, 0x38, 0x69, 0xd2, 0x4d, 0xbd, 0x67, 0x07, 0xa1,
- 0x51, 0x2d, 0x07, 0xf1, 0xc1, 0xed, 0xf2, 0x70, 0xef, 0x75, 0x3e, 0x85, 0x84, 0x7d, 0xd8, 0xda,
- 0xef, 0x94, 0xe0, 0xdc, 0x22, 0x0d, 0x42, 0x8f, 0x8b, 0x9c, 0x1d, 0x5b, 0x5d, 0x6a, 0xb0, 0x56,
- 0x78, 0xb3, 0x00, 0x23, 0xb6, 0xbe, 0x41, 0x6d, 0xa6, 0xbd, 0xd8, 0xd7, 0xbc, 0x3a, 0xb4, 0x22,
- 0x18, 0xcc, 0x65, 0x6a, 0x99, 0x73, 0x48, 0xa9, 0x06, 0x91, 0x88, 0x92, 0x3d, 0x9b, 0xd4, 0x0d,
- 0xbb, 0xe7, 0x07, 0xd4, 0x5b, 0x75, 0xbd, 0x40, 0xda, 0x93, 0xe1, 0xa4, 0x3e, 0x17, 0x65, 0x61,
- 0x9c, 0x8e, 0xcc, 0x00, 0x18, 0xb6, 0x45, 0x9d, 0x80, 0x97, 0x12, 0xe3, 0x8a, 0xa8, 0xf6, 0x9d,
- 0x0b, 0x73, 0x30, 0x46, 0xc5, 0x58, 0x75, 0x5c, 0xc7, 0x0a, 0x5c, 0xc1, 0xaa, 0x9c, 0x64, 0xb5,
- 0x12, 0x65, 0x61, 0x9c, 0x8e, 0x17, 0xa3, 0x81, 0x67, 0x19, 0x3e, 0x2f, 0x56, 0x49, 0x15, 0x8b,
- 0xb2, 0x30, 0x4e, 0xc7, 0x74, 0x5e, 0xec, 0xfb, 0x0f, 0xa5, 0xf3, 0xbe, 0x55, 0x87, 0x0b, 0x09,
- 0xb1, 0x06, 0x7a, 0x40, 0x37, 0x7b, 0x76, 0x8b, 0x06, 0xaa, 0x01, 0x87, 0xd4, 0x85, 0x3f, 0x17,
- 0xb5, 0xbb, 0x88, 0xaa, 0x30, 0x8e, 0xa6, 0xdd, 0xfb, 0x2a, 0x78, 0xa0, 0xb6, 0x9f, 0x86, 0xba,
- 0xa3, 0x07, 0x3e, 0x1f, 0xb8, 0x72, 0x8c, 0x86, 0x66, 0xd8, 0x35, 0x95, 0x81, 0x11, 0x0d, 0x59,
- 0x85, 0x07, 0xa5, 0x88, 0x2f, 0xdf, 0xea, 0xba, 0x5e, 0x40, 0x3d, 0x51, 0x56, 0xaa, 0x53, 0x59,
- 0xf6, 0xc1, 0x95, 0x0c, 0x1a, 0xcc, 0x2c, 0x49, 0x56, 0xe0, 0xb4, 0x21, 0x76, 0x9a, 0xa9, 0xed,
- 0xea, 0xa6, 0x02, 0x14, 0x0e, 0xae, 0x70, 0x69, 0x34, 0xd7, 0x4f, 0x82, 0x59, 0xe5, 0xd2, 0xbd,
- 0x79, 0x64, 0xa8, 0xde, 0x5c, 0x1d, 0xa6, 0x37, 0xd7, 0x86, 0xeb, 0xcd, 0xf5, 0x83, 0xf5, 0x66,
- 0x26, 0x79, 0xd6, 0x8f, 0xa8, 0xc7, 0xcc, 0x13, 0xa1, 0x61, 0x63, 0x81, 0x0c, 0xa1, 0xe4, 0x5b,
- 0x19, 0x34, 0x98, 0x59, 0x92, 0x6c, 0xc0, 0x39, 0x91, 0x7e, 0xd9, 0x31, 0xbc, 0xdd, 0x2e, 0x53,
- 0x3c, 0x31, 0xdc, 0x46, 0xc2, 0xc3, 0x78, 0xae, 0x35, 0x90, 0x12, 0xef, 0x80, 0x42, 0xfe, 0x37,
- 0x8c, 0x89, 0x56, 0x5a, 0xd1, 0xbb, 0x1c, 0x56, 0x84, 0x35, 0x3c, 0x24, 0x61, 0xc7, 0xe6, 0xe2,
- 0x99, 0x98, 0xa4, 0x25, 0xb3, 0x70, 0xa2, 0xbb, 0x63, 0xb0, 0xc7, 0xa5, 0xcd, 0x6b, 0x94, 0x9a,
- 0xd4, 0xe4, 0xbb, 0x35, 0xf5, 0xe6, 0xc3, 0xca, 0xd1, 0xb1, 0x9a, 0xcc, 0xc6, 0x34, 0x3d, 0x79,
- 0x0e, 0x46, 0xfd, 0x40, 0xf7, 0x02, 0xe9, 0xd6, 0x9b, 0x18, 0x17, 0x61, 0x1f, 0xca, 0xeb, 0xd5,
- 0x8a, 0xe5, 0x61, 0x82, 0x32, 0x53, 0x5f, 0x9c, 0x38, 0x3e, 0x7d, 0x91, 0x67, 0xb6, 0xfa, 0x93,
- 0x22, 0x5c, 0x5c, 0xa4, 0xc1, 0x8a, 0xeb, 0x48, 0xa7, 0x68, 0x96, 0xda, 0x3f, 0x90, 0x4f, 0x34,
- 0xa9, 0xb4, 0x8b, 0x47, 0xaa, 0xb4, 0x4b, 0x47, 0xa4, 0xb4, 0xcb, 0xc7, 0xa8, 0xb4, 0x7f, 0xaf,
- 0x08, 0x0f, 0x27, 0x24, 0xb9, 0xea, 0x9a, 0x6a, 0xc2, 0x7f, 0x5f, 0x80, 0x07, 0x10, 0xe0, 0x6d,
- 0x61, 0x77, 0xf2, 0x6d, 0xad, 0x94, 0xc5, 0xf3, 0xd5, 0xb4, 0xc5, 0xf3, 0x4a, 0x1e, 0xcd, 0x97,
- 0xc1, 0xe1, 0x40, 0x1a, 0xef, 0x45, 0x20, 0x9e, 0xdc, 0x84, 0x13, 0xae, 0x9f, 0x98, 0xd1, 0x13,
- 0xc6, 0x95, 0x61, 0x1f, 0x05, 0x66, 0x94, 0x22, 0x2d, 0x78, 0xc8, 0xa7, 0x4e, 0x60, 0x39, 0xd4,
- 0x4e, 0xc2, 0x09, 0x6b, 0xe8, 0x51, 0x09, 0xf7, 0x50, 0x2b, 0x8b, 0x08, 0xb3, 0xcb, 0xe6, 0x99,
- 0x07, 0xfe, 0x1c, 0xb8, 0xc9, 0x29, 0x44, 0x73, 0x64, 0x16, 0xcb, 0x9b, 0x69, 0x8b, 0xe5, 0xd5,
- 0xfc, 0xed, 0x36, 0x9c, 0xb5, 0x32, 0x03, 0xc0, 0x5b, 0x21, 0x6e, 0xae, 0x84, 0x4a, 0x1a, 0xc3,
- 0x1c, 0x8c, 0x51, 0x31, 0x05, 0xa4, 0xe4, 0x1c, 0xb7, 0x54, 0x42, 0x05, 0xd4, 0x8a, 0x67, 0x62,
- 0x92, 0x76, 0xa0, 0xb5, 0x53, 0x19, 0xda, 0xda, 0x79, 0x11, 0x48, 0xc2, 0xf1, 0x28, 0xf0, 0x46,
- 0x92, 0x61, 0x8d, 0x4b, 0x7d, 0x14, 0x98, 0x51, 0x6a, 0x40, 0x57, 0xae, 0x1e, 0x6d, 0x57, 0xae,
- 0x0d, 0xdf, 0x95, 0xc9, 0xab, 0x70, 0x96, 0xb3, 0x92, 0xf2, 0x49, 0x02, 0x0b, 0xbb, 0xe7, 0x03,
- 0x12, 0xf8, 0x2c, 0x0e, 0x22, 0xc4, 0xc1, 0x18, 0xac, 0x7d, 0x0c, 0x8f, 0x9a, 0x8c, 0xb9, 0x6e,
- 0x0f, 0xb6, 0x89, 0xe6, 0x32, 0x68, 0x30, 0xb3, 0x24, 0xeb, 0x62, 0x01, 0xeb, 0x86, 0xfa, 0x86,
- 0x4d, 0x4d, 0x19, 0xd6, 0x19, 0x76, 0xb1, 0xb5, 0xe5, 0x96, 0xcc, 0xc1, 0x18, 0x55, 0x96, 0x99,
- 0x32, 0x7a, 0x48, 0x33, 0x65, 0x91, 0x7b, 0xe9, 0x37, 0x13, 0xd6, 0x90, 0xb4, 0x75, 0xc2, 0x40,
- 0xdd, 0xb9, 0x34, 0x01, 0xf6, 0x97, 0xe1, 0x56, 0xa2, 0xe1, 0x59, 0xdd, 0xc0, 0x4f, 0x62, 0x8d,
- 0xa7, 0xac, 0xc4, 0x0c, 0x1a, 0xcc, 0x2c, 0xc9, 0xec, 0xf3, 0x2d, 0xaa, 0xdb, 0xc1, 0x56, 0x12,
- 0xf0, 0x44, 0xd2, 0x3e, 0xbf, 0xd2, 0x4f, 0x82, 0x59, 0xe5, 0x32, 0x15, 0xd2, 0xc9, 0xfb, 0xd3,
- 0xac, 0xfa, 0x4a, 0x09, 0xce, 0x2e, 0xd2, 0x20, 0x8c, 0xab, 0x79, 0xdf, 0x8d, 0xf2, 0x2e, 0xb8,
- 0x51, 0xbe, 0x59, 0x81, 0xd3, 0x8b, 0x34, 0xe8, 0xb3, 0xc6, 0xfe, 0x8b, 0x8a, 0x7f, 0x05, 0x4e,
- 0x47, 0xa1, 0x5c, 0xad, 0xc0, 0xf5, 0x84, 0x2e, 0x4f, 0xad, 0x96, 0x5b, 0xfd, 0x24, 0x98, 0x55,
- 0x8e, 0x7c, 0x1a, 0x1e, 0xe6, 0xaa, 0xde, 0x69, 0x0b, 0xff, 0xac, 0x70, 0x26, 0xc4, 0x8e, 0x09,
- 0x4c, 0x4a, 0xc8, 0x87, 0x5b, 0xd9, 0x64, 0x38, 0xa8, 0x3c, 0xf9, 0x22, 0x8c, 0x76, 0xad, 0x2e,
- 0xb5, 0x2d, 0x87, 0xdb, 0x67, 0xb9, 0x43, 0x42, 0x56, 0x63, 0x60, 0xd1, 0x02, 0x2e, 0x9e, 0x8a,
- 0x09, 0x86, 0x99, 0x3d, 0xb5, 0x76, 0x8c, 0x3d, 0xf5, 0xdf, 0x8a, 0x50, 0x5d, 0xf4, 0xdc, 0x5e,
- 0xb7, 0xb9, 0x4b, 0xda, 0x30, 0x72, 0x93, 0x6f, 0x9e, 0xc9, 0xad, 0xa9, 0xe1, 0xc3, 0xa1, 0xc5,
- 0x1e, 0x5c, 0x64, 0x12, 0x89, 0x77, 0x94, 0xf0, 0xac, 0x13, 0x6f, 0xd3, 0x5d, 0x6a, 0xca, 0x3d,
- 0xb4, 0xb0, 0x13, 0x5f, 0x65, 0x89, 0x28, 0xf2, 0x48, 0x07, 0x4e, 0xe8, 0xb6, 0xed, 0xde, 0xa4,
- 0xe6, 0xb2, 0x1e, 0x50, 0x87, 0xfa, 0x6a, 0x4b, 0xf2, 0xb0, 0x6e, 0x69, 0xbe, 0xaf, 0x3f, 0x9b,
- 0x84, 0xc2, 0x34, 0x36, 0x79, 0x0d, 0xaa, 0x7e, 0xe0, 0x7a, 0xca, 0xd8, 0x6a, 0xcc, 0xcc, 0x0d,
- 0xdf, 0xe8, 0xcd, 0x4f, 0xb5, 0x04, 0x94, 0xf0, 0xd9, 0xcb, 0x17, 0x54, 0x0c, 0xb4, 0x6f, 0x14,
- 0x00, 0xae, 0xac, 0xad, 0xad, 0xca, 0xed, 0x05, 0x13, 0xca, 0x7a, 0x2f, 0xdc, 0xa8, 0x1c, 0x7e,
- 0x43, 0x30, 0x11, 0x0f, 0x29, 0xf7, 0xf0, 0x7a, 0xc1, 0x16, 0x72, 0x74, 0xf2, 0x41, 0xa8, 0x4a,
- 0x03, 0x59, 0x8a, 0x3d, 0x0c, 0x2d, 0x90, 0x46, 0x34, 0xaa, 0x7c, 0xed, 0xb7, 0x8a, 0x00, 0x4b,
- 0xa6, 0x4d, 0x5b, 0x2a, 0x82, 0xbd, 0x1e, 0x6c, 0x79, 0xd4, 0xdf, 0x72, 0x6d, 0x73, 0xc8, 0xdd,
- 0x54, 0xee, 0xf3, 0x5f, 0x53, 0x20, 0x18, 0xe1, 0x11, 0x13, 0x46, 0xfd, 0x80, 0x76, 0x97, 0x9c,
- 0x80, 0x7a, 0x3b, 0xba, 0x3d, 0xe4, 0x26, 0xca, 0x49, 0xe1, 0x17, 0x89, 0x70, 0x30, 0x81, 0x4a,
- 0x74, 0x68, 0x58, 0x8e, 0x21, 0x06, 0x48, 0x73, 0x77, 0xc8, 0x8e, 0x74, 0x82, 0xad, 0x38, 0x96,
- 0x22, 0x18, 0x8c, 0x63, 0x6a, 0x3f, 0x28, 0xc2, 0x19, 0xce, 0x8f, 0x55, 0x23, 0x11, 0x8f, 0x49,
- 0xfe, 0x6f, 0xdf, 0x39, 0xb8, 0xff, 0x79, 0x30, 0xd6, 0xe2, 0x18, 0xd5, 0x0a, 0x0d, 0xf4, 0xc8,
- 0x9e, 0x8b, 0xd2, 0x62, 0x87, 0xdf, 0x7a, 0x50, 0xf6, 0xd9, 0x7c, 0x25, 0xa4, 0xd7, 0x1a, 0xba,
- 0x0b, 0x65, 0x7f, 0x00, 0x9f, 0xbd, 0xc2, 0x5d, 0x63, 0x3e, 0x6b, 0x71, 0x76, 0xe4, 0x0b, 0x30,
- 0xe2, 0x07, 0x7a, 0xd0, 0x53, 0x43, 0x73, 0xfd, 0xa8, 0x19, 0x73, 0xf0, 0x68, 0x1e, 0x11, 0xef,
- 0x28, 0x99, 0x6a, 0x3f, 0x28, 0xc0, 0xb9, 0xec, 0x82, 0xcb, 0x96, 0x1f, 0x90, 0xff, 0xd3, 0x27,
- 0xf6, 0x03, 0xb6, 0x38, 0x2b, 0xcd, 0x85, 0x1e, 0x06, 0x64, 0xab, 0x94, 0x98, 0xc8, 0x03, 0xa8,
- 0x58, 0x01, 0xed, 0xa8, 0xf5, 0xe5, 0xf5, 0x23, 0xfe, 0xf4, 0x98, 0x6a, 0x67, 0x5c, 0x50, 0x30,
- 0xd3, 0xde, 0x2a, 0x0e, 0xfa, 0x64, 0xae, 0x3e, 0xec, 0x64, 0xcc, 0xef, 0xd5, 0x7c, 0x31, 0xbf,
- 0xc9, 0x0a, 0xf5, 0x87, 0xfe, 0xfe, 0xbf, 0xfe, 0xd0, 0xdf, 0xeb, 0xf9, 0x43, 0x7f, 0x53, 0x62,
- 0x18, 0x18, 0x01, 0xfc, 0x4e, 0x09, 0xce, 0xdf, 0xa9, 0xdb, 0x30, 0x7d, 0x26, 0x7b, 0x67, 0x5e,
- 0x7d, 0x76, 0xe7, 0x7e, 0x48, 0x66, 0xa0, 0xd2, 0xdd, 0xd2, 0x7d, 0x65, 0x94, 0xa9, 0x05, 0x4b,
- 0x65, 0x95, 0x25, 0xde, 0x66, 0x93, 0x06, 0x37, 0xe6, 0xf8, 0x2b, 0x0a, 0x52, 0x36, 0x1d, 0x77,
- 0xa8, 0xef, 0x47, 0x3e, 0x81, 0x70, 0x3a, 0x5e, 0x11, 0xc9, 0xa8, 0xf2, 0x49, 0x00, 0x23, 0xc2,
- 0xc5, 0x2c, 0x35, 0xd3, 0xf0, 0x81, 0x5c, 0x19, 0x61, 0xe2, 0xd1, 0x47, 0xc9, 0xdd, 0x0a, 0xc9,
- 0x8b, 0x4c, 0x41, 0x39, 0x88, 0x82, 0x76, 0xd5, 0xd2, 0xbc, 0x9c, 0x61, 0x9f, 0x72, 0x3a, 0xb6,
- 0xb0, 0x77, 0x37, 0xb8, 0x53, 0xdd, 0x94, 0xfb, 0xe7, 0x96, 0xeb, 0x70, 0x83, 0xac, 0x14, 0x2d,
- 0xec, 0xaf, 0xf7, 0x51, 0x60, 0x46, 0x29, 0xed, 0xaf, 0x6a, 0x70, 0x26, 0xbb, 0x3f, 0x30, 0xb9,
- 0xed, 0x50, 0xcf, 0x67, 0xd8, 0x85, 0xa4, 0xdc, 0x6e, 0x88, 0x64, 0x54, 0xf9, 0xef, 0xe9, 0x80,
- 0xb3, 0x6f, 0x16, 0xe0, 0xac, 0x27, 0xf7, 0x88, 0xee, 0x45, 0xd0, 0xd9, 0xa3, 0xc2, 0x9d, 0x31,
- 0x80, 0x21, 0x0e, 0xae, 0x0b, 0xf9, 0x8d, 0x02, 0x4c, 0x74, 0x52, 0x7e, 0x8e, 0x63, 0x3c, 0x30,
- 0xc6, 0xa3, 0xe2, 0x57, 0x06, 0xf0, 0xc3, 0x81, 0x35, 0x21, 0x5f, 0x84, 0x46, 0x97, 0xf5, 0x0b,
- 0x3f, 0xa0, 0x8e, 0xa1, 0xce, 0x8c, 0x0d, 0x3f, 0x92, 0x56, 0x23, 0x2c, 0x15, 0x8a, 0x26, 0xec,
- 0x83, 0x58, 0x06, 0xc6, 0x39, 0xde, 0xe7, 0x27, 0xc4, 0x2e, 0x41, 0xcd, 0xa7, 0x41, 0x60, 0x39,
- 0x6d, 0xb1, 0xde, 0xa8, 0x8b, 0xb1, 0xd2, 0x92, 0x69, 0x18, 0xe6, 0x92, 0x0f, 0x41, 0x9d, 0x6f,
- 0x39, 0xcd, 0x7a, 0x6d, 0x7f, 0xa2, 0xce, 0xc3, 0xc5, 0xc6, 0x44, 0x00, 0x9c, 0x4c, 0xc4, 0x28,
- 0x9f, 0x3c, 0x05, 0xa3, 0x1b, 0x7c, 0xf8, 0xca, 0xe3, 0xbc, 0xc2, 0xc7, 0xc5, 0xad, 0xb5, 0x66,
- 0x2c, 0x1d, 0x13, 0x54, 0x64, 0x06, 0x80, 0x86, 0xfb, 0x72, 0x69, 0x7f, 0x56, 0xb4, 0x63, 0x87,
- 0x31, 0x2a, 0xf2, 0x28, 0x94, 0x02, 0xdb, 0xe7, 0x3e, 0xac, 0x5a, 0xb4, 0x04, 0x5d, 0x5b, 0x6e,
- 0x21, 0x4b, 0xd7, 0x7e, 0x56, 0x80, 0x13, 0xa9, 0xc3, 0x25, 0xac, 0x48, 0xcf, 0xb3, 0xe5, 0x34,
- 0x12, 0x16, 0x59, 0xc7, 0x65, 0x64, 0xe9, 0xe4, 0x55, 0x69, 0x96, 0x17, 0x73, 0xde, 0x5c, 0x70,
- 0x4d, 0x0f, 0x7c, 0x66, 0x87, 0xf7, 0x59, 0xe4, 0x7c, 0x9b, 0x2f, 0xaa, 0x8f, 0xd4, 0x03, 0xb1,
- 0x6d, 0xbe, 0x28, 0x0f, 0x13, 0x94, 0x29, 0x87, 0x5f, 0xf9, 0x20, 0x0e, 0x3f, 0xed, 0x6b, 0xc5,
- 0x98, 0x04, 0xa4, 0x65, 0x7f, 0x17, 0x09, 0x3c, 0xc1, 0x14, 0x68, 0xa8, 0xdc, 0xeb, 0x71, 0xfd,
- 0xc7, 0x95, 0xb1, 0xcc, 0x25, 0x2f, 0x09, 0xd9, 0x97, 0x72, 0x9e, 0x42, 0x5d, 0x5b, 0x6e, 0x89,
- 0xe8, 0x2a, 0xd5, 0x6a, 0x61, 0x13, 0x94, 0x8f, 0xa9, 0x09, 0xb4, 0x3f, 0x2b, 0x41, 0xe3, 0x45,
- 0x77, 0xe3, 0x3d, 0x12, 0x41, 0x9d, 0xad, 0xa6, 0x8a, 0xef, 0xa2, 0x9a, 0x5a, 0x87, 0x87, 0x83,
- 0xc0, 0x6e, 0x51, 0xc3, 0x75, 0x4c, 0x7f, 0x76, 0x33, 0xa0, 0xde, 0x82, 0xe5, 0x58, 0xfe, 0x16,
- 0x35, 0xe5, 0x76, 0xd2, 0x23, 0xfb, 0x7b, 0x93, 0x0f, 0xaf, 0xad, 0x2d, 0x67, 0x91, 0xe0, 0xa0,
- 0xb2, 0x7c, 0xda, 0xd0, 0x8d, 0x6d, 0x77, 0x73, 0x93, 0x9f, 0x94, 0x91, 0x31, 0x37, 0x62, 0xda,
- 0x88, 0xa5, 0x63, 0x82, 0x4a, 0x7b, 0xbb, 0x08, 0xf5, 0xf0, 0xe4, 0x3b, 0x79, 0x1c, 0xaa, 0x1b,
- 0x9e, 0xbb, 0x4d, 0x3d, 0xb1, 0x73, 0x27, 0x4f, 0xca, 0x34, 0x45, 0x12, 0xaa, 0x3c, 0xf2, 0x18,
- 0x54, 0x02, 0xb7, 0x6b, 0x19, 0x69, 0x87, 0xda, 0x1a, 0x4b, 0x44, 0x91, 0x77, 0x7c, 0x1d, 0xfc,
- 0x89, 0x84, 0x69, 0x57, 0x1f, 0x68, 0x8c, 0xbd, 0x02, 0x65, 0x5f, 0xf7, 0x6d, 0xa9, 0x4f, 0x73,
- 0x1c, 0x22, 0x9f, 0x6d, 0x2d, 0xcb, 0x43, 0xe4, 0xb3, 0xad, 0x65, 0xe4, 0xa0, 0xda, 0x8f, 0x8b,
- 0xd0, 0x10, 0x72, 0x13, 0xb3, 0xc2, 0x51, 0x4a, 0xee, 0x79, 0x1e, 0x4a, 0xe1, 0xf7, 0x3a, 0xd4,
- 0xe3, 0x6e, 0x26, 0x39, 0xc9, 0xc5, 0xf7, 0x07, 0xa2, 0xcc, 0x30, 0x9c, 0x22, 0x4a, 0x52, 0xa2,
- 0x2f, 0x1f, 0xa3, 0xe8, 0x2b, 0x07, 0x12, 0xfd, 0xc8, 0x71, 0x88, 0xfe, 0xcd, 0x22, 0xd4, 0x97,
- 0xad, 0x4d, 0x6a, 0xec, 0x1a, 0x36, 0x3f, 0x13, 0x68, 0x52, 0x9b, 0x06, 0x74, 0xd1, 0xd3, 0x0d,
- 0xba, 0x4a, 0x3d, 0x8b, 0xdf, 0xd9, 0xc2, 0xc6, 0x07, 0x9f, 0x81, 0xe4, 0x99, 0xc0, 0xf9, 0x01,
- 0x34, 0x38, 0xb0, 0x34, 0x59, 0x82, 0x51, 0x93, 0xfa, 0x96, 0x47, 0xcd, 0xd5, 0xd8, 0x42, 0xe5,
- 0x71, 0xa5, 0x6a, 0xe6, 0x63, 0x79, 0xb7, 0xf7, 0x26, 0xc7, 0x94, 0x83, 0x52, 0xac, 0x58, 0x12,
- 0x45, 0xd9, 0x90, 0xef, 0xea, 0x3d, 0x3f, 0xab, 0x8e, 0xb1, 0x21, 0xbf, 0x9a, 0x4d, 0x82, 0x83,
- 0xca, 0x6a, 0x15, 0x28, 0x2d, 0xbb, 0x6d, 0xed, 0xad, 0x12, 0x84, 0x97, 0xfb, 0x90, 0xff, 0x5f,
- 0x80, 0x86, 0xee, 0x38, 0x6e, 0x20, 0x2f, 0xce, 0x11, 0x3b, 0xf0, 0x98, 0xfb, 0x0e, 0xa1, 0xa9,
- 0xd9, 0x08, 0x54, 0x6c, 0xde, 0x86, 0x1b, 0xca, 0xb1, 0x1c, 0x8c, 0xf3, 0x26, 0xbd, 0xd4, 0x7e,
- 0xf2, 0x4a, 0xfe, 0x5a, 0x1c, 0x60, 0xf7, 0xf8, 0xdc, 0x27, 0xe1, 0x64, 0xba, 0xb2, 0x87, 0xd9,
- 0x0e, 0xca, 0xb5, 0x31, 0x5f, 0x04, 0x88, 0x62, 0x4a, 0xee, 0x81, 0x13, 0xcb, 0x4a, 0x38, 0xb1,
- 0x16, 0x87, 0x17, 0x70, 0x58, 0xe9, 0x81, 0x8e, 0xab, 0xd7, 0x53, 0x8e, 0xab, 0xa5, 0xa3, 0x60,
- 0x76, 0x67, 0x67, 0xd5, 0x6f, 0x16, 0xe0, 0x64, 0x44, 0x2c, 0x4f, 0xc8, 0x3e, 0x0b, 0x63, 0x1e,
- 0xd5, 0xcd, 0xa6, 0x1e, 0x18, 0x5b, 0x3c, 0xd4, 0xbb, 0xc0, 0x63, 0xb3, 0x4f, 0xed, 0xef, 0x4d,
- 0x8e, 0x61, 0x3c, 0x03, 0x93, 0x74, 0x44, 0x87, 0x06, 0x4b, 0x58, 0xb3, 0x3a, 0xd4, 0xed, 0x05,
- 0x43, 0x7a, 0x4d, 0xf9, 0x82, 0x05, 0x23, 0x18, 0x8c, 0x63, 0x6a, 0xef, 0x14, 0x60, 0x3c, 0x5e,
- 0xe1, 0x63, 0xf7, 0xa8, 0x6d, 0x25, 0x3d, 0x6a, 0x73, 0x47, 0xd0, 0x26, 0x03, 0xbc, 0x68, 0x3f,
- 0xa9, 0xc5, 0x3f, 0x8d, 0x7b, 0xce, 0xe2, 0xce, 0x82, 0xc2, 0x1d, 0x9d, 0x05, 0xef, 0xfd, 0x5b,
- 0x63, 0x06, 0x59, 0xb9, 0xe5, 0xfb, 0xd8, 0xca, 0x7d, 0x37, 0xaf, 0x9e, 0x89, 0x5d, 0x9f, 0x32,
- 0x92, 0xe3, 0xfa, 0x94, 0x4e, 0x78, 0x7d, 0x4a, 0xf5, 0xc8, 0x26, 0x9d, 0x83, 0x5c, 0xa1, 0x52,
- 0xbb, 0xa7, 0x57, 0xa8, 0xd4, 0x8f, 0xeb, 0x0a, 0x15, 0xc8, 0x7b, 0x85, 0xca, 0x57, 0x0b, 0x30,
- 0x6e, 0x26, 0x4e, 0xcc, 0x72, 0xdf, 0x42, 0x1e, 0x55, 0x93, 0x3c, 0x80, 0x2b, 0x8e, 0x4c, 0x25,
- 0xd3, 0x30, 0xc5, 0x52, 0xfb, 0x51, 0x39, 0xae, 0x07, 0xee, 0xb5, 0xab, 0xfa, 0x99, 0xa4, 0xab,
- 0xfa, 0x62, 0xda, 0x55, 0x7d, 0x22, 0x16, 0x45, 0x1a, 0x77, 0x57, 0x7f, 0x38, 0x36, 0x3d, 0xb2,
- 0x39, 0x69, 0x2c, 0x92, 0x74, 0xc6, 0x14, 0xf9, 0x61, 0xa8, 0xf9, 0xea, 0x1a, 0x46, 0xb1, 0xb0,
- 0x89, 0xda, 0x45, 0x5d, 0x91, 0x18, 0x52, 0x30, 0x4b, 0xdc, 0xa3, 0xba, 0xef, 0x3a, 0x69, 0x4b,
- 0x1c, 0x79, 0x2a, 0xca, 0xdc, 0xb8, 0xcb, 0x7c, 0xe4, 0x2e, 0x2e, 0x73, 0x1d, 0x1a, 0xb6, 0xee,
- 0x07, 0xeb, 0x5d, 0x53, 0x0f, 0xa8, 0x29, 0xc7, 0xdb, 0xff, 0x38, 0x98, 0xae, 0x62, 0xfa, 0x2f,
- 0x32, 0x08, 0x97, 0x23, 0x18, 0x8c, 0x63, 0x12, 0x13, 0x46, 0xd9, 0x2b, 0x1f, 0x0d, 0xe6, 0xac,
- 0xba, 0x02, 0xe0, 0x30, 0x3c, 0x42, 0x4f, 0xcf, 0x72, 0x0c, 0x07, 0x13, 0xa8, 0x03, 0xbc, 0xea,
- 0xf5, 0xa1, 0xbc, 0xea, 0x5f, 0xad, 0x43, 0xe3, 0x9a, 0x1e, 0x58, 0x3b, 0x94, 0xef, 0xe2, 0x1c,
- 0x8f, 0x2b, 0xfd, 0x57, 0x0a, 0x70, 0x26, 0x19, 0xaa, 0x77, 0x8c, 0xfe, 0x74, 0x7e, 0xf1, 0x07,
- 0x66, 0x72, 0xc3, 0x01, 0xb5, 0xe0, 0x9e, 0xf5, 0xbe, 0xc8, 0xbf, 0xe3, 0xf6, 0xac, 0xb7, 0x06,
- 0x31, 0xc4, 0xc1, 0x75, 0x79, 0xaf, 0x78, 0xd6, 0xef, 0xef, 0x8b, 0xd9, 0x52, 0x7e, 0xff, 0xea,
- 0x7d, 0xe3, 0xf7, 0xaf, 0xdd, 0x17, 0xc6, 0x56, 0x37, 0xe6, 0xf7, 0xaf, 0xe7, 0x8c, 0x3f, 0x91,
- 0xd1, 0xed, 0x02, 0x6d, 0xd0, 0xfe, 0x01, 0x3f, 0x98, 0xae, 0xfc, 0xb1, 0xcc, 0x46, 0xd9, 0xd0,
- 0x7d, 0xcb, 0x90, 0x6a, 0x2f, 0xc7, 0x45, 0x94, 0xea, 0xc6, 0x2e, 0xb1, 0x4d, 0xcd, 0x5f, 0x51,
- 0x60, 0x47, 0x37, 0x83, 0x15, 0x73, 0xdd, 0x0c, 0x46, 0xe6, 0xa0, 0xec, 0xb0, 0xd5, 0x73, 0xe9,
- 0xd0, 0x77, 0x81, 0x5d, 0xbb, 0x4a, 0x77, 0x91, 0x17, 0xd6, 0xde, 0x2e, 0x02, 0xb0, 0xcf, 0x3f,
- 0x98, 0x07, 0xfe, 0x83, 0x50, 0xf5, 0x7b, 0x7c, 0xad, 0x2c, 0x15, 0x76, 0x14, 0xb4, 0x23, 0x92,
- 0x51, 0xe5, 0x93, 0xc7, 0xa0, 0xf2, 0x7a, 0x8f, 0xf6, 0xd4, 0x76, 0x72, 0x68, 0xae, 0x7d, 0x8a,
- 0x25, 0xa2, 0xc8, 0x3b, 0x3e, 0x6f, 0x9a, 0xf2, 0xd4, 0x57, 0x8e, 0xcb, 0x53, 0x5f, 0x87, 0xea,
- 0x35, 0x97, 0xc7, 0x00, 0x6a, 0xff, 0x5c, 0x04, 0x88, 0x62, 0xac, 0xc8, 0x37, 0x0a, 0xf0, 0x50,
- 0x38, 0xe0, 0x02, 0x61, 0x75, 0xcf, 0xd9, 0xba, 0xd5, 0xc9, 0xed, 0xb5, 0xcf, 0x1a, 0xec, 0x7c,
- 0x06, 0x5a, 0xcd, 0x62, 0x87, 0xd9, 0xb5, 0x20, 0x08, 0x35, 0xda, 0xe9, 0x06, 0xbb, 0xf3, 0x96,
- 0x27, 0x7b, 0x60, 0x66, 0x28, 0xdf, 0x65, 0x49, 0x23, 0x8a, 0xca, 0xa5, 0x21, 0x1f, 0x44, 0x2a,
- 0x07, 0x43, 0x1c, 0xb2, 0x05, 0x35, 0xc7, 0x7d, 0xd5, 0x67, 0xe2, 0x90, 0xdd, 0xf1, 0x85, 0xe1,
- 0x45, 0x2e, 0xc4, 0x2a, 0xbc, 0xbc, 0xf2, 0x05, 0xab, 0x8e, 0x14, 0xf6, 0xd7, 0x8b, 0x70, 0x3a,
- 0x43, 0x0e, 0xe4, 0x05, 0x38, 0x29, 0xc3, 0xd9, 0xa2, 0xeb, 0x89, 0x0b, 0xd1, 0xf5, 0xc4, 0xad,
- 0x54, 0x1e, 0xf6, 0x51, 0x93, 0x57, 0x01, 0x74, 0xc3, 0xa0, 0xbe, 0xbf, 0xe2, 0x9a, 0xca, 0x1e,
- 0x7d, 0x7e, 0x7f, 0x6f, 0x12, 0x66, 0xc3, 0xd4, 0xdb, 0x7b, 0x93, 0x1f, 0xc9, 0x8a, 0x50, 0x4d,
- 0xc9, 0x39, 0x2a, 0x80, 0x31, 0x48, 0xf2, 0x39, 0x00, 0xb1, 0xf4, 0x0a, 0x0f, 0xd1, 0xdf, 0xc5,
- 0x5f, 0x31, 0xa5, 0xae, 0x2b, 0x9a, 0xfa, 0x54, 0x4f, 0x77, 0x02, 0x2b, 0xd8, 0x15, 0x77, 0x96,
- 0xdc, 0x08, 0x51, 0x30, 0x86, 0xa8, 0xfd, 0x71, 0x11, 0x6a, 0xca, 0x53, 0x7a, 0x0f, 0xdc, 0x63,
- 0xed, 0x84, 0x7b, 0xec, 0x88, 0x62, 0x52, 0xb3, 0x9c, 0x63, 0x6e, 0xca, 0x39, 0xb6, 0x98, 0x9f,
- 0xd5, 0x9d, 0x5d, 0x63, 0xdf, 0x2e, 0xc2, 0xb8, 0x22, 0xcd, 0xeb, 0x18, 0xfb, 0x04, 0x9c, 0x10,
- 0x7b, 0xc9, 0x2b, 0xfa, 0x2d, 0x71, 0x7d, 0x0b, 0x17, 0x58, 0x59, 0x84, 0x81, 0x36, 0x93, 0x59,
- 0x98, 0xa6, 0x65, 0xdd, 0x5a, 0x24, 0xad, 0xb3, 0x75, 0x84, 0xd8, 0x7d, 0x12, 0xeb, 0x1d, 0xde,
- 0xad, 0x9b, 0xa9, 0x3c, 0xec, 0xa3, 0x4e, 0x7b, 0xe6, 0xca, 0xc7, 0xe0, 0x99, 0xfb, 0xeb, 0x02,
- 0x8c, 0x46, 0xf2, 0x3a, 0x76, 0xbf, 0xdc, 0x66, 0xd2, 0x2f, 0x37, 0x9b, 0xbb, 0x3b, 0x0c, 0xf0,
- 0xca, 0xfd, 0x42, 0x15, 0x12, 0xa1, 0xd1, 0x64, 0x03, 0xce, 0x59, 0x99, 0x01, 0x5e, 0xb1, 0xd9,
- 0x26, 0x3c, 0xeb, 0xbb, 0x34, 0x90, 0x12, 0xef, 0x80, 0x42, 0x7a, 0x50, 0xdb, 0xa1, 0x5e, 0x60,
- 0x19, 0x54, 0x7d, 0xdf, 0x62, 0x6e, 0x93, 0x4c, 0xfa, 0x1e, 0x43, 0x99, 0xde, 0x90, 0x0c, 0x30,
- 0x64, 0x45, 0x36, 0xa0, 0x42, 0xcd, 0x36, 0x55, 0x17, 0xea, 0xe4, 0xbc, 0xae, 0x32, 0x94, 0x27,
- 0x7b, 0xf3, 0x51, 0x40, 0x13, 0x1f, 0xea, 0xb6, 0xda, 0x5b, 0x92, 0xfd, 0x70, 0x78, 0x03, 0x2b,
- 0xdc, 0xa5, 0x8a, 0xce, 0xda, 0x87, 0x49, 0x18, 0xf1, 0x21, 0xdb, 0xa1, 0x93, 0xab, 0x72, 0x44,
- 0x93, 0xc7, 0x1d, 0x5c, 0x5c, 0x3e, 0xd4, 0x6f, 0xea, 0x01, 0xf5, 0x3a, 0xba, 0xb7, 0x2d, 0x57,
- 0x1b, 0xc3, 0x7f, 0xe1, 0x4b, 0x0a, 0x29, 0xfa, 0xc2, 0x30, 0x09, 0x23, 0x3e, 0xc4, 0x85, 0x7a,
- 0x20, 0xcd, 0x67, 0xe5, 0xc9, 0x1b, 0x9e, 0xa9, 0x32, 0xc4, 0x7d, 0x19, 0x22, 0xad, 0x5e, 0x31,
- 0xe2, 0x41, 0x76, 0x12, 0x57, 0xf9, 0x8a, 0x0b, 0x9c, 0x9b, 0x39, 0x3c, 0xc2, 0x12, 0x2a, 0x52,
- 0x37, 0xd9, 0x57, 0x02, 0x6b, 0x6f, 0x57, 0xa2, 0x69, 0xf9, 0x5e, 0xfb, 0xa9, 0x9e, 0x4a, 0xfa,
- 0xa9, 0x2e, 0xa4, 0xfd, 0x54, 0xa9, 0x2d, 0xca, 0xc3, 0x07, 0x55, 0xa6, 0x3c, 0x44, 0xe5, 0x63,
- 0xf0, 0x10, 0x3d, 0x09, 0x8d, 0x1d, 0x3e, 0x13, 0x88, 0xdb, 0x79, 0x2a, 0x5c, 0x8d, 0xf0, 0x99,
- 0xfd, 0x46, 0x94, 0x8c, 0x71, 0x1a, 0x56, 0x44, 0x58, 0x20, 0xd1, 0xf5, 0xa6, 0xb2, 0x48, 0x2b,
- 0x4a, 0xc6, 0x38, 0x0d, 0x8f, 0xc7, 0xb2, 0x9c, 0x6d, 0x51, 0xa0, 0xca, 0x0b, 0x88, 0x78, 0x2c,
- 0x95, 0x88, 0x51, 0x3e, 0xb9, 0x04, 0xb5, 0x9e, 0xb9, 0x29, 0x68, 0x6b, 0x9c, 0x96, 0x5b, 0x98,
- 0xeb, 0xf3, 0x0b, 0xf2, 0xb6, 0x20, 0x95, 0xcb, 0x6a, 0xd2, 0xd1, 0xbb, 0x2a, 0x83, 0xaf, 0x0d,
- 0x65, 0x4d, 0x56, 0xa2, 0x64, 0x8c, 0xd3, 0x90, 0x8f, 0xc1, 0xb8, 0x47, 0xcd, 0x9e, 0x41, 0xc3,
- 0x52, 0xc0, 0x4b, 0x71, 0xaf, 0x28, 0x26, 0x72, 0x30, 0x45, 0x39, 0xc0, 0xcf, 0xd5, 0x18, 0xca,
- 0xcf, 0xf5, 0xfd, 0x02, 0x90, 0xfe, 0xf8, 0x65, 0xb2, 0x05, 0x23, 0x0e, 0xf7, 0x7e, 0xe5, 0xbe,
- 0x10, 0x39, 0xe6, 0x44, 0x13, 0xd3, 0x92, 0x4c, 0x90, 0xf8, 0xc4, 0x81, 0x1a, 0xbd, 0x15, 0x50,
- 0xcf, 0x09, 0xcf, 0x33, 0x1c, 0xcd, 0xe5, 0xcb, 0x62, 0x35, 0x20, 0x91, 0x31, 0xe4, 0xa1, 0xfd,
- 0xb0, 0x08, 0x8d, 0x18, 0xdd, 0xdd, 0x16, 0x95, 0xfc, 0x48, 0xb5, 0x70, 0x3a, 0xad, 0x7b, 0xb6,
- 0x1c, 0x61, 0xb1, 0x23, 0xd5, 0x32, 0x0b, 0x97, 0x31, 0x4e, 0x47, 0x66, 0x00, 0x3a, 0xba, 0x1f,
- 0x50, 0x8f, 0x6b, 0xdf, 0xd4, 0x41, 0xe6, 0x95, 0x30, 0x07, 0x63, 0x54, 0xe4, 0xa2, 0xbc, 0x3e,
- 0xbb, 0x9c, 0xbc, 0x78, 0x6e, 0xc0, 0xdd, 0xd8, 0x95, 0x23, 0xb8, 0x1b, 0x9b, 0xb4, 0xe1, 0xa4,
- 0xaa, 0xb5, 0xca, 0x3d, 0xdc, 0xb5, 0x64, 0x62, 0xfd, 0x92, 0x82, 0xc0, 0x3e, 0x50, 0xed, 0xed,
- 0x02, 0x8c, 0x25, 0x5c, 0x1e, 0xe2, 0xca, 0x38, 0x15, 0x7d, 0x9f, 0xb8, 0x32, 0x2e, 0x16, 0x34,
- 0xff, 0x04, 0x8c, 0x08, 0x01, 0xa5, 0x83, 0xea, 0x84, 0x08, 0x51, 0xe6, 0xb2, 0xb9, 0x4c, 0x3a,
- 0x55, 0xd3, 0x73, 0x99, 0xf4, 0xba, 0xa2, 0xca, 0x17, 0xee, 0x76, 0x51, 0xbb, 0x7e, 0x77, 0xbb,
- 0x48, 0xc7, 0x90, 0x42, 0xfb, 0x51, 0x09, 0x78, 0x08, 0x0a, 0x79, 0x16, 0xea, 0x1d, 0x6a, 0x6c,
- 0xe9, 0x8e, 0xe5, 0xab, 0x2b, 0x23, 0xd9, 0xea, 0xb6, 0xbe, 0xa2, 0x12, 0x6f, 0x33, 0x80, 0xd9,
- 0xd6, 0x32, 0x8f, 0xf2, 0x8e, 0x68, 0x89, 0x01, 0x23, 0x6d, 0xdf, 0xd7, 0xbb, 0x56, 0xee, 0x1d,
- 0x50, 0x71, 0x45, 0x9f, 0x18, 0x44, 0xe2, 0x19, 0x25, 0x34, 0x31, 0xa0, 0xd2, 0xb5, 0x75, 0xcb,
- 0xc9, 0xfd, 0x8f, 0x12, 0xf6, 0x05, 0xab, 0x0c, 0x49, 0xb8, 0x74, 0xf8, 0x23, 0x0a, 0x6c, 0xd2,
- 0x83, 0x86, 0x6f, 0x78, 0x7a, 0xc7, 0xdf, 0xd2, 0x67, 0x9e, 0x7e, 0x26, 0xb7, 0x91, 0x14, 0xb1,
- 0x12, 0x73, 0xf6, 0x1c, 0xce, 0xae, 0xb4, 0xae, 0xcc, 0xce, 0x3c, 0xfd, 0x0c, 0xc6, 0xf9, 0xc4,
- 0xd9, 0x3e, 0xfd, 0xe4, 0x8c, 0xec, 0xf7, 0x47, 0xce, 0xf6, 0xe9, 0x27, 0x67, 0x30, 0xce, 0x47,
- 0xfb, 0xf7, 0x02, 0xd4, 0x43, 0x5a, 0xb2, 0x0e, 0xc0, 0x46, 0xa0, 0xbc, 0x54, 0xef, 0x50, 0x17,
- 0xdc, 0xf3, 0x55, 0xf1, 0x7a, 0x58, 0x18, 0x63, 0x40, 0x19, 0xb7, 0x0e, 0x16, 0x8f, 0xfa, 0xd6,
- 0xc1, 0x69, 0xa8, 0x6f, 0xe9, 0x8e, 0xe9, 0x6f, 0xe9, 0xdb, 0x62, 0x22, 0x8a, 0xdd, 0xc3, 0x79,
- 0x45, 0x65, 0x60, 0x44, 0xa3, 0xfd, 0x4b, 0x05, 0xc4, 0xb6, 0x25, 0x1b, 0x2a, 0xa6, 0xe5, 0x8b,
- 0xb8, 0xd9, 0x02, 0x2f, 0x19, 0x0e, 0x95, 0x79, 0x99, 0x8e, 0x21, 0x05, 0x39, 0x0b, 0xa5, 0x8e,
- 0xe5, 0xc8, 0x1d, 0x0f, 0xee, 0xf0, 0x5a, 0xb1, 0x1c, 0x64, 0x69, 0x3c, 0x4b, 0xbf, 0x25, 0x43,
- 0x9e, 0x44, 0x96, 0x7e, 0x0b, 0x59, 0x1a, 0x5b, 0x82, 0xda, 0xae, 0xbb, 0xbd, 0xa1, 0x1b, 0xdb,
- 0x2a, 0x32, 0xaa, 0xcc, 0x15, 0x21, 0x5f, 0x82, 0x2e, 0x27, 0xb3, 0x30, 0x4d, 0x4b, 0x16, 0xe1,
- 0x84, 0xe1, 0xba, 0xb6, 0xe9, 0xde, 0x74, 0x54, 0x71, 0x61, 0x3a, 0xf0, 0x9d, 0x84, 0x79, 0xda,
- 0xf5, 0xa8, 0xc1, 0xec, 0x8b, 0xb9, 0x24, 0x11, 0xa6, 0x4b, 0x91, 0x75, 0x78, 0xf8, 0x0d, 0xea,
- 0xb9, 0x72, 0xba, 0x68, 0xd9, 0x94, 0x76, 0x15, 0xa0, 0x30, 0x2c, 0x78, 0xa4, 0xd6, 0x67, 0xb2,
- 0x49, 0x70, 0x50, 0x59, 0x1e, 0xf3, 0xa9, 0x7b, 0x6d, 0x1a, 0xac, 0x7a, 0xae, 0x41, 0x7d, 0xdf,
- 0x72, 0xda, 0x0a, 0xb6, 0x1a, 0xc1, 0xae, 0x65, 0x93, 0xe0, 0xa0, 0xb2, 0xe4, 0x65, 0x98, 0x10,
- 0x59, 0x42, 0x6b, 0xcf, 0xee, 0xe8, 0x96, 0xad, 0x6f, 0x58, 0xb6, 0xfa, 0x27, 0xd7, 0x98, 0xd8,
- 0xa0, 0x58, 0x1b, 0x40, 0x83, 0x03, 0x4b, 0xf3, 0x3f, 0x69, 0xc9, 0xed, 0xa9, 0x55, 0xea, 0xf1,
- 0x7e, 0x20, 0xed, 0x19, 0xf1, 0x27, 0xad, 0x54, 0x1e, 0xf6, 0x51, 0x13, 0x84, 0x33, 0x7c, 0xbb,
- 0x7b, 0xbd, 0x9b, 0x12, 0xba, 0xb4, 0x70, 0xf8, 0x3e, 0x54, 0x2b, 0x93, 0x02, 0x07, 0x94, 0x64,
- 0xdf, 0xcb, 0x73, 0xe6, 0xdd, 0x9b, 0x4e, 0x1a, 0xb5, 0x11, 0x7d, 0x6f, 0x6b, 0x00, 0x0d, 0x0e,
- 0x2c, 0xad, 0xfd, 0x51, 0x11, 0xc6, 0x12, 0x27, 0x9f, 0xef, 0xbb, 0x13, 0xa6, 0xcc, 0x54, 0xec,
- 0xf8, 0xed, 0xa5, 0xf9, 0x2b, 0x54, 0x37, 0xa9, 0x77, 0x95, 0xaa, 0x53, 0xea, 0x7c, 0xf4, 0xaf,
- 0x24, 0x72, 0x30, 0x45, 0x49, 0x36, 0xa1, 0x22, 0x1c, 0x9f, 0x79, 0xff, 0x69, 0xa0, 0x64, 0xc4,
- 0xbd, 0x9f, 0x5c, 0x37, 0x08, 0xdf, 0xa7, 0x80, 0xd7, 0x02, 0x18, 0x8d, 0x53, 0xb0, 0x11, 0x1f,
- 0x59, 0x55, 0xd5, 0x84, 0x45, 0xb5, 0x04, 0xa5, 0x20, 0x18, 0xf6, 0xec, 0xaa, 0x70, 0xa4, 0xaf,
- 0x2d, 0x23, 0xc3, 0xd0, 0x36, 0x59, 0xdb, 0xf9, 0xbe, 0xe5, 0x3a, 0xf2, 0x22, 0xe3, 0x75, 0xa8,
- 0x06, 0xd2, 0x97, 0x34, 0xdc, 0xd9, 0x5b, 0xee, 0xd7, 0x55, 0x7e, 0x24, 0x85, 0xa5, 0xfd, 0x4d,
- 0x11, 0xea, 0xe1, 0xba, 0xef, 0x00, 0x17, 0x04, 0xbb, 0x50, 0x0f, 0x03, 0x63, 0x72, 0xff, 0x9f,
- 0x2c, 0x8a, 0xd7, 0xe0, 0x4b, 0x95, 0xf0, 0x15, 0x23, 0x1e, 0xf1, 0xa0, 0x9b, 0x52, 0x8e, 0xa0,
- 0x9b, 0x2e, 0x54, 0x03, 0xcf, 0x6a, 0xb7, 0xa5, 0x11, 0x9a, 0x27, 0xea, 0x26, 0x14, 0xd7, 0x9a,
- 0x00, 0x94, 0x92, 0x15, 0x2f, 0xa8, 0xd8, 0x68, 0xaf, 0xc1, 0xc9, 0x34, 0x25, 0xb7, 0xd0, 0x8c,
- 0x2d, 0x6a, 0xf6, 0x6c, 0x25, 0xe3, 0xc8, 0x42, 0x93, 0xe9, 0x18, 0x52, 0xb0, 0x55, 0x1a, 0x6b,
- 0xa6, 0x37, 0x5c, 0x47, 0xad, 0x7f, 0xb9, 0xb1, 0xbb, 0x26, 0xd3, 0x30, 0xcc, 0xd5, 0xfe, 0xa9,
- 0x04, 0x67, 0xa3, 0xd5, 0xfb, 0x8a, 0xee, 0xe8, 0xed, 0x03, 0xfc, 0x94, 0xea, 0xfd, 0xd3, 0x0c,
- 0x87, 0xbd, 0xe5, 0xbd, 0x74, 0x1f, 0xdc, 0xf2, 0xfe, 0xe3, 0x02, 0xf0, 0x20, 0x3e, 0xf2, 0x45,
- 0x18, 0xd5, 0x63, 0xff, 0x23, 0x94, 0xcd, 0x79, 0x39, 0x77, 0x73, 0xf2, 0x58, 0xc1, 0x30, 0x28,
- 0x25, 0x9e, 0x8a, 0x09, 0x86, 0xc4, 0x85, 0xda, 0xa6, 0x6e, 0xdb, 0xcc, 0x68, 0xc9, 0xbd, 0x1b,
- 0x91, 0x60, 0xce, 0xbb, 0xf9, 0x82, 0x84, 0xc6, 0x90, 0x89, 0xf6, 0x8f, 0x05, 0x18, 0x6b, 0xd9,
- 0x96, 0x69, 0x39, 0xed, 0x63, 0xbc, 0xde, 0xfd, 0x3a, 0x54, 0x7c, 0xdb, 0x32, 0xe9, 0x90, 0xf3,
- 0xb8, 0xd0, 0x20, 0x0c, 0x00, 0x05, 0x4e, 0xf2, 0xbe, 0xf8, 0xd2, 0x01, 0xee, 0x8b, 0xff, 0xe9,
- 0x08, 0xc8, 0x40, 0x50, 0xd2, 0x83, 0x7a, 0x5b, 0x5d, 0x43, 0x2d, 0xbf, 0xf1, 0x4a, 0x8e, 0x2b,
- 0xcc, 0x12, 0x17, 0x5a, 0x8b, 0x59, 0x37, 0x4c, 0xc4, 0x88, 0x13, 0xa1, 0xc9, 0x5f, 0x50, 0xce,
+ // 7415 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x24, 0xc7,
+ 0x75, 0xb6, 0xe6, 0xc6, 0x99, 0x39, 0x43, 0x72, 0x77, 0x6b, 0xa5, 0x15, 0x77, 0xb5, 0x5a, 0xae,
+ 0x5b, 0xbf, 0xf4, 0xaf, 0x7f, 0xdb, 0xe4, 0x2f, 0xfe, 0xba, 0xd9, 0xbf, 0x6d, 0x89, 0x43, 0x2e,
+ 0xb9, 0xd4, 0x92, 0xbb, 0xf4, 0x19, 0x72, 0x25, 0x5b, 0xbf, 0xad, 0xbf, 0xd9, 0x5d, 0x1c, 0xb6,
+ 0xd8, 0xd3, 0x3d, 0xea, 0xee, 0xe1, 0x2e, 0xe5, 0x04, 0xbe, 0x3d, 0x48, 0x41, 0x12, 0x24, 0xf0,
+ 0x93, 0x81, 0xc0, 0x09, 0x12, 0x04, 0xf0, 0x83, 0xe1, 0x3c, 0x04, 0x50, 0x1e, 0x02, 0xe4, 0x06,
+ 0x04, 0x89, 0x13, 0xe4, 0xe2, 0x87, 0x00, 0x51, 0x10, 0x80, 0x88, 0x19, 0xe4, 0x21, 0x09, 0x62,
+ 0x18, 0x31, 0x10, 0xdb, 0x0b, 0x03, 0x0e, 0xea, 0xd6, 0xb7, 0xe9, 0xd9, 0x25, 0xa7, 0xc9, 0xd5,
+ 0x2a, 0xd1, 0x5b, 0x77, 0xd5, 0xa9, 0xef, 0x54, 0x9f, 0xba, 0x9d, 0x3a, 0x75, 0xea, 0x34, 0x2c,
+ 0xb6, 0xad, 0x60, 0xab, 0xb7, 0x31, 0x65, 0xb8, 0x9d, 0x69, 0xa7, 0xd7, 0xd1, 0xbb, 0x9e, 0xfb,
+ 0x1a, 0x7f, 0xd8, 0xb4, 0xdd, 0x9b, 0xd3, 0xdd, 0xed, 0xf6, 0xb4, 0xde, 0xb5, 0xfc, 0x28, 0x65,
+ 0xe7, 0x49, 0xdd, 0xee, 0x6e, 0xe9, 0x4f, 0x4e, 0xb7, 0xa9, 0x43, 0x3d, 0x3d, 0xa0, 0xe6, 0x54,
+ 0xd7, 0x73, 0x03, 0x97, 0x3c, 0x1b, 0x01, 0x4d, 0x29, 0xa0, 0x29, 0x55, 0x6c, 0xaa, 0xbb, 0xdd,
+ 0x9e, 0x62, 0x40, 0x51, 0x8a, 0x02, 0x3a, 0xf7, 0x91, 0x58, 0x0d, 0xda, 0x6e, 0xdb, 0x9d, 0xe6,
+ 0x78, 0x1b, 0xbd, 0x4d, 0xfe, 0xc6, 0x5f, 0xf8, 0x93, 0xe0, 0x73, 0x4e, 0xdb, 0x7e, 0xce, 0x9f,
+ 0xb2, 0x5c, 0x56, 0xad, 0x69, 0xc3, 0xf5, 0xe8, 0xf4, 0x4e, 0x5f, 0x5d, 0xce, 0x3d, 0x15, 0xd1,
+ 0x74, 0x74, 0x63, 0xcb, 0x72, 0xa8, 0xb7, 0xab, 0xbe, 0x65, 0xda, 0xa3, 0xbe, 0xdb, 0xf3, 0x0c,
+ 0x7a, 0xa8, 0x52, 0xfe, 0x74, 0x87, 0x06, 0x7a, 0x16, 0xaf, 0xe9, 0x41, 0xa5, 0xbc, 0x9e, 0x13,
+ 0x58, 0x9d, 0x7e, 0x36, 0xcf, 0xdc, 0xad, 0x80, 0x6f, 0x6c, 0xd1, 0x8e, 0x9e, 0x2e, 0xa7, 0xfd,
+ 0x7d, 0x1d, 0x4e, 0xcf, 0x6e, 0xf8, 0x81, 0xa7, 0x1b, 0xc1, 0xaa, 0x6b, 0xae, 0xd1, 0x4e, 0xd7,
+ 0xd6, 0x03, 0x4a, 0xb6, 0xa1, 0xc6, 0xea, 0x66, 0xea, 0x81, 0x3e, 0x51, 0xb8, 0x58, 0xb8, 0xd4,
+ 0x98, 0x99, 0x9d, 0x1a, 0xb2, 0x2d, 0xa6, 0x56, 0x24, 0x50, 0x73, 0x74, 0x7f, 0x6f, 0xb2, 0xa6,
+ 0xde, 0x30, 0x64, 0x40, 0xbe, 0x56, 0x80, 0x51, 0xc7, 0x35, 0x69, 0x8b, 0xda, 0xd4, 0x08, 0x5c,
+ 0x6f, 0xa2, 0x78, 0xb1, 0x74, 0xa9, 0x31, 0xf3, 0xb9, 0xa1, 0x39, 0x66, 0x7c, 0xd1, 0xd4, 0xb5,
+ 0x18, 0x83, 0xcb, 0x4e, 0xe0, 0xed, 0x36, 0x1f, 0xfc, 0xf6, 0xde, 0xe4, 0x03, 0xfb, 0x7b, 0x93,
+ 0xa3, 0xf1, 0x2c, 0x4c, 0xd4, 0x84, 0xac, 0x43, 0x23, 0x70, 0x6d, 0x26, 0x32, 0xcb, 0x75, 0xfc,
+ 0x89, 0x12, 0xaf, 0xd8, 0x85, 0x29, 0x21, 0x6d, 0xc6, 0x7e, 0x8a, 0x75, 0x97, 0xa9, 0x9d, 0x27,
+ 0xa7, 0xd6, 0x42, 0xb2, 0xe6, 0x69, 0x09, 0xdc, 0x88, 0xd2, 0x7c, 0x8c, 0xe3, 0x10, 0x0a, 0x27,
+ 0x7c, 0x6a, 0xf4, 0x3c, 0x2b, 0xd8, 0x9d, 0x73, 0x9d, 0x80, 0xde, 0x0a, 0x26, 0xca, 0x5c, 0xca,
+ 0x4f, 0x64, 0x41, 0xaf, 0xba, 0x66, 0x2b, 0x49, 0xdd, 0x3c, 0xbd, 0xbf, 0x37, 0x79, 0x22, 0x95,
+ 0x88, 0x69, 0x4c, 0xe2, 0xc0, 0x49, 0xab, 0xa3, 0xb7, 0xe9, 0x6a, 0xcf, 0xb6, 0x5b, 0xd4, 0xf0,
+ 0x68, 0xe0, 0x4f, 0x54, 0xf8, 0x27, 0x5c, 0xca, 0xe2, 0xb3, 0xec, 0x1a, 0xba, 0x7d, 0x7d, 0xe3,
+ 0x35, 0x6a, 0x04, 0x48, 0x37, 0xa9, 0x47, 0x1d, 0x83, 0x36, 0x27, 0xe4, 0xc7, 0x9c, 0x5c, 0x4a,
+ 0x21, 0x61, 0x1f, 0x36, 0x59, 0x84, 0x53, 0x5d, 0xcf, 0x72, 0x79, 0x15, 0x6c, 0xdd, 0xf7, 0xaf,
+ 0xe9, 0x1d, 0x3a, 0x31, 0x72, 0xb1, 0x70, 0xa9, 0xde, 0x3c, 0x2b, 0x61, 0x4e, 0xad, 0xa6, 0x09,
+ 0xb0, 0xbf, 0x0c, 0xb9, 0x04, 0x35, 0x95, 0x38, 0x51, 0xbd, 0x58, 0xb8, 0x54, 0x11, 0x7d, 0x47,
+ 0x95, 0xc5, 0x30, 0x97, 0x2c, 0x40, 0x4d, 0xdf, 0xdc, 0xb4, 0x1c, 0x46, 0x59, 0xe3, 0x22, 0x3c,
+ 0x9f, 0xf5, 0x69, 0xb3, 0x92, 0x46, 0xe0, 0xa8, 0x37, 0x0c, 0xcb, 0x92, 0x17, 0x81, 0xf8, 0xd4,
+ 0xdb, 0xb1, 0x0c, 0x3a, 0x6b, 0x18, 0x6e, 0xcf, 0x09, 0x78, 0xdd, 0xeb, 0xbc, 0xee, 0xe7, 0x64,
+ 0xdd, 0x49, 0xab, 0x8f, 0x02, 0x33, 0x4a, 0x91, 0x17, 0xe0, 0xa4, 0x1c, 0x76, 0x91, 0x14, 0x80,
+ 0x23, 0x3d, 0xc8, 0x04, 0x89, 0xa9, 0x3c, 0xec, 0xa3, 0x26, 0x26, 0x9c, 0xd7, 0x7b, 0x81, 0xdb,
+ 0x61, 0x90, 0x49, 0xa6, 0x6b, 0xee, 0x36, 0x75, 0x26, 0x1a, 0x17, 0x0b, 0x97, 0x6a, 0xcd, 0x8b,
+ 0xfb, 0x7b, 0x93, 0xe7, 0x67, 0xef, 0x40, 0x87, 0x77, 0x44, 0x21, 0xd7, 0xa1, 0x6e, 0x3a, 0xfe,
+ 0xaa, 0x6b, 0x5b, 0xc6, 0xee, 0xc4, 0x28, 0xaf, 0xe0, 0x93, 0xf2, 0x53, 0xeb, 0xf3, 0xd7, 0x5a,
+ 0x22, 0xe3, 0xf6, 0xde, 0xe4, 0xf9, 0xfe, 0xd9, 0x71, 0x2a, 0xcc, 0xc7, 0x08, 0x83, 0xac, 0x70,
+ 0xc0, 0x39, 0xd7, 0xd9, 0xb4, 0xda, 0x13, 0x63, 0xbc, 0x35, 0x2e, 0x0e, 0xe8, 0xd0, 0xf3, 0xd7,
+ 0x5a, 0x82, 0xae, 0x39, 0x26, 0xd9, 0x89, 0x57, 0x8c, 0x10, 0xce, 0x3d, 0x0f, 0xa7, 0xfa, 0x46,
+ 0x2d, 0x39, 0x09, 0xa5, 0x6d, 0xba, 0xcb, 0x27, 0xa5, 0x3a, 0xb2, 0x47, 0xf2, 0x20, 0x54, 0x76,
+ 0x74, 0xbb, 0x47, 0x27, 0x8a, 0x3c, 0x4d, 0xbc, 0x7c, 0xac, 0xf8, 0x5c, 0x41, 0xfb, 0x8d, 0x12,
+ 0x8c, 0xaa, 0xb9, 0xa0, 0x65, 0x39, 0xdb, 0xe4, 0x25, 0x28, 0xd9, 0x6e, 0x5b, 0xce, 0x68, 0x1f,
+ 0x1f, 0x7a, 0x7e, 0x59, 0x76, 0xdb, 0xcd, 0xea, 0xfe, 0xde, 0x64, 0x69, 0xd9, 0x6d, 0x23, 0x43,
+ 0x24, 0x06, 0x54, 0xb6, 0xf5, 0xcd, 0x6d, 0x9d, 0xd7, 0xa1, 0x31, 0xd3, 0x1c, 0x1a, 0xfa, 0x2a,
+ 0x43, 0x61, 0x75, 0x6d, 0xd6, 0xf7, 0xf7, 0x26, 0x2b, 0xfc, 0x15, 0x05, 0x36, 0x71, 0xa1, 0xbe,
+ 0x61, 0xeb, 0xc6, 0xf6, 0x96, 0x6b, 0xd3, 0x89, 0x52, 0x4e, 0x46, 0x4d, 0x85, 0x24, 0x1a, 0x20,
+ 0x7c, 0xc5, 0x88, 0x07, 0x31, 0x60, 0xa4, 0x67, 0xfa, 0x96, 0xb3, 0x2d, 0x67, 0xa7, 0xe7, 0x87,
+ 0xe6, 0xb6, 0x3e, 0xcf, 0xbf, 0x09, 0xf6, 0xf7, 0x26, 0x47, 0xc4, 0x33, 0x4a, 0x68, 0xed, 0x7b,
+ 0x0d, 0x18, 0x57, 0x8d, 0x74, 0x83, 0x7a, 0x01, 0xbd, 0x45, 0x2e, 0x42, 0xd9, 0x61, 0x83, 0x86,
+ 0x37, 0x72, 0x73, 0x54, 0xf6, 0xc9, 0x32, 0x1f, 0x2c, 0x3c, 0x87, 0xd5, 0x4c, 0x2c, 0xb8, 0x52,
+ 0xe0, 0xc3, 0xd7, 0xac, 0xc5, 0x61, 0x44, 0xcd, 0xc4, 0x33, 0x4a, 0x68, 0xf2, 0x0a, 0x94, 0xf9,
+ 0xc7, 0x0b, 0x51, 0x7f, 0x62, 0x78, 0x16, 0xec, 0xd3, 0x6b, 0xec, 0x0b, 0xf8, 0x87, 0x73, 0x50,
+ 0xd6, 0x15, 0x7b, 0xe6, 0xa6, 0x14, 0xec, 0xc7, 0x73, 0x08, 0x76, 0x41, 0x74, 0xc5, 0xf5, 0xf9,
+ 0x05, 0x64, 0x88, 0xe4, 0x97, 0x0a, 0x70, 0xca, 0x70, 0x9d, 0x40, 0x67, 0x4a, 0x80, 0x5a, 0xfe,
+ 0x26, 0x2a, 0x9c, 0xcf, 0x8b, 0x43, 0xf3, 0x99, 0x4b, 0x23, 0x36, 0x1f, 0x62, 0xb3, 0x79, 0x5f,
+ 0x32, 0xf6, 0xf3, 0x26, 0xbf, 0x52, 0x80, 0x87, 0xd8, 0x2c, 0xdb, 0x47, 0xcc, 0xd7, 0x86, 0xa3,
+ 0xad, 0xd5, 0xd9, 0xfd, 0xbd, 0xc9, 0x87, 0x96, 0xb2, 0x98, 0x61, 0x76, 0x1d, 0x58, 0xed, 0x4e,
+ 0xeb, 0xfd, 0x0a, 0x03, 0x5f, 0x77, 0x1a, 0x33, 0xcb, 0x47, 0xa9, 0x84, 0x34, 0x1f, 0x91, 0x5d,
+ 0x39, 0x4b, 0xe7, 0xc2, 0xac, 0x5a, 0x90, 0xcb, 0x50, 0xdd, 0x71, 0xed, 0x5e, 0x87, 0xfa, 0x13,
+ 0x35, 0xbe, 0x72, 0x9f, 0xcb, 0x9a, 0x50, 0x6f, 0x70, 0x92, 0xe6, 0x09, 0x09, 0x5f, 0x15, 0xef,
+ 0x3e, 0xaa, 0xb2, 0xc4, 0x82, 0x11, 0xdb, 0xea, 0x58, 0x81, 0xcf, 0x97, 0xb4, 0xc6, 0xcc, 0xe5,
+ 0xa1, 0x3f, 0x4b, 0x0c, 0xd1, 0x65, 0x0e, 0x26, 0x46, 0x8d, 0x78, 0x46, 0xc9, 0x80, 0x4d, 0x85,
+ 0xbe, 0xa1, 0xdb, 0x62, 0xc9, 0x6b, 0xcc, 0x7c, 0x72, 0xf8, 0x61, 0xc3, 0x50, 0x9a, 0x63, 0xf2,
+ 0x9b, 0x2a, 0xfc, 0x15, 0x05, 0x36, 0xf9, 0x2c, 0x8c, 0x27, 0x5a, 0xd3, 0x9f, 0x68, 0x70, 0xe9,
+ 0x3c, 0x9a, 0x25, 0x9d, 0x90, 0xaa, 0x79, 0x46, 0x82, 0x8d, 0x27, 0x7a, 0x88, 0x8f, 0x29, 0x30,
+ 0x72, 0x15, 0x6a, 0xbe, 0x65, 0x52, 0x43, 0xf7, 0xfc, 0x89, 0xd1, 0x83, 0x00, 0x9f, 0x94, 0xc0,
+ 0xb5, 0x96, 0x2c, 0x86, 0x21, 0x00, 0x99, 0x02, 0xe8, 0xea, 0x5e, 0x60, 0x09, 0x15, 0x72, 0x8c,
+ 0xab, 0x33, 0xe3, 0xfb, 0x7b, 0x93, 0xb0, 0x1a, 0xa6, 0x62, 0x8c, 0x82, 0xd1, 0xb3, 0xb2, 0x4b,
+ 0x4e, 0xb7, 0x17, 0xf8, 0x13, 0xe3, 0x17, 0x4b, 0x97, 0xea, 0x82, 0xbe, 0x15, 0xa6, 0x62, 0x8c,
+ 0x82, 0x7c, 0xab, 0x00, 0x8f, 0x44, 0xaf, 0xfd, 0x83, 0xec, 0xc4, 0x91, 0x0f, 0xb2, 0xc9, 0xfd,
+ 0xbd, 0xc9, 0x47, 0x5a, 0x83, 0x59, 0xe2, 0x9d, 0xea, 0xa3, 0xbd, 0x04, 0x63, 0xb3, 0xbd, 0x60,
+ 0xcb, 0xf5, 0xac, 0x37, 0xb8, 0x3a, 0x4c, 0x16, 0xa0, 0x12, 0x70, 0xb5, 0x46, 0xac, 0xcb, 0x8f,
+ 0x67, 0x89, 0x5a, 0xa8, 0x98, 0x57, 0xe9, 0xae, 0xd2, 0x06, 0xc4, 0xfa, 0x28, 0xd4, 0x1c, 0x51,
+ 0x5c, 0xfb, 0xf5, 0x02, 0xd4, 0x9b, 0xba, 0x6f, 0x19, 0x0c, 0x9e, 0xcc, 0x41, 0xb9, 0xe7, 0x53,
+ 0xef, 0x70, 0xa0, 0x7c, 0x96, 0x5e, 0xf7, 0xa9, 0x87, 0xbc, 0x30, 0xb9, 0x0e, 0xb5, 0xae, 0xee,
+ 0xfb, 0x37, 0x5d, 0xcf, 0x94, 0x2b, 0xcd, 0x01, 0x81, 0x84, 0xbe, 0x2a, 0x8b, 0x62, 0x08, 0xa2,
+ 0x35, 0x20, 0x5a, 0x6a, 0xb5, 0x1f, 0x14, 0xe0, 0x74, 0xb3, 0xb7, 0xb9, 0x49, 0x3d, 0xa9, 0x9e,
+ 0x09, 0xc5, 0x87, 0x50, 0xa8, 0x78, 0xd4, 0xb4, 0x7c, 0x59, 0xf7, 0xf9, 0xa1, 0x9b, 0x0e, 0x19,
+ 0x8a, 0xd4, 0xb3, 0xb8, 0xbc, 0x78, 0x02, 0x0a, 0x74, 0xd2, 0x83, 0xfa, 0x6b, 0x34, 0xf0, 0x03,
+ 0x8f, 0xea, 0x1d, 0xf9, 0x75, 0x57, 0x86, 0x66, 0xf5, 0x22, 0x0d, 0x5a, 0x1c, 0x29, 0xae, 0xd6,
+ 0x85, 0x89, 0x18, 0x71, 0xd2, 0xfe, 0xa8, 0x02, 0xa3, 0x73, 0x6e, 0x67, 0xc3, 0x72, 0xa8, 0x79,
+ 0xd9, 0x6c, 0x53, 0xf2, 0x2a, 0x94, 0xa9, 0xd9, 0xa6, 0xf2, 0x6b, 0x87, 0x5f, 0x67, 0x19, 0x58,
+ 0xa4, 0x2d, 0xb0, 0x37, 0xe4, 0xc0, 0x64, 0x19, 0xc6, 0x37, 0x3d, 0xb7, 0x23, 0xa6, 0xae, 0xb5,
+ 0xdd, 0xae, 0x54, 0x15, 0x9b, 0xff, 0x43, 0x4d, 0x07, 0x0b, 0x89, 0xdc, 0xdb, 0x7b, 0x93, 0x10,
+ 0xbd, 0x61, 0xaa, 0x2c, 0x79, 0x19, 0x26, 0xa2, 0x94, 0x70, 0x0c, 0xcf, 0x31, 0xbd, 0x9a, 0xab,
+ 0x0a, 0x95, 0xe6, 0xf9, 0xfd, 0xbd, 0xc9, 0x89, 0x85, 0x01, 0x34, 0x38, 0xb0, 0x34, 0x79, 0xb3,
+ 0x00, 0x27, 0xa3, 0x4c, 0x31, 0xaf, 0x4a, 0x0d, 0xe1, 0x88, 0x26, 0x6c, 0xbe, 0x01, 0x59, 0x48,
+ 0xb1, 0xc0, 0x3e, 0xa6, 0x64, 0x01, 0x46, 0x03, 0x37, 0x26, 0xaf, 0x0a, 0x97, 0x97, 0xa6, 0x76,
+ 0xcc, 0x6b, 0xee, 0x40, 0x69, 0x25, 0xca, 0x11, 0x84, 0x33, 0xea, 0x3d, 0x25, 0xa9, 0x11, 0x2e,
+ 0xa9, 0x73, 0xfb, 0x7b, 0x93, 0x67, 0xd6, 0x32, 0x29, 0x70, 0x40, 0x49, 0xf2, 0xa5, 0x02, 0x8c,
+ 0xab, 0x2c, 0x29, 0xa3, 0xea, 0x51, 0xca, 0x88, 0xb0, 0x1e, 0xb1, 0x96, 0x60, 0x80, 0x29, 0x86,
+ 0xda, 0x8f, 0xca, 0x50, 0x0f, 0x67, 0x36, 0xf2, 0x18, 0x54, 0xf8, 0x5e, 0x58, 0x2a, 0xac, 0xe1,
+ 0x92, 0xc5, 0xb7, 0xcc, 0x28, 0xf2, 0xc8, 0xe3, 0x50, 0x35, 0xdc, 0x4e, 0x47, 0x77, 0x4c, 0x6e,
+ 0xdf, 0xa8, 0x37, 0x1b, 0x6c, 0xa5, 0x9e, 0x13, 0x49, 0xa8, 0xf2, 0xc8, 0x79, 0x28, 0xeb, 0x5e,
+ 0x5b, 0x98, 0x1a, 0xea, 0x62, 0x3e, 0x9a, 0xf5, 0xda, 0x3e, 0xf2, 0x54, 0xf2, 0x51, 0x28, 0x51,
+ 0x67, 0x67, 0xa2, 0x3c, 0x58, 0x15, 0xb8, 0xec, 0xec, 0xdc, 0xd0, 0xbd, 0x66, 0x43, 0xd6, 0xa1,
+ 0x74, 0xd9, 0xd9, 0x41, 0x56, 0x86, 0x2c, 0x43, 0x95, 0x3a, 0x3b, 0xac, 0xed, 0xa5, 0x0d, 0xe0,
+ 0x03, 0x03, 0x8a, 0x33, 0x12, 0xa9, 0x15, 0x87, 0x0a, 0x85, 0x4c, 0x46, 0x05, 0x41, 0x3e, 0x0d,
+ 0xa3, 0x42, 0xb7, 0x58, 0x61, 0x6d, 0xe2, 0x4f, 0x8c, 0x70, 0xc8, 0xc9, 0xc1, 0xca, 0x09, 0xa7,
+ 0x8b, 0x6c, 0x2e, 0xb1, 0x44, 0x1f, 0x13, 0x50, 0xe4, 0xd3, 0x50, 0x57, 0xe6, 0x34, 0xd5, 0xb2,
+ 0x99, 0xe6, 0x0a, 0x94, 0x44, 0x48, 0x5f, 0xef, 0x59, 0x1e, 0xed, 0x50, 0x27, 0xf0, 0x9b, 0xa7,
+ 0xd4, 0x06, 0x56, 0xe5, 0xfa, 0x18, 0xa1, 0x91, 0x8d, 0x7e, 0xbb, 0x8b, 0x30, 0x1a, 0x3c, 0x36,
+ 0x60, 0x56, 0x1f, 0xc2, 0xe8, 0xf2, 0x39, 0x38, 0x11, 0x1a, 0x46, 0xe4, 0xde, 0x5a, 0x98, 0x11,
+ 0x9e, 0x62, 0xc5, 0x97, 0x92, 0x59, 0xb7, 0xf7, 0x26, 0x1f, 0xcd, 0xd8, 0x5d, 0x47, 0x04, 0x98,
+ 0x06, 0xd3, 0xfe, 0xa0, 0x04, 0xfd, 0x6a, 0x77, 0x52, 0x68, 0x85, 0xa3, 0x16, 0x5a, 0xfa, 0x83,
+ 0xc4, 0xf4, 0xf9, 0x9c, 0x2c, 0x96, 0xff, 0xa3, 0xb2, 0x1a, 0xa6, 0x74, 0xd4, 0x0d, 0x73, 0xbf,
+ 0x8c, 0x1d, 0xed, 0xad, 0x32, 0x8c, 0xcf, 0xeb, 0xb4, 0xe3, 0x3a, 0x77, 0xdd, 0x84, 0x14, 0xee,
+ 0x8b, 0x4d, 0xc8, 0x25, 0xa8, 0x79, 0xb4, 0x6b, 0x5b, 0x86, 0xee, 0xf3, 0xa6, 0x97, 0xe6, 0x38,
+ 0x94, 0x69, 0x18, 0xe6, 0x0e, 0xd8, 0x7c, 0x96, 0xee, 0xcb, 0xcd, 0x67, 0xf9, 0xdd, 0xdf, 0x7c,
+ 0x6a, 0x5f, 0x2a, 0x02, 0x57, 0x54, 0xc8, 0x45, 0x28, 0xb3, 0x45, 0x38, 0x6d, 0xf2, 0xe0, 0x1d,
+ 0x87, 0xe7, 0x90, 0x73, 0x50, 0x0c, 0x5c, 0x39, 0xf2, 0x40, 0xe6, 0x17, 0xd7, 0x5c, 0x2c, 0x06,
+ 0x2e, 0x79, 0x03, 0xc0, 0x70, 0x1d, 0xd3, 0x52, 0x56, 0xea, 0x7c, 0x1f, 0xb6, 0xe0, 0x7a, 0x37,
+ 0x75, 0xcf, 0x9c, 0x0b, 0x11, 0xc5, 0xf6, 0x23, 0x7a, 0xc7, 0x18, 0x37, 0xf2, 0x3c, 0x8c, 0xb8,
+ 0xce, 0x42, 0xcf, 0xb6, 0xb9, 0x40, 0xeb, 0xcd, 0xff, 0xc9, 0xf6, 0x84, 0xd7, 0x79, 0xca, 0xed,
+ 0xbd, 0xc9, 0xb3, 0x42, 0xbf, 0x65, 0x6f, 0x2f, 0x79, 0x56, 0x60, 0x39, 0xed, 0x56, 0xe0, 0xe9,
+ 0x01, 0x6d, 0xef, 0xa2, 0x2c, 0xa6, 0x7d, 0xb5, 0x00, 0x8d, 0x05, 0xeb, 0x16, 0x35, 0x5f, 0xb2,
+ 0x1c, 0xd3, 0xbd, 0x49, 0x10, 0x46, 0x6c, 0xea, 0xb4, 0x83, 0x2d, 0xd9, 0xfb, 0xa7, 0x62, 0x63,
+ 0x2d, 0x3c, 0xdc, 0x88, 0xea, 0xdf, 0xa1, 0x81, 0xce, 0x46, 0xdf, 0x7c, 0x4f, 0x9a, 0xdf, 0xc5,
+ 0xa6, 0x94, 0x23, 0xa0, 0x44, 0x22, 0xd3, 0x50, 0x17, 0xda, 0xa7, 0xe5, 0xb4, 0xb9, 0x0c, 0x6b,
+ 0xd1, 0xa4, 0xd7, 0x52, 0x19, 0x18, 0xd1, 0x68, 0xbb, 0x70, 0xaa, 0x4f, 0x0c, 0xc4, 0x84, 0x72,
+ 0xa0, 0xb7, 0xd5, 0xfc, 0xba, 0x30, 0xb4, 0x80, 0xd7, 0xf4, 0x76, 0x4c, 0xb8, 0x7c, 0x8d, 0x5f,
+ 0xd3, 0xd9, 0x1a, 0xcf, 0xd0, 0xb5, 0x9f, 0x14, 0xa0, 0xb6, 0xd0, 0x73, 0x0c, 0xbe, 0x37, 0xba,
+ 0xbb, 0x29, 0x4c, 0x29, 0x0c, 0xc5, 0x4c, 0x85, 0xa1, 0x07, 0x23, 0xdb, 0x37, 0x43, 0x85, 0xa2,
+ 0x31, 0xb3, 0x32, 0x7c, 0xaf, 0x90, 0x55, 0x9a, 0xba, 0xca, 0xf1, 0xc4, 0x19, 0xca, 0xb8, 0xac,
+ 0xd0, 0xc8, 0xd5, 0x97, 0x38, 0x53, 0xc9, 0xec, 0xdc, 0x47, 0xa1, 0x11, 0x23, 0x3b, 0x94, 0xd1,
+ 0xf6, 0x77, 0xca, 0x30, 0xb2, 0xd8, 0x6a, 0xcd, 0xae, 0x2e, 0x91, 0xa7, 0xa1, 0x21, 0xcd, 0xeb,
+ 0xd7, 0x22, 0x19, 0x84, 0xa7, 0x2b, 0xad, 0x28, 0x0b, 0xe3, 0x74, 0x4c, 0x1d, 0xf3, 0xa8, 0x6e,
+ 0x77, 0xe4, 0x60, 0x09, 0xd5, 0x31, 0x64, 0x89, 0x28, 0xf2, 0x88, 0x0e, 0xe3, 0x6c, 0x87, 0xc7,
+ 0x44, 0x28, 0x76, 0x6f, 0x72, 0xd8, 0x1c, 0x70, 0x7f, 0xc7, 0x95, 0xc4, 0xf5, 0x04, 0x00, 0xa6,
+ 0x00, 0xc9, 0x73, 0x50, 0xd3, 0x7b, 0xc1, 0x16, 0x57, 0xa0, 0xc5, 0xd8, 0x38, 0xcf, 0x4f, 0x1f,
+ 0x64, 0xda, 0xed, 0xbd, 0xc9, 0xd1, 0xab, 0xd8, 0x7c, 0x5a, 0xbd, 0x63, 0x48, 0xcd, 0x2a, 0xa7,
+ 0x76, 0x8c, 0xb2, 0x72, 0x95, 0x43, 0x57, 0x6e, 0x35, 0x01, 0x80, 0x29, 0x40, 0xf2, 0x0a, 0x8c,
+ 0x6e, 0xd3, 0xdd, 0x40, 0xdf, 0x90, 0x0c, 0x46, 0x0e, 0xc3, 0xe0, 0x24, 0x53, 0xe1, 0xae, 0xc6,
+ 0x8a, 0x63, 0x02, 0x8c, 0xf8, 0xf0, 0xe0, 0x36, 0xf5, 0x36, 0xa8, 0xe7, 0xca, 0xdd, 0xa7, 0x64,
+ 0x52, 0x3d, 0x0c, 0x93, 0x89, 0xfd, 0xbd, 0xc9, 0x07, 0xaf, 0x66, 0xc0, 0x60, 0x26, 0xb8, 0xf6,
+ 0xe3, 0x22, 0x9c, 0x58, 0x14, 0xe7, 0x9b, 0xae, 0x27, 0x16, 0x61, 0x72, 0x16, 0x4a, 0x5e, 0xb7,
+ 0xc7, 0x7b, 0x4e, 0x49, 0xd8, 0x49, 0x71, 0x75, 0x1d, 0x59, 0x1a, 0x79, 0x19, 0x6a, 0xa6, 0x9c,
+ 0x32, 0xe4, 0xe6, 0xf7, 0xb0, 0x13, 0x0d, 0x5f, 0x04, 0xd5, 0x1b, 0x86, 0x68, 0x4c, 0xd3, 0xef,
+ 0xf8, 0xed, 0x96, 0xf5, 0x06, 0x95, 0xfb, 0x41, 0xae, 0xe9, 0xaf, 0x88, 0x24, 0x54, 0x79, 0x6c,
+ 0x55, 0xdd, 0xa6, 0xbb, 0x62, 0x37, 0x54, 0x8e, 0x56, 0xd5, 0xab, 0x32, 0x0d, 0xc3, 0x5c, 0x32,
+ 0xa9, 0x06, 0x0b, 0xeb, 0x05, 0x65, 0xb1, 0x93, 0xbf, 0xc1, 0x12, 0xe4, 0xb8, 0x61, 0x53, 0xe6,
+ 0x6b, 0x56, 0x10, 0x50, 0x4f, 0x36, 0xe3, 0x50, 0x53, 0xe6, 0x8b, 0x1c, 0x01, 0x25, 0x12, 0xf9,
+ 0x10, 0xd4, 0x39, 0x78, 0xd3, 0x76, 0x37, 0x78, 0xc3, 0xd5, 0xc5, 0x9e, 0xfe, 0x86, 0x4a, 0xc4,
+ 0x28, 0x5f, 0xfb, 0x69, 0x11, 0xce, 0x2c, 0xd2, 0x40, 0x68, 0x35, 0xf3, 0xb4, 0x6b, 0xbb, 0xbb,
+ 0x4c, 0xb5, 0x44, 0xfa, 0x3a, 0x79, 0x01, 0xc0, 0xf2, 0x37, 0x5a, 0x3b, 0x06, 0x1f, 0x07, 0x62,
+ 0x0c, 0x5f, 0x94, 0x43, 0x12, 0x96, 0x5a, 0x4d, 0x99, 0x73, 0x3b, 0xf1, 0x86, 0xb1, 0x32, 0xd1,
+ 0xf6, 0xaa, 0x78, 0x87, 0xed, 0x55, 0x0b, 0xa0, 0x1b, 0x29, 0xa8, 0x25, 0x4e, 0xf9, 0x7f, 0x14,
+ 0x9b, 0xc3, 0xe8, 0xa6, 0x31, 0x98, 0x3c, 0x2a, 0xa3, 0x03, 0x27, 0x4d, 0xba, 0xa9, 0xf7, 0xec,
+ 0x20, 0x54, 0xaa, 0xe5, 0x20, 0x3e, 0xb8, 0x5e, 0x1e, 0x9e, 0xbd, 0xce, 0xa7, 0x90, 0xb0, 0x0f,
+ 0x5b, 0xfb, 0xdd, 0x12, 0x9c, 0x5b, 0xa4, 0x41, 0x68, 0x71, 0x91, 0xb3, 0x63, 0xab, 0x4b, 0x0d,
+ 0xd6, 0x0a, 0x6f, 0x16, 0x60, 0xc4, 0xd6, 0x37, 0xa8, 0xcd, 0x56, 0x2f, 0xf6, 0x35, 0xaf, 0x0e,
+ 0xbd, 0x10, 0x0c, 0xe6, 0x32, 0xb5, 0xcc, 0x39, 0xa4, 0x96, 0x06, 0x91, 0x88, 0x92, 0x3d, 0x9b,
+ 0xd4, 0x0d, 0xbb, 0xe7, 0x07, 0xd4, 0x5b, 0x75, 0xbd, 0x40, 0xea, 0x93, 0xe1, 0xa4, 0x3e, 0x17,
+ 0x65, 0x61, 0x9c, 0x8e, 0xcc, 0x00, 0x18, 0xb6, 0x45, 0x9d, 0x80, 0x97, 0x12, 0xe3, 0x8a, 0xa8,
+ 0xf6, 0x9d, 0x0b, 0x73, 0x30, 0x46, 0xc5, 0x58, 0x75, 0x5c, 0xc7, 0x0a, 0x5c, 0xc1, 0xaa, 0x9c,
+ 0x64, 0xb5, 0x12, 0x65, 0x61, 0x9c, 0x8e, 0x17, 0xa3, 0x81, 0x67, 0x19, 0x3e, 0x2f, 0x56, 0x49,
+ 0x15, 0x8b, 0xb2, 0x30, 0x4e, 0xc7, 0xd6, 0xbc, 0xd8, 0xf7, 0x1f, 0x6a, 0xcd, 0xfb, 0x66, 0x1d,
+ 0x2e, 0x24, 0xc4, 0x1a, 0xe8, 0x01, 0xdd, 0xec, 0xd9, 0x2d, 0x1a, 0xa8, 0x06, 0x1c, 0x72, 0x2d,
+ 0xfc, 0xf9, 0xa8, 0xdd, 0x85, 0x57, 0x85, 0x71, 0x34, 0xed, 0xde, 0x57, 0xc1, 0x03, 0xb5, 0xfd,
+ 0x34, 0xd4, 0x1d, 0x3d, 0xf0, 0xf9, 0xc0, 0x95, 0x63, 0x34, 0x54, 0xc3, 0xae, 0xa9, 0x0c, 0x8c,
+ 0x68, 0xc8, 0x2a, 0x3c, 0x28, 0x45, 0x7c, 0xf9, 0x56, 0xd7, 0xf5, 0x02, 0xea, 0x89, 0xb2, 0x72,
+ 0x39, 0x95, 0x65, 0x1f, 0x5c, 0xc9, 0xa0, 0xc1, 0xcc, 0x92, 0x64, 0x05, 0x4e, 0x1b, 0xe2, 0xa4,
+ 0x99, 0xda, 0xae, 0x6e, 0x2a, 0x40, 0x61, 0xe0, 0x0a, 0xb7, 0x46, 0x73, 0xfd, 0x24, 0x98, 0x55,
+ 0x2e, 0xdd, 0x9b, 0x47, 0x86, 0xea, 0xcd, 0xd5, 0x61, 0x7a, 0x73, 0x6d, 0xb8, 0xde, 0x5c, 0x3f,
+ 0x58, 0x6f, 0x66, 0x92, 0x67, 0xfd, 0x88, 0x7a, 0x4c, 0x3d, 0x11, 0x2b, 0x6c, 0xcc, 0x91, 0x21,
+ 0x94, 0x7c, 0x2b, 0x83, 0x06, 0x33, 0x4b, 0x92, 0x0d, 0x38, 0x27, 0xd2, 0x2f, 0x3b, 0x86, 0xb7,
+ 0xdb, 0x65, 0x0b, 0x4f, 0x0c, 0xb7, 0x91, 0xb0, 0x30, 0x9e, 0x6b, 0x0d, 0xa4, 0xc4, 0x3b, 0xa0,
+ 0x90, 0xff, 0x0b, 0x63, 0xa2, 0x95, 0x56, 0xf4, 0x2e, 0x87, 0x15, 0x6e, 0x0d, 0x0f, 0x49, 0xd8,
+ 0xb1, 0xb9, 0x78, 0x26, 0x26, 0x69, 0xc9, 0x2c, 0x9c, 0xe8, 0xee, 0x18, 0xec, 0x71, 0x69, 0xf3,
+ 0x1a, 0xa5, 0x26, 0x35, 0xf9, 0x69, 0x4d, 0xbd, 0xf9, 0xb0, 0x32, 0x74, 0xac, 0x26, 0xb3, 0x31,
+ 0x4d, 0x4f, 0x9e, 0x83, 0x51, 0x3f, 0xd0, 0xbd, 0x40, 0x9a, 0xf5, 0x26, 0xc6, 0x85, 0xdb, 0x87,
+ 0xb2, 0x7a, 0xb5, 0x62, 0x79, 0x98, 0xa0, 0xcc, 0x5c, 0x2f, 0x4e, 0x1c, 0xdf, 0x7a, 0x91, 0x67,
+ 0xb6, 0xfa, 0xd3, 0x22, 0x5c, 0x5c, 0xa4, 0xc1, 0x8a, 0xeb, 0x48, 0xa3, 0x68, 0xd6, 0xb2, 0x7f,
+ 0x20, 0x9b, 0x68, 0x72, 0xd1, 0x2e, 0x1e, 0xe9, 0xa2, 0x5d, 0x3a, 0xa2, 0x45, 0xbb, 0x7c, 0x8c,
+ 0x8b, 0xf6, 0xef, 0x17, 0xe1, 0xe1, 0x84, 0x24, 0x57, 0x5d, 0x53, 0x4d, 0xf8, 0xef, 0x0b, 0xf0,
+ 0x00, 0x02, 0xbc, 0x2d, 0xf4, 0x4e, 0x7e, 0xac, 0x95, 0xd2, 0x78, 0xbe, 0x92, 0xd6, 0x78, 0x5e,
+ 0xc9, 0xb3, 0xf2, 0x65, 0x70, 0x38, 0xd0, 0x8a, 0xf7, 0x22, 0x10, 0x4f, 0x1e, 0xc2, 0x09, 0xd3,
+ 0x4f, 0x4c, 0xe9, 0x09, 0xfd, 0xca, 0xb0, 0x8f, 0x02, 0x33, 0x4a, 0x91, 0x16, 0x3c, 0xe4, 0x53,
+ 0x27, 0xb0, 0x1c, 0x6a, 0x27, 0xe1, 0x84, 0x36, 0xf4, 0xa8, 0x84, 0x7b, 0xa8, 0x95, 0x45, 0x84,
+ 0xd9, 0x65, 0xf3, 0xcc, 0x03, 0x7f, 0x01, 0x5c, 0xe5, 0x14, 0xa2, 0x39, 0x32, 0x8d, 0xe5, 0xcd,
+ 0xb4, 0xc6, 0xf2, 0x6a, 0xfe, 0x76, 0x1b, 0x4e, 0x5b, 0x99, 0x01, 0xe0, 0xad, 0x10, 0x57, 0x57,
+ 0xc2, 0x45, 0x1a, 0xc3, 0x1c, 0x8c, 0x51, 0xb1, 0x05, 0x48, 0xc9, 0x39, 0xae, 0xa9, 0x84, 0x0b,
+ 0x50, 0x2b, 0x9e, 0x89, 0x49, 0xda, 0x81, 0xda, 0x4e, 0x65, 0x68, 0x6d, 0xe7, 0x45, 0x20, 0x09,
+ 0xc3, 0xa3, 0xc0, 0x1b, 0x49, 0xba, 0x35, 0x2e, 0xf5, 0x51, 0x60, 0x46, 0xa9, 0x01, 0x5d, 0xb9,
+ 0x7a, 0xb4, 0x5d, 0xb9, 0x36, 0x7c, 0x57, 0x26, 0xaf, 0xc2, 0x59, 0xce, 0x4a, 0xca, 0x27, 0x09,
+ 0x2c, 0xf4, 0x9e, 0x0f, 0x48, 0xe0, 0xb3, 0x38, 0x88, 0x10, 0x07, 0x63, 0xb0, 0xf6, 0x31, 0x3c,
+ 0x6a, 0x32, 0xe6, 0xba, 0x3d, 0x58, 0x27, 0x9a, 0xcb, 0xa0, 0xc1, 0xcc, 0x92, 0xac, 0x8b, 0x05,
+ 0xac, 0x1b, 0xea, 0x1b, 0x36, 0x35, 0xa5, 0x5b, 0x67, 0xd8, 0xc5, 0xd6, 0x96, 0x5b, 0x32, 0x07,
+ 0x63, 0x54, 0x59, 0x6a, 0xca, 0xe8, 0x21, 0xd5, 0x94, 0x45, 0x6e, 0xa5, 0xdf, 0x4c, 0x68, 0x43,
+ 0x52, 0xd7, 0x09, 0x1d, 0x75, 0xe7, 0xd2, 0x04, 0xd8, 0x5f, 0x86, 0x6b, 0x89, 0x86, 0x67, 0x75,
+ 0x03, 0x3f, 0x89, 0x35, 0x9e, 0xd2, 0x12, 0x33, 0x68, 0x30, 0xb3, 0x24, 0xd3, 0xcf, 0xb7, 0xa8,
+ 0x6e, 0x07, 0x5b, 0x49, 0xc0, 0x13, 0x49, 0xfd, 0xfc, 0x4a, 0x3f, 0x09, 0x66, 0x95, 0xcb, 0x5c,
+ 0x90, 0x4e, 0xde, 0x9f, 0x6a, 0xd5, 0x97, 0x4b, 0x70, 0x76, 0x91, 0x06, 0xa1, 0x5f, 0xcd, 0xfb,
+ 0x66, 0x94, 0x77, 0xc1, 0x8c, 0xf2, 0x8d, 0x0a, 0x9c, 0x5e, 0xa4, 0x41, 0x9f, 0x36, 0xf6, 0xdf,
+ 0x54, 0xfc, 0x2b, 0x70, 0x3a, 0x72, 0xe5, 0x6a, 0x05, 0xae, 0x27, 0xd6, 0xf2, 0xd4, 0x6e, 0xb9,
+ 0xd5, 0x4f, 0x82, 0x59, 0xe5, 0xc8, 0xa7, 0xe1, 0x61, 0xbe, 0xd4, 0x3b, 0x6d, 0x61, 0x9f, 0x15,
+ 0xc6, 0x84, 0xd8, 0x35, 0x81, 0x49, 0x09, 0xf9, 0x70, 0x2b, 0x9b, 0x0c, 0x07, 0x95, 0x27, 0x5f,
+ 0x80, 0xd1, 0xae, 0xd5, 0xa5, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0xb7, 0x4b, 0xc8, 0x6a, 0x0c, 0x2c,
+ 0xda, 0xc0, 0xc5, 0x53, 0x31, 0xc1, 0x30, 0xb3, 0xa7, 0xd6, 0x8e, 0xb1, 0xa7, 0xfe, 0x7b, 0x11,
+ 0xaa, 0x8b, 0x9e, 0xdb, 0xeb, 0x36, 0x77, 0x49, 0x1b, 0x46, 0x6e, 0xf2, 0xc3, 0x33, 0x79, 0x34,
+ 0x35, 0xbc, 0x3b, 0xb4, 0x38, 0x83, 0x8b, 0x54, 0x22, 0xf1, 0x8e, 0x12, 0x9e, 0x75, 0xe2, 0x6d,
+ 0xba, 0x4b, 0x4d, 0x79, 0x86, 0x16, 0x76, 0xe2, 0xab, 0x2c, 0x11, 0x45, 0x1e, 0xe9, 0xc0, 0x09,
+ 0xdd, 0xb6, 0xdd, 0x9b, 0xd4, 0x5c, 0xd6, 0x03, 0xea, 0x50, 0x5f, 0x1d, 0x49, 0x1e, 0xd6, 0x2c,
+ 0xcd, 0xcf, 0xf5, 0x67, 0x93, 0x50, 0x98, 0xc6, 0x26, 0xaf, 0x41, 0xd5, 0x0f, 0x5c, 0x4f, 0x29,
+ 0x5b, 0x8d, 0x99, 0xb9, 0xe1, 0x1b, 0xbd, 0xf9, 0xa9, 0x96, 0x80, 0x12, 0x36, 0x7b, 0xf9, 0x82,
+ 0x8a, 0x81, 0xf6, 0xf5, 0x02, 0xc0, 0x95, 0xb5, 0xb5, 0x55, 0x79, 0xbc, 0x60, 0x42, 0x59, 0xef,
+ 0x85, 0x07, 0x95, 0xc3, 0x1f, 0x08, 0x26, 0xfc, 0x21, 0xe5, 0x19, 0x5e, 0x2f, 0xd8, 0x42, 0x8e,
+ 0x4e, 0x3e, 0x08, 0x55, 0xa9, 0x20, 0x4b, 0xb1, 0x87, 0xae, 0x05, 0x52, 0x89, 0x46, 0x95, 0xaf,
+ 0xfd, 0x76, 0x11, 0x60, 0xc9, 0xb4, 0x69, 0x4b, 0x79, 0xb0, 0xd7, 0x83, 0x2d, 0x8f, 0xfa, 0x5b,
+ 0xae, 0x6d, 0x0e, 0x79, 0x9a, 0xca, 0x6d, 0xfe, 0x6b, 0x0a, 0x04, 0x23, 0x3c, 0x62, 0xc2, 0xa8,
+ 0x1f, 0xd0, 0xee, 0x92, 0x13, 0x50, 0x6f, 0x47, 0xb7, 0x87, 0x3c, 0x44, 0x39, 0x29, 0xec, 0x22,
+ 0x11, 0x0e, 0x26, 0x50, 0x89, 0x0e, 0x0d, 0xcb, 0x31, 0xc4, 0x00, 0x69, 0xee, 0x0e, 0xd9, 0x91,
+ 0x4e, 0xb0, 0x1d, 0xc7, 0x52, 0x04, 0x83, 0x71, 0x4c, 0xed, 0xfb, 0x45, 0x38, 0xc3, 0xf9, 0xb1,
+ 0x6a, 0x24, 0xfc, 0x31, 0xc9, 0xff, 0xef, 0xbb, 0x07, 0xf7, 0xbf, 0x0f, 0xc6, 0x5a, 0x5c, 0xa3,
+ 0x5a, 0xa1, 0x81, 0x1e, 0xe9, 0x73, 0x51, 0x5a, 0xec, 0xf2, 0x5b, 0x0f, 0xca, 0x3e, 0x9b, 0xaf,
+ 0x84, 0xf4, 0x5a, 0x43, 0x77, 0xa1, 0xec, 0x0f, 0xe0, 0xb3, 0x57, 0x78, 0x6a, 0xcc, 0x67, 0x2d,
+ 0xce, 0x8e, 0xfc, 0x2c, 0x8c, 0xf8, 0x81, 0x1e, 0xf4, 0xd4, 0xd0, 0x5c, 0x3f, 0x6a, 0xc6, 0x1c,
+ 0x3c, 0x9a, 0x47, 0xc4, 0x3b, 0x4a, 0xa6, 0xda, 0xf7, 0x0b, 0x70, 0x2e, 0xbb, 0xe0, 0xb2, 0xe5,
+ 0x07, 0xe4, 0xff, 0xf5, 0x89, 0xfd, 0x80, 0x2d, 0xce, 0x4a, 0x73, 0xa1, 0x87, 0x0e, 0xd9, 0x2a,
+ 0x25, 0x26, 0xf2, 0x00, 0x2a, 0x56, 0x40, 0x3b, 0x6a, 0x7f, 0x79, 0xfd, 0x88, 0x3f, 0x3d, 0xb6,
+ 0xb4, 0x33, 0x2e, 0x28, 0x98, 0x69, 0x6f, 0x15, 0x07, 0x7d, 0x32, 0x5f, 0x3e, 0xec, 0xa4, 0xcf,
+ 0xef, 0xd5, 0x7c, 0x3e, 0xbf, 0xc9, 0x0a, 0xf5, 0xbb, 0xfe, 0xfe, 0x4c, 0xbf, 0xeb, 0xef, 0xf5,
+ 0xfc, 0xae, 0xbf, 0x29, 0x31, 0x0c, 0xf4, 0x00, 0x7e, 0xa7, 0x04, 0xe7, 0xef, 0xd4, 0x6d, 0xd8,
+ 0x7a, 0x26, 0x7b, 0x67, 0xde, 0xf5, 0xec, 0xce, 0xfd, 0x90, 0xcc, 0x40, 0xa5, 0xbb, 0xa5, 0xfb,
+ 0x4a, 0x29, 0x53, 0x1b, 0x96, 0xca, 0x2a, 0x4b, 0xbc, 0xcd, 0x26, 0x0d, 0xae, 0xcc, 0xf1, 0x57,
+ 0x14, 0xa4, 0x6c, 0x3a, 0xee, 0x50, 0xdf, 0x8f, 0x6c, 0x02, 0xe1, 0x74, 0xbc, 0x22, 0x92, 0x51,
+ 0xe5, 0x93, 0x00, 0x46, 0x84, 0x89, 0x59, 0xae, 0x4c, 0xc3, 0x3b, 0x72, 0x65, 0xb8, 0x89, 0x47,
+ 0x1f, 0x25, 0x4f, 0x2b, 0x24, 0x2f, 0x32, 0x05, 0xe5, 0x20, 0x72, 0xda, 0x55, 0x5b, 0xf3, 0x72,
+ 0x86, 0x7e, 0xca, 0xe9, 0xd8, 0xc6, 0xde, 0xdd, 0xe0, 0x46, 0x75, 0x53, 0x9e, 0x9f, 0x5b, 0xae,
+ 0xc3, 0x15, 0xb2, 0x52, 0xb4, 0xb1, 0xbf, 0xde, 0x47, 0x81, 0x19, 0xa5, 0xb4, 0xbf, 0xae, 0xc1,
+ 0x99, 0xec, 0xfe, 0xc0, 0xe4, 0xb6, 0x43, 0x3d, 0x9f, 0x61, 0x17, 0x92, 0x72, 0xbb, 0x21, 0x92,
+ 0x51, 0xe5, 0xbf, 0xa7, 0x1d, 0xce, 0xbe, 0x51, 0x80, 0xb3, 0x9e, 0x3c, 0x23, 0xba, 0x17, 0x4e,
+ 0x67, 0x8f, 0x0a, 0x73, 0xc6, 0x00, 0x86, 0x38, 0xb8, 0x2e, 0xe4, 0x37, 0x0b, 0x30, 0xd1, 0x49,
+ 0xd9, 0x39, 0x8e, 0xf1, 0xc2, 0x18, 0xf7, 0x8a, 0x5f, 0x19, 0xc0, 0x0f, 0x07, 0xd6, 0x84, 0x7c,
+ 0x01, 0x1a, 0x5d, 0xd6, 0x2f, 0xfc, 0x80, 0x3a, 0x86, 0xba, 0x33, 0x36, 0xfc, 0x48, 0x5a, 0x8d,
+ 0xb0, 0x94, 0x2b, 0x9a, 0xd0, 0x0f, 0x62, 0x19, 0x18, 0xe7, 0x78, 0x9f, 0xdf, 0x10, 0xbb, 0x04,
+ 0x35, 0x9f, 0x06, 0x81, 0xe5, 0xb4, 0xc5, 0x7e, 0xa3, 0x2e, 0xc6, 0x4a, 0x4b, 0xa6, 0x61, 0x98,
+ 0x4b, 0x3e, 0x04, 0x75, 0x7e, 0xe4, 0x34, 0xeb, 0xb5, 0xfd, 0x89, 0x3a, 0x77, 0x17, 0x1b, 0x13,
+ 0x0e, 0x70, 0x32, 0x11, 0xa3, 0x7c, 0xf2, 0x14, 0x8c, 0x6e, 0xf0, 0xe1, 0x2b, 0xaf, 0xf3, 0x0a,
+ 0x1b, 0x17, 0xd7, 0xd6, 0x9a, 0xb1, 0x74, 0x4c, 0x50, 0x91, 0x19, 0x00, 0x1a, 0x9e, 0xcb, 0xa5,
+ 0xed, 0x59, 0xd1, 0x89, 0x1d, 0xc6, 0xa8, 0xc8, 0xa3, 0x50, 0x0a, 0x6c, 0x9f, 0xdb, 0xb0, 0x6a,
+ 0xd1, 0x16, 0x74, 0x6d, 0xb9, 0x85, 0x2c, 0x5d, 0xfb, 0x69, 0x01, 0x4e, 0xa4, 0x2e, 0x97, 0xb0,
+ 0x22, 0x3d, 0xcf, 0x96, 0xd3, 0x48, 0x58, 0x64, 0x1d, 0x97, 0x91, 0xa5, 0x93, 0x57, 0xa5, 0x5a,
+ 0x5e, 0xcc, 0x19, 0xb9, 0xe0, 0x9a, 0x1e, 0xf8, 0x4c, 0x0f, 0xef, 0xd3, 0xc8, 0xf9, 0x31, 0x5f,
+ 0x54, 0x1f, 0xb9, 0x0e, 0xc4, 0x8e, 0xf9, 0xa2, 0x3c, 0x4c, 0x50, 0xa6, 0x0c, 0x7e, 0xe5, 0x83,
+ 0x18, 0xfc, 0xb4, 0xaf, 0x16, 0x63, 0x12, 0x90, 0x9a, 0xfd, 0x5d, 0x24, 0xf0, 0x04, 0x5b, 0x40,
+ 0xc3, 0xc5, 0xbd, 0x1e, 0x5f, 0xff, 0xf8, 0x62, 0x2c, 0x73, 0xc9, 0x4b, 0x42, 0xf6, 0xa5, 0x9c,
+ 0xb7, 0x50, 0xd7, 0x96, 0x5b, 0xc2, 0xbb, 0x4a, 0xb5, 0x5a, 0xd8, 0x04, 0xe5, 0x63, 0x6a, 0x02,
+ 0xed, 0xcf, 0x4b, 0xd0, 0x78, 0xd1, 0xdd, 0x78, 0x8f, 0x78, 0x50, 0x67, 0x2f, 0x53, 0xc5, 0x77,
+ 0x71, 0x99, 0x5a, 0x87, 0x87, 0x83, 0xc0, 0x6e, 0x51, 0xc3, 0x75, 0x4c, 0x7f, 0x76, 0x33, 0xa0,
+ 0xde, 0x82, 0xe5, 0x58, 0xfe, 0x16, 0x35, 0xe5, 0x71, 0xd2, 0x23, 0xfb, 0x7b, 0x93, 0x0f, 0xaf,
+ 0xad, 0x2d, 0x67, 0x91, 0xe0, 0xa0, 0xb2, 0x7c, 0xda, 0xd0, 0x8d, 0x6d, 0x77, 0x73, 0x93, 0xdf,
+ 0x94, 0x91, 0x3e, 0x37, 0x62, 0xda, 0x88, 0xa5, 0x63, 0x82, 0x4a, 0x7b, 0xbb, 0x08, 0xf5, 0xf0,
+ 0xe6, 0x3b, 0x79, 0x1c, 0xaa, 0x1b, 0x9e, 0xbb, 0x4d, 0x3d, 0x71, 0x72, 0x27, 0x6f, 0xca, 0x34,
+ 0x45, 0x12, 0xaa, 0x3c, 0xf2, 0x18, 0x54, 0x02, 0xb7, 0x6b, 0x19, 0x69, 0x83, 0xda, 0x1a, 0x4b,
+ 0x44, 0x91, 0x77, 0x7c, 0x1d, 0xfc, 0x89, 0x84, 0x6a, 0x57, 0x1f, 0xa8, 0x8c, 0xbd, 0x02, 0x65,
+ 0x5f, 0xf7, 0x6d, 0xb9, 0x9e, 0xe6, 0xb8, 0x44, 0x3e, 0xdb, 0x5a, 0x96, 0x97, 0xc8, 0x67, 0x5b,
+ 0xcb, 0xc8, 0x41, 0xb5, 0x1f, 0x15, 0xa1, 0x21, 0xe4, 0x26, 0x66, 0x85, 0xa3, 0x94, 0xdc, 0xf3,
+ 0xdc, 0x95, 0xc2, 0xef, 0x75, 0xa8, 0xc7, 0xcd, 0x4c, 0x72, 0x92, 0x8b, 0x9f, 0x0f, 0x44, 0x99,
+ 0xa1, 0x3b, 0x45, 0x94, 0xa4, 0x44, 0x5f, 0x3e, 0x46, 0xd1, 0x57, 0x0e, 0x24, 0xfa, 0x91, 0xe3,
+ 0x10, 0xfd, 0x9b, 0x45, 0xa8, 0x2f, 0x5b, 0x9b, 0xd4, 0xd8, 0x35, 0x6c, 0x7e, 0x27, 0xd0, 0xa4,
+ 0x36, 0x0d, 0xe8, 0xa2, 0xa7, 0x1b, 0x74, 0x95, 0x7a, 0x16, 0x8f, 0xd9, 0xc2, 0xc6, 0x07, 0x9f,
+ 0x81, 0xe4, 0x9d, 0xc0, 0xf9, 0x01, 0x34, 0x38, 0xb0, 0x34, 0x59, 0x82, 0x51, 0x93, 0xfa, 0x96,
+ 0x47, 0xcd, 0xd5, 0xd8, 0x46, 0xe5, 0x71, 0xb5, 0xd4, 0xcc, 0xc7, 0xf2, 0x6e, 0xef, 0x4d, 0x8e,
+ 0x29, 0x03, 0xa5, 0xd8, 0xb1, 0x24, 0x8a, 0xb2, 0x21, 0xdf, 0xd5, 0x7b, 0x7e, 0x56, 0x1d, 0x63,
+ 0x43, 0x7e, 0x35, 0x9b, 0x04, 0x07, 0x95, 0xd5, 0x2a, 0x50, 0x5a, 0x76, 0xdb, 0xda, 0x5b, 0x25,
+ 0x08, 0x83, 0xfb, 0x90, 0x9f, 0x2b, 0x40, 0x43, 0x77, 0x1c, 0x37, 0x90, 0x81, 0x73, 0xc4, 0x09,
+ 0x3c, 0xe6, 0x8e, 0x21, 0x34, 0x35, 0x1b, 0x81, 0x8a, 0xc3, 0xdb, 0xf0, 0x40, 0x39, 0x96, 0x83,
+ 0x71, 0xde, 0xa4, 0x97, 0x3a, 0x4f, 0x5e, 0xc9, 0x5f, 0x8b, 0x03, 0x9c, 0x1e, 0x9f, 0xfb, 0x24,
+ 0x9c, 0x4c, 0x57, 0xf6, 0x30, 0xc7, 0x41, 0xb9, 0x0e, 0xe6, 0x8b, 0x00, 0x91, 0x4f, 0xc9, 0x3d,
+ 0x30, 0x62, 0x59, 0x09, 0x23, 0xd6, 0xe2, 0xf0, 0x02, 0x0e, 0x2b, 0x3d, 0xd0, 0x70, 0xf5, 0x7a,
+ 0xca, 0x70, 0xb5, 0x74, 0x14, 0xcc, 0xee, 0x6c, 0xac, 0xfa, 0xad, 0x02, 0x9c, 0x8c, 0x88, 0xe5,
+ 0x0d, 0xd9, 0x67, 0x61, 0xcc, 0xa3, 0xba, 0xd9, 0xd4, 0x03, 0x63, 0x8b, 0xbb, 0x7a, 0x17, 0xb8,
+ 0x6f, 0xf6, 0xa9, 0xfd, 0xbd, 0xc9, 0x31, 0x8c, 0x67, 0x60, 0x92, 0x8e, 0xe8, 0xd0, 0x60, 0x09,
+ 0x6b, 0x56, 0x87, 0xba, 0xbd, 0x60, 0x48, 0xab, 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c,
+ 0xed, 0x9d, 0x02, 0x8c, 0xc7, 0x2b, 0x7c, 0xec, 0x16, 0xb5, 0xad, 0xa4, 0x45, 0x6d, 0xee, 0x08,
+ 0xda, 0x64, 0x80, 0x15, 0xed, 0xc7, 0xb5, 0xf8, 0xa7, 0x71, 0xcb, 0x59, 0xdc, 0x58, 0x50, 0xb8,
+ 0xa3, 0xb1, 0xe0, 0xbd, 0x1f, 0x35, 0x66, 0x90, 0x96, 0x5b, 0xbe, 0x8f, 0xb5, 0xdc, 0x77, 0x33,
+ 0xf4, 0x4c, 0x2c, 0x7c, 0xca, 0x48, 0x8e, 0xf0, 0x29, 0x9d, 0x30, 0x7c, 0x4a, 0xf5, 0xc8, 0x26,
+ 0x9d, 0x83, 0x84, 0x50, 0xa9, 0xdd, 0xd3, 0x10, 0x2a, 0xf5, 0xe3, 0x0a, 0xa1, 0x02, 0x79, 0x43,
+ 0xa8, 0x7c, 0xa5, 0x00, 0xe3, 0x66, 0xe2, 0xc6, 0x2c, 0xb7, 0x2d, 0xe4, 0x59, 0x6a, 0x92, 0x17,
+ 0x70, 0xc5, 0x95, 0xa9, 0x64, 0x1a, 0xa6, 0x58, 0x6a, 0x3f, 0x2c, 0xc7, 0xd7, 0x81, 0x7b, 0x6d,
+ 0xaa, 0x7e, 0x26, 0x69, 0xaa, 0xbe, 0x98, 0x36, 0x55, 0x9f, 0x88, 0x79, 0x91, 0xc6, 0xcd, 0xd5,
+ 0x1f, 0x8e, 0x4d, 0x8f, 0x6c, 0x4e, 0x1a, 0x8b, 0x24, 0x9d, 0x31, 0x45, 0x7e, 0x18, 0x6a, 0xbe,
+ 0x0a, 0xc3, 0x28, 0x36, 0x36, 0x51, 0xbb, 0xa8, 0x10, 0x89, 0x21, 0x05, 0xd3, 0xc4, 0x3d, 0xaa,
+ 0xfb, 0xae, 0x93, 0xd6, 0xc4, 0x91, 0xa7, 0xa2, 0xcc, 0x8d, 0x9b, 0xcc, 0x47, 0xee, 0x62, 0x32,
+ 0xd7, 0xa1, 0x61, 0xeb, 0x7e, 0xb0, 0xde, 0x35, 0xf5, 0x80, 0x9a, 0x72, 0xbc, 0xfd, 0xaf, 0x83,
+ 0xad, 0x55, 0x6c, 0xfd, 0x8b, 0x14, 0xc2, 0xe5, 0x08, 0x06, 0xe3, 0x98, 0xc4, 0x84, 0x51, 0xf6,
+ 0xca, 0x47, 0x83, 0x39, 0xab, 0x42, 0x00, 0x1c, 0x86, 0x47, 0x68, 0xe9, 0x59, 0x8e, 0xe1, 0x60,
+ 0x02, 0x75, 0x80, 0x55, 0xbd, 0x3e, 0x94, 0x55, 0xfd, 0x2b, 0x75, 0x68, 0x5c, 0xd3, 0x03, 0x6b,
+ 0x87, 0xf2, 0x53, 0x9c, 0xe3, 0x31, 0xa5, 0xff, 0x6a, 0x01, 0xce, 0x24, 0x5d, 0xf5, 0x8e, 0xd1,
+ 0x9e, 0xce, 0x03, 0x7f, 0x60, 0x26, 0x37, 0x1c, 0x50, 0x0b, 0x6e, 0x59, 0xef, 0xf3, 0xfc, 0x3b,
+ 0x6e, 0xcb, 0x7a, 0x6b, 0x10, 0x43, 0x1c, 0x5c, 0x97, 0xf7, 0x8a, 0x65, 0xfd, 0xfe, 0x0e, 0xcc,
+ 0x96, 0xb2, 0xfb, 0x57, 0xef, 0x1b, 0xbb, 0x7f, 0xed, 0xbe, 0x50, 0xb6, 0xba, 0x31, 0xbb, 0x7f,
+ 0x3d, 0xa7, 0xff, 0x89, 0xf4, 0x6e, 0x17, 0x68, 0x83, 0xce, 0x0f, 0xf8, 0xc5, 0x74, 0x65, 0x8f,
+ 0x65, 0x3a, 0xca, 0x86, 0xee, 0x5b, 0x86, 0x5c, 0xf6, 0x72, 0x04, 0xa2, 0x54, 0x11, 0xbb, 0xc4,
+ 0x31, 0x35, 0x7f, 0x45, 0x81, 0x1d, 0x45, 0x06, 0x2b, 0xe6, 0x8a, 0x0c, 0x46, 0xe6, 0xa0, 0xec,
+ 0xb0, 0xdd, 0x73, 0xe9, 0xd0, 0xb1, 0xc0, 0xae, 0x5d, 0xa5, 0xbb, 0xc8, 0x0b, 0x6b, 0x6f, 0x17,
+ 0x01, 0xd8, 0xe7, 0x1f, 0xcc, 0x02, 0xff, 0x41, 0xa8, 0xfa, 0x3d, 0xbe, 0x57, 0x96, 0x0b, 0x76,
+ 0xe4, 0xb4, 0x23, 0x92, 0x51, 0xe5, 0x93, 0xc7, 0xa0, 0xf2, 0x7a, 0x8f, 0xf6, 0xd4, 0x71, 0x72,
+ 0xa8, 0xae, 0x7d, 0x8a, 0x25, 0xa2, 0xc8, 0x3b, 0x3e, 0x6b, 0x9a, 0xb2, 0xd4, 0x57, 0x8e, 0xcb,
+ 0x52, 0x5f, 0x87, 0xea, 0x35, 0x97, 0xfb, 0x00, 0x6a, 0xff, 0x52, 0x04, 0x88, 0x7c, 0xac, 0xc8,
+ 0xd7, 0x0b, 0xf0, 0x50, 0x38, 0xe0, 0x02, 0xa1, 0x75, 0xcf, 0xd9, 0xba, 0xd5, 0xc9, 0x6d, 0xb5,
+ 0xcf, 0x1a, 0xec, 0x7c, 0x06, 0x5a, 0xcd, 0x62, 0x87, 0xd9, 0xb5, 0x20, 0x08, 0x35, 0xda, 0xe9,
+ 0x06, 0xbb, 0xf3, 0x96, 0x27, 0x7b, 0x60, 0xa6, 0x2b, 0xdf, 0x65, 0x49, 0x23, 0x8a, 0xca, 0xad,
+ 0x21, 0x1f, 0x44, 0x2a, 0x07, 0x43, 0x1c, 0xb2, 0x05, 0x35, 0xc7, 0x7d, 0xd5, 0x67, 0xe2, 0x90,
+ 0xdd, 0xf1, 0x85, 0xe1, 0x45, 0x2e, 0xc4, 0x2a, 0xac, 0xbc, 0xf2, 0x05, 0xab, 0x8e, 0x14, 0xf6,
+ 0xd7, 0x8a, 0x70, 0x3a, 0x43, 0x0e, 0xe4, 0x05, 0x38, 0x29, 0xdd, 0xd9, 0xa2, 0xf0, 0xc4, 0x85,
+ 0x28, 0x3c, 0x71, 0x2b, 0x95, 0x87, 0x7d, 0xd4, 0xe4, 0x55, 0x00, 0xdd, 0x30, 0xa8, 0xef, 0xaf,
+ 0xb8, 0xa6, 0xd2, 0x47, 0x9f, 0xdf, 0xdf, 0x9b, 0x84, 0xd9, 0x30, 0xf5, 0xf6, 0xde, 0xe4, 0x47,
+ 0xb2, 0x3c, 0x54, 0x53, 0x72, 0x8e, 0x0a, 0x60, 0x0c, 0x92, 0x7c, 0x0e, 0x40, 0x6c, 0xbd, 0xc2,
+ 0x4b, 0xf4, 0x77, 0xb1, 0x57, 0x4c, 0xa9, 0x70, 0x45, 0x53, 0x9f, 0xea, 0xe9, 0x4e, 0x60, 0x05,
+ 0xbb, 0x22, 0x66, 0xc9, 0x8d, 0x10, 0x05, 0x63, 0x88, 0xda, 0x9f, 0x14, 0xa1, 0xa6, 0x2c, 0xa5,
+ 0xf7, 0xc0, 0x3c, 0xd6, 0x4e, 0x98, 0xc7, 0x8e, 0xc8, 0x27, 0x35, 0xcb, 0x38, 0xe6, 0xa6, 0x8c,
+ 0x63, 0x8b, 0xf9, 0x59, 0xdd, 0xd9, 0x34, 0xf6, 0xad, 0x22, 0x8c, 0x2b, 0xd2, 0xbc, 0x86, 0xb1,
+ 0x4f, 0xc0, 0x09, 0x71, 0x96, 0xbc, 0xa2, 0xdf, 0x12, 0xe1, 0x5b, 0xb8, 0xc0, 0xca, 0xc2, 0x0d,
+ 0xb4, 0x99, 0xcc, 0xc2, 0x34, 0x2d, 0xeb, 0xd6, 0x22, 0x69, 0x9d, 0xed, 0x23, 0xc4, 0xe9, 0x93,
+ 0xd8, 0xef, 0xf0, 0x6e, 0xdd, 0x4c, 0xe5, 0x61, 0x1f, 0x75, 0xda, 0x32, 0x57, 0x3e, 0x06, 0xcb,
+ 0xdc, 0xdf, 0x14, 0x60, 0x34, 0x92, 0xd7, 0xb1, 0xdb, 0xe5, 0x36, 0x93, 0x76, 0xb9, 0xd9, 0xdc,
+ 0xdd, 0x61, 0x80, 0x55, 0xee, 0x17, 0xab, 0x90, 0x70, 0x8d, 0x26, 0x1b, 0x70, 0xce, 0xca, 0x74,
+ 0xf0, 0x8a, 0xcd, 0x36, 0xe1, 0x5d, 0xdf, 0xa5, 0x81, 0x94, 0x78, 0x07, 0x14, 0xd2, 0x83, 0xda,
+ 0x0e, 0xf5, 0x02, 0xcb, 0xa0, 0xea, 0xfb, 0x16, 0x73, 0xab, 0x64, 0xd2, 0xf6, 0x18, 0xca, 0xf4,
+ 0x86, 0x64, 0x80, 0x21, 0x2b, 0xb2, 0x01, 0x15, 0x6a, 0xb6, 0xa9, 0x0a, 0xa8, 0x93, 0x33, 0x5c,
+ 0x65, 0x28, 0x4f, 0xf6, 0xe6, 0xa3, 0x80, 0x26, 0x3e, 0xd4, 0x6d, 0x75, 0xb6, 0x24, 0xfb, 0xe1,
+ 0xf0, 0x0a, 0x56, 0x78, 0x4a, 0x15, 0xdd, 0xb5, 0x0f, 0x93, 0x30, 0xe2, 0x43, 0xb6, 0x43, 0x23,
+ 0x57, 0xe5, 0x88, 0x26, 0x8f, 0x3b, 0x98, 0xb8, 0x7c, 0xa8, 0xdf, 0xd4, 0x03, 0xea, 0x75, 0x74,
+ 0x6f, 0x5b, 0xee, 0x36, 0x86, 0xff, 0xc2, 0x97, 0x14, 0x52, 0xf4, 0x85, 0x61, 0x12, 0x46, 0x7c,
+ 0x88, 0x0b, 0xf5, 0x40, 0xaa, 0xcf, 0xca, 0x92, 0x37, 0x3c, 0x53, 0xa5, 0x88, 0xfb, 0xd2, 0x45,
+ 0x5a, 0xbd, 0x62, 0xc4, 0x83, 0xec, 0x24, 0x42, 0xf9, 0x8a, 0x00, 0xce, 0xcd, 0x1c, 0x16, 0x61,
+ 0x09, 0x15, 0x2d, 0x37, 0xd9, 0x21, 0x81, 0xb5, 0xb7, 0x2b, 0xd1, 0xb4, 0x7c, 0xaf, 0xed, 0x54,
+ 0x4f, 0x25, 0xed, 0x54, 0x17, 0xd2, 0x76, 0xaa, 0xd4, 0x11, 0xe5, 0xe1, 0x9d, 0x2a, 0x53, 0x16,
+ 0xa2, 0xf2, 0x31, 0x58, 0x88, 0x9e, 0x84, 0xc6, 0x0e, 0x9f, 0x09, 0x44, 0x74, 0x9e, 0x0a, 0x5f,
+ 0x46, 0xf8, 0xcc, 0x7e, 0x23, 0x4a, 0xc6, 0x38, 0x0d, 0x2b, 0x22, 0x34, 0x90, 0x28, 0xbc, 0xa9,
+ 0x2c, 0xd2, 0x8a, 0x92, 0x31, 0x4e, 0xc3, 0xfd, 0xb1, 0x2c, 0x67, 0x5b, 0x14, 0xa8, 0xf2, 0x02,
+ 0xc2, 0x1f, 0x4b, 0x25, 0x62, 0x94, 0x4f, 0x2e, 0x41, 0xad, 0x67, 0x6e, 0x0a, 0xda, 0x1a, 0xa7,
+ 0xe5, 0x1a, 0xe6, 0xfa, 0xfc, 0x82, 0x8c, 0x16, 0xa4, 0x72, 0x59, 0x4d, 0x3a, 0x7a, 0x57, 0x65,
+ 0xf0, 0xbd, 0xa1, 0xac, 0xc9, 0x4a, 0x94, 0x8c, 0x71, 0x1a, 0xf2, 0x31, 0x18, 0xf7, 0xa8, 0xd9,
+ 0x33, 0x68, 0x58, 0x0a, 0x78, 0x29, 0x6e, 0x15, 0xc5, 0x44, 0x0e, 0xa6, 0x28, 0x07, 0xd8, 0xb9,
+ 0x1a, 0x43, 0xd9, 0xb9, 0xbe, 0x57, 0x00, 0xd2, 0xef, 0xbf, 0x4c, 0xb6, 0x60, 0xc4, 0xe1, 0xd6,
+ 0xaf, 0xdc, 0x01, 0x91, 0x63, 0x46, 0x34, 0x31, 0x2d, 0xc9, 0x04, 0x89, 0x4f, 0x1c, 0xa8, 0xd1,
+ 0x5b, 0x01, 0xf5, 0x9c, 0xf0, 0x3e, 0xc3, 0xd1, 0x04, 0x5f, 0x16, 0xbb, 0x01, 0x89, 0x8c, 0x21,
+ 0x0f, 0xed, 0x07, 0x45, 0x68, 0xc4, 0xe8, 0xee, 0xb6, 0xa9, 0xe4, 0x57, 0xaa, 0x85, 0xd1, 0x69,
+ 0xdd, 0xb3, 0xe5, 0x08, 0x8b, 0x5d, 0xa9, 0x96, 0x59, 0xb8, 0x8c, 0x71, 0x3a, 0x32, 0x03, 0xd0,
+ 0xd1, 0xfd, 0x80, 0x7a, 0x7c, 0xf5, 0x4d, 0x5d, 0x64, 0x5e, 0x09, 0x73, 0x30, 0x46, 0x45, 0x2e,
+ 0xca, 0xf0, 0xd9, 0xe5, 0x64, 0xe0, 0xb9, 0x01, 0xb1, 0xb1, 0x2b, 0x47, 0x10, 0x1b, 0x9b, 0xb4,
+ 0xe1, 0xa4, 0xaa, 0xb5, 0xca, 0x3d, 0x5c, 0x58, 0x32, 0xb1, 0x7f, 0x49, 0x41, 0x60, 0x1f, 0xa8,
+ 0xf6, 0x76, 0x01, 0xc6, 0x12, 0x26, 0x0f, 0x11, 0x32, 0x4e, 0x79, 0xdf, 0x27, 0x42, 0xc6, 0xc5,
+ 0x9c, 0xe6, 0x9f, 0x80, 0x11, 0x21, 0xa0, 0xb4, 0x53, 0x9d, 0x10, 0x21, 0xca, 0x5c, 0x36, 0x97,
+ 0x49, 0xa3, 0x6a, 0x7a, 0x2e, 0x93, 0x56, 0x57, 0x54, 0xf9, 0xc2, 0xdc, 0x2e, 0x6a, 0xd7, 0x6f,
+ 0x6e, 0x17, 0xe9, 0x18, 0x52, 0x68, 0x3f, 0x2c, 0x01, 0x77, 0x41, 0x21, 0xcf, 0x42, 0xbd, 0x43,
+ 0x8d, 0x2d, 0xdd, 0xb1, 0x7c, 0x15, 0x32, 0x92, 0xed, 0x6e, 0xeb, 0x2b, 0x2a, 0xf1, 0x36, 0x03,
+ 0x98, 0x6d, 0x2d, 0x73, 0x2f, 0xef, 0x88, 0x96, 0x18, 0x30, 0xd2, 0xf6, 0x7d, 0xbd, 0x6b, 0xe5,
+ 0x3e, 0x01, 0x15, 0x21, 0xfa, 0xc4, 0x20, 0x12, 0xcf, 0x28, 0xa1, 0x89, 0x01, 0x95, 0xae, 0xad,
+ 0x5b, 0x4e, 0xee, 0x7f, 0x94, 0xb0, 0x2f, 0x58, 0x65, 0x48, 0xc2, 0xa4, 0xc3, 0x1f, 0x51, 0x60,
+ 0x93, 0x1e, 0x34, 0x7c, 0xc3, 0xd3, 0x3b, 0xfe, 0x96, 0x3e, 0xf3, 0xf4, 0x33, 0xb9, 0x95, 0xa4,
+ 0x88, 0x95, 0x98, 0xb3, 0xe7, 0x70, 0x76, 0xa5, 0x75, 0x65, 0x76, 0xe6, 0xe9, 0x67, 0x30, 0xce,
+ 0x27, 0xce, 0xf6, 0xe9, 0x27, 0x67, 0x64, 0xbf, 0x3f, 0x72, 0xb6, 0x4f, 0x3f, 0x39, 0x83, 0x71,
+ 0x3e, 0xda, 0x7f, 0x14, 0xa0, 0x1e, 0xd2, 0x92, 0x75, 0x00, 0x36, 0x02, 0x65, 0x50, 0xbd, 0x43,
+ 0x05, 0xb8, 0xe7, 0xbb, 0xe2, 0xf5, 0xb0, 0x30, 0xc6, 0x80, 0x32, 0xa2, 0x0e, 0x16, 0x8f, 0x3a,
+ 0xea, 0xe0, 0x34, 0xd4, 0xb7, 0x74, 0xc7, 0xf4, 0xb7, 0xf4, 0x6d, 0x31, 0x11, 0xc5, 0xe2, 0x70,
+ 0x5e, 0x51, 0x19, 0x18, 0xd1, 0x68, 0xff, 0x5a, 0x01, 0x71, 0x6c, 0xc9, 0x86, 0x8a, 0x69, 0xf9,
+ 0xc2, 0x6f, 0xb6, 0xc0, 0x4b, 0x86, 0x43, 0x65, 0x5e, 0xa6, 0x63, 0x48, 0x41, 0xce, 0x42, 0xa9,
+ 0x63, 0x39, 0xf2, 0xc4, 0x83, 0x1b, 0xbc, 0x56, 0x2c, 0x07, 0x59, 0x1a, 0xcf, 0xd2, 0x6f, 0x49,
+ 0x97, 0x27, 0x91, 0xa5, 0xdf, 0x42, 0x96, 0xc6, 0xb6, 0xa0, 0xb6, 0xeb, 0x6e, 0x6f, 0xe8, 0xc6,
+ 0xb6, 0xf2, 0x8c, 0x2a, 0xf3, 0x85, 0x90, 0x6f, 0x41, 0x97, 0x93, 0x59, 0x98, 0xa6, 0x25, 0x8b,
+ 0x70, 0xc2, 0x70, 0x5d, 0xdb, 0x74, 0x6f, 0x3a, 0xaa, 0xb8, 0x50, 0x1d, 0xf8, 0x49, 0xc2, 0x3c,
+ 0xed, 0x7a, 0xd4, 0x60, 0xfa, 0xc5, 0x5c, 0x92, 0x08, 0xd3, 0xa5, 0xc8, 0x3a, 0x3c, 0xfc, 0x06,
+ 0xf5, 0x5c, 0x39, 0x5d, 0xb4, 0x6c, 0x4a, 0xbb, 0x0a, 0x50, 0x28, 0x16, 0xdc, 0x53, 0xeb, 0x33,
+ 0xd9, 0x24, 0x38, 0xa8, 0x2c, 0xf7, 0xf9, 0xd4, 0xbd, 0x36, 0x0d, 0x56, 0x3d, 0xd7, 0xa0, 0xbe,
+ 0x6f, 0x39, 0x6d, 0x05, 0x5b, 0x8d, 0x60, 0xd7, 0xb2, 0x49, 0x70, 0x50, 0x59, 0xf2, 0x32, 0x4c,
+ 0x88, 0x2c, 0xb1, 0x6a, 0xcf, 0xee, 0xe8, 0x96, 0xad, 0x6f, 0x58, 0xb6, 0xfa, 0x27, 0xd7, 0x98,
+ 0x38, 0xa0, 0x58, 0x1b, 0x40, 0x83, 0x03, 0x4b, 0xf3, 0x3f, 0x69, 0xc9, 0xe3, 0xa9, 0x55, 0xea,
+ 0xf1, 0x7e, 0x20, 0xf5, 0x19, 0xf1, 0x27, 0xad, 0x54, 0x1e, 0xf6, 0x51, 0x13, 0x84, 0x33, 0xfc,
+ 0xb8, 0x7b, 0xbd, 0x9b, 0x12, 0xba, 0xd4, 0x70, 0xf8, 0x39, 0x54, 0x2b, 0x93, 0x02, 0x07, 0x94,
+ 0x64, 0xdf, 0xcb, 0x73, 0xe6, 0xdd, 0x9b, 0x4e, 0x1a, 0xb5, 0x11, 0x7d, 0x6f, 0x6b, 0x00, 0x0d,
+ 0x0e, 0x2c, 0xad, 0xfd, 0x71, 0x11, 0xc6, 0x12, 0x37, 0x9f, 0xef, 0xbb, 0x1b, 0xa6, 0x4c, 0x55,
+ 0xec, 0xf8, 0xed, 0xa5, 0xf9, 0x2b, 0x54, 0x37, 0xa9, 0x77, 0x95, 0xaa, 0x5b, 0xea, 0x7c, 0xf4,
+ 0xaf, 0x24, 0x72, 0x30, 0x45, 0x49, 0x36, 0xa1, 0x22, 0x0c, 0x9f, 0x79, 0xff, 0x69, 0xa0, 0x64,
+ 0xc4, 0xad, 0x9f, 0x7c, 0x6d, 0x10, 0xb6, 0x4f, 0x01, 0xaf, 0x05, 0x30, 0x1a, 0xa7, 0x60, 0x23,
+ 0x3e, 0xd2, 0xaa, 0xaa, 0x09, 0x8d, 0x6a, 0x09, 0x4a, 0x41, 0x30, 0xec, 0xdd, 0x55, 0x61, 0x48,
+ 0x5f, 0x5b, 0x46, 0x86, 0xa1, 0x6d, 0xb2, 0xb6, 0xf3, 0x7d, 0xcb, 0x75, 0x64, 0x20, 0xe3, 0x75,
+ 0xa8, 0x06, 0xd2, 0x96, 0x34, 0xdc, 0xdd, 0x5b, 0x6e, 0xd7, 0x55, 0x76, 0x24, 0x85, 0xa5, 0xfd,
+ 0x6d, 0x11, 0xea, 0xe1, 0xbe, 0xef, 0x00, 0x01, 0x82, 0x5d, 0xa8, 0x87, 0x8e, 0x31, 0xb9, 0xff,
+ 0x4f, 0x16, 0xf9, 0x6b, 0xf0, 0xad, 0x4a, 0xf8, 0x8a, 0x11, 0x8f, 0xb8, 0xd3, 0x4d, 0x29, 0x87,
+ 0xd3, 0x4d, 0x17, 0xaa, 0x81, 0x67, 0xb5, 0xdb, 0x52, 0x09, 0xcd, 0xe3, 0x75, 0x13, 0x8a, 0x6b,
+ 0x4d, 0x00, 0x4a, 0xc9, 0x8a, 0x17, 0x54, 0x6c, 0xb4, 0xd7, 0xe0, 0x64, 0x9a, 0x92, 0x6b, 0x68,
+ 0xc6, 0x16, 0x35, 0x7b, 0xb6, 0x92, 0x71, 0xa4, 0xa1, 0xc9, 0x74, 0x0c, 0x29, 0xd8, 0x2e, 0x8d,
+ 0x35, 0xd3, 0x1b, 0xae, 0xa3, 0xf6, 0xbf, 0x5c, 0xd9, 0x5d, 0x93, 0x69, 0x18, 0xe6, 0x6a, 0xff,
+ 0x5c, 0x82, 0xb3, 0xd1, 0xee, 0x7d, 0x45, 0x77, 0xf4, 0xf6, 0x01, 0x7e, 0x4a, 0xf5, 0xfe, 0x6d,
+ 0x86, 0xc3, 0x46, 0x79, 0x2f, 0xdd, 0x07, 0x51, 0xde, 0x7f, 0x54, 0x00, 0xee, 0xc4, 0x47, 0xbe,
+ 0x00, 0xa3, 0x7a, 0xec, 0x7f, 0x84, 0xb2, 0x39, 0x2f, 0xe7, 0x6e, 0x4e, 0xee, 0x2b, 0x18, 0x3a,
+ 0xa5, 0xc4, 0x53, 0x31, 0xc1, 0x90, 0xb8, 0x50, 0xdb, 0xd4, 0x6d, 0x9b, 0x29, 0x2d, 0xb9, 0x4f,
+ 0x23, 0x12, 0xcc, 0x79, 0x37, 0x5f, 0x90, 0xd0, 0x18, 0x32, 0xd1, 0xfe, 0xa9, 0x00, 0x63, 0x2d,
+ 0xdb, 0x32, 0x2d, 0xa7, 0x7d, 0x8c, 0xe1, 0xdd, 0xaf, 0x43, 0xc5, 0xb7, 0x2d, 0x93, 0x0e, 0x39,
+ 0x8f, 0x8b, 0x15, 0x84, 0x01, 0xa0, 0xc0, 0x49, 0xc6, 0x8b, 0x2f, 0x1d, 0x20, 0x5e, 0xfc, 0x4f,
+ 0x46, 0x40, 0x3a, 0x82, 0x92, 0x1e, 0xd4, 0xdb, 0x2a, 0x0c, 0xb5, 0xfc, 0xc6, 0x2b, 0x39, 0x42,
+ 0x98, 0x25, 0x02, 0x5a, 0x8b, 0x59, 0x37, 0x4c, 0xc4, 0x88, 0x13, 0xa1, 0xc9, 0x5f, 0x50, 0xce,
0xe7, 0xfc, 0x05, 0xa5, 0x60, 0xd7, 0xff, 0x13, 0x4a, 0x1d, 0xca, 0x5b, 0x41, 0xd0, 0x95, 0xe3,
- 0x6a, 0xf8, 0x48, 0xdf, 0xe8, 0x16, 0x0d, 0x61, 0x8d, 0xb0, 0x77, 0xe4, 0xd0, 0x8c, 0x85, 0xa3,
- 0x87, 0x7f, 0x3e, 0x9a, 0xcb, 0xb5, 0x23, 0x1d, 0x67, 0xc1, 0xde, 0x91, 0x43, 0x93, 0xcf, 0x43,
- 0x23, 0xf0, 0x74, 0xc7, 0xdf, 0x74, 0xbd, 0x0e, 0xf5, 0xe4, 0x32, 0x6e, 0x21, 0xc7, 0x5f, 0x18,
- 0xd7, 0x22, 0x34, 0xb1, 0xd5, 0x95, 0x48, 0xc2, 0x38, 0x37, 0xb2, 0x0d, 0xb5, 0x9e, 0x29, 0x2a,
- 0x26, 0xfd, 0x1b, 0xb3, 0x79, 0x7e, 0xac, 0x19, 0xdb, 0x6f, 0x56, 0x6f, 0x18, 0x32, 0x48, 0xfe,
- 0xe4, 0xab, 0x7a, 0x54, 0x3f, 0xf9, 0x8a, 0xf7, 0xc6, 0xac, 0x23, 0xfe, 0xa4, 0x23, 0x2d, 0x4a,
- 0xa7, 0x2d, 0xc3, 0x65, 0x16, 0x72, 0x1b, 0x7b, 0x82, 0x65, 0x23, 0xb4, 0x4a, 0x9d, 0x36, 0x2a,
- 0x1e, 0x5a, 0x07, 0xa4, 0x1b, 0x9a, 0x18, 0x89, 0x5f, 0x61, 0x88, 0x73, 0x27, 0xd3, 0x07, 0x9b,
- 0x0f, 0xc2, 0x7f, 0x32, 0xc4, 0xae, 0xe2, 0xcd, 0xfc, 0xe7, 0x85, 0xf6, 0xb7, 0x45, 0x28, 0xad,
- 0x2d, 0xb7, 0xc4, 0xf5, 0x7a, 0xfc, 0x3f, 0x33, 0xb4, 0xb5, 0x6d, 0x75, 0x6f, 0x50, 0xcf, 0xda,
- 0xdc, 0x95, 0xab, 0xd3, 0xd8, 0xf5, 0x7a, 0x69, 0x0a, 0xcc, 0x28, 0x45, 0x5e, 0x81, 0x51, 0x43,
- 0x9f, 0xa3, 0x5e, 0x30, 0xcc, 0xda, 0x9b, 0x1f, 0xb0, 0x9b, 0x9b, 0x8d, 0x8a, 0x63, 0x02, 0x8c,
- 0xac, 0x03, 0x18, 0x11, 0x74, 0xe9, 0xd0, 0x1e, 0x83, 0x18, 0x70, 0x0c, 0x88, 0x20, 0xd4, 0xb7,
- 0x19, 0x29, 0x47, 0x2d, 0x1f, 0x06, 0x95, 0xf7, 0x9c, 0xab, 0xaa, 0x2c, 0x46, 0x30, 0x9a, 0x03,
- 0x63, 0x89, 0xff, 0x63, 0x90, 0x8f, 0x42, 0xcd, 0xed, 0xc6, 0xa6, 0xd3, 0x3a, 0x5f, 0x4e, 0xd7,
- 0xae, 0xcb, 0xb4, 0xdb, 0x7b, 0x93, 0x63, 0xcb, 0x6e, 0xdb, 0x32, 0x54, 0x02, 0x86, 0xe4, 0x44,
- 0x83, 0x11, 0x7e, 0x2a, 0x46, 0xfd, 0x1d, 0x83, 0xeb, 0x0e, 0x7e, 0x81, 0xbd, 0x8f, 0x32, 0x47,
- 0xfb, 0x52, 0x19, 0xa2, 0xcd, 0x1b, 0xe2, 0xc3, 0x88, 0x88, 0xfa, 0x95, 0x33, 0xf7, 0xb1, 0x06,
- 0x18, 0x4b, 0x56, 0xa4, 0x0d, 0xa5, 0xd7, 0xdc, 0x8d, 0xdc, 0x13, 0x77, 0xec, 0x38, 0xac, 0x70,
- 0x27, 0xc5, 0x12, 0x90, 0x71, 0x20, 0xbf, 0x5a, 0x80, 0x53, 0x7e, 0xda, 0xe8, 0x94, 0xdd, 0x01,
- 0xf3, 0x5b, 0xd7, 0x69, 0x33, 0x56, 0x46, 0x50, 0x0e, 0xca, 0xc6, 0xfe, 0xba, 0x30, 0xf9, 0x8b,
- 0x5d, 0x15, 0xd9, 0x9d, 0x16, 0x73, 0xfe, 0xd3, 0x2d, 0x29, 0xff, 0x64, 0x1a, 0x4a, 0x56, 0xda,
- 0x57, 0x8a, 0xd0, 0x88, 0xcd, 0xd6, 0xb9, 0x7f, 0xba, 0x72, 0x2b, 0xf5, 0xd3, 0x95, 0xd5, 0xe1,
- 0x37, 0x19, 0xa3, 0x5a, 0x1d, 0xf7, 0x7f, 0x57, 0xfe, 0xb4, 0x08, 0xa5, 0xf5, 0xf9, 0x85, 0xe4,
- 0x72, 0xb1, 0x70, 0x0f, 0x96, 0x8b, 0x5b, 0x50, 0xdd, 0xe8, 0x59, 0x76, 0x60, 0x39, 0xb9, 0x0f,
- 0xec, 0xab, 0x7f, 0xd4, 0xc8, 0x73, 0xaf, 0x02, 0x15, 0x15, 0x3c, 0x69, 0x43, 0xb5, 0x2d, 0x6e,
- 0x4c, 0xcb, 0x1d, 0x7a, 0x25, 0x6f, 0x5e, 0x13, 0x8c, 0xe4, 0x0b, 0x2a, 0x74, 0xed, 0x0b, 0x20,
- 0xff, 0x72, 0x4d, 0xfc, 0xe3, 0x91, 0x66, 0x68, 0x8c, 0x66, 0x49, 0x54, 0xfb, 0x3c, 0x84, 0x96,
- 0xc0, 0x3d, 0x6f, 0x4e, 0xed, 0x5f, 0x0b, 0x90, 0x34, 0x7e, 0xee, 0x7d, 0x8f, 0xda, 0x4e, 0xf7,
- 0xa8, 0xf9, 0xa3, 0x18, 0x80, 0xd9, 0x9d, 0x4a, 0xfb, 0x83, 0x22, 0x8c, 0xdc, 0xb3, 0x83, 0x96,
- 0x34, 0x11, 0x49, 0x36, 0x97, 0x73, 0x72, 0x1c, 0x18, 0x47, 0xd6, 0x49, 0xc5, 0x91, 0xe5, 0xfd,
- 0xb3, 0xe6, 0x5d, 0xa2, 0xc8, 0xfe, 0xb2, 0x00, 0x72, 0x6a, 0x5e, 0x72, 0xfc, 0x40, 0x77, 0x0c,
- 0xfe, 0x83, 0x77, 0xa9, 0x07, 0xf2, 0x86, 0x2b, 0xc8, 0x90, 0x1e, 0xa1, 0xfa, 0xf9, 0xb3, 0x9a,
- 0xf7, 0xc9, 0x87, 0xa1, 0xb6, 0xe5, 0xfa, 0x01, 0x9f, 0xeb, 0x8b, 0x49, 0xdf, 0xce, 0x15, 0x99,
- 0x8e, 0x21, 0x45, 0x7a, 0x5b, 0xaf, 0x32, 0x78, 0x5b, 0x4f, 0xfb, 0x56, 0x11, 0x46, 0xdf, 0x2b,
- 0xa7, 0x45, 0xb3, 0xe2, 0xee, 0x4a, 0x39, 0xe3, 0xee, 0xca, 0x87, 0x89, 0xbb, 0xd3, 0xbe, 0x5b,
- 0x00, 0xb8, 0x67, 0x47, 0x55, 0xcd, 0x64, 0x48, 0x5c, 0xee, 0x7e, 0x95, 0x1d, 0x10, 0xf7, 0xbb,
- 0x15, 0xf5, 0x49, 0x3c, 0x1c, 0xee, 0xcd, 0x02, 0x8c, 0xeb, 0x89, 0x10, 0xb3, 0xdc, 0xe6, 0x65,
- 0x2a, 0x62, 0x2d, 0x3c, 0x96, 0x97, 0x4c, 0xc7, 0x14, 0x5b, 0xf2, 0x5c, 0x74, 0x4d, 0xea, 0xb5,
- 0xa8, 0xdb, 0xf7, 0xdd, 0x6f, 0xca, 0x4d, 0x9d, 0x04, 0xe5, 0x5d, 0x42, 0xfa, 0x4a, 0x47, 0x12,
- 0xd2, 0x17, 0x3f, 0xac, 0x54, 0xbe, 0xe3, 0x61, 0xa5, 0x1d, 0xa8, 0x6f, 0x7a, 0x6e, 0x87, 0x47,
- 0xcd, 0xc9, 0x7f, 0x72, 0x5e, 0xce, 0xa1, 0x53, 0xa2, 0xbf, 0x51, 0x47, 0xaa, 0x75, 0x41, 0xe1,
- 0x63, 0xc4, 0x8a, 0x3b, 0xa5, 0x5d, 0xc1, 0x75, 0xe4, 0x28, 0xb9, 0x86, 0x73, 0xc9, 0x9a, 0x40,
- 0x47, 0xc5, 0x26, 0x19, 0x29, 0x57, 0xbd, 0x37, 0x91, 0x72, 0xda, 0xcf, 0x97, 0xd5, 0x04, 0x76,
- 0xdf, 0xdd, 0xc8, 0xf7, 0xde, 0x3f, 0xe2, 0x98, 0x3e, 0x7f, 0x58, 0xbd, 0x87, 0xe7, 0x0f, 0x6b,
- 0x43, 0xc5, 0x65, 0xed, 0x95, 0x20, 0xb5, 0x76, 0x7a, 0x7f, 0x87, 0xe2, 0x3f, 0xd5, 0x0e, 0xc5,
- 0x5b, 0x45, 0x88, 0x26, 0x82, 0x43, 0x86, 0x5a, 0xbc, 0x0c, 0xb5, 0x8e, 0x7e, 0x6b, 0x9e, 0xda,
- 0xfa, 0x6e, 0x9e, 0x1f, 0x29, 0xae, 0x48, 0x0c, 0x0c, 0xd1, 0x88, 0x0f, 0x60, 0x85, 0x97, 0x19,
- 0xe7, 0xf6, 0x38, 0x47, 0xf7, 0x22, 0x0b, 0x9f, 0x56, 0xf4, 0x8e, 0x31, 0x36, 0xda, 0x5f, 0x14,
- 0x41, 0xde, 0x7a, 0x4d, 0x28, 0x54, 0x36, 0xad, 0x5b, 0xd4, 0xcc, 0x1d, 0x76, 0x18, 0xfb, 0xbd,
- 0xad, 0x70, 0xa9, 0xf3, 0x04, 0x14, 0xe8, 0xdc, 0x57, 0x2a, 0xb6, 0x48, 0xa4, 0xfc, 0x72, 0xf8,
- 0x4a, 0xe3, 0x5b, 0x2d, 0xd2, 0x57, 0x2a, 0x92, 0x50, 0xf1, 0x10, 0xae, 0x59, 0xbe, 0x4f, 0x2d,
- 0x45, 0x9a, 0xc7, 0x35, 0x1b, 0xdb, 0xef, 0x56, 0xae, 0x59, 0x5f, 0x1c, 0x40, 0x96, 0x3c, 0x9a,
- 0x9f, 0xfd, 0xce, 0xf7, 0x2e, 0x3c, 0xf0, 0xdd, 0xef, 0x5d, 0x78, 0xe0, 0x9d, 0xef, 0x5d, 0x78,
- 0xe0, 0x4b, 0xfb, 0x17, 0x0a, 0xdf, 0xd9, 0xbf, 0x50, 0xf8, 0xee, 0xfe, 0x85, 0xc2, 0x3b, 0xfb,
- 0x17, 0x0a, 0x7f, 0xbf, 0x7f, 0xa1, 0xf0, 0x4b, 0xff, 0x70, 0xe1, 0x81, 0xcf, 0x3c, 0x1b, 0x55,
- 0x61, 0x5a, 0x55, 0x61, 0x5a, 0x31, 0x9c, 0xee, 0x6e, 0xb7, 0xa7, 0x59, 0x15, 0xa2, 0x14, 0x55,
- 0x85, 0xff, 0x08, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x6f, 0x91, 0x67, 0x6c, 0x92, 0x00, 0x00,
+ 0x6a, 0x78, 0x4f, 0xdf, 0x28, 0x8a, 0x86, 0xd0, 0x46, 0xd8, 0x3b, 0x72, 0x68, 0xc6, 0xc2, 0xd1,
+ 0xc3, 0x3f, 0x1f, 0xcd, 0xe5, 0x3a, 0x91, 0x8e, 0xb3, 0x60, 0xef, 0xc8, 0xa1, 0xc9, 0xe7, 0xa1,
+ 0x11, 0x78, 0xba, 0xe3, 0x6f, 0xba, 0x5e, 0x87, 0x7a, 0x72, 0x1b, 0xb7, 0x90, 0xe3, 0x2f, 0x8c,
+ 0x6b, 0x11, 0x9a, 0x38, 0xea, 0x4a, 0x24, 0x61, 0x9c, 0x1b, 0xd9, 0x86, 0x5a, 0xcf, 0x14, 0x15,
+ 0x93, 0xf6, 0x8d, 0xd9, 0x3c, 0x3f, 0xd6, 0x8c, 0x9d, 0x37, 0xab, 0x37, 0x0c, 0x19, 0x24, 0x7f,
+ 0xf2, 0x55, 0x3d, 0xaa, 0x9f, 0x7c, 0xc5, 0x7b, 0x63, 0xd6, 0x15, 0x7f, 0xd2, 0x91, 0x1a, 0xa5,
+ 0xd3, 0x96, 0xee, 0x32, 0x0b, 0xb9, 0x95, 0x3d, 0xc1, 0xb2, 0x11, 0x6a, 0xa5, 0x4e, 0x1b, 0x15,
+ 0x0f, 0xad, 0x03, 0xd2, 0x0c, 0x4d, 0x8c, 0xc4, 0xaf, 0x30, 0xc4, 0xbd, 0x93, 0xe9, 0x83, 0xcd,
+ 0x07, 0xe1, 0x3f, 0x19, 0x62, 0xa1, 0x78, 0x33, 0xff, 0x79, 0xa1, 0xfd, 0x5d, 0x11, 0x4a, 0x6b,
+ 0xcb, 0x2d, 0x11, 0x5e, 0x8f, 0xff, 0x67, 0x86, 0xb6, 0xb6, 0xad, 0xee, 0x0d, 0xea, 0x59, 0x9b,
+ 0xbb, 0x72, 0x77, 0x1a, 0x0b, 0xaf, 0x97, 0xa6, 0xc0, 0x8c, 0x52, 0xe4, 0x15, 0x18, 0x35, 0xf4,
+ 0x39, 0xea, 0x05, 0xc3, 0xec, 0xbd, 0xf9, 0x05, 0xbb, 0xb9, 0xd9, 0xa8, 0x38, 0x26, 0xc0, 0xc8,
+ 0x3a, 0x80, 0x11, 0x41, 0x97, 0x0e, 0x6d, 0x31, 0x88, 0x01, 0xc7, 0x80, 0x08, 0x42, 0x7d, 0x9b,
+ 0x91, 0x72, 0xd4, 0xf2, 0x61, 0x50, 0x79, 0xcf, 0xb9, 0xaa, 0xca, 0x62, 0x04, 0xa3, 0x39, 0x30,
+ 0x96, 0xf8, 0x3f, 0x06, 0xf9, 0x28, 0xd4, 0xdc, 0x6e, 0x6c, 0x3a, 0xad, 0xf3, 0xed, 0x74, 0xed,
+ 0xba, 0x4c, 0xbb, 0xbd, 0x37, 0x39, 0xb6, 0xec, 0xb6, 0x2d, 0x43, 0x25, 0x60, 0x48, 0x4e, 0x34,
+ 0x18, 0xe1, 0xb7, 0x62, 0xd4, 0xdf, 0x31, 0xf8, 0xda, 0xc1, 0x03, 0xd8, 0xfb, 0x28, 0x73, 0xb4,
+ 0x2f, 0x96, 0x21, 0x3a, 0xbc, 0x21, 0x3e, 0x8c, 0x08, 0xaf, 0x5f, 0x39, 0x73, 0x1f, 0xab, 0x83,
+ 0xb1, 0x64, 0x45, 0xda, 0x50, 0x7a, 0xcd, 0xdd, 0xc8, 0x3d, 0x71, 0xc7, 0xae, 0xc3, 0x0a, 0x73,
+ 0x52, 0x2c, 0x01, 0x19, 0x07, 0xf2, 0x6b, 0x05, 0x38, 0xe5, 0xa7, 0x95, 0x4e, 0xd9, 0x1d, 0x30,
+ 0xbf, 0x76, 0x9d, 0x56, 0x63, 0xa5, 0x07, 0xe5, 0xa0, 0x6c, 0xec, 0xaf, 0x0b, 0x93, 0xbf, 0x38,
+ 0x55, 0x91, 0xdd, 0x69, 0x31, 0xe7, 0x3f, 0xdd, 0x92, 0xf2, 0x4f, 0xa6, 0xa1, 0x64, 0xa5, 0x7d,
+ 0xb9, 0x08, 0x8d, 0xd8, 0x6c, 0x9d, 0xfb, 0xa7, 0x2b, 0xb7, 0x52, 0x3f, 0x5d, 0x59, 0x1d, 0xfe,
+ 0x90, 0x31, 0xaa, 0xd5, 0x71, 0xff, 0x77, 0xe5, 0xcf, 0x8a, 0x50, 0x5a, 0x9f, 0x5f, 0x48, 0x6e,
+ 0x17, 0x0b, 0xf7, 0x60, 0xbb, 0xb8, 0x05, 0xd5, 0x8d, 0x9e, 0x65, 0x07, 0x96, 0x93, 0xfb, 0xc2,
+ 0xbe, 0xfa, 0x47, 0x8d, 0xbc, 0xf7, 0x2a, 0x50, 0x51, 0xc1, 0x93, 0x36, 0x54, 0xdb, 0x22, 0x62,
+ 0x5a, 0x6e, 0xd7, 0x2b, 0x19, 0x79, 0x4d, 0x30, 0x92, 0x2f, 0xa8, 0xd0, 0xb5, 0x5d, 0x90, 0x7f,
+ 0xb9, 0xbe, 0xe7, 0xd2, 0xd4, 0x3e, 0x0f, 0xa1, 0x16, 0x70, 0xef, 0x99, 0xff, 0x5b, 0x01, 0x92,
+ 0x8a, 0xcf, 0xbd, 0xef, 0x4d, 0xdb, 0xe9, 0xde, 0x34, 0x7f, 0x14, 0x83, 0x2f, 0xbb, 0x43, 0x69,
+ 0x7f, 0x58, 0x84, 0x91, 0x7b, 0x76, 0xc9, 0x92, 0x26, 0xbc, 0xc8, 0xe6, 0x72, 0x4e, 0x8c, 0x03,
+ 0x7d, 0xc8, 0x3a, 0x29, 0x1f, 0xb2, 0xbc, 0x7f, 0xd5, 0xbc, 0x8b, 0x07, 0xd9, 0x5f, 0x15, 0x40,
+ 0x4e, 0xcb, 0x4b, 0x8e, 0x1f, 0xe8, 0x8e, 0xc1, 0x7f, 0xee, 0x2e, 0xd7, 0x80, 0xbc, 0xae, 0x0a,
+ 0xd2, 0x9d, 0x47, 0x2c, 0xfb, 0xfc, 0x59, 0xcd, 0xf9, 0xe4, 0xc3, 0x50, 0xdb, 0x72, 0xfd, 0x80,
+ 0xcf, 0xf3, 0xc5, 0xa4, 0x5d, 0xe7, 0x8a, 0x4c, 0xc7, 0x90, 0x22, 0x7d, 0xa4, 0x57, 0x19, 0x7c,
+ 0xa4, 0xa7, 0x7d, 0xb3, 0x08, 0xa3, 0xef, 0x95, 0x9b, 0xa2, 0x59, 0x3e, 0x77, 0xa5, 0x9c, 0x3e,
+ 0x77, 0xe5, 0xc3, 0xf8, 0xdc, 0x69, 0xdf, 0x29, 0x00, 0xdc, 0xb3, 0x6b, 0xaa, 0x66, 0xd2, 0x1d,
+ 0x2e, 0x77, 0xbf, 0xca, 0x76, 0x86, 0xfb, 0xbd, 0x8a, 0xfa, 0x24, 0xee, 0x0a, 0xf7, 0x66, 0x01,
+ 0xc6, 0xf5, 0x84, 0x7b, 0x59, 0x6e, 0xd5, 0x32, 0xe5, 0xad, 0x16, 0x5e, 0xc9, 0x4b, 0xa6, 0x63,
+ 0x8a, 0x2d, 0x79, 0x2e, 0x0a, 0x91, 0x7a, 0x2d, 0xea, 0xf6, 0x7d, 0xb1, 0x4d, 0xb9, 0x9a, 0x93,
+ 0xa0, 0xbc, 0x8b, 0x3b, 0x5f, 0xe9, 0x48, 0xdc, 0xf9, 0xe2, 0x17, 0x95, 0xca, 0x77, 0xbc, 0xa8,
+ 0xb4, 0x03, 0xf5, 0x4d, 0xcf, 0xed, 0x70, 0x8f, 0x39, 0xf9, 0x3f, 0xce, 0xcb, 0x39, 0xd6, 0x94,
+ 0xe8, 0x4f, 0xd4, 0x91, 0x8d, 0x67, 0x41, 0xe1, 0x63, 0xc4, 0x8a, 0x1b, 0xa4, 0x5d, 0xc1, 0x75,
+ 0xe4, 0x28, 0xb9, 0x86, 0x73, 0xc9, 0x9a, 0x40, 0x47, 0xc5, 0x26, 0xe9, 0x25, 0x57, 0xbd, 0x37,
+ 0x5e, 0x72, 0xda, 0x2f, 0x94, 0xd5, 0x04, 0x76, 0xdf, 0x45, 0xe3, 0x7b, 0xef, 0x5f, 0x6f, 0x4c,
+ 0xdf, 0x3d, 0xac, 0xde, 0xc3, 0xbb, 0x87, 0xb5, 0xa1, 0x7c, 0xb2, 0xf6, 0x4a, 0x90, 0xda, 0x37,
+ 0xbd, 0x7f, 0x3a, 0xf1, 0x5f, 0xea, 0x74, 0xe2, 0xad, 0x22, 0x44, 0x13, 0xc1, 0x21, 0xdd, 0x2c,
+ 0x5e, 0x86, 0x5a, 0x47, 0xbf, 0x35, 0x4f, 0x6d, 0x7d, 0x37, 0xcf, 0x4f, 0x14, 0x57, 0x24, 0x06,
+ 0x86, 0x68, 0xc4, 0x07, 0xb0, 0xc2, 0x40, 0xc6, 0xb9, 0xad, 0xcd, 0x51, 0x4c, 0x64, 0x61, 0xcf,
+ 0x8a, 0xde, 0x31, 0xc6, 0x46, 0xfb, 0xcb, 0x22, 0xc8, 0x88, 0xd7, 0x84, 0x42, 0x65, 0xd3, 0xba,
+ 0x45, 0xcd, 0xdc, 0x2e, 0x87, 0xb1, 0x5f, 0xdb, 0x0a, 0x73, 0x3a, 0x4f, 0x40, 0x81, 0xce, 0xed,
+ 0xa4, 0xe2, 0x78, 0x44, 0xca, 0x2f, 0x87, 0x9d, 0x34, 0x7e, 0xcc, 0x22, 0xed, 0xa4, 0x22, 0x09,
+ 0x15, 0x0f, 0x61, 0x96, 0xe5, 0x67, 0xd4, 0x52, 0xa4, 0x79, 0xcc, 0xb2, 0xb1, 0xb3, 0x6e, 0x65,
+ 0x96, 0xf5, 0xc5, 0xe5, 0x63, 0xc9, 0xa3, 0xf9, 0xd9, 0x6f, 0x7f, 0xf7, 0xc2, 0x03, 0xdf, 0xf9,
+ 0xee, 0x85, 0x07, 0xde, 0xf9, 0xee, 0x85, 0x07, 0xbe, 0xb8, 0x7f, 0xa1, 0xf0, 0xed, 0xfd, 0x0b,
+ 0x85, 0xef, 0xec, 0x5f, 0x28, 0xbc, 0xb3, 0x7f, 0xa1, 0xf0, 0x0f, 0xfb, 0x17, 0x0a, 0xbf, 0xfc,
+ 0x8f, 0x17, 0x1e, 0xf8, 0xcc, 0xb3, 0x51, 0x15, 0xa6, 0x55, 0x15, 0xa6, 0x15, 0xc3, 0xe9, 0xee,
+ 0x76, 0x7b, 0x9a, 0x55, 0x21, 0x4a, 0x51, 0x55, 0xf8, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x12,
+ 0x35, 0xa0, 0x09, 0x68, 0x92, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -8367,16 +8367,18 @@ func (m *UDSink) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
- {
- size, err := m.Container.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
+ if m.Container != nil {
+ {
+ size, err := m.Container.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
}
- i -= size
- i = encodeVarintGenerated(dAtA, i, uint64(size))
+ i--
+ dAtA[i] = 0xa
}
- i--
- dAtA[i] = 0xa
return len(dAtA) - i, nil
}
@@ -10911,8 +10913,10 @@ func (m *UDSink) Size() (n int) {
}
var l int
_ = l
- l = m.Container.Size()
- n += 1 + l + sovGenerated(uint64(l))
+ if m.Container != nil {
+ l = m.Container.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
return n
}
@@ -12439,7 +12443,7 @@ func (this *UDSink) String() string {
return "nil"
}
s := strings.Join([]string{`&UDSink{`,
- `Container:` + strings.Replace(strings.Replace(this.Container.String(), "Container", "Container", 1), `&`, ``, 1) + `,`,
+ `Container:` + strings.Replace(this.Container.String(), "Container", "Container", 1) + `,`,
`}`,
}, "")
return s
@@ -29017,6 +29021,9 @@ func (m *UDSink) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
+ if m.Container == nil {
+ m.Container = &Container{}
+ }
if err := m.Container.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index 5ba6a78c12..81c9cf56e6 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -296,7 +296,9 @@ func (mv MonoVertex) simpleCopy() MonoVertex {
}
func (mv MonoVertex) GetPodSpec(req GetMonoVertexPodSpecReq) (*corev1.PodSpec, error) {
- monoVtxBytes, err := json.Marshal(mv.simpleCopy())
+ copiedSpec := mv.simpleCopy()
+ copiedSpec.Spec.Scale = Scale{}
+ monoVtxBytes, err := json.Marshal(copiedSpec)
if err != nil {
return nil, errors.New("failed to marshal mono vertex spec")
}
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
index 2f2fee9a39..aeb809db05 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
@@ -19,7 +19,47 @@ package v1alpha1
import (
"testing"
+ "github.com/stretchr/testify/assert"
+ corev1 "k8s.io/api/core/v1"
+ resource "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/utils/ptr"
+)
+
+var (
+ testMvtx = MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "default",
+ },
+ Spec: MonoVertexSpec{
+ Scale: Scale{
+ Min: ptr.To[int32](2),
+ Max: ptr.To[int32](4),
+ },
+ Source: &Source{
+ UDSource: &UDSource{
+ Container: &Container{
+ Image: "test-image1",
+ },
+ },
+ UDTransformer: &UDTransformer{
+ Container: &Container{
+ Image: "test-image2",
+ },
+ },
+ },
+ Sink: &Sink{
+ AbstractSink: AbstractSink{
+ UDSink: &UDSink{
+ Container: &Container{
+ Image: "test-image3",
+ },
+ },
+ },
+ },
+ },
+ }
)
func TestMonoVertex_GetDaemonServiceObj(t *testing.T) {
@@ -83,3 +123,51 @@ func TestMonoVertexStatus_MarkDeployFailed(t *testing.T) {
t.Errorf("MarkDeployFailed should set the Message correctly, got %s", mvs.Message)
}
}
+
+func TestMonoVertexGetPodSpec(t *testing.T) {
+
+ t.Run("test get pod spec - okay", func(t *testing.T) {
+ req := GetMonoVertexPodSpecReq{
+ Image: "my-image",
+ PullPolicy: corev1.PullIfNotPresent,
+ Env: []corev1.EnvVar{
+ {
+ Name: "ENV_VAR_NAME",
+ Value: "ENV_VAR_VALUE",
+ },
+ },
+ DefaultResources: corev1.ResourceRequirements{
+ Limits: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("200m"),
+ corev1.ResourceMemory: resource.MustParse("200Mi"),
+ },
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("100m"),
+ corev1.ResourceMemory: resource.MustParse("100Mi"),
+ },
+ },
+ }
+ podSpec, err := testMvtx.GetPodSpec(req)
+ assert.NoError(t, err)
+ assert.Equal(t, 4, len(podSpec.Containers))
+ assert.Equal(t, 1, len(podSpec.Volumes))
+ assert.Equal(t, "my-image", podSpec.Containers[0].Image)
+ assert.Equal(t, corev1.PullIfNotPresent, podSpec.Containers[0].ImagePullPolicy)
+ assert.Equal(t, "100m", podSpec.Containers[0].Resources.Requests.Cpu().String())
+ assert.Equal(t, "200m", podSpec.Containers[0].Resources.Limits.Cpu().String())
+ assert.Equal(t, "100Mi", podSpec.Containers[0].Resources.Requests.Memory().String())
+ assert.Equal(t, "200Mi", podSpec.Containers[0].Resources.Limits.Memory().String())
+ assert.Equal(t, "test-image1", podSpec.Containers[1].Image)
+ assert.Equal(t, "test-image2", podSpec.Containers[2].Image)
+ assert.Equal(t, "test-image3", podSpec.Containers[3].Image)
+ for _, c := range podSpec.Containers {
+ assert.Equal(t, 1, len(c.VolumeMounts))
+ }
+ envNames := []string{}
+ for _, env := range podSpec.Containers[0].Env {
+ envNames = append(envNames, env.Name)
+ }
+ assert.Contains(t, envNames, "ENV_VAR_NAME")
+ assert.Contains(t, envNames, EnvMonoVertexObject)
+ })
+}
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index ac9d1140ef..79d63b30c4 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -4899,8 +4899,7 @@ func schema_pkg_apis_numaflow_v1alpha1_UDSink(ref common.ReferenceCallback) comm
Properties: map[string]spec.Schema{
"container": {
SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Container"),
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Container"),
},
},
},
diff --git a/pkg/apis/numaflow/v1alpha1/sink_test.go b/pkg/apis/numaflow/v1alpha1/sink_test.go
index 32860fc1dd..0fe7f002af 100644
--- a/pkg/apis/numaflow/v1alpha1/sink_test.go
+++ b/pkg/apis/numaflow/v1alpha1/sink_test.go
@@ -45,7 +45,7 @@ func Test_Sink_getUDSinkContainer(t *testing.T) {
x := Sink{
AbstractSink: AbstractSink{
UDSink: &UDSink{
- Container: Container{
+ Container: &Container{
Image: "my-image",
Args: []string{"my-arg"},
SecurityContext: &corev1.SecurityContext{},
@@ -84,7 +84,7 @@ func Test_Sink_getFallbackUDSinkContainer(t *testing.T) {
x := Sink{
AbstractSink: AbstractSink{
UDSink: &UDSink{
- Container: Container{
+ Container: &Container{
Image: "my-image",
Args: []string{"my-arg"},
SecurityContext: &corev1.SecurityContext{},
@@ -96,7 +96,7 @@ func Test_Sink_getFallbackUDSinkContainer(t *testing.T) {
},
Fallback: &AbstractSink{
UDSink: &UDSink{
- Container: Container{
+ Container: &Container{
Image: "my-image",
Args: []string{"my-arg"},
SecurityContext: &corev1.SecurityContext{},
diff --git a/pkg/apis/numaflow/v1alpha1/user_defined_sink.go b/pkg/apis/numaflow/v1alpha1/user_defined_sink.go
index 4edc970e59..2f10670ae6 100644
--- a/pkg/apis/numaflow/v1alpha1/user_defined_sink.go
+++ b/pkg/apis/numaflow/v1alpha1/user_defined_sink.go
@@ -17,5 +17,5 @@ limitations under the License.
package v1alpha1
type UDSink struct {
- Container Container `json:"container" protobuf:"bytes,1,opt,name=container"`
+ Container *Container `json:"container" protobuf:"bytes,1,opt,name=container"`
}
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
index 4336e15e31..1a4534c3ec 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
@@ -324,7 +324,7 @@ func TestGetPodSpec(t *testing.T) {
testObj.Spec.Sink = &Sink{
AbstractSink: AbstractSink{
UDSink: &UDSink{
- Container: Container{
+ Container: &Container{
Image: "image",
Command: []string{"cmd"},
Args: []string{"arg0"},
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index 475ef0ea31..b450852954 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -2524,7 +2524,11 @@ func (in *UDF) DeepCopy() *UDF {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UDSink) DeepCopyInto(out *UDSink) {
*out = *in
- in.Container.DeepCopyInto(&out.Container)
+ if in.Container != nil {
+ in, out := &in.Container, &out.Container
+ *out = new(Container)
+ (*in).DeepCopyInto(*out)
+ }
return
}
diff --git a/pkg/reconciler/vertex/controller_test.go b/pkg/reconciler/vertex/controller_test.go
index f0f9128a8f..dedc25898e 100644
--- a/pkg/reconciler/vertex/controller_test.go
+++ b/pkg/reconciler/vertex/controller_test.go
@@ -317,7 +317,7 @@ func Test_BuildPodSpec(t *testing.T) {
testObj.Spec.Sink = &dfv1.Sink{
AbstractSink: dfv1.AbstractSink{
UDSink: &dfv1.UDSink{
- Container: dfv1.Container{
+ Container: &dfv1.Container{
Image: "image",
Command: []string{"cmd"},
Args: []string{"arg0"},
From 6f19b17e94ab812b1fbda501127896535cef2ab4 Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Thu, 22 Aug 2024 08:56:48 +0530
Subject: [PATCH 023/188] chore: metrics and https support for serving (#1985)
Signed-off-by: Yashash H L
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
rust/Cargo.lock | 124 +------------
rust/monovertex/Cargo.toml | 4 +-
rust/monovertex/src/metrics.rs | 2 +-
rust/serving/Cargo.toml | 9 +-
rust/serving/src/app.rs | 101 +++++++----
.../src/app/callback/store/redisstore.rs | 10 +-
rust/serving/src/app/tracker.rs | 2 +-
rust/serving/src/config.rs | 37 ++--
rust/serving/src/error.rs | 44 ++---
rust/serving/src/lib.rs | 39 +++--
rust/serving/src/metrics.rs | 165 +++++++++++++-----
rust/serving/src/pipeline.rs | 9 +-
rust/src/bin/main.rs | 4 +-
13 files changed, 290 insertions(+), 260 deletions(-)
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 94bce16b9a..13b368b4ab 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -17,18 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-[[package]]
-name = "ahash"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
-dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -524,21 +512,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
-
[[package]]
name = "crunchy"
version = "0.2.2"
@@ -908,9 +881,6 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash",
-]
[[package]]
name = "headers"
@@ -1443,45 +1413,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
-[[package]]
-name = "metrics"
-version = "0.23.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261"
-dependencies = [
- "ahash",
- "portable-atomic",
-]
-
-[[package]]
-name = "metrics-exporter-prometheus"
-version = "0.15.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f0c8427b39666bf970460908b213ec09b3b350f20c0c2eabcbba51704a08e6"
-dependencies = [
- "base64 0.22.1",
- "indexmap 2.3.0",
- "metrics",
- "metrics-util",
- "quanta",
- "thiserror",
-]
-
-[[package]]
-name = "metrics-util"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4259040465c955f9f2f1a4a8a16dc46726169bca0f88e8fb2dbeced487c3e828"
-dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
- "hashbrown 0.14.5",
- "metrics",
- "num_cpus",
- "quanta",
- "sketches-ddsketch",
-]
-
[[package]]
name = "mime"
version = "0.3.17"
@@ -1653,16 +1584,6 @@ dependencies = [
"autocfg",
]
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
[[package]]
name = "numaflow"
version = "0.1.0"
@@ -2041,21 +1962,6 @@ dependencies = [
"prost",
]
-[[package]]
-name = "quanta"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5"
-dependencies = [
- "crossbeam-utils",
- "libc",
- "once_cell",
- "raw-cpuid",
- "wasi",
- "web-sys",
- "winapi",
-]
-
[[package]]
name = "quote"
version = "1.0.36"
@@ -2095,15 +2001,6 @@ dependencies = [
"getrandom",
]
-[[package]]
-name = "raw-cpuid"
-version = "11.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d"
-dependencies = [
- "bitflags 2.6.0",
-]
-
[[package]]
name = "rcgen"
version = "0.13.1"
@@ -2597,17 +2494,20 @@ dependencies = [
"async-nats",
"axum",
"axum-macros",
+ "axum-server",
"backoff",
"base64 0.22.1",
"chrono",
"config",
"hyper-util",
- "metrics",
- "metrics-exporter-prometheus",
+ "parking_lot",
+ "prometheus-client",
+ "rcgen",
"redis",
"serde",
"serde_json",
"tempfile",
+ "thiserror",
"tokio",
"tower",
"tower-http",
@@ -2691,12 +2591,6 @@ dependencies = [
"rand_core",
]
-[[package]]
-name = "sketches-ddsketch"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85636c14b73d81f541e525f585c0a2109e6744e1565b5c1668e31c70c10ed65c"
-
[[package]]
name = "slab"
version = "0.4.9"
@@ -2888,9 +2782,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.39.2"
+version = "1.39.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
+checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
dependencies = [
"backtrace",
"bytes",
@@ -3090,9 +2984,9 @@ dependencies = [
[[package]]
name = "tower-layer"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index 4efb9658b2..d56a502541 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -6,10 +6,10 @@ edition = "2021"
[dependencies]
axum = "0.7.5"
axum-server = { version = "0.7.1", features = ["tls-rustls"] }
-tonic = "0.12.0"
+tonic = "0.12.1"
bytes = "1.7.1"
thiserror = "1.0.63"
-tokio = { version = "1.39.2", features = ["full"] }
+tokio = { version = "1.39.3", features = ["full"] }
tracing = "0.1.40"
tokio-util = "0.7.11"
tokio-stream = "0.1.15"
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index 789ba47a3b..7a87b508a4 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -174,7 +174,7 @@ static MONOVTX_METRICS_LABELS: OnceLock> = OnceLock::new()
// forward_metrics_labels is a helper function used to fetch the
// MONOVTX_METRICS_LABELS object
pub(crate) fn forward_metrics_labels() -> &'static Vec<(String, String)> {
- crate::metrics::MONOVTX_METRICS_LABELS.get_or_init(|| {
+ MONOVTX_METRICS_LABELS.get_or_init(|| {
let common_labels = vec![
(
MONO_VERTEX_NAME_LABEL.to_string(),
diff --git a/rust/serving/Cargo.toml b/rust/serving/Cargo.toml
index 635bb4f208..2dfb9b9c33 100644
--- a/rust/serving/Cargo.toml
+++ b/rust/serving/Cargo.toml
@@ -11,13 +11,12 @@ all-tests = ["redis-tests", "nats-tests"]
[dependencies]
async-nats = "0.35.1"
axum = "0.7.5"
+axum-server = { version = "0.7.1", features = ["tls-rustls"] }
axum-macros = "0.4.1"
hyper-util = { version = "0.1.6", features = ["client-legacy"] }
-metrics = { version = "0.23.0", default-features = false }
-metrics-exporter-prometheus = { version = "0.15.3", default-features = false }
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120"
-tokio = { version = "1.36.0", features = ["full"] }
+tokio = { version = "1.39.3", features = ["full"] }
tower = "0.4.13"
tower-http = { version = "0.5.2", features = ["trace", "timeout"] }
tracing = "0.1.40"
@@ -30,4 +29,8 @@ trait-variant = "0.1.2"
chrono = { version = "0.4", features = ["serde"] }
backoff = { path = "../backoff" }
base64 = "0.22.1"
+rcgen = "0.13.1"
+parking_lot = "0.12.3"
+prometheus-client = "0.22.3"
+thiserror = "1.0.63"
diff --git a/rust/serving/src/app.rs b/rust/serving/src/app.rs
index 87a789159b..3043f3e34b 100644
--- a/rust/serving/src/app.rs
+++ b/rust/serving/src/app.rs
@@ -1,4 +1,5 @@
use std::env;
+use std::net::SocketAddr;
use std::time::Duration;
use async_nats::jetstream;
@@ -8,14 +9,15 @@ use axum::http::StatusCode;
use axum::middleware::Next;
use axum::response::Response;
use axum::{body::Body, http::Request, middleware, response::IntoResponse, routing::get, Router};
+use axum_server::tls_rustls::RustlsConfig;
+use axum_server::Handle;
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::rt::TokioExecutor;
-use tokio::net::TcpListener;
use tokio::signal;
use tower::ServiceBuilder;
use tower_http::timeout::TimeoutLayer;
use tower_http::trace::{DefaultOnResponse, TraceLayer};
-use tracing::{debug, info_span, Level};
+use tracing::{debug, info, info_span, Level};
use uuid::Uuid;
use self::{
@@ -25,6 +27,7 @@ use self::{
use crate::app::callback::store::Store;
use crate::app::tracker::MessageGraph;
use crate::pipeline::pipeline_spec;
+use crate::Error::{InitError, MetricsServer};
use crate::{app::callback::state::State as CallbackState, config, metrics::capture_metrics};
/// manage callbacks
@@ -48,14 +51,10 @@ const ENV_NUMAFLOW_SERVING_AUTH_TOKEN: &str = "NUMAFLOW_SERVING_AUTH_TOKEN";
// - [ ] outer fallback for /v1/direct
/// Start the main application Router and the axum server.
-pub(crate) async fn start_main_server(addr: A) -> crate::Result<()>
-where
- A: tokio::net::ToSocketAddrs + std::fmt::Debug,
-{
- let listener = TcpListener::bind(&addr)
- .await
- .map_err(|e| format!("Creating listener on {:?}: {}", addr, e))?;
-
+pub(crate) async fn start_main_server(
+ addr: SocketAddr,
+ tls_config: RustlsConfig,
+) -> crate::Result<()> {
debug!(?addr, "App server started");
let layers = ServiceBuilder::new()
@@ -90,8 +89,14 @@ where
.layer(middleware::from_fn(auth_middleware));
// Create the message graph from the pipeline spec and the redis store
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec())
- .map_err(|e| format!("Creating message graph from pipeline spec: {:?}", e))?;
+ let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).map_err(|e| {
+ InitError(format!(
+ "Creating message graph from pipeline spec: {:?}",
+ e
+ ))
+ })?;
+
+ // Create a redis store to store the callbacks and the custom responses
let redis_store = callback::store::redisstore::RedisConnection::new(
&config().redis.addr,
config().redis.max_tasks,
@@ -99,18 +104,51 @@ where
.await?;
let state = CallbackState::new(msg_graph, redis_store).await?;
+ let handle = Handle::new();
+ // Spawn a task to gracefully shutdown server.
+ tokio::spawn(graceful_shutdown(handle.clone()));
+
// Create a Jetstream context
let js_context = create_js_context().await?;
let router = setup_app(js_context, state).await?.layer(layers);
- axum::serve(listener, router)
- .with_graceful_shutdown(shutdown_signal())
+ axum_server::bind_rustls(addr, tls_config)
+ .handle(handle)
+ .serve(router.into_make_service())
.await
- .map_err(|e| format!("Starting web server: {}", e))?;
+ .map_err(|e| MetricsServer(format!("Starting web server for metrics: {}", e)))?;
Ok(())
}
+// Gracefully shutdown the server on receiving SIGINT or SIGTERM
+// by sending a shutdown signal to the server using the handle.
+async fn graceful_shutdown(handle: Handle) {
+ let ctrl_c = async {
+ signal::ctrl_c()
+ .await
+ .expect("failed to install Ctrl+C handler");
+ };
+
+ let terminate = async {
+ signal::unix::signal(signal::unix::SignalKind::terminate())
+ .expect("failed to install signal handler")
+ .recv()
+ .await;
+ };
+
+ tokio::select! {
+ _ = ctrl_c => {},
+ _ = terminate => {},
+ }
+
+ info!("sending graceful shutdown signal");
+
+ // Signal the server to shutdown using Handle.
+ // TODO: make the duration configurable
+ handle.graceful_shutdown(Some(Duration::from_secs(30)));
+}
+
async fn create_js_context() -> crate::Result {
// Check for user and password in the Jetstream configuration
let js_config = &config().jetstream;
@@ -130,11 +168,11 @@ async fn create_js_context() -> crate::Result {
_ => async_nats::connect(&js_config.url).await,
}
.map_err(|e| {
- format!(
+ InitError(format!(
"Connecting to jetstream server {}: {}",
&config().jetstream.url,
e
- )
+ ))
})?;
Ok(jetstream::new(js_client))
}
@@ -240,30 +278,11 @@ async fn routes(
.merge(message_path_handler))
}
-async fn shutdown_signal() {
- let ctrl_c = async {
- signal::ctrl_c()
- .await
- .expect("failed to install Ctrl+C handler");
- };
-
- let terminate = async {
- signal::unix::signal(signal::unix::SignalKind::terminate())
- .expect("failed to install signal handler")
- .recv()
- .await;
- };
-
- tokio::select! {
- _ = ctrl_c => {},
- _ = terminate => {},
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
use crate::app::callback::store::memstore::InMemoryStore;
+ use crate::config::cert_key_pair;
use async_nats::jetstream::stream;
use axum::http::StatusCode;
use std::net::SocketAddr;
@@ -272,9 +291,15 @@ mod tests {
#[tokio::test]
async fn test_start_main_server() {
+ let (cert, key) = cert_key_pair();
+
+ let tls_config = RustlsConfig::from_pem(cert.pem().into(), key.serialize_pem().into())
+ .await
+ .unwrap();
+
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
let server = tokio::spawn(async move {
- let result = start_main_server(addr).await;
+ let result = start_main_server(addr, tls_config).await;
assert!(result.is_ok())
});
diff --git a/rust/serving/src/app/callback/store/redisstore.rs b/rust/serving/src/app/callback/store/redisstore.rs
index dea8f0b41d..002f68b6c1 100644
--- a/rust/serving/src/app/callback/store/redisstore.rs
+++ b/rust/serving/src/app/callback/store/redisstore.rs
@@ -7,12 +7,12 @@ use tokio::sync::Semaphore;
use backoff::retry::Retry;
use backoff::strategy::fixed;
+use super::PayloadToSave;
use crate::app::callback::CallbackRequest;
use crate::consts::SAVED;
+use crate::Error::Connection;
use crate::{config, Error};
-use super::PayloadToSave;
-
const LPUSH: &str = "LPUSH";
const LRANGE: &str = "LRANGE";
const EXPIRE: &str = "EXPIRE";
@@ -27,12 +27,12 @@ pub(crate) struct RedisConnection {
impl RedisConnection {
/// Creates a new RedisConnection with concurrent operations on Redis set by max_tasks.
pub(crate) async fn new(addr: &str, max_tasks: usize) -> crate::Result {
- let client =
- redis::Client::open(addr).map_err(|e| format!("Creating Redis client: {e:?}"))?;
+ let client = redis::Client::open(addr)
+ .map_err(|e| Connection(format!("Creating Redis client: {e:?}")))?;
let conn = client
.get_connection_manager()
.await
- .map_err(|e| format!("Connecting to Redis server: {e:?}"))?;
+ .map_err(|e| Connection(format!("Connecting to Redis server: {e:?}")))?;
Ok(Self {
conn_manager: conn,
max_tasks,
diff --git a/rust/serving/src/app/tracker.rs b/rust/serving/src/app/tracker.rs
index 8487ac5696..cdd31a1c99 100644
--- a/rust/serving/src/app/tracker.rs
+++ b/rust/serving/src/app/tracker.rs
@@ -108,7 +108,7 @@ impl MessageGraph {
if result {
match serde_json::to_string(&subgraph) {
Ok(json) => Ok(Some(json)),
- Err(e) => Err(Error::SubGraphGeneratorError(e.to_string())),
+ Err(e) => Err(Error::SubGraphGenerator(e.to_string())),
}
} else {
Ok(None)
diff --git a/rust/serving/src/config.rs b/rust/serving/src/config.rs
index 0f9e8f6238..ab40485876 100644
--- a/rust/serving/src/config.rs
+++ b/rust/serving/src/config.rs
@@ -1,13 +1,15 @@
-use std::fmt::Debug;
-use std::path::Path;
-use std::{env, sync::OnceLock};
-
+use async_nats::rustls;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
use config::Config;
+use rcgen::{generate_simple_self_signed, Certificate, CertifiedKey, KeyPair};
use serde::{Deserialize, Serialize};
+use std::fmt::Debug;
+use std::path::Path;
+use std::{env, sync::OnceLock};
use tracing::info;
+use crate::Error::ParseConfig;
use crate::{Error, Result};
const ENV_PREFIX: &str = "NUMAFLOW_SERVING";
@@ -33,12 +35,23 @@ pub fn config() -> &'static Settings {
})
}
+static GLOBAL_TLS_CONFIG: OnceLock<(Certificate, KeyPair)> = OnceLock::new();
+
+fn init_cert_key_pair() -> std::result::Result<(Certificate, KeyPair), String> {
+ let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
+ let CertifiedKey { cert, key_pair } = generate_simple_self_signed(vec!["localhost".into()])
+ .map_err(|e| format!("Failed to generate cert {:?}", e))?;
+ Ok((cert, key_pair))
+}
+
+pub fn cert_key_pair() -> &'static (Certificate, KeyPair) {
+ GLOBAL_TLS_CONFIG.get_or_init(|| init_cert_key_pair().expect("Failed to initialize TLS config"))
+}
+
#[derive(Debug, Deserialize)]
pub struct JetStreamConfig {
pub stream: String,
pub url: String,
- pub user: Option,
- pub password: Option,
}
#[derive(Debug, Deserialize)]
@@ -94,11 +107,11 @@ impl Settings {
.separator("."),
)
.build()
- .map_err(|e| format!("generating runtime configuration: {e:?}"))?;
+ .map_err(|e| ParseConfig(format!("generating runtime configuration: {e:?}")))?;
let mut settings = settings
.try_deserialize::()
- .map_err(|e| format!("parsing runtime configuration: {e:?}"))?;
+ .map_err(|e| ParseConfig(format!("parsing runtime configuration: {e:?}")))?;
// Update JetStreamConfig from environment variables
if let Ok(url) = env::var(ENV_NUMAFLOW_SERVING_JETSTREAM_URL) {
@@ -114,10 +127,10 @@ impl Settings {
Ok(source_spec_encoded) => {
let source_spec_decoded = BASE64_STANDARD
.decode(source_spec_encoded.as_bytes())
- .map_err(|e| format!("decoding NUMAFLOW_SERVING_SOURCE: {e:?}"))?;
+ .map_err(|e| ParseConfig(format!("decoding NUMAFLOW_SERVING_SOURCE: {e:?}")))?;
let source_spec = serde_json::from_slice::(&source_spec_decoded)
- .map_err(|e| format!("parsing NUMAFLOW_SERVING_SOURCE: {e:?}"))?;
+ .map_err(|e| ParseConfig(format!("parsing NUMAFLOW_SERVING_SOURCE: {e:?}")))?;
// Update tid_header from source_spec
if let Some(msg_id_header_key) = source_spec.msg_id_header_key {
@@ -130,10 +143,10 @@ impl Settings {
// Update redis.ttl_secs from environment variable
settings.redis.ttl_secs = match env::var(ENV_NUMAFLOW_SERVING_STORE_TTL) {
Ok(ttl_secs) => Some(ttl_secs.parse().map_err(|e| {
- format!(
+ ParseConfig(format!(
"parsing NUMAFLOW_SERVING_STORE_TTL: expected u32, got {:?}",
e
- )
+ ))
})?),
Err(_) => None,
};
diff --git a/rust/serving/src/error.rs b/rust/serving/src/error.rs
index 83d4546b2e..d53509c939 100644
--- a/rust/serving/src/error.rs
+++ b/rust/serving/src/error.rs
@@ -1,46 +1,50 @@
+use thiserror::Error;
+
+// TODO: introduce module level error handling
+
pub type Result = std::result::Result;
-#[derive(Debug)]
+#[derive(Error, Debug, Clone)]
pub enum Error {
- ConfigMissingEnv(&'static str),
+ #[error("ParseConfig Error - {0}")]
+ ParseConfig(String),
// callback errors
// TODO: store the ID too?
+ #[error("IDNotFound Error - {0}")]
IDNotFound(&'static str),
+ #[error("SubGraphGenerator Error - {0}")]
// subgraph generator errors
- SubGraphGeneratorError(String),
+ SubGraphGenerator(String),
+ #[error("StoreWrite Error - {0}")]
// Store write errors
StoreWrite(String),
+ #[error("SubGraphNotFound Error - {0}")]
// Sub Graph Not Found Error
SubGraphNotFound(&'static str),
+ #[error("SubGraphInvalidInput Error - {0}")]
// Sub Graph Invalid Input Error
SubGraphInvalidInput(String),
+ #[error("StoreRead Error - {0}")]
// Store read errors
StoreRead(String),
- // catch-all variant for now
- Other(String),
-}
+ #[error("Metrics Error - {0}")]
+ // Metrics errors
+ MetricsServer(String),
-impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- // reuse the debug implementation for now
- write!(f, "{self:?}")
- }
-}
+ #[error("Connection Error - {0}")]
+ Connection(String),
-impl std::error::Error for Error {}
+ #[error("Init Error - {0}")]
+ InitError(String),
-impl From for Error
-where
- T: Into,
-{
- fn from(value: T) -> Self {
- Error::Other(value.into())
- }
+ #[error("Other Error - {0}")]
+ // catch-all variant for now
+ Other(String),
}
diff --git a/rust/serving/src/lib.rs b/rust/serving/src/lib.rs
index 4a86d0a0bf..72de843d8d 100644
--- a/rust/serving/src/lib.rs
+++ b/rust/serving/src/lib.rs
@@ -1,9 +1,11 @@
pub use self::error::{Error, Result};
use crate::app::start_main_server;
-use crate::config::config;
-use crate::metrics::start_metrics_server;
+use crate::config::{cert_key_pair, config};
+use crate::metrics::start_https_metrics_server;
use crate::pipeline::pipeline_spec;
-use tracing::{error, info};
+use axum_server::tls_rustls::RustlsConfig;
+use std::net::SocketAddr;
+use tracing::info;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
@@ -14,7 +16,13 @@ mod error;
mod metrics;
mod pipeline;
-pub async fn serve() {
+pub async fn serve() -> std::result::Result<(), Box> {
+ let (cert, key) = cert_key_pair();
+
+ let tls_config = RustlsConfig::from_pem(cert.pem().into(), key.serialize_pem().into())
+ .await
+ .map_err(|e| format!("Failed to create tls config {:?}", e))?;
+
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
@@ -27,19 +35,22 @@ pub async fn serve() {
info!(config = ?config(), pipeline_spec = ? pipeline_spec(), "Starting server with config and pipeline spec");
- let metrics_server_handle = tokio::spawn(start_metrics_server((
- "0.0.0.0",
- config().metrics_server_listen_port,
- )));
- let app_server_handle = tokio::spawn(start_main_server(("0.0.0.0", config().app_listen_port)));
+ // Start the metrics server, which serves the prometheus metrics.
+ let metrics_addr: SocketAddr =
+ format!("0.0.0.0:{}", &config().metrics_server_listen_port).parse()?;
+
+ let metrics_server_handle =
+ tokio::spawn(start_https_metrics_server(metrics_addr, tls_config.clone()));
+
+ let app_addr: SocketAddr = format!("0.0.0.0:{}", &config().app_listen_port).parse()?;
+
+ // Start the main server, which serves the application.
+ let app_server_handle = tokio::spawn(start_main_server(app_addr, tls_config));
// TODO: is try_join the best? we need to short-circuit at the first failure
- let servers = tokio::try_join!(flatten(app_server_handle), flatten(metrics_server_handle));
+ tokio::try_join!(flatten(app_server_handle), flatten(metrics_server_handle))?;
- if let Err(e) = servers {
- error!(error=?e, "Failed to run the servers");
- std::process::exit(1)
- }
+ Ok(())
}
async fn flatten(handle: tokio::task::JoinHandle>) -> Result {
diff --git a/rust/serving/src/metrics.rs b/rust/serving/src/metrics.rs
index caddadb377..cd4277efa3 100644
--- a/rust/serving/src/metrics.rs
+++ b/rust/serving/src/metrics.rs
@@ -1,5 +1,7 @@
-use std::{future::ready, time::Instant};
-
+use crate::Error::MetricsServer;
+use axum::body::Body;
+use axum::http::StatusCode;
+use axum::response::IntoResponse;
use axum::{
extract::{MatchedPath, Request},
middleware::Next,
@@ -7,52 +9,111 @@ use axum::{
routing::get,
Router,
};
-use metrics::{counter, histogram};
-use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle};
-use tokio::net::{TcpListener, ToSocketAddrs};
+use axum_server::tls_rustls::RustlsConfig;
+use prometheus_client::encoding::text::encode;
+use prometheus_client::metrics::counter::Counter;
+use prometheus_client::metrics::family::Family;
+use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
+use prometheus_client::registry::Registry;
+use std::net::SocketAddr;
+use std::sync::OnceLock;
+use std::time::Instant;
use tracing::debug;
-/// Collect and emit prometheus metrics.
+// Define the labels for the metrics
+pub const SERVING_METHOD_LABEL: &str = "method";
+pub const SERVING_PATH_LABEL: &str = "path";
+const SERVING_STATUS_LABEL: &str = "status";
-/// Metrics router and server
-pub(crate) async fn start_metrics_server(addr: A) -> crate::Result<()>
-where
- A: ToSocketAddrs + std::fmt::Debug,
-{
- // setup_metrics_recorder should only be invoked once
- let recorder_handle = setup_metrics_recorder()?;
- let metrics_app = Router::new().route("/metrics", get(move || ready(recorder_handle.render())));
+// Define the metrics
+const HTTP_REQUESTS_TOTAL: &str = "http_requests_total";
+const HTTP_REQUESTS_DURATION: &str = "http_requests_duration";
- let listener = TcpListener::bind(&addr)
- .await
- .map_err(|e| format!("Creating listener on {:?}: {}", addr, e))?;
+#[derive(Default)]
+pub struct GlobalRegistry {
+ pub registry: parking_lot::Mutex,
+}
+
+impl GlobalRegistry {
+ fn new() -> Self {
+ GlobalRegistry {
+ registry: parking_lot::Mutex::new(Registry::default()),
+ }
+ }
+}
+
+static GLOBAL_REGISTER: OnceLock = OnceLock::new();
+
+fn global_registry() -> &'static GlobalRegistry {
+ GLOBAL_REGISTER.get_or_init(GlobalRegistry::new)
+}
+
+pub struct ServingMetrics {
+ pub http_requests_total: Family, Counter>,
+ pub http_requests_duration: Family, Histogram>,
+}
+
+impl ServingMetrics {
+ fn new() -> Self {
+ let http_requests_total = Family::, Counter>::default();
+ let http_requests_duration =
+ Family::, Histogram>::new_with_constructor(|| {
+ Histogram::new(exponential_buckets(0.001, 2.0, 20))
+ });
+
+ let metrics = Self {
+ http_requests_total,
+ http_requests_duration,
+ };
+
+ let mut registry = global_registry().registry.lock();
+
+ registry.register(
+ HTTP_REQUESTS_TOTAL,
+ "A Counter to keep track of the total number of HTTP requests",
+ metrics.http_requests_total.clone(),
+ );
+ registry.register(
+ HTTP_REQUESTS_DURATION,
+ "A Histogram to keep track of the duration of HTTP requests",
+ metrics.http_requests_duration.clone(),
+ );
+
+ metrics
+ }
+}
- debug!("metrics server started at addr: {:?}", addr);
+static SERVING_METRICS: OnceLock = OnceLock::new();
+
+pub(crate) fn serving_metrics() -> &'static ServingMetrics {
+ SERVING_METRICS.get_or_init(ServingMetrics::new)
+}
- axum::serve(listener, metrics_app)
+pub(crate) async fn start_https_metrics_server(
+ addr: SocketAddr,
+ tls_config: RustlsConfig,
+) -> crate::Result<()> {
+ let metrics_app = Router::new().route("/metrics", get(metrics_handler));
+
+ axum_server::bind_rustls(addr, tls_config)
+ .serve(metrics_app.into_make_service())
.await
- .map_err(|e| format!("Starting web server for metrics: {}", e))?;
+ .map_err(|e| MetricsServer(format!("Starting web server for metrics: {}", e)))?;
+
Ok(())
}
-/// setup the Prometheus metrics recorder.
-fn setup_metrics_recorder() -> crate::Result {
- // 1 micro-sec < t < 1000 seconds
- let log_to_power_of_sqrt2_bins: [f64; 62] = (0..62)
- .map(|i| 2_f64.sqrt().powf(i as f64))
- .collect::>()
- .try_into()
- .unwrap();
-
- let prometheus_handle = PrometheusBuilder::new()
- .set_buckets_for_metric(
- Matcher::Full("http_requests_duration_micros".to_string()),
- &log_to_power_of_sqrt2_bins,
- )
- .map_err(|e| format!("Prometheus set_buckets_for_metric: {}", e))?
- .install_recorder()
- .map_err(|e| format!("Prometheus install_recorder: {}", e))?;
- Ok(prometheus_handle)
+// metrics_handler is used to generate and return a snapshot of the
+// current state of the metrics in the global registry
+pub async fn metrics_handler() -> impl IntoResponse {
+ let state = global_registry().registry.lock();
+ let mut buffer = String::new();
+ encode(&mut buffer, &state).unwrap();
+ debug!("Exposing Metrics: {:?}", buffer);
+ axum::http::Response::builder()
+ .status(StatusCode::OK)
+ .body(Body::from(buffer))
+ .unwrap()
}
/// Emit request metrics and also latency metrics.
@@ -73,11 +134,21 @@ pub(crate) async fn capture_metrics(request: Request, next: Next) -> Response {
let latency = start.elapsed().as_micros() as f64;
let status = response.status().as_u16().to_string();
- let labels = [("method", method), ("path", path), ("status", status)];
+ let labels = vec![
+ (SERVING_METHOD_LABEL.to_string(), method),
+ (SERVING_PATH_LABEL.to_string(), path),
+ (SERVING_STATUS_LABEL.to_string(), status),
+ ];
- histogram!("http_requests_duration_micros", &labels).record(latency);
+ serving_metrics()
+ .http_requests_duration
+ .get_or_create(&labels)
+ .observe(latency);
- counter!("http_requests_total", &labels).increment(1);
+ serving_metrics()
+ .http_requests_total
+ .get_or_create(&labels)
+ .inc();
response
}
@@ -87,19 +158,25 @@ mod tests {
use std::net::SocketAddr;
use std::time::Duration;
+ use super::*;
+ use crate::config::cert_key_pair;
use axum::body::Body;
use axum::http::{HeaderMap, StatusCode};
use axum::middleware;
use tokio::time::sleep;
use tower::ServiceExt;
- use super::*;
-
#[tokio::test]
async fn test_start_metrics_server() {
+ let (cert, key) = cert_key_pair();
+
+ let tls_config = RustlsConfig::from_pem(cert.pem().into(), key.serialize_pem().into())
+ .await
+ .unwrap();
+
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
let server = tokio::spawn(async move {
- let result = start_metrics_server(addr).await;
+ let result = start_https_metrics_server(addr, tls_config).await;
assert!(result.is_ok())
});
diff --git a/rust/serving/src/pipeline.rs b/rust/serving/src/pipeline.rs
index 6a51d65627..7f98c30611 100644
--- a/rust/serving/src/pipeline.rs
+++ b/rust/serving/src/pipeline.rs
@@ -1,6 +1,7 @@
use std::env;
use std::sync::OnceLock;
+use crate::Error::ParseConfig;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
use serde::{Deserialize, Serialize};
@@ -79,18 +80,18 @@ impl Pipeline {
// If the environment variable is set, decode and parse the pipeline
let decoded = BASE64_STANDARD
.decode(env_value.as_bytes())
- .map_err(|e| format!("decoding pipeline from env: {e:?}"))?;
+ .map_err(|e| ParseConfig(format!("decoding pipeline from env: {e:?}")))?;
serde_json::from_slice::(&decoded)
- .map_err(|e| format!("parsing pipeline from env: {e:?}"))?
+ .map_err(|e| ParseConfig(format!("parsing pipeline from env: {e:?}")))?
}
Err(_) => {
// If the environment variable is not set, read the pipeline from a file
let file_path = "./config/pipeline_spec.json";
let file_contents = std::fs::read_to_string(file_path)
- .map_err(|e| format!("reading pipeline file: {e:?}"))?;
+ .map_err(|e| ParseConfig(format!("reading pipeline file: {e:?}")))?;
serde_json::from_str::(&file_contents)
- .map_err(|e| format!("parsing pipeline file: {e:?}"))?
+ .map_err(|e| ParseConfig(format!("parsing pipeline file: {e:?}")))?
}
};
Ok(pipeline)
diff --git a/rust/src/bin/main.rs b/rust/src/bin/main.rs
index 8c29f33f73..9d102c019b 100644
--- a/rust/src/bin/main.rs
+++ b/rust/src/bin/main.rs
@@ -7,7 +7,9 @@ async fn main() {
// Based on the argument, run the appropriate component.
if args.contains(&"--serving".to_string()) {
- serving::serve().await;
+ if let Err(e) = serving::serve().await {
+ error!("Error running serving: {}", e);
+ }
} else if args.contains(&"--servesink".to_string()) {
if let Err(e) = servesink::servesink().await {
info!("Error running servesink: {}", e);
From deb1626ece55579d30e6d9003abe854980cc2923 Mon Sep 17 00:00:00 2001
From: Sreekanth
Date: Thu, 22 Aug 2024 17:44:49 +0530
Subject: [PATCH 024/188] fix: test coverage generation for Rust code (#1993)
Signed-off-by: Sreekanth
---
.github/workflows/ci.yaml | 11 +++++++++--
.github/workflows/nightly-build.yml | 2 ++
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index eb48e60d61..02dd5feeb5 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -102,10 +102,16 @@ jobs:
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
- components: llvm-tools-preview
+ cache-workspaces: rust -> target
+
+ - name: Install llvm-tools-preview
+ working-directory: ./rust
+ run: rustup component add llvm-tools-preview
- name: Install grcov
- run: cargo install grcov
+ uses: taiki-e/install-action@v2
+ with:
+ tool: grcov
- name: Install Protobuf Compiler
run: sudo apt-get install -y protobuf-compiler
@@ -153,6 +159,7 @@ jobs:
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
+ cache-workspaces: rust -> target
rustflags: ''
- name: Configure sccache
run: |
diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml
index 3a671b2275..641bc2d593 100644
--- a/.github/workflows/nightly-build.yml
+++ b/.github/workflows/nightly-build.yml
@@ -50,6 +50,7 @@ jobs:
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
+ cache-workspaces: rust -> target
rustflags: ''
- name: Configure sccache
run: |
@@ -82,6 +83,7 @@ jobs:
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
+ cache-workspaces: rust -> target
rustflags: ''
- name: Configure sccache
run: |
From 04b259ac53d993b0d04f3c82bd2815da607b112e Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Fri, 23 Aug 2024 13:58:09 +0530
Subject: [PATCH 025/188] chore: use numaflow models to create minimum pipeline
spec for serving (#1995)
Signed-off-by: Yashash H L
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
pkg/apis/numaflow/v1alpha1/vertex_types.go | 4 +-
rust/Cargo.lock | 1 +
rust/serving/Cargo.toml | 1 +
rust/serving/src/app.rs | 12 ++--
rust/serving/src/app/callback.rs | 10 ++--
rust/serving/src/app/callback/state.rs | 10 ++--
rust/serving/src/app/jetstream_proxy.rs | 10 ++--
rust/serving/src/app/message_path.rs | 4 +-
rust/serving/src/app/tracker.rs | 12 ++--
rust/serving/src/config.rs | 5 +-
rust/serving/src/lib.rs | 4 +-
rust/serving/src/pipeline.rs | 68 +++++++++++++++++-----
12 files changed, 92 insertions(+), 49 deletions(-)
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types.go b/pkg/apis/numaflow/v1alpha1/vertex_types.go
index b4a26f4815..5b97700944 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types.go
@@ -424,7 +424,7 @@ func (v Vertex) getServingContainer(req GetVertexPodSpecReq) (corev1.Container,
HTTPGet: &corev1.HTTPGetAction{
Path: "/readyz",
Port: intstr.FromInt32(VertexHTTPSPort),
- Scheme: corev1.URISchemeHTTP,
+ Scheme: corev1.URISchemeHTTPS,
},
},
InitialDelaySeconds: 3,
@@ -437,7 +437,7 @@ func (v Vertex) getServingContainer(req GetVertexPodSpecReq) (corev1.Container,
HTTPGet: &corev1.HTTPGetAction{
Path: "/livez",
Port: intstr.FromInt32(VertexHTTPSPort),
- Scheme: corev1.URISchemeHTTP,
+ Scheme: corev1.URISchemeHTTPS,
},
},
InitialDelaySeconds: 20,
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 13b368b4ab..c269c5e60d 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -2500,6 +2500,7 @@ dependencies = [
"chrono",
"config",
"hyper-util",
+ "numaflow-models",
"parking_lot",
"prometheus-client",
"rcgen",
diff --git a/rust/serving/Cargo.toml b/rust/serving/Cargo.toml
index 2dfb9b9c33..0af3c74e95 100644
--- a/rust/serving/Cargo.toml
+++ b/rust/serving/Cargo.toml
@@ -33,4 +33,5 @@ rcgen = "0.13.1"
parking_lot = "0.12.3"
prometheus-client = "0.22.3"
thiserror = "1.0.63"
+numaflow-models = { path = "../numaflow-models" }
diff --git a/rust/serving/src/app.rs b/rust/serving/src/app.rs
index 3043f3e34b..ad8403e022 100644
--- a/rust/serving/src/app.rs
+++ b/rust/serving/src/app.rs
@@ -26,7 +26,7 @@ use self::{
};
use crate::app::callback::store::Store;
use crate::app::tracker::MessageGraph;
-use crate::pipeline::pipeline_spec;
+use crate::pipeline::min_pipeline_spec;
use crate::Error::{InitError, MetricsServer};
use crate::{app::callback::state::State as CallbackState, config, metrics::capture_metrics};
@@ -89,7 +89,7 @@ pub(crate) async fn start_main_server(
.layer(middleware::from_fn(auth_middleware));
// Create the message graph from the pipeline spec and the redis store
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).map_err(|e| {
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).map_err(|e| {
InitError(format!(
"Creating message graph from pipeline spec: {:?}",
e
@@ -328,7 +328,7 @@ mod tests {
assert!(stream.is_ok());
let mem_store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let callback_state = CallbackState::new(msg_graph, mem_store).await.unwrap();
@@ -354,7 +354,7 @@ mod tests {
assert!(stream.is_ok());
let mem_store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let callback_state = CallbackState::new(msg_graph, mem_store).await.unwrap();
@@ -387,7 +387,7 @@ mod tests {
assert!(stream.is_ok());
let mem_store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let callback_state = CallbackState::new(msg_graph, mem_store).await.unwrap();
@@ -427,7 +427,7 @@ mod tests {
assert!(stream.is_ok());
let mem_store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let callback_state = CallbackState::new(msg_graph, mem_store).await.unwrap();
let app = Router::new()
diff --git a/rust/serving/src/app/callback.rs b/rust/serving/src/app/callback.rs
index 0734535a8c..6ecbc87ccc 100644
--- a/rust/serving/src/app/callback.rs
+++ b/rust/serving/src/app/callback.rs
@@ -75,14 +75,14 @@ mod tests {
use crate::app::callback::store::memstore::InMemoryStore;
use crate::app::tracker::MessageGraph;
- use crate::pipeline::pipeline_spec;
+ use crate::pipeline::min_pipeline_spec;
use super::*;
#[tokio::test]
async fn test_callback_failure() {
let store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let state = CallbackState::new(msg_graph, store).await.unwrap();
let app = callback_handler(state);
@@ -109,7 +109,7 @@ mod tests {
#[tokio::test]
async fn test_callback_success() {
let store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let mut state = CallbackState::new(msg_graph, store).await.unwrap();
let x = state.register("test_id".to_string());
@@ -160,7 +160,7 @@ mod tests {
#[tokio::test]
async fn test_callback_save() {
let store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let state = CallbackState::new(msg_graph, store).await.unwrap();
let app = callback_handler(state);
@@ -179,7 +179,7 @@ mod tests {
#[tokio::test]
async fn test_without_id_header() {
let store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let state = CallbackState::new(msg_graph, store).await.unwrap();
let app = callback_handler(state);
diff --git a/rust/serving/src/app/callback/state.rs b/rust/serving/src/app/callback/state.rs
index da971c86d0..aebf68d3a5 100644
--- a/rust/serving/src/app/callback/state.rs
+++ b/rust/serving/src/app/callback/state.rs
@@ -236,13 +236,13 @@ mod tests {
use axum::body::Bytes;
use crate::app::callback::store::memstore::InMemoryStore;
- use crate::pipeline::pipeline_spec;
+ use crate::pipeline::min_pipeline_spec;
use super::*;
#[tokio::test]
async fn test_state() {
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let store = InMemoryStore::new();
let mut state = State::new(msg_graph, store).await.unwrap();
@@ -302,7 +302,7 @@ mod tests {
#[tokio::test]
async fn test_retrieve_saved_no_entry() {
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let store = InMemoryStore::new();
let mut state = State::new(msg_graph, store).await.unwrap();
@@ -317,7 +317,7 @@ mod tests {
#[tokio::test]
async fn test_insert_callback_requests_invalid_id() {
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let store = InMemoryStore::new();
let mut state = State::new(msg_graph, store).await.unwrap();
@@ -338,7 +338,7 @@ mod tests {
#[tokio::test]
async fn test_retrieve_subgraph_from_storage_no_entry() {
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let store = InMemoryStore::new();
let mut state = State::new(msg_graph, store).await.unwrap();
diff --git a/rust/serving/src/app/jetstream_proxy.rs b/rust/serving/src/app/jetstream_proxy.rs
index 6a56266f72..dd80f40eda 100644
--- a/rust/serving/src/app/jetstream_proxy.rs
+++ b/rust/serving/src/app/jetstream_proxy.rs
@@ -57,7 +57,7 @@ pub(crate) async fn jetstream_proxy(
callback: callback_store,
stream: &config().jetstream.stream,
callback_url: format!(
- "http://{}:{}/v1/process/callback",
+ "https://{}:{}/v1/process/callback",
config().host_ip,
config().app_listen_port
),
@@ -277,7 +277,7 @@ mod tests {
use crate::app::callback::store::PayloadToSave;
use crate::app::callback::CallbackRequest;
use crate::app::tracker::MessageGraph;
- use crate::pipeline::pipeline_spec;
+ use crate::pipeline::min_pipeline_spec;
use crate::Error;
use super::*;
@@ -323,7 +323,7 @@ mod tests {
.map_err(|e| format!("creating stream {}: {}", &config().jetstream.url, e))?;
let mock_store = MockStore {};
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec())
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec())
.map_err(|e| format!("Failed to create message graph from pipeline spec: {:?}", e))?;
let callback_state = CallbackState::new(msg_graph, mock_store).await?;
@@ -401,7 +401,7 @@ mod tests {
.map_err(|e| format!("creating stream {}: {}", &config().jetstream.url, e));
let mem_store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let mut callback_state = CallbackState::new(msg_graph, mem_store).await.unwrap();
@@ -465,7 +465,7 @@ mod tests {
.map_err(|e| format!("creating stream {}: {}", &config().jetstream.url, e));
let mem_store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let mut callback_state = CallbackState::new(msg_graph, mem_store).await.unwrap();
diff --git a/rust/serving/src/app/message_path.rs b/rust/serving/src/app/message_path.rs
index 4b362b68ea..54139566f8 100644
--- a/rust/serving/src/app/message_path.rs
+++ b/rust/serving/src/app/message_path.rs
@@ -46,14 +46,14 @@ mod tests {
use crate::app::callback::store::memstore::InMemoryStore;
use crate::app::tracker::MessageGraph;
- use crate::pipeline::pipeline_spec;
+ use crate::pipeline::min_pipeline_spec;
use super::*;
#[tokio::test]
async fn test_message_path_not_present() {
let store = InMemoryStore::new();
- let msg_graph = MessageGraph::from_pipeline(pipeline_spec()).unwrap();
+ let msg_graph = MessageGraph::from_pipeline(min_pipeline_spec()).unwrap();
let state = CallbackState::new(msg_graph, store).await.unwrap();
let app = get_message_path(state);
diff --git a/rust/serving/src/app/tracker.rs b/rust/serving/src/app/tracker.rs
index cdd31a1c99..85d3c2b76d 100644
--- a/rust/serving/src/app/tracker.rs
+++ b/rust/serving/src/app/tracker.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::app::callback::CallbackRequest;
-use crate::pipeline::{Edge, OperatorType, Pipeline};
+use crate::pipeline::{Edge, PipelineDCG, OperatorType};
use crate::Error;
fn compare_slice(operator: &OperatorType, a: &[String], b: &[String]) -> bool {
@@ -225,7 +225,7 @@ impl MessageGraph {
}
// from_env reads the pipeline stored in the environment variable and creates a MessageGraph from it.
- pub(crate) fn from_pipeline(pipeline_spec: &Pipeline) -> Result {
+ pub(crate) fn from_pipeline(pipeline_spec: &PipelineDCG) -> Result {
let mut dag = Graph::with_capacity(pipeline_spec.edges.len());
for edge in &pipeline_spec.edges {
dag.entry(edge.from.clone()).or_default().push(edge.clone());
@@ -369,7 +369,7 @@ mod tests {
#[test]
fn test_generate_subgraph_complex() {
- let pipeline = Pipeline {
+ let pipeline = PipelineDCG {
vertices: vec![
Vertex {
name: "a".to_string(),
@@ -561,7 +561,7 @@ mod tests {
#[test]
fn test_simple_dropped_message() {
- let pipeline = Pipeline {
+ let pipeline = PipelineDCG {
vertices: vec![
Vertex {
name: "a".to_string(),
@@ -636,7 +636,7 @@ mod tests {
#[test]
fn test_complex_dropped_message() {
- let pipeline = Pipeline {
+ let pipeline = PipelineDCG {
vertices: vec![
Vertex {
name: "a".to_string(),
@@ -805,7 +805,7 @@ mod tests {
#[test]
fn test_simple_cycle_pipeline() {
- let pipeline = Pipeline {
+ let pipeline = PipelineDCG {
vertices: vec![
Vertex {
name: "a".to_string(),
diff --git a/rust/serving/src/config.rs b/rust/serving/src/config.rs
index ab40485876..eb3b9ac361 100644
--- a/rust/serving/src/config.rs
+++ b/rust/serving/src/config.rs
@@ -1,3 +1,5 @@
+use crate::Error::ParseConfig;
+use crate::{Error, Result};
use async_nats::rustls;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
@@ -9,9 +11,6 @@ use std::path::Path;
use std::{env, sync::OnceLock};
use tracing::info;
-use crate::Error::ParseConfig;
-use crate::{Error, Result};
-
const ENV_PREFIX: &str = "NUMAFLOW_SERVING";
const ENV_NUMAFLOW_SERVING_SOURCE_OBJECT: &str = "NUMAFLOW_SERVING_SOURCE_OBJECT";
const ENV_NUMAFLOW_SERVING_JETSTREAM_URL: &str = "NUMAFLOW_ISBSVC_JETSTREAM_URL";
diff --git a/rust/serving/src/lib.rs b/rust/serving/src/lib.rs
index 72de843d8d..86265a5ed0 100644
--- a/rust/serving/src/lib.rs
+++ b/rust/serving/src/lib.rs
@@ -2,7 +2,7 @@ pub use self::error::{Error, Result};
use crate::app::start_main_server;
use crate::config::{cert_key_pair, config};
use crate::metrics::start_https_metrics_server;
-use crate::pipeline::pipeline_spec;
+use crate::pipeline::min_pipeline_spec;
use axum_server::tls_rustls::RustlsConfig;
use std::net::SocketAddr;
use tracing::info;
@@ -33,7 +33,7 @@ pub async fn serve() -> std::result::Result<(), Box &'static Pipeline {
- static PIPELINE: OnceLock = OnceLock::new();
- PIPELINE.get_or_init(|| match Pipeline::load() {
+pub fn min_pipeline_spec() -> &'static PipelineDCG {
+ static PIPELINE: OnceLock = OnceLock::new();
+ PIPELINE.get_or_init(|| match PipelineDCG::load() {
Ok(pipeline) => pipeline,
- Err(e) => panic!("Failed to load pipeline: {:?}", e),
+ Err(e) => panic!("Failed to load minimum pipeline spec: {:?}", e),
})
}
@@ -39,6 +40,17 @@ impl OperatorType {
}
}
+impl From for OperatorType {
+ fn from(s: String) -> Self {
+ match s.as_str() {
+ "and" => OperatorType::And,
+ "or" => OperatorType::Or,
+ "not" => OperatorType::Not,
+ _ => panic!("Invalid operator type: {}", s),
+ }
+ }
+}
+
// Tag is a struct that contains the information about the tags for the edge
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tag {
@@ -60,10 +72,11 @@ pub struct Edge {
pub conditions: Option,
}
-// Pipeline is a struct that contains the information about the pipeline.
+/// DCG (directed compute graph) of the pipeline with minimal information build using vertices and edges
+/// from the pipeline spec
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde()]
-pub struct Pipeline {
+pub struct PipelineDCG {
pub vertices: Vec,
pub edges: Vec,
}
@@ -73,16 +86,16 @@ pub struct Vertex {
pub name: String,
}
-impl Pipeline {
+impl PipelineDCG {
pub fn load() -> crate::Result {
- let pipeline = match env::var(ENV_MIN_PIPELINE_SPEC) {
+ let full_pipeline_spec = match env::var(ENV_MIN_PIPELINE_SPEC) {
Ok(env_value) => {
// If the environment variable is set, decode and parse the pipeline
let decoded = BASE64_STANDARD
.decode(env_value.as_bytes())
.map_err(|e| ParseConfig(format!("decoding pipeline from env: {e:?}")))?;
- serde_json::from_slice::(&decoded)
+ serde_json::from_slice::(&decoded)
.map_err(|e| ParseConfig(format!("parsing pipeline from env: {e:?}")))?
}
Err(_) => {
@@ -90,11 +103,41 @@ impl Pipeline {
let file_path = "./config/pipeline_spec.json";
let file_contents = std::fs::read_to_string(file_path)
.map_err(|e| ParseConfig(format!("reading pipeline file: {e:?}")))?;
- serde_json::from_str::(&file_contents)
+ serde_json::from_str::(&file_contents)
.map_err(|e| ParseConfig(format!("parsing pipeline file: {e:?}")))?
}
};
- Ok(pipeline)
+
+ let vertices: Vec = full_pipeline_spec
+ .vertices
+ .ok_or(ParseConfig("missing vertices in pipeline spec".to_string()))?
+ .iter()
+ .map(|v| Vertex {
+ name: v.name.clone(),
+ })
+ .collect();
+
+ let edges: Vec = full_pipeline_spec
+ .edges
+ .ok_or(ParseConfig("missing edges in pipeline spec".to_string()))?
+ .iter()
+ .map(|e| {
+ let conditions = e.conditions.clone().map(|c| Conditions {
+ tags: Some(Tag {
+ operator: c.tags.operator.map(|o| o.into()),
+ values: c.tags.values.clone(),
+ }),
+ });
+
+ Edge {
+ from: e.from.clone(),
+ to: e.to.clone(),
+ conditions,
+ }
+ })
+ .collect();
+
+ Ok(PipelineDCG { vertices, edges })
}
}
@@ -104,8 +147,7 @@ mod tests {
#[test]
fn test_pipeline_load() {
- let pipeline = pipeline_spec();
-
+ let pipeline = min_pipeline_spec();
assert_eq!(pipeline.vertices.len(), 3);
assert_eq!(pipeline.edges.len(), 2);
assert_eq!(pipeline.vertices[0].name, "in");
From ae02243b3f30de8da407b148bbac7cb2e48a68c4 Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Fri, 23 Aug 2024 19:14:20 -0400
Subject: [PATCH 026/188] fix: e2e testing isbsvc deletion timeout issue
(#1997)
Signed-off-by: Keran Yang
---
pkg/apis/numaflow/v1alpha1/const.go | 17 +++++----
pkg/apis/numaflow/v1alpha1/pipeline_types.go | 2 +-
pkg/reconciler/isbsvc/installer/jetstream.go | 2 +-
test/fixtures/e2e_suite.go | 24 ++++++++----
test/fixtures/expect.go | 2 +-
test/fixtures/given.go | 6 +--
test/fixtures/when.go | 40 +++++++++++++++++++-
7 files changed, 70 insertions(+), 23 deletions(-)
diff --git a/pkg/apis/numaflow/v1alpha1/const.go b/pkg/apis/numaflow/v1alpha1/const.go
index 177e6f73a1..8677101378 100644
--- a/pkg/apis/numaflow/v1alpha1/const.go
+++ b/pkg/apis/numaflow/v1alpha1/const.go
@@ -87,14 +87,15 @@ const (
ServingSourceContainer = "serving"
// components
- ComponentISBSvc = "isbsvc"
- ComponentDaemon = "daemon"
- ComponentVertex = "vertex"
- ComponentMonoVertex = "mono-vertex"
- ComponentMonoVertexDaemon = "mono-vertex-daemon"
- ComponentJob = "job"
- ComponentSideInputManager = "side-inputs-manager"
- ComponentUXServer = "numaflow-ux"
+ ComponentISBSvc = "isbsvc"
+ ComponentDaemon = "daemon"
+ ComponentVertex = "vertex"
+ ComponentMonoVertex = "mono-vertex"
+ ComponentMonoVertexDaemon = "mono-vertex-daemon"
+ ComponentJob = "job"
+ ComponentSideInputManager = "side-inputs-manager"
+ ComponentUXServer = "numaflow-ux"
+ ComponentControllerManager = "controller-manager"
// controllers
ControllerISBSvc = "isbsvc-controller"
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types.go b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
index 010b53bf20..07d62f673d 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
@@ -690,7 +690,7 @@ func (pls *PipelineStatus) MarkDeployFailed(reason, message string) {
pls.SetPhase(PipelinePhaseFailed, message)
}
-// MarkVerticesHealthy set the daemon service of the pipeline is healthy.
+// MarkDaemonServiceHealthy set the daemon service of the pipeline is healthy.
func (pls *PipelineStatus) MarkDaemonServiceHealthy() {
pls.MarkTrue(PipelineConditionDaemonServiceHealthy)
}
diff --git a/pkg/reconciler/isbsvc/installer/jetstream.go b/pkg/reconciler/isbsvc/installer/jetstream.go
index d887f79e38..f0faa09caa 100644
--- a/pkg/reconciler/isbsvc/installer/jetstream.go
+++ b/pkg/reconciler/isbsvc/installer/jetstream.go
@@ -411,7 +411,7 @@ func (r *jetStreamInstaller) createConfigMap(ctx context.Context) error {
svcName := generateJetStreamServiceName(r.isbSvc)
ssName := generateJetStreamStatefulSetName(r.isbSvc)
replicas := r.isbSvc.Spec.JetStream.GetReplicas()
- routes := []string{}
+ var routes []string
for j := 0; j < replicas; j++ {
routes = append(routes, fmt.Sprintf("nats://%s-%s.%s.%s.svc:%s", ssName, strconv.Itoa(j), svcName, r.isbSvc.Namespace, strconv.Itoa(int(clusterPort))))
}
diff --git a/test/fixtures/e2e_suite.go b/test/fixtures/e2e_suite.go
index 64b27e3eb9..be1255a037 100644
--- a/test/fixtures/e2e_suite.go
+++ b/test/fixtures/e2e_suite.go
@@ -38,10 +38,13 @@ import (
)
const (
- Namespace = "numaflow-system"
- Label = "numaflow-e2e"
- LabelValue = "true"
- ISBSvcName = "numaflow-e2e"
+ Namespace = "numaflow-system"
+ Label = "numaflow-e2e"
+ LabelValue = "true"
+ ISBSvcName = "numaflow-e2e"
+ // the number 90 is carefully chosen to ensure the test suite can finish within a reasonable time without timing out.
+ // please exercise caution when updating this value, as it may cause e2e tests to be flaky.
+ // if updated, consider running the entire e2e test suite multiple times to ensure stability.
defaultTimeout = 90 * time.Second
LogSourceVertexStarted = "Start processing source messages"
@@ -139,13 +142,18 @@ func (s *E2ESuite) TearDownSuite() {
When().
Wait(5 * time.Second).
DeleteISBSvc().
- Wait(3 * time.Second).
+ Wait(3 * time.Second)
+ // force deleting the ISB svc pods because we have seen pods stuck in terminating state after CRD deletion,
+ // which causes e2e tests to timeout, this is a workaround to avoid the issue.
+ deleteISBPodsCMD := fmt.Sprintf("kubectl delete pods -n %s -l %s=%s,%s=%s --ignore-not-found=true --grace-period=0 --force", Namespace, dfv1.KeyComponent, dfv1.ComponentISBSvc, dfv1.KeyISBSvcName, ISBSvcName)
+ s.Given().When().Exec("sh", []string{"-c", deleteISBPodsCMD}, OutputRegexp(""))
+ s.Given().ISBSvc(getISBSvcSpec()).
+ When().
Expect().
ISBSvcDeleted(defaultTimeout)
-
s.T().Log("ISB svc is deleted")
- deleteCMD := fmt.Sprintf("kubectl delete -k ../../config/apps/redis -n %s --ignore-not-found=true", Namespace)
- s.Given().When().Exec("sh", []string{"-c", deleteCMD}, OutputRegexp(`service "redis" deleted`))
+ deleteRedisCMD := fmt.Sprintf("kubectl delete -k ../../config/apps/redis -n %s --ignore-not-found=true", Namespace)
+ s.Given().When().Exec("sh", []string{"-c", deleteRedisCMD}, OutputRegexp(`service "redis" deleted`))
s.T().Log("Redis resources are deleted")
close(s.stopch)
}
diff --git a/test/fixtures/expect.go b/test/fixtures/expect.go
index 2e550653fe..afa30447d9 100644
--- a/test/fixtures/expect.go
+++ b/test/fixtures/expect.go
@@ -76,7 +76,7 @@ func (t *Expect) ISBSvcDeleted(timeout time.Duration) *Expect {
t.t.Fatalf("Expected ISB svc to be deleted: %v", err)
}
- labelSelector := fmt.Sprintf("%s=isbsvc-controller,%s=%s", dfv1.KeyManagedBy, dfv1.KeyISBSvcName, ISBSvcName)
+ labelSelector := fmt.Sprintf("%s=%s,%s=%s", dfv1.KeyManagedBy, dfv1.ControllerISBSvc, dfv1.KeyISBSvcName, ISBSvcName)
opts := metav1.ListOptions{LabelSelector: labelSelector}
timeoutCh := make(chan bool, 1)
go func() {
diff --git a/test/fixtures/given.go b/test/fixtures/given.go
index 30a4ab01a7..e6f2d0dbef 100644
--- a/test/fixtures/given.go
+++ b/test/fixtures/given.go
@@ -43,7 +43,7 @@ type Given struct {
kubeClient kubernetes.Interface
}
-// creates an ISBSvc based on the parameter, this may be:
+// ISBSvc creates an ISBSvc based on the parameter, this may be:
//
// 1. A file name if it starts with "@"
// 2. Raw YAML.
@@ -61,7 +61,7 @@ func (g *Given) ISBSvc(text string) *Given {
return g
}
-// creates a Pipeline based on the parameter, this may be:
+// Pipeline creates a Pipeline based on the parameter, this may be:
//
// 1. A file name if it starts with "@"
// 2. Raw YAML.
@@ -79,7 +79,7 @@ func (g *Given) Pipeline(text string) *Given {
return g
}
-// / creates a MonoVertex based on the parameter, this may be:
+// MonoVertex creates a MonoVertex based on the parameter, this may be:
//
// 1. A file name if it starts with "@"
// 2. Raw YAML.
diff --git a/test/fixtures/when.go b/test/fixtures/when.go
index 7ab85ae772..986085bdd6 100644
--- a/test/fixtures/when.go
+++ b/test/fixtures/when.go
@@ -276,7 +276,7 @@ func (w *When) TerminateAllPodPortForwards() *When {
return w
}
-func (w *When) StreamVertexPodlogs(vertexName, containerName string) *When {
+func (w *When) StreamVertexPodLogs(vertexName, containerName string) *When {
w.t.Helper()
ctx := context.Background()
labelSelector := fmt.Sprintf("%s=%s,%s=%s", dfv1.KeyPipelineName, w.pipeline.Name, dfv1.KeyVertexName, vertexName)
@@ -295,6 +295,44 @@ func (w *When) StreamVertexPodlogs(vertexName, containerName string) *When {
return w
}
+func (w *When) StreamISBLogs() *When {
+ w.t.Helper()
+ ctx := context.Background()
+ labelSelector := fmt.Sprintf("%s=%s,%s=%s", dfv1.KeyComponent, dfv1.ComponentISBSvc, dfv1.KeyManagedBy, dfv1.ControllerISBSvc)
+ podList, err := w.kubeClient.CoreV1().Pods(Namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"})
+ if err != nil {
+ w.t.Fatalf("Error getting ISB service pods: %v", err)
+ }
+ for _, pod := range podList.Items {
+ stopCh := make(chan struct{}, 1)
+ streamPodLogs(ctx, w.kubeClient, Namespace, pod.Name, "main", stopCh)
+ if w.streamLogsStopChannels == nil {
+ w.streamLogsStopChannels = make(map[string]chan struct{})
+ }
+ w.streamLogsStopChannels[pod.Name+":main"] = stopCh
+ }
+ return w
+}
+
+func (w *When) StreamControllerLogs() *When {
+ w.t.Helper()
+ ctx := context.Background()
+ labelSelector := fmt.Sprintf("%s=%s", dfv1.KeyComponent, dfv1.ComponentControllerManager)
+ podList, err := w.kubeClient.CoreV1().Pods(Namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"})
+ if err != nil {
+ w.t.Fatalf("Error getting the controller pods: %v", err)
+ }
+ for _, pod := range podList.Items {
+ stopCh := make(chan struct{}, 1)
+ streamPodLogs(ctx, w.kubeClient, Namespace, pod.Name, "controller-manager", stopCh)
+ if w.streamLogsStopChannels == nil {
+ w.streamLogsStopChannels = make(map[string]chan struct{})
+ }
+ w.streamLogsStopChannels[pod.Name+":controller-manager"] = stopCh
+ }
+ return w
+}
+
func (w *When) TerminateAllPodLogs() *When {
w.t.Helper()
if len(w.streamLogsStopChannels) > 0 {
From 86c381f7efd16cbd29277e518668619815b2c2d8 Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Sat, 24 Aug 2024 10:34:51 +0530
Subject: [PATCH 027/188] chore: fix servesink (#1999)
Signed-off-by: Yashash H L
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
rust/monovertex/src/forwarder.rs | 7 +-
rust/monovertex/src/source.rs | 4 +-
rust/servesink/src/lib.rs | 200 +++++++++++++++++++++++++++----
3 files changed, 178 insertions(+), 33 deletions(-)
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index 7be4824b6e..d9f68fc608 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -168,13 +168,10 @@ impl Forwarder {
// Applies transformation to the messages if transformer is present
// we concurrently apply transformation to all the messages.
async fn apply_transformer(&self, messages: Vec) -> Result> {
- let transformer_client;
- if let Some(trf_client) = &self.transformer_client {
- transformer_client = trf_client;
- } else {
+ let Some(transformer_client) = &self.transformer_client else {
// return early if there is no transformer
return Ok(messages);
- }
+ };
let start_time = tokio::time::Instant::now();
let mut jh = JoinSet::new();
diff --git a/rust/monovertex/src/source.rs b/rust/monovertex/src/source.rs
index a58922d5c7..681b0beb58 100644
--- a/rust/monovertex/src/source.rs
+++ b/rust/monovertex/src/source.rs
@@ -104,8 +104,6 @@ impl SourceClient {
Ok(self.client.ack_fn(request).await?.into_inner())
}
- #[allow(dead_code)]
- // TODO: remove dead_code
pub(crate) async fn pending_fn(&mut self) -> Result {
let request = Request::new(());
let response = self
@@ -114,7 +112,7 @@ impl SourceClient {
.await?
.into_inner()
.result
- .map_or(0, |r| r.count);
+ .map_or(-1, |r| r.count); // default to -1(unavailable)
Ok(response)
}
diff --git a/rust/servesink/src/lib.rs b/rust/servesink/src/lib.rs
index 5663b61b07..b54ed8b580 100644
--- a/rust/servesink/src/lib.rs
+++ b/rust/servesink/src/lib.rs
@@ -5,6 +5,10 @@ use reqwest::Client;
use tracing::{error, warn};
use tracing_subscriber::prelude::*;
+const NUMAFLOW_CALLBACK_URL_HEADER: &str = "X-Numaflow-Callback-Url";
+const NUMAFLOW_ID_HEADER: &str = "X-Numaflow-Id";
+
+/// servesink is a Numaflow Sink which forwards the payload to the Numaflow serving URL.
pub async fn servesink() -> Result<(), Box> {
tracing_subscriber::registry()
.with(
@@ -14,14 +18,14 @@ pub async fn servesink() -> Result<(), Box> {
.with(tracing_subscriber::fmt::layer().with_ansi(false))
.init();
- sink::Server::new(Logger::new()).start().await
+ sink::Server::new(ServeSink::new()).start().await
}
-struct Logger {
+struct ServeSink {
client: Client,
}
-impl Logger {
+impl ServeSink {
fn new() -> Self {
Self {
client: Client::new(),
@@ -30,43 +34,189 @@ impl Logger {
}
#[tonic::async_trait]
-impl sink::Sinker for Logger {
+impl sink::Sinker for ServeSink {
async fn sink(&self, mut input: tokio::sync::mpsc::Receiver) -> Vec {
let mut responses: Vec = Vec::new();
while let Some(datum) = input.recv().await {
- // do something better, but for now let's just log it.
- // please note that `from_utf8` is working because the input in this
- // example uses utf-8 data.
- let response = match std::str::from_utf8(&datum.value) {
- Ok(_v) => {
- // record the response
- Response::ok(datum.id)
+ // if the callback url is absent, ignore the request
+ let url = match datum.headers.get(NUMAFLOW_CALLBACK_URL_HEADER) {
+ Some(url) => url,
+ None => {
+ warn!(
+ "Missing {} header, Ignoring the request",
+ NUMAFLOW_CALLBACK_URL_HEADER
+ );
+ responses.push(Response::ok(datum.id));
+ continue;
}
- Err(e) => Response::failure(datum.id, format!("Invalid UTF-8 sequence: {}", e)),
- };
- // return the responses
- responses.push(response);
- let Some(url) = datum.headers.get("X-Numaflow-Callback-Url") else {
- warn!("X-Numaflow-Callback-Url header is not found in the payload");
- continue;
};
- let Some(numaflow_id) = datum.headers.get("X-Numaflow-Id") else {
- warn!("X-Numaflow-Id header is not found in the payload");
- continue;
+
+ // if the numaflow id is absent, ignore the request
+ let numaflow_id = match datum.headers.get(NUMAFLOW_ID_HEADER) {
+ Some(id) => id,
+ None => {
+ warn!(
+ "Missing {} header, Ignoring the request",
+ NUMAFLOW_ID_HEADER
+ );
+ responses.push(Response::ok(datum.id));
+ continue;
+ }
};
+
let resp = self
.client
.post(format!("{}_{}", url, "save"))
- .header("X-Numaflow-Id", numaflow_id)
+ .header(NUMAFLOW_ID_HEADER, numaflow_id)
.header("id", numaflow_id)
.body(datum.value)
.send()
.await;
- if let Err(e) = resp {
- error!(error=?e, url=url, "Sending result to numaserve")
- }
+
+ let response = match resp {
+ Ok(_) => Response::ok(datum.id),
+ Err(e) => {
+ error!("Sending result to serving URL {:?}", e);
+ Response::failure(datum.id, format!("Failed to send: {}", e))
+ }
+ };
+
+ responses.push(response);
}
responses
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use numaflow::sink::{SinkRequest, Sinker};
+ use std::collections::HashMap;
+ use tokio::io::{AsyncReadExt, AsyncWriteExt};
+ use tokio::net::TcpListener;
+ use tokio::sync::mpsc;
+
+ #[tokio::test]
+ async fn test_serve_sink_without_url_header() {
+ let serve_sink = ServeSink::new();
+ let (tx, rx) = mpsc::channel(1);
+
+ let mut headers = HashMap::new();
+ headers.insert(NUMAFLOW_ID_HEADER.to_string(), "12345".to_string());
+
+ let request = SinkRequest {
+ keys: vec![],
+ id: "1".to_string(),
+ value: b"test".to_vec(),
+ watermark: Default::default(),
+ headers,
+ event_time: Default::default(),
+ };
+
+ tx.send(request).await.unwrap();
+ drop(tx); // Close the sender to end the stream
+
+ let responses = serve_sink.sink(rx).await;
+ assert_eq!(responses.len(), 1);
+ assert!(responses[0].success);
+ }
+
+ #[tokio::test]
+ async fn test_serve_sink_without_id_header() {
+ let serve_sink = ServeSink::new();
+ let (tx, rx) = mpsc::channel(1);
+
+ let mut headers = HashMap::new();
+ headers.insert(
+ NUMAFLOW_CALLBACK_URL_HEADER.to_string(),
+ "http://localhost:8080".to_string(),
+ );
+
+ let request = SinkRequest {
+ keys: vec![],
+ id: "1".to_string(),
+ value: b"test".to_vec(),
+ watermark: Default::default(),
+ headers,
+ event_time: Default::default(),
+ };
+
+ tx.send(request).await.unwrap();
+ drop(tx); // Close the sender to end the stream
+
+ let responses = serve_sink.sink(rx).await;
+ assert_eq!(responses.len(), 1);
+ assert!(responses[0].success);
+ }
+
+ async fn start_server() -> (String, mpsc::Sender<()>) {
+ let (shutdown_tx, mut shutdown_rx) = mpsc::channel(1);
+ let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
+ let addr = listener.local_addr().unwrap();
+ let addr_str = format!("{}", addr);
+ tokio::spawn(async move {
+ loop {
+ tokio::select! {
+ _ = shutdown_rx.recv() => {
+ break;
+ }
+ Ok((mut socket, _)) = listener.accept() => {
+ tokio::spawn(async move {
+ let mut buffer = [0; 1024];
+ let _ = socket.read(&mut buffer).await.unwrap();
+ let request = String::from_utf8_lossy(&buffer[..]);
+ let response = if request.contains("/error") {
+ "HTTP/1.1 500 INTERNAL SERVER ERROR\r\n\
+ content-length: 0\r\n\
+ \r\n"
+ } else {
+ "HTTP/1.1 200 OK\r\n\
+ content-length: 0\r\n\
+ \r\n"
+ };
+ socket.write_all(response.as_bytes()).await.unwrap();
+ });
+ }
+ }
+ }
+ });
+ (addr_str, shutdown_tx)
+ }
+
+ #[tokio::test]
+ async fn test_serve_sink() {
+ let serve_sink = ServeSink::new();
+
+ let (addr, shutdown_tx) = start_server().await;
+
+ let (tx, rx) = mpsc::channel(1);
+
+ let mut headers = HashMap::new();
+ headers.insert(NUMAFLOW_ID_HEADER.to_string(), "12345".to_string());
+
+ headers.insert(
+ NUMAFLOW_CALLBACK_URL_HEADER.to_string(),
+ format!("http://{}/sync", addr),
+ );
+
+ let request = SinkRequest {
+ keys: vec![],
+ id: "1".to_string(),
+ value: b"test".to_vec(),
+ watermark: Default::default(),
+ headers,
+ event_time: Default::default(),
+ };
+
+ tx.send(request).await.unwrap();
+ drop(tx); // Close the sender to end the stream
+
+ let responses = serve_sink.sink(rx).await;
+ assert_eq!(responses.len(), 1);
+ assert!(responses[0].success);
+
+ // Stop the server
+ shutdown_tx.send(()).await.unwrap();
+ }
+}
From 1f2f482a0f50a2527630e00262f473f1483cefce Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Fri, 23 Aug 2024 23:51:41 -0700
Subject: [PATCH 028/188] chore: disallow MonoVertex replicas less than min or
greater than max (#1994)
---
pkg/reconciler/monovertex/controller.go | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index b345369c66..3bec61394e 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -110,7 +110,9 @@ func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.Mon
}()
monoVtx.Status.SetObservedGeneration(monoVtx.Generation)
- mr.scaler.StartWatching(mVtxKey)
+ if monoVtx.Scalable() {
+ mr.scaler.StartWatching(mVtxKey)
+ }
// TODO: handle lifecycle changes
// Regular mono vertex change
@@ -155,6 +157,16 @@ func (mr *monoVertexReconciler) reconcileNonLifecycleChanges(ctx context.Context
func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1.MonoVertex) error {
desiredReplicas := monoVtx.GetReplicas()
+ // Don't allow replicas to be out of the range of min and max when auto scaling is enabled
+ if s := monoVtx.Spec.Scale; !s.Disabled {
+ max := int(s.GetMaxReplicas())
+ min := int(s.GetMinReplicas())
+ if desiredReplicas < min {
+ desiredReplicas = min
+ } else if desiredReplicas > max {
+ desiredReplicas = max
+ }
+ }
// Set metrics
defer func() {
reconciler.MonoVertexDesiredReplicas.WithLabelValues(monoVtx.Namespace, monoVtx.Name).Set(float64(desiredReplicas))
From 11aa5b65977736dae34ad3cf029cb55751bd41c3 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Sun, 25 Aug 2024 09:57:33 -0700
Subject: [PATCH 029/188] test: avoid building image twice in e2e testing ci
(#2001)
Signed-off-by: Derek Wang
---
.github/workflows/ci.yaml | 2 +-
Makefile | 6 ++++-
config/apps/kafka/kustomization.yaml | 7 ++---
config/apps/nats/kustomization.yaml | 7 ++---
config/apps/redis/kustomization.yaml | 7 ++---
.../examples/transformer/kustomization.yaml | 26 ++++++++++---------
test/manifests/kustomization.yaml | 6 +++--
7 files changed, 36 insertions(+), 25 deletions(-)
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 02dd5feeb5..9eaa4de89b 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -243,4 +243,4 @@ jobs:
- name: Run tests
env:
GOPATH: /home/runner/go
- run: KUBECONFIG=~/.kube/numaflow-e2e-config VERSION=${{ github.sha }} ISBSVC=${{matrix.driver}} make test-${{matrix.case}}
+ run: KUBECONFIG=~/.kube/numaflow-e2e-config VERSION=${{ github.sha }} ISBSVC=${{matrix.driver}} SKIP_IMAGE_BUILD=true make test-${{matrix.case}}
diff --git a/Makefile b/Makefile
index d31c9095e6..a4bc2012bc 100644
--- a/Makefile
+++ b/Makefile
@@ -127,7 +127,11 @@ test-idle-source-e2e:
test-builtin-source-e2e:
test-%:
$(MAKE) cleanup-e2e
- $(MAKE) image e2eapi-image
+ifndef SKIP_IMAGE_BUILD
+ # Skip building image in CI since the image would have been built during "make start"
+ $(MAKE) image
+endif
+ $(MAKE) e2eapi-image
$(MAKE) restart-control-plane-components
cat test/manifests/e2e-api-pod.yaml | sed 's@quay.io/numaproj/@$(IMAGE_NAMESPACE)/@' | sed 's/:latest/:$(VERSION)/' | kubectl -n numaflow-system apply -f -
go generate $(shell find ./test/$* -name '*.go')
diff --git a/config/apps/kafka/kustomization.yaml b/config/apps/kafka/kustomization.yaml
index b59416a95f..965156ee4a 100644
--- a/config/apps/kafka/kustomization.yaml
+++ b/config/apps/kafka/kustomization.yaml
@@ -4,6 +4,7 @@ kind: Kustomization
resources:
- kafka-minimal.yaml
-commonLabels:
- "numaflow-e2e": "true"
-
+labels:
+ - includeSelectors: true
+ pairs:
+ numaflow-e2e: "true"
diff --git a/config/apps/nats/kustomization.yaml b/config/apps/nats/kustomization.yaml
index 9d42b1a927..374d50a960 100644
--- a/config/apps/nats/kustomization.yaml
+++ b/config/apps/nats/kustomization.yaml
@@ -5,6 +5,7 @@ resources:
- nats.yaml
- nats-auth-fake-token.yaml
-commonLabels:
- "numaflow-e2e": "true"
-
+labels:
+ - includeSelectors: true
+ pairs:
+ numaflow-e2e: "true"
diff --git a/config/apps/redis/kustomization.yaml b/config/apps/redis/kustomization.yaml
index 488495a419..453d88b4b7 100644
--- a/config/apps/redis/kustomization.yaml
+++ b/config/apps/redis/kustomization.yaml
@@ -5,6 +5,7 @@ kind: Kustomization
resources:
- redis-minimal.yaml
-commonLabels:
- "numaflow-e2e": "true"
-
+labels:
+ - includeSelectors: true
+ pairs:
+ numaflow-e2e: "true"
diff --git a/docs/user-guide/reference/kustomize/examples/transformer/kustomization.yaml b/docs/user-guide/reference/kustomize/examples/transformer/kustomization.yaml
index afa859a44b..92d6bd308d 100644
--- a/docs/user-guide/reference/kustomize/examples/transformer/kustomization.yaml
+++ b/docs/user-guide/reference/kustomize/examples/transformer/kustomization.yaml
@@ -2,29 +2,31 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configurations:
-- https://raw.githubusercontent.com/numaproj/numaflow/main/docs/user-guide/kustomize/numaflow-transformer-config.yaml
+ - https://raw.githubusercontent.com/numaproj/numaflow/main/docs/user-guide/kustomize/numaflow-transformer-config.yaml
namePrefix: my-
resources:
-- my-pipeline.yaml
+ - my-pipeline.yaml
configMapGenerator:
-- name: my-cm
- literals:
- - FOO=BAR
+ - literals:
+ - FOO=BAR
+ name: my-cm
secretGenerator:
-- name: my-secret
- literals:
- - password=Pa5SW0rD
+ - literals:
+ - password=Pa5SW0rD
+ name: my-secret
-commonLabels:
- foo: bar
+labels:
+ - includeSelectors: true
+ pairs:
+ foo: bar
commonAnnotations:
foo: bar
images:
-- name: my-pipeline/my-udf
- newTag: my-version
+ - name: my-pipeline/my-udf
+ newTag: my-version
diff --git a/test/manifests/kustomization.yaml b/test/manifests/kustomization.yaml
index 87a4407f00..86071ab881 100644
--- a/test/manifests/kustomization.yaml
+++ b/test/manifests/kustomization.yaml
@@ -60,5 +60,7 @@ patches:
namespace: numaflow-system
-commonLabels:
- "app.kubernetes.io/part-of": "numaflow"
+labels:
+ - includeSelectors: true
+ pairs:
+ app.kubernetes.io/part-of: numaflow
From 102d1de1230a5a9baf29128757b12e6af4413bf3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 25 Aug 2024 15:33:17 -0400
Subject: [PATCH 030/188] chore(deps): bump micromatch from 4.0.7 to 4.0.8 in
/ui (#2002)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
ui/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/ui/yarn.lock b/ui/yarn.lock
index c6b1eb6c88..eda56fac2e 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -8091,9 +8091,9 @@ methods@~1.1.2:
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
- version "4.0.7"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5"
- integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+ integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
braces "^3.0.3"
picomatch "^2.3.1"
From 91f372ca9ea413041ad157746530481d78114fcf Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Mon, 26 Aug 2024 11:23:22 -0700
Subject: [PATCH 031/188] feat: more flexible scaling with `replicasPerScaleUp`
and `replicasPerScaleDown` (#2003)
Signed-off-by: Derek Wang
---
api/json-schema/schema.json | 17 +-
api/openapi-spec/swagger.json | 17 +-
.../numaflow.numaproj.io_monovertices.yaml | 9 +-
.../full/numaflow.numaproj.io_pipelines.yaml | 9 +-
.../full/numaflow.numaproj.io_vertices.yaml | 9 +-
config/install.yaml | 27 +-
config/namespace-install.yaml | 27 +-
docs/APIs.md | 68 +-
docs/user-guide/reference/autoscaling.md | 8 +-
pkg/apis/numaflow/v1alpha1/generated.pb.go | 1026 +++++++++--------
pkg/apis/numaflow/v1alpha1/generated.proto | 32 +-
.../numaflow/v1alpha1/openapi_generated.go | 23 +-
pkg/apis/numaflow/v1alpha1/scale.go | 54 +-
pkg/apis/numaflow/v1alpha1/scale_test.go | 12 +-
.../v1alpha1/zz_generated.deepcopy.go | 19 +-
pkg/reconciler/monovertex/scaling/scaling.go | 11 +-
pkg/reconciler/vertex/scaling/scaling.go | 13 +-
rust/numaflow-models/src/models/scale.rs | 17 +-
18 files changed, 768 insertions(+), 630 deletions(-)
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index 9a119f6a1d..e3601b73a4 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19734,11 +19734,6 @@
"io.numaproj.numaflow.v1alpha1.Scale": {
"description": "Scale defines the parameters for autoscaling.",
"properties": {
- "cooldownSeconds": {
- "description": "Deprecated: Use scaleUpCooldownSeconds and scaleDownCooldownSeconds instead. Cooldown seconds after a scaling operation before another one.",
- "format": "int64",
- "type": "integer"
- },
"disabled": {
"description": "Whether to disable autoscaling. Set to \"true\" when using Kubernetes HPA or any other 3rd party autoscaling strategies.",
"type": "boolean"
@@ -19759,7 +19754,17 @@
"type": "integer"
},
"replicasPerScale": {
- "description": "ReplicasPerScale defines maximum replicas can be scaled up or down at once. The is use to prevent too aggressive scaling operations",
+ "description": "DeprecatedReplicasPerScale defines the number of maximum replicas that can be changed in a single scale up or down operation. The is use to prevent from too aggressive scaling operations Deprecated: Use ReplicasPerScaleUp and ReplicasPerScaleDown instead",
+ "format": "int64",
+ "type": "integer"
+ },
+ "replicasPerScaleDown": {
+ "description": "ReplicasPerScaleDown defines the number of maximum replicas that can be changed in a single scaled down operation. The is use to prevent from too aggressive scaling down operations",
+ "format": "int64",
+ "type": "integer"
+ },
+ "replicasPerScaleUp": {
+ "description": "ReplicasPerScaleUp defines the number of maximum replicas that can be changed in a single scaled up operation. The is use to prevent from too aggressive scaling up operations",
"format": "int64",
"type": "integer"
},
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index e0be1c1a7f..55540a4b09 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19721,11 +19721,6 @@
"description": "Scale defines the parameters for autoscaling.",
"type": "object",
"properties": {
- "cooldownSeconds": {
- "description": "Deprecated: Use scaleUpCooldownSeconds and scaleDownCooldownSeconds instead. Cooldown seconds after a scaling operation before another one.",
- "type": "integer",
- "format": "int64"
- },
"disabled": {
"description": "Whether to disable autoscaling. Set to \"true\" when using Kubernetes HPA or any other 3rd party autoscaling strategies.",
"type": "boolean"
@@ -19746,7 +19741,17 @@
"format": "int32"
},
"replicasPerScale": {
- "description": "ReplicasPerScale defines maximum replicas can be scaled up or down at once. The is use to prevent too aggressive scaling operations",
+ "description": "DeprecatedReplicasPerScale defines the number of maximum replicas that can be changed in a single scale up or down operation. The is use to prevent from too aggressive scaling operations Deprecated: Use ReplicasPerScaleUp and ReplicasPerScaleDown instead",
+ "type": "integer",
+ "format": "int64"
+ },
+ "replicasPerScaleDown": {
+ "description": "ReplicasPerScaleDown defines the number of maximum replicas that can be changed in a single scaled down operation. The is use to prevent from too aggressive scaling down operations",
+ "type": "integer",
+ "format": "int64"
+ },
+ "replicasPerScaleUp": {
+ "description": "ReplicasPerScaleUp defines the number of maximum replicas that can be changed in a single scaled up operation. The is use to prevent from too aggressive scaling up operations",
"type": "integer",
"format": "int64"
},
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index ece346f242..7967f0362d 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -2210,9 +2210,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -2227,6 +2224,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
diff --git a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
index 0f8f7d3c77..00c4a0b008 100644
--- a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
@@ -5939,9 +5939,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -5956,6 +5953,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
diff --git a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
index e832cec26a..cd7e10a99f 100644
--- a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
@@ -1587,9 +1587,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -1604,6 +1601,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
diff --git a/config/install.yaml b/config/install.yaml
index b8778fdb89..cf347b8bcf 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -4822,9 +4822,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -4839,6 +4836,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
@@ -14096,9 +14099,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -14113,6 +14113,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
@@ -19518,9 +19524,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -19535,6 +19538,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index c265de29f4..be25fca8be 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -4822,9 +4822,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -4839,6 +4836,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
@@ -14096,9 +14099,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -14113,6 +14113,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
@@ -19518,9 +19524,6 @@ spec:
type: string
scale:
properties:
- cooldownSeconds:
- format: int32
- type: integer
disabled:
type: boolean
lookbackSeconds:
@@ -19535,6 +19538,12 @@ spec:
replicasPerScale:
format: int32
type: integer
+ replicasPerScaleDown:
+ format: int32
+ type: integer
+ replicasPerScaleUp:
+ format: int32
+ type: integer
scaleDownCooldownSeconds:
format: int32
type: integer
diff --git a/docs/APIs.md b/docs/APIs.md
index 2fcab4c372..a5c42c14f7 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -8428,26 +8428,6 @@ processing rate.
-cooldownSeconds
uint32
-
-
-
-
-(Optional)
-
-
-Deprecated: Use scaleUpCooldownSeconds and scaleDownCooldownSeconds
-instead. Cooldown seconds after a scaling operation before another one.
-
-
-
-
-
-
-
-
-
-
zeroReplicaSleepSeconds
uint32
@@ -8522,8 +8502,10 @@ have buffers to read.
(Optional)
-ReplicasPerScale defines maximum replicas can be scaled up or down at
-once. The is use to prevent too aggressive scaling operations
+DeprecatedReplicasPerScale defines the number of maximum replicas that
+can be changed in a single scale up or down operation. The is use to
+prevent from too aggressive scaling operations Deprecated: Use
+ReplicasPerScaleUp and ReplicasPerScaleDown instead
@@ -8572,6 +8554,48 @@ CooldownSeconds if not set.
+
+
+
+
+replicasPerScaleUp
uint32
+
+
+
+
+(Optional)
+
+
+ReplicasPerScaleUp defines the number of maximum replicas that can be
+changed in a single scaled up operation. The is use to prevent from too
+aggressive scaling up operations
+
+
+
+
+
+
+
+
+
+
+replicasPerScaleDown
uint32
+
+
+
+
+(Optional)
+
+
+ReplicasPerScaleDown defines the number of maximum replicas that can be
+changed in a single scaled down operation. The is use to prevent from
+too aggressive scaling down operations
+
+
+
+
+
+
diff --git a/docs/user-guide/reference/autoscaling.md b/docs/user-guide/reference/autoscaling.md
index 6226e7a9b6..e78cc0ec59 100644
--- a/docs/user-guide/reference/autoscaling.md
+++ b/docs/user-guide/reference/autoscaling.md
@@ -35,7 +35,8 @@ spec:
zeroReplicaSleepSeconds: 120 # Optional, defaults to 120.
targetProcessingSeconds: 20 # Optional, defaults to 20.
targetBufferAvailability: 50 # Optional, defaults to 50.
- replicasPerScale: 2 # Optional, defaults to 2.
+ replicasPerScaleUp: 2 # Optional, defaults to 2.
+ replicasPerScaleDown: 2 # Optional, defaults to 2.
```
- `disabled` - Whether to disable Numaflow autoscaling, defaults to `false`.
@@ -60,8 +61,11 @@ spec:
support autoscaling, typically increasing the value leads to lower processing rate, thus less replicas.
- `targetBufferAvailability` - Targeted buffer availability in percentage, defaults to `50`. It is only effective for `UDF`
and `Sink` vertices, it determines how aggressive you want to do for autoscaling, increasing the value will bring more replicas.
-- `replicasPerScale` - Maximum number of replicas change happens in one scale up or down operation, defaults to `2`. For
+- `replicasPerScaleUp` - Maximum number of replica change happens in one scale up operation, defaults to `2`. For
example, if current replica number is 3, the calculated desired replica number is 8; instead of scaling up the vertex to 8, it only does 5.
+- `replicasPerScaleDown` - Maximum number of replica change happens in one scale down operation, defaults to `2`. For
+ example, if current replica number is 9, the calculated desired replica number is 4; instead of scaling down the vertex to 4, it only does 7.
+- `replicasPerScale` - (Deprecated: Use `replicasPerScaleUp` and `replicasPerScaleDown` instead, will be removed in v1.5) Maximum number of replica change happens in one scale up or down operation, defaults to `2`.
To disable Numaflow autoscaling, set `disabled: true` as following.
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index 0b8823d5d0..b152ea965c 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -2704,471 +2704,472 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7415 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x24, 0xc7,
- 0x75, 0xb6, 0xe6, 0xc6, 0x99, 0x39, 0x43, 0x72, 0x77, 0x6b, 0xa5, 0x15, 0x77, 0xb5, 0x5a, 0xae,
- 0x5b, 0xbf, 0xf4, 0xaf, 0x7f, 0xdb, 0xe4, 0x2f, 0xfe, 0xba, 0xd9, 0xbf, 0x6d, 0x89, 0x43, 0x2e,
- 0xb9, 0xd4, 0x92, 0xbb, 0xf4, 0x19, 0x72, 0x25, 0x5b, 0xbf, 0xad, 0xbf, 0xd9, 0x5d, 0x1c, 0xb6,
- 0xd8, 0xd3, 0x3d, 0xea, 0xee, 0xe1, 0x2e, 0xe5, 0x04, 0xbe, 0x3d, 0x48, 0x41, 0x12, 0x24, 0xf0,
- 0x93, 0x81, 0xc0, 0x09, 0x12, 0x04, 0xf0, 0x83, 0xe1, 0x3c, 0x04, 0x50, 0x1e, 0x02, 0xe4, 0x06,
- 0x04, 0x89, 0x13, 0xe4, 0xe2, 0x87, 0x00, 0x51, 0x10, 0x80, 0x88, 0x19, 0xe4, 0x21, 0x09, 0x62,
- 0x18, 0x31, 0x10, 0xdb, 0x0b, 0x03, 0x0e, 0xea, 0xd6, 0xb7, 0xe9, 0xd9, 0x25, 0xa7, 0xc9, 0xd5,
- 0x2a, 0xd1, 0x5b, 0x77, 0xd5, 0xa9, 0xef, 0x54, 0x9f, 0xba, 0x9d, 0x3a, 0x75, 0xea, 0x34, 0x2c,
- 0xb6, 0xad, 0x60, 0xab, 0xb7, 0x31, 0x65, 0xb8, 0x9d, 0x69, 0xa7, 0xd7, 0xd1, 0xbb, 0x9e, 0xfb,
- 0x1a, 0x7f, 0xd8, 0xb4, 0xdd, 0x9b, 0xd3, 0xdd, 0xed, 0xf6, 0xb4, 0xde, 0xb5, 0xfc, 0x28, 0x65,
- 0xe7, 0x49, 0xdd, 0xee, 0x6e, 0xe9, 0x4f, 0x4e, 0xb7, 0xa9, 0x43, 0x3d, 0x3d, 0xa0, 0xe6, 0x54,
- 0xd7, 0x73, 0x03, 0x97, 0x3c, 0x1b, 0x01, 0x4d, 0x29, 0xa0, 0x29, 0x55, 0x6c, 0xaa, 0xbb, 0xdd,
- 0x9e, 0x62, 0x40, 0x51, 0x8a, 0x02, 0x3a, 0xf7, 0x91, 0x58, 0x0d, 0xda, 0x6e, 0xdb, 0x9d, 0xe6,
- 0x78, 0x1b, 0xbd, 0x4d, 0xfe, 0xc6, 0x5f, 0xf8, 0x93, 0xe0, 0x73, 0x4e, 0xdb, 0x7e, 0xce, 0x9f,
- 0xb2, 0x5c, 0x56, 0xad, 0x69, 0xc3, 0xf5, 0xe8, 0xf4, 0x4e, 0x5f, 0x5d, 0xce, 0x3d, 0x15, 0xd1,
- 0x74, 0x74, 0x63, 0xcb, 0x72, 0xa8, 0xb7, 0xab, 0xbe, 0x65, 0xda, 0xa3, 0xbe, 0xdb, 0xf3, 0x0c,
- 0x7a, 0xa8, 0x52, 0xfe, 0x74, 0x87, 0x06, 0x7a, 0x16, 0xaf, 0xe9, 0x41, 0xa5, 0xbc, 0x9e, 0x13,
- 0x58, 0x9d, 0x7e, 0x36, 0xcf, 0xdc, 0xad, 0x80, 0x6f, 0x6c, 0xd1, 0x8e, 0x9e, 0x2e, 0xa7, 0xfd,
- 0x7d, 0x1d, 0x4e, 0xcf, 0x6e, 0xf8, 0x81, 0xa7, 0x1b, 0xc1, 0xaa, 0x6b, 0xae, 0xd1, 0x4e, 0xd7,
- 0xd6, 0x03, 0x4a, 0xb6, 0xa1, 0xc6, 0xea, 0x66, 0xea, 0x81, 0x3e, 0x51, 0xb8, 0x58, 0xb8, 0xd4,
- 0x98, 0x99, 0x9d, 0x1a, 0xb2, 0x2d, 0xa6, 0x56, 0x24, 0x50, 0x73, 0x74, 0x7f, 0x6f, 0xb2, 0xa6,
- 0xde, 0x30, 0x64, 0x40, 0xbe, 0x56, 0x80, 0x51, 0xc7, 0x35, 0x69, 0x8b, 0xda, 0xd4, 0x08, 0x5c,
- 0x6f, 0xa2, 0x78, 0xb1, 0x74, 0xa9, 0x31, 0xf3, 0xb9, 0xa1, 0x39, 0x66, 0x7c, 0xd1, 0xd4, 0xb5,
- 0x18, 0x83, 0xcb, 0x4e, 0xe0, 0xed, 0x36, 0x1f, 0xfc, 0xf6, 0xde, 0xe4, 0x03, 0xfb, 0x7b, 0x93,
- 0xa3, 0xf1, 0x2c, 0x4c, 0xd4, 0x84, 0xac, 0x43, 0x23, 0x70, 0x6d, 0x26, 0x32, 0xcb, 0x75, 0xfc,
- 0x89, 0x12, 0xaf, 0xd8, 0x85, 0x29, 0x21, 0x6d, 0xc6, 0x7e, 0x8a, 0x75, 0x97, 0xa9, 0x9d, 0x27,
- 0xa7, 0xd6, 0x42, 0xb2, 0xe6, 0x69, 0x09, 0xdc, 0x88, 0xd2, 0x7c, 0x8c, 0xe3, 0x10, 0x0a, 0x27,
- 0x7c, 0x6a, 0xf4, 0x3c, 0x2b, 0xd8, 0x9d, 0x73, 0x9d, 0x80, 0xde, 0x0a, 0x26, 0xca, 0x5c, 0xca,
- 0x4f, 0x64, 0x41, 0xaf, 0xba, 0x66, 0x2b, 0x49, 0xdd, 0x3c, 0xbd, 0xbf, 0x37, 0x79, 0x22, 0x95,
- 0x88, 0x69, 0x4c, 0xe2, 0xc0, 0x49, 0xab, 0xa3, 0xb7, 0xe9, 0x6a, 0xcf, 0xb6, 0x5b, 0xd4, 0xf0,
- 0x68, 0xe0, 0x4f, 0x54, 0xf8, 0x27, 0x5c, 0xca, 0xe2, 0xb3, 0xec, 0x1a, 0xba, 0x7d, 0x7d, 0xe3,
- 0x35, 0x6a, 0x04, 0x48, 0x37, 0xa9, 0x47, 0x1d, 0x83, 0x36, 0x27, 0xe4, 0xc7, 0x9c, 0x5c, 0x4a,
- 0x21, 0x61, 0x1f, 0x36, 0x59, 0x84, 0x53, 0x5d, 0xcf, 0x72, 0x79, 0x15, 0x6c, 0xdd, 0xf7, 0xaf,
- 0xe9, 0x1d, 0x3a, 0x31, 0x72, 0xb1, 0x70, 0xa9, 0xde, 0x3c, 0x2b, 0x61, 0x4e, 0xad, 0xa6, 0x09,
- 0xb0, 0xbf, 0x0c, 0xb9, 0x04, 0x35, 0x95, 0x38, 0x51, 0xbd, 0x58, 0xb8, 0x54, 0x11, 0x7d, 0x47,
- 0x95, 0xc5, 0x30, 0x97, 0x2c, 0x40, 0x4d, 0xdf, 0xdc, 0xb4, 0x1c, 0x46, 0x59, 0xe3, 0x22, 0x3c,
- 0x9f, 0xf5, 0x69, 0xb3, 0x92, 0x46, 0xe0, 0xa8, 0x37, 0x0c, 0xcb, 0x92, 0x17, 0x81, 0xf8, 0xd4,
- 0xdb, 0xb1, 0x0c, 0x3a, 0x6b, 0x18, 0x6e, 0xcf, 0x09, 0x78, 0xdd, 0xeb, 0xbc, 0xee, 0xe7, 0x64,
- 0xdd, 0x49, 0xab, 0x8f, 0x02, 0x33, 0x4a, 0x91, 0x17, 0xe0, 0xa4, 0x1c, 0x76, 0x91, 0x14, 0x80,
- 0x23, 0x3d, 0xc8, 0x04, 0x89, 0xa9, 0x3c, 0xec, 0xa3, 0x26, 0x26, 0x9c, 0xd7, 0x7b, 0x81, 0xdb,
- 0x61, 0x90, 0x49, 0xa6, 0x6b, 0xee, 0x36, 0x75, 0x26, 0x1a, 0x17, 0x0b, 0x97, 0x6a, 0xcd, 0x8b,
- 0xfb, 0x7b, 0x93, 0xe7, 0x67, 0xef, 0x40, 0x87, 0x77, 0x44, 0x21, 0xd7, 0xa1, 0x6e, 0x3a, 0xfe,
- 0xaa, 0x6b, 0x5b, 0xc6, 0xee, 0xc4, 0x28, 0xaf, 0xe0, 0x93, 0xf2, 0x53, 0xeb, 0xf3, 0xd7, 0x5a,
- 0x22, 0xe3, 0xf6, 0xde, 0xe4, 0xf9, 0xfe, 0xd9, 0x71, 0x2a, 0xcc, 0xc7, 0x08, 0x83, 0xac, 0x70,
- 0xc0, 0x39, 0xd7, 0xd9, 0xb4, 0xda, 0x13, 0x63, 0xbc, 0x35, 0x2e, 0x0e, 0xe8, 0xd0, 0xf3, 0xd7,
- 0x5a, 0x82, 0xae, 0x39, 0x26, 0xd9, 0x89, 0x57, 0x8c, 0x10, 0xce, 0x3d, 0x0f, 0xa7, 0xfa, 0x46,
- 0x2d, 0x39, 0x09, 0xa5, 0x6d, 0xba, 0xcb, 0x27, 0xa5, 0x3a, 0xb2, 0x47, 0xf2, 0x20, 0x54, 0x76,
- 0x74, 0xbb, 0x47, 0x27, 0x8a, 0x3c, 0x4d, 0xbc, 0x7c, 0xac, 0xf8, 0x5c, 0x41, 0xfb, 0x8d, 0x12,
- 0x8c, 0xaa, 0xb9, 0xa0, 0x65, 0x39, 0xdb, 0xe4, 0x25, 0x28, 0xd9, 0x6e, 0x5b, 0xce, 0x68, 0x1f,
- 0x1f, 0x7a, 0x7e, 0x59, 0x76, 0xdb, 0xcd, 0xea, 0xfe, 0xde, 0x64, 0x69, 0xd9, 0x6d, 0x23, 0x43,
- 0x24, 0x06, 0x54, 0xb6, 0xf5, 0xcd, 0x6d, 0x9d, 0xd7, 0xa1, 0x31, 0xd3, 0x1c, 0x1a, 0xfa, 0x2a,
- 0x43, 0x61, 0x75, 0x6d, 0xd6, 0xf7, 0xf7, 0x26, 0x2b, 0xfc, 0x15, 0x05, 0x36, 0x71, 0xa1, 0xbe,
- 0x61, 0xeb, 0xc6, 0xf6, 0x96, 0x6b, 0xd3, 0x89, 0x52, 0x4e, 0x46, 0x4d, 0x85, 0x24, 0x1a, 0x20,
- 0x7c, 0xc5, 0x88, 0x07, 0x31, 0x60, 0xa4, 0x67, 0xfa, 0x96, 0xb3, 0x2d, 0x67, 0xa7, 0xe7, 0x87,
- 0xe6, 0xb6, 0x3e, 0xcf, 0xbf, 0x09, 0xf6, 0xf7, 0x26, 0x47, 0xc4, 0x33, 0x4a, 0x68, 0xed, 0x7b,
- 0x0d, 0x18, 0x57, 0x8d, 0x74, 0x83, 0x7a, 0x01, 0xbd, 0x45, 0x2e, 0x42, 0xd9, 0x61, 0x83, 0x86,
- 0x37, 0x72, 0x73, 0x54, 0xf6, 0xc9, 0x32, 0x1f, 0x2c, 0x3c, 0x87, 0xd5, 0x4c, 0x2c, 0xb8, 0x52,
- 0xe0, 0xc3, 0xd7, 0xac, 0xc5, 0x61, 0x44, 0xcd, 0xc4, 0x33, 0x4a, 0x68, 0xf2, 0x0a, 0x94, 0xf9,
- 0xc7, 0x0b, 0x51, 0x7f, 0x62, 0x78, 0x16, 0xec, 0xd3, 0x6b, 0xec, 0x0b, 0xf8, 0x87, 0x73, 0x50,
- 0xd6, 0x15, 0x7b, 0xe6, 0xa6, 0x14, 0xec, 0xc7, 0x73, 0x08, 0x76, 0x41, 0x74, 0xc5, 0xf5, 0xf9,
- 0x05, 0x64, 0x88, 0xe4, 0x97, 0x0a, 0x70, 0xca, 0x70, 0x9d, 0x40, 0x67, 0x4a, 0x80, 0x5a, 0xfe,
- 0x26, 0x2a, 0x9c, 0xcf, 0x8b, 0x43, 0xf3, 0x99, 0x4b, 0x23, 0x36, 0x1f, 0x62, 0xb3, 0x79, 0x5f,
- 0x32, 0xf6, 0xf3, 0x26, 0xbf, 0x52, 0x80, 0x87, 0xd8, 0x2c, 0xdb, 0x47, 0xcc, 0xd7, 0x86, 0xa3,
- 0xad, 0xd5, 0xd9, 0xfd, 0xbd, 0xc9, 0x87, 0x96, 0xb2, 0x98, 0x61, 0x76, 0x1d, 0x58, 0xed, 0x4e,
- 0xeb, 0xfd, 0x0a, 0x03, 0x5f, 0x77, 0x1a, 0x33, 0xcb, 0x47, 0xa9, 0x84, 0x34, 0x1f, 0x91, 0x5d,
- 0x39, 0x4b, 0xe7, 0xc2, 0xac, 0x5a, 0x90, 0xcb, 0x50, 0xdd, 0x71, 0xed, 0x5e, 0x87, 0xfa, 0x13,
- 0x35, 0xbe, 0x72, 0x9f, 0xcb, 0x9a, 0x50, 0x6f, 0x70, 0x92, 0xe6, 0x09, 0x09, 0x5f, 0x15, 0xef,
- 0x3e, 0xaa, 0xb2, 0xc4, 0x82, 0x11, 0xdb, 0xea, 0x58, 0x81, 0xcf, 0x97, 0xb4, 0xc6, 0xcc, 0xe5,
- 0xa1, 0x3f, 0x4b, 0x0c, 0xd1, 0x65, 0x0e, 0x26, 0x46, 0x8d, 0x78, 0x46, 0xc9, 0x80, 0x4d, 0x85,
- 0xbe, 0xa1, 0xdb, 0x62, 0xc9, 0x6b, 0xcc, 0x7c, 0x72, 0xf8, 0x61, 0xc3, 0x50, 0x9a, 0x63, 0xf2,
- 0x9b, 0x2a, 0xfc, 0x15, 0x05, 0x36, 0xf9, 0x2c, 0x8c, 0x27, 0x5a, 0xd3, 0x9f, 0x68, 0x70, 0xe9,
- 0x3c, 0x9a, 0x25, 0x9d, 0x90, 0xaa, 0x79, 0x46, 0x82, 0x8d, 0x27, 0x7a, 0x88, 0x8f, 0x29, 0x30,
- 0x72, 0x15, 0x6a, 0xbe, 0x65, 0x52, 0x43, 0xf7, 0xfc, 0x89, 0xd1, 0x83, 0x00, 0x9f, 0x94, 0xc0,
- 0xb5, 0x96, 0x2c, 0x86, 0x21, 0x00, 0x99, 0x02, 0xe8, 0xea, 0x5e, 0x60, 0x09, 0x15, 0x72, 0x8c,
- 0xab, 0x33, 0xe3, 0xfb, 0x7b, 0x93, 0xb0, 0x1a, 0xa6, 0x62, 0x8c, 0x82, 0xd1, 0xb3, 0xb2, 0x4b,
- 0x4e, 0xb7, 0x17, 0xf8, 0x13, 0xe3, 0x17, 0x4b, 0x97, 0xea, 0x82, 0xbe, 0x15, 0xa6, 0x62, 0x8c,
- 0x82, 0x7c, 0xab, 0x00, 0x8f, 0x44, 0xaf, 0xfd, 0x83, 0xec, 0xc4, 0x91, 0x0f, 0xb2, 0xc9, 0xfd,
- 0xbd, 0xc9, 0x47, 0x5a, 0x83, 0x59, 0xe2, 0x9d, 0xea, 0xa3, 0xbd, 0x04, 0x63, 0xb3, 0xbd, 0x60,
- 0xcb, 0xf5, 0xac, 0x37, 0xb8, 0x3a, 0x4c, 0x16, 0xa0, 0x12, 0x70, 0xb5, 0x46, 0xac, 0xcb, 0x8f,
- 0x67, 0x89, 0x5a, 0xa8, 0x98, 0x57, 0xe9, 0xae, 0xd2, 0x06, 0xc4, 0xfa, 0x28, 0xd4, 0x1c, 0x51,
- 0x5c, 0xfb, 0xf5, 0x02, 0xd4, 0x9b, 0xba, 0x6f, 0x19, 0x0c, 0x9e, 0xcc, 0x41, 0xb9, 0xe7, 0x53,
- 0xef, 0x70, 0xa0, 0x7c, 0x96, 0x5e, 0xf7, 0xa9, 0x87, 0xbc, 0x30, 0xb9, 0x0e, 0xb5, 0xae, 0xee,
- 0xfb, 0x37, 0x5d, 0xcf, 0x94, 0x2b, 0xcd, 0x01, 0x81, 0x84, 0xbe, 0x2a, 0x8b, 0x62, 0x08, 0xa2,
- 0x35, 0x20, 0x5a, 0x6a, 0xb5, 0x1f, 0x14, 0xe0, 0x74, 0xb3, 0xb7, 0xb9, 0x49, 0x3d, 0xa9, 0x9e,
- 0x09, 0xc5, 0x87, 0x50, 0xa8, 0x78, 0xd4, 0xb4, 0x7c, 0x59, 0xf7, 0xf9, 0xa1, 0x9b, 0x0e, 0x19,
- 0x8a, 0xd4, 0xb3, 0xb8, 0xbc, 0x78, 0x02, 0x0a, 0x74, 0xd2, 0x83, 0xfa, 0x6b, 0x34, 0xf0, 0x03,
- 0x8f, 0xea, 0x1d, 0xf9, 0x75, 0x57, 0x86, 0x66, 0xf5, 0x22, 0x0d, 0x5a, 0x1c, 0x29, 0xae, 0xd6,
- 0x85, 0x89, 0x18, 0x71, 0xd2, 0xfe, 0xa8, 0x02, 0xa3, 0x73, 0x6e, 0x67, 0xc3, 0x72, 0xa8, 0x79,
- 0xd9, 0x6c, 0x53, 0xf2, 0x2a, 0x94, 0xa9, 0xd9, 0xa6, 0xf2, 0x6b, 0x87, 0x5f, 0x67, 0x19, 0x58,
- 0xa4, 0x2d, 0xb0, 0x37, 0xe4, 0xc0, 0x64, 0x19, 0xc6, 0x37, 0x3d, 0xb7, 0x23, 0xa6, 0xae, 0xb5,
- 0xdd, 0xae, 0x54, 0x15, 0x9b, 0xff, 0x43, 0x4d, 0x07, 0x0b, 0x89, 0xdc, 0xdb, 0x7b, 0x93, 0x10,
- 0xbd, 0x61, 0xaa, 0x2c, 0x79, 0x19, 0x26, 0xa2, 0x94, 0x70, 0x0c, 0xcf, 0x31, 0xbd, 0x9a, 0xab,
- 0x0a, 0x95, 0xe6, 0xf9, 0xfd, 0xbd, 0xc9, 0x89, 0x85, 0x01, 0x34, 0x38, 0xb0, 0x34, 0x79, 0xb3,
- 0x00, 0x27, 0xa3, 0x4c, 0x31, 0xaf, 0x4a, 0x0d, 0xe1, 0x88, 0x26, 0x6c, 0xbe, 0x01, 0x59, 0x48,
- 0xb1, 0xc0, 0x3e, 0xa6, 0x64, 0x01, 0x46, 0x03, 0x37, 0x26, 0xaf, 0x0a, 0x97, 0x97, 0xa6, 0x76,
- 0xcc, 0x6b, 0xee, 0x40, 0x69, 0x25, 0xca, 0x11, 0x84, 0x33, 0xea, 0x3d, 0x25, 0xa9, 0x11, 0x2e,
- 0xa9, 0x73, 0xfb, 0x7b, 0x93, 0x67, 0xd6, 0x32, 0x29, 0x70, 0x40, 0x49, 0xf2, 0xa5, 0x02, 0x8c,
- 0xab, 0x2c, 0x29, 0xa3, 0xea, 0x51, 0xca, 0x88, 0xb0, 0x1e, 0xb1, 0x96, 0x60, 0x80, 0x29, 0x86,
- 0xda, 0x8f, 0xca, 0x50, 0x0f, 0x67, 0x36, 0xf2, 0x18, 0x54, 0xf8, 0x5e, 0x58, 0x2a, 0xac, 0xe1,
- 0x92, 0xc5, 0xb7, 0xcc, 0x28, 0xf2, 0xc8, 0xe3, 0x50, 0x35, 0xdc, 0x4e, 0x47, 0x77, 0x4c, 0x6e,
- 0xdf, 0xa8, 0x37, 0x1b, 0x6c, 0xa5, 0x9e, 0x13, 0x49, 0xa8, 0xf2, 0xc8, 0x79, 0x28, 0xeb, 0x5e,
- 0x5b, 0x98, 0x1a, 0xea, 0x62, 0x3e, 0x9a, 0xf5, 0xda, 0x3e, 0xf2, 0x54, 0xf2, 0x51, 0x28, 0x51,
- 0x67, 0x67, 0xa2, 0x3c, 0x58, 0x15, 0xb8, 0xec, 0xec, 0xdc, 0xd0, 0xbd, 0x66, 0x43, 0xd6, 0xa1,
- 0x74, 0xd9, 0xd9, 0x41, 0x56, 0x86, 0x2c, 0x43, 0x95, 0x3a, 0x3b, 0xac, 0xed, 0xa5, 0x0d, 0xe0,
- 0x03, 0x03, 0x8a, 0x33, 0x12, 0xa9, 0x15, 0x87, 0x0a, 0x85, 0x4c, 0x46, 0x05, 0x41, 0x3e, 0x0d,
- 0xa3, 0x42, 0xb7, 0x58, 0x61, 0x6d, 0xe2, 0x4f, 0x8c, 0x70, 0xc8, 0xc9, 0xc1, 0xca, 0x09, 0xa7,
- 0x8b, 0x6c, 0x2e, 0xb1, 0x44, 0x1f, 0x13, 0x50, 0xe4, 0xd3, 0x50, 0x57, 0xe6, 0x34, 0xd5, 0xb2,
- 0x99, 0xe6, 0x0a, 0x94, 0x44, 0x48, 0x5f, 0xef, 0x59, 0x1e, 0xed, 0x50, 0x27, 0xf0, 0x9b, 0xa7,
- 0xd4, 0x06, 0x56, 0xe5, 0xfa, 0x18, 0xa1, 0x91, 0x8d, 0x7e, 0xbb, 0x8b, 0x30, 0x1a, 0x3c, 0x36,
- 0x60, 0x56, 0x1f, 0xc2, 0xe8, 0xf2, 0x39, 0x38, 0x11, 0x1a, 0x46, 0xe4, 0xde, 0x5a, 0x98, 0x11,
- 0x9e, 0x62, 0xc5, 0x97, 0x92, 0x59, 0xb7, 0xf7, 0x26, 0x1f, 0xcd, 0xd8, 0x5d, 0x47, 0x04, 0x98,
- 0x06, 0xd3, 0xfe, 0xa0, 0x04, 0xfd, 0x6a, 0x77, 0x52, 0x68, 0x85, 0xa3, 0x16, 0x5a, 0xfa, 0x83,
- 0xc4, 0xf4, 0xf9, 0x9c, 0x2c, 0x96, 0xff, 0xa3, 0xb2, 0x1a, 0xa6, 0x74, 0xd4, 0x0d, 0x73, 0xbf,
- 0x8c, 0x1d, 0xed, 0xad, 0x32, 0x8c, 0xcf, 0xeb, 0xb4, 0xe3, 0x3a, 0x77, 0xdd, 0x84, 0x14, 0xee,
- 0x8b, 0x4d, 0xc8, 0x25, 0xa8, 0x79, 0xb4, 0x6b, 0x5b, 0x86, 0xee, 0xf3, 0xa6, 0x97, 0xe6, 0x38,
- 0x94, 0x69, 0x18, 0xe6, 0x0e, 0xd8, 0x7c, 0x96, 0xee, 0xcb, 0xcd, 0x67, 0xf9, 0xdd, 0xdf, 0x7c,
- 0x6a, 0x5f, 0x2a, 0x02, 0x57, 0x54, 0xc8, 0x45, 0x28, 0xb3, 0x45, 0x38, 0x6d, 0xf2, 0xe0, 0x1d,
- 0x87, 0xe7, 0x90, 0x73, 0x50, 0x0c, 0x5c, 0x39, 0xf2, 0x40, 0xe6, 0x17, 0xd7, 0x5c, 0x2c, 0x06,
- 0x2e, 0x79, 0x03, 0xc0, 0x70, 0x1d, 0xd3, 0x52, 0x56, 0xea, 0x7c, 0x1f, 0xb6, 0xe0, 0x7a, 0x37,
- 0x75, 0xcf, 0x9c, 0x0b, 0x11, 0xc5, 0xf6, 0x23, 0x7a, 0xc7, 0x18, 0x37, 0xf2, 0x3c, 0x8c, 0xb8,
- 0xce, 0x42, 0xcf, 0xb6, 0xb9, 0x40, 0xeb, 0xcd, 0xff, 0xc9, 0xf6, 0x84, 0xd7, 0x79, 0xca, 0xed,
- 0xbd, 0xc9, 0xb3, 0x42, 0xbf, 0x65, 0x6f, 0x2f, 0x79, 0x56, 0x60, 0x39, 0xed, 0x56, 0xe0, 0xe9,
- 0x01, 0x6d, 0xef, 0xa2, 0x2c, 0xa6, 0x7d, 0xb5, 0x00, 0x8d, 0x05, 0xeb, 0x16, 0x35, 0x5f, 0xb2,
- 0x1c, 0xd3, 0xbd, 0x49, 0x10, 0x46, 0x6c, 0xea, 0xb4, 0x83, 0x2d, 0xd9, 0xfb, 0xa7, 0x62, 0x63,
- 0x2d, 0x3c, 0xdc, 0x88, 0xea, 0xdf, 0xa1, 0x81, 0xce, 0x46, 0xdf, 0x7c, 0x4f, 0x9a, 0xdf, 0xc5,
- 0xa6, 0x94, 0x23, 0xa0, 0x44, 0x22, 0xd3, 0x50, 0x17, 0xda, 0xa7, 0xe5, 0xb4, 0xb9, 0x0c, 0x6b,
- 0xd1, 0xa4, 0xd7, 0x52, 0x19, 0x18, 0xd1, 0x68, 0xbb, 0x70, 0xaa, 0x4f, 0x0c, 0xc4, 0x84, 0x72,
- 0xa0, 0xb7, 0xd5, 0xfc, 0xba, 0x30, 0xb4, 0x80, 0xd7, 0xf4, 0x76, 0x4c, 0xb8, 0x7c, 0x8d, 0x5f,
- 0xd3, 0xd9, 0x1a, 0xcf, 0xd0, 0xb5, 0x9f, 0x14, 0xa0, 0xb6, 0xd0, 0x73, 0x0c, 0xbe, 0x37, 0xba,
- 0xbb, 0x29, 0x4c, 0x29, 0x0c, 0xc5, 0x4c, 0x85, 0xa1, 0x07, 0x23, 0xdb, 0x37, 0x43, 0x85, 0xa2,
- 0x31, 0xb3, 0x32, 0x7c, 0xaf, 0x90, 0x55, 0x9a, 0xba, 0xca, 0xf1, 0xc4, 0x19, 0xca, 0xb8, 0xac,
- 0xd0, 0xc8, 0xd5, 0x97, 0x38, 0x53, 0xc9, 0xec, 0xdc, 0x47, 0xa1, 0x11, 0x23, 0x3b, 0x94, 0xd1,
- 0xf6, 0x77, 0xca, 0x30, 0xb2, 0xd8, 0x6a, 0xcd, 0xae, 0x2e, 0x91, 0xa7, 0xa1, 0x21, 0xcd, 0xeb,
- 0xd7, 0x22, 0x19, 0x84, 0xa7, 0x2b, 0xad, 0x28, 0x0b, 0xe3, 0x74, 0x4c, 0x1d, 0xf3, 0xa8, 0x6e,
- 0x77, 0xe4, 0x60, 0x09, 0xd5, 0x31, 0x64, 0x89, 0x28, 0xf2, 0x88, 0x0e, 0xe3, 0x6c, 0x87, 0xc7,
- 0x44, 0x28, 0x76, 0x6f, 0x72, 0xd8, 0x1c, 0x70, 0x7f, 0xc7, 0x95, 0xc4, 0xf5, 0x04, 0x00, 0xa6,
- 0x00, 0xc9, 0x73, 0x50, 0xd3, 0x7b, 0xc1, 0x16, 0x57, 0xa0, 0xc5, 0xd8, 0x38, 0xcf, 0x4f, 0x1f,
- 0x64, 0xda, 0xed, 0xbd, 0xc9, 0xd1, 0xab, 0xd8, 0x7c, 0x5a, 0xbd, 0x63, 0x48, 0xcd, 0x2a, 0xa7,
- 0x76, 0x8c, 0xb2, 0x72, 0x95, 0x43, 0x57, 0x6e, 0x35, 0x01, 0x80, 0x29, 0x40, 0xf2, 0x0a, 0x8c,
- 0x6e, 0xd3, 0xdd, 0x40, 0xdf, 0x90, 0x0c, 0x46, 0x0e, 0xc3, 0xe0, 0x24, 0x53, 0xe1, 0xae, 0xc6,
- 0x8a, 0x63, 0x02, 0x8c, 0xf8, 0xf0, 0xe0, 0x36, 0xf5, 0x36, 0xa8, 0xe7, 0xca, 0xdd, 0xa7, 0x64,
- 0x52, 0x3d, 0x0c, 0x93, 0x89, 0xfd, 0xbd, 0xc9, 0x07, 0xaf, 0x66, 0xc0, 0x60, 0x26, 0xb8, 0xf6,
- 0xe3, 0x22, 0x9c, 0x58, 0x14, 0xe7, 0x9b, 0xae, 0x27, 0x16, 0x61, 0x72, 0x16, 0x4a, 0x5e, 0xb7,
- 0xc7, 0x7b, 0x4e, 0x49, 0xd8, 0x49, 0x71, 0x75, 0x1d, 0x59, 0x1a, 0x79, 0x19, 0x6a, 0xa6, 0x9c,
- 0x32, 0xe4, 0xe6, 0xf7, 0xb0, 0x13, 0x0d, 0x5f, 0x04, 0xd5, 0x1b, 0x86, 0x68, 0x4c, 0xd3, 0xef,
- 0xf8, 0xed, 0x96, 0xf5, 0x06, 0x95, 0xfb, 0x41, 0xae, 0xe9, 0xaf, 0x88, 0x24, 0x54, 0x79, 0x6c,
- 0x55, 0xdd, 0xa6, 0xbb, 0x62, 0x37, 0x54, 0x8e, 0x56, 0xd5, 0xab, 0x32, 0x0d, 0xc3, 0x5c, 0x32,
- 0xa9, 0x06, 0x0b, 0xeb, 0x05, 0x65, 0xb1, 0x93, 0xbf, 0xc1, 0x12, 0xe4, 0xb8, 0x61, 0x53, 0xe6,
- 0x6b, 0x56, 0x10, 0x50, 0x4f, 0x36, 0xe3, 0x50, 0x53, 0xe6, 0x8b, 0x1c, 0x01, 0x25, 0x12, 0xf9,
- 0x10, 0xd4, 0x39, 0x78, 0xd3, 0x76, 0x37, 0x78, 0xc3, 0xd5, 0xc5, 0x9e, 0xfe, 0x86, 0x4a, 0xc4,
- 0x28, 0x5f, 0xfb, 0x69, 0x11, 0xce, 0x2c, 0xd2, 0x40, 0x68, 0x35, 0xf3, 0xb4, 0x6b, 0xbb, 0xbb,
- 0x4c, 0xb5, 0x44, 0xfa, 0x3a, 0x79, 0x01, 0xc0, 0xf2, 0x37, 0x5a, 0x3b, 0x06, 0x1f, 0x07, 0x62,
- 0x0c, 0x5f, 0x94, 0x43, 0x12, 0x96, 0x5a, 0x4d, 0x99, 0x73, 0x3b, 0xf1, 0x86, 0xb1, 0x32, 0xd1,
- 0xf6, 0xaa, 0x78, 0x87, 0xed, 0x55, 0x0b, 0xa0, 0x1b, 0x29, 0xa8, 0x25, 0x4e, 0xf9, 0x7f, 0x14,
- 0x9b, 0xc3, 0xe8, 0xa6, 0x31, 0x98, 0x3c, 0x2a, 0xa3, 0x03, 0x27, 0x4d, 0xba, 0xa9, 0xf7, 0xec,
- 0x20, 0x54, 0xaa, 0xe5, 0x20, 0x3e, 0xb8, 0x5e, 0x1e, 0x9e, 0xbd, 0xce, 0xa7, 0x90, 0xb0, 0x0f,
- 0x5b, 0xfb, 0xdd, 0x12, 0x9c, 0x5b, 0xa4, 0x41, 0x68, 0x71, 0x91, 0xb3, 0x63, 0xab, 0x4b, 0x0d,
- 0xd6, 0x0a, 0x6f, 0x16, 0x60, 0xc4, 0xd6, 0x37, 0xa8, 0xcd, 0x56, 0x2f, 0xf6, 0x35, 0xaf, 0x0e,
- 0xbd, 0x10, 0x0c, 0xe6, 0x32, 0xb5, 0xcc, 0x39, 0xa4, 0x96, 0x06, 0x91, 0x88, 0x92, 0x3d, 0x9b,
- 0xd4, 0x0d, 0xbb, 0xe7, 0x07, 0xd4, 0x5b, 0x75, 0xbd, 0x40, 0xea, 0x93, 0xe1, 0xa4, 0x3e, 0x17,
- 0x65, 0x61, 0x9c, 0x8e, 0xcc, 0x00, 0x18, 0xb6, 0x45, 0x9d, 0x80, 0x97, 0x12, 0xe3, 0x8a, 0xa8,
- 0xf6, 0x9d, 0x0b, 0x73, 0x30, 0x46, 0xc5, 0x58, 0x75, 0x5c, 0xc7, 0x0a, 0x5c, 0xc1, 0xaa, 0x9c,
- 0x64, 0xb5, 0x12, 0x65, 0x61, 0x9c, 0x8e, 0x17, 0xa3, 0x81, 0x67, 0x19, 0x3e, 0x2f, 0x56, 0x49,
- 0x15, 0x8b, 0xb2, 0x30, 0x4e, 0xc7, 0xd6, 0xbc, 0xd8, 0xf7, 0x1f, 0x6a, 0xcd, 0xfb, 0x66, 0x1d,
- 0x2e, 0x24, 0xc4, 0x1a, 0xe8, 0x01, 0xdd, 0xec, 0xd9, 0x2d, 0x1a, 0xa8, 0x06, 0x1c, 0x72, 0x2d,
- 0xfc, 0xf9, 0xa8, 0xdd, 0x85, 0x57, 0x85, 0x71, 0x34, 0xed, 0xde, 0x57, 0xc1, 0x03, 0xb5, 0xfd,
- 0x34, 0xd4, 0x1d, 0x3d, 0xf0, 0xf9, 0xc0, 0x95, 0x63, 0x34, 0x54, 0xc3, 0xae, 0xa9, 0x0c, 0x8c,
- 0x68, 0xc8, 0x2a, 0x3c, 0x28, 0x45, 0x7c, 0xf9, 0x56, 0xd7, 0xf5, 0x02, 0xea, 0x89, 0xb2, 0x72,
- 0x39, 0x95, 0x65, 0x1f, 0x5c, 0xc9, 0xa0, 0xc1, 0xcc, 0x92, 0x64, 0x05, 0x4e, 0x1b, 0xe2, 0xa4,
- 0x99, 0xda, 0xae, 0x6e, 0x2a, 0x40, 0x61, 0xe0, 0x0a, 0xb7, 0x46, 0x73, 0xfd, 0x24, 0x98, 0x55,
- 0x2e, 0xdd, 0x9b, 0x47, 0x86, 0xea, 0xcd, 0xd5, 0x61, 0x7a, 0x73, 0x6d, 0xb8, 0xde, 0x5c, 0x3f,
- 0x58, 0x6f, 0x66, 0x92, 0x67, 0xfd, 0x88, 0x7a, 0x4c, 0x3d, 0x11, 0x2b, 0x6c, 0xcc, 0x91, 0x21,
- 0x94, 0x7c, 0x2b, 0x83, 0x06, 0x33, 0x4b, 0x92, 0x0d, 0x38, 0x27, 0xd2, 0x2f, 0x3b, 0x86, 0xb7,
- 0xdb, 0x65, 0x0b, 0x4f, 0x0c, 0xb7, 0x91, 0xb0, 0x30, 0x9e, 0x6b, 0x0d, 0xa4, 0xc4, 0x3b, 0xa0,
- 0x90, 0xff, 0x0b, 0x63, 0xa2, 0x95, 0x56, 0xf4, 0x2e, 0x87, 0x15, 0x6e, 0x0d, 0x0f, 0x49, 0xd8,
- 0xb1, 0xb9, 0x78, 0x26, 0x26, 0x69, 0xc9, 0x2c, 0x9c, 0xe8, 0xee, 0x18, 0xec, 0x71, 0x69, 0xf3,
- 0x1a, 0xa5, 0x26, 0x35, 0xf9, 0x69, 0x4d, 0xbd, 0xf9, 0xb0, 0x32, 0x74, 0xac, 0x26, 0xb3, 0x31,
- 0x4d, 0x4f, 0x9e, 0x83, 0x51, 0x3f, 0xd0, 0xbd, 0x40, 0x9a, 0xf5, 0x26, 0xc6, 0x85, 0xdb, 0x87,
- 0xb2, 0x7a, 0xb5, 0x62, 0x79, 0x98, 0xa0, 0xcc, 0x5c, 0x2f, 0x4e, 0x1c, 0xdf, 0x7a, 0x91, 0x67,
- 0xb6, 0xfa, 0xd3, 0x22, 0x5c, 0x5c, 0xa4, 0xc1, 0x8a, 0xeb, 0x48, 0xa3, 0x68, 0xd6, 0xb2, 0x7f,
- 0x20, 0x9b, 0x68, 0x72, 0xd1, 0x2e, 0x1e, 0xe9, 0xa2, 0x5d, 0x3a, 0xa2, 0x45, 0xbb, 0x7c, 0x8c,
- 0x8b, 0xf6, 0xef, 0x17, 0xe1, 0xe1, 0x84, 0x24, 0x57, 0x5d, 0x53, 0x4d, 0xf8, 0xef, 0x0b, 0xf0,
- 0x00, 0x02, 0xbc, 0x2d, 0xf4, 0x4e, 0x7e, 0xac, 0x95, 0xd2, 0x78, 0xbe, 0x92, 0xd6, 0x78, 0x5e,
- 0xc9, 0xb3, 0xf2, 0x65, 0x70, 0x38, 0xd0, 0x8a, 0xf7, 0x22, 0x10, 0x4f, 0x1e, 0xc2, 0x09, 0xd3,
- 0x4f, 0x4c, 0xe9, 0x09, 0xfd, 0xca, 0xb0, 0x8f, 0x02, 0x33, 0x4a, 0x91, 0x16, 0x3c, 0xe4, 0x53,
- 0x27, 0xb0, 0x1c, 0x6a, 0x27, 0xe1, 0x84, 0x36, 0xf4, 0xa8, 0x84, 0x7b, 0xa8, 0x95, 0x45, 0x84,
- 0xd9, 0x65, 0xf3, 0xcc, 0x03, 0x7f, 0x01, 0x5c, 0xe5, 0x14, 0xa2, 0x39, 0x32, 0x8d, 0xe5, 0xcd,
- 0xb4, 0xc6, 0xf2, 0x6a, 0xfe, 0x76, 0x1b, 0x4e, 0x5b, 0x99, 0x01, 0xe0, 0xad, 0x10, 0x57, 0x57,
- 0xc2, 0x45, 0x1a, 0xc3, 0x1c, 0x8c, 0x51, 0xb1, 0x05, 0x48, 0xc9, 0x39, 0xae, 0xa9, 0x84, 0x0b,
- 0x50, 0x2b, 0x9e, 0x89, 0x49, 0xda, 0x81, 0xda, 0x4e, 0x65, 0x68, 0x6d, 0xe7, 0x45, 0x20, 0x09,
- 0xc3, 0xa3, 0xc0, 0x1b, 0x49, 0xba, 0x35, 0x2e, 0xf5, 0x51, 0x60, 0x46, 0xa9, 0x01, 0x5d, 0xb9,
- 0x7a, 0xb4, 0x5d, 0xb9, 0x36, 0x7c, 0x57, 0x26, 0xaf, 0xc2, 0x59, 0xce, 0x4a, 0xca, 0x27, 0x09,
- 0x2c, 0xf4, 0x9e, 0x0f, 0x48, 0xe0, 0xb3, 0x38, 0x88, 0x10, 0x07, 0x63, 0xb0, 0xf6, 0x31, 0x3c,
- 0x6a, 0x32, 0xe6, 0xba, 0x3d, 0x58, 0x27, 0x9a, 0xcb, 0xa0, 0xc1, 0xcc, 0x92, 0xac, 0x8b, 0x05,
- 0xac, 0x1b, 0xea, 0x1b, 0x36, 0x35, 0xa5, 0x5b, 0x67, 0xd8, 0xc5, 0xd6, 0x96, 0x5b, 0x32, 0x07,
- 0x63, 0x54, 0x59, 0x6a, 0xca, 0xe8, 0x21, 0xd5, 0x94, 0x45, 0x6e, 0xa5, 0xdf, 0x4c, 0x68, 0x43,
- 0x52, 0xd7, 0x09, 0x1d, 0x75, 0xe7, 0xd2, 0x04, 0xd8, 0x5f, 0x86, 0x6b, 0x89, 0x86, 0x67, 0x75,
- 0x03, 0x3f, 0x89, 0x35, 0x9e, 0xd2, 0x12, 0x33, 0x68, 0x30, 0xb3, 0x24, 0xd3, 0xcf, 0xb7, 0xa8,
- 0x6e, 0x07, 0x5b, 0x49, 0xc0, 0x13, 0x49, 0xfd, 0xfc, 0x4a, 0x3f, 0x09, 0x66, 0x95, 0xcb, 0x5c,
- 0x90, 0x4e, 0xde, 0x9f, 0x6a, 0xd5, 0x97, 0x4b, 0x70, 0x76, 0x91, 0x06, 0xa1, 0x5f, 0xcd, 0xfb,
- 0x66, 0x94, 0x77, 0xc1, 0x8c, 0xf2, 0x8d, 0x0a, 0x9c, 0x5e, 0xa4, 0x41, 0x9f, 0x36, 0xf6, 0xdf,
- 0x54, 0xfc, 0x2b, 0x70, 0x3a, 0x72, 0xe5, 0x6a, 0x05, 0xae, 0x27, 0xd6, 0xf2, 0xd4, 0x6e, 0xb9,
- 0xd5, 0x4f, 0x82, 0x59, 0xe5, 0xc8, 0xa7, 0xe1, 0x61, 0xbe, 0xd4, 0x3b, 0x6d, 0x61, 0x9f, 0x15,
- 0xc6, 0x84, 0xd8, 0x35, 0x81, 0x49, 0x09, 0xf9, 0x70, 0x2b, 0x9b, 0x0c, 0x07, 0x95, 0x27, 0x5f,
- 0x80, 0xd1, 0xae, 0xd5, 0xa5, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0xb7, 0x4b, 0xc8, 0x6a, 0x0c, 0x2c,
- 0xda, 0xc0, 0xc5, 0x53, 0x31, 0xc1, 0x30, 0xb3, 0xa7, 0xd6, 0x8e, 0xb1, 0xa7, 0xfe, 0x7b, 0x11,
- 0xaa, 0x8b, 0x9e, 0xdb, 0xeb, 0x36, 0x77, 0x49, 0x1b, 0x46, 0x6e, 0xf2, 0xc3, 0x33, 0x79, 0x34,
- 0x35, 0xbc, 0x3b, 0xb4, 0x38, 0x83, 0x8b, 0x54, 0x22, 0xf1, 0x8e, 0x12, 0x9e, 0x75, 0xe2, 0x6d,
- 0xba, 0x4b, 0x4d, 0x79, 0x86, 0x16, 0x76, 0xe2, 0xab, 0x2c, 0x11, 0x45, 0x1e, 0xe9, 0xc0, 0x09,
- 0xdd, 0xb6, 0xdd, 0x9b, 0xd4, 0x5c, 0xd6, 0x03, 0xea, 0x50, 0x5f, 0x1d, 0x49, 0x1e, 0xd6, 0x2c,
- 0xcd, 0xcf, 0xf5, 0x67, 0x93, 0x50, 0x98, 0xc6, 0x26, 0xaf, 0x41, 0xd5, 0x0f, 0x5c, 0x4f, 0x29,
- 0x5b, 0x8d, 0x99, 0xb9, 0xe1, 0x1b, 0xbd, 0xf9, 0xa9, 0x96, 0x80, 0x12, 0x36, 0x7b, 0xf9, 0x82,
- 0x8a, 0x81, 0xf6, 0xf5, 0x02, 0xc0, 0x95, 0xb5, 0xb5, 0x55, 0x79, 0xbc, 0x60, 0x42, 0x59, 0xef,
- 0x85, 0x07, 0x95, 0xc3, 0x1f, 0x08, 0x26, 0xfc, 0x21, 0xe5, 0x19, 0x5e, 0x2f, 0xd8, 0x42, 0x8e,
- 0x4e, 0x3e, 0x08, 0x55, 0xa9, 0x20, 0x4b, 0xb1, 0x87, 0xae, 0x05, 0x52, 0x89, 0x46, 0x95, 0xaf,
- 0xfd, 0x76, 0x11, 0x60, 0xc9, 0xb4, 0x69, 0x4b, 0x79, 0xb0, 0xd7, 0x83, 0x2d, 0x8f, 0xfa, 0x5b,
- 0xae, 0x6d, 0x0e, 0x79, 0x9a, 0xca, 0x6d, 0xfe, 0x6b, 0x0a, 0x04, 0x23, 0x3c, 0x62, 0xc2, 0xa8,
- 0x1f, 0xd0, 0xee, 0x92, 0x13, 0x50, 0x6f, 0x47, 0xb7, 0x87, 0x3c, 0x44, 0x39, 0x29, 0xec, 0x22,
- 0x11, 0x0e, 0x26, 0x50, 0x89, 0x0e, 0x0d, 0xcb, 0x31, 0xc4, 0x00, 0x69, 0xee, 0x0e, 0xd9, 0x91,
- 0x4e, 0xb0, 0x1d, 0xc7, 0x52, 0x04, 0x83, 0x71, 0x4c, 0xed, 0xfb, 0x45, 0x38, 0xc3, 0xf9, 0xb1,
- 0x6a, 0x24, 0xfc, 0x31, 0xc9, 0xff, 0xef, 0xbb, 0x07, 0xf7, 0xbf, 0x0f, 0xc6, 0x5a, 0x5c, 0xa3,
- 0x5a, 0xa1, 0x81, 0x1e, 0xe9, 0x73, 0x51, 0x5a, 0xec, 0xf2, 0x5b, 0x0f, 0xca, 0x3e, 0x9b, 0xaf,
- 0x84, 0xf4, 0x5a, 0x43, 0x77, 0xa1, 0xec, 0x0f, 0xe0, 0xb3, 0x57, 0x78, 0x6a, 0xcc, 0x67, 0x2d,
- 0xce, 0x8e, 0xfc, 0x2c, 0x8c, 0xf8, 0x81, 0x1e, 0xf4, 0xd4, 0xd0, 0x5c, 0x3f, 0x6a, 0xc6, 0x1c,
- 0x3c, 0x9a, 0x47, 0xc4, 0x3b, 0x4a, 0xa6, 0xda, 0xf7, 0x0b, 0x70, 0x2e, 0xbb, 0xe0, 0xb2, 0xe5,
- 0x07, 0xe4, 0xff, 0xf5, 0x89, 0xfd, 0x80, 0x2d, 0xce, 0x4a, 0x73, 0xa1, 0x87, 0x0e, 0xd9, 0x2a,
- 0x25, 0x26, 0xf2, 0x00, 0x2a, 0x56, 0x40, 0x3b, 0x6a, 0x7f, 0x79, 0xfd, 0x88, 0x3f, 0x3d, 0xb6,
- 0xb4, 0x33, 0x2e, 0x28, 0x98, 0x69, 0x6f, 0x15, 0x07, 0x7d, 0x32, 0x5f, 0x3e, 0xec, 0xa4, 0xcf,
- 0xef, 0xd5, 0x7c, 0x3e, 0xbf, 0xc9, 0x0a, 0xf5, 0xbb, 0xfe, 0xfe, 0x4c, 0xbf, 0xeb, 0xef, 0xf5,
- 0xfc, 0xae, 0xbf, 0x29, 0x31, 0x0c, 0xf4, 0x00, 0x7e, 0xa7, 0x04, 0xe7, 0xef, 0xd4, 0x6d, 0xd8,
- 0x7a, 0x26, 0x7b, 0x67, 0xde, 0xf5, 0xec, 0xce, 0xfd, 0x90, 0xcc, 0x40, 0xa5, 0xbb, 0xa5, 0xfb,
- 0x4a, 0x29, 0x53, 0x1b, 0x96, 0xca, 0x2a, 0x4b, 0xbc, 0xcd, 0x26, 0x0d, 0xae, 0xcc, 0xf1, 0x57,
- 0x14, 0xa4, 0x6c, 0x3a, 0xee, 0x50, 0xdf, 0x8f, 0x6c, 0x02, 0xe1, 0x74, 0xbc, 0x22, 0x92, 0x51,
- 0xe5, 0x93, 0x00, 0x46, 0x84, 0x89, 0x59, 0xae, 0x4c, 0xc3, 0x3b, 0x72, 0x65, 0xb8, 0x89, 0x47,
- 0x1f, 0x25, 0x4f, 0x2b, 0x24, 0x2f, 0x32, 0x05, 0xe5, 0x20, 0x72, 0xda, 0x55, 0x5b, 0xf3, 0x72,
- 0x86, 0x7e, 0xca, 0xe9, 0xd8, 0xc6, 0xde, 0xdd, 0xe0, 0x46, 0x75, 0x53, 0x9e, 0x9f, 0x5b, 0xae,
- 0xc3, 0x15, 0xb2, 0x52, 0xb4, 0xb1, 0xbf, 0xde, 0x47, 0x81, 0x19, 0xa5, 0xb4, 0xbf, 0xae, 0xc1,
- 0x99, 0xec, 0xfe, 0xc0, 0xe4, 0xb6, 0x43, 0x3d, 0x9f, 0x61, 0x17, 0x92, 0x72, 0xbb, 0x21, 0x92,
- 0x51, 0xe5, 0xbf, 0xa7, 0x1d, 0xce, 0xbe, 0x51, 0x80, 0xb3, 0x9e, 0x3c, 0x23, 0xba, 0x17, 0x4e,
- 0x67, 0x8f, 0x0a, 0x73, 0xc6, 0x00, 0x86, 0x38, 0xb8, 0x2e, 0xe4, 0x37, 0x0b, 0x30, 0xd1, 0x49,
- 0xd9, 0x39, 0x8e, 0xf1, 0xc2, 0x18, 0xf7, 0x8a, 0x5f, 0x19, 0xc0, 0x0f, 0x07, 0xd6, 0x84, 0x7c,
- 0x01, 0x1a, 0x5d, 0xd6, 0x2f, 0xfc, 0x80, 0x3a, 0x86, 0xba, 0x33, 0x36, 0xfc, 0x48, 0x5a, 0x8d,
- 0xb0, 0x94, 0x2b, 0x9a, 0xd0, 0x0f, 0x62, 0x19, 0x18, 0xe7, 0x78, 0x9f, 0xdf, 0x10, 0xbb, 0x04,
- 0x35, 0x9f, 0x06, 0x81, 0xe5, 0xb4, 0xc5, 0x7e, 0xa3, 0x2e, 0xc6, 0x4a, 0x4b, 0xa6, 0x61, 0x98,
- 0x4b, 0x3e, 0x04, 0x75, 0x7e, 0xe4, 0x34, 0xeb, 0xb5, 0xfd, 0x89, 0x3a, 0x77, 0x17, 0x1b, 0x13,
- 0x0e, 0x70, 0x32, 0x11, 0xa3, 0x7c, 0xf2, 0x14, 0x8c, 0x6e, 0xf0, 0xe1, 0x2b, 0xaf, 0xf3, 0x0a,
- 0x1b, 0x17, 0xd7, 0xd6, 0x9a, 0xb1, 0x74, 0x4c, 0x50, 0x91, 0x19, 0x00, 0x1a, 0x9e, 0xcb, 0xa5,
- 0xed, 0x59, 0xd1, 0x89, 0x1d, 0xc6, 0xa8, 0xc8, 0xa3, 0x50, 0x0a, 0x6c, 0x9f, 0xdb, 0xb0, 0x6a,
- 0xd1, 0x16, 0x74, 0x6d, 0xb9, 0x85, 0x2c, 0x5d, 0xfb, 0x69, 0x01, 0x4e, 0xa4, 0x2e, 0x97, 0xb0,
- 0x22, 0x3d, 0xcf, 0x96, 0xd3, 0x48, 0x58, 0x64, 0x1d, 0x97, 0x91, 0xa5, 0x93, 0x57, 0xa5, 0x5a,
- 0x5e, 0xcc, 0x19, 0xb9, 0xe0, 0x9a, 0x1e, 0xf8, 0x4c, 0x0f, 0xef, 0xd3, 0xc8, 0xf9, 0x31, 0x5f,
- 0x54, 0x1f, 0xb9, 0x0e, 0xc4, 0x8e, 0xf9, 0xa2, 0x3c, 0x4c, 0x50, 0xa6, 0x0c, 0x7e, 0xe5, 0x83,
- 0x18, 0xfc, 0xb4, 0xaf, 0x16, 0x63, 0x12, 0x90, 0x9a, 0xfd, 0x5d, 0x24, 0xf0, 0x04, 0x5b, 0x40,
- 0xc3, 0xc5, 0xbd, 0x1e, 0x5f, 0xff, 0xf8, 0x62, 0x2c, 0x73, 0xc9, 0x4b, 0x42, 0xf6, 0xa5, 0x9c,
- 0xb7, 0x50, 0xd7, 0x96, 0x5b, 0xc2, 0xbb, 0x4a, 0xb5, 0x5a, 0xd8, 0x04, 0xe5, 0x63, 0x6a, 0x02,
- 0xed, 0xcf, 0x4b, 0xd0, 0x78, 0xd1, 0xdd, 0x78, 0x8f, 0x78, 0x50, 0x67, 0x2f, 0x53, 0xc5, 0x77,
- 0x71, 0x99, 0x5a, 0x87, 0x87, 0x83, 0xc0, 0x6e, 0x51, 0xc3, 0x75, 0x4c, 0x7f, 0x76, 0x33, 0xa0,
- 0xde, 0x82, 0xe5, 0x58, 0xfe, 0x16, 0x35, 0xe5, 0x71, 0xd2, 0x23, 0xfb, 0x7b, 0x93, 0x0f, 0xaf,
- 0xad, 0x2d, 0x67, 0x91, 0xe0, 0xa0, 0xb2, 0x7c, 0xda, 0xd0, 0x8d, 0x6d, 0x77, 0x73, 0x93, 0xdf,
- 0x94, 0x91, 0x3e, 0x37, 0x62, 0xda, 0x88, 0xa5, 0x63, 0x82, 0x4a, 0x7b, 0xbb, 0x08, 0xf5, 0xf0,
- 0xe6, 0x3b, 0x79, 0x1c, 0xaa, 0x1b, 0x9e, 0xbb, 0x4d, 0x3d, 0x71, 0x72, 0x27, 0x6f, 0xca, 0x34,
- 0x45, 0x12, 0xaa, 0x3c, 0xf2, 0x18, 0x54, 0x02, 0xb7, 0x6b, 0x19, 0x69, 0x83, 0xda, 0x1a, 0x4b,
- 0x44, 0x91, 0x77, 0x7c, 0x1d, 0xfc, 0x89, 0x84, 0x6a, 0x57, 0x1f, 0xa8, 0x8c, 0xbd, 0x02, 0x65,
- 0x5f, 0xf7, 0x6d, 0xb9, 0x9e, 0xe6, 0xb8, 0x44, 0x3e, 0xdb, 0x5a, 0x96, 0x97, 0xc8, 0x67, 0x5b,
- 0xcb, 0xc8, 0x41, 0xb5, 0x1f, 0x15, 0xa1, 0x21, 0xe4, 0x26, 0x66, 0x85, 0xa3, 0x94, 0xdc, 0xf3,
- 0xdc, 0x95, 0xc2, 0xef, 0x75, 0xa8, 0xc7, 0xcd, 0x4c, 0x72, 0x92, 0x8b, 0x9f, 0x0f, 0x44, 0x99,
- 0xa1, 0x3b, 0x45, 0x94, 0xa4, 0x44, 0x5f, 0x3e, 0x46, 0xd1, 0x57, 0x0e, 0x24, 0xfa, 0x91, 0xe3,
- 0x10, 0xfd, 0x9b, 0x45, 0xa8, 0x2f, 0x5b, 0x9b, 0xd4, 0xd8, 0x35, 0x6c, 0x7e, 0x27, 0xd0, 0xa4,
- 0x36, 0x0d, 0xe8, 0xa2, 0xa7, 0x1b, 0x74, 0x95, 0x7a, 0x16, 0x8f, 0xd9, 0xc2, 0xc6, 0x07, 0x9f,
- 0x81, 0xe4, 0x9d, 0xc0, 0xf9, 0x01, 0x34, 0x38, 0xb0, 0x34, 0x59, 0x82, 0x51, 0x93, 0xfa, 0x96,
- 0x47, 0xcd, 0xd5, 0xd8, 0x46, 0xe5, 0x71, 0xb5, 0xd4, 0xcc, 0xc7, 0xf2, 0x6e, 0xef, 0x4d, 0x8e,
- 0x29, 0x03, 0xa5, 0xd8, 0xb1, 0x24, 0x8a, 0xb2, 0x21, 0xdf, 0xd5, 0x7b, 0x7e, 0x56, 0x1d, 0x63,
- 0x43, 0x7e, 0x35, 0x9b, 0x04, 0x07, 0x95, 0xd5, 0x2a, 0x50, 0x5a, 0x76, 0xdb, 0xda, 0x5b, 0x25,
- 0x08, 0x83, 0xfb, 0x90, 0x9f, 0x2b, 0x40, 0x43, 0x77, 0x1c, 0x37, 0x90, 0x81, 0x73, 0xc4, 0x09,
- 0x3c, 0xe6, 0x8e, 0x21, 0x34, 0x35, 0x1b, 0x81, 0x8a, 0xc3, 0xdb, 0xf0, 0x40, 0x39, 0x96, 0x83,
- 0x71, 0xde, 0xa4, 0x97, 0x3a, 0x4f, 0x5e, 0xc9, 0x5f, 0x8b, 0x03, 0x9c, 0x1e, 0x9f, 0xfb, 0x24,
- 0x9c, 0x4c, 0x57, 0xf6, 0x30, 0xc7, 0x41, 0xb9, 0x0e, 0xe6, 0x8b, 0x00, 0x91, 0x4f, 0xc9, 0x3d,
- 0x30, 0x62, 0x59, 0x09, 0x23, 0xd6, 0xe2, 0xf0, 0x02, 0x0e, 0x2b, 0x3d, 0xd0, 0x70, 0xf5, 0x7a,
- 0xca, 0x70, 0xb5, 0x74, 0x14, 0xcc, 0xee, 0x6c, 0xac, 0xfa, 0xad, 0x02, 0x9c, 0x8c, 0x88, 0xe5,
- 0x0d, 0xd9, 0x67, 0x61, 0xcc, 0xa3, 0xba, 0xd9, 0xd4, 0x03, 0x63, 0x8b, 0xbb, 0x7a, 0x17, 0xb8,
- 0x6f, 0xf6, 0xa9, 0xfd, 0xbd, 0xc9, 0x31, 0x8c, 0x67, 0x60, 0x92, 0x8e, 0xe8, 0xd0, 0x60, 0x09,
- 0x6b, 0x56, 0x87, 0xba, 0xbd, 0x60, 0x48, 0xab, 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c,
- 0xed, 0x9d, 0x02, 0x8c, 0xc7, 0x2b, 0x7c, 0xec, 0x16, 0xb5, 0xad, 0xa4, 0x45, 0x6d, 0xee, 0x08,
- 0xda, 0x64, 0x80, 0x15, 0xed, 0xc7, 0xb5, 0xf8, 0xa7, 0x71, 0xcb, 0x59, 0xdc, 0x58, 0x50, 0xb8,
- 0xa3, 0xb1, 0xe0, 0xbd, 0x1f, 0x35, 0x66, 0x90, 0x96, 0x5b, 0xbe, 0x8f, 0xb5, 0xdc, 0x77, 0x33,
- 0xf4, 0x4c, 0x2c, 0x7c, 0xca, 0x48, 0x8e, 0xf0, 0x29, 0x9d, 0x30, 0x7c, 0x4a, 0xf5, 0xc8, 0x26,
- 0x9d, 0x83, 0x84, 0x50, 0xa9, 0xdd, 0xd3, 0x10, 0x2a, 0xf5, 0xe3, 0x0a, 0xa1, 0x02, 0x79, 0x43,
- 0xa8, 0x7c, 0xa5, 0x00, 0xe3, 0x66, 0xe2, 0xc6, 0x2c, 0xb7, 0x2d, 0xe4, 0x59, 0x6a, 0x92, 0x17,
- 0x70, 0xc5, 0x95, 0xa9, 0x64, 0x1a, 0xa6, 0x58, 0x6a, 0x3f, 0x2c, 0xc7, 0xd7, 0x81, 0x7b, 0x6d,
- 0xaa, 0x7e, 0x26, 0x69, 0xaa, 0xbe, 0x98, 0x36, 0x55, 0x9f, 0x88, 0x79, 0x91, 0xc6, 0xcd, 0xd5,
- 0x1f, 0x8e, 0x4d, 0x8f, 0x6c, 0x4e, 0x1a, 0x8b, 0x24, 0x9d, 0x31, 0x45, 0x7e, 0x18, 0x6a, 0xbe,
- 0x0a, 0xc3, 0x28, 0x36, 0x36, 0x51, 0xbb, 0xa8, 0x10, 0x89, 0x21, 0x05, 0xd3, 0xc4, 0x3d, 0xaa,
- 0xfb, 0xae, 0x93, 0xd6, 0xc4, 0x91, 0xa7, 0xa2, 0xcc, 0x8d, 0x9b, 0xcc, 0x47, 0xee, 0x62, 0x32,
- 0xd7, 0xa1, 0x61, 0xeb, 0x7e, 0xb0, 0xde, 0x35, 0xf5, 0x80, 0x9a, 0x72, 0xbc, 0xfd, 0xaf, 0x83,
- 0xad, 0x55, 0x6c, 0xfd, 0x8b, 0x14, 0xc2, 0xe5, 0x08, 0x06, 0xe3, 0x98, 0xc4, 0x84, 0x51, 0xf6,
- 0xca, 0x47, 0x83, 0x39, 0xab, 0x42, 0x00, 0x1c, 0x86, 0x47, 0x68, 0xe9, 0x59, 0x8e, 0xe1, 0x60,
- 0x02, 0x75, 0x80, 0x55, 0xbd, 0x3e, 0x94, 0x55, 0xfd, 0x2b, 0x75, 0x68, 0x5c, 0xd3, 0x03, 0x6b,
- 0x87, 0xf2, 0x53, 0x9c, 0xe3, 0x31, 0xa5, 0xff, 0x6a, 0x01, 0xce, 0x24, 0x5d, 0xf5, 0x8e, 0xd1,
- 0x9e, 0xce, 0x03, 0x7f, 0x60, 0x26, 0x37, 0x1c, 0x50, 0x0b, 0x6e, 0x59, 0xef, 0xf3, 0xfc, 0x3b,
- 0x6e, 0xcb, 0x7a, 0x6b, 0x10, 0x43, 0x1c, 0x5c, 0x97, 0xf7, 0x8a, 0x65, 0xfd, 0xfe, 0x0e, 0xcc,
- 0x96, 0xb2, 0xfb, 0x57, 0xef, 0x1b, 0xbb, 0x7f, 0xed, 0xbe, 0x50, 0xb6, 0xba, 0x31, 0xbb, 0x7f,
- 0x3d, 0xa7, 0xff, 0x89, 0xf4, 0x6e, 0x17, 0x68, 0x83, 0xce, 0x0f, 0xf8, 0xc5, 0x74, 0x65, 0x8f,
- 0x65, 0x3a, 0xca, 0x86, 0xee, 0x5b, 0x86, 0x5c, 0xf6, 0x72, 0x04, 0xa2, 0x54, 0x11, 0xbb, 0xc4,
- 0x31, 0x35, 0x7f, 0x45, 0x81, 0x1d, 0x45, 0x06, 0x2b, 0xe6, 0x8a, 0x0c, 0x46, 0xe6, 0xa0, 0xec,
- 0xb0, 0xdd, 0x73, 0xe9, 0xd0, 0xb1, 0xc0, 0xae, 0x5d, 0xa5, 0xbb, 0xc8, 0x0b, 0x6b, 0x6f, 0x17,
- 0x01, 0xd8, 0xe7, 0x1f, 0xcc, 0x02, 0xff, 0x41, 0xa8, 0xfa, 0x3d, 0xbe, 0x57, 0x96, 0x0b, 0x76,
- 0xe4, 0xb4, 0x23, 0x92, 0x51, 0xe5, 0x93, 0xc7, 0xa0, 0xf2, 0x7a, 0x8f, 0xf6, 0xd4, 0x71, 0x72,
- 0xa8, 0xae, 0x7d, 0x8a, 0x25, 0xa2, 0xc8, 0x3b, 0x3e, 0x6b, 0x9a, 0xb2, 0xd4, 0x57, 0x8e, 0xcb,
- 0x52, 0x5f, 0x87, 0xea, 0x35, 0x97, 0xfb, 0x00, 0x6a, 0xff, 0x52, 0x04, 0x88, 0x7c, 0xac, 0xc8,
- 0xd7, 0x0b, 0xf0, 0x50, 0x38, 0xe0, 0x02, 0xa1, 0x75, 0xcf, 0xd9, 0xba, 0xd5, 0xc9, 0x6d, 0xb5,
- 0xcf, 0x1a, 0xec, 0x7c, 0x06, 0x5a, 0xcd, 0x62, 0x87, 0xd9, 0xb5, 0x20, 0x08, 0x35, 0xda, 0xe9,
- 0x06, 0xbb, 0xf3, 0x96, 0x27, 0x7b, 0x60, 0xa6, 0x2b, 0xdf, 0x65, 0x49, 0x23, 0x8a, 0xca, 0xad,
- 0x21, 0x1f, 0x44, 0x2a, 0x07, 0x43, 0x1c, 0xb2, 0x05, 0x35, 0xc7, 0x7d, 0xd5, 0x67, 0xe2, 0x90,
- 0xdd, 0xf1, 0x85, 0xe1, 0x45, 0x2e, 0xc4, 0x2a, 0xac, 0xbc, 0xf2, 0x05, 0xab, 0x8e, 0x14, 0xf6,
- 0xd7, 0x8a, 0x70, 0x3a, 0x43, 0x0e, 0xe4, 0x05, 0x38, 0x29, 0xdd, 0xd9, 0xa2, 0xf0, 0xc4, 0x85,
- 0x28, 0x3c, 0x71, 0x2b, 0x95, 0x87, 0x7d, 0xd4, 0xe4, 0x55, 0x00, 0xdd, 0x30, 0xa8, 0xef, 0xaf,
- 0xb8, 0xa6, 0xd2, 0x47, 0x9f, 0xdf, 0xdf, 0x9b, 0x84, 0xd9, 0x30, 0xf5, 0xf6, 0xde, 0xe4, 0x47,
- 0xb2, 0x3c, 0x54, 0x53, 0x72, 0x8e, 0x0a, 0x60, 0x0c, 0x92, 0x7c, 0x0e, 0x40, 0x6c, 0xbd, 0xc2,
- 0x4b, 0xf4, 0x77, 0xb1, 0x57, 0x4c, 0xa9, 0x70, 0x45, 0x53, 0x9f, 0xea, 0xe9, 0x4e, 0x60, 0x05,
- 0xbb, 0x22, 0x66, 0xc9, 0x8d, 0x10, 0x05, 0x63, 0x88, 0xda, 0x9f, 0x14, 0xa1, 0xa6, 0x2c, 0xa5,
- 0xf7, 0xc0, 0x3c, 0xd6, 0x4e, 0x98, 0xc7, 0x8e, 0xc8, 0x27, 0x35, 0xcb, 0x38, 0xe6, 0xa6, 0x8c,
- 0x63, 0x8b, 0xf9, 0x59, 0xdd, 0xd9, 0x34, 0xf6, 0xad, 0x22, 0x8c, 0x2b, 0xd2, 0xbc, 0x86, 0xb1,
- 0x4f, 0xc0, 0x09, 0x71, 0x96, 0xbc, 0xa2, 0xdf, 0x12, 0xe1, 0x5b, 0xb8, 0xc0, 0xca, 0xc2, 0x0d,
- 0xb4, 0x99, 0xcc, 0xc2, 0x34, 0x2d, 0xeb, 0xd6, 0x22, 0x69, 0x9d, 0xed, 0x23, 0xc4, 0xe9, 0x93,
- 0xd8, 0xef, 0xf0, 0x6e, 0xdd, 0x4c, 0xe5, 0x61, 0x1f, 0x75, 0xda, 0x32, 0x57, 0x3e, 0x06, 0xcb,
- 0xdc, 0xdf, 0x14, 0x60, 0x34, 0x92, 0xd7, 0xb1, 0xdb, 0xe5, 0x36, 0x93, 0x76, 0xb9, 0xd9, 0xdc,
- 0xdd, 0x61, 0x80, 0x55, 0xee, 0x17, 0xab, 0x90, 0x70, 0x8d, 0x26, 0x1b, 0x70, 0xce, 0xca, 0x74,
- 0xf0, 0x8a, 0xcd, 0x36, 0xe1, 0x5d, 0xdf, 0xa5, 0x81, 0x94, 0x78, 0x07, 0x14, 0xd2, 0x83, 0xda,
- 0x0e, 0xf5, 0x02, 0xcb, 0xa0, 0xea, 0xfb, 0x16, 0x73, 0xab, 0x64, 0xd2, 0xf6, 0x18, 0xca, 0xf4,
- 0x86, 0x64, 0x80, 0x21, 0x2b, 0xb2, 0x01, 0x15, 0x6a, 0xb6, 0xa9, 0x0a, 0xa8, 0x93, 0x33, 0x5c,
- 0x65, 0x28, 0x4f, 0xf6, 0xe6, 0xa3, 0x80, 0x26, 0x3e, 0xd4, 0x6d, 0x75, 0xb6, 0x24, 0xfb, 0xe1,
- 0xf0, 0x0a, 0x56, 0x78, 0x4a, 0x15, 0xdd, 0xb5, 0x0f, 0x93, 0x30, 0xe2, 0x43, 0xb6, 0x43, 0x23,
- 0x57, 0xe5, 0x88, 0x26, 0x8f, 0x3b, 0x98, 0xb8, 0x7c, 0xa8, 0xdf, 0xd4, 0x03, 0xea, 0x75, 0x74,
- 0x6f, 0x5b, 0xee, 0x36, 0x86, 0xff, 0xc2, 0x97, 0x14, 0x52, 0xf4, 0x85, 0x61, 0x12, 0x46, 0x7c,
- 0x88, 0x0b, 0xf5, 0x40, 0xaa, 0xcf, 0xca, 0x92, 0x37, 0x3c, 0x53, 0xa5, 0x88, 0xfb, 0xd2, 0x45,
- 0x5a, 0xbd, 0x62, 0xc4, 0x83, 0xec, 0x24, 0x42, 0xf9, 0x8a, 0x00, 0xce, 0xcd, 0x1c, 0x16, 0x61,
- 0x09, 0x15, 0x2d, 0x37, 0xd9, 0x21, 0x81, 0xb5, 0xb7, 0x2b, 0xd1, 0xb4, 0x7c, 0xaf, 0xed, 0x54,
- 0x4f, 0x25, 0xed, 0x54, 0x17, 0xd2, 0x76, 0xaa, 0xd4, 0x11, 0xe5, 0xe1, 0x9d, 0x2a, 0x53, 0x16,
- 0xa2, 0xf2, 0x31, 0x58, 0x88, 0x9e, 0x84, 0xc6, 0x0e, 0x9f, 0x09, 0x44, 0x74, 0x9e, 0x0a, 0x5f,
- 0x46, 0xf8, 0xcc, 0x7e, 0x23, 0x4a, 0xc6, 0x38, 0x0d, 0x2b, 0x22, 0x34, 0x90, 0x28, 0xbc, 0xa9,
- 0x2c, 0xd2, 0x8a, 0x92, 0x31, 0x4e, 0xc3, 0xfd, 0xb1, 0x2c, 0x67, 0x5b, 0x14, 0xa8, 0xf2, 0x02,
- 0xc2, 0x1f, 0x4b, 0x25, 0x62, 0x94, 0x4f, 0x2e, 0x41, 0xad, 0x67, 0x6e, 0x0a, 0xda, 0x1a, 0xa7,
- 0xe5, 0x1a, 0xe6, 0xfa, 0xfc, 0x82, 0x8c, 0x16, 0xa4, 0x72, 0x59, 0x4d, 0x3a, 0x7a, 0x57, 0x65,
- 0xf0, 0xbd, 0xa1, 0xac, 0xc9, 0x4a, 0x94, 0x8c, 0x71, 0x1a, 0xf2, 0x31, 0x18, 0xf7, 0xa8, 0xd9,
- 0x33, 0x68, 0x58, 0x0a, 0x78, 0x29, 0x6e, 0x15, 0xc5, 0x44, 0x0e, 0xa6, 0x28, 0x07, 0xd8, 0xb9,
- 0x1a, 0x43, 0xd9, 0xb9, 0xbe, 0x57, 0x00, 0xd2, 0xef, 0xbf, 0x4c, 0xb6, 0x60, 0xc4, 0xe1, 0xd6,
- 0xaf, 0xdc, 0x01, 0x91, 0x63, 0x46, 0x34, 0x31, 0x2d, 0xc9, 0x04, 0x89, 0x4f, 0x1c, 0xa8, 0xd1,
- 0x5b, 0x01, 0xf5, 0x9c, 0xf0, 0x3e, 0xc3, 0xd1, 0x04, 0x5f, 0x16, 0xbb, 0x01, 0x89, 0x8c, 0x21,
- 0x0f, 0xed, 0x07, 0x45, 0x68, 0xc4, 0xe8, 0xee, 0xb6, 0xa9, 0xe4, 0x57, 0xaa, 0x85, 0xd1, 0x69,
- 0xdd, 0xb3, 0xe5, 0x08, 0x8b, 0x5d, 0xa9, 0x96, 0x59, 0xb8, 0x8c, 0x71, 0x3a, 0x32, 0x03, 0xd0,
- 0xd1, 0xfd, 0x80, 0x7a, 0x7c, 0xf5, 0x4d, 0x5d, 0x64, 0x5e, 0x09, 0x73, 0x30, 0x46, 0x45, 0x2e,
- 0xca, 0xf0, 0xd9, 0xe5, 0x64, 0xe0, 0xb9, 0x01, 0xb1, 0xb1, 0x2b, 0x47, 0x10, 0x1b, 0x9b, 0xb4,
- 0xe1, 0xa4, 0xaa, 0xb5, 0xca, 0x3d, 0x5c, 0x58, 0x32, 0xb1, 0x7f, 0x49, 0x41, 0x60, 0x1f, 0xa8,
- 0xf6, 0x76, 0x01, 0xc6, 0x12, 0x26, 0x0f, 0x11, 0x32, 0x4e, 0x79, 0xdf, 0x27, 0x42, 0xc6, 0xc5,
- 0x9c, 0xe6, 0x9f, 0x80, 0x11, 0x21, 0xa0, 0xb4, 0x53, 0x9d, 0x10, 0x21, 0xca, 0x5c, 0x36, 0x97,
- 0x49, 0xa3, 0x6a, 0x7a, 0x2e, 0x93, 0x56, 0x57, 0x54, 0xf9, 0xc2, 0xdc, 0x2e, 0x6a, 0xd7, 0x6f,
- 0x6e, 0x17, 0xe9, 0x18, 0x52, 0x68, 0x3f, 0x2c, 0x01, 0x77, 0x41, 0x21, 0xcf, 0x42, 0xbd, 0x43,
- 0x8d, 0x2d, 0xdd, 0xb1, 0x7c, 0x15, 0x32, 0x92, 0xed, 0x6e, 0xeb, 0x2b, 0x2a, 0xf1, 0x36, 0x03,
- 0x98, 0x6d, 0x2d, 0x73, 0x2f, 0xef, 0x88, 0x96, 0x18, 0x30, 0xd2, 0xf6, 0x7d, 0xbd, 0x6b, 0xe5,
- 0x3e, 0x01, 0x15, 0x21, 0xfa, 0xc4, 0x20, 0x12, 0xcf, 0x28, 0xa1, 0x89, 0x01, 0x95, 0xae, 0xad,
- 0x5b, 0x4e, 0xee, 0x7f, 0x94, 0xb0, 0x2f, 0x58, 0x65, 0x48, 0xc2, 0xa4, 0xc3, 0x1f, 0x51, 0x60,
- 0x93, 0x1e, 0x34, 0x7c, 0xc3, 0xd3, 0x3b, 0xfe, 0x96, 0x3e, 0xf3, 0xf4, 0x33, 0xb9, 0x95, 0xa4,
- 0x88, 0x95, 0x98, 0xb3, 0xe7, 0x70, 0x76, 0xa5, 0x75, 0x65, 0x76, 0xe6, 0xe9, 0x67, 0x30, 0xce,
- 0x27, 0xce, 0xf6, 0xe9, 0x27, 0x67, 0x64, 0xbf, 0x3f, 0x72, 0xb6, 0x4f, 0x3f, 0x39, 0x83, 0x71,
- 0x3e, 0xda, 0x7f, 0x14, 0xa0, 0x1e, 0xd2, 0x92, 0x75, 0x00, 0x36, 0x02, 0x65, 0x50, 0xbd, 0x43,
- 0x05, 0xb8, 0xe7, 0xbb, 0xe2, 0xf5, 0xb0, 0x30, 0xc6, 0x80, 0x32, 0xa2, 0x0e, 0x16, 0x8f, 0x3a,
- 0xea, 0xe0, 0x34, 0xd4, 0xb7, 0x74, 0xc7, 0xf4, 0xb7, 0xf4, 0x6d, 0x31, 0x11, 0xc5, 0xe2, 0x70,
- 0x5e, 0x51, 0x19, 0x18, 0xd1, 0x68, 0xff, 0x5a, 0x01, 0x71, 0x6c, 0xc9, 0x86, 0x8a, 0x69, 0xf9,
- 0xc2, 0x6f, 0xb6, 0xc0, 0x4b, 0x86, 0x43, 0x65, 0x5e, 0xa6, 0x63, 0x48, 0x41, 0xce, 0x42, 0xa9,
- 0x63, 0x39, 0xf2, 0xc4, 0x83, 0x1b, 0xbc, 0x56, 0x2c, 0x07, 0x59, 0x1a, 0xcf, 0xd2, 0x6f, 0x49,
- 0x97, 0x27, 0x91, 0xa5, 0xdf, 0x42, 0x96, 0xc6, 0xb6, 0xa0, 0xb6, 0xeb, 0x6e, 0x6f, 0xe8, 0xc6,
- 0xb6, 0xf2, 0x8c, 0x2a, 0xf3, 0x85, 0x90, 0x6f, 0x41, 0x97, 0x93, 0x59, 0x98, 0xa6, 0x25, 0x8b,
- 0x70, 0xc2, 0x70, 0x5d, 0xdb, 0x74, 0x6f, 0x3a, 0xaa, 0xb8, 0x50, 0x1d, 0xf8, 0x49, 0xc2, 0x3c,
- 0xed, 0x7a, 0xd4, 0x60, 0xfa, 0xc5, 0x5c, 0x92, 0x08, 0xd3, 0xa5, 0xc8, 0x3a, 0x3c, 0xfc, 0x06,
- 0xf5, 0x5c, 0x39, 0x5d, 0xb4, 0x6c, 0x4a, 0xbb, 0x0a, 0x50, 0x28, 0x16, 0xdc, 0x53, 0xeb, 0x33,
- 0xd9, 0x24, 0x38, 0xa8, 0x2c, 0xf7, 0xf9, 0xd4, 0xbd, 0x36, 0x0d, 0x56, 0x3d, 0xd7, 0xa0, 0xbe,
- 0x6f, 0x39, 0x6d, 0x05, 0x5b, 0x8d, 0x60, 0xd7, 0xb2, 0x49, 0x70, 0x50, 0x59, 0xf2, 0x32, 0x4c,
- 0x88, 0x2c, 0xb1, 0x6a, 0xcf, 0xee, 0xe8, 0x96, 0xad, 0x6f, 0x58, 0xb6, 0xfa, 0x27, 0xd7, 0x98,
- 0x38, 0xa0, 0x58, 0x1b, 0x40, 0x83, 0x03, 0x4b, 0xf3, 0x3f, 0x69, 0xc9, 0xe3, 0xa9, 0x55, 0xea,
- 0xf1, 0x7e, 0x20, 0xf5, 0x19, 0xf1, 0x27, 0xad, 0x54, 0x1e, 0xf6, 0x51, 0x13, 0x84, 0x33, 0xfc,
- 0xb8, 0x7b, 0xbd, 0x9b, 0x12, 0xba, 0xd4, 0x70, 0xf8, 0x39, 0x54, 0x2b, 0x93, 0x02, 0x07, 0x94,
- 0x64, 0xdf, 0xcb, 0x73, 0xe6, 0xdd, 0x9b, 0x4e, 0x1a, 0xb5, 0x11, 0x7d, 0x6f, 0x6b, 0x00, 0x0d,
- 0x0e, 0x2c, 0xad, 0xfd, 0x71, 0x11, 0xc6, 0x12, 0x37, 0x9f, 0xef, 0xbb, 0x1b, 0xa6, 0x4c, 0x55,
- 0xec, 0xf8, 0xed, 0xa5, 0xf9, 0x2b, 0x54, 0x37, 0xa9, 0x77, 0x95, 0xaa, 0x5b, 0xea, 0x7c, 0xf4,
- 0xaf, 0x24, 0x72, 0x30, 0x45, 0x49, 0x36, 0xa1, 0x22, 0x0c, 0x9f, 0x79, 0xff, 0x69, 0xa0, 0x64,
- 0xc4, 0xad, 0x9f, 0x7c, 0x6d, 0x10, 0xb6, 0x4f, 0x01, 0xaf, 0x05, 0x30, 0x1a, 0xa7, 0x60, 0x23,
- 0x3e, 0xd2, 0xaa, 0xaa, 0x09, 0x8d, 0x6a, 0x09, 0x4a, 0x41, 0x30, 0xec, 0xdd, 0x55, 0x61, 0x48,
- 0x5f, 0x5b, 0x46, 0x86, 0xa1, 0x6d, 0xb2, 0xb6, 0xf3, 0x7d, 0xcb, 0x75, 0x64, 0x20, 0xe3, 0x75,
- 0xa8, 0x06, 0xd2, 0x96, 0x34, 0xdc, 0xdd, 0x5b, 0x6e, 0xd7, 0x55, 0x76, 0x24, 0x85, 0xa5, 0xfd,
- 0x6d, 0x11, 0xea, 0xe1, 0xbe, 0xef, 0x00, 0x01, 0x82, 0x5d, 0xa8, 0x87, 0x8e, 0x31, 0xb9, 0xff,
- 0x4f, 0x16, 0xf9, 0x6b, 0xf0, 0xad, 0x4a, 0xf8, 0x8a, 0x11, 0x8f, 0xb8, 0xd3, 0x4d, 0x29, 0x87,
- 0xd3, 0x4d, 0x17, 0xaa, 0x81, 0x67, 0xb5, 0xdb, 0x52, 0x09, 0xcd, 0xe3, 0x75, 0x13, 0x8a, 0x6b,
- 0x4d, 0x00, 0x4a, 0xc9, 0x8a, 0x17, 0x54, 0x6c, 0xb4, 0xd7, 0xe0, 0x64, 0x9a, 0x92, 0x6b, 0x68,
- 0xc6, 0x16, 0x35, 0x7b, 0xb6, 0x92, 0x71, 0xa4, 0xa1, 0xc9, 0x74, 0x0c, 0x29, 0xd8, 0x2e, 0x8d,
- 0x35, 0xd3, 0x1b, 0xae, 0xa3, 0xf6, 0xbf, 0x5c, 0xd9, 0x5d, 0x93, 0x69, 0x18, 0xe6, 0x6a, 0xff,
- 0x5c, 0x82, 0xb3, 0xd1, 0xee, 0x7d, 0x45, 0x77, 0xf4, 0xf6, 0x01, 0x7e, 0x4a, 0xf5, 0xfe, 0x6d,
- 0x86, 0xc3, 0x46, 0x79, 0x2f, 0xdd, 0x07, 0x51, 0xde, 0x7f, 0x54, 0x00, 0xee, 0xc4, 0x47, 0xbe,
- 0x00, 0xa3, 0x7a, 0xec, 0x7f, 0x84, 0xb2, 0x39, 0x2f, 0xe7, 0x6e, 0x4e, 0xee, 0x2b, 0x18, 0x3a,
- 0xa5, 0xc4, 0x53, 0x31, 0xc1, 0x90, 0xb8, 0x50, 0xdb, 0xd4, 0x6d, 0x9b, 0x29, 0x2d, 0xb9, 0x4f,
- 0x23, 0x12, 0xcc, 0x79, 0x37, 0x5f, 0x90, 0xd0, 0x18, 0x32, 0xd1, 0xfe, 0xa9, 0x00, 0x63, 0x2d,
- 0xdb, 0x32, 0x2d, 0xa7, 0x7d, 0x8c, 0xe1, 0xdd, 0xaf, 0x43, 0xc5, 0xb7, 0x2d, 0x93, 0x0e, 0x39,
- 0x8f, 0x8b, 0x15, 0x84, 0x01, 0xa0, 0xc0, 0x49, 0xc6, 0x8b, 0x2f, 0x1d, 0x20, 0x5e, 0xfc, 0x4f,
- 0x46, 0x40, 0x3a, 0x82, 0x92, 0x1e, 0xd4, 0xdb, 0x2a, 0x0c, 0xb5, 0xfc, 0xc6, 0x2b, 0x39, 0x42,
- 0x98, 0x25, 0x02, 0x5a, 0x8b, 0x59, 0x37, 0x4c, 0xc4, 0x88, 0x13, 0xa1, 0xc9, 0x5f, 0x50, 0xce,
- 0xe7, 0xfc, 0x05, 0xa5, 0x60, 0xd7, 0xff, 0x13, 0x4a, 0x1d, 0xca, 0x5b, 0x41, 0xd0, 0x95, 0xe3,
- 0x6a, 0x78, 0x4f, 0xdf, 0x28, 0x8a, 0x86, 0xd0, 0x46, 0xd8, 0x3b, 0x72, 0x68, 0xc6, 0xc2, 0xd1,
- 0xc3, 0x3f, 0x1f, 0xcd, 0xe5, 0x3a, 0x91, 0x8e, 0xb3, 0x60, 0xef, 0xc8, 0xa1, 0xc9, 0xe7, 0xa1,
- 0x11, 0x78, 0xba, 0xe3, 0x6f, 0xba, 0x5e, 0x87, 0x7a, 0x72, 0x1b, 0xb7, 0x90, 0xe3, 0x2f, 0x8c,
- 0x6b, 0x11, 0x9a, 0x38, 0xea, 0x4a, 0x24, 0x61, 0x9c, 0x1b, 0xd9, 0x86, 0x5a, 0xcf, 0x14, 0x15,
- 0x93, 0xf6, 0x8d, 0xd9, 0x3c, 0x3f, 0xd6, 0x8c, 0x9d, 0x37, 0xab, 0x37, 0x0c, 0x19, 0x24, 0x7f,
- 0xf2, 0x55, 0x3d, 0xaa, 0x9f, 0x7c, 0xc5, 0x7b, 0x63, 0xd6, 0x15, 0x7f, 0xd2, 0x91, 0x1a, 0xa5,
- 0xd3, 0x96, 0xee, 0x32, 0x0b, 0xb9, 0x95, 0x3d, 0xc1, 0xb2, 0x11, 0x6a, 0xa5, 0x4e, 0x1b, 0x15,
- 0x0f, 0xad, 0x03, 0xd2, 0x0c, 0x4d, 0x8c, 0xc4, 0xaf, 0x30, 0xc4, 0xbd, 0x93, 0xe9, 0x83, 0xcd,
- 0x07, 0xe1, 0x3f, 0x19, 0x62, 0xa1, 0x78, 0x33, 0xff, 0x79, 0xa1, 0xfd, 0x5d, 0x11, 0x4a, 0x6b,
- 0xcb, 0x2d, 0x11, 0x5e, 0x8f, 0xff, 0x67, 0x86, 0xb6, 0xb6, 0xad, 0xee, 0x0d, 0xea, 0x59, 0x9b,
- 0xbb, 0x72, 0x77, 0x1a, 0x0b, 0xaf, 0x97, 0xa6, 0xc0, 0x8c, 0x52, 0xe4, 0x15, 0x18, 0x35, 0xf4,
- 0x39, 0xea, 0x05, 0xc3, 0xec, 0xbd, 0xf9, 0x05, 0xbb, 0xb9, 0xd9, 0xa8, 0x38, 0x26, 0xc0, 0xc8,
- 0x3a, 0x80, 0x11, 0x41, 0x97, 0x0e, 0x6d, 0x31, 0x88, 0x01, 0xc7, 0x80, 0x08, 0x42, 0x7d, 0x9b,
- 0x91, 0x72, 0xd4, 0xf2, 0x61, 0x50, 0x79, 0xcf, 0xb9, 0xaa, 0xca, 0x62, 0x04, 0xa3, 0x39, 0x30,
- 0x96, 0xf8, 0x3f, 0x06, 0xf9, 0x28, 0xd4, 0xdc, 0x6e, 0x6c, 0x3a, 0xad, 0xf3, 0xed, 0x74, 0xed,
- 0xba, 0x4c, 0xbb, 0xbd, 0x37, 0x39, 0xb6, 0xec, 0xb6, 0x2d, 0x43, 0x25, 0x60, 0x48, 0x4e, 0x34,
- 0x18, 0xe1, 0xb7, 0x62, 0xd4, 0xdf, 0x31, 0xf8, 0xda, 0xc1, 0x03, 0xd8, 0xfb, 0x28, 0x73, 0xb4,
- 0x2f, 0x96, 0x21, 0x3a, 0xbc, 0x21, 0x3e, 0x8c, 0x08, 0xaf, 0x5f, 0x39, 0x73, 0x1f, 0xab, 0x83,
- 0xb1, 0x64, 0x45, 0xda, 0x50, 0x7a, 0xcd, 0xdd, 0xc8, 0x3d, 0x71, 0xc7, 0xae, 0xc3, 0x0a, 0x73,
- 0x52, 0x2c, 0x01, 0x19, 0x07, 0xf2, 0x6b, 0x05, 0x38, 0xe5, 0xa7, 0x95, 0x4e, 0xd9, 0x1d, 0x30,
- 0xbf, 0x76, 0x9d, 0x56, 0x63, 0xa5, 0x07, 0xe5, 0xa0, 0x6c, 0xec, 0xaf, 0x0b, 0x93, 0xbf, 0x38,
- 0x55, 0x91, 0xdd, 0x69, 0x31, 0xe7, 0x3f, 0xdd, 0x92, 0xf2, 0x4f, 0xa6, 0xa1, 0x64, 0xa5, 0x7d,
- 0xb9, 0x08, 0x8d, 0xd8, 0x6c, 0x9d, 0xfb, 0xa7, 0x2b, 0xb7, 0x52, 0x3f, 0x5d, 0x59, 0x1d, 0xfe,
- 0x90, 0x31, 0xaa, 0xd5, 0x71, 0xff, 0x77, 0xe5, 0xcf, 0x8a, 0x50, 0x5a, 0x9f, 0x5f, 0x48, 0x6e,
- 0x17, 0x0b, 0xf7, 0x60, 0xbb, 0xb8, 0x05, 0xd5, 0x8d, 0x9e, 0x65, 0x07, 0x96, 0x93, 0xfb, 0xc2,
- 0xbe, 0xfa, 0x47, 0x8d, 0xbc, 0xf7, 0x2a, 0x50, 0x51, 0xc1, 0x93, 0x36, 0x54, 0xdb, 0x22, 0x62,
- 0x5a, 0x6e, 0xd7, 0x2b, 0x19, 0x79, 0x4d, 0x30, 0x92, 0x2f, 0xa8, 0xd0, 0xb5, 0x5d, 0x90, 0x7f,
- 0xb9, 0xbe, 0xe7, 0xd2, 0xd4, 0x3e, 0x0f, 0xa1, 0x16, 0x70, 0xef, 0x99, 0xff, 0x5b, 0x01, 0x92,
- 0x8a, 0xcf, 0xbd, 0xef, 0x4d, 0xdb, 0xe9, 0xde, 0x34, 0x7f, 0x14, 0x83, 0x2f, 0xbb, 0x43, 0x69,
- 0x7f, 0x58, 0x84, 0x91, 0x7b, 0x76, 0xc9, 0x92, 0x26, 0xbc, 0xc8, 0xe6, 0x72, 0x4e, 0x8c, 0x03,
- 0x7d, 0xc8, 0x3a, 0x29, 0x1f, 0xb2, 0xbc, 0x7f, 0xd5, 0xbc, 0x8b, 0x07, 0xd9, 0x5f, 0x15, 0x40,
- 0x4e, 0xcb, 0x4b, 0x8e, 0x1f, 0xe8, 0x8e, 0xc1, 0x7f, 0xee, 0x2e, 0xd7, 0x80, 0xbc, 0xae, 0x0a,
- 0xd2, 0x9d, 0x47, 0x2c, 0xfb, 0xfc, 0x59, 0xcd, 0xf9, 0xe4, 0xc3, 0x50, 0xdb, 0x72, 0xfd, 0x80,
- 0xcf, 0xf3, 0xc5, 0xa4, 0x5d, 0xe7, 0x8a, 0x4c, 0xc7, 0x90, 0x22, 0x7d, 0xa4, 0x57, 0x19, 0x7c,
- 0xa4, 0xa7, 0x7d, 0xb3, 0x08, 0xa3, 0xef, 0x95, 0x9b, 0xa2, 0x59, 0x3e, 0x77, 0xa5, 0x9c, 0x3e,
- 0x77, 0xe5, 0xc3, 0xf8, 0xdc, 0x69, 0xdf, 0x29, 0x00, 0xdc, 0xb3, 0x6b, 0xaa, 0x66, 0xd2, 0x1d,
- 0x2e, 0x77, 0xbf, 0xca, 0x76, 0x86, 0xfb, 0xbd, 0x8a, 0xfa, 0x24, 0xee, 0x0a, 0xf7, 0x66, 0x01,
- 0xc6, 0xf5, 0x84, 0x7b, 0x59, 0x6e, 0xd5, 0x32, 0xe5, 0xad, 0x16, 0x5e, 0xc9, 0x4b, 0xa6, 0x63,
- 0x8a, 0x2d, 0x79, 0x2e, 0x0a, 0x91, 0x7a, 0x2d, 0xea, 0xf6, 0x7d, 0xb1, 0x4d, 0xb9, 0x9a, 0x93,
- 0xa0, 0xbc, 0x8b, 0x3b, 0x5f, 0xe9, 0x48, 0xdc, 0xf9, 0xe2, 0x17, 0x95, 0xca, 0x77, 0xbc, 0xa8,
- 0xb4, 0x03, 0xf5, 0x4d, 0xcf, 0xed, 0x70, 0x8f, 0x39, 0xf9, 0x3f, 0xce, 0xcb, 0x39, 0xd6, 0x94,
- 0xe8, 0x4f, 0xd4, 0x91, 0x8d, 0x67, 0x41, 0xe1, 0x63, 0xc4, 0x8a, 0x1b, 0xa4, 0x5d, 0xc1, 0x75,
- 0xe4, 0x28, 0xb9, 0x86, 0x73, 0xc9, 0x9a, 0x40, 0x47, 0xc5, 0x26, 0xe9, 0x25, 0x57, 0xbd, 0x37,
- 0x5e, 0x72, 0xda, 0x2f, 0x94, 0xd5, 0x04, 0x76, 0xdf, 0x45, 0xe3, 0x7b, 0xef, 0x5f, 0x6f, 0x4c,
- 0xdf, 0x3d, 0xac, 0xde, 0xc3, 0xbb, 0x87, 0xb5, 0xa1, 0x7c, 0xb2, 0xf6, 0x4a, 0x90, 0xda, 0x37,
- 0xbd, 0x7f, 0x3a, 0xf1, 0x5f, 0xea, 0x74, 0xe2, 0xad, 0x22, 0x44, 0x13, 0xc1, 0x21, 0xdd, 0x2c,
- 0x5e, 0x86, 0x5a, 0x47, 0xbf, 0x35, 0x4f, 0x6d, 0x7d, 0x37, 0xcf, 0x4f, 0x14, 0x57, 0x24, 0x06,
- 0x86, 0x68, 0xc4, 0x07, 0xb0, 0xc2, 0x40, 0xc6, 0xb9, 0xad, 0xcd, 0x51, 0x4c, 0x64, 0x61, 0xcf,
- 0x8a, 0xde, 0x31, 0xc6, 0x46, 0xfb, 0xcb, 0x22, 0xc8, 0x88, 0xd7, 0x84, 0x42, 0x65, 0xd3, 0xba,
- 0x45, 0xcd, 0xdc, 0x2e, 0x87, 0xb1, 0x5f, 0xdb, 0x0a, 0x73, 0x3a, 0x4f, 0x40, 0x81, 0xce, 0xed,
- 0xa4, 0xe2, 0x78, 0x44, 0xca, 0x2f, 0x87, 0x9d, 0x34, 0x7e, 0xcc, 0x22, 0xed, 0xa4, 0x22, 0x09,
- 0x15, 0x0f, 0x61, 0x96, 0xe5, 0x67, 0xd4, 0x52, 0xa4, 0x79, 0xcc, 0xb2, 0xb1, 0xb3, 0x6e, 0x65,
- 0x96, 0xf5, 0xc5, 0xe5, 0x63, 0xc9, 0xa3, 0xf9, 0xd9, 0x6f, 0x7f, 0xf7, 0xc2, 0x03, 0xdf, 0xf9,
- 0xee, 0x85, 0x07, 0xde, 0xf9, 0xee, 0x85, 0x07, 0xbe, 0xb8, 0x7f, 0xa1, 0xf0, 0xed, 0xfd, 0x0b,
- 0x85, 0xef, 0xec, 0x5f, 0x28, 0xbc, 0xb3, 0x7f, 0xa1, 0xf0, 0x0f, 0xfb, 0x17, 0x0a, 0xbf, 0xfc,
- 0x8f, 0x17, 0x1e, 0xf8, 0xcc, 0xb3, 0x51, 0x15, 0xa6, 0x55, 0x15, 0xa6, 0x15, 0xc3, 0xe9, 0xee,
- 0x76, 0x7b, 0x9a, 0x55, 0x21, 0x4a, 0x51, 0x55, 0xf8, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x12,
- 0x35, 0xa0, 0x09, 0x68, 0x92, 0x00, 0x00,
+ // 7439 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x1d, 0xd7,
+ 0x75, 0xb6, 0xcf, 0x8d, 0x3c, 0x67, 0x1d, 0x92, 0xa2, 0xb6, 0x64, 0x99, 0x92, 0x65, 0x51, 0x19,
+ 0xff, 0xf6, 0xaf, 0xfc, 0x49, 0xc8, 0xdf, 0xfc, 0x7d, 0x4b, 0xfe, 0x24, 0x36, 0x0f, 0x29, 0x52,
+ 0x94, 0x48, 0x89, 0x59, 0x87, 0x94, 0x9d, 0xf8, 0x4f, 0xfc, 0x0f, 0x67, 0x36, 0x0f, 0xc7, 0x9c,
+ 0x33, 0x73, 0x3c, 0x33, 0x87, 0x12, 0x9d, 0x16, 0xb9, 0x3d, 0xd8, 0x45, 0x5b, 0xb4, 0xc8, 0x53,
+ 0x80, 0x22, 0x2d, 0x5a, 0x14, 0xc8, 0x43, 0x90, 0x3e, 0x14, 0x70, 0x1f, 0x0a, 0xf4, 0x8a, 0xa2,
+ 0x4d, 0x8b, 0x5e, 0xf2, 0x50, 0xa0, 0x2e, 0x0a, 0x10, 0x0d, 0x8b, 0x3e, 0xb4, 0x40, 0x83, 0xa0,
+ 0x01, 0x9a, 0x44, 0x08, 0x90, 0x62, 0xdf, 0xe6, 0x76, 0xe6, 0x48, 0xe4, 0x19, 0x52, 0x96, 0x5b,
+ 0xbf, 0xcd, 0xec, 0xbd, 0xf6, 0xb7, 0xf6, 0xac, 0x7d, 0x5b, 0x7b, 0xed, 0xb5, 0xd7, 0xc0, 0x62,
+ 0xcb, 0x0a, 0xb6, 0xba, 0x1b, 0x53, 0x86, 0xdb, 0x9e, 0x76, 0xba, 0x6d, 0xbd, 0xe3, 0xb9, 0xaf,
+ 0xf1, 0x87, 0x4d, 0xdb, 0xbd, 0x35, 0xdd, 0xd9, 0x6e, 0x4d, 0xeb, 0x1d, 0xcb, 0x8f, 0x52, 0x76,
+ 0x9e, 0xd2, 0xed, 0xce, 0x96, 0xfe, 0xd4, 0x74, 0x8b, 0x3a, 0xd4, 0xd3, 0x03, 0x6a, 0x4e, 0x75,
+ 0x3c, 0x37, 0x70, 0xc9, 0x73, 0x11, 0xd0, 0x94, 0x02, 0x9a, 0x52, 0xc5, 0xa6, 0x3a, 0xdb, 0xad,
+ 0x29, 0x06, 0x14, 0xa5, 0x28, 0xa0, 0x73, 0x1f, 0x89, 0xd5, 0xa0, 0xe5, 0xb6, 0xdc, 0x69, 0x8e,
+ 0xb7, 0xd1, 0xdd, 0xe4, 0x6f, 0xfc, 0x85, 0x3f, 0x09, 0x3e, 0xe7, 0xb4, 0xed, 0xe7, 0xfd, 0x29,
+ 0xcb, 0x65, 0xd5, 0x9a, 0x36, 0x5c, 0x8f, 0x4e, 0xef, 0xf4, 0xd4, 0xe5, 0xdc, 0xd3, 0x11, 0x4d,
+ 0x5b, 0x37, 0xb6, 0x2c, 0x87, 0x7a, 0xbb, 0xea, 0x5b, 0xa6, 0x3d, 0xea, 0xbb, 0x5d, 0xcf, 0xa0,
+ 0x87, 0x2a, 0xe5, 0x4f, 0xb7, 0x69, 0xa0, 0x67, 0xf1, 0x9a, 0xee, 0x57, 0xca, 0xeb, 0x3a, 0x81,
+ 0xd5, 0xee, 0x65, 0xf3, 0xec, 0xbd, 0x0a, 0xf8, 0xc6, 0x16, 0x6d, 0xeb, 0xe9, 0x72, 0xda, 0x3f,
+ 0xd4, 0xe0, 0xd4, 0xec, 0x86, 0x1f, 0x78, 0xba, 0x11, 0xac, 0xba, 0xe6, 0x1a, 0x6d, 0x77, 0x6c,
+ 0x3d, 0xa0, 0x64, 0x1b, 0xaa, 0xac, 0x6e, 0xa6, 0x1e, 0xe8, 0x13, 0x85, 0x8b, 0x85, 0x4b, 0xf5,
+ 0x99, 0xd9, 0xa9, 0x01, 0xdb, 0x62, 0x6a, 0x45, 0x02, 0x35, 0x46, 0xf6, 0xf7, 0x26, 0xab, 0xea,
+ 0x0d, 0x43, 0x06, 0xe4, 0x6b, 0x05, 0x18, 0x71, 0x5c, 0x93, 0x36, 0xa9, 0x4d, 0x8d, 0xc0, 0xf5,
+ 0x26, 0x8a, 0x17, 0x4b, 0x97, 0xea, 0x33, 0x9f, 0x1b, 0x98, 0x63, 0xc6, 0x17, 0x4d, 0x5d, 0x8f,
+ 0x31, 0xb8, 0xec, 0x04, 0xde, 0x6e, 0xe3, 0xf4, 0xb7, 0xf7, 0x26, 0x1f, 0xda, 0xdf, 0x9b, 0x1c,
+ 0x89, 0x67, 0x61, 0xa2, 0x26, 0x64, 0x1d, 0xea, 0x81, 0x6b, 0x33, 0x91, 0x59, 0xae, 0xe3, 0x4f,
+ 0x94, 0x78, 0xc5, 0x2e, 0x4c, 0x09, 0x69, 0x33, 0xf6, 0x53, 0xac, 0xbb, 0x4c, 0xed, 0x3c, 0x35,
+ 0xb5, 0x16, 0x92, 0x35, 0x4e, 0x49, 0xe0, 0x7a, 0x94, 0xe6, 0x63, 0x1c, 0x87, 0x50, 0x38, 0xe1,
+ 0x53, 0xa3, 0xeb, 0x59, 0xc1, 0xee, 0x9c, 0xeb, 0x04, 0xf4, 0x76, 0x30, 0x51, 0xe6, 0x52, 0x7e,
+ 0x32, 0x0b, 0x7a, 0xd5, 0x35, 0x9b, 0x49, 0xea, 0xc6, 0xa9, 0xfd, 0xbd, 0xc9, 0x13, 0xa9, 0x44,
+ 0x4c, 0x63, 0x12, 0x07, 0xc6, 0xad, 0xb6, 0xde, 0xa2, 0xab, 0x5d, 0xdb, 0x6e, 0x52, 0xc3, 0xa3,
+ 0x81, 0x3f, 0x51, 0xe1, 0x9f, 0x70, 0x29, 0x8b, 0xcf, 0xb2, 0x6b, 0xe8, 0xf6, 0x8d, 0x8d, 0xd7,
+ 0xa8, 0x11, 0x20, 0xdd, 0xa4, 0x1e, 0x75, 0x0c, 0xda, 0x98, 0x90, 0x1f, 0x33, 0xbe, 0x94, 0x42,
+ 0xc2, 0x1e, 0x6c, 0xb2, 0x08, 0x27, 0x3b, 0x9e, 0xe5, 0xf2, 0x2a, 0xd8, 0xba, 0xef, 0x5f, 0xd7,
+ 0xdb, 0x74, 0x62, 0xe8, 0x62, 0xe1, 0x52, 0xad, 0x71, 0x56, 0xc2, 0x9c, 0x5c, 0x4d, 0x13, 0x60,
+ 0x6f, 0x19, 0x72, 0x09, 0xaa, 0x2a, 0x71, 0x62, 0xf8, 0x62, 0xe1, 0x52, 0x45, 0xf4, 0x1d, 0x55,
+ 0x16, 0xc3, 0x5c, 0xb2, 0x00, 0x55, 0x7d, 0x73, 0xd3, 0x72, 0x18, 0x65, 0x95, 0x8b, 0xf0, 0x7c,
+ 0xd6, 0xa7, 0xcd, 0x4a, 0x1a, 0x81, 0xa3, 0xde, 0x30, 0x2c, 0x4b, 0xae, 0x02, 0xf1, 0xa9, 0xb7,
+ 0x63, 0x19, 0x74, 0xd6, 0x30, 0xdc, 0xae, 0x13, 0xf0, 0xba, 0xd7, 0x78, 0xdd, 0xcf, 0xc9, 0xba,
+ 0x93, 0x66, 0x0f, 0x05, 0x66, 0x94, 0x22, 0x2f, 0xc2, 0xb8, 0x1c, 0x76, 0x91, 0x14, 0x80, 0x23,
+ 0x9d, 0x66, 0x82, 0xc4, 0x54, 0x1e, 0xf6, 0x50, 0x13, 0x13, 0xce, 0xeb, 0xdd, 0xc0, 0x6d, 0x33,
+ 0xc8, 0x24, 0xd3, 0x35, 0x77, 0x9b, 0x3a, 0x13, 0xf5, 0x8b, 0x85, 0x4b, 0xd5, 0xc6, 0xc5, 0xfd,
+ 0xbd, 0xc9, 0xf3, 0xb3, 0x77, 0xa1, 0xc3, 0xbb, 0xa2, 0x90, 0x1b, 0x50, 0x33, 0x1d, 0x7f, 0xd5,
+ 0xb5, 0x2d, 0x63, 0x77, 0x62, 0x84, 0x57, 0xf0, 0x29, 0xf9, 0xa9, 0xb5, 0xf9, 0xeb, 0x4d, 0x91,
+ 0x71, 0x67, 0x6f, 0xf2, 0x7c, 0xef, 0xec, 0x38, 0x15, 0xe6, 0x63, 0x84, 0x41, 0x56, 0x38, 0xe0,
+ 0x9c, 0xeb, 0x6c, 0x5a, 0xad, 0x89, 0x51, 0xde, 0x1a, 0x17, 0xfb, 0x74, 0xe8, 0xf9, 0xeb, 0x4d,
+ 0x41, 0xd7, 0x18, 0x95, 0xec, 0xc4, 0x2b, 0x46, 0x08, 0xe7, 0x5e, 0x80, 0x93, 0x3d, 0xa3, 0x96,
+ 0x8c, 0x43, 0x69, 0x9b, 0xee, 0xf2, 0x49, 0xa9, 0x86, 0xec, 0x91, 0x9c, 0x86, 0xca, 0x8e, 0x6e,
+ 0x77, 0xe9, 0x44, 0x91, 0xa7, 0x89, 0x97, 0x8f, 0x15, 0x9f, 0x2f, 0x68, 0xbf, 0x51, 0x82, 0x11,
+ 0x35, 0x17, 0x34, 0x2d, 0x67, 0x9b, 0xbc, 0x04, 0x25, 0xdb, 0x6d, 0xc9, 0x19, 0xed, 0xe3, 0x03,
+ 0xcf, 0x2f, 0xcb, 0x6e, 0xab, 0x31, 0xbc, 0xbf, 0x37, 0x59, 0x5a, 0x76, 0x5b, 0xc8, 0x10, 0x89,
+ 0x01, 0x95, 0x6d, 0x7d, 0x73, 0x5b, 0xe7, 0x75, 0xa8, 0xcf, 0x34, 0x06, 0x86, 0xbe, 0xc6, 0x50,
+ 0x58, 0x5d, 0x1b, 0xb5, 0xfd, 0xbd, 0xc9, 0x0a, 0x7f, 0x45, 0x81, 0x4d, 0x5c, 0xa8, 0x6d, 0xd8,
+ 0xba, 0xb1, 0xbd, 0xe5, 0xda, 0x74, 0xa2, 0x94, 0x93, 0x51, 0x43, 0x21, 0x89, 0x06, 0x08, 0x5f,
+ 0x31, 0xe2, 0x41, 0x0c, 0x18, 0xea, 0x9a, 0xbe, 0xe5, 0x6c, 0xcb, 0xd9, 0xe9, 0x85, 0x81, 0xb9,
+ 0xad, 0xcf, 0xf3, 0x6f, 0x82, 0xfd, 0xbd, 0xc9, 0x21, 0xf1, 0x8c, 0x12, 0x5a, 0xfb, 0x5e, 0x1d,
+ 0xc6, 0x54, 0x23, 0xdd, 0xa4, 0x5e, 0x40, 0x6f, 0x93, 0x8b, 0x50, 0x76, 0xd8, 0xa0, 0xe1, 0x8d,
+ 0xdc, 0x18, 0x91, 0x7d, 0xb2, 0xcc, 0x07, 0x0b, 0xcf, 0x61, 0x35, 0x13, 0x0b, 0xae, 0x14, 0xf8,
+ 0xe0, 0x35, 0x6b, 0x72, 0x18, 0x51, 0x33, 0xf1, 0x8c, 0x12, 0x9a, 0xbc, 0x02, 0x65, 0xfe, 0xf1,
+ 0x42, 0xd4, 0x9f, 0x18, 0x9c, 0x05, 0xfb, 0xf4, 0x2a, 0xfb, 0x02, 0xfe, 0xe1, 0x1c, 0x94, 0x75,
+ 0xc5, 0xae, 0xb9, 0x29, 0x05, 0xfb, 0xf1, 0x1c, 0x82, 0x5d, 0x10, 0x5d, 0x71, 0x7d, 0x7e, 0x01,
+ 0x19, 0x22, 0xf9, 0xa5, 0x02, 0x9c, 0x34, 0x5c, 0x27, 0xd0, 0x99, 0x12, 0xa0, 0x96, 0xbf, 0x89,
+ 0x0a, 0xe7, 0x73, 0x75, 0x60, 0x3e, 0x73, 0x69, 0xc4, 0xc6, 0xc3, 0x6c, 0x36, 0xef, 0x49, 0xc6,
+ 0x5e, 0xde, 0xe4, 0x57, 0x0a, 0xf0, 0x30, 0x9b, 0x65, 0x7b, 0x88, 0xf9, 0xda, 0x70, 0xb4, 0xb5,
+ 0x3a, 0xbb, 0xbf, 0x37, 0xf9, 0xf0, 0x52, 0x16, 0x33, 0xcc, 0xae, 0x03, 0xab, 0xdd, 0x29, 0xbd,
+ 0x57, 0x61, 0xe0, 0xeb, 0x4e, 0x7d, 0x66, 0xf9, 0x28, 0x95, 0x90, 0xc6, 0xa3, 0xb2, 0x2b, 0x67,
+ 0xe9, 0x5c, 0x98, 0x55, 0x0b, 0x72, 0x19, 0x86, 0x77, 0x5c, 0xbb, 0xdb, 0xa6, 0xfe, 0x44, 0x95,
+ 0xaf, 0xdc, 0xe7, 0xb2, 0x26, 0xd4, 0x9b, 0x9c, 0xa4, 0x71, 0x42, 0xc2, 0x0f, 0x8b, 0x77, 0x1f,
+ 0x55, 0x59, 0x62, 0xc1, 0x90, 0x6d, 0xb5, 0xad, 0xc0, 0xe7, 0x4b, 0x5a, 0x7d, 0xe6, 0xf2, 0xc0,
+ 0x9f, 0x25, 0x86, 0xe8, 0x32, 0x07, 0x13, 0xa3, 0x46, 0x3c, 0xa3, 0x64, 0xc0, 0xa6, 0x42, 0xdf,
+ 0xd0, 0x6d, 0xb1, 0xe4, 0xd5, 0x67, 0x3e, 0x39, 0xf8, 0xb0, 0x61, 0x28, 0x8d, 0x51, 0xf9, 0x4d,
+ 0x15, 0xfe, 0x8a, 0x02, 0x9b, 0x7c, 0x16, 0xc6, 0x12, 0xad, 0xe9, 0x4f, 0xd4, 0xb9, 0x74, 0x1e,
+ 0xcb, 0x92, 0x4e, 0x48, 0xd5, 0x38, 0x23, 0xc1, 0xc6, 0x12, 0x3d, 0xc4, 0xc7, 0x14, 0x18, 0xb9,
+ 0x06, 0x55, 0xdf, 0x32, 0xa9, 0xa1, 0x7b, 0xfe, 0xc4, 0xc8, 0x41, 0x80, 0xc7, 0x25, 0x70, 0xb5,
+ 0x29, 0x8b, 0x61, 0x08, 0x40, 0xa6, 0x00, 0x3a, 0xba, 0x17, 0x58, 0x42, 0x85, 0x1c, 0xe5, 0xea,
+ 0xcc, 0xd8, 0xfe, 0xde, 0x24, 0xac, 0x86, 0xa9, 0x18, 0xa3, 0x60, 0xf4, 0xac, 0xec, 0x92, 0xd3,
+ 0xe9, 0x06, 0xfe, 0xc4, 0xd8, 0xc5, 0xd2, 0xa5, 0x9a, 0xa0, 0x6f, 0x86, 0xa9, 0x18, 0xa3, 0x20,
+ 0xdf, 0x2a, 0xc0, 0xa3, 0xd1, 0x6b, 0xef, 0x20, 0x3b, 0x71, 0xe4, 0x83, 0x6c, 0x72, 0x7f, 0x6f,
+ 0xf2, 0xd1, 0x66, 0x7f, 0x96, 0x78, 0xb7, 0xfa, 0x68, 0x2f, 0xc1, 0xe8, 0x6c, 0x37, 0xd8, 0x72,
+ 0x3d, 0xeb, 0x0d, 0xae, 0x0e, 0x93, 0x05, 0xa8, 0x04, 0x5c, 0xad, 0x11, 0xeb, 0xf2, 0x13, 0x59,
+ 0xa2, 0x16, 0x2a, 0xe6, 0x35, 0xba, 0xab, 0xb4, 0x01, 0xb1, 0x3e, 0x0a, 0x35, 0x47, 0x14, 0xd7,
+ 0x7e, 0xbd, 0x00, 0xb5, 0x86, 0xee, 0x5b, 0x06, 0x83, 0x27, 0x73, 0x50, 0xee, 0xfa, 0xd4, 0x3b,
+ 0x1c, 0x28, 0x9f, 0xa5, 0xd7, 0x7d, 0xea, 0x21, 0x2f, 0x4c, 0x6e, 0x40, 0xb5, 0xa3, 0xfb, 0xfe,
+ 0x2d, 0xd7, 0x33, 0xe5, 0x4a, 0x73, 0x40, 0x20, 0xa1, 0xaf, 0xca, 0xa2, 0x18, 0x82, 0x68, 0x75,
+ 0x88, 0x96, 0x5a, 0xed, 0x07, 0x05, 0x38, 0xd5, 0xe8, 0x6e, 0x6e, 0x52, 0x4f, 0xaa, 0x67, 0x42,
+ 0xf1, 0x21, 0x14, 0x2a, 0x1e, 0x35, 0x2d, 0x5f, 0xd6, 0x7d, 0x7e, 0xe0, 0xa6, 0x43, 0x86, 0x22,
+ 0xf5, 0x2c, 0x2e, 0x2f, 0x9e, 0x80, 0x02, 0x9d, 0x74, 0xa1, 0xf6, 0x1a, 0x0d, 0xfc, 0xc0, 0xa3,
+ 0x7a, 0x5b, 0x7e, 0xdd, 0x95, 0x81, 0x59, 0x5d, 0xa5, 0x41, 0x93, 0x23, 0xc5, 0xd5, 0xba, 0x30,
+ 0x11, 0x23, 0x4e, 0xda, 0x1f, 0x55, 0x60, 0x64, 0xce, 0x6d, 0x6f, 0x58, 0x0e, 0x35, 0x2f, 0x9b,
+ 0x2d, 0x4a, 0x5e, 0x85, 0x32, 0x35, 0x5b, 0x54, 0x7e, 0xed, 0xe0, 0xeb, 0x2c, 0x03, 0x8b, 0xb4,
+ 0x05, 0xf6, 0x86, 0x1c, 0x98, 0x2c, 0xc3, 0xd8, 0xa6, 0xe7, 0xb6, 0xc5, 0xd4, 0xb5, 0xb6, 0xdb,
+ 0x91, 0xaa, 0x62, 0xe3, 0x7f, 0xa8, 0xe9, 0x60, 0x21, 0x91, 0x7b, 0x67, 0x6f, 0x12, 0xa2, 0x37,
+ 0x4c, 0x95, 0x25, 0x2f, 0xc3, 0x44, 0x94, 0x12, 0x8e, 0xe1, 0x39, 0xa6, 0x57, 0x73, 0x55, 0xa1,
+ 0xd2, 0x38, 0xbf, 0xbf, 0x37, 0x39, 0xb1, 0xd0, 0x87, 0x06, 0xfb, 0x96, 0x26, 0x6f, 0x16, 0x60,
+ 0x3c, 0xca, 0x14, 0xf3, 0xaa, 0xd4, 0x10, 0x8e, 0x68, 0xc2, 0xe6, 0x1b, 0x90, 0x85, 0x14, 0x0b,
+ 0xec, 0x61, 0x4a, 0x16, 0x60, 0x24, 0x70, 0x63, 0xf2, 0xaa, 0x70, 0x79, 0x69, 0x6a, 0xc7, 0xbc,
+ 0xe6, 0xf6, 0x95, 0x56, 0xa2, 0x1c, 0x41, 0x38, 0xa3, 0xde, 0x53, 0x92, 0x1a, 0xe2, 0x92, 0x3a,
+ 0xb7, 0xbf, 0x37, 0x79, 0x66, 0x2d, 0x93, 0x02, 0xfb, 0x94, 0x24, 0x5f, 0x2a, 0xc0, 0x98, 0xca,
+ 0x92, 0x32, 0x1a, 0x3e, 0x4a, 0x19, 0x11, 0xd6, 0x23, 0xd6, 0x12, 0x0c, 0x30, 0xc5, 0x50, 0xfb,
+ 0x51, 0x19, 0x6a, 0xe1, 0xcc, 0x46, 0x1e, 0x87, 0x0a, 0xdf, 0x0b, 0x4b, 0x85, 0x35, 0x5c, 0xb2,
+ 0xf8, 0x96, 0x19, 0x45, 0x1e, 0x79, 0x02, 0x86, 0x0d, 0xb7, 0xdd, 0xd6, 0x1d, 0x93, 0xdb, 0x37,
+ 0x6a, 0x8d, 0x3a, 0x5b, 0xa9, 0xe7, 0x44, 0x12, 0xaa, 0x3c, 0x72, 0x1e, 0xca, 0xba, 0xd7, 0x12,
+ 0xa6, 0x86, 0x9a, 0x98, 0x8f, 0x66, 0xbd, 0x96, 0x8f, 0x3c, 0x95, 0x7c, 0x14, 0x4a, 0xd4, 0xd9,
+ 0x99, 0x28, 0xf7, 0x57, 0x05, 0x2e, 0x3b, 0x3b, 0x37, 0x75, 0xaf, 0x51, 0x97, 0x75, 0x28, 0x5d,
+ 0x76, 0x76, 0x90, 0x95, 0x21, 0xcb, 0x30, 0x4c, 0x9d, 0x1d, 0xd6, 0xf6, 0xd2, 0x06, 0xf0, 0x81,
+ 0x3e, 0xc5, 0x19, 0x89, 0xd4, 0x8a, 0x43, 0x85, 0x42, 0x26, 0xa3, 0x82, 0x20, 0x9f, 0x86, 0x11,
+ 0xa1, 0x5b, 0xac, 0xb0, 0x36, 0xf1, 0x27, 0x86, 0x38, 0xe4, 0x64, 0x7f, 0xe5, 0x84, 0xd3, 0x45,
+ 0x36, 0x97, 0x58, 0xa2, 0x8f, 0x09, 0x28, 0xf2, 0x69, 0xa8, 0x29, 0x73, 0x9a, 0x6a, 0xd9, 0x4c,
+ 0x73, 0x05, 0x4a, 0x22, 0xa4, 0xaf, 0x77, 0x2d, 0x8f, 0xb6, 0xa9, 0x13, 0xf8, 0x8d, 0x93, 0x6a,
+ 0x03, 0xab, 0x72, 0x7d, 0x8c, 0xd0, 0xc8, 0x46, 0xaf, 0xdd, 0x45, 0x18, 0x0d, 0x1e, 0xef, 0x33,
+ 0xab, 0x0f, 0x60, 0x74, 0xf9, 0x1c, 0x9c, 0x08, 0x0d, 0x23, 0x72, 0x6f, 0x2d, 0xcc, 0x08, 0x4f,
+ 0xb3, 0xe2, 0x4b, 0xc9, 0xac, 0x3b, 0x7b, 0x93, 0x8f, 0x65, 0xec, 0xae, 0x23, 0x02, 0x4c, 0x83,
+ 0x69, 0x7f, 0x50, 0x82, 0x5e, 0xb5, 0x3b, 0x29, 0xb4, 0xc2, 0x51, 0x0b, 0x2d, 0xfd, 0x41, 0x62,
+ 0xfa, 0x7c, 0x5e, 0x16, 0xcb, 0xff, 0x51, 0x59, 0x0d, 0x53, 0x3a, 0xea, 0x86, 0x79, 0x50, 0xc6,
+ 0x8e, 0xf6, 0x56, 0x19, 0xc6, 0xe6, 0x75, 0xda, 0x76, 0x9d, 0x7b, 0x6e, 0x42, 0x0a, 0x0f, 0xc4,
+ 0x26, 0xe4, 0x12, 0x54, 0x3d, 0xda, 0xb1, 0x2d, 0x43, 0xf7, 0x79, 0xd3, 0x4b, 0x73, 0x1c, 0xca,
+ 0x34, 0x0c, 0x73, 0xfb, 0x6c, 0x3e, 0x4b, 0x0f, 0xe4, 0xe6, 0xb3, 0xfc, 0xee, 0x6f, 0x3e, 0xb5,
+ 0x2f, 0x15, 0x81, 0x2b, 0x2a, 0xe4, 0x22, 0x94, 0xd9, 0x22, 0x9c, 0x36, 0x79, 0xf0, 0x8e, 0xc3,
+ 0x73, 0xc8, 0x39, 0x28, 0x06, 0xae, 0x1c, 0x79, 0x20, 0xf3, 0x8b, 0x6b, 0x2e, 0x16, 0x03, 0x97,
+ 0xbc, 0x01, 0x60, 0xb8, 0x8e, 0x69, 0x29, 0x2b, 0x75, 0xbe, 0x0f, 0x5b, 0x70, 0xbd, 0x5b, 0xba,
+ 0x67, 0xce, 0x85, 0x88, 0x62, 0xfb, 0x11, 0xbd, 0x63, 0x8c, 0x1b, 0x79, 0x01, 0x86, 0x5c, 0x67,
+ 0xa1, 0x6b, 0xdb, 0x5c, 0xa0, 0xb5, 0xc6, 0xff, 0x64, 0x7b, 0xc2, 0x1b, 0x3c, 0xe5, 0xce, 0xde,
+ 0xe4, 0x59, 0xa1, 0xdf, 0xb2, 0xb7, 0x97, 0x3c, 0x2b, 0xb0, 0x9c, 0x56, 0x33, 0xf0, 0xf4, 0x80,
+ 0xb6, 0x76, 0x51, 0x16, 0xd3, 0xbe, 0x5a, 0x80, 0xfa, 0x82, 0x75, 0x9b, 0x9a, 0x2f, 0x59, 0x8e,
+ 0xe9, 0xde, 0x22, 0x08, 0x43, 0x36, 0x75, 0x5a, 0xc1, 0x96, 0xec, 0xfd, 0x53, 0xb1, 0xb1, 0x16,
+ 0x1e, 0x6e, 0x44, 0xf5, 0x6f, 0xd3, 0x40, 0x67, 0xa3, 0x6f, 0xbe, 0x2b, 0xcd, 0xef, 0x62, 0x53,
+ 0xca, 0x11, 0x50, 0x22, 0x91, 0x69, 0xa8, 0x09, 0xed, 0xd3, 0x72, 0x5a, 0x5c, 0x86, 0xd5, 0x68,
+ 0xd2, 0x6b, 0xaa, 0x0c, 0x8c, 0x68, 0xb4, 0x5d, 0x38, 0xd9, 0x23, 0x06, 0x62, 0x42, 0x39, 0xd0,
+ 0x5b, 0x6a, 0x7e, 0x5d, 0x18, 0x58, 0xc0, 0x6b, 0x7a, 0x2b, 0x26, 0x5c, 0xbe, 0xc6, 0xaf, 0xe9,
+ 0x6c, 0x8d, 0x67, 0xe8, 0xda, 0x4f, 0x0a, 0x50, 0x5d, 0xe8, 0x3a, 0x06, 0xdf, 0x1b, 0xdd, 0xdb,
+ 0x14, 0xa6, 0x14, 0x86, 0x62, 0xa6, 0xc2, 0xd0, 0x85, 0xa1, 0xed, 0x5b, 0xa1, 0x42, 0x51, 0x9f,
+ 0x59, 0x19, 0xbc, 0x57, 0xc8, 0x2a, 0x4d, 0x5d, 0xe3, 0x78, 0xe2, 0x0c, 0x65, 0x4c, 0x56, 0x68,
+ 0xe8, 0xda, 0x4b, 0x9c, 0xa9, 0x64, 0x76, 0xee, 0xa3, 0x50, 0x8f, 0x91, 0x1d, 0xca, 0x68, 0xfb,
+ 0x3b, 0x65, 0x18, 0x5a, 0x6c, 0x36, 0x67, 0x57, 0x97, 0xc8, 0x33, 0x50, 0x97, 0xe6, 0xf5, 0xeb,
+ 0x91, 0x0c, 0xc2, 0xd3, 0x95, 0x66, 0x94, 0x85, 0x71, 0x3a, 0xa6, 0x8e, 0x79, 0x54, 0xb7, 0xdb,
+ 0x72, 0xb0, 0x84, 0xea, 0x18, 0xb2, 0x44, 0x14, 0x79, 0x44, 0x87, 0x31, 0xb6, 0xc3, 0x63, 0x22,
+ 0x14, 0xbb, 0x37, 0x39, 0x6c, 0x0e, 0xb8, 0xbf, 0xe3, 0x4a, 0xe2, 0x7a, 0x02, 0x00, 0x53, 0x80,
+ 0xe4, 0x79, 0xa8, 0xea, 0xdd, 0x60, 0x8b, 0x2b, 0xd0, 0x62, 0x6c, 0x9c, 0xe7, 0xa7, 0x0f, 0x32,
+ 0xed, 0xce, 0xde, 0xe4, 0xc8, 0x35, 0x6c, 0x3c, 0xa3, 0xde, 0x31, 0xa4, 0x66, 0x95, 0x53, 0x3b,
+ 0x46, 0x59, 0xb9, 0xca, 0xa1, 0x2b, 0xb7, 0x9a, 0x00, 0xc0, 0x14, 0x20, 0x79, 0x05, 0x46, 0xb6,
+ 0xe9, 0x6e, 0xa0, 0x6f, 0x48, 0x06, 0x43, 0x87, 0x61, 0x30, 0xce, 0x54, 0xb8, 0x6b, 0xb1, 0xe2,
+ 0x98, 0x00, 0x23, 0x3e, 0x9c, 0xde, 0xa6, 0xde, 0x06, 0xf5, 0x5c, 0xb9, 0xfb, 0x94, 0x4c, 0x86,
+ 0x0f, 0xc3, 0x64, 0x62, 0x7f, 0x6f, 0xf2, 0xf4, 0xb5, 0x0c, 0x18, 0xcc, 0x04, 0xd7, 0x7e, 0x5c,
+ 0x84, 0x13, 0x8b, 0xe2, 0x7c, 0xd3, 0xf5, 0xc4, 0x22, 0x4c, 0xce, 0x42, 0xc9, 0xeb, 0x74, 0x79,
+ 0xcf, 0x29, 0x09, 0x3b, 0x29, 0xae, 0xae, 0x23, 0x4b, 0x23, 0x2f, 0x43, 0xd5, 0x94, 0x53, 0x86,
+ 0xdc, 0xfc, 0x1e, 0x76, 0xa2, 0xe1, 0x8b, 0xa0, 0x7a, 0xc3, 0x10, 0x8d, 0x69, 0xfa, 0x6d, 0xbf,
+ 0xd5, 0xb4, 0xde, 0xa0, 0x72, 0x3f, 0xc8, 0x35, 0xfd, 0x15, 0x91, 0x84, 0x2a, 0x8f, 0xad, 0xaa,
+ 0xdb, 0x74, 0x57, 0xec, 0x86, 0xca, 0xd1, 0xaa, 0x7a, 0x4d, 0xa6, 0x61, 0x98, 0x4b, 0x26, 0xd5,
+ 0x60, 0x61, 0xbd, 0xa0, 0x2c, 0x76, 0xf2, 0x37, 0x59, 0x82, 0x1c, 0x37, 0x6c, 0xca, 0x7c, 0xcd,
+ 0x0a, 0x02, 0xea, 0xc9, 0x66, 0x1c, 0x68, 0xca, 0xbc, 0xca, 0x11, 0x50, 0x22, 0x91, 0x0f, 0x41,
+ 0x8d, 0x83, 0x37, 0x6c, 0x77, 0x83, 0x37, 0x5c, 0x4d, 0xec, 0xe9, 0x6f, 0xaa, 0x44, 0x8c, 0xf2,
+ 0xb5, 0x9f, 0x16, 0xe1, 0xcc, 0x22, 0x0d, 0x84, 0x56, 0x33, 0x4f, 0x3b, 0xb6, 0xbb, 0xcb, 0x54,
+ 0x4b, 0xa4, 0xaf, 0x93, 0x17, 0x01, 0x2c, 0x7f, 0xa3, 0xb9, 0x63, 0xf0, 0x71, 0x20, 0xc6, 0xf0,
+ 0x45, 0x39, 0x24, 0x61, 0xa9, 0xd9, 0x90, 0x39, 0x77, 0x12, 0x6f, 0x18, 0x2b, 0x13, 0x6d, 0xaf,
+ 0x8a, 0x77, 0xd9, 0x5e, 0x35, 0x01, 0x3a, 0x91, 0x82, 0x5a, 0xe2, 0x94, 0xff, 0x47, 0xb1, 0x39,
+ 0x8c, 0x6e, 0x1a, 0x83, 0xc9, 0xa3, 0x32, 0x3a, 0x30, 0x6e, 0xd2, 0x4d, 0xbd, 0x6b, 0x07, 0xa1,
+ 0x52, 0x2d, 0x07, 0xf1, 0xc1, 0xf5, 0xf2, 0xf0, 0xec, 0x75, 0x3e, 0x85, 0x84, 0x3d, 0xd8, 0xda,
+ 0xef, 0x96, 0xe0, 0xdc, 0x22, 0x0d, 0x42, 0x8b, 0x8b, 0x9c, 0x1d, 0x9b, 0x1d, 0x6a, 0xb0, 0x56,
+ 0x78, 0xb3, 0x00, 0x43, 0xb6, 0xbe, 0x41, 0x6d, 0xb6, 0x7a, 0xb1, 0xaf, 0x79, 0x75, 0xe0, 0x85,
+ 0xa0, 0x3f, 0x97, 0xa9, 0x65, 0xce, 0x21, 0xb5, 0x34, 0x88, 0x44, 0x94, 0xec, 0xd9, 0xa4, 0x6e,
+ 0xd8, 0x5d, 0x3f, 0xa0, 0xde, 0xaa, 0xeb, 0x05, 0x52, 0x9f, 0x0c, 0x27, 0xf5, 0xb9, 0x28, 0x0b,
+ 0xe3, 0x74, 0x64, 0x06, 0xc0, 0xb0, 0x2d, 0xea, 0x04, 0xbc, 0x94, 0x18, 0x57, 0x44, 0xb5, 0xef,
+ 0x5c, 0x98, 0x83, 0x31, 0x2a, 0xc6, 0xaa, 0xed, 0x3a, 0x56, 0xe0, 0x0a, 0x56, 0xe5, 0x24, 0xab,
+ 0x95, 0x28, 0x0b, 0xe3, 0x74, 0xbc, 0x18, 0x0d, 0x3c, 0xcb, 0xf0, 0x79, 0xb1, 0x4a, 0xaa, 0x58,
+ 0x94, 0x85, 0x71, 0x3a, 0xb6, 0xe6, 0xc5, 0xbe, 0xff, 0x50, 0x6b, 0xde, 0x37, 0x6b, 0x70, 0x21,
+ 0x21, 0xd6, 0x40, 0x0f, 0xe8, 0x66, 0xd7, 0x6e, 0xd2, 0x40, 0x35, 0xe0, 0x80, 0x6b, 0xe1, 0xcf,
+ 0x47, 0xed, 0x2e, 0xbc, 0x2a, 0x8c, 0xa3, 0x69, 0xf7, 0x9e, 0x0a, 0x1e, 0xa8, 0xed, 0xa7, 0xa1,
+ 0xe6, 0xe8, 0x81, 0xcf, 0x07, 0xae, 0x1c, 0xa3, 0xa1, 0x1a, 0x76, 0x5d, 0x65, 0x60, 0x44, 0x43,
+ 0x56, 0xe1, 0xb4, 0x14, 0xf1, 0xe5, 0xdb, 0x1d, 0xd7, 0x0b, 0xa8, 0x27, 0xca, 0xca, 0xe5, 0x54,
+ 0x96, 0x3d, 0xbd, 0x92, 0x41, 0x83, 0x99, 0x25, 0xc9, 0x0a, 0x9c, 0x32, 0xc4, 0x49, 0x33, 0xb5,
+ 0x5d, 0xdd, 0x54, 0x80, 0xc2, 0xc0, 0x15, 0x6e, 0x8d, 0xe6, 0x7a, 0x49, 0x30, 0xab, 0x5c, 0xba,
+ 0x37, 0x0f, 0x0d, 0xd4, 0x9b, 0x87, 0x07, 0xe9, 0xcd, 0xd5, 0xc1, 0x7a, 0x73, 0xed, 0x60, 0xbd,
+ 0x99, 0x49, 0x9e, 0xf5, 0x23, 0xea, 0x31, 0xf5, 0x44, 0xac, 0xb0, 0x31, 0x47, 0x86, 0x50, 0xf2,
+ 0xcd, 0x0c, 0x1a, 0xcc, 0x2c, 0x49, 0x36, 0xe0, 0x9c, 0x48, 0xbf, 0xec, 0x18, 0xde, 0x6e, 0x87,
+ 0x2d, 0x3c, 0x31, 0xdc, 0x7a, 0xc2, 0xc2, 0x78, 0xae, 0xd9, 0x97, 0x12, 0xef, 0x82, 0x42, 0xfe,
+ 0x2f, 0x8c, 0x8a, 0x56, 0x5a, 0xd1, 0x3b, 0x1c, 0x56, 0xb8, 0x35, 0x3c, 0x2c, 0x61, 0x47, 0xe7,
+ 0xe2, 0x99, 0x98, 0xa4, 0x25, 0xb3, 0x70, 0xa2, 0xb3, 0x63, 0xb0, 0xc7, 0xa5, 0xcd, 0xeb, 0x94,
+ 0x9a, 0xd4, 0xe4, 0xa7, 0x35, 0xb5, 0xc6, 0x23, 0xca, 0xd0, 0xb1, 0x9a, 0xcc, 0xc6, 0x34, 0x3d,
+ 0x79, 0x1e, 0x46, 0xfc, 0x40, 0xf7, 0x02, 0x69, 0xd6, 0x9b, 0x18, 0x13, 0x6e, 0x1f, 0xca, 0xea,
+ 0xd5, 0x8c, 0xe5, 0x61, 0x82, 0x32, 0x73, 0xbd, 0x38, 0x71, 0x7c, 0xeb, 0x45, 0x9e, 0xd9, 0xea,
+ 0xcf, 0x8a, 0x70, 0x71, 0x91, 0x06, 0x2b, 0xae, 0x23, 0x8d, 0xa2, 0x59, 0xcb, 0xfe, 0x81, 0x6c,
+ 0xa2, 0xc9, 0x45, 0xbb, 0x78, 0xa4, 0x8b, 0x76, 0xe9, 0x88, 0x16, 0xed, 0xf2, 0x31, 0x2e, 0xda,
+ 0xbf, 0x5f, 0x84, 0x47, 0x12, 0x92, 0x5c, 0x75, 0x4d, 0x35, 0xe1, 0xbf, 0x2f, 0xc0, 0x03, 0x08,
+ 0xf0, 0x8e, 0xd0, 0x3b, 0xf9, 0xb1, 0x56, 0x4a, 0xe3, 0xf9, 0x4a, 0x5a, 0xe3, 0x79, 0x25, 0xcf,
+ 0xca, 0x97, 0xc1, 0xe1, 0x40, 0x2b, 0xde, 0x55, 0x20, 0x9e, 0x3c, 0x84, 0x13, 0xa6, 0x9f, 0x98,
+ 0xd2, 0x13, 0xfa, 0x95, 0x61, 0x0f, 0x05, 0x66, 0x94, 0x22, 0x4d, 0x78, 0xd8, 0xa7, 0x4e, 0x60,
+ 0x39, 0xd4, 0x4e, 0xc2, 0x09, 0x6d, 0xe8, 0x31, 0x09, 0xf7, 0x70, 0x33, 0x8b, 0x08, 0xb3, 0xcb,
+ 0xe6, 0x99, 0x07, 0xfe, 0x12, 0xb8, 0xca, 0x29, 0x44, 0x73, 0x64, 0x1a, 0xcb, 0x9b, 0x69, 0x8d,
+ 0xe5, 0xd5, 0xfc, 0xed, 0x36, 0x98, 0xb6, 0x32, 0x03, 0xc0, 0x5b, 0x21, 0xae, 0xae, 0x84, 0x8b,
+ 0x34, 0x86, 0x39, 0x18, 0xa3, 0x62, 0x0b, 0x90, 0x92, 0x73, 0x5c, 0x53, 0x09, 0x17, 0xa0, 0x66,
+ 0x3c, 0x13, 0x93, 0xb4, 0x7d, 0xb5, 0x9d, 0xca, 0xc0, 0xda, 0xce, 0x55, 0x20, 0x09, 0xc3, 0xa3,
+ 0xc0, 0x1b, 0x4a, 0xba, 0x35, 0x2e, 0xf5, 0x50, 0x60, 0x46, 0xa9, 0x3e, 0x5d, 0x79, 0xf8, 0x68,
+ 0xbb, 0x72, 0x75, 0xf0, 0xae, 0x4c, 0x5e, 0x85, 0xb3, 0x9c, 0x95, 0x94, 0x4f, 0x12, 0x58, 0xe8,
+ 0x3d, 0x1f, 0x90, 0xc0, 0x67, 0xb1, 0x1f, 0x21, 0xf6, 0xc7, 0x60, 0xed, 0x63, 0x78, 0xd4, 0x64,
+ 0xcc, 0x75, 0xbb, 0xbf, 0x4e, 0x34, 0x97, 0x41, 0x83, 0x99, 0x25, 0x59, 0x17, 0x0b, 0x58, 0x37,
+ 0xd4, 0x37, 0x6c, 0x6a, 0x4a, 0xb7, 0xce, 0xb0, 0x8b, 0xad, 0x2d, 0x37, 0x65, 0x0e, 0xc6, 0xa8,
+ 0xb2, 0xd4, 0x94, 0x91, 0x43, 0xaa, 0x29, 0x8b, 0xdc, 0x4a, 0xbf, 0x99, 0xd0, 0x86, 0xa4, 0xae,
+ 0x13, 0x3a, 0xea, 0xce, 0xa5, 0x09, 0xb0, 0xb7, 0x0c, 0xd7, 0x12, 0x0d, 0xcf, 0xea, 0x04, 0x7e,
+ 0x12, 0x6b, 0x2c, 0xa5, 0x25, 0x66, 0xd0, 0x60, 0x66, 0x49, 0xa6, 0x9f, 0x6f, 0x51, 0xdd, 0x0e,
+ 0xb6, 0x92, 0x80, 0x27, 0x92, 0xfa, 0xf9, 0x95, 0x5e, 0x12, 0xcc, 0x2a, 0x97, 0xb9, 0x20, 0x8d,
+ 0x3f, 0x98, 0x6a, 0xd5, 0x97, 0x4b, 0x70, 0x76, 0x91, 0x06, 0xa1, 0x5f, 0xcd, 0xfb, 0x66, 0x94,
+ 0x77, 0xc1, 0x8c, 0xf2, 0x8d, 0x0a, 0x9c, 0x5a, 0xa4, 0x41, 0x8f, 0x36, 0xf6, 0xdf, 0x54, 0xfc,
+ 0x2b, 0x70, 0x2a, 0x72, 0xe5, 0x6a, 0x06, 0xae, 0x27, 0xd6, 0xf2, 0xd4, 0x6e, 0xb9, 0xd9, 0x4b,
+ 0x82, 0x59, 0xe5, 0xc8, 0xa7, 0xe1, 0x11, 0xbe, 0xd4, 0x3b, 0x2d, 0x61, 0x9f, 0x15, 0xc6, 0x84,
+ 0xd8, 0x35, 0x81, 0x49, 0x09, 0xf9, 0x48, 0x33, 0x9b, 0x0c, 0xfb, 0x95, 0x27, 0x5f, 0x80, 0x91,
+ 0x8e, 0xd5, 0xa1, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0xb7, 0x4b, 0xc8, 0x6a, 0x0c, 0x2c, 0xda, 0xc0,
+ 0xc5, 0x53, 0x31, 0xc1, 0x30, 0xb3, 0xa7, 0x56, 0x8f, 0xb1, 0xa7, 0xfe, 0x7b, 0x11, 0x86, 0x17,
+ 0x3d, 0xb7, 0xdb, 0x69, 0xec, 0x92, 0x16, 0x0c, 0xdd, 0xe2, 0x87, 0x67, 0xf2, 0x68, 0x6a, 0x70,
+ 0x77, 0x68, 0x71, 0x06, 0x17, 0xa9, 0x44, 0xe2, 0x1d, 0x25, 0x3c, 0xeb, 0xc4, 0xdb, 0x74, 0x97,
+ 0x9a, 0xf2, 0x0c, 0x2d, 0xec, 0xc4, 0xd7, 0x58, 0x22, 0x8a, 0x3c, 0xd2, 0x86, 0x13, 0xba, 0x6d,
+ 0xbb, 0xb7, 0xa8, 0xb9, 0xac, 0x07, 0xd4, 0xa1, 0xbe, 0x3a, 0x92, 0x3c, 0xac, 0x59, 0x9a, 0x9f,
+ 0xeb, 0xcf, 0x26, 0xa1, 0x30, 0x8d, 0x4d, 0x5e, 0x83, 0x61, 0x3f, 0x70, 0x3d, 0xa5, 0x6c, 0xd5,
+ 0x67, 0xe6, 0x06, 0x6f, 0xf4, 0xc6, 0xa7, 0x9a, 0x02, 0x4a, 0xd8, 0xec, 0xe5, 0x0b, 0x2a, 0x06,
+ 0xda, 0xd7, 0x0b, 0x00, 0x57, 0xd6, 0xd6, 0x56, 0xe5, 0xf1, 0x82, 0x09, 0x65, 0xbd, 0x1b, 0x1e,
+ 0x54, 0x0e, 0x7e, 0x20, 0x98, 0xf0, 0x87, 0x94, 0x67, 0x78, 0xdd, 0x60, 0x0b, 0x39, 0x3a, 0xf9,
+ 0x20, 0x0c, 0x4b, 0x05, 0x59, 0x8a, 0x3d, 0x74, 0x2d, 0x90, 0x4a, 0x34, 0xaa, 0x7c, 0xed, 0xb7,
+ 0x8b, 0x00, 0x4b, 0xa6, 0x4d, 0x9b, 0xca, 0x83, 0xbd, 0x16, 0x6c, 0x79, 0xd4, 0xdf, 0x72, 0x6d,
+ 0x73, 0xc0, 0xd3, 0x54, 0x6e, 0xf3, 0x5f, 0x53, 0x20, 0x18, 0xe1, 0x11, 0x13, 0x46, 0xfc, 0x80,
+ 0x76, 0x96, 0x9c, 0x80, 0x7a, 0x3b, 0xba, 0x3d, 0xe0, 0x21, 0xca, 0xb8, 0xb0, 0x8b, 0x44, 0x38,
+ 0x98, 0x40, 0x25, 0x3a, 0xd4, 0x2d, 0xc7, 0x10, 0x03, 0xa4, 0xb1, 0x3b, 0x60, 0x47, 0x3a, 0xc1,
+ 0x76, 0x1c, 0x4b, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0xef, 0x17, 0xe1, 0x0c, 0xe7, 0xc7, 0xaa, 0x91,
+ 0xf0, 0xc7, 0x24, 0xff, 0xbf, 0xe7, 0x1e, 0xdc, 0xff, 0x3e, 0x18, 0x6b, 0x71, 0x8d, 0x6a, 0x85,
+ 0x06, 0x7a, 0xa4, 0xcf, 0x45, 0x69, 0xb1, 0xcb, 0x6f, 0x5d, 0x28, 0xfb, 0x6c, 0xbe, 0x12, 0xd2,
+ 0x6b, 0x0e, 0xdc, 0x85, 0xb2, 0x3f, 0x80, 0xcf, 0x5e, 0xe1, 0xa9, 0x31, 0x9f, 0xb5, 0x38, 0x3b,
+ 0xf2, 0xb3, 0x30, 0xe4, 0x07, 0x7a, 0xd0, 0x55, 0x43, 0x73, 0xfd, 0xa8, 0x19, 0x73, 0xf0, 0x68,
+ 0x1e, 0x11, 0xef, 0x28, 0x99, 0x6a, 0xdf, 0x2f, 0xc0, 0xb9, 0xec, 0x82, 0xcb, 0x96, 0x1f, 0x90,
+ 0xff, 0xd7, 0x23, 0xf6, 0x03, 0xb6, 0x38, 0x2b, 0xcd, 0x85, 0x1e, 0x3a, 0x64, 0xab, 0x94, 0x98,
+ 0xc8, 0x03, 0xa8, 0x58, 0x01, 0x6d, 0xab, 0xfd, 0xe5, 0x8d, 0x23, 0xfe, 0xf4, 0xd8, 0xd2, 0xce,
+ 0xb8, 0xa0, 0x60, 0xa6, 0xbd, 0x55, 0xec, 0xf7, 0xc9, 0x7c, 0xf9, 0xb0, 0x93, 0x3e, 0xbf, 0xd7,
+ 0xf2, 0xf9, 0xfc, 0x26, 0x2b, 0xd4, 0xeb, 0xfa, 0xfb, 0x33, 0xbd, 0xae, 0xbf, 0x37, 0xf2, 0xbb,
+ 0xfe, 0xa6, 0xc4, 0xd0, 0xd7, 0x03, 0xf8, 0x9d, 0x12, 0x9c, 0xbf, 0x5b, 0xb7, 0x61, 0xeb, 0x99,
+ 0xec, 0x9d, 0x79, 0xd7, 0xb3, 0xbb, 0xf7, 0x43, 0x32, 0x03, 0x95, 0xce, 0x96, 0xee, 0x2b, 0xa5,
+ 0x4c, 0x6d, 0x58, 0x2a, 0xab, 0x2c, 0xf1, 0x0e, 0x9b, 0x34, 0xb8, 0x32, 0xc7, 0x5f, 0x51, 0x90,
+ 0xb2, 0xe9, 0xb8, 0x4d, 0x7d, 0x3f, 0xb2, 0x09, 0x84, 0xd3, 0xf1, 0x8a, 0x48, 0x46, 0x95, 0x4f,
+ 0x02, 0x18, 0x12, 0x26, 0x66, 0xb9, 0x32, 0x0d, 0xee, 0xc8, 0x95, 0xe1, 0x26, 0x1e, 0x7d, 0x94,
+ 0x3c, 0xad, 0x90, 0xbc, 0xc8, 0x14, 0x94, 0x83, 0xc8, 0x69, 0x57, 0x6d, 0xcd, 0xcb, 0x19, 0xfa,
+ 0x29, 0xa7, 0x63, 0x1b, 0x7b, 0x77, 0x83, 0x1b, 0xd5, 0x4d, 0x79, 0x7e, 0x6e, 0xb9, 0x0e, 0x57,
+ 0xc8, 0x4a, 0xd1, 0xc6, 0xfe, 0x46, 0x0f, 0x05, 0x66, 0x94, 0xd2, 0xfe, 0xa6, 0x0a, 0x67, 0xb2,
+ 0xfb, 0x03, 0x93, 0xdb, 0x0e, 0xf5, 0x7c, 0x86, 0x5d, 0x48, 0xca, 0xed, 0xa6, 0x48, 0x46, 0x95,
+ 0xff, 0x9e, 0x76, 0x38, 0xfb, 0x46, 0x01, 0xce, 0x7a, 0xf2, 0x8c, 0xe8, 0x7e, 0x38, 0x9d, 0x3d,
+ 0x26, 0xcc, 0x19, 0x7d, 0x18, 0x62, 0xff, 0xba, 0x90, 0xdf, 0x2c, 0xc0, 0x44, 0x3b, 0x65, 0xe7,
+ 0x38, 0xc6, 0x0b, 0x63, 0xdc, 0x2b, 0x7e, 0xa5, 0x0f, 0x3f, 0xec, 0x5b, 0x13, 0xf2, 0x05, 0xa8,
+ 0x77, 0x58, 0xbf, 0xf0, 0x03, 0xea, 0x18, 0xea, 0xce, 0xd8, 0xe0, 0x23, 0x69, 0x35, 0xc2, 0x52,
+ 0xae, 0x68, 0x42, 0x3f, 0x88, 0x65, 0x60, 0x9c, 0xe3, 0x03, 0x7e, 0x43, 0xec, 0x12, 0x54, 0x7d,
+ 0x1a, 0x04, 0x96, 0xd3, 0x12, 0xfb, 0x8d, 0x9a, 0x18, 0x2b, 0x4d, 0x99, 0x86, 0x61, 0x2e, 0xf9,
+ 0x10, 0xd4, 0xf8, 0x91, 0xd3, 0xac, 0xd7, 0xf2, 0x27, 0x6a, 0xdc, 0x5d, 0x6c, 0x54, 0x38, 0xc0,
+ 0xc9, 0x44, 0x8c, 0xf2, 0xc9, 0xd3, 0x30, 0xb2, 0xc1, 0x87, 0xaf, 0xbc, 0xce, 0x2b, 0x6c, 0x5c,
+ 0x5c, 0x5b, 0x6b, 0xc4, 0xd2, 0x31, 0x41, 0x45, 0x66, 0x00, 0x68, 0x78, 0x2e, 0x97, 0xb6, 0x67,
+ 0x45, 0x27, 0x76, 0x18, 0xa3, 0x22, 0x8f, 0x41, 0x29, 0xb0, 0x7d, 0x6e, 0xc3, 0xaa, 0x46, 0x5b,
+ 0xd0, 0xb5, 0xe5, 0x26, 0xb2, 0x74, 0xed, 0xa7, 0x05, 0x38, 0x91, 0xba, 0x5c, 0xc2, 0x8a, 0x74,
+ 0x3d, 0x5b, 0x4e, 0x23, 0x61, 0x91, 0x75, 0x5c, 0x46, 0x96, 0x4e, 0x5e, 0x95, 0x6a, 0x79, 0x31,
+ 0x67, 0xe4, 0x82, 0xeb, 0x7a, 0xe0, 0x33, 0x3d, 0xbc, 0x47, 0x23, 0xe7, 0xc7, 0x7c, 0x51, 0x7d,
+ 0xe4, 0x3a, 0x10, 0x3b, 0xe6, 0x8b, 0xf2, 0x30, 0x41, 0x99, 0x32, 0xf8, 0x95, 0x0f, 0x62, 0xf0,
+ 0xd3, 0xbe, 0x5a, 0x8c, 0x49, 0x40, 0x6a, 0xf6, 0xf7, 0x90, 0xc0, 0x93, 0x6c, 0x01, 0x0d, 0x17,
+ 0xf7, 0x5a, 0x7c, 0xfd, 0xe3, 0x8b, 0xb1, 0xcc, 0x25, 0x2f, 0x09, 0xd9, 0x97, 0x72, 0xde, 0x42,
+ 0x5d, 0x5b, 0x6e, 0x0a, 0xef, 0x2a, 0xd5, 0x6a, 0x61, 0x13, 0x94, 0x8f, 0xa9, 0x09, 0xb4, 0xbf,
+ 0x28, 0x41, 0xfd, 0xaa, 0xbb, 0xf1, 0x1e, 0xf1, 0xa0, 0xce, 0x5e, 0xa6, 0x8a, 0xef, 0xe2, 0x32,
+ 0xb5, 0x0e, 0x8f, 0x04, 0x81, 0xdd, 0xa4, 0x86, 0xeb, 0x98, 0xfe, 0xec, 0x66, 0x40, 0xbd, 0x05,
+ 0xcb, 0xb1, 0xfc, 0x2d, 0x6a, 0xca, 0xe3, 0xa4, 0x47, 0xf7, 0xf7, 0x26, 0x1f, 0x59, 0x5b, 0x5b,
+ 0xce, 0x22, 0xc1, 0x7e, 0x65, 0xf9, 0xb4, 0xa1, 0x1b, 0xdb, 0xee, 0xe6, 0x26, 0xbf, 0x29, 0x23,
+ 0x7d, 0x6e, 0xc4, 0xb4, 0x11, 0x4b, 0xc7, 0x04, 0x95, 0xf6, 0x76, 0x11, 0x6a, 0xe1, 0xcd, 0x77,
+ 0xf2, 0x04, 0x0c, 0x6f, 0x78, 0xee, 0x36, 0xf5, 0xc4, 0xc9, 0x9d, 0xbc, 0x29, 0xd3, 0x10, 0x49,
+ 0xa8, 0xf2, 0xc8, 0xe3, 0x50, 0x09, 0xdc, 0x8e, 0x65, 0xa4, 0x0d, 0x6a, 0x6b, 0x2c, 0x11, 0x45,
+ 0xde, 0xf1, 0x75, 0xf0, 0x27, 0x13, 0xaa, 0x5d, 0xad, 0xaf, 0x32, 0xf6, 0x0a, 0x94, 0x7d, 0xdd,
+ 0xb7, 0xe5, 0x7a, 0x9a, 0xe3, 0x12, 0xf9, 0x6c, 0x73, 0x59, 0x5e, 0x22, 0x9f, 0x6d, 0x2e, 0x23,
+ 0x07, 0xd5, 0x7e, 0x54, 0x84, 0xba, 0x90, 0x9b, 0x98, 0x15, 0x8e, 0x52, 0x72, 0x2f, 0x70, 0x57,
+ 0x0a, 0xbf, 0xdb, 0xa6, 0x1e, 0x37, 0x33, 0xc9, 0x49, 0x2e, 0x7e, 0x3e, 0x10, 0x65, 0x86, 0xee,
+ 0x14, 0x51, 0x92, 0x12, 0x7d, 0xf9, 0x18, 0x45, 0x5f, 0x39, 0x90, 0xe8, 0x87, 0x8e, 0x43, 0xf4,
+ 0x6f, 0x16, 0xa1, 0xb6, 0x6c, 0x6d, 0x52, 0x63, 0xd7, 0xb0, 0xf9, 0x9d, 0x40, 0x93, 0xda, 0x34,
+ 0xa0, 0x8b, 0x9e, 0x6e, 0xd0, 0x55, 0xea, 0x59, 0x3c, 0x66, 0x0b, 0x1b, 0x1f, 0x7c, 0x06, 0x92,
+ 0x77, 0x02, 0xe7, 0xfb, 0xd0, 0x60, 0xdf, 0xd2, 0x64, 0x09, 0x46, 0x4c, 0xea, 0x5b, 0x1e, 0x35,
+ 0x57, 0x63, 0x1b, 0x95, 0x27, 0xd4, 0x52, 0x33, 0x1f, 0xcb, 0xbb, 0xb3, 0x37, 0x39, 0xaa, 0x0c,
+ 0x94, 0x62, 0xc7, 0x92, 0x28, 0xca, 0x86, 0x7c, 0x47, 0xef, 0xfa, 0x59, 0x75, 0x8c, 0x0d, 0xf9,
+ 0xd5, 0x6c, 0x12, 0xec, 0x57, 0x56, 0xab, 0x40, 0x69, 0xd9, 0x6d, 0x69, 0x6f, 0x95, 0x20, 0x0c,
+ 0xee, 0x43, 0x7e, 0xae, 0x00, 0x75, 0xdd, 0x71, 0xdc, 0x40, 0x06, 0xce, 0x11, 0x27, 0xf0, 0x98,
+ 0x3b, 0x86, 0xd0, 0xd4, 0x6c, 0x04, 0x2a, 0x0e, 0x6f, 0xc3, 0x03, 0xe5, 0x58, 0x0e, 0xc6, 0x79,
+ 0x93, 0x6e, 0xea, 0x3c, 0x79, 0x25, 0x7f, 0x2d, 0x0e, 0x70, 0x7a, 0x7c, 0xee, 0x93, 0x30, 0x9e,
+ 0xae, 0xec, 0x61, 0x8e, 0x83, 0x72, 0x1d, 0xcc, 0x17, 0x01, 0x22, 0x9f, 0x92, 0xfb, 0x60, 0xc4,
+ 0xb2, 0x12, 0x46, 0xac, 0xc5, 0xc1, 0x05, 0x1c, 0x56, 0xba, 0xaf, 0xe1, 0xea, 0xf5, 0x94, 0xe1,
+ 0x6a, 0xe9, 0x28, 0x98, 0xdd, 0xdd, 0x58, 0xf5, 0x5b, 0x05, 0x18, 0x8f, 0x88, 0xe5, 0x0d, 0xd9,
+ 0xe7, 0x60, 0xd4, 0xa3, 0xba, 0xd9, 0xd0, 0x03, 0x63, 0x8b, 0xbb, 0x7a, 0x17, 0xb8, 0x6f, 0xf6,
+ 0xc9, 0xfd, 0xbd, 0xc9, 0x51, 0x8c, 0x67, 0x60, 0x92, 0x8e, 0xe8, 0x50, 0x67, 0x09, 0x6b, 0x56,
+ 0x9b, 0xba, 0xdd, 0x60, 0x40, 0xab, 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c, 0xed, 0x9d,
+ 0x02, 0x8c, 0xc5, 0x2b, 0x7c, 0xec, 0x16, 0xb5, 0xad, 0xa4, 0x45, 0x6d, 0xee, 0x08, 0xda, 0xa4,
+ 0x8f, 0x15, 0xed, 0xc7, 0xd5, 0xf8, 0xa7, 0x71, 0xcb, 0x59, 0xdc, 0x58, 0x50, 0xb8, 0xab, 0xb1,
+ 0xe0, 0xbd, 0x1f, 0x35, 0xa6, 0x9f, 0x96, 0x5b, 0x7e, 0x80, 0xb5, 0xdc, 0x77, 0x33, 0xf4, 0x4c,
+ 0x2c, 0x7c, 0xca, 0x50, 0x8e, 0xf0, 0x29, 0xed, 0x30, 0x7c, 0xca, 0xf0, 0x91, 0x4d, 0x3a, 0x07,
+ 0x09, 0xa1, 0x52, 0xbd, 0xaf, 0x21, 0x54, 0x6a, 0xc7, 0x15, 0x42, 0x05, 0xf2, 0x86, 0x50, 0xf9,
+ 0x4a, 0x01, 0xc6, 0xcc, 0xc4, 0x8d, 0x59, 0x6e, 0x5b, 0xc8, 0xb3, 0xd4, 0x24, 0x2f, 0xe0, 0x8a,
+ 0x2b, 0x53, 0xc9, 0x34, 0x4c, 0xb1, 0xd4, 0x7e, 0x58, 0x8e, 0xaf, 0x03, 0xf7, 0xdb, 0x54, 0xfd,
+ 0x6c, 0xd2, 0x54, 0x7d, 0x31, 0x6d, 0xaa, 0x3e, 0x11, 0xf3, 0x22, 0x8d, 0x9b, 0xab, 0x3f, 0x1c,
+ 0x9b, 0x1e, 0xd9, 0x9c, 0x34, 0x1a, 0x49, 0x3a, 0x63, 0x8a, 0xfc, 0x30, 0x54, 0x7d, 0x15, 0x86,
+ 0x51, 0x6c, 0x6c, 0xa2, 0x76, 0x51, 0x21, 0x12, 0x43, 0x0a, 0xa6, 0x89, 0x7b, 0x54, 0xf7, 0x5d,
+ 0x27, 0xad, 0x89, 0x23, 0x4f, 0x45, 0x99, 0x1b, 0x37, 0x99, 0x0f, 0xdd, 0xc3, 0x64, 0xae, 0x43,
+ 0xdd, 0xd6, 0xfd, 0x60, 0xbd, 0x63, 0xea, 0x01, 0x35, 0xe5, 0x78, 0xfb, 0x5f, 0x07, 0x5b, 0xab,
+ 0xd8, 0xfa, 0x17, 0x29, 0x84, 0xcb, 0x11, 0x0c, 0xc6, 0x31, 0x89, 0x09, 0x23, 0xec, 0x95, 0x8f,
+ 0x06, 0x73, 0x56, 0x85, 0x00, 0x38, 0x0c, 0x8f, 0xd0, 0xd2, 0xb3, 0x1c, 0xc3, 0xc1, 0x04, 0x6a,
+ 0x1f, 0xab, 0x7a, 0x6d, 0x20, 0xab, 0xfa, 0x57, 0x6a, 0x50, 0xbf, 0xae, 0x07, 0xd6, 0x0e, 0xe5,
+ 0xa7, 0x38, 0xc7, 0x63, 0x4a, 0xff, 0xd5, 0x02, 0x9c, 0x49, 0xba, 0xea, 0x1d, 0xa3, 0x3d, 0x9d,
+ 0x07, 0xfe, 0xc0, 0x4c, 0x6e, 0xd8, 0xa7, 0x16, 0xdc, 0xb2, 0xde, 0xe3, 0xf9, 0x77, 0xdc, 0x96,
+ 0xf5, 0x66, 0x3f, 0x86, 0xd8, 0xbf, 0x2e, 0xef, 0x15, 0xcb, 0xfa, 0x83, 0x1d, 0x98, 0x2d, 0x65,
+ 0xf7, 0x1f, 0x7e, 0x60, 0xec, 0xfe, 0xd5, 0x07, 0x42, 0xd9, 0xea, 0xc4, 0xec, 0xfe, 0xb5, 0x9c,
+ 0xfe, 0x27, 0xd2, 0xbb, 0x5d, 0xa0, 0xf5, 0x3b, 0x3f, 0xe0, 0x17, 0xd3, 0x95, 0x3d, 0x96, 0xe9,
+ 0x28, 0x1b, 0xba, 0x6f, 0x19, 0x72, 0xd9, 0xcb, 0x11, 0x88, 0x52, 0x45, 0xec, 0x12, 0xc7, 0xd4,
+ 0xfc, 0x15, 0x05, 0x76, 0x14, 0x19, 0xac, 0x98, 0x2b, 0x32, 0x18, 0x99, 0x83, 0xb2, 0xc3, 0x76,
+ 0xcf, 0xa5, 0x43, 0xc7, 0x02, 0xbb, 0x7e, 0x8d, 0xee, 0x22, 0x2f, 0xac, 0xbd, 0x5d, 0x04, 0x60,
+ 0x9f, 0x7f, 0x30, 0x0b, 0xfc, 0x07, 0x61, 0xd8, 0xef, 0xf2, 0xbd, 0xb2, 0x5c, 0xb0, 0x23, 0xa7,
+ 0x1d, 0x91, 0x8c, 0x2a, 0x9f, 0x3c, 0x0e, 0x95, 0xd7, 0xbb, 0xb4, 0xab, 0x8e, 0x93, 0x43, 0x75,
+ 0xed, 0x53, 0x2c, 0x11, 0x45, 0xde, 0xf1, 0x59, 0xd3, 0x94, 0xa5, 0xbe, 0x72, 0x5c, 0x96, 0xfa,
+ 0x1a, 0x0c, 0x5f, 0x77, 0xb9, 0x0f, 0xa0, 0xf6, 0xaf, 0x45, 0x80, 0xc8, 0xc7, 0x8a, 0x7c, 0xbd,
+ 0x00, 0x0f, 0x87, 0x03, 0x2e, 0x10, 0x5a, 0xf7, 0x9c, 0xad, 0x5b, 0xed, 0xdc, 0x56, 0xfb, 0xac,
+ 0xc1, 0xce, 0x67, 0xa0, 0xd5, 0x2c, 0x76, 0x98, 0x5d, 0x0b, 0x82, 0x50, 0xa5, 0xed, 0x4e, 0xb0,
+ 0x3b, 0x6f, 0x79, 0xb2, 0x07, 0x66, 0xba, 0xf2, 0x5d, 0x96, 0x34, 0xa2, 0xa8, 0xdc, 0x1a, 0xf2,
+ 0x41, 0xa4, 0x72, 0x30, 0xc4, 0x21, 0x5b, 0x50, 0x75, 0xdc, 0x57, 0x7d, 0x26, 0x0e, 0xd9, 0x1d,
+ 0x5f, 0x1c, 0x5c, 0xe4, 0x42, 0xac, 0xc2, 0xca, 0x2b, 0x5f, 0x70, 0xd8, 0x91, 0xc2, 0xfe, 0x5a,
+ 0x11, 0x4e, 0x65, 0xc8, 0x81, 0xbc, 0x08, 0xe3, 0xd2, 0x9d, 0x2d, 0x0a, 0x4f, 0x5c, 0x88, 0xc2,
+ 0x13, 0x37, 0x53, 0x79, 0xd8, 0x43, 0x4d, 0x5e, 0x05, 0xd0, 0x0d, 0x83, 0xfa, 0xfe, 0x8a, 0x6b,
+ 0x2a, 0x7d, 0xf4, 0x85, 0xfd, 0xbd, 0x49, 0x98, 0x0d, 0x53, 0xef, 0xec, 0x4d, 0x7e, 0x24, 0xcb,
+ 0x43, 0x35, 0x25, 0xe7, 0xa8, 0x00, 0xc6, 0x20, 0xc9, 0xe7, 0x00, 0xc4, 0xd6, 0x2b, 0xbc, 0x44,
+ 0x7f, 0x0f, 0x7b, 0xc5, 0x94, 0x0a, 0x57, 0x34, 0xf5, 0xa9, 0xae, 0xee, 0x04, 0x56, 0xb0, 0x2b,
+ 0x62, 0x96, 0xdc, 0x0c, 0x51, 0x30, 0x86, 0xa8, 0xfd, 0x69, 0x11, 0xaa, 0xca, 0x52, 0x7a, 0x1f,
+ 0xcc, 0x63, 0xad, 0x84, 0x79, 0xec, 0x88, 0x7c, 0x52, 0xb3, 0x8c, 0x63, 0x6e, 0xca, 0x38, 0xb6,
+ 0x98, 0x9f, 0xd5, 0xdd, 0x4d, 0x63, 0xdf, 0x2a, 0xc2, 0x98, 0x22, 0xcd, 0x6b, 0x18, 0xfb, 0x04,
+ 0x9c, 0x10, 0x67, 0xc9, 0x2b, 0xfa, 0x6d, 0x11, 0xbe, 0x85, 0x0b, 0xac, 0x2c, 0xdc, 0x40, 0x1b,
+ 0xc9, 0x2c, 0x4c, 0xd3, 0xb2, 0x6e, 0x2d, 0x92, 0xd6, 0xd9, 0x3e, 0x42, 0x9c, 0x3e, 0x89, 0xfd,
+ 0x0e, 0xef, 0xd6, 0x8d, 0x54, 0x1e, 0xf6, 0x50, 0xa7, 0x2d, 0x73, 0xe5, 0x63, 0xb0, 0xcc, 0xfd,
+ 0x6d, 0x01, 0x46, 0x22, 0x79, 0x1d, 0xbb, 0x5d, 0x6e, 0x33, 0x69, 0x97, 0x9b, 0xcd, 0xdd, 0x1d,
+ 0xfa, 0x58, 0xe5, 0x7e, 0x71, 0x18, 0x12, 0xae, 0xd1, 0x64, 0x03, 0xce, 0x59, 0x99, 0x0e, 0x5e,
+ 0xb1, 0xd9, 0x26, 0xbc, 0xeb, 0xbb, 0xd4, 0x97, 0x12, 0xef, 0x82, 0x42, 0xba, 0x50, 0xdd, 0xa1,
+ 0x5e, 0x60, 0x19, 0x54, 0x7d, 0xdf, 0x62, 0x6e, 0x95, 0x4c, 0xda, 0x1e, 0x43, 0x99, 0xde, 0x94,
+ 0x0c, 0x30, 0x64, 0x45, 0x36, 0xa0, 0x42, 0xcd, 0x16, 0x55, 0x01, 0x75, 0x72, 0x86, 0xab, 0x0c,
+ 0xe5, 0xc9, 0xde, 0x7c, 0x14, 0xd0, 0xc4, 0x87, 0x9a, 0xad, 0xce, 0x96, 0x64, 0x3f, 0x1c, 0x5c,
+ 0xc1, 0x0a, 0x4f, 0xa9, 0xa2, 0xbb, 0xf6, 0x61, 0x12, 0x46, 0x7c, 0xc8, 0x76, 0x68, 0xe4, 0xaa,
+ 0x1c, 0xd1, 0xe4, 0x71, 0x17, 0x13, 0x97, 0x0f, 0xb5, 0x5b, 0x7a, 0x40, 0xbd, 0xb6, 0xee, 0x6d,
+ 0xcb, 0xdd, 0xc6, 0xe0, 0x5f, 0xf8, 0x92, 0x42, 0x8a, 0xbe, 0x30, 0x4c, 0xc2, 0x88, 0x0f, 0x71,
+ 0xa1, 0x16, 0x48, 0xf5, 0x59, 0x59, 0xf2, 0x06, 0x67, 0xaa, 0x14, 0x71, 0x5f, 0xba, 0x48, 0xab,
+ 0x57, 0x8c, 0x78, 0x90, 0x9d, 0x44, 0x28, 0x5f, 0x11, 0xc0, 0xb9, 0x91, 0xc3, 0x22, 0x2c, 0xa1,
+ 0xa2, 0xe5, 0x26, 0x3b, 0x24, 0xb0, 0xf6, 0x76, 0x25, 0x9a, 0x96, 0xef, 0xb7, 0x9d, 0xea, 0xe9,
+ 0xa4, 0x9d, 0xea, 0x42, 0xda, 0x4e, 0x95, 0x3a, 0xa2, 0x3c, 0xbc, 0x53, 0x65, 0xca, 0x42, 0x54,
+ 0x3e, 0x06, 0x0b, 0xd1, 0x53, 0x50, 0xdf, 0xe1, 0x33, 0x81, 0x88, 0xce, 0x53, 0xe1, 0xcb, 0x08,
+ 0x9f, 0xd9, 0x6f, 0x46, 0xc9, 0x18, 0xa7, 0x61, 0x45, 0x84, 0x06, 0x12, 0x85, 0x37, 0x95, 0x45,
+ 0x9a, 0x51, 0x32, 0xc6, 0x69, 0xb8, 0x3f, 0x96, 0xe5, 0x6c, 0x8b, 0x02, 0xc3, 0xbc, 0x80, 0xf0,
+ 0xc7, 0x52, 0x89, 0x18, 0xe5, 0x93, 0x4b, 0x50, 0xed, 0x9a, 0x9b, 0x82, 0xb6, 0xca, 0x69, 0xb9,
+ 0x86, 0xb9, 0x3e, 0xbf, 0x20, 0xa3, 0x05, 0xa9, 0x5c, 0x56, 0x93, 0xb6, 0xde, 0x51, 0x19, 0x7c,
+ 0x6f, 0x28, 0x6b, 0xb2, 0x12, 0x25, 0x63, 0x9c, 0x86, 0x7c, 0x0c, 0xc6, 0x3c, 0x6a, 0x76, 0x0d,
+ 0x1a, 0x96, 0x02, 0x5e, 0x8a, 0x5b, 0x45, 0x31, 0x91, 0x83, 0x29, 0xca, 0x3e, 0x76, 0xae, 0xfa,
+ 0x40, 0x76, 0xae, 0xef, 0x15, 0x80, 0xf4, 0xfa, 0x2f, 0x93, 0x2d, 0x18, 0x72, 0xb8, 0xf5, 0x2b,
+ 0x77, 0x40, 0xe4, 0x98, 0x11, 0x4d, 0x4c, 0x4b, 0x32, 0x41, 0xe2, 0x13, 0x07, 0xaa, 0xf4, 0x76,
+ 0x40, 0x3d, 0x27, 0xbc, 0xcf, 0x70, 0x34, 0xc1, 0x97, 0xc5, 0x6e, 0x40, 0x22, 0x63, 0xc8, 0x43,
+ 0xfb, 0x41, 0x11, 0xea, 0x31, 0xba, 0x7b, 0x6d, 0x2a, 0xf9, 0x95, 0x6a, 0x61, 0x74, 0x5a, 0xf7,
+ 0x6c, 0x39, 0xc2, 0x62, 0x57, 0xaa, 0x65, 0x16, 0x2e, 0x63, 0x9c, 0x8e, 0xcc, 0x00, 0xb4, 0x75,
+ 0x3f, 0xa0, 0x1e, 0x5f, 0x7d, 0x53, 0x17, 0x99, 0x57, 0xc2, 0x1c, 0x8c, 0x51, 0x91, 0x8b, 0x32,
+ 0x7c, 0x76, 0x39, 0x19, 0x78, 0xae, 0x4f, 0x6c, 0xec, 0xca, 0x11, 0xc4, 0xc6, 0x26, 0x2d, 0x18,
+ 0x57, 0xb5, 0x56, 0xb9, 0x87, 0x0b, 0x4b, 0x26, 0xf6, 0x2f, 0x29, 0x08, 0xec, 0x01, 0xd5, 0xde,
+ 0x2e, 0xc0, 0x68, 0xc2, 0xe4, 0x21, 0x42, 0xc6, 0x29, 0xef, 0xfb, 0x44, 0xc8, 0xb8, 0x98, 0xd3,
+ 0xfc, 0x93, 0x30, 0x24, 0x04, 0x94, 0x76, 0xaa, 0x13, 0x22, 0x44, 0x99, 0xcb, 0xe6, 0x32, 0x69,
+ 0x54, 0x4d, 0xcf, 0x65, 0xd2, 0xea, 0x8a, 0x2a, 0x5f, 0x98, 0xdb, 0x45, 0xed, 0x7a, 0xcd, 0xed,
+ 0x22, 0x1d, 0x43, 0x0a, 0xed, 0x87, 0x25, 0xe0, 0x2e, 0x28, 0xe4, 0x39, 0xa8, 0xb5, 0xa9, 0xb1,
+ 0xa5, 0x3b, 0x96, 0xaf, 0x42, 0x46, 0xb2, 0xdd, 0x6d, 0x6d, 0x45, 0x25, 0xde, 0x61, 0x00, 0xb3,
+ 0xcd, 0x65, 0xee, 0xe5, 0x1d, 0xd1, 0x12, 0x03, 0x86, 0x5a, 0xbe, 0xaf, 0x77, 0xac, 0xdc, 0x27,
+ 0xa0, 0x22, 0x44, 0x9f, 0x18, 0x44, 0xe2, 0x19, 0x25, 0x34, 0x31, 0xa0, 0xd2, 0xb1, 0x75, 0xcb,
+ 0xc9, 0xfd, 0x8f, 0x12, 0xf6, 0x05, 0xab, 0x0c, 0x49, 0x98, 0x74, 0xf8, 0x23, 0x0a, 0x6c, 0xd2,
+ 0x85, 0xba, 0x6f, 0x78, 0x7a, 0xdb, 0xdf, 0xd2, 0x67, 0x9e, 0x79, 0x36, 0xb7, 0x92, 0x14, 0xb1,
+ 0x12, 0x73, 0xf6, 0x1c, 0xce, 0xae, 0x34, 0xaf, 0xcc, 0xce, 0x3c, 0xf3, 0x2c, 0xc6, 0xf9, 0xc4,
+ 0xd9, 0x3e, 0xf3, 0xd4, 0x8c, 0xec, 0xf7, 0x47, 0xce, 0xf6, 0x99, 0xa7, 0x66, 0x30, 0xce, 0x47,
+ 0xfb, 0x8f, 0x02, 0xd4, 0x42, 0x5a, 0xb2, 0x0e, 0xc0, 0x46, 0xa0, 0x0c, 0xaa, 0x77, 0xa8, 0x00,
+ 0xf7, 0x7c, 0x57, 0xbc, 0x1e, 0x16, 0xc6, 0x18, 0x50, 0x46, 0xd4, 0xc1, 0xe2, 0x51, 0x47, 0x1d,
+ 0x9c, 0x86, 0xda, 0x96, 0xee, 0x98, 0xfe, 0x96, 0xbe, 0x2d, 0x26, 0xa2, 0x58, 0x1c, 0xce, 0x2b,
+ 0x2a, 0x03, 0x23, 0x1a, 0xed, 0x8f, 0x87, 0x40, 0x1c, 0x5b, 0xb2, 0xa1, 0x62, 0x5a, 0xbe, 0xf0,
+ 0x9b, 0x2d, 0xf0, 0x92, 0xe1, 0x50, 0x99, 0x97, 0xe9, 0x18, 0x52, 0x90, 0xb3, 0x50, 0x6a, 0x5b,
+ 0x8e, 0x3c, 0xf1, 0xe0, 0x06, 0xaf, 0x15, 0xcb, 0x41, 0x96, 0xc6, 0xb3, 0xf4, 0xdb, 0xd2, 0xe5,
+ 0x49, 0x64, 0xe9, 0xb7, 0x91, 0xa5, 0xb1, 0x2d, 0xa8, 0xed, 0xba, 0xdb, 0x1b, 0xba, 0xb1, 0xad,
+ 0x3c, 0xa3, 0xca, 0x7c, 0x21, 0xe4, 0x5b, 0xd0, 0xe5, 0x64, 0x16, 0xa6, 0x69, 0xc9, 0x3a, 0x3c,
+ 0xf2, 0x06, 0xf5, 0x5c, 0x39, 0xca, 0x9b, 0x36, 0xa5, 0x1d, 0x05, 0x23, 0x54, 0x08, 0xee, 0x60,
+ 0xf5, 0x99, 0x6c, 0x12, 0xec, 0x57, 0x96, 0xbb, 0x6a, 0xea, 0x5e, 0x8b, 0x06, 0xab, 0x9e, 0x6b,
+ 0x50, 0xdf, 0xb7, 0x9c, 0x96, 0x82, 0x1d, 0x8a, 0x60, 0xd7, 0xb2, 0x49, 0xb0, 0x5f, 0x59, 0xf2,
+ 0x32, 0x4c, 0x88, 0x2c, 0xb1, 0xd8, 0xce, 0xee, 0xe8, 0x96, 0xad, 0x6f, 0x58, 0xb6, 0xfa, 0xe9,
+ 0xd6, 0xa8, 0x38, 0x57, 0x58, 0xeb, 0x43, 0x83, 0x7d, 0x4b, 0x93, 0xab, 0x30, 0xae, 0x4e, 0x95,
+ 0x56, 0xa9, 0xd7, 0x0c, 0x8f, 0xb2, 0x47, 0x1b, 0x17, 0xd8, 0x7e, 0x6f, 0x9e, 0x76, 0x3c, 0x6a,
+ 0x70, 0xad, 0x2b, 0x45, 0x85, 0x3d, 0xe5, 0x08, 0xc2, 0x19, 0x7e, 0x5e, 0xbd, 0xde, 0x99, 0x73,
+ 0x5d, 0xdb, 0x74, 0x6f, 0x39, 0xea, 0xdb, 0x85, 0x62, 0xc3, 0x0f, 0x92, 0x9a, 0x99, 0x14, 0xd8,
+ 0xa7, 0x24, 0xfb, 0x72, 0x9e, 0x33, 0xef, 0xde, 0x72, 0xd2, 0xa8, 0x10, 0x7d, 0x79, 0xb3, 0x0f,
+ 0x0d, 0xf6, 0x2d, 0x4d, 0x16, 0x80, 0xa4, 0xbf, 0x60, 0xbd, 0xc3, 0x95, 0xa1, 0xd1, 0xc6, 0x19,
+ 0x11, 0x1f, 0x23, 0x9d, 0x8b, 0x19, 0x25, 0xc8, 0x32, 0x9c, 0x4e, 0xa7, 0x32, 0x76, 0xdc, 0x49,
+ 0x7e, 0x54, 0x44, 0xc6, 0xc4, 0x8c, 0x7c, 0xcc, 0x2c, 0xa5, 0xfd, 0x49, 0x11, 0x46, 0x13, 0x17,
+ 0xaa, 0x1f, 0xb8, 0x8b, 0xab, 0x4c, 0x03, 0x6d, 0xfb, 0xad, 0xa5, 0xf9, 0x2b, 0x54, 0x37, 0xa9,
+ 0x77, 0x8d, 0xaa, 0xcb, 0xef, 0x7c, 0x52, 0x59, 0x49, 0xe4, 0x60, 0x8a, 0x92, 0x6c, 0x42, 0x45,
+ 0xd8, 0x53, 0xf3, 0xfe, 0x2a, 0x41, 0xc9, 0x88, 0x1b, 0x55, 0xf9, 0x92, 0x23, 0x4c, 0xaa, 0x02,
+ 0x5e, 0x0b, 0x60, 0x24, 0x4e, 0xc1, 0x26, 0x92, 0x48, 0x59, 0x1b, 0x4e, 0x28, 0x6a, 0x4b, 0x50,
+ 0x0a, 0x82, 0x41, 0xaf, 0xc4, 0x0a, 0xfb, 0xfc, 0xda, 0x32, 0x32, 0x0c, 0x6d, 0x93, 0xb5, 0x9d,
+ 0xef, 0x5b, 0xae, 0x23, 0xe3, 0x23, 0xaf, 0xc3, 0x70, 0x20, 0x4d, 0x54, 0x83, 0x5d, 0xe9, 0xe5,
+ 0xe6, 0x62, 0x65, 0x9e, 0x52, 0x58, 0xda, 0xdf, 0x15, 0xa1, 0x16, 0x6e, 0x27, 0x0f, 0x10, 0x77,
+ 0xd8, 0x85, 0x5a, 0xe8, 0x6f, 0x93, 0xfb, 0xb7, 0x67, 0x91, 0x1b, 0x08, 0xdf, 0x01, 0x85, 0xaf,
+ 0x18, 0xf1, 0x88, 0xfb, 0xf2, 0x94, 0x72, 0xf8, 0xf2, 0x74, 0x60, 0x38, 0xf0, 0xac, 0x56, 0x4b,
+ 0xea, 0xb6, 0x79, 0x9c, 0x79, 0x42, 0x71, 0xad, 0x09, 0x40, 0x29, 0x59, 0xf1, 0x82, 0x8a, 0x8d,
+ 0xf6, 0x1a, 0x8c, 0xa7, 0x29, 0xb9, 0xe2, 0x67, 0x6c, 0x51, 0xb3, 0x6b, 0x2b, 0x19, 0x47, 0x8a,
+ 0x9f, 0x4c, 0xc7, 0x90, 0x82, 0x6d, 0xfe, 0x58, 0x33, 0xbd, 0xe1, 0x3a, 0x6a, 0x5b, 0xcd, 0x75,
+ 0xe8, 0x35, 0x99, 0x86, 0x61, 0xae, 0xf6, 0x2f, 0x25, 0x38, 0x1b, 0x19, 0x05, 0x56, 0x74, 0x47,
+ 0x6f, 0x1d, 0xe0, 0x5f, 0x57, 0xef, 0x5f, 0x92, 0x38, 0x6c, 0xf0, 0xf8, 0xd2, 0x03, 0x10, 0x3c,
+ 0xfe, 0x47, 0x05, 0xe0, 0xbe, 0x81, 0xe4, 0x0b, 0x30, 0xa2, 0xc7, 0x7e, 0x73, 0x28, 0x9b, 0xf3,
+ 0x72, 0xee, 0xe6, 0xe4, 0x2e, 0x88, 0xa1, 0xaf, 0x4b, 0x3c, 0x15, 0x13, 0x0c, 0x89, 0x0b, 0xd5,
+ 0x4d, 0xdd, 0xb6, 0x99, 0x2e, 0x94, 0xfb, 0x90, 0x23, 0xc1, 0x9c, 0x77, 0xf3, 0x05, 0x09, 0x8d,
+ 0x21, 0x13, 0xed, 0x9f, 0x0b, 0x30, 0xda, 0xb4, 0x2d, 0xd3, 0x72, 0x5a, 0xc7, 0x18, 0x35, 0xfe,
+ 0x06, 0x54, 0x7c, 0xdb, 0x32, 0xe9, 0x80, 0xf3, 0xb8, 0x58, 0x41, 0x18, 0x00, 0x0a, 0x9c, 0x64,
+ 0x18, 0xfa, 0xd2, 0x01, 0xc2, 0xd0, 0xff, 0x64, 0x08, 0xa4, 0x7f, 0x29, 0xe9, 0x42, 0xad, 0xa5,
+ 0xa2, 0x5b, 0xcb, 0x6f, 0xbc, 0x92, 0x23, 0x32, 0x5a, 0x22, 0x4e, 0xb6, 0x98, 0x75, 0xc3, 0x44,
+ 0x8c, 0x38, 0x11, 0x9a, 0xfc, 0xb3, 0xe5, 0x7c, 0xce, 0x3f, 0x5b, 0x0a, 0x76, 0xbd, 0xff, 0xb6,
+ 0xd4, 0xa1, 0xbc, 0x15, 0x04, 0x1d, 0x39, 0xae, 0x06, 0x77, 0x20, 0x8e, 0x82, 0x73, 0x08, 0x6d,
+ 0x84, 0xbd, 0x23, 0x87, 0x66, 0x2c, 0x1c, 0x3d, 0xfc, 0xa1, 0xd2, 0x5c, 0xae, 0x83, 0xee, 0x38,
+ 0x0b, 0xf6, 0x8e, 0x1c, 0x9a, 0x7c, 0x1e, 0xea, 0x81, 0xa7, 0x3b, 0xfe, 0xa6, 0xeb, 0xb5, 0xa9,
+ 0x27, 0x77, 0x87, 0x0b, 0x39, 0x7e, 0xee, 0xb8, 0x16, 0xa1, 0x89, 0x13, 0xb4, 0x44, 0x12, 0xc6,
+ 0xb9, 0x91, 0x6d, 0xa8, 0x76, 0x4d, 0x51, 0x31, 0x69, 0x36, 0x99, 0xcd, 0xf3, 0xbf, 0xce, 0xd8,
+ 0x31, 0xb6, 0x7a, 0xc3, 0x90, 0x41, 0xf2, 0xdf, 0x61, 0xc3, 0x47, 0xf5, 0xef, 0xb0, 0x78, 0x6f,
+ 0xcc, 0x8a, 0x1c, 0x40, 0xda, 0x52, 0xa3, 0x74, 0x5a, 0xd2, 0x0b, 0x67, 0x21, 0xb7, 0xb2, 0x27,
+ 0x58, 0xd6, 0x43, 0xad, 0xd4, 0x69, 0xa1, 0xe2, 0xa1, 0xb5, 0x41, 0x5a, 0xb7, 0x89, 0x91, 0xf8,
+ 0xc3, 0x86, 0xb8, 0xce, 0x32, 0x7d, 0xb0, 0xf9, 0x20, 0xfc, 0xd5, 0x43, 0x2c, 0xc2, 0x6f, 0xe6,
+ 0xaf, 0x34, 0xb4, 0xbf, 0x2f, 0x42, 0x69, 0x6d, 0xb9, 0x29, 0xa2, 0xf6, 0xf1, 0xdf, 0xd7, 0xd0,
+ 0xe6, 0xb6, 0xd5, 0xb9, 0x49, 0x3d, 0x6b, 0x73, 0x57, 0x6e, 0x7a, 0x63, 0x51, 0xfb, 0xd2, 0x14,
+ 0x98, 0x51, 0x8a, 0xbc, 0x02, 0x23, 0x86, 0x3e, 0x47, 0xbd, 0x60, 0x90, 0x2d, 0x3d, 0xbf, 0xb7,
+ 0x37, 0x37, 0x1b, 0x15, 0xc7, 0x04, 0x18, 0x59, 0x07, 0x30, 0x22, 0xe8, 0xd2, 0xa1, 0x0d, 0x11,
+ 0x31, 0xe0, 0x18, 0x10, 0x41, 0xa8, 0x6d, 0x33, 0x52, 0x8e, 0x5a, 0x3e, 0x0c, 0x2a, 0xef, 0x39,
+ 0xd7, 0x54, 0x59, 0x8c, 0x60, 0x34, 0x07, 0x46, 0x13, 0xbf, 0xdd, 0x20, 0x1f, 0x85, 0xaa, 0xdb,
+ 0x89, 0x4d, 0xa7, 0x35, 0xee, 0xef, 0x57, 0xbd, 0x21, 0xd3, 0xee, 0xec, 0x4d, 0x8e, 0x2e, 0xbb,
+ 0x2d, 0xcb, 0x50, 0x09, 0x18, 0x92, 0x13, 0x0d, 0x86, 0xf8, 0x65, 0x1b, 0xf5, 0xd3, 0x0d, 0xbe,
+ 0x76, 0xf0, 0xb8, 0xf8, 0x3e, 0xca, 0x1c, 0xed, 0x8b, 0x65, 0x88, 0xce, 0x84, 0x88, 0x0f, 0x43,
+ 0xc2, 0x99, 0x58, 0xce, 0xdc, 0xc7, 0xea, 0xb7, 0x2c, 0x59, 0x91, 0x16, 0x94, 0x5e, 0x73, 0x37,
+ 0x72, 0x4f, 0xdc, 0xb1, 0x5b, 0xb6, 0xc2, 0x4a, 0x15, 0x4b, 0x40, 0xc6, 0x81, 0xfc, 0x5a, 0x01,
+ 0x4e, 0xfa, 0x69, 0xa5, 0x53, 0x76, 0x07, 0xcc, 0xaf, 0x5d, 0xa7, 0xd5, 0x58, 0xe9, 0x98, 0xd9,
+ 0x2f, 0x1b, 0x7b, 0xeb, 0xc2, 0xe4, 0x2f, 0x0e, 0x6b, 0x64, 0x77, 0x5a, 0xcc, 0xf9, 0xab, 0xb8,
+ 0xa4, 0xfc, 0x93, 0x69, 0x28, 0x59, 0x69, 0x5f, 0x2e, 0x42, 0x3d, 0x36, 0x5b, 0xe7, 0xfe, 0x97,
+ 0xcb, 0xed, 0xd4, 0xbf, 0x5c, 0x56, 0x07, 0x3f, 0xbb, 0x8c, 0x6a, 0x75, 0xdc, 0xbf, 0x73, 0xf9,
+ 0xf3, 0x22, 0x94, 0xd6, 0xe7, 0x17, 0x92, 0xdb, 0xc5, 0xc2, 0x7d, 0xd8, 0x2e, 0x6e, 0xc1, 0xf0,
+ 0x46, 0xd7, 0xb2, 0x03, 0xcb, 0xc9, 0x1d, 0x07, 0x40, 0xfd, 0xfa, 0x46, 0x5e, 0xa7, 0x15, 0xa8,
+ 0xa8, 0xe0, 0x49, 0x0b, 0x86, 0x5b, 0x22, 0x10, 0x5b, 0x6e, 0x8f, 0x2e, 0x19, 0xd0, 0x4d, 0x30,
+ 0x92, 0x2f, 0xa8, 0xd0, 0xb5, 0x5d, 0x90, 0x3f, 0xcf, 0xbe, 0xef, 0xd2, 0xd4, 0x3e, 0x0f, 0xa1,
+ 0x16, 0x70, 0xff, 0x99, 0xff, 0x5b, 0x01, 0x92, 0x8a, 0xcf, 0xfd, 0xef, 0x4d, 0xdb, 0xe9, 0xde,
+ 0x34, 0x7f, 0x14, 0x83, 0x2f, 0xbb, 0x43, 0x69, 0x7f, 0x58, 0x84, 0xa1, 0xfb, 0x76, 0x77, 0x93,
+ 0x26, 0x9c, 0xd3, 0xe6, 0x72, 0x4e, 0x8c, 0x7d, 0x5d, 0xd3, 0xda, 0x29, 0xd7, 0xb4, 0xbc, 0x3f,
+ 0xeb, 0xbc, 0x87, 0x63, 0xda, 0x5f, 0x17, 0x40, 0x4e, 0xcb, 0x4b, 0x8e, 0x1f, 0xe8, 0x8e, 0xc1,
+ 0xff, 0x19, 0x2f, 0xd7, 0x80, 0xbc, 0x1e, 0x10, 0xd2, 0x4b, 0x48, 0x2c, 0xfb, 0xfc, 0x59, 0xcd,
+ 0xf9, 0xe4, 0xc3, 0x50, 0xdd, 0x72, 0xfd, 0x80, 0xcf, 0xf3, 0xc5, 0xa4, 0x5d, 0xe7, 0x8a, 0x4c,
+ 0xc7, 0x90, 0x22, 0x7d, 0x52, 0x58, 0xe9, 0x7f, 0x52, 0xa8, 0x7d, 0xb3, 0x08, 0x23, 0xef, 0x95,
+ 0x0b, 0xa8, 0x59, 0xae, 0x7c, 0xa5, 0x9c, 0xae, 0x7c, 0xe5, 0xc3, 0xb8, 0xf2, 0x69, 0xdf, 0x29,
+ 0x00, 0xdc, 0xb7, 0xdb, 0xaf, 0x66, 0xd2, 0xcb, 0x2e, 0x77, 0xbf, 0xca, 0xf6, 0xb1, 0xfb, 0xbd,
+ 0x8a, 0xfa, 0x24, 0xee, 0x61, 0xf7, 0x66, 0x01, 0xc6, 0xf4, 0x84, 0xd7, 0x5a, 0x6e, 0xd5, 0x32,
+ 0xe5, 0x04, 0x17, 0xde, 0xf4, 0x4b, 0xa6, 0x63, 0x8a, 0x2d, 0x79, 0x3e, 0x8a, 0xbc, 0x7a, 0x3d,
+ 0xea, 0xf6, 0x3d, 0x21, 0x53, 0xb9, 0x9a, 0x93, 0xa0, 0xbc, 0x87, 0x97, 0x60, 0xe9, 0x48, 0xbc,
+ 0x04, 0xe3, 0xf7, 0x9f, 0xca, 0x77, 0xbd, 0xff, 0xb4, 0x03, 0xb5, 0x4d, 0xcf, 0x6d, 0x73, 0x47,
+ 0x3c, 0xf9, 0x9b, 0xcf, 0xcb, 0x39, 0xd6, 0x94, 0xe8, 0x07, 0xd7, 0x91, 0x8d, 0x67, 0x41, 0xe1,
+ 0x63, 0xc4, 0x8a, 0x1b, 0xa4, 0x5d, 0xc1, 0x75, 0xe8, 0x28, 0xb9, 0x86, 0x73, 0xc9, 0x9a, 0x40,
+ 0x47, 0xc5, 0x26, 0xe9, 0x7c, 0x37, 0x7c, 0x7f, 0x9c, 0xef, 0xb4, 0x5f, 0x28, 0xab, 0x09, 0xec,
+ 0x81, 0x0b, 0xf2, 0xf7, 0xde, 0xbf, 0x35, 0x99, 0xbe, 0xd2, 0x38, 0x7c, 0x1f, 0xaf, 0x34, 0x56,
+ 0x07, 0x72, 0xf5, 0xda, 0x2b, 0x41, 0x6a, 0xdf, 0xf4, 0xfe, 0xe9, 0xc4, 0x7f, 0xa9, 0xd3, 0x89,
+ 0xb7, 0x8a, 0x10, 0x4d, 0x04, 0x87, 0xf4, 0xde, 0x78, 0x19, 0xaa, 0x6d, 0xfd, 0xf6, 0x3c, 0xb5,
+ 0xf5, 0xdd, 0x3c, 0xff, 0x66, 0x5c, 0x91, 0x18, 0x18, 0xa2, 0x11, 0x1f, 0xc0, 0x0a, 0xe3, 0x23,
+ 0xe7, 0xb6, 0x36, 0x47, 0xa1, 0x96, 0x85, 0x3d, 0x2b, 0x7a, 0xc7, 0x18, 0x1b, 0xed, 0xaf, 0x8a,
+ 0x20, 0x03, 0x69, 0x13, 0x0a, 0x95, 0x4d, 0xeb, 0x36, 0x35, 0x73, 0x7b, 0x32, 0xc6, 0xfe, 0x98,
+ 0x2b, 0xcc, 0xe9, 0x3c, 0x01, 0x05, 0x3a, 0xb7, 0x93, 0x8a, 0xe3, 0x11, 0x29, 0xbf, 0x1c, 0x76,
+ 0xd2, 0xf8, 0x31, 0x8b, 0xb4, 0x93, 0x8a, 0x24, 0x54, 0x3c, 0x84, 0x59, 0x96, 0x9f, 0x51, 0x4b,
+ 0x91, 0xe6, 0x31, 0xcb, 0xc6, 0xce, 0xba, 0x95, 0x59, 0xd6, 0x17, 0x77, 0x9a, 0x25, 0x8f, 0xc6,
+ 0x67, 0xbf, 0xfd, 0xdd, 0x0b, 0x0f, 0x7d, 0xe7, 0xbb, 0x17, 0x1e, 0x7a, 0xe7, 0xbb, 0x17, 0x1e,
+ 0xfa, 0xe2, 0xfe, 0x85, 0xc2, 0xb7, 0xf7, 0x2f, 0x14, 0xbe, 0xb3, 0x7f, 0xa1, 0xf0, 0xce, 0xfe,
+ 0x85, 0xc2, 0x3f, 0xee, 0x5f, 0x28, 0xfc, 0xf2, 0x3f, 0x5d, 0x78, 0xe8, 0x33, 0xcf, 0x45, 0x55,
+ 0x98, 0x56, 0x55, 0x98, 0x56, 0x0c, 0xa7, 0x3b, 0xdb, 0xad, 0x69, 0x56, 0x85, 0x28, 0x45, 0x55,
+ 0xe1, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x9c, 0x8e, 0xe2, 0xbf, 0x92, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -7436,39 +7437,44 @@ func (m *Scale) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ if m.ReplicasPerScaleDown != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.ReplicasPerScaleDown))
+ i--
+ dAtA[i] = 0x60
+ }
+ if m.ReplicasPerScaleUp != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.ReplicasPerScaleUp))
+ i--
+ dAtA[i] = 0x58
+ }
if m.ScaleDownCooldownSeconds != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.ScaleDownCooldownSeconds))
i--
- dAtA[i] = 0x58
+ dAtA[i] = 0x50
}
if m.ScaleUpCooldownSeconds != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.ScaleUpCooldownSeconds))
i--
- dAtA[i] = 0x50
+ dAtA[i] = 0x48
}
- if m.ReplicasPerScale != nil {
- i = encodeVarintGenerated(dAtA, i, uint64(*m.ReplicasPerScale))
+ if m.DeprecatedReplicasPerScale != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.DeprecatedReplicasPerScale))
i--
- dAtA[i] = 0x48
+ dAtA[i] = 0x40
}
if m.TargetBufferAvailability != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.TargetBufferAvailability))
i--
- dAtA[i] = 0x40
+ dAtA[i] = 0x38
}
if m.TargetProcessingSeconds != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.TargetProcessingSeconds))
i--
- dAtA[i] = 0x38
+ dAtA[i] = 0x30
}
if m.ZeroReplicaSleepSeconds != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.ZeroReplicaSleepSeconds))
i--
- dAtA[i] = 0x30
- }
- if m.DeprecatedCooldownSeconds != nil {
- i = encodeVarintGenerated(dAtA, i, uint64(*m.DeprecatedCooldownSeconds))
- i--
dAtA[i] = 0x28
}
if m.LookbackSeconds != nil {
@@ -10571,9 +10577,6 @@ func (m *Scale) Size() (n int) {
if m.LookbackSeconds != nil {
n += 1 + sovGenerated(uint64(*m.LookbackSeconds))
}
- if m.DeprecatedCooldownSeconds != nil {
- n += 1 + sovGenerated(uint64(*m.DeprecatedCooldownSeconds))
- }
if m.ZeroReplicaSleepSeconds != nil {
n += 1 + sovGenerated(uint64(*m.ZeroReplicaSleepSeconds))
}
@@ -10583,8 +10586,8 @@ func (m *Scale) Size() (n int) {
if m.TargetBufferAvailability != nil {
n += 1 + sovGenerated(uint64(*m.TargetBufferAvailability))
}
- if m.ReplicasPerScale != nil {
- n += 1 + sovGenerated(uint64(*m.ReplicasPerScale))
+ if m.DeprecatedReplicasPerScale != nil {
+ n += 1 + sovGenerated(uint64(*m.DeprecatedReplicasPerScale))
}
if m.ScaleUpCooldownSeconds != nil {
n += 1 + sovGenerated(uint64(*m.ScaleUpCooldownSeconds))
@@ -10592,6 +10595,12 @@ func (m *Scale) Size() (n int) {
if m.ScaleDownCooldownSeconds != nil {
n += 1 + sovGenerated(uint64(*m.ScaleDownCooldownSeconds))
}
+ if m.ReplicasPerScaleUp != nil {
+ n += 1 + sovGenerated(uint64(*m.ReplicasPerScaleUp))
+ }
+ if m.ReplicasPerScaleDown != nil {
+ n += 1 + sovGenerated(uint64(*m.ReplicasPerScaleDown))
+ }
return n
}
@@ -12226,13 +12235,14 @@ func (this *Scale) String() string {
`Min:` + valueToStringGenerated(this.Min) + `,`,
`Max:` + valueToStringGenerated(this.Max) + `,`,
`LookbackSeconds:` + valueToStringGenerated(this.LookbackSeconds) + `,`,
- `DeprecatedCooldownSeconds:` + valueToStringGenerated(this.DeprecatedCooldownSeconds) + `,`,
`ZeroReplicaSleepSeconds:` + valueToStringGenerated(this.ZeroReplicaSleepSeconds) + `,`,
`TargetProcessingSeconds:` + valueToStringGenerated(this.TargetProcessingSeconds) + `,`,
`TargetBufferAvailability:` + valueToStringGenerated(this.TargetBufferAvailability) + `,`,
- `ReplicasPerScale:` + valueToStringGenerated(this.ReplicasPerScale) + `,`,
+ `DeprecatedReplicasPerScale:` + valueToStringGenerated(this.DeprecatedReplicasPerScale) + `,`,
`ScaleUpCooldownSeconds:` + valueToStringGenerated(this.ScaleUpCooldownSeconds) + `,`,
`ScaleDownCooldownSeconds:` + valueToStringGenerated(this.ScaleDownCooldownSeconds) + `,`,
+ `ReplicasPerScaleUp:` + valueToStringGenerated(this.ReplicasPerScaleUp) + `,`,
+ `ReplicasPerScaleDown:` + valueToStringGenerated(this.ReplicasPerScaleDown) + `,`,
`}`,
}, "")
return s
@@ -26397,7 +26407,7 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
m.LookbackSeconds = &v
case 5:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedCooldownSeconds", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field ZeroReplicaSleepSeconds", wireType)
}
var v uint32
for shift := uint(0); ; shift += 7 {
@@ -26414,10 +26424,10 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
break
}
}
- m.DeprecatedCooldownSeconds = &v
+ m.ZeroReplicaSleepSeconds = &v
case 6:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ZeroReplicaSleepSeconds", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field TargetProcessingSeconds", wireType)
}
var v uint32
for shift := uint(0); ; shift += 7 {
@@ -26434,10 +26444,10 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
break
}
}
- m.ZeroReplicaSleepSeconds = &v
+ m.TargetProcessingSeconds = &v
case 7:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TargetProcessingSeconds", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field TargetBufferAvailability", wireType)
}
var v uint32
for shift := uint(0); ; shift += 7 {
@@ -26454,10 +26464,10 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
break
}
}
- m.TargetProcessingSeconds = &v
+ m.TargetBufferAvailability = &v
case 8:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TargetBufferAvailability", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedReplicasPerScale", wireType)
}
var v uint32
for shift := uint(0); ; shift += 7 {
@@ -26474,10 +26484,10 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
break
}
}
- m.TargetBufferAvailability = &v
+ m.DeprecatedReplicasPerScale = &v
case 9:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ReplicasPerScale", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field ScaleUpCooldownSeconds", wireType)
}
var v uint32
for shift := uint(0); ; shift += 7 {
@@ -26494,10 +26504,10 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
break
}
}
- m.ReplicasPerScale = &v
+ m.ScaleUpCooldownSeconds = &v
case 10:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ScaleUpCooldownSeconds", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field ScaleDownCooldownSeconds", wireType)
}
var v uint32
for shift := uint(0); ; shift += 7 {
@@ -26514,10 +26524,10 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
break
}
}
- m.ScaleUpCooldownSeconds = &v
+ m.ScaleDownCooldownSeconds = &v
case 11:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ScaleDownCooldownSeconds", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field ReplicasPerScaleUp", wireType)
}
var v uint32
for shift := uint(0); ; shift += 7 {
@@ -26534,7 +26544,27 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
break
}
}
- m.ScaleDownCooldownSeconds = &v
+ m.ReplicasPerScaleUp = &v
+ case 12:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ReplicasPerScaleDown", wireType)
+ }
+ var v uint32
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.ReplicasPerScaleDown = &v
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 8f12a27eca..588b26c9af 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -1264,41 +1264,47 @@ message Scale {
// +optional
optional uint32 lookbackSeconds = 4;
- // Deprecated: Use scaleUpCooldownSeconds and scaleDownCooldownSeconds instead.
- // Cooldown seconds after a scaling operation before another one.
- // +optional
- optional uint32 cooldownSeconds = 5;
-
// After scaling down the source vertex to 0, sleep how many seconds before scaling the source vertex back up to peek.
// +optional
- optional uint32 zeroReplicaSleepSeconds = 6;
+ optional uint32 zeroReplicaSleepSeconds = 5;
// TargetProcessingSeconds is used to tune the aggressiveness of autoscaling for source vertices, it measures how fast
// you want the vertex to process all the pending messages. Typically increasing the value, which leads to lower processing
// rate, thus less replicas. It's only effective for source vertices.
// +optional
- optional uint32 targetProcessingSeconds = 7;
+ optional uint32 targetProcessingSeconds = 6;
// TargetBufferAvailability is used to define the target percentage of the buffer availability.
// A valid and meaningful value should be less than the BufferUsageLimit defined in the Edge spec (or Pipeline spec), for example, 50.
// It only applies to UDF and Sink vertices because only they have buffers to read.
// +optional
- optional uint32 targetBufferAvailability = 8;
+ optional uint32 targetBufferAvailability = 7;
- // ReplicasPerScale defines maximum replicas can be scaled up or down at once.
- // The is use to prevent too aggressive scaling operations
+ // DeprecatedReplicasPerScale defines the number of maximum replicas that can be changed in a single scale up or down operation.
+ // The is use to prevent from too aggressive scaling operations
+ // Deprecated: Use ReplicasPerScaleUp and ReplicasPerScaleDown instead
// +optional
- optional uint32 replicasPerScale = 9;
+ optional uint32 replicasPerScale = 8;
// ScaleUpCooldownSeconds defines the cooldown seconds after a scaling operation, before a follow-up scaling up.
// It defaults to the CooldownSeconds if not set.
// +optional
- optional uint32 scaleUpCooldownSeconds = 10;
+ optional uint32 scaleUpCooldownSeconds = 9;
// ScaleDownCooldownSeconds defines the cooldown seconds after a scaling operation, before a follow-up scaling down.
// It defaults to the CooldownSeconds if not set.
// +optional
- optional uint32 scaleDownCooldownSeconds = 11;
+ optional uint32 scaleDownCooldownSeconds = 10;
+
+ // ReplicasPerScaleUp defines the number of maximum replicas that can be changed in a single scaled up operation.
+ // The is use to prevent from too aggressive scaling up operations
+ // +optional
+ optional uint32 replicasPerScaleUp = 11;
+
+ // ReplicasPerScaleDown defines the number of maximum replicas that can be changed in a single scaled down operation.
+ // The is use to prevent from too aggressive scaling down operations
+ // +optional
+ optional uint32 replicasPerScaleDown = 12;
}
// ServingSource is the HTTP endpoint for Numaflow.
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 79d63b30c4..7189543f3b 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -4167,13 +4167,6 @@ func schema_pkg_apis_numaflow_v1alpha1_Scale(ref common.ReferenceCallback) commo
Format: "int64",
},
},
- "cooldownSeconds": {
- SchemaProps: spec.SchemaProps{
- Description: "Deprecated: Use scaleUpCooldownSeconds and scaleDownCooldownSeconds instead. Cooldown seconds after a scaling operation before another one.",
- Type: []string{"integer"},
- Format: "int64",
- },
- },
"zeroReplicaSleepSeconds": {
SchemaProps: spec.SchemaProps{
Description: "After scaling down the source vertex to 0, sleep how many seconds before scaling the source vertex back up to peek.",
@@ -4197,7 +4190,7 @@ func schema_pkg_apis_numaflow_v1alpha1_Scale(ref common.ReferenceCallback) commo
},
"replicasPerScale": {
SchemaProps: spec.SchemaProps{
- Description: "ReplicasPerScale defines maximum replicas can be scaled up or down at once. The is use to prevent too aggressive scaling operations",
+ Description: "DeprecatedReplicasPerScale defines the number of maximum replicas that can be changed in a single scale up or down operation. The is use to prevent from too aggressive scaling operations Deprecated: Use ReplicasPerScaleUp and ReplicasPerScaleDown instead",
Type: []string{"integer"},
Format: "int64",
},
@@ -4216,6 +4209,20 @@ func schema_pkg_apis_numaflow_v1alpha1_Scale(ref common.ReferenceCallback) commo
Format: "int64",
},
},
+ "replicasPerScaleUp": {
+ SchemaProps: spec.SchemaProps{
+ Description: "ReplicasPerScaleUp defines the number of maximum replicas that can be changed in a single scaled up operation. The is use to prevent from too aggressive scaling up operations",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ "replicasPerScaleDown": {
+ SchemaProps: spec.SchemaProps{
+ Description: "ReplicasPerScaleDown defines the number of maximum replicas that can be changed in a single scaled down operation. The is use to prevent from too aggressive scaling down operations",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
},
},
},
diff --git a/pkg/apis/numaflow/v1alpha1/scale.go b/pkg/apis/numaflow/v1alpha1/scale.go
index c775468b17..9fbe58da04 100644
--- a/pkg/apis/numaflow/v1alpha1/scale.go
+++ b/pkg/apis/numaflow/v1alpha1/scale.go
@@ -31,35 +31,40 @@ type Scale struct {
// Lookback seconds to calculate the average pending messages and processing rate.
// +optional
LookbackSeconds *uint32 `json:"lookbackSeconds,omitempty" protobuf:"varint,4,opt,name=lookbackSeconds"`
- // Deprecated: Use scaleUpCooldownSeconds and scaleDownCooldownSeconds instead.
- // Cooldown seconds after a scaling operation before another one.
- // +optional
- DeprecatedCooldownSeconds *uint32 `json:"cooldownSeconds,omitempty" protobuf:"varint,5,opt,name=cooldownSeconds"`
// After scaling down the source vertex to 0, sleep how many seconds before scaling the source vertex back up to peek.
// +optional
- ZeroReplicaSleepSeconds *uint32 `json:"zeroReplicaSleepSeconds,omitempty" protobuf:"varint,6,opt,name=zeroReplicaSleepSeconds"`
+ ZeroReplicaSleepSeconds *uint32 `json:"zeroReplicaSleepSeconds,omitempty" protobuf:"varint,5,opt,name=zeroReplicaSleepSeconds"`
// TargetProcessingSeconds is used to tune the aggressiveness of autoscaling for source vertices, it measures how fast
// you want the vertex to process all the pending messages. Typically increasing the value, which leads to lower processing
// rate, thus less replicas. It's only effective for source vertices.
// +optional
- TargetProcessingSeconds *uint32 `json:"targetProcessingSeconds,omitempty" protobuf:"varint,7,opt,name=targetProcessingSeconds"`
+ TargetProcessingSeconds *uint32 `json:"targetProcessingSeconds,omitempty" protobuf:"varint,6,opt,name=targetProcessingSeconds"`
// TargetBufferAvailability is used to define the target percentage of the buffer availability.
// A valid and meaningful value should be less than the BufferUsageLimit defined in the Edge spec (or Pipeline spec), for example, 50.
// It only applies to UDF and Sink vertices because only they have buffers to read.
// +optional
- TargetBufferAvailability *uint32 `json:"targetBufferAvailability,omitempty" protobuf:"varint,8,opt,name=targetBufferAvailability"`
- // ReplicasPerScale defines maximum replicas can be scaled up or down at once.
- // The is use to prevent too aggressive scaling operations
+ TargetBufferAvailability *uint32 `json:"targetBufferAvailability,omitempty" protobuf:"varint,7,opt,name=targetBufferAvailability"`
+ // DeprecatedReplicasPerScale defines the number of maximum replicas that can be changed in a single scale up or down operation.
+ // The is use to prevent from too aggressive scaling operations
+ // Deprecated: Use ReplicasPerScaleUp and ReplicasPerScaleDown instead
// +optional
- ReplicasPerScale *uint32 `json:"replicasPerScale,omitempty" protobuf:"varint,9,opt,name=replicasPerScale"`
+ DeprecatedReplicasPerScale *uint32 `json:"replicasPerScale,omitempty" protobuf:"varint,8,opt,name=replicasPerScale"`
// ScaleUpCooldownSeconds defines the cooldown seconds after a scaling operation, before a follow-up scaling up.
// It defaults to the CooldownSeconds if not set.
// +optional
- ScaleUpCooldownSeconds *uint32 `json:"scaleUpCooldownSeconds,omitempty" protobuf:"varint,10,opt,name=scaleUpCooldownSeconds"`
+ ScaleUpCooldownSeconds *uint32 `json:"scaleUpCooldownSeconds,omitempty" protobuf:"varint,9,opt,name=scaleUpCooldownSeconds"`
// ScaleDownCooldownSeconds defines the cooldown seconds after a scaling operation, before a follow-up scaling down.
// It defaults to the CooldownSeconds if not set.
// +optional
- ScaleDownCooldownSeconds *uint32 `json:"scaleDownCooldownSeconds,omitempty" protobuf:"varint,11,opt,name=scaleDownCooldownSeconds"`
+ ScaleDownCooldownSeconds *uint32 `json:"scaleDownCooldownSeconds,omitempty" protobuf:"varint,10,opt,name=scaleDownCooldownSeconds"`
+ // ReplicasPerScaleUp defines the number of maximum replicas that can be changed in a single scaled up operation.
+ // The is use to prevent from too aggressive scaling up operations
+ // +optional
+ ReplicasPerScaleUp *uint32 `json:"replicasPerScaleUp,omitempty" protobuf:"varint,11,opt,name=replicasPerScaleUp"`
+ // ReplicasPerScaleDown defines the number of maximum replicas that can be changed in a single scaled down operation.
+ // The is use to prevent from too aggressive scaling down operations
+ // +optional
+ ReplicasPerScaleDown *uint32 `json:"replicasPerScaleDown,omitempty" protobuf:"varint,12,opt,name=replicasPerScaleDown"`
}
func (s Scale) GetLookbackSeconds() int {
@@ -73,9 +78,6 @@ func (s Scale) GetScaleUpCooldownSeconds() int {
if s.ScaleUpCooldownSeconds != nil {
return int(*s.ScaleUpCooldownSeconds)
}
- if s.DeprecatedCooldownSeconds != nil {
- return int(*s.DeprecatedCooldownSeconds)
- }
return DefaultCooldownSeconds
}
@@ -83,9 +85,6 @@ func (s Scale) GetScaleDownCooldownSeconds() int {
if s.ScaleDownCooldownSeconds != nil {
return int(*s.ScaleDownCooldownSeconds)
}
- if s.DeprecatedCooldownSeconds != nil {
- return int(*s.DeprecatedCooldownSeconds)
- }
return DefaultCooldownSeconds
}
@@ -110,9 +109,22 @@ func (s Scale) GetTargetBufferAvailability() int {
return DefaultTargetBufferAvailability
}
-func (s Scale) GetReplicasPerScale() int {
- if s.ReplicasPerScale != nil {
- return int(*s.ReplicasPerScale)
+func (s Scale) GetReplicasPerScaleUp() int {
+ if s.ReplicasPerScaleUp != nil {
+ return int(*s.ReplicasPerScaleUp)
+ }
+ if s.DeprecatedReplicasPerScale != nil {
+ return int(*s.DeprecatedReplicasPerScale)
+ }
+ return DefaultReplicasPerScale
+}
+
+func (s Scale) GetReplicasPerScaleDown() int {
+ if s.ReplicasPerScaleDown != nil {
+ return int(*s.ReplicasPerScaleDown)
+ }
+ if s.DeprecatedReplicasPerScale != nil {
+ return int(*s.DeprecatedReplicasPerScale)
}
return DefaultReplicasPerScale
}
diff --git a/pkg/apis/numaflow/v1alpha1/scale_test.go b/pkg/apis/numaflow/v1alpha1/scale_test.go
index 9bb09399eb..49bfbb9a91 100644
--- a/pkg/apis/numaflow/v1alpha1/scale_test.go
+++ b/pkg/apis/numaflow/v1alpha1/scale_test.go
@@ -30,14 +30,16 @@ func Test_Scale_Parameters(t *testing.T) {
assert.Equal(t, DefaultCooldownSeconds, s.GetScaleUpCooldownSeconds())
assert.Equal(t, DefaultCooldownSeconds, s.GetScaleDownCooldownSeconds())
assert.Equal(t, DefaultLookbackSeconds, s.GetLookbackSeconds())
- assert.Equal(t, DefaultReplicasPerScale, s.GetReplicasPerScale())
+ assert.Equal(t, DefaultReplicasPerScale, s.GetReplicasPerScaleUp())
+ assert.Equal(t, DefaultReplicasPerScale, s.GetReplicasPerScaleDown())
assert.Equal(t, DefaultTargetBufferAvailability, s.GetTargetBufferAvailability())
assert.Equal(t, DefaultTargetProcessingSeconds, s.GetTargetProcessingSeconds())
assert.Equal(t, DefaultZeroReplicaSleepSeconds, s.GetZeroReplicaSleepSeconds())
upcds := uint32(100)
downcds := uint32(99)
lbs := uint32(101)
- rps := uint32(3)
+ rpsu := uint32(3)
+ rpsd := uint32(4)
tps := uint32(102)
tbu := uint32(33)
zrss := uint32(44)
@@ -47,7 +49,8 @@ func Test_Scale_Parameters(t *testing.T) {
ScaleUpCooldownSeconds: &upcds,
ScaleDownCooldownSeconds: &downcds,
LookbackSeconds: &lbs,
- ReplicasPerScale: &rps,
+ ReplicasPerScaleUp: &rpsu,
+ ReplicasPerScaleDown: &rpsd,
TargetProcessingSeconds: &tps,
TargetBufferAvailability: &tbu,
ZeroReplicaSleepSeconds: &zrss,
@@ -57,7 +60,8 @@ func Test_Scale_Parameters(t *testing.T) {
assert.Equal(t, int(upcds), s.GetScaleUpCooldownSeconds())
assert.Equal(t, int(downcds), s.GetScaleDownCooldownSeconds())
assert.Equal(t, int(lbs), s.GetLookbackSeconds())
- assert.Equal(t, int(rps), s.GetReplicasPerScale())
+ assert.Equal(t, int(rpsu), s.GetReplicasPerScaleUp())
+ assert.Equal(t, int(rpsd), s.GetReplicasPerScaleDown())
assert.Equal(t, int(tbu), s.GetTargetBufferAvailability())
assert.Equal(t, int(tps), s.GetTargetProcessingSeconds())
assert.Equal(t, int(zrss), s.GetZeroReplicaSleepSeconds())
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index b450852954..81d1a53700 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -2035,11 +2035,6 @@ func (in *Scale) DeepCopyInto(out *Scale) {
*out = new(uint32)
**out = **in
}
- if in.DeprecatedCooldownSeconds != nil {
- in, out := &in.DeprecatedCooldownSeconds, &out.DeprecatedCooldownSeconds
- *out = new(uint32)
- **out = **in
- }
if in.ZeroReplicaSleepSeconds != nil {
in, out := &in.ZeroReplicaSleepSeconds, &out.ZeroReplicaSleepSeconds
*out = new(uint32)
@@ -2055,8 +2050,8 @@ func (in *Scale) DeepCopyInto(out *Scale) {
*out = new(uint32)
**out = **in
}
- if in.ReplicasPerScale != nil {
- in, out := &in.ReplicasPerScale, &out.ReplicasPerScale
+ if in.DeprecatedReplicasPerScale != nil {
+ in, out := &in.DeprecatedReplicasPerScale, &out.DeprecatedReplicasPerScale
*out = new(uint32)
**out = **in
}
@@ -2070,6 +2065,16 @@ func (in *Scale) DeepCopyInto(out *Scale) {
*out = new(uint32)
**out = **in
}
+ if in.ReplicasPerScaleUp != nil {
+ in, out := &in.ReplicasPerScaleUp, &out.ReplicasPerScaleUp
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.ReplicasPerScaleDown != nil {
+ in, out := &in.ReplicasPerScaleDown, &out.ReplicasPerScaleDown
+ *out = new(uint32)
+ **out = **in
+ }
return
}
diff --git a/pkg/reconciler/monovertex/scaling/scaling.go b/pkg/reconciler/monovertex/scaling/scaling.go
index 7557b236b4..fa085fc2b4 100644
--- a/pkg/reconciler/monovertex/scaling/scaling.go
+++ b/pkg/reconciler/monovertex/scaling/scaling.go
@@ -239,11 +239,11 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
if current > max || current < min { // Someone might have manually scaled up/down the MonoVertex
return s.patchMonoVertexReplicas(ctx, monoVtx, desired)
}
- maxAllowed := int32(monoVtx.Spec.Scale.GetReplicasPerScale())
if desired < current {
+ maxAllowedDown := int32(monoVtx.Spec.Scale.GetReplicasPerScaleDown())
diff := current - desired
- if diff > maxAllowed {
- diff = maxAllowed
+ if diff > maxAllowedDown {
+ diff = maxAllowedDown
}
if secondsSinceLastScale < scaleDownCooldown {
log.Infof("Cooldown period for scaling down, skip scaling.")
@@ -252,9 +252,10 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
return s.patchMonoVertexReplicas(ctx, monoVtx, current-diff) // We scale down gradually
}
if desired > current {
+ maxAllowedUp := int32(monoVtx.Spec.Scale.GetReplicasPerScaleUp())
diff := desired - current
- if diff > maxAllowed {
- diff = maxAllowed
+ if diff > maxAllowedUp {
+ diff = maxAllowedUp
}
if secondsSinceLastScale < scaleUpCooldown {
log.Infof("Cooldown period for scaling up, skip scaling.")
diff --git a/pkg/reconciler/vertex/scaling/scaling.go b/pkg/reconciler/vertex/scaling/scaling.go
index 741f11a668..03f581e6d7 100644
--- a/pkg/reconciler/vertex/scaling/scaling.go
+++ b/pkg/reconciler/vertex/scaling/scaling.go
@@ -311,11 +311,11 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
if current > max || current < min { // Someone might have manually scaled up/down the vertex
return s.patchVertexReplicas(ctx, vertex, desired)
}
- maxAllowed := int32(vertex.Spec.Scale.GetReplicasPerScale())
if desired < current {
+ maxAllowedDown := int32(vertex.Spec.Scale.GetReplicasPerScaleDown())
diff := current - desired
- if diff > maxAllowed {
- diff = maxAllowed
+ if diff > maxAllowedDown {
+ diff = maxAllowedDown
}
if secondsSinceLastScale < scaleDownCooldown {
log.Infof("Cooldown period for scaling down, skip scaling.")
@@ -338,9 +338,10 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
log.Infof("Vertex %s has back pressure in downstream vertices, skip scaling.", key)
return nil
}
+ maxAllowedUp := int32(vertex.Spec.Scale.GetReplicasPerScaleUp())
diff := desired - current
- if diff > maxAllowed {
- diff = maxAllowed
+ if diff > maxAllowedUp {
+ diff = maxAllowedUp
}
if secondsSinceLastScale < scaleUpCooldown {
log.Infof("Cooldown period for scaling up, skip scaling.")
@@ -375,7 +376,7 @@ func (s *Scaler) desiredReplicas(_ context.Context, vertex *dfv1.Vertex, partiti
// then we figure out how many replicas are needed to keep the available buffer length at target level.
if pending >= partitionBufferLengths[i] {
// Simply return current replica number + max allowed if the pending messages are more than available buffer length
- desired = int32(vertex.Status.Replicas) + int32(vertex.Spec.Scale.GetReplicasPerScale())
+ desired = int32(vertex.Status.Replicas) + int32(vertex.Spec.Scale.GetReplicasPerScaleUp())
} else {
singleReplicaContribution := float64(partitionBufferLengths[i]-pending) / float64(vertex.Status.Replicas)
desired = int32(math.Round(float64(partitionAvailableBufferLengths[i]) / singleReplicaContribution))
diff --git a/rust/numaflow-models/src/models/scale.rs b/rust/numaflow-models/src/models/scale.rs
index 5037968d7c..2cf2f89ccf 100644
--- a/rust/numaflow-models/src/models/scale.rs
+++ b/rust/numaflow-models/src/models/scale.rs
@@ -20,9 +20,6 @@ limitations under the License.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Scale {
- /// Deprecated: Use scaleUpCooldownSeconds and scaleDownCooldownSeconds instead. Cooldown seconds after a scaling operation before another one.
- #[serde(rename = "cooldownSeconds", skip_serializing_if = "Option::is_none")]
- pub cooldown_seconds: Option,
/// Whether to disable autoscaling. Set to \"true\" when using Kubernetes HPA or any other 3rd party autoscaling strategies.
#[serde(rename = "disabled", skip_serializing_if = "Option::is_none")]
pub disabled: Option,
@@ -35,9 +32,18 @@ pub struct Scale {
/// Minimum replicas.
#[serde(rename = "min", skip_serializing_if = "Option::is_none")]
pub min: Option,
- /// ReplicasPerScale defines maximum replicas can be scaled up or down at once. The is use to prevent too aggressive scaling operations
+ /// DeprecatedReplicasPerScale defines the number of maximum replicas that can be changed in a single scale up or down operation. The is use to prevent from too aggressive scaling operations Deprecated: Use ReplicasPerScaleUp and ReplicasPerScaleDown instead
#[serde(rename = "replicasPerScale", skip_serializing_if = "Option::is_none")]
pub replicas_per_scale: Option,
+ /// ReplicasPerScaleDown defines the number of maximum replicas that can be changed in a single scaled down operation. The is use to prevent from too aggressive scaling down operations
+ #[serde(
+ rename = "replicasPerScaleDown",
+ skip_serializing_if = "Option::is_none"
+ )]
+ pub replicas_per_scale_down: Option,
+ /// ReplicasPerScaleUp defines the number of maximum replicas that can be changed in a single scaled up operation. The is use to prevent from too aggressive scaling up operations
+ #[serde(rename = "replicasPerScaleUp", skip_serializing_if = "Option::is_none")]
+ pub replicas_per_scale_up: Option,
/// ScaleDownCooldownSeconds defines the cooldown seconds after a scaling operation, before a follow-up scaling down. It defaults to the CooldownSeconds if not set.
#[serde(
rename = "scaleDownCooldownSeconds",
@@ -74,12 +80,13 @@ impl Scale {
/// Scale defines the parameters for autoscaling.
pub fn new() -> Scale {
Scale {
- cooldown_seconds: None,
disabled: None,
lookback_seconds: None,
max: None,
min: None,
replicas_per_scale: None,
+ replicas_per_scale_down: None,
+ replicas_per_scale_up: None,
scale_down_cooldown_seconds: None,
scale_up_cooldown_seconds: None,
target_buffer_availability: None,
From 53d1131d82c8029e546c2f39305d1bcf80f1b60e Mon Sep 17 00:00:00 2001
From: xdevxy <115589853+xdevxy@users.noreply.github.com>
Date: Mon, 26 Aug 2024 11:31:13 -0700
Subject: [PATCH 032/188] fix: log format with config load error (#2000)
Signed-off-by: Hao Hao
---
pkg/reconciler/cmd/start.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pkg/reconciler/cmd/start.go b/pkg/reconciler/cmd/start.go
index 0565aefad1..0e5f86542c 100644
--- a/pkg/reconciler/cmd/start.go
+++ b/pkg/reconciler/cmd/start.go
@@ -51,7 +51,7 @@ import (
func Start(namespaced bool, managedNamespace string) {
logger := logging.NewLogger().Named("controller-manager")
config, err := reconciler.LoadConfig(func(err error) {
- logger.Errorf("Failed to reload global configuration file", zap.Error(err))
+ logger.Errorw("Failed to reload global configuration file", zap.Error(err))
})
if err != nil {
logger.Fatalw("Failed to load global configuration file", zap.Error(err))
From 79ed1d1f1423eac24b6c2edd2cf6aa56c6754324 Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Mon, 26 Aug 2024 17:02:45 -0700
Subject: [PATCH 033/188] chore: mark imageErr as unhealthy (#2004)
Signed-off-by: Sidhant Kohli
---
pkg/reconciler/util.go | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/pkg/reconciler/util.go b/pkg/reconciler/util.go
index f12573ab08..70f0fe7376 100644
--- a/pkg/reconciler/util.go
+++ b/pkg/reconciler/util.go
@@ -18,6 +18,7 @@ package reconciler
import (
"fmt"
+ "slices"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@@ -25,6 +26,10 @@ import (
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
)
+// unhealthyWaitingStatus contains the status messages for a pod in waiting state
+// which should be considered as unhealthy
+var unhealthyWaitingStatus = []string{"CrashLoopBackOff", "ImagePullBackOff"}
+
// CheckVertexPodsStatus checks the status by iterating over pods objects
func CheckVertexPodsStatus(vertexPods *corev1.PodList) (healthy bool, reason string, message string) {
// TODO: Need to revisit later.
@@ -45,7 +50,7 @@ func CheckVertexPodsStatus(vertexPods *corev1.PodList) (healthy bool, reason str
func isPodHealthy(pod *corev1.Pod) (healthy bool, reason string) {
for _, c := range pod.Status.ContainerStatuses {
- if c.State.Waiting != nil && c.State.Waiting.Reason == "CrashLoopBackOff" {
+ if c.State.Waiting != nil && slices.Contains(unhealthyWaitingStatus, c.State.Waiting.Reason) {
return false, c.State.Waiting.Reason
}
if c.State.Terminated != nil && c.State.Terminated.Reason == "Error" {
From 2ba54117d7015126c6894d196d42848bd2e37644 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Wed, 28 Aug 2024 16:50:14 -0700
Subject: [PATCH 034/188] feat: enable resourceClaims for vertex and monovtx
(#2009)
Signed-off-by: Derek Wang
---
api/json-schema/schema.json | 90 ++
api/openapi-spec/swagger.json | 90 ++
...w.numaproj.io_interstepbufferservices.yaml | 32 +
.../numaflow.numaproj.io_monovertices.yaml | 32 +
.../full/numaflow.numaproj.io_pipelines.yaml | 80 ++
.../full/numaflow.numaproj.io_vertices.yaml | 16 +
config/install.yaml | 160 +++
config/namespace-install.yaml | 160 +++
docs/APIs.md | 23 +
.../configuration/container-resources.md | 19 +-
.../configuration/pipeline-customization.md | 11 +-
.../configuration/pod-specifications.md | 37 +
mkdocs.yml | 1 +
pkg/apis/numaflow/v1alpha1/generated.pb.go | 994 ++++++++++--------
pkg/apis/numaflow/v1alpha1/generated.proto | 8 +
.../numaflow/v1alpha1/openapi_generated.go | 220 +++-
pkg/apis/numaflow/v1alpha1/pod_template.go | 10 +
.../numaflow/v1alpha1/pod_template_test.go | 6 +
.../v1alpha1/zz_generated.deepcopy.go | 7 +
rust/numaflow-models/Makefile | 1 +
.../src/models/abstract_pod_template.rs | 4 +
.../src/models/abstract_vertex.rs | 4 +
.../src/models/daemon_template.rs | 4 +
.../src/models/jet_stream_buffer_service.rs | 4 +
.../src/models/job_template.rs | 4 +
.../src/models/mono_vertex_spec.rs | 4 +
.../src/models/native_redis.rs | 4 +
.../models/side_inputs_manager_template.rs | 4 +
.../numaflow-models/src/models/vertex_spec.rs | 4 +
.../src/models/vertex_template.rs | 4 +
30 files changed, 1557 insertions(+), 480 deletions(-)
create mode 100644 docs/user-guide/reference/configuration/pod-specifications.md
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index e3601b73a4..66b8540090 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -17600,6 +17600,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -17717,6 +17726,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -18002,6 +18020,15 @@
"format": "int32",
"type": "integer"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -18766,6 +18793,15 @@
"format": "int32",
"type": "integer"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -18906,6 +18942,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -19165,6 +19210,15 @@
"format": "int32",
"type": "integer"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -19321,6 +19375,15 @@
"format": "int32",
"type": "integer"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -19946,6 +20009,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -20374,6 +20446,15 @@
"format": "int32",
"type": "integer"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -20545,6 +20626,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index 55540a4b09..a7eff01898 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -17605,6 +17605,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -17725,6 +17734,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -18007,6 +18025,15 @@
"type": "integer",
"format": "int32"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -18762,6 +18789,15 @@
"type": "integer",
"format": "int32"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -18902,6 +18938,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -19161,6 +19206,15 @@
"type": "integer",
"format": "int32"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -19317,6 +19371,15 @@
"type": "integer",
"format": "int32"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -19933,6 +19996,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -20356,6 +20428,15 @@
"type": "integer",
"format": "int32"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
@@ -20523,6 +20604,15 @@
"description": "If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/",
"type": "string"
},
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim"
+ },
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
"runtimeClassName": {
"description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
"type": "string"
diff --git a/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml b/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml
index c09fb798f4..acc540cd10 100644
--- a/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml
@@ -1044,6 +1044,22 @@ spec:
default: 3
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -2172,6 +2188,22 @@ spec:
default: 3
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index 7967f0362d..a26426074f 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -1428,6 +1428,22 @@ spec:
replicas:
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -2206,6 +2222,22 @@ spec:
default: 1
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
diff --git a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
index 00c4a0b008..070ba0b033 100644
--- a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
@@ -1904,6 +1904,22 @@ spec:
replicas:
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -2617,6 +2633,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -3508,6 +3540,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -4396,6 +4444,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -5935,6 +5999,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
diff --git a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
index cd7e10a99f..d1a77b9375 100644
--- a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
@@ -1583,6 +1583,22 @@ spec:
default: 1
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
diff --git a/config/install.yaml b/config/install.yaml
index cf347b8bcf..575bac0cc3 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -1043,6 +1043,22 @@ spec:
default: 3
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -2171,6 +2187,22 @@ spec:
default: 3
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -4040,6 +4072,22 @@ spec:
replicas:
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -4818,6 +4866,22 @@ spec:
default: 1
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
@@ -10064,6 +10128,22 @@ spec:
replicas:
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -10777,6 +10857,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -11668,6 +11764,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -12556,6 +12668,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -14095,6 +14223,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
@@ -19520,6 +19664,22 @@ spec:
default: 1
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index be25fca8be..3b3737b3a9 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -1043,6 +1043,22 @@ spec:
default: 3
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -2171,6 +2187,22 @@ spec:
default: 3
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -4040,6 +4072,22 @@ spec:
replicas:
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -4818,6 +4866,22 @@ spec:
default: 1
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
@@ -10064,6 +10128,22 @@ spec:
replicas:
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -10777,6 +10857,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -11668,6 +11764,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -12556,6 +12668,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
securityContext:
@@ -14095,6 +14223,22 @@ spec:
type: integer
priorityClassName:
type: string
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
@@ -19520,6 +19664,22 @@ spec:
default: 1
format: int32
type: integer
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ source:
+ properties:
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ type: object
+ required:
+ - name
+ type: object
+ type: array
runtimeClassName:
type: string
scale:
diff --git a/docs/APIs.md b/docs/APIs.md
index a5c42c14f7..46a865e83b 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -369,6 +369,29 @@ merged to the generated DNS configuration based on DNSPolicy.
+
+
+
+
+resourceClaims
+
+\[\]Kubernetes core/v1.PodResourceClaim
+
+
+
+
+(Optional)
+
+
+ResourceClaims defines which ResourceClaims must be allocated and
+reserved before the Pod is allowed to start. The resources will be made
+available to those containers which consume them by name.
+
+
+
+
+
+
diff --git a/docs/user-guide/reference/configuration/container-resources.md b/docs/user-guide/reference/configuration/container-resources.md
index 31e3e410d4..09609be34f 100644
--- a/docs/user-guide/reference/configuration/container-resources.md
+++ b/docs/user-guide/reference/configuration/container-resources.md
@@ -24,6 +24,8 @@ spec:
requests:
cpu: "1"
memory: 4Gi
+ claims:
+ - name: my-claim
```
## UDF Container
@@ -47,6 +49,8 @@ spec:
requests:
cpu: "1"
memory: 4Gi
+ claims:
+ - name: my-claim
```
## UDSource Container
@@ -71,6 +75,8 @@ spec:
requests:
cpu: "1"
memory: 4Gi
+ claims:
+ - name: my-claim
```
## Source Transformer Container
@@ -95,6 +101,8 @@ spec:
requests:
cpu: "1"
memory: 4Gi
+ claims:
+ - name: my-claim
```
## UDSink Container
@@ -109,6 +117,9 @@ metadata:
spec:
vertices:
- name: my-vertex
+ resourceClaims:
+ - name: my-claim
+ xxx
sink:
udsink:
container:
@@ -119,6 +130,8 @@ spec:
requests:
cpu: "1"
memory: 4Gi
+ claims:
+ - name: my-claim
```
## Init Container
@@ -136,11 +149,11 @@ spec:
initContainerTemplate:
resources:
limits:
- cpu: "3"
- memory: 6Gi
+ cpu: "2"
+ memory: 2Gi
requests:
cpu: "1"
- memory: 4Gi
+ memory: 1Gi
```
Container resources for [user init-containers](init-containers.md) are instead specified at `.spec.vertices[*].initContainers[*].resources`.
diff --git a/docs/user-guide/reference/configuration/pipeline-customization.md b/docs/user-guide/reference/configuration/pipeline-customization.md
index 55e9e2c466..d332fc8e6f 100644
--- a/docs/user-guide/reference/configuration/pipeline-customization.md
+++ b/docs/user-guide/reference/configuration/pipeline-customization.md
@@ -39,6 +39,9 @@ spec:
priorityClassName: my-priority-class-name
priority: 50
serviceAccountName: my-service-account
+ resourceClaims:
+ - name: my-claim
+ xxx
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
@@ -61,6 +64,8 @@ spec:
resources:
limits:
memory: 500Mi
+ claims:
+ - name: my-claim
initContainerTemplate:
env:
- name: MY_ENV_NAME
@@ -137,7 +142,6 @@ spec:
my-label-name: my-label-value
annotations:
my-annotation-name: my-annotation-value
- # Pod spec
nodeSelector:
my-node-label-name: my-node-label-value
tolerations:
@@ -210,6 +214,9 @@ spec:
priorityClassName: my-priority-class-name
priority: 50
serviceAccountName: my-service-account
+ resourceClaims:
+ - name: my-claim
+ xxx
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
@@ -228,6 +235,8 @@ spec:
resources:
limits:
memory: 500Mi
+ claims:
+ - name: my-claim
initContainerTemplate:
env:
- name: MY_ENV_NAME
diff --git a/docs/user-guide/reference/configuration/pod-specifications.md b/docs/user-guide/reference/configuration/pod-specifications.md
new file mode 100644
index 0000000000..0587ca169a
--- /dev/null
+++ b/docs/user-guide/reference/configuration/pod-specifications.md
@@ -0,0 +1,37 @@
+# Pod Specifications
+
+Most of the Kunernetes Pod specification fields are supported in the spec of `Pipeline`, `MonoVertex` and `InterStepBufferService`. Those fields include:
+
+- `nodeSelector`
+- `tolerations`
+- `securityContext`
+- `imagePullSecrets`
+- `priorityClassName`
+- `priority`
+- `affinity`
+- `serviceAccountName`
+- `runtimeClassName`
+- `automountServiceAccountToken`
+- `dnsPolicy`
+- `dnsConfig`
+- `resourceClaims`
+
+All the fields above are optional, click [here](../../../APIs.md#numaflow.numaproj.io/v1alpha1.AbstractPodTemplate) to see full list of supported fields.
+
+These fields can be specified in the `Pipeline` spec under:
+
+- `spec.vertices[*]`
+- `spec.templates.daemon`
+- `spec.templates.job`
+- `spec.templates.sideInputsManager`
+- `spec.templates.vertex`
+
+Or in the `MonoVertex` spec under:
+
+- `spec`
+- `spec.daemonTemplate`
+
+Or in `InterStepBufferService` spec at:
+
+- `spec.jetstream`
+- `spec.redis.native`
diff --git a/mkdocs.yml b/mkdocs.yml
index 43cd0a7950..7c9161d480 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -93,6 +93,7 @@ nav:
- user-guide/reference/multi-partition.md
- user-guide/reference/side-inputs.md
- Configuration:
+ - user-guide/reference/configuration/pod-specifications.md
- user-guide/reference/configuration/container-resources.md
- user-guide/reference/configuration/volumes.md
- user-guide/reference/configuration/environment-variables.md
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index b152ea965c..d905171bec 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -2704,472 +2704,474 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7439 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x1d, 0xd7,
- 0x75, 0xb6, 0xcf, 0x8d, 0x3c, 0x67, 0x1d, 0x92, 0xa2, 0xb6, 0x64, 0x99, 0x92, 0x65, 0x51, 0x19,
- 0xff, 0xf6, 0xaf, 0xfc, 0x49, 0xc8, 0xdf, 0xfc, 0x7d, 0x4b, 0xfe, 0x24, 0x36, 0x0f, 0x29, 0x52,
- 0x94, 0x48, 0x89, 0x59, 0x87, 0x94, 0x9d, 0xf8, 0x4f, 0xfc, 0x0f, 0x67, 0x36, 0x0f, 0xc7, 0x9c,
- 0x33, 0x73, 0x3c, 0x33, 0x87, 0x12, 0x9d, 0x16, 0xb9, 0x3d, 0xd8, 0x45, 0x5b, 0xb4, 0xc8, 0x53,
- 0x80, 0x22, 0x2d, 0x5a, 0x14, 0xc8, 0x43, 0x90, 0x3e, 0x14, 0x70, 0x1f, 0x0a, 0xf4, 0x8a, 0xa2,
- 0x4d, 0x8b, 0x5e, 0xf2, 0x50, 0xa0, 0x2e, 0x0a, 0x10, 0x0d, 0x8b, 0x3e, 0xb4, 0x40, 0x83, 0xa0,
- 0x01, 0x9a, 0x44, 0x08, 0x90, 0x62, 0xdf, 0xe6, 0x76, 0xe6, 0x48, 0xe4, 0x19, 0x52, 0x96, 0x5b,
- 0xbf, 0xcd, 0xec, 0xbd, 0xf6, 0xb7, 0xf6, 0xac, 0x7d, 0x5b, 0x7b, 0xed, 0xb5, 0xd7, 0xc0, 0x62,
- 0xcb, 0x0a, 0xb6, 0xba, 0x1b, 0x53, 0x86, 0xdb, 0x9e, 0x76, 0xba, 0x6d, 0xbd, 0xe3, 0xb9, 0xaf,
- 0xf1, 0x87, 0x4d, 0xdb, 0xbd, 0x35, 0xdd, 0xd9, 0x6e, 0x4d, 0xeb, 0x1d, 0xcb, 0x8f, 0x52, 0x76,
- 0x9e, 0xd2, 0xed, 0xce, 0x96, 0xfe, 0xd4, 0x74, 0x8b, 0x3a, 0xd4, 0xd3, 0x03, 0x6a, 0x4e, 0x75,
- 0x3c, 0x37, 0x70, 0xc9, 0x73, 0x11, 0xd0, 0x94, 0x02, 0x9a, 0x52, 0xc5, 0xa6, 0x3a, 0xdb, 0xad,
- 0x29, 0x06, 0x14, 0xa5, 0x28, 0xa0, 0x73, 0x1f, 0x89, 0xd5, 0xa0, 0xe5, 0xb6, 0xdc, 0x69, 0x8e,
- 0xb7, 0xd1, 0xdd, 0xe4, 0x6f, 0xfc, 0x85, 0x3f, 0x09, 0x3e, 0xe7, 0xb4, 0xed, 0xe7, 0xfd, 0x29,
- 0xcb, 0x65, 0xd5, 0x9a, 0x36, 0x5c, 0x8f, 0x4e, 0xef, 0xf4, 0xd4, 0xe5, 0xdc, 0xd3, 0x11, 0x4d,
- 0x5b, 0x37, 0xb6, 0x2c, 0x87, 0x7a, 0xbb, 0xea, 0x5b, 0xa6, 0x3d, 0xea, 0xbb, 0x5d, 0xcf, 0xa0,
- 0x87, 0x2a, 0xe5, 0x4f, 0xb7, 0x69, 0xa0, 0x67, 0xf1, 0x9a, 0xee, 0x57, 0xca, 0xeb, 0x3a, 0x81,
- 0xd5, 0xee, 0x65, 0xf3, 0xec, 0xbd, 0x0a, 0xf8, 0xc6, 0x16, 0x6d, 0xeb, 0xe9, 0x72, 0xda, 0x3f,
- 0xd4, 0xe0, 0xd4, 0xec, 0x86, 0x1f, 0x78, 0xba, 0x11, 0xac, 0xba, 0xe6, 0x1a, 0x6d, 0x77, 0x6c,
- 0x3d, 0xa0, 0x64, 0x1b, 0xaa, 0xac, 0x6e, 0xa6, 0x1e, 0xe8, 0x13, 0x85, 0x8b, 0x85, 0x4b, 0xf5,
- 0x99, 0xd9, 0xa9, 0x01, 0xdb, 0x62, 0x6a, 0x45, 0x02, 0x35, 0x46, 0xf6, 0xf7, 0x26, 0xab, 0xea,
- 0x0d, 0x43, 0x06, 0xe4, 0x6b, 0x05, 0x18, 0x71, 0x5c, 0x93, 0x36, 0xa9, 0x4d, 0x8d, 0xc0, 0xf5,
- 0x26, 0x8a, 0x17, 0x4b, 0x97, 0xea, 0x33, 0x9f, 0x1b, 0x98, 0x63, 0xc6, 0x17, 0x4d, 0x5d, 0x8f,
- 0x31, 0xb8, 0xec, 0x04, 0xde, 0x6e, 0xe3, 0xf4, 0xb7, 0xf7, 0x26, 0x1f, 0xda, 0xdf, 0x9b, 0x1c,
- 0x89, 0x67, 0x61, 0xa2, 0x26, 0x64, 0x1d, 0xea, 0x81, 0x6b, 0x33, 0x91, 0x59, 0xae, 0xe3, 0x4f,
- 0x94, 0x78, 0xc5, 0x2e, 0x4c, 0x09, 0x69, 0x33, 0xf6, 0x53, 0xac, 0xbb, 0x4c, 0xed, 0x3c, 0x35,
- 0xb5, 0x16, 0x92, 0x35, 0x4e, 0x49, 0xe0, 0x7a, 0x94, 0xe6, 0x63, 0x1c, 0x87, 0x50, 0x38, 0xe1,
- 0x53, 0xa3, 0xeb, 0x59, 0xc1, 0xee, 0x9c, 0xeb, 0x04, 0xf4, 0x76, 0x30, 0x51, 0xe6, 0x52, 0x7e,
- 0x32, 0x0b, 0x7a, 0xd5, 0x35, 0x9b, 0x49, 0xea, 0xc6, 0xa9, 0xfd, 0xbd, 0xc9, 0x13, 0xa9, 0x44,
- 0x4c, 0x63, 0x12, 0x07, 0xc6, 0xad, 0xb6, 0xde, 0xa2, 0xab, 0x5d, 0xdb, 0x6e, 0x52, 0xc3, 0xa3,
- 0x81, 0x3f, 0x51, 0xe1, 0x9f, 0x70, 0x29, 0x8b, 0xcf, 0xb2, 0x6b, 0xe8, 0xf6, 0x8d, 0x8d, 0xd7,
- 0xa8, 0x11, 0x20, 0xdd, 0xa4, 0x1e, 0x75, 0x0c, 0xda, 0x98, 0x90, 0x1f, 0x33, 0xbe, 0x94, 0x42,
- 0xc2, 0x1e, 0x6c, 0xb2, 0x08, 0x27, 0x3b, 0x9e, 0xe5, 0xf2, 0x2a, 0xd8, 0xba, 0xef, 0x5f, 0xd7,
- 0xdb, 0x74, 0x62, 0xe8, 0x62, 0xe1, 0x52, 0xad, 0x71, 0x56, 0xc2, 0x9c, 0x5c, 0x4d, 0x13, 0x60,
- 0x6f, 0x19, 0x72, 0x09, 0xaa, 0x2a, 0x71, 0x62, 0xf8, 0x62, 0xe1, 0x52, 0x45, 0xf4, 0x1d, 0x55,
- 0x16, 0xc3, 0x5c, 0xb2, 0x00, 0x55, 0x7d, 0x73, 0xd3, 0x72, 0x18, 0x65, 0x95, 0x8b, 0xf0, 0x7c,
- 0xd6, 0xa7, 0xcd, 0x4a, 0x1a, 0x81, 0xa3, 0xde, 0x30, 0x2c, 0x4b, 0xae, 0x02, 0xf1, 0xa9, 0xb7,
- 0x63, 0x19, 0x74, 0xd6, 0x30, 0xdc, 0xae, 0x13, 0xf0, 0xba, 0xd7, 0x78, 0xdd, 0xcf, 0xc9, 0xba,
- 0x93, 0x66, 0x0f, 0x05, 0x66, 0x94, 0x22, 0x2f, 0xc2, 0xb8, 0x1c, 0x76, 0x91, 0x14, 0x80, 0x23,
- 0x9d, 0x66, 0x82, 0xc4, 0x54, 0x1e, 0xf6, 0x50, 0x13, 0x13, 0xce, 0xeb, 0xdd, 0xc0, 0x6d, 0x33,
- 0xc8, 0x24, 0xd3, 0x35, 0x77, 0x9b, 0x3a, 0x13, 0xf5, 0x8b, 0x85, 0x4b, 0xd5, 0xc6, 0xc5, 0xfd,
- 0xbd, 0xc9, 0xf3, 0xb3, 0x77, 0xa1, 0xc3, 0xbb, 0xa2, 0x90, 0x1b, 0x50, 0x33, 0x1d, 0x7f, 0xd5,
- 0xb5, 0x2d, 0x63, 0x77, 0x62, 0x84, 0x57, 0xf0, 0x29, 0xf9, 0xa9, 0xb5, 0xf9, 0xeb, 0x4d, 0x91,
- 0x71, 0x67, 0x6f, 0xf2, 0x7c, 0xef, 0xec, 0x38, 0x15, 0xe6, 0x63, 0x84, 0x41, 0x56, 0x38, 0xe0,
- 0x9c, 0xeb, 0x6c, 0x5a, 0xad, 0x89, 0x51, 0xde, 0x1a, 0x17, 0xfb, 0x74, 0xe8, 0xf9, 0xeb, 0x4d,
- 0x41, 0xd7, 0x18, 0x95, 0xec, 0xc4, 0x2b, 0x46, 0x08, 0xe7, 0x5e, 0x80, 0x93, 0x3d, 0xa3, 0x96,
- 0x8c, 0x43, 0x69, 0x9b, 0xee, 0xf2, 0x49, 0xa9, 0x86, 0xec, 0x91, 0x9c, 0x86, 0xca, 0x8e, 0x6e,
- 0x77, 0xe9, 0x44, 0x91, 0xa7, 0x89, 0x97, 0x8f, 0x15, 0x9f, 0x2f, 0x68, 0xbf, 0x51, 0x82, 0x11,
- 0x35, 0x17, 0x34, 0x2d, 0x67, 0x9b, 0xbc, 0x04, 0x25, 0xdb, 0x6d, 0xc9, 0x19, 0xed, 0xe3, 0x03,
- 0xcf, 0x2f, 0xcb, 0x6e, 0xab, 0x31, 0xbc, 0xbf, 0x37, 0x59, 0x5a, 0x76, 0x5b, 0xc8, 0x10, 0x89,
- 0x01, 0x95, 0x6d, 0x7d, 0x73, 0x5b, 0xe7, 0x75, 0xa8, 0xcf, 0x34, 0x06, 0x86, 0xbe, 0xc6, 0x50,
- 0x58, 0x5d, 0x1b, 0xb5, 0xfd, 0xbd, 0xc9, 0x0a, 0x7f, 0x45, 0x81, 0x4d, 0x5c, 0xa8, 0x6d, 0xd8,
- 0xba, 0xb1, 0xbd, 0xe5, 0xda, 0x74, 0xa2, 0x94, 0x93, 0x51, 0x43, 0x21, 0x89, 0x06, 0x08, 0x5f,
- 0x31, 0xe2, 0x41, 0x0c, 0x18, 0xea, 0x9a, 0xbe, 0xe5, 0x6c, 0xcb, 0xd9, 0xe9, 0x85, 0x81, 0xb9,
- 0xad, 0xcf, 0xf3, 0x6f, 0x82, 0xfd, 0xbd, 0xc9, 0x21, 0xf1, 0x8c, 0x12, 0x5a, 0xfb, 0x5e, 0x1d,
- 0xc6, 0x54, 0x23, 0xdd, 0xa4, 0x5e, 0x40, 0x6f, 0x93, 0x8b, 0x50, 0x76, 0xd8, 0xa0, 0xe1, 0x8d,
- 0xdc, 0x18, 0x91, 0x7d, 0xb2, 0xcc, 0x07, 0x0b, 0xcf, 0x61, 0x35, 0x13, 0x0b, 0xae, 0x14, 0xf8,
- 0xe0, 0x35, 0x6b, 0x72, 0x18, 0x51, 0x33, 0xf1, 0x8c, 0x12, 0x9a, 0xbc, 0x02, 0x65, 0xfe, 0xf1,
- 0x42, 0xd4, 0x9f, 0x18, 0x9c, 0x05, 0xfb, 0xf4, 0x2a, 0xfb, 0x02, 0xfe, 0xe1, 0x1c, 0x94, 0x75,
- 0xc5, 0xae, 0xb9, 0x29, 0x05, 0xfb, 0xf1, 0x1c, 0x82, 0x5d, 0x10, 0x5d, 0x71, 0x7d, 0x7e, 0x01,
- 0x19, 0x22, 0xf9, 0xa5, 0x02, 0x9c, 0x34, 0x5c, 0x27, 0xd0, 0x99, 0x12, 0xa0, 0x96, 0xbf, 0x89,
- 0x0a, 0xe7, 0x73, 0x75, 0x60, 0x3e, 0x73, 0x69, 0xc4, 0xc6, 0xc3, 0x6c, 0x36, 0xef, 0x49, 0xc6,
- 0x5e, 0xde, 0xe4, 0x57, 0x0a, 0xf0, 0x30, 0x9b, 0x65, 0x7b, 0x88, 0xf9, 0xda, 0x70, 0xb4, 0xb5,
- 0x3a, 0xbb, 0xbf, 0x37, 0xf9, 0xf0, 0x52, 0x16, 0x33, 0xcc, 0xae, 0x03, 0xab, 0xdd, 0x29, 0xbd,
- 0x57, 0x61, 0xe0, 0xeb, 0x4e, 0x7d, 0x66, 0xf9, 0x28, 0x95, 0x90, 0xc6, 0xa3, 0xb2, 0x2b, 0x67,
- 0xe9, 0x5c, 0x98, 0x55, 0x0b, 0x72, 0x19, 0x86, 0x77, 0x5c, 0xbb, 0xdb, 0xa6, 0xfe, 0x44, 0x95,
- 0xaf, 0xdc, 0xe7, 0xb2, 0x26, 0xd4, 0x9b, 0x9c, 0xa4, 0x71, 0x42, 0xc2, 0x0f, 0x8b, 0x77, 0x1f,
- 0x55, 0x59, 0x62, 0xc1, 0x90, 0x6d, 0xb5, 0xad, 0xc0, 0xe7, 0x4b, 0x5a, 0x7d, 0xe6, 0xf2, 0xc0,
- 0x9f, 0x25, 0x86, 0xe8, 0x32, 0x07, 0x13, 0xa3, 0x46, 0x3c, 0xa3, 0x64, 0xc0, 0xa6, 0x42, 0xdf,
- 0xd0, 0x6d, 0xb1, 0xe4, 0xd5, 0x67, 0x3e, 0x39, 0xf8, 0xb0, 0x61, 0x28, 0x8d, 0x51, 0xf9, 0x4d,
- 0x15, 0xfe, 0x8a, 0x02, 0x9b, 0x7c, 0x16, 0xc6, 0x12, 0xad, 0xe9, 0x4f, 0xd4, 0xb9, 0x74, 0x1e,
- 0xcb, 0x92, 0x4e, 0x48, 0xd5, 0x38, 0x23, 0xc1, 0xc6, 0x12, 0x3d, 0xc4, 0xc7, 0x14, 0x18, 0xb9,
- 0x06, 0x55, 0xdf, 0x32, 0xa9, 0xa1, 0x7b, 0xfe, 0xc4, 0xc8, 0x41, 0x80, 0xc7, 0x25, 0x70, 0xb5,
- 0x29, 0x8b, 0x61, 0x08, 0x40, 0xa6, 0x00, 0x3a, 0xba, 0x17, 0x58, 0x42, 0x85, 0x1c, 0xe5, 0xea,
- 0xcc, 0xd8, 0xfe, 0xde, 0x24, 0xac, 0x86, 0xa9, 0x18, 0xa3, 0x60, 0xf4, 0xac, 0xec, 0x92, 0xd3,
- 0xe9, 0x06, 0xfe, 0xc4, 0xd8, 0xc5, 0xd2, 0xa5, 0x9a, 0xa0, 0x6f, 0x86, 0xa9, 0x18, 0xa3, 0x20,
- 0xdf, 0x2a, 0xc0, 0xa3, 0xd1, 0x6b, 0xef, 0x20, 0x3b, 0x71, 0xe4, 0x83, 0x6c, 0x72, 0x7f, 0x6f,
- 0xf2, 0xd1, 0x66, 0x7f, 0x96, 0x78, 0xb7, 0xfa, 0x68, 0x2f, 0xc1, 0xe8, 0x6c, 0x37, 0xd8, 0x72,
- 0x3d, 0xeb, 0x0d, 0xae, 0x0e, 0x93, 0x05, 0xa8, 0x04, 0x5c, 0xad, 0x11, 0xeb, 0xf2, 0x13, 0x59,
- 0xa2, 0x16, 0x2a, 0xe6, 0x35, 0xba, 0xab, 0xb4, 0x01, 0xb1, 0x3e, 0x0a, 0x35, 0x47, 0x14, 0xd7,
- 0x7e, 0xbd, 0x00, 0xb5, 0x86, 0xee, 0x5b, 0x06, 0x83, 0x27, 0x73, 0x50, 0xee, 0xfa, 0xd4, 0x3b,
- 0x1c, 0x28, 0x9f, 0xa5, 0xd7, 0x7d, 0xea, 0x21, 0x2f, 0x4c, 0x6e, 0x40, 0xb5, 0xa3, 0xfb, 0xfe,
- 0x2d, 0xd7, 0x33, 0xe5, 0x4a, 0x73, 0x40, 0x20, 0xa1, 0xaf, 0xca, 0xa2, 0x18, 0x82, 0x68, 0x75,
- 0x88, 0x96, 0x5a, 0xed, 0x07, 0x05, 0x38, 0xd5, 0xe8, 0x6e, 0x6e, 0x52, 0x4f, 0xaa, 0x67, 0x42,
- 0xf1, 0x21, 0x14, 0x2a, 0x1e, 0x35, 0x2d, 0x5f, 0xd6, 0x7d, 0x7e, 0xe0, 0xa6, 0x43, 0x86, 0x22,
- 0xf5, 0x2c, 0x2e, 0x2f, 0x9e, 0x80, 0x02, 0x9d, 0x74, 0xa1, 0xf6, 0x1a, 0x0d, 0xfc, 0xc0, 0xa3,
- 0x7a, 0x5b, 0x7e, 0xdd, 0x95, 0x81, 0x59, 0x5d, 0xa5, 0x41, 0x93, 0x23, 0xc5, 0xd5, 0xba, 0x30,
- 0x11, 0x23, 0x4e, 0xda, 0x1f, 0x55, 0x60, 0x64, 0xce, 0x6d, 0x6f, 0x58, 0x0e, 0x35, 0x2f, 0x9b,
- 0x2d, 0x4a, 0x5e, 0x85, 0x32, 0x35, 0x5b, 0x54, 0x7e, 0xed, 0xe0, 0xeb, 0x2c, 0x03, 0x8b, 0xb4,
- 0x05, 0xf6, 0x86, 0x1c, 0x98, 0x2c, 0xc3, 0xd8, 0xa6, 0xe7, 0xb6, 0xc5, 0xd4, 0xb5, 0xb6, 0xdb,
- 0x91, 0xaa, 0x62, 0xe3, 0x7f, 0xa8, 0xe9, 0x60, 0x21, 0x91, 0x7b, 0x67, 0x6f, 0x12, 0xa2, 0x37,
- 0x4c, 0x95, 0x25, 0x2f, 0xc3, 0x44, 0x94, 0x12, 0x8e, 0xe1, 0x39, 0xa6, 0x57, 0x73, 0x55, 0xa1,
- 0xd2, 0x38, 0xbf, 0xbf, 0x37, 0x39, 0xb1, 0xd0, 0x87, 0x06, 0xfb, 0x96, 0x26, 0x6f, 0x16, 0x60,
- 0x3c, 0xca, 0x14, 0xf3, 0xaa, 0xd4, 0x10, 0x8e, 0x68, 0xc2, 0xe6, 0x1b, 0x90, 0x85, 0x14, 0x0b,
- 0xec, 0x61, 0x4a, 0x16, 0x60, 0x24, 0x70, 0x63, 0xf2, 0xaa, 0x70, 0x79, 0x69, 0x6a, 0xc7, 0xbc,
- 0xe6, 0xf6, 0x95, 0x56, 0xa2, 0x1c, 0x41, 0x38, 0xa3, 0xde, 0x53, 0x92, 0x1a, 0xe2, 0x92, 0x3a,
- 0xb7, 0xbf, 0x37, 0x79, 0x66, 0x2d, 0x93, 0x02, 0xfb, 0x94, 0x24, 0x5f, 0x2a, 0xc0, 0x98, 0xca,
- 0x92, 0x32, 0x1a, 0x3e, 0x4a, 0x19, 0x11, 0xd6, 0x23, 0xd6, 0x12, 0x0c, 0x30, 0xc5, 0x50, 0xfb,
- 0x51, 0x19, 0x6a, 0xe1, 0xcc, 0x46, 0x1e, 0x87, 0x0a, 0xdf, 0x0b, 0x4b, 0x85, 0x35, 0x5c, 0xb2,
- 0xf8, 0x96, 0x19, 0x45, 0x1e, 0x79, 0x02, 0x86, 0x0d, 0xb7, 0xdd, 0xd6, 0x1d, 0x93, 0xdb, 0x37,
- 0x6a, 0x8d, 0x3a, 0x5b, 0xa9, 0xe7, 0x44, 0x12, 0xaa, 0x3c, 0x72, 0x1e, 0xca, 0xba, 0xd7, 0x12,
- 0xa6, 0x86, 0x9a, 0x98, 0x8f, 0x66, 0xbd, 0x96, 0x8f, 0x3c, 0x95, 0x7c, 0x14, 0x4a, 0xd4, 0xd9,
- 0x99, 0x28, 0xf7, 0x57, 0x05, 0x2e, 0x3b, 0x3b, 0x37, 0x75, 0xaf, 0x51, 0x97, 0x75, 0x28, 0x5d,
- 0x76, 0x76, 0x90, 0x95, 0x21, 0xcb, 0x30, 0x4c, 0x9d, 0x1d, 0xd6, 0xf6, 0xd2, 0x06, 0xf0, 0x81,
- 0x3e, 0xc5, 0x19, 0x89, 0xd4, 0x8a, 0x43, 0x85, 0x42, 0x26, 0xa3, 0x82, 0x20, 0x9f, 0x86, 0x11,
- 0xa1, 0x5b, 0xac, 0xb0, 0x36, 0xf1, 0x27, 0x86, 0x38, 0xe4, 0x64, 0x7f, 0xe5, 0x84, 0xd3, 0x45,
- 0x36, 0x97, 0x58, 0xa2, 0x8f, 0x09, 0x28, 0xf2, 0x69, 0xa8, 0x29, 0x73, 0x9a, 0x6a, 0xd9, 0x4c,
- 0x73, 0x05, 0x4a, 0x22, 0xa4, 0xaf, 0x77, 0x2d, 0x8f, 0xb6, 0xa9, 0x13, 0xf8, 0x8d, 0x93, 0x6a,
- 0x03, 0xab, 0x72, 0x7d, 0x8c, 0xd0, 0xc8, 0x46, 0xaf, 0xdd, 0x45, 0x18, 0x0d, 0x1e, 0xef, 0x33,
- 0xab, 0x0f, 0x60, 0x74, 0xf9, 0x1c, 0x9c, 0x08, 0x0d, 0x23, 0x72, 0x6f, 0x2d, 0xcc, 0x08, 0x4f,
- 0xb3, 0xe2, 0x4b, 0xc9, 0xac, 0x3b, 0x7b, 0x93, 0x8f, 0x65, 0xec, 0xae, 0x23, 0x02, 0x4c, 0x83,
- 0x69, 0x7f, 0x50, 0x82, 0x5e, 0xb5, 0x3b, 0x29, 0xb4, 0xc2, 0x51, 0x0b, 0x2d, 0xfd, 0x41, 0x62,
- 0xfa, 0x7c, 0x5e, 0x16, 0xcb, 0xff, 0x51, 0x59, 0x0d, 0x53, 0x3a, 0xea, 0x86, 0x79, 0x50, 0xc6,
- 0x8e, 0xf6, 0x56, 0x19, 0xc6, 0xe6, 0x75, 0xda, 0x76, 0x9d, 0x7b, 0x6e, 0x42, 0x0a, 0x0f, 0xc4,
- 0x26, 0xe4, 0x12, 0x54, 0x3d, 0xda, 0xb1, 0x2d, 0x43, 0xf7, 0x79, 0xd3, 0x4b, 0x73, 0x1c, 0xca,
- 0x34, 0x0c, 0x73, 0xfb, 0x6c, 0x3e, 0x4b, 0x0f, 0xe4, 0xe6, 0xb3, 0xfc, 0xee, 0x6f, 0x3e, 0xb5,
- 0x2f, 0x15, 0x81, 0x2b, 0x2a, 0xe4, 0x22, 0x94, 0xd9, 0x22, 0x9c, 0x36, 0x79, 0xf0, 0x8e, 0xc3,
- 0x73, 0xc8, 0x39, 0x28, 0x06, 0xae, 0x1c, 0x79, 0x20, 0xf3, 0x8b, 0x6b, 0x2e, 0x16, 0x03, 0x97,
- 0xbc, 0x01, 0x60, 0xb8, 0x8e, 0x69, 0x29, 0x2b, 0x75, 0xbe, 0x0f, 0x5b, 0x70, 0xbd, 0x5b, 0xba,
- 0x67, 0xce, 0x85, 0x88, 0x62, 0xfb, 0x11, 0xbd, 0x63, 0x8c, 0x1b, 0x79, 0x01, 0x86, 0x5c, 0x67,
- 0xa1, 0x6b, 0xdb, 0x5c, 0xa0, 0xb5, 0xc6, 0xff, 0x64, 0x7b, 0xc2, 0x1b, 0x3c, 0xe5, 0xce, 0xde,
- 0xe4, 0x59, 0xa1, 0xdf, 0xb2, 0xb7, 0x97, 0x3c, 0x2b, 0xb0, 0x9c, 0x56, 0x33, 0xf0, 0xf4, 0x80,
- 0xb6, 0x76, 0x51, 0x16, 0xd3, 0xbe, 0x5a, 0x80, 0xfa, 0x82, 0x75, 0x9b, 0x9a, 0x2f, 0x59, 0x8e,
- 0xe9, 0xde, 0x22, 0x08, 0x43, 0x36, 0x75, 0x5a, 0xc1, 0x96, 0xec, 0xfd, 0x53, 0xb1, 0xb1, 0x16,
- 0x1e, 0x6e, 0x44, 0xf5, 0x6f, 0xd3, 0x40, 0x67, 0xa3, 0x6f, 0xbe, 0x2b, 0xcd, 0xef, 0x62, 0x53,
- 0xca, 0x11, 0x50, 0x22, 0x91, 0x69, 0xa8, 0x09, 0xed, 0xd3, 0x72, 0x5a, 0x5c, 0x86, 0xd5, 0x68,
- 0xd2, 0x6b, 0xaa, 0x0c, 0x8c, 0x68, 0xb4, 0x5d, 0x38, 0xd9, 0x23, 0x06, 0x62, 0x42, 0x39, 0xd0,
- 0x5b, 0x6a, 0x7e, 0x5d, 0x18, 0x58, 0xc0, 0x6b, 0x7a, 0x2b, 0x26, 0x5c, 0xbe, 0xc6, 0xaf, 0xe9,
- 0x6c, 0x8d, 0x67, 0xe8, 0xda, 0x4f, 0x0a, 0x50, 0x5d, 0xe8, 0x3a, 0x06, 0xdf, 0x1b, 0xdd, 0xdb,
- 0x14, 0xa6, 0x14, 0x86, 0x62, 0xa6, 0xc2, 0xd0, 0x85, 0xa1, 0xed, 0x5b, 0xa1, 0x42, 0x51, 0x9f,
- 0x59, 0x19, 0xbc, 0x57, 0xc8, 0x2a, 0x4d, 0x5d, 0xe3, 0x78, 0xe2, 0x0c, 0x65, 0x4c, 0x56, 0x68,
- 0xe8, 0xda, 0x4b, 0x9c, 0xa9, 0x64, 0x76, 0xee, 0xa3, 0x50, 0x8f, 0x91, 0x1d, 0xca, 0x68, 0xfb,
- 0x3b, 0x65, 0x18, 0x5a, 0x6c, 0x36, 0x67, 0x57, 0x97, 0xc8, 0x33, 0x50, 0x97, 0xe6, 0xf5, 0xeb,
- 0x91, 0x0c, 0xc2, 0xd3, 0x95, 0x66, 0x94, 0x85, 0x71, 0x3a, 0xa6, 0x8e, 0x79, 0x54, 0xb7, 0xdb,
- 0x72, 0xb0, 0x84, 0xea, 0x18, 0xb2, 0x44, 0x14, 0x79, 0x44, 0x87, 0x31, 0xb6, 0xc3, 0x63, 0x22,
- 0x14, 0xbb, 0x37, 0x39, 0x6c, 0x0e, 0xb8, 0xbf, 0xe3, 0x4a, 0xe2, 0x7a, 0x02, 0x00, 0x53, 0x80,
- 0xe4, 0x79, 0xa8, 0xea, 0xdd, 0x60, 0x8b, 0x2b, 0xd0, 0x62, 0x6c, 0x9c, 0xe7, 0xa7, 0x0f, 0x32,
- 0xed, 0xce, 0xde, 0xe4, 0xc8, 0x35, 0x6c, 0x3c, 0xa3, 0xde, 0x31, 0xa4, 0x66, 0x95, 0x53, 0x3b,
- 0x46, 0x59, 0xb9, 0xca, 0xa1, 0x2b, 0xb7, 0x9a, 0x00, 0xc0, 0x14, 0x20, 0x79, 0x05, 0x46, 0xb6,
- 0xe9, 0x6e, 0xa0, 0x6f, 0x48, 0x06, 0x43, 0x87, 0x61, 0x30, 0xce, 0x54, 0xb8, 0x6b, 0xb1, 0xe2,
- 0x98, 0x00, 0x23, 0x3e, 0x9c, 0xde, 0xa6, 0xde, 0x06, 0xf5, 0x5c, 0xb9, 0xfb, 0x94, 0x4c, 0x86,
- 0x0f, 0xc3, 0x64, 0x62, 0x7f, 0x6f, 0xf2, 0xf4, 0xb5, 0x0c, 0x18, 0xcc, 0x04, 0xd7, 0x7e, 0x5c,
- 0x84, 0x13, 0x8b, 0xe2, 0x7c, 0xd3, 0xf5, 0xc4, 0x22, 0x4c, 0xce, 0x42, 0xc9, 0xeb, 0x74, 0x79,
- 0xcf, 0x29, 0x09, 0x3b, 0x29, 0xae, 0xae, 0x23, 0x4b, 0x23, 0x2f, 0x43, 0xd5, 0x94, 0x53, 0x86,
- 0xdc, 0xfc, 0x1e, 0x76, 0xa2, 0xe1, 0x8b, 0xa0, 0x7a, 0xc3, 0x10, 0x8d, 0x69, 0xfa, 0x6d, 0xbf,
- 0xd5, 0xb4, 0xde, 0xa0, 0x72, 0x3f, 0xc8, 0x35, 0xfd, 0x15, 0x91, 0x84, 0x2a, 0x8f, 0xad, 0xaa,
- 0xdb, 0x74, 0x57, 0xec, 0x86, 0xca, 0xd1, 0xaa, 0x7a, 0x4d, 0xa6, 0x61, 0x98, 0x4b, 0x26, 0xd5,
- 0x60, 0x61, 0xbd, 0xa0, 0x2c, 0x76, 0xf2, 0x37, 0x59, 0x82, 0x1c, 0x37, 0x6c, 0xca, 0x7c, 0xcd,
- 0x0a, 0x02, 0xea, 0xc9, 0x66, 0x1c, 0x68, 0xca, 0xbc, 0xca, 0x11, 0x50, 0x22, 0x91, 0x0f, 0x41,
- 0x8d, 0x83, 0x37, 0x6c, 0x77, 0x83, 0x37, 0x5c, 0x4d, 0xec, 0xe9, 0x6f, 0xaa, 0x44, 0x8c, 0xf2,
- 0xb5, 0x9f, 0x16, 0xe1, 0xcc, 0x22, 0x0d, 0x84, 0x56, 0x33, 0x4f, 0x3b, 0xb6, 0xbb, 0xcb, 0x54,
- 0x4b, 0xa4, 0xaf, 0x93, 0x17, 0x01, 0x2c, 0x7f, 0xa3, 0xb9, 0x63, 0xf0, 0x71, 0x20, 0xc6, 0xf0,
- 0x45, 0x39, 0x24, 0x61, 0xa9, 0xd9, 0x90, 0x39, 0x77, 0x12, 0x6f, 0x18, 0x2b, 0x13, 0x6d, 0xaf,
- 0x8a, 0x77, 0xd9, 0x5e, 0x35, 0x01, 0x3a, 0x91, 0x82, 0x5a, 0xe2, 0x94, 0xff, 0x47, 0xb1, 0x39,
- 0x8c, 0x6e, 0x1a, 0x83, 0xc9, 0xa3, 0x32, 0x3a, 0x30, 0x6e, 0xd2, 0x4d, 0xbd, 0x6b, 0x07, 0xa1,
- 0x52, 0x2d, 0x07, 0xf1, 0xc1, 0xf5, 0xf2, 0xf0, 0xec, 0x75, 0x3e, 0x85, 0x84, 0x3d, 0xd8, 0xda,
- 0xef, 0x96, 0xe0, 0xdc, 0x22, 0x0d, 0x42, 0x8b, 0x8b, 0x9c, 0x1d, 0x9b, 0x1d, 0x6a, 0xb0, 0x56,
- 0x78, 0xb3, 0x00, 0x43, 0xb6, 0xbe, 0x41, 0x6d, 0xb6, 0x7a, 0xb1, 0xaf, 0x79, 0x75, 0xe0, 0x85,
- 0xa0, 0x3f, 0x97, 0xa9, 0x65, 0xce, 0x21, 0xb5, 0x34, 0x88, 0x44, 0x94, 0xec, 0xd9, 0xa4, 0x6e,
- 0xd8, 0x5d, 0x3f, 0xa0, 0xde, 0xaa, 0xeb, 0x05, 0x52, 0x9f, 0x0c, 0x27, 0xf5, 0xb9, 0x28, 0x0b,
- 0xe3, 0x74, 0x64, 0x06, 0xc0, 0xb0, 0x2d, 0xea, 0x04, 0xbc, 0x94, 0x18, 0x57, 0x44, 0xb5, 0xef,
- 0x5c, 0x98, 0x83, 0x31, 0x2a, 0xc6, 0xaa, 0xed, 0x3a, 0x56, 0xe0, 0x0a, 0x56, 0xe5, 0x24, 0xab,
- 0x95, 0x28, 0x0b, 0xe3, 0x74, 0xbc, 0x18, 0x0d, 0x3c, 0xcb, 0xf0, 0x79, 0xb1, 0x4a, 0xaa, 0x58,
- 0x94, 0x85, 0x71, 0x3a, 0xb6, 0xe6, 0xc5, 0xbe, 0xff, 0x50, 0x6b, 0xde, 0x37, 0x6b, 0x70, 0x21,
- 0x21, 0xd6, 0x40, 0x0f, 0xe8, 0x66, 0xd7, 0x6e, 0xd2, 0x40, 0x35, 0xe0, 0x80, 0x6b, 0xe1, 0xcf,
- 0x47, 0xed, 0x2e, 0xbc, 0x2a, 0x8c, 0xa3, 0x69, 0xf7, 0x9e, 0x0a, 0x1e, 0xa8, 0xed, 0xa7, 0xa1,
- 0xe6, 0xe8, 0x81, 0xcf, 0x07, 0xae, 0x1c, 0xa3, 0xa1, 0x1a, 0x76, 0x5d, 0x65, 0x60, 0x44, 0x43,
- 0x56, 0xe1, 0xb4, 0x14, 0xf1, 0xe5, 0xdb, 0x1d, 0xd7, 0x0b, 0xa8, 0x27, 0xca, 0xca, 0xe5, 0x54,
- 0x96, 0x3d, 0xbd, 0x92, 0x41, 0x83, 0x99, 0x25, 0xc9, 0x0a, 0x9c, 0x32, 0xc4, 0x49, 0x33, 0xb5,
- 0x5d, 0xdd, 0x54, 0x80, 0xc2, 0xc0, 0x15, 0x6e, 0x8d, 0xe6, 0x7a, 0x49, 0x30, 0xab, 0x5c, 0xba,
- 0x37, 0x0f, 0x0d, 0xd4, 0x9b, 0x87, 0x07, 0xe9, 0xcd, 0xd5, 0xc1, 0x7a, 0x73, 0xed, 0x60, 0xbd,
- 0x99, 0x49, 0x9e, 0xf5, 0x23, 0xea, 0x31, 0xf5, 0x44, 0xac, 0xb0, 0x31, 0x47, 0x86, 0x50, 0xf2,
- 0xcd, 0x0c, 0x1a, 0xcc, 0x2c, 0x49, 0x36, 0xe0, 0x9c, 0x48, 0xbf, 0xec, 0x18, 0xde, 0x6e, 0x87,
- 0x2d, 0x3c, 0x31, 0xdc, 0x7a, 0xc2, 0xc2, 0x78, 0xae, 0xd9, 0x97, 0x12, 0xef, 0x82, 0x42, 0xfe,
- 0x2f, 0x8c, 0x8a, 0x56, 0x5a, 0xd1, 0x3b, 0x1c, 0x56, 0xb8, 0x35, 0x3c, 0x2c, 0x61, 0x47, 0xe7,
- 0xe2, 0x99, 0x98, 0xa4, 0x25, 0xb3, 0x70, 0xa2, 0xb3, 0x63, 0xb0, 0xc7, 0xa5, 0xcd, 0xeb, 0x94,
- 0x9a, 0xd4, 0xe4, 0xa7, 0x35, 0xb5, 0xc6, 0x23, 0xca, 0xd0, 0xb1, 0x9a, 0xcc, 0xc6, 0x34, 0x3d,
- 0x79, 0x1e, 0x46, 0xfc, 0x40, 0xf7, 0x02, 0x69, 0xd6, 0x9b, 0x18, 0x13, 0x6e, 0x1f, 0xca, 0xea,
- 0xd5, 0x8c, 0xe5, 0x61, 0x82, 0x32, 0x73, 0xbd, 0x38, 0x71, 0x7c, 0xeb, 0x45, 0x9e, 0xd9, 0xea,
- 0xcf, 0x8a, 0x70, 0x71, 0x91, 0x06, 0x2b, 0xae, 0x23, 0x8d, 0xa2, 0x59, 0xcb, 0xfe, 0x81, 0x6c,
- 0xa2, 0xc9, 0x45, 0xbb, 0x78, 0xa4, 0x8b, 0x76, 0xe9, 0x88, 0x16, 0xed, 0xf2, 0x31, 0x2e, 0xda,
- 0xbf, 0x5f, 0x84, 0x47, 0x12, 0x92, 0x5c, 0x75, 0x4d, 0x35, 0xe1, 0xbf, 0x2f, 0xc0, 0x03, 0x08,
- 0xf0, 0x8e, 0xd0, 0x3b, 0xf9, 0xb1, 0x56, 0x4a, 0xe3, 0xf9, 0x4a, 0x5a, 0xe3, 0x79, 0x25, 0xcf,
- 0xca, 0x97, 0xc1, 0xe1, 0x40, 0x2b, 0xde, 0x55, 0x20, 0x9e, 0x3c, 0x84, 0x13, 0xa6, 0x9f, 0x98,
- 0xd2, 0x13, 0xfa, 0x95, 0x61, 0x0f, 0x05, 0x66, 0x94, 0x22, 0x4d, 0x78, 0xd8, 0xa7, 0x4e, 0x60,
- 0x39, 0xd4, 0x4e, 0xc2, 0x09, 0x6d, 0xe8, 0x31, 0x09, 0xf7, 0x70, 0x33, 0x8b, 0x08, 0xb3, 0xcb,
- 0xe6, 0x99, 0x07, 0xfe, 0x12, 0xb8, 0xca, 0x29, 0x44, 0x73, 0x64, 0x1a, 0xcb, 0x9b, 0x69, 0x8d,
- 0xe5, 0xd5, 0xfc, 0xed, 0x36, 0x98, 0xb6, 0x32, 0x03, 0xc0, 0x5b, 0x21, 0xae, 0xae, 0x84, 0x8b,
- 0x34, 0x86, 0x39, 0x18, 0xa3, 0x62, 0x0b, 0x90, 0x92, 0x73, 0x5c, 0x53, 0x09, 0x17, 0xa0, 0x66,
- 0x3c, 0x13, 0x93, 0xb4, 0x7d, 0xb5, 0x9d, 0xca, 0xc0, 0xda, 0xce, 0x55, 0x20, 0x09, 0xc3, 0xa3,
- 0xc0, 0x1b, 0x4a, 0xba, 0x35, 0x2e, 0xf5, 0x50, 0x60, 0x46, 0xa9, 0x3e, 0x5d, 0x79, 0xf8, 0x68,
- 0xbb, 0x72, 0x75, 0xf0, 0xae, 0x4c, 0x5e, 0x85, 0xb3, 0x9c, 0x95, 0x94, 0x4f, 0x12, 0x58, 0xe8,
- 0x3d, 0x1f, 0x90, 0xc0, 0x67, 0xb1, 0x1f, 0x21, 0xf6, 0xc7, 0x60, 0xed, 0x63, 0x78, 0xd4, 0x64,
- 0xcc, 0x75, 0xbb, 0xbf, 0x4e, 0x34, 0x97, 0x41, 0x83, 0x99, 0x25, 0x59, 0x17, 0x0b, 0x58, 0x37,
- 0xd4, 0x37, 0x6c, 0x6a, 0x4a, 0xb7, 0xce, 0xb0, 0x8b, 0xad, 0x2d, 0x37, 0x65, 0x0e, 0xc6, 0xa8,
- 0xb2, 0xd4, 0x94, 0x91, 0x43, 0xaa, 0x29, 0x8b, 0xdc, 0x4a, 0xbf, 0x99, 0xd0, 0x86, 0xa4, 0xae,
- 0x13, 0x3a, 0xea, 0xce, 0xa5, 0x09, 0xb0, 0xb7, 0x0c, 0xd7, 0x12, 0x0d, 0xcf, 0xea, 0x04, 0x7e,
- 0x12, 0x6b, 0x2c, 0xa5, 0x25, 0x66, 0xd0, 0x60, 0x66, 0x49, 0xa6, 0x9f, 0x6f, 0x51, 0xdd, 0x0e,
- 0xb6, 0x92, 0x80, 0x27, 0x92, 0xfa, 0xf9, 0x95, 0x5e, 0x12, 0xcc, 0x2a, 0x97, 0xb9, 0x20, 0x8d,
- 0x3f, 0x98, 0x6a, 0xd5, 0x97, 0x4b, 0x70, 0x76, 0x91, 0x06, 0xa1, 0x5f, 0xcd, 0xfb, 0x66, 0x94,
- 0x77, 0xc1, 0x8c, 0xf2, 0x8d, 0x0a, 0x9c, 0x5a, 0xa4, 0x41, 0x8f, 0x36, 0xf6, 0xdf, 0x54, 0xfc,
- 0x2b, 0x70, 0x2a, 0x72, 0xe5, 0x6a, 0x06, 0xae, 0x27, 0xd6, 0xf2, 0xd4, 0x6e, 0xb9, 0xd9, 0x4b,
- 0x82, 0x59, 0xe5, 0xc8, 0xa7, 0xe1, 0x11, 0xbe, 0xd4, 0x3b, 0x2d, 0x61, 0x9f, 0x15, 0xc6, 0x84,
- 0xd8, 0x35, 0x81, 0x49, 0x09, 0xf9, 0x48, 0x33, 0x9b, 0x0c, 0xfb, 0x95, 0x27, 0x5f, 0x80, 0x91,
- 0x8e, 0xd5, 0xa1, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0xb7, 0x4b, 0xc8, 0x6a, 0x0c, 0x2c, 0xda, 0xc0,
- 0xc5, 0x53, 0x31, 0xc1, 0x30, 0xb3, 0xa7, 0x56, 0x8f, 0xb1, 0xa7, 0xfe, 0x7b, 0x11, 0x86, 0x17,
- 0x3d, 0xb7, 0xdb, 0x69, 0xec, 0x92, 0x16, 0x0c, 0xdd, 0xe2, 0x87, 0x67, 0xf2, 0x68, 0x6a, 0x70,
- 0x77, 0x68, 0x71, 0x06, 0x17, 0xa9, 0x44, 0xe2, 0x1d, 0x25, 0x3c, 0xeb, 0xc4, 0xdb, 0x74, 0x97,
- 0x9a, 0xf2, 0x0c, 0x2d, 0xec, 0xc4, 0xd7, 0x58, 0x22, 0x8a, 0x3c, 0xd2, 0x86, 0x13, 0xba, 0x6d,
- 0xbb, 0xb7, 0xa8, 0xb9, 0xac, 0x07, 0xd4, 0xa1, 0xbe, 0x3a, 0x92, 0x3c, 0xac, 0x59, 0x9a, 0x9f,
- 0xeb, 0xcf, 0x26, 0xa1, 0x30, 0x8d, 0x4d, 0x5e, 0x83, 0x61, 0x3f, 0x70, 0x3d, 0xa5, 0x6c, 0xd5,
- 0x67, 0xe6, 0x06, 0x6f, 0xf4, 0xc6, 0xa7, 0x9a, 0x02, 0x4a, 0xd8, 0xec, 0xe5, 0x0b, 0x2a, 0x06,
- 0xda, 0xd7, 0x0b, 0x00, 0x57, 0xd6, 0xd6, 0x56, 0xe5, 0xf1, 0x82, 0x09, 0x65, 0xbd, 0x1b, 0x1e,
- 0x54, 0x0e, 0x7e, 0x20, 0x98, 0xf0, 0x87, 0x94, 0x67, 0x78, 0xdd, 0x60, 0x0b, 0x39, 0x3a, 0xf9,
- 0x20, 0x0c, 0x4b, 0x05, 0x59, 0x8a, 0x3d, 0x74, 0x2d, 0x90, 0x4a, 0x34, 0xaa, 0x7c, 0xed, 0xb7,
- 0x8b, 0x00, 0x4b, 0xa6, 0x4d, 0x9b, 0xca, 0x83, 0xbd, 0x16, 0x6c, 0x79, 0xd4, 0xdf, 0x72, 0x6d,
- 0x73, 0xc0, 0xd3, 0x54, 0x6e, 0xf3, 0x5f, 0x53, 0x20, 0x18, 0xe1, 0x11, 0x13, 0x46, 0xfc, 0x80,
- 0x76, 0x96, 0x9c, 0x80, 0x7a, 0x3b, 0xba, 0x3d, 0xe0, 0x21, 0xca, 0xb8, 0xb0, 0x8b, 0x44, 0x38,
- 0x98, 0x40, 0x25, 0x3a, 0xd4, 0x2d, 0xc7, 0x10, 0x03, 0xa4, 0xb1, 0x3b, 0x60, 0x47, 0x3a, 0xc1,
- 0x76, 0x1c, 0x4b, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0xef, 0x17, 0xe1, 0x0c, 0xe7, 0xc7, 0xaa, 0x91,
- 0xf0, 0xc7, 0x24, 0xff, 0xbf, 0xe7, 0x1e, 0xdc, 0xff, 0x3e, 0x18, 0x6b, 0x71, 0x8d, 0x6a, 0x85,
- 0x06, 0x7a, 0xa4, 0xcf, 0x45, 0x69, 0xb1, 0xcb, 0x6f, 0x5d, 0x28, 0xfb, 0x6c, 0xbe, 0x12, 0xd2,
- 0x6b, 0x0e, 0xdc, 0x85, 0xb2, 0x3f, 0x80, 0xcf, 0x5e, 0xe1, 0xa9, 0x31, 0x9f, 0xb5, 0x38, 0x3b,
- 0xf2, 0xb3, 0x30, 0xe4, 0x07, 0x7a, 0xd0, 0x55, 0x43, 0x73, 0xfd, 0xa8, 0x19, 0x73, 0xf0, 0x68,
- 0x1e, 0x11, 0xef, 0x28, 0x99, 0x6a, 0xdf, 0x2f, 0xc0, 0xb9, 0xec, 0x82, 0xcb, 0x96, 0x1f, 0x90,
- 0xff, 0xd7, 0x23, 0xf6, 0x03, 0xb6, 0x38, 0x2b, 0xcd, 0x85, 0x1e, 0x3a, 0x64, 0xab, 0x94, 0x98,
- 0xc8, 0x03, 0xa8, 0x58, 0x01, 0x6d, 0xab, 0xfd, 0xe5, 0x8d, 0x23, 0xfe, 0xf4, 0xd8, 0xd2, 0xce,
- 0xb8, 0xa0, 0x60, 0xa6, 0xbd, 0x55, 0xec, 0xf7, 0xc9, 0x7c, 0xf9, 0xb0, 0x93, 0x3e, 0xbf, 0xd7,
- 0xf2, 0xf9, 0xfc, 0x26, 0x2b, 0xd4, 0xeb, 0xfa, 0xfb, 0x33, 0xbd, 0xae, 0xbf, 0x37, 0xf2, 0xbb,
- 0xfe, 0xa6, 0xc4, 0xd0, 0xd7, 0x03, 0xf8, 0x9d, 0x12, 0x9c, 0xbf, 0x5b, 0xb7, 0x61, 0xeb, 0x99,
- 0xec, 0x9d, 0x79, 0xd7, 0xb3, 0xbb, 0xf7, 0x43, 0x32, 0x03, 0x95, 0xce, 0x96, 0xee, 0x2b, 0xa5,
- 0x4c, 0x6d, 0x58, 0x2a, 0xab, 0x2c, 0xf1, 0x0e, 0x9b, 0x34, 0xb8, 0x32, 0xc7, 0x5f, 0x51, 0x90,
- 0xb2, 0xe9, 0xb8, 0x4d, 0x7d, 0x3f, 0xb2, 0x09, 0x84, 0xd3, 0xf1, 0x8a, 0x48, 0x46, 0x95, 0x4f,
- 0x02, 0x18, 0x12, 0x26, 0x66, 0xb9, 0x32, 0x0d, 0xee, 0xc8, 0x95, 0xe1, 0x26, 0x1e, 0x7d, 0x94,
- 0x3c, 0xad, 0x90, 0xbc, 0xc8, 0x14, 0x94, 0x83, 0xc8, 0x69, 0x57, 0x6d, 0xcd, 0xcb, 0x19, 0xfa,
- 0x29, 0xa7, 0x63, 0x1b, 0x7b, 0x77, 0x83, 0x1b, 0xd5, 0x4d, 0x79, 0x7e, 0x6e, 0xb9, 0x0e, 0x57,
- 0xc8, 0x4a, 0xd1, 0xc6, 0xfe, 0x46, 0x0f, 0x05, 0x66, 0x94, 0xd2, 0xfe, 0xa6, 0x0a, 0x67, 0xb2,
- 0xfb, 0x03, 0x93, 0xdb, 0x0e, 0xf5, 0x7c, 0x86, 0x5d, 0x48, 0xca, 0xed, 0xa6, 0x48, 0x46, 0x95,
- 0xff, 0x9e, 0x76, 0x38, 0xfb, 0x46, 0x01, 0xce, 0x7a, 0xf2, 0x8c, 0xe8, 0x7e, 0x38, 0x9d, 0x3d,
- 0x26, 0xcc, 0x19, 0x7d, 0x18, 0x62, 0xff, 0xba, 0x90, 0xdf, 0x2c, 0xc0, 0x44, 0x3b, 0x65, 0xe7,
- 0x38, 0xc6, 0x0b, 0x63, 0xdc, 0x2b, 0x7e, 0xa5, 0x0f, 0x3f, 0xec, 0x5b, 0x13, 0xf2, 0x05, 0xa8,
- 0x77, 0x58, 0xbf, 0xf0, 0x03, 0xea, 0x18, 0xea, 0xce, 0xd8, 0xe0, 0x23, 0x69, 0x35, 0xc2, 0x52,
- 0xae, 0x68, 0x42, 0x3f, 0x88, 0x65, 0x60, 0x9c, 0xe3, 0x03, 0x7e, 0x43, 0xec, 0x12, 0x54, 0x7d,
- 0x1a, 0x04, 0x96, 0xd3, 0x12, 0xfb, 0x8d, 0x9a, 0x18, 0x2b, 0x4d, 0x99, 0x86, 0x61, 0x2e, 0xf9,
- 0x10, 0xd4, 0xf8, 0x91, 0xd3, 0xac, 0xd7, 0xf2, 0x27, 0x6a, 0xdc, 0x5d, 0x6c, 0x54, 0x38, 0xc0,
- 0xc9, 0x44, 0x8c, 0xf2, 0xc9, 0xd3, 0x30, 0xb2, 0xc1, 0x87, 0xaf, 0xbc, 0xce, 0x2b, 0x6c, 0x5c,
- 0x5c, 0x5b, 0x6b, 0xc4, 0xd2, 0x31, 0x41, 0x45, 0x66, 0x00, 0x68, 0x78, 0x2e, 0x97, 0xb6, 0x67,
- 0x45, 0x27, 0x76, 0x18, 0xa3, 0x22, 0x8f, 0x41, 0x29, 0xb0, 0x7d, 0x6e, 0xc3, 0xaa, 0x46, 0x5b,
- 0xd0, 0xb5, 0xe5, 0x26, 0xb2, 0x74, 0xed, 0xa7, 0x05, 0x38, 0x91, 0xba, 0x5c, 0xc2, 0x8a, 0x74,
- 0x3d, 0x5b, 0x4e, 0x23, 0x61, 0x91, 0x75, 0x5c, 0x46, 0x96, 0x4e, 0x5e, 0x95, 0x6a, 0x79, 0x31,
- 0x67, 0xe4, 0x82, 0xeb, 0x7a, 0xe0, 0x33, 0x3d, 0xbc, 0x47, 0x23, 0xe7, 0xc7, 0x7c, 0x51, 0x7d,
- 0xe4, 0x3a, 0x10, 0x3b, 0xe6, 0x8b, 0xf2, 0x30, 0x41, 0x99, 0x32, 0xf8, 0x95, 0x0f, 0x62, 0xf0,
- 0xd3, 0xbe, 0x5a, 0x8c, 0x49, 0x40, 0x6a, 0xf6, 0xf7, 0x90, 0xc0, 0x93, 0x6c, 0x01, 0x0d, 0x17,
- 0xf7, 0x5a, 0x7c, 0xfd, 0xe3, 0x8b, 0xb1, 0xcc, 0x25, 0x2f, 0x09, 0xd9, 0x97, 0x72, 0xde, 0x42,
- 0x5d, 0x5b, 0x6e, 0x0a, 0xef, 0x2a, 0xd5, 0x6a, 0x61, 0x13, 0x94, 0x8f, 0xa9, 0x09, 0xb4, 0xbf,
- 0x28, 0x41, 0xfd, 0xaa, 0xbb, 0xf1, 0x1e, 0xf1, 0xa0, 0xce, 0x5e, 0xa6, 0x8a, 0xef, 0xe2, 0x32,
- 0xb5, 0x0e, 0x8f, 0x04, 0x81, 0xdd, 0xa4, 0x86, 0xeb, 0x98, 0xfe, 0xec, 0x66, 0x40, 0xbd, 0x05,
- 0xcb, 0xb1, 0xfc, 0x2d, 0x6a, 0xca, 0xe3, 0xa4, 0x47, 0xf7, 0xf7, 0x26, 0x1f, 0x59, 0x5b, 0x5b,
- 0xce, 0x22, 0xc1, 0x7e, 0x65, 0xf9, 0xb4, 0xa1, 0x1b, 0xdb, 0xee, 0xe6, 0x26, 0xbf, 0x29, 0x23,
- 0x7d, 0x6e, 0xc4, 0xb4, 0x11, 0x4b, 0xc7, 0x04, 0x95, 0xf6, 0x76, 0x11, 0x6a, 0xe1, 0xcd, 0x77,
- 0xf2, 0x04, 0x0c, 0x6f, 0x78, 0xee, 0x36, 0xf5, 0xc4, 0xc9, 0x9d, 0xbc, 0x29, 0xd3, 0x10, 0x49,
- 0xa8, 0xf2, 0xc8, 0xe3, 0x50, 0x09, 0xdc, 0x8e, 0x65, 0xa4, 0x0d, 0x6a, 0x6b, 0x2c, 0x11, 0x45,
- 0xde, 0xf1, 0x75, 0xf0, 0x27, 0x13, 0xaa, 0x5d, 0xad, 0xaf, 0x32, 0xf6, 0x0a, 0x94, 0x7d, 0xdd,
- 0xb7, 0xe5, 0x7a, 0x9a, 0xe3, 0x12, 0xf9, 0x6c, 0x73, 0x59, 0x5e, 0x22, 0x9f, 0x6d, 0x2e, 0x23,
- 0x07, 0xd5, 0x7e, 0x54, 0x84, 0xba, 0x90, 0x9b, 0x98, 0x15, 0x8e, 0x52, 0x72, 0x2f, 0x70, 0x57,
- 0x0a, 0xbf, 0xdb, 0xa6, 0x1e, 0x37, 0x33, 0xc9, 0x49, 0x2e, 0x7e, 0x3e, 0x10, 0x65, 0x86, 0xee,
- 0x14, 0x51, 0x92, 0x12, 0x7d, 0xf9, 0x18, 0x45, 0x5f, 0x39, 0x90, 0xe8, 0x87, 0x8e, 0x43, 0xf4,
- 0x6f, 0x16, 0xa1, 0xb6, 0x6c, 0x6d, 0x52, 0x63, 0xd7, 0xb0, 0xf9, 0x9d, 0x40, 0x93, 0xda, 0x34,
- 0xa0, 0x8b, 0x9e, 0x6e, 0xd0, 0x55, 0xea, 0x59, 0x3c, 0x66, 0x0b, 0x1b, 0x1f, 0x7c, 0x06, 0x92,
- 0x77, 0x02, 0xe7, 0xfb, 0xd0, 0x60, 0xdf, 0xd2, 0x64, 0x09, 0x46, 0x4c, 0xea, 0x5b, 0x1e, 0x35,
- 0x57, 0x63, 0x1b, 0x95, 0x27, 0xd4, 0x52, 0x33, 0x1f, 0xcb, 0xbb, 0xb3, 0x37, 0x39, 0xaa, 0x0c,
- 0x94, 0x62, 0xc7, 0x92, 0x28, 0xca, 0x86, 0x7c, 0x47, 0xef, 0xfa, 0x59, 0x75, 0x8c, 0x0d, 0xf9,
- 0xd5, 0x6c, 0x12, 0xec, 0x57, 0x56, 0xab, 0x40, 0x69, 0xd9, 0x6d, 0x69, 0x6f, 0x95, 0x20, 0x0c,
- 0xee, 0x43, 0x7e, 0xae, 0x00, 0x75, 0xdd, 0x71, 0xdc, 0x40, 0x06, 0xce, 0x11, 0x27, 0xf0, 0x98,
- 0x3b, 0x86, 0xd0, 0xd4, 0x6c, 0x04, 0x2a, 0x0e, 0x6f, 0xc3, 0x03, 0xe5, 0x58, 0x0e, 0xc6, 0x79,
- 0x93, 0x6e, 0xea, 0x3c, 0x79, 0x25, 0x7f, 0x2d, 0x0e, 0x70, 0x7a, 0x7c, 0xee, 0x93, 0x30, 0x9e,
- 0xae, 0xec, 0x61, 0x8e, 0x83, 0x72, 0x1d, 0xcc, 0x17, 0x01, 0x22, 0x9f, 0x92, 0xfb, 0x60, 0xc4,
- 0xb2, 0x12, 0x46, 0xac, 0xc5, 0xc1, 0x05, 0x1c, 0x56, 0xba, 0xaf, 0xe1, 0xea, 0xf5, 0x94, 0xe1,
- 0x6a, 0xe9, 0x28, 0x98, 0xdd, 0xdd, 0x58, 0xf5, 0x5b, 0x05, 0x18, 0x8f, 0x88, 0xe5, 0x0d, 0xd9,
- 0xe7, 0x60, 0xd4, 0xa3, 0xba, 0xd9, 0xd0, 0x03, 0x63, 0x8b, 0xbb, 0x7a, 0x17, 0xb8, 0x6f, 0xf6,
- 0xc9, 0xfd, 0xbd, 0xc9, 0x51, 0x8c, 0x67, 0x60, 0x92, 0x8e, 0xe8, 0x50, 0x67, 0x09, 0x6b, 0x56,
- 0x9b, 0xba, 0xdd, 0x60, 0x40, 0xab, 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c, 0xed, 0x9d,
- 0x02, 0x8c, 0xc5, 0x2b, 0x7c, 0xec, 0x16, 0xb5, 0xad, 0xa4, 0x45, 0x6d, 0xee, 0x08, 0xda, 0xa4,
- 0x8f, 0x15, 0xed, 0xc7, 0xd5, 0xf8, 0xa7, 0x71, 0xcb, 0x59, 0xdc, 0x58, 0x50, 0xb8, 0xab, 0xb1,
- 0xe0, 0xbd, 0x1f, 0x35, 0xa6, 0x9f, 0x96, 0x5b, 0x7e, 0x80, 0xb5, 0xdc, 0x77, 0x33, 0xf4, 0x4c,
- 0x2c, 0x7c, 0xca, 0x50, 0x8e, 0xf0, 0x29, 0xed, 0x30, 0x7c, 0xca, 0xf0, 0x91, 0x4d, 0x3a, 0x07,
- 0x09, 0xa1, 0x52, 0xbd, 0xaf, 0x21, 0x54, 0x6a, 0xc7, 0x15, 0x42, 0x05, 0xf2, 0x86, 0x50, 0xf9,
- 0x4a, 0x01, 0xc6, 0xcc, 0xc4, 0x8d, 0x59, 0x6e, 0x5b, 0xc8, 0xb3, 0xd4, 0x24, 0x2f, 0xe0, 0x8a,
- 0x2b, 0x53, 0xc9, 0x34, 0x4c, 0xb1, 0xd4, 0x7e, 0x58, 0x8e, 0xaf, 0x03, 0xf7, 0xdb, 0x54, 0xfd,
- 0x6c, 0xd2, 0x54, 0x7d, 0x31, 0x6d, 0xaa, 0x3e, 0x11, 0xf3, 0x22, 0x8d, 0x9b, 0xab, 0x3f, 0x1c,
- 0x9b, 0x1e, 0xd9, 0x9c, 0x34, 0x1a, 0x49, 0x3a, 0x63, 0x8a, 0xfc, 0x30, 0x54, 0x7d, 0x15, 0x86,
- 0x51, 0x6c, 0x6c, 0xa2, 0x76, 0x51, 0x21, 0x12, 0x43, 0x0a, 0xa6, 0x89, 0x7b, 0x54, 0xf7, 0x5d,
- 0x27, 0xad, 0x89, 0x23, 0x4f, 0x45, 0x99, 0x1b, 0x37, 0x99, 0x0f, 0xdd, 0xc3, 0x64, 0xae, 0x43,
- 0xdd, 0xd6, 0xfd, 0x60, 0xbd, 0x63, 0xea, 0x01, 0x35, 0xe5, 0x78, 0xfb, 0x5f, 0x07, 0x5b, 0xab,
- 0xd8, 0xfa, 0x17, 0x29, 0x84, 0xcb, 0x11, 0x0c, 0xc6, 0x31, 0x89, 0x09, 0x23, 0xec, 0x95, 0x8f,
- 0x06, 0x73, 0x56, 0x85, 0x00, 0x38, 0x0c, 0x8f, 0xd0, 0xd2, 0xb3, 0x1c, 0xc3, 0xc1, 0x04, 0x6a,
- 0x1f, 0xab, 0x7a, 0x6d, 0x20, 0xab, 0xfa, 0x57, 0x6a, 0x50, 0xbf, 0xae, 0x07, 0xd6, 0x0e, 0xe5,
- 0xa7, 0x38, 0xc7, 0x63, 0x4a, 0xff, 0xd5, 0x02, 0x9c, 0x49, 0xba, 0xea, 0x1d, 0xa3, 0x3d, 0x9d,
- 0x07, 0xfe, 0xc0, 0x4c, 0x6e, 0xd8, 0xa7, 0x16, 0xdc, 0xb2, 0xde, 0xe3, 0xf9, 0x77, 0xdc, 0x96,
- 0xf5, 0x66, 0x3f, 0x86, 0xd8, 0xbf, 0x2e, 0xef, 0x15, 0xcb, 0xfa, 0x83, 0x1d, 0x98, 0x2d, 0x65,
- 0xf7, 0x1f, 0x7e, 0x60, 0xec, 0xfe, 0xd5, 0x07, 0x42, 0xd9, 0xea, 0xc4, 0xec, 0xfe, 0xb5, 0x9c,
- 0xfe, 0x27, 0xd2, 0xbb, 0x5d, 0xa0, 0xf5, 0x3b, 0x3f, 0xe0, 0x17, 0xd3, 0x95, 0x3d, 0x96, 0xe9,
- 0x28, 0x1b, 0xba, 0x6f, 0x19, 0x72, 0xd9, 0xcb, 0x11, 0x88, 0x52, 0x45, 0xec, 0x12, 0xc7, 0xd4,
- 0xfc, 0x15, 0x05, 0x76, 0x14, 0x19, 0xac, 0x98, 0x2b, 0x32, 0x18, 0x99, 0x83, 0xb2, 0xc3, 0x76,
- 0xcf, 0xa5, 0x43, 0xc7, 0x02, 0xbb, 0x7e, 0x8d, 0xee, 0x22, 0x2f, 0xac, 0xbd, 0x5d, 0x04, 0x60,
- 0x9f, 0x7f, 0x30, 0x0b, 0xfc, 0x07, 0x61, 0xd8, 0xef, 0xf2, 0xbd, 0xb2, 0x5c, 0xb0, 0x23, 0xa7,
- 0x1d, 0x91, 0x8c, 0x2a, 0x9f, 0x3c, 0x0e, 0x95, 0xd7, 0xbb, 0xb4, 0xab, 0x8e, 0x93, 0x43, 0x75,
- 0xed, 0x53, 0x2c, 0x11, 0x45, 0xde, 0xf1, 0x59, 0xd3, 0x94, 0xa5, 0xbe, 0x72, 0x5c, 0x96, 0xfa,
- 0x1a, 0x0c, 0x5f, 0x77, 0xb9, 0x0f, 0xa0, 0xf6, 0xaf, 0x45, 0x80, 0xc8, 0xc7, 0x8a, 0x7c, 0xbd,
- 0x00, 0x0f, 0x87, 0x03, 0x2e, 0x10, 0x5a, 0xf7, 0x9c, 0xad, 0x5b, 0xed, 0xdc, 0x56, 0xfb, 0xac,
- 0xc1, 0xce, 0x67, 0xa0, 0xd5, 0x2c, 0x76, 0x98, 0x5d, 0x0b, 0x82, 0x50, 0xa5, 0xed, 0x4e, 0xb0,
- 0x3b, 0x6f, 0x79, 0xb2, 0x07, 0x66, 0xba, 0xf2, 0x5d, 0x96, 0x34, 0xa2, 0xa8, 0xdc, 0x1a, 0xf2,
- 0x41, 0xa4, 0x72, 0x30, 0xc4, 0x21, 0x5b, 0x50, 0x75, 0xdc, 0x57, 0x7d, 0x26, 0x0e, 0xd9, 0x1d,
- 0x5f, 0x1c, 0x5c, 0xe4, 0x42, 0xac, 0xc2, 0xca, 0x2b, 0x5f, 0x70, 0xd8, 0x91, 0xc2, 0xfe, 0x5a,
- 0x11, 0x4e, 0x65, 0xc8, 0x81, 0xbc, 0x08, 0xe3, 0xd2, 0x9d, 0x2d, 0x0a, 0x4f, 0x5c, 0x88, 0xc2,
- 0x13, 0x37, 0x53, 0x79, 0xd8, 0x43, 0x4d, 0x5e, 0x05, 0xd0, 0x0d, 0x83, 0xfa, 0xfe, 0x8a, 0x6b,
- 0x2a, 0x7d, 0xf4, 0x85, 0xfd, 0xbd, 0x49, 0x98, 0x0d, 0x53, 0xef, 0xec, 0x4d, 0x7e, 0x24, 0xcb,
- 0x43, 0x35, 0x25, 0xe7, 0xa8, 0x00, 0xc6, 0x20, 0xc9, 0xe7, 0x00, 0xc4, 0xd6, 0x2b, 0xbc, 0x44,
- 0x7f, 0x0f, 0x7b, 0xc5, 0x94, 0x0a, 0x57, 0x34, 0xf5, 0xa9, 0xae, 0xee, 0x04, 0x56, 0xb0, 0x2b,
- 0x62, 0x96, 0xdc, 0x0c, 0x51, 0x30, 0x86, 0xa8, 0xfd, 0x69, 0x11, 0xaa, 0xca, 0x52, 0x7a, 0x1f,
- 0xcc, 0x63, 0xad, 0x84, 0x79, 0xec, 0x88, 0x7c, 0x52, 0xb3, 0x8c, 0x63, 0x6e, 0xca, 0x38, 0xb6,
- 0x98, 0x9f, 0xd5, 0xdd, 0x4d, 0x63, 0xdf, 0x2a, 0xc2, 0x98, 0x22, 0xcd, 0x6b, 0x18, 0xfb, 0x04,
- 0x9c, 0x10, 0x67, 0xc9, 0x2b, 0xfa, 0x6d, 0x11, 0xbe, 0x85, 0x0b, 0xac, 0x2c, 0xdc, 0x40, 0x1b,
- 0xc9, 0x2c, 0x4c, 0xd3, 0xb2, 0x6e, 0x2d, 0x92, 0xd6, 0xd9, 0x3e, 0x42, 0x9c, 0x3e, 0x89, 0xfd,
- 0x0e, 0xef, 0xd6, 0x8d, 0x54, 0x1e, 0xf6, 0x50, 0xa7, 0x2d, 0x73, 0xe5, 0x63, 0xb0, 0xcc, 0xfd,
- 0x6d, 0x01, 0x46, 0x22, 0x79, 0x1d, 0xbb, 0x5d, 0x6e, 0x33, 0x69, 0x97, 0x9b, 0xcd, 0xdd, 0x1d,
- 0xfa, 0x58, 0xe5, 0x7e, 0x71, 0x18, 0x12, 0xae, 0xd1, 0x64, 0x03, 0xce, 0x59, 0x99, 0x0e, 0x5e,
- 0xb1, 0xd9, 0x26, 0xbc, 0xeb, 0xbb, 0xd4, 0x97, 0x12, 0xef, 0x82, 0x42, 0xba, 0x50, 0xdd, 0xa1,
- 0x5e, 0x60, 0x19, 0x54, 0x7d, 0xdf, 0x62, 0x6e, 0x95, 0x4c, 0xda, 0x1e, 0x43, 0x99, 0xde, 0x94,
- 0x0c, 0x30, 0x64, 0x45, 0x36, 0xa0, 0x42, 0xcd, 0x16, 0x55, 0x01, 0x75, 0x72, 0x86, 0xab, 0x0c,
- 0xe5, 0xc9, 0xde, 0x7c, 0x14, 0xd0, 0xc4, 0x87, 0x9a, 0xad, 0xce, 0x96, 0x64, 0x3f, 0x1c, 0x5c,
- 0xc1, 0x0a, 0x4f, 0xa9, 0xa2, 0xbb, 0xf6, 0x61, 0x12, 0x46, 0x7c, 0xc8, 0x76, 0x68, 0xe4, 0xaa,
- 0x1c, 0xd1, 0xe4, 0x71, 0x17, 0x13, 0x97, 0x0f, 0xb5, 0x5b, 0x7a, 0x40, 0xbd, 0xb6, 0xee, 0x6d,
- 0xcb, 0xdd, 0xc6, 0xe0, 0x5f, 0xf8, 0x92, 0x42, 0x8a, 0xbe, 0x30, 0x4c, 0xc2, 0x88, 0x0f, 0x71,
- 0xa1, 0x16, 0x48, 0xf5, 0x59, 0x59, 0xf2, 0x06, 0x67, 0xaa, 0x14, 0x71, 0x5f, 0xba, 0x48, 0xab,
- 0x57, 0x8c, 0x78, 0x90, 0x9d, 0x44, 0x28, 0x5f, 0x11, 0xc0, 0xb9, 0x91, 0xc3, 0x22, 0x2c, 0xa1,
- 0xa2, 0xe5, 0x26, 0x3b, 0x24, 0xb0, 0xf6, 0x76, 0x25, 0x9a, 0x96, 0xef, 0xb7, 0x9d, 0xea, 0xe9,
- 0xa4, 0x9d, 0xea, 0x42, 0xda, 0x4e, 0x95, 0x3a, 0xa2, 0x3c, 0xbc, 0x53, 0x65, 0xca, 0x42, 0x54,
- 0x3e, 0x06, 0x0b, 0xd1, 0x53, 0x50, 0xdf, 0xe1, 0x33, 0x81, 0x88, 0xce, 0x53, 0xe1, 0xcb, 0x08,
- 0x9f, 0xd9, 0x6f, 0x46, 0xc9, 0x18, 0xa7, 0x61, 0x45, 0x84, 0x06, 0x12, 0x85, 0x37, 0x95, 0x45,
- 0x9a, 0x51, 0x32, 0xc6, 0x69, 0xb8, 0x3f, 0x96, 0xe5, 0x6c, 0x8b, 0x02, 0xc3, 0xbc, 0x80, 0xf0,
- 0xc7, 0x52, 0x89, 0x18, 0xe5, 0x93, 0x4b, 0x50, 0xed, 0x9a, 0x9b, 0x82, 0xb6, 0xca, 0x69, 0xb9,
- 0x86, 0xb9, 0x3e, 0xbf, 0x20, 0xa3, 0x05, 0xa9, 0x5c, 0x56, 0x93, 0xb6, 0xde, 0x51, 0x19, 0x7c,
- 0x6f, 0x28, 0x6b, 0xb2, 0x12, 0x25, 0x63, 0x9c, 0x86, 0x7c, 0x0c, 0xc6, 0x3c, 0x6a, 0x76, 0x0d,
- 0x1a, 0x96, 0x02, 0x5e, 0x8a, 0x5b, 0x45, 0x31, 0x91, 0x83, 0x29, 0xca, 0x3e, 0x76, 0xae, 0xfa,
- 0x40, 0x76, 0xae, 0xef, 0x15, 0x80, 0xf4, 0xfa, 0x2f, 0x93, 0x2d, 0x18, 0x72, 0xb8, 0xf5, 0x2b,
- 0x77, 0x40, 0xe4, 0x98, 0x11, 0x4d, 0x4c, 0x4b, 0x32, 0x41, 0xe2, 0x13, 0x07, 0xaa, 0xf4, 0x76,
- 0x40, 0x3d, 0x27, 0xbc, 0xcf, 0x70, 0x34, 0xc1, 0x97, 0xc5, 0x6e, 0x40, 0x22, 0x63, 0xc8, 0x43,
- 0xfb, 0x41, 0x11, 0xea, 0x31, 0xba, 0x7b, 0x6d, 0x2a, 0xf9, 0x95, 0x6a, 0x61, 0x74, 0x5a, 0xf7,
- 0x6c, 0x39, 0xc2, 0x62, 0x57, 0xaa, 0x65, 0x16, 0x2e, 0x63, 0x9c, 0x8e, 0xcc, 0x00, 0xb4, 0x75,
- 0x3f, 0xa0, 0x1e, 0x5f, 0x7d, 0x53, 0x17, 0x99, 0x57, 0xc2, 0x1c, 0x8c, 0x51, 0x91, 0x8b, 0x32,
- 0x7c, 0x76, 0x39, 0x19, 0x78, 0xae, 0x4f, 0x6c, 0xec, 0xca, 0x11, 0xc4, 0xc6, 0x26, 0x2d, 0x18,
- 0x57, 0xb5, 0x56, 0xb9, 0x87, 0x0b, 0x4b, 0x26, 0xf6, 0x2f, 0x29, 0x08, 0xec, 0x01, 0xd5, 0xde,
- 0x2e, 0xc0, 0x68, 0xc2, 0xe4, 0x21, 0x42, 0xc6, 0x29, 0xef, 0xfb, 0x44, 0xc8, 0xb8, 0x98, 0xd3,
- 0xfc, 0x93, 0x30, 0x24, 0x04, 0x94, 0x76, 0xaa, 0x13, 0x22, 0x44, 0x99, 0xcb, 0xe6, 0x32, 0x69,
- 0x54, 0x4d, 0xcf, 0x65, 0xd2, 0xea, 0x8a, 0x2a, 0x5f, 0x98, 0xdb, 0x45, 0xed, 0x7a, 0xcd, 0xed,
- 0x22, 0x1d, 0x43, 0x0a, 0xed, 0x87, 0x25, 0xe0, 0x2e, 0x28, 0xe4, 0x39, 0xa8, 0xb5, 0xa9, 0xb1,
- 0xa5, 0x3b, 0x96, 0xaf, 0x42, 0x46, 0xb2, 0xdd, 0x6d, 0x6d, 0x45, 0x25, 0xde, 0x61, 0x00, 0xb3,
- 0xcd, 0x65, 0xee, 0xe5, 0x1d, 0xd1, 0x12, 0x03, 0x86, 0x5a, 0xbe, 0xaf, 0x77, 0xac, 0xdc, 0x27,
- 0xa0, 0x22, 0x44, 0x9f, 0x18, 0x44, 0xe2, 0x19, 0x25, 0x34, 0x31, 0xa0, 0xd2, 0xb1, 0x75, 0xcb,
- 0xc9, 0xfd, 0x8f, 0x12, 0xf6, 0x05, 0xab, 0x0c, 0x49, 0x98, 0x74, 0xf8, 0x23, 0x0a, 0x6c, 0xd2,
- 0x85, 0xba, 0x6f, 0x78, 0x7a, 0xdb, 0xdf, 0xd2, 0x67, 0x9e, 0x79, 0x36, 0xb7, 0x92, 0x14, 0xb1,
- 0x12, 0x73, 0xf6, 0x1c, 0xce, 0xae, 0x34, 0xaf, 0xcc, 0xce, 0x3c, 0xf3, 0x2c, 0xc6, 0xf9, 0xc4,
- 0xd9, 0x3e, 0xf3, 0xd4, 0x8c, 0xec, 0xf7, 0x47, 0xce, 0xf6, 0x99, 0xa7, 0x66, 0x30, 0xce, 0x47,
- 0xfb, 0x8f, 0x02, 0xd4, 0x42, 0x5a, 0xb2, 0x0e, 0xc0, 0x46, 0xa0, 0x0c, 0xaa, 0x77, 0xa8, 0x00,
- 0xf7, 0x7c, 0x57, 0xbc, 0x1e, 0x16, 0xc6, 0x18, 0x50, 0x46, 0xd4, 0xc1, 0xe2, 0x51, 0x47, 0x1d,
- 0x9c, 0x86, 0xda, 0x96, 0xee, 0x98, 0xfe, 0x96, 0xbe, 0x2d, 0x26, 0xa2, 0x58, 0x1c, 0xce, 0x2b,
- 0x2a, 0x03, 0x23, 0x1a, 0xed, 0x8f, 0x87, 0x40, 0x1c, 0x5b, 0xb2, 0xa1, 0x62, 0x5a, 0xbe, 0xf0,
- 0x9b, 0x2d, 0xf0, 0x92, 0xe1, 0x50, 0x99, 0x97, 0xe9, 0x18, 0x52, 0x90, 0xb3, 0x50, 0x6a, 0x5b,
- 0x8e, 0x3c, 0xf1, 0xe0, 0x06, 0xaf, 0x15, 0xcb, 0x41, 0x96, 0xc6, 0xb3, 0xf4, 0xdb, 0xd2, 0xe5,
- 0x49, 0x64, 0xe9, 0xb7, 0x91, 0xa5, 0xb1, 0x2d, 0xa8, 0xed, 0xba, 0xdb, 0x1b, 0xba, 0xb1, 0xad,
- 0x3c, 0xa3, 0xca, 0x7c, 0x21, 0xe4, 0x5b, 0xd0, 0xe5, 0x64, 0x16, 0xa6, 0x69, 0xc9, 0x3a, 0x3c,
- 0xf2, 0x06, 0xf5, 0x5c, 0x39, 0xca, 0x9b, 0x36, 0xa5, 0x1d, 0x05, 0x23, 0x54, 0x08, 0xee, 0x60,
- 0xf5, 0x99, 0x6c, 0x12, 0xec, 0x57, 0x96, 0xbb, 0x6a, 0xea, 0x5e, 0x8b, 0x06, 0xab, 0x9e, 0x6b,
- 0x50, 0xdf, 0xb7, 0x9c, 0x96, 0x82, 0x1d, 0x8a, 0x60, 0xd7, 0xb2, 0x49, 0xb0, 0x5f, 0x59, 0xf2,
- 0x32, 0x4c, 0x88, 0x2c, 0xb1, 0xd8, 0xce, 0xee, 0xe8, 0x96, 0xad, 0x6f, 0x58, 0xb6, 0xfa, 0xe9,
- 0xd6, 0xa8, 0x38, 0x57, 0x58, 0xeb, 0x43, 0x83, 0x7d, 0x4b, 0x93, 0xab, 0x30, 0xae, 0x4e, 0x95,
- 0x56, 0xa9, 0xd7, 0x0c, 0x8f, 0xb2, 0x47, 0x1b, 0x17, 0xd8, 0x7e, 0x6f, 0x9e, 0x76, 0x3c, 0x6a,
- 0x70, 0xad, 0x2b, 0x45, 0x85, 0x3d, 0xe5, 0x08, 0xc2, 0x19, 0x7e, 0x5e, 0xbd, 0xde, 0x99, 0x73,
- 0x5d, 0xdb, 0x74, 0x6f, 0x39, 0xea, 0xdb, 0x85, 0x62, 0xc3, 0x0f, 0x92, 0x9a, 0x99, 0x14, 0xd8,
- 0xa7, 0x24, 0xfb, 0x72, 0x9e, 0x33, 0xef, 0xde, 0x72, 0xd2, 0xa8, 0x10, 0x7d, 0x79, 0xb3, 0x0f,
- 0x0d, 0xf6, 0x2d, 0x4d, 0x16, 0x80, 0xa4, 0xbf, 0x60, 0xbd, 0xc3, 0x95, 0xa1, 0xd1, 0xc6, 0x19,
- 0x11, 0x1f, 0x23, 0x9d, 0x8b, 0x19, 0x25, 0xc8, 0x32, 0x9c, 0x4e, 0xa7, 0x32, 0x76, 0xdc, 0x49,
- 0x7e, 0x54, 0x44, 0xc6, 0xc4, 0x8c, 0x7c, 0xcc, 0x2c, 0xa5, 0xfd, 0x49, 0x11, 0x46, 0x13, 0x17,
- 0xaa, 0x1f, 0xb8, 0x8b, 0xab, 0x4c, 0x03, 0x6d, 0xfb, 0xad, 0xa5, 0xf9, 0x2b, 0x54, 0x37, 0xa9,
- 0x77, 0x8d, 0xaa, 0xcb, 0xef, 0x7c, 0x52, 0x59, 0x49, 0xe4, 0x60, 0x8a, 0x92, 0x6c, 0x42, 0x45,
- 0xd8, 0x53, 0xf3, 0xfe, 0x2a, 0x41, 0xc9, 0x88, 0x1b, 0x55, 0xf9, 0x92, 0x23, 0x4c, 0xaa, 0x02,
- 0x5e, 0x0b, 0x60, 0x24, 0x4e, 0xc1, 0x26, 0x92, 0x48, 0x59, 0x1b, 0x4e, 0x28, 0x6a, 0x4b, 0x50,
- 0x0a, 0x82, 0x41, 0xaf, 0xc4, 0x0a, 0xfb, 0xfc, 0xda, 0x32, 0x32, 0x0c, 0x6d, 0x93, 0xb5, 0x9d,
- 0xef, 0x5b, 0xae, 0x23, 0xe3, 0x23, 0xaf, 0xc3, 0x70, 0x20, 0x4d, 0x54, 0x83, 0x5d, 0xe9, 0xe5,
- 0xe6, 0x62, 0x65, 0x9e, 0x52, 0x58, 0xda, 0xdf, 0x15, 0xa1, 0x16, 0x6e, 0x27, 0x0f, 0x10, 0x77,
- 0xd8, 0x85, 0x5a, 0xe8, 0x6f, 0x93, 0xfb, 0xb7, 0x67, 0x91, 0x1b, 0x08, 0xdf, 0x01, 0x85, 0xaf,
- 0x18, 0xf1, 0x88, 0xfb, 0xf2, 0x94, 0x72, 0xf8, 0xf2, 0x74, 0x60, 0x38, 0xf0, 0xac, 0x56, 0x4b,
- 0xea, 0xb6, 0x79, 0x9c, 0x79, 0x42, 0x71, 0xad, 0x09, 0x40, 0x29, 0x59, 0xf1, 0x82, 0x8a, 0x8d,
- 0xf6, 0x1a, 0x8c, 0xa7, 0x29, 0xb9, 0xe2, 0x67, 0x6c, 0x51, 0xb3, 0x6b, 0x2b, 0x19, 0x47, 0x8a,
- 0x9f, 0x4c, 0xc7, 0x90, 0x82, 0x6d, 0xfe, 0x58, 0x33, 0xbd, 0xe1, 0x3a, 0x6a, 0x5b, 0xcd, 0x75,
- 0xe8, 0x35, 0x99, 0x86, 0x61, 0xae, 0xf6, 0x2f, 0x25, 0x38, 0x1b, 0x19, 0x05, 0x56, 0x74, 0x47,
- 0x6f, 0x1d, 0xe0, 0x5f, 0x57, 0xef, 0x5f, 0x92, 0x38, 0x6c, 0xf0, 0xf8, 0xd2, 0x03, 0x10, 0x3c,
- 0xfe, 0x47, 0x05, 0xe0, 0xbe, 0x81, 0xe4, 0x0b, 0x30, 0xa2, 0xc7, 0x7e, 0x73, 0x28, 0x9b, 0xf3,
- 0x72, 0xee, 0xe6, 0xe4, 0x2e, 0x88, 0xa1, 0xaf, 0x4b, 0x3c, 0x15, 0x13, 0x0c, 0x89, 0x0b, 0xd5,
- 0x4d, 0xdd, 0xb6, 0x99, 0x2e, 0x94, 0xfb, 0x90, 0x23, 0xc1, 0x9c, 0x77, 0xf3, 0x05, 0x09, 0x8d,
- 0x21, 0x13, 0xed, 0x9f, 0x0b, 0x30, 0xda, 0xb4, 0x2d, 0xd3, 0x72, 0x5a, 0xc7, 0x18, 0x35, 0xfe,
- 0x06, 0x54, 0x7c, 0xdb, 0x32, 0xe9, 0x80, 0xf3, 0xb8, 0x58, 0x41, 0x18, 0x00, 0x0a, 0x9c, 0x64,
- 0x18, 0xfa, 0xd2, 0x01, 0xc2, 0xd0, 0xff, 0x64, 0x08, 0xa4, 0x7f, 0x29, 0xe9, 0x42, 0xad, 0xa5,
- 0xa2, 0x5b, 0xcb, 0x6f, 0xbc, 0x92, 0x23, 0x32, 0x5a, 0x22, 0x4e, 0xb6, 0x98, 0x75, 0xc3, 0x44,
- 0x8c, 0x38, 0x11, 0x9a, 0xfc, 0xb3, 0xe5, 0x7c, 0xce, 0x3f, 0x5b, 0x0a, 0x76, 0xbd, 0xff, 0xb6,
- 0xd4, 0xa1, 0xbc, 0x15, 0x04, 0x1d, 0x39, 0xae, 0x06, 0x77, 0x20, 0x8e, 0x82, 0x73, 0x08, 0x6d,
- 0x84, 0xbd, 0x23, 0x87, 0x66, 0x2c, 0x1c, 0x3d, 0xfc, 0xa1, 0xd2, 0x5c, 0xae, 0x83, 0xee, 0x38,
- 0x0b, 0xf6, 0x8e, 0x1c, 0x9a, 0x7c, 0x1e, 0xea, 0x81, 0xa7, 0x3b, 0xfe, 0xa6, 0xeb, 0xb5, 0xa9,
- 0x27, 0x77, 0x87, 0x0b, 0x39, 0x7e, 0xee, 0xb8, 0x16, 0xa1, 0x89, 0x13, 0xb4, 0x44, 0x12, 0xc6,
- 0xb9, 0x91, 0x6d, 0xa8, 0x76, 0x4d, 0x51, 0x31, 0x69, 0x36, 0x99, 0xcd, 0xf3, 0xbf, 0xce, 0xd8,
- 0x31, 0xb6, 0x7a, 0xc3, 0x90, 0x41, 0xf2, 0xdf, 0x61, 0xc3, 0x47, 0xf5, 0xef, 0xb0, 0x78, 0x6f,
- 0xcc, 0x8a, 0x1c, 0x40, 0xda, 0x52, 0xa3, 0x74, 0x5a, 0xd2, 0x0b, 0x67, 0x21, 0xb7, 0xb2, 0x27,
- 0x58, 0xd6, 0x43, 0xad, 0xd4, 0x69, 0xa1, 0xe2, 0xa1, 0xb5, 0x41, 0x5a, 0xb7, 0x89, 0x91, 0xf8,
- 0xc3, 0x86, 0xb8, 0xce, 0x32, 0x7d, 0xb0, 0xf9, 0x20, 0xfc, 0xd5, 0x43, 0x2c, 0xc2, 0x6f, 0xe6,
- 0xaf, 0x34, 0xb4, 0xbf, 0x2f, 0x42, 0x69, 0x6d, 0xb9, 0x29, 0xa2, 0xf6, 0xf1, 0xdf, 0xd7, 0xd0,
- 0xe6, 0xb6, 0xd5, 0xb9, 0x49, 0x3d, 0x6b, 0x73, 0x57, 0x6e, 0x7a, 0x63, 0x51, 0xfb, 0xd2, 0x14,
- 0x98, 0x51, 0x8a, 0xbc, 0x02, 0x23, 0x86, 0x3e, 0x47, 0xbd, 0x60, 0x90, 0x2d, 0x3d, 0xbf, 0xb7,
- 0x37, 0x37, 0x1b, 0x15, 0xc7, 0x04, 0x18, 0x59, 0x07, 0x30, 0x22, 0xe8, 0xd2, 0xa1, 0x0d, 0x11,
- 0x31, 0xe0, 0x18, 0x10, 0x41, 0xa8, 0x6d, 0x33, 0x52, 0x8e, 0x5a, 0x3e, 0x0c, 0x2a, 0xef, 0x39,
- 0xd7, 0x54, 0x59, 0x8c, 0x60, 0x34, 0x07, 0x46, 0x13, 0xbf, 0xdd, 0x20, 0x1f, 0x85, 0xaa, 0xdb,
- 0x89, 0x4d, 0xa7, 0x35, 0xee, 0xef, 0x57, 0xbd, 0x21, 0xd3, 0xee, 0xec, 0x4d, 0x8e, 0x2e, 0xbb,
- 0x2d, 0xcb, 0x50, 0x09, 0x18, 0x92, 0x13, 0x0d, 0x86, 0xf8, 0x65, 0x1b, 0xf5, 0xd3, 0x0d, 0xbe,
- 0x76, 0xf0, 0xb8, 0xf8, 0x3e, 0xca, 0x1c, 0xed, 0x8b, 0x65, 0x88, 0xce, 0x84, 0x88, 0x0f, 0x43,
- 0xc2, 0x99, 0x58, 0xce, 0xdc, 0xc7, 0xea, 0xb7, 0x2c, 0x59, 0x91, 0x16, 0x94, 0x5e, 0x73, 0x37,
- 0x72, 0x4f, 0xdc, 0xb1, 0x5b, 0xb6, 0xc2, 0x4a, 0x15, 0x4b, 0x40, 0xc6, 0x81, 0xfc, 0x5a, 0x01,
- 0x4e, 0xfa, 0x69, 0xa5, 0x53, 0x76, 0x07, 0xcc, 0xaf, 0x5d, 0xa7, 0xd5, 0x58, 0xe9, 0x98, 0xd9,
- 0x2f, 0x1b, 0x7b, 0xeb, 0xc2, 0xe4, 0x2f, 0x0e, 0x6b, 0x64, 0x77, 0x5a, 0xcc, 0xf9, 0xab, 0xb8,
- 0xa4, 0xfc, 0x93, 0x69, 0x28, 0x59, 0x69, 0x5f, 0x2e, 0x42, 0x3d, 0x36, 0x5b, 0xe7, 0xfe, 0x97,
- 0xcb, 0xed, 0xd4, 0xbf, 0x5c, 0x56, 0x07, 0x3f, 0xbb, 0x8c, 0x6a, 0x75, 0xdc, 0xbf, 0x73, 0xf9,
- 0xf3, 0x22, 0x94, 0xd6, 0xe7, 0x17, 0x92, 0xdb, 0xc5, 0xc2, 0x7d, 0xd8, 0x2e, 0x6e, 0xc1, 0xf0,
- 0x46, 0xd7, 0xb2, 0x03, 0xcb, 0xc9, 0x1d, 0x07, 0x40, 0xfd, 0xfa, 0x46, 0x5e, 0xa7, 0x15, 0xa8,
- 0xa8, 0xe0, 0x49, 0x0b, 0x86, 0x5b, 0x22, 0x10, 0x5b, 0x6e, 0x8f, 0x2e, 0x19, 0xd0, 0x4d, 0x30,
- 0x92, 0x2f, 0xa8, 0xd0, 0xb5, 0x5d, 0x90, 0x3f, 0xcf, 0xbe, 0xef, 0xd2, 0xd4, 0x3e, 0x0f, 0xa1,
- 0x16, 0x70, 0xff, 0x99, 0xff, 0x5b, 0x01, 0x92, 0x8a, 0xcf, 0xfd, 0xef, 0x4d, 0xdb, 0xe9, 0xde,
- 0x34, 0x7f, 0x14, 0x83, 0x2f, 0xbb, 0x43, 0x69, 0x7f, 0x58, 0x84, 0xa1, 0xfb, 0x76, 0x77, 0x93,
- 0x26, 0x9c, 0xd3, 0xe6, 0x72, 0x4e, 0x8c, 0x7d, 0x5d, 0xd3, 0xda, 0x29, 0xd7, 0xb4, 0xbc, 0x3f,
- 0xeb, 0xbc, 0x87, 0x63, 0xda, 0x5f, 0x17, 0x40, 0x4e, 0xcb, 0x4b, 0x8e, 0x1f, 0xe8, 0x8e, 0xc1,
- 0xff, 0x19, 0x2f, 0xd7, 0x80, 0xbc, 0x1e, 0x10, 0xd2, 0x4b, 0x48, 0x2c, 0xfb, 0xfc, 0x59, 0xcd,
- 0xf9, 0xe4, 0xc3, 0x50, 0xdd, 0x72, 0xfd, 0x80, 0xcf, 0xf3, 0xc5, 0xa4, 0x5d, 0xe7, 0x8a, 0x4c,
- 0xc7, 0x90, 0x22, 0x7d, 0x52, 0x58, 0xe9, 0x7f, 0x52, 0xa8, 0x7d, 0xb3, 0x08, 0x23, 0xef, 0x95,
- 0x0b, 0xa8, 0x59, 0xae, 0x7c, 0xa5, 0x9c, 0xae, 0x7c, 0xe5, 0xc3, 0xb8, 0xf2, 0x69, 0xdf, 0x29,
- 0x00, 0xdc, 0xb7, 0xdb, 0xaf, 0x66, 0xd2, 0xcb, 0x2e, 0x77, 0xbf, 0xca, 0xf6, 0xb1, 0xfb, 0xbd,
- 0x8a, 0xfa, 0x24, 0xee, 0x61, 0xf7, 0x66, 0x01, 0xc6, 0xf4, 0x84, 0xd7, 0x5a, 0x6e, 0xd5, 0x32,
- 0xe5, 0x04, 0x17, 0xde, 0xf4, 0x4b, 0xa6, 0x63, 0x8a, 0x2d, 0x79, 0x3e, 0x8a, 0xbc, 0x7a, 0x3d,
- 0xea, 0xf6, 0x3d, 0x21, 0x53, 0xb9, 0x9a, 0x93, 0xa0, 0xbc, 0x87, 0x97, 0x60, 0xe9, 0x48, 0xbc,
- 0x04, 0xe3, 0xf7, 0x9f, 0xca, 0x77, 0xbd, 0xff, 0xb4, 0x03, 0xb5, 0x4d, 0xcf, 0x6d, 0x73, 0x47,
- 0x3c, 0xf9, 0x9b, 0xcf, 0xcb, 0x39, 0xd6, 0x94, 0xe8, 0x07, 0xd7, 0x91, 0x8d, 0x67, 0x41, 0xe1,
- 0x63, 0xc4, 0x8a, 0x1b, 0xa4, 0x5d, 0xc1, 0x75, 0xe8, 0x28, 0xb9, 0x86, 0x73, 0xc9, 0x9a, 0x40,
- 0x47, 0xc5, 0x26, 0xe9, 0x7c, 0x37, 0x7c, 0x7f, 0x9c, 0xef, 0xb4, 0x5f, 0x28, 0xab, 0x09, 0xec,
- 0x81, 0x0b, 0xf2, 0xf7, 0xde, 0xbf, 0x35, 0x99, 0xbe, 0xd2, 0x38, 0x7c, 0x1f, 0xaf, 0x34, 0x56,
- 0x07, 0x72, 0xf5, 0xda, 0x2b, 0x41, 0x6a, 0xdf, 0xf4, 0xfe, 0xe9, 0xc4, 0x7f, 0xa9, 0xd3, 0x89,
- 0xb7, 0x8a, 0x10, 0x4d, 0x04, 0x87, 0xf4, 0xde, 0x78, 0x19, 0xaa, 0x6d, 0xfd, 0xf6, 0x3c, 0xb5,
- 0xf5, 0xdd, 0x3c, 0xff, 0x66, 0x5c, 0x91, 0x18, 0x18, 0xa2, 0x11, 0x1f, 0xc0, 0x0a, 0xe3, 0x23,
- 0xe7, 0xb6, 0x36, 0x47, 0xa1, 0x96, 0x85, 0x3d, 0x2b, 0x7a, 0xc7, 0x18, 0x1b, 0xed, 0xaf, 0x8a,
- 0x20, 0x03, 0x69, 0x13, 0x0a, 0x95, 0x4d, 0xeb, 0x36, 0x35, 0x73, 0x7b, 0x32, 0xc6, 0xfe, 0x98,
- 0x2b, 0xcc, 0xe9, 0x3c, 0x01, 0x05, 0x3a, 0xb7, 0x93, 0x8a, 0xe3, 0x11, 0x29, 0xbf, 0x1c, 0x76,
- 0xd2, 0xf8, 0x31, 0x8b, 0xb4, 0x93, 0x8a, 0x24, 0x54, 0x3c, 0x84, 0x59, 0x96, 0x9f, 0x51, 0x4b,
- 0x91, 0xe6, 0x31, 0xcb, 0xc6, 0xce, 0xba, 0x95, 0x59, 0xd6, 0x17, 0x77, 0x9a, 0x25, 0x8f, 0xc6,
- 0x67, 0xbf, 0xfd, 0xdd, 0x0b, 0x0f, 0x7d, 0xe7, 0xbb, 0x17, 0x1e, 0x7a, 0xe7, 0xbb, 0x17, 0x1e,
- 0xfa, 0xe2, 0xfe, 0x85, 0xc2, 0xb7, 0xf7, 0x2f, 0x14, 0xbe, 0xb3, 0x7f, 0xa1, 0xf0, 0xce, 0xfe,
- 0x85, 0xc2, 0x3f, 0xee, 0x5f, 0x28, 0xfc, 0xf2, 0x3f, 0x5d, 0x78, 0xe8, 0x33, 0xcf, 0x45, 0x55,
- 0x98, 0x56, 0x55, 0x98, 0x56, 0x0c, 0xa7, 0x3b, 0xdb, 0xad, 0x69, 0x56, 0x85, 0x28, 0x45, 0x55,
- 0xe1, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x9c, 0x8e, 0xe2, 0xbf, 0x92, 0x00, 0x00,
+ // 7464 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x1d, 0xd7,
+ 0x75, 0xae, 0xcf, 0x1f, 0x79, 0xce, 0x3a, 0x24, 0x45, 0x6d, 0xc9, 0x32, 0x25, 0xcb, 0xa2, 0x32,
+ 0x8e, 0x7d, 0x95, 0x9b, 0x84, 0xbc, 0xe6, 0xf5, 0x5f, 0x72, 0x93, 0xd8, 0x3c, 0xa4, 0x48, 0x51,
+ 0x22, 0x25, 0x66, 0x1d, 0x52, 0x76, 0xe2, 0x9b, 0xf8, 0x0e, 0x67, 0x36, 0x0f, 0xc7, 0x9c, 0x33,
+ 0x73, 0x3c, 0x33, 0x87, 0x12, 0x9d, 0x7b, 0x91, 0xbf, 0x07, 0xfb, 0xa2, 0x2d, 0x5a, 0xe4, 0x29,
+ 0x40, 0x91, 0x16, 0x2d, 0x0a, 0xe4, 0x21, 0x48, 0x1f, 0x0a, 0xb8, 0x0f, 0x05, 0xfa, 0x93, 0xa2,
+ 0x68, 0xd3, 0xa2, 0x3f, 0x79, 0x28, 0x50, 0xf7, 0x85, 0x68, 0x58, 0xf4, 0xa1, 0x05, 0x1a, 0x04,
+ 0x0d, 0xd0, 0x24, 0x42, 0x80, 0x14, 0xfb, 0x6f, 0xfe, 0xce, 0x1c, 0x89, 0x3c, 0x43, 0xca, 0x72,
+ 0xeb, 0xb7, 0x99, 0xbd, 0xd7, 0xfe, 0xd6, 0xde, 0x6b, 0xf6, 0xcf, 0xda, 0x6b, 0xaf, 0xbd, 0x06,
+ 0x16, 0x5b, 0x56, 0xb0, 0xd5, 0xdd, 0x98, 0x32, 0xdc, 0xf6, 0xb4, 0xd3, 0x6d, 0xeb, 0x1d, 0xcf,
+ 0x7d, 0x8d, 0x3f, 0x6c, 0xda, 0xee, 0xad, 0xe9, 0xce, 0x76, 0x6b, 0x5a, 0xef, 0x58, 0x7e, 0x94,
+ 0xb2, 0xf3, 0x94, 0x6e, 0x77, 0xb6, 0xf4, 0xa7, 0xa6, 0x5b, 0xd4, 0xa1, 0x9e, 0x1e, 0x50, 0x73,
+ 0xaa, 0xe3, 0xb9, 0x81, 0x4b, 0x9e, 0x8b, 0x80, 0xa6, 0x14, 0xd0, 0x94, 0x2a, 0x36, 0xd5, 0xd9,
+ 0x6e, 0x4d, 0x31, 0xa0, 0x28, 0x45, 0x01, 0x9d, 0xfb, 0x68, 0xac, 0x06, 0x2d, 0xb7, 0xe5, 0x4e,
+ 0x73, 0xbc, 0x8d, 0xee, 0x26, 0x7f, 0xe3, 0x2f, 0xfc, 0x49, 0xf0, 0x39, 0xa7, 0x6d, 0x3f, 0xef,
+ 0x4f, 0x59, 0x2e, 0xab, 0xd6, 0xb4, 0xe1, 0x7a, 0x74, 0x7a, 0xa7, 0xa7, 0x2e, 0xe7, 0x9e, 0x8e,
+ 0x68, 0xda, 0xba, 0xb1, 0x65, 0x39, 0xd4, 0xdb, 0x55, 0x6d, 0x99, 0xf6, 0xa8, 0xef, 0x76, 0x3d,
+ 0x83, 0x1e, 0xaa, 0x94, 0x3f, 0xdd, 0xa6, 0x81, 0x9e, 0xc5, 0x6b, 0xba, 0x5f, 0x29, 0xaf, 0xeb,
+ 0x04, 0x56, 0xbb, 0x97, 0xcd, 0xb3, 0xf7, 0x2a, 0xe0, 0x1b, 0x5b, 0xb4, 0xad, 0xa7, 0xcb, 0x69,
+ 0xdf, 0x01, 0x38, 0x35, 0xbb, 0xe1, 0x07, 0x9e, 0x6e, 0x04, 0xab, 0xae, 0xb9, 0x46, 0xdb, 0x1d,
+ 0x5b, 0x0f, 0x28, 0xd9, 0x86, 0x2a, 0xab, 0x9b, 0xa9, 0x07, 0xfa, 0x44, 0xe1, 0x62, 0xe1, 0x52,
+ 0x7d, 0x66, 0x76, 0x6a, 0xc0, 0x6f, 0x31, 0xb5, 0x22, 0x81, 0x1a, 0x23, 0xfb, 0x7b, 0x93, 0x55,
+ 0xf5, 0x86, 0x21, 0x03, 0xf2, 0xf5, 0x02, 0x8c, 0x38, 0xae, 0x49, 0x9b, 0xd4, 0xa6, 0x46, 0xe0,
+ 0x7a, 0x13, 0xc5, 0x8b, 0xa5, 0x4b, 0xf5, 0x99, 0xcf, 0x0f, 0xcc, 0x31, 0xa3, 0x45, 0x53, 0xd7,
+ 0x63, 0x0c, 0x2e, 0x3b, 0x81, 0xb7, 0xdb, 0x38, 0xfd, 0xdd, 0xbd, 0xc9, 0x87, 0xf6, 0xf7, 0x26,
+ 0x47, 0xe2, 0x59, 0x98, 0xa8, 0x09, 0x59, 0x87, 0x7a, 0xe0, 0xda, 0x4c, 0x64, 0x96, 0xeb, 0xf8,
+ 0x13, 0x25, 0x5e, 0xb1, 0x0b, 0x53, 0x42, 0xda, 0x8c, 0xfd, 0x14, 0xeb, 0x2e, 0x53, 0x3b, 0x4f,
+ 0x4d, 0xad, 0x85, 0x64, 0x8d, 0x53, 0x12, 0xb8, 0x1e, 0xa5, 0xf9, 0x18, 0xc7, 0x21, 0x14, 0x4e,
+ 0xf8, 0xd4, 0xe8, 0x7a, 0x56, 0xb0, 0x3b, 0xe7, 0x3a, 0x01, 0xbd, 0x1d, 0x4c, 0x94, 0xb9, 0x94,
+ 0x9f, 0xcc, 0x82, 0x5e, 0x75, 0xcd, 0x66, 0x92, 0xba, 0x71, 0x6a, 0x7f, 0x6f, 0xf2, 0x44, 0x2a,
+ 0x11, 0xd3, 0x98, 0xc4, 0x81, 0x71, 0xab, 0xad, 0xb7, 0xe8, 0x6a, 0xd7, 0xb6, 0x9b, 0xd4, 0xf0,
+ 0x68, 0xe0, 0x4f, 0x54, 0x78, 0x13, 0x2e, 0x65, 0xf1, 0x59, 0x76, 0x0d, 0xdd, 0xbe, 0xb1, 0xf1,
+ 0x1a, 0x35, 0x02, 0xa4, 0x9b, 0xd4, 0xa3, 0x8e, 0x41, 0x1b, 0x13, 0xb2, 0x31, 0xe3, 0x4b, 0x29,
+ 0x24, 0xec, 0xc1, 0x26, 0x8b, 0x70, 0xb2, 0xe3, 0x59, 0x2e, 0xaf, 0x82, 0xad, 0xfb, 0xfe, 0x75,
+ 0xbd, 0x4d, 0x27, 0x86, 0x2e, 0x16, 0x2e, 0xd5, 0x1a, 0x67, 0x25, 0xcc, 0xc9, 0xd5, 0x34, 0x01,
+ 0xf6, 0x96, 0x21, 0x97, 0xa0, 0xaa, 0x12, 0x27, 0x86, 0x2f, 0x16, 0x2e, 0x55, 0x44, 0xdf, 0x51,
+ 0x65, 0x31, 0xcc, 0x25, 0x0b, 0x50, 0xd5, 0x37, 0x37, 0x2d, 0x87, 0x51, 0x56, 0xb9, 0x08, 0xcf,
+ 0x67, 0x35, 0x6d, 0x56, 0xd2, 0x08, 0x1c, 0xf5, 0x86, 0x61, 0x59, 0x72, 0x15, 0x88, 0x4f, 0xbd,
+ 0x1d, 0xcb, 0xa0, 0xb3, 0x86, 0xe1, 0x76, 0x9d, 0x80, 0xd7, 0xbd, 0xc6, 0xeb, 0x7e, 0x4e, 0xd6,
+ 0x9d, 0x34, 0x7b, 0x28, 0x30, 0xa3, 0x14, 0x79, 0x11, 0xc6, 0xe5, 0xb0, 0x8b, 0xa4, 0x00, 0x1c,
+ 0xe9, 0x34, 0x13, 0x24, 0xa6, 0xf2, 0xb0, 0x87, 0x9a, 0x98, 0x70, 0x5e, 0xef, 0x06, 0x6e, 0x9b,
+ 0x41, 0x26, 0x99, 0xae, 0xb9, 0xdb, 0xd4, 0x99, 0xa8, 0x5f, 0x2c, 0x5c, 0xaa, 0x36, 0x2e, 0xee,
+ 0xef, 0x4d, 0x9e, 0x9f, 0xbd, 0x0b, 0x1d, 0xde, 0x15, 0x85, 0xdc, 0x80, 0x9a, 0xe9, 0xf8, 0xab,
+ 0xae, 0x6d, 0x19, 0xbb, 0x13, 0x23, 0xbc, 0x82, 0x4f, 0xc9, 0xa6, 0xd6, 0xe6, 0xaf, 0x37, 0x45,
+ 0xc6, 0x9d, 0xbd, 0xc9, 0xf3, 0xbd, 0xb3, 0xe3, 0x54, 0x98, 0x8f, 0x11, 0x06, 0x59, 0xe1, 0x80,
+ 0x73, 0xae, 0xb3, 0x69, 0xb5, 0x26, 0x46, 0xf9, 0xd7, 0xb8, 0xd8, 0xa7, 0x43, 0xcf, 0x5f, 0x6f,
+ 0x0a, 0xba, 0xc6, 0xa8, 0x64, 0x27, 0x5e, 0x31, 0x42, 0x20, 0x26, 0x8c, 0xa9, 0x79, 0x75, 0xce,
+ 0xd6, 0xad, 0xb6, 0x3f, 0x31, 0xc6, 0x3b, 0xef, 0x07, 0xfb, 0x60, 0x62, 0x9c, 0xb8, 0x71, 0x46,
+ 0x36, 0x65, 0x2c, 0x91, 0xec, 0x63, 0x0a, 0xf3, 0xdc, 0x0b, 0x70, 0xb2, 0x67, 0x6e, 0x20, 0xe3,
+ 0x50, 0xda, 0xa6, 0xbb, 0x7c, 0xea, 0xab, 0x21, 0x7b, 0x24, 0xa7, 0xa1, 0xb2, 0xa3, 0xdb, 0x5d,
+ 0x3a, 0x51, 0xe4, 0x69, 0xe2, 0xe5, 0xe3, 0xc5, 0xe7, 0x0b, 0xda, 0x6f, 0x96, 0x60, 0x44, 0xcd,
+ 0x38, 0x4d, 0xcb, 0xd9, 0x26, 0x2f, 0x41, 0xc9, 0x76, 0x5b, 0x72, 0xde, 0xfc, 0xc4, 0xc0, 0xb3,
+ 0xd8, 0xb2, 0xdb, 0x6a, 0x0c, 0xef, 0xef, 0x4d, 0x96, 0x96, 0xdd, 0x16, 0x32, 0x44, 0x62, 0x40,
+ 0x65, 0x5b, 0xdf, 0xdc, 0xd6, 0x79, 0x1d, 0xea, 0x33, 0x8d, 0x81, 0xa1, 0xaf, 0x31, 0x14, 0x56,
+ 0xd7, 0x46, 0x6d, 0x7f, 0x6f, 0xb2, 0xc2, 0x5f, 0x51, 0x60, 0x13, 0x17, 0x6a, 0x1b, 0xb6, 0x6e,
+ 0x6c, 0x6f, 0xb9, 0x36, 0x9d, 0x28, 0xe5, 0x64, 0xd4, 0x50, 0x48, 0xe2, 0x33, 0x87, 0xaf, 0x18,
+ 0xf1, 0x20, 0x06, 0x0c, 0x75, 0x4d, 0xdf, 0x72, 0xb6, 0xe5, 0x1c, 0xf8, 0xc2, 0xc0, 0xdc, 0xd6,
+ 0xe7, 0x79, 0x9b, 0x60, 0x7f, 0x6f, 0x72, 0x48, 0x3c, 0xa3, 0x84, 0xd6, 0x7e, 0x50, 0x87, 0x31,
+ 0xf5, 0x91, 0x6e, 0x52, 0x2f, 0xa0, 0xb7, 0xc9, 0x45, 0x28, 0x3b, 0x6c, 0x68, 0xf2, 0x8f, 0xdc,
+ 0x18, 0x91, 0xdd, 0xa5, 0xcc, 0x87, 0x24, 0xcf, 0x61, 0x35, 0x13, 0x5d, 0x45, 0x0a, 0x7c, 0xf0,
+ 0x9a, 0x35, 0x39, 0x8c, 0xa8, 0x99, 0x78, 0x46, 0x09, 0x4d, 0x5e, 0x81, 0x32, 0x6f, 0xbc, 0x10,
+ 0xf5, 0x27, 0x07, 0x67, 0xc1, 0x9a, 0x5e, 0x65, 0x2d, 0xe0, 0x0d, 0xe7, 0xa0, 0xac, 0x2b, 0x76,
+ 0xcd, 0x4d, 0x29, 0xd8, 0x4f, 0xe4, 0x10, 0xec, 0x82, 0xe8, 0x8a, 0xeb, 0xf3, 0x0b, 0xc8, 0x10,
+ 0xc9, 0x2f, 0x17, 0xe0, 0xa4, 0xe1, 0x3a, 0x81, 0xce, 0x54, 0x0d, 0xb5, 0xc8, 0x4e, 0x54, 0x38,
+ 0x9f, 0xab, 0x03, 0xf3, 0x99, 0x4b, 0x23, 0x36, 0x1e, 0x66, 0x6b, 0x46, 0x4f, 0x32, 0xf6, 0xf2,
+ 0x26, 0xbf, 0x5a, 0x80, 0x87, 0xd9, 0x5c, 0xde, 0x43, 0xcc, 0x57, 0xa0, 0xa3, 0xad, 0xd5, 0xd9,
+ 0xfd, 0xbd, 0xc9, 0x87, 0x97, 0xb2, 0x98, 0x61, 0x76, 0x1d, 0x58, 0xed, 0x4e, 0xe9, 0xbd, 0x6a,
+ 0x09, 0x5f, 0xdd, 0xea, 0x33, 0xcb, 0x47, 0xa9, 0xea, 0x34, 0x1e, 0x95, 0x5d, 0x39, 0x4b, 0xb3,
+ 0xc3, 0xac, 0x5a, 0x90, 0xcb, 0x30, 0xbc, 0xe3, 0xda, 0xdd, 0x36, 0xf5, 0x27, 0xaa, 0x7c, 0x8a,
+ 0x3d, 0x97, 0x35, 0xc5, 0xde, 0xe4, 0x24, 0x8d, 0x13, 0x12, 0x7e, 0x58, 0xbc, 0xfb, 0xa8, 0xca,
+ 0x12, 0x0b, 0x86, 0x6c, 0xab, 0x6d, 0x05, 0x3e, 0x5f, 0x38, 0xeb, 0x33, 0x97, 0x07, 0x6e, 0x96,
+ 0x18, 0xa2, 0xcb, 0x1c, 0x4c, 0x8c, 0x1a, 0xf1, 0x8c, 0x92, 0x01, 0x9b, 0x0a, 0x7d, 0x43, 0xb7,
+ 0xc5, 0xc2, 0x5a, 0x9f, 0xf9, 0xd4, 0xe0, 0xc3, 0x86, 0xa1, 0x34, 0x46, 0x65, 0x9b, 0x2a, 0xfc,
+ 0x15, 0x05, 0x36, 0xf9, 0x1c, 0x8c, 0x25, 0xbe, 0xa6, 0x3f, 0x51, 0xe7, 0xd2, 0x79, 0x2c, 0x4b,
+ 0x3a, 0x21, 0x55, 0xb4, 0xf2, 0x24, 0x7a, 0x88, 0x8f, 0x29, 0x30, 0x72, 0x0d, 0xaa, 0xbe, 0x65,
+ 0x52, 0x43, 0xf7, 0xfc, 0x89, 0x91, 0x83, 0x00, 0x8f, 0x4b, 0xe0, 0x6a, 0x53, 0x16, 0xc3, 0x10,
+ 0x80, 0x4c, 0x01, 0x74, 0x74, 0x2f, 0xb0, 0x84, 0xa2, 0x3a, 0xca, 0x95, 0xa6, 0xb1, 0xfd, 0xbd,
+ 0x49, 0x58, 0x0d, 0x53, 0x31, 0x46, 0xc1, 0xe8, 0x59, 0xd9, 0x25, 0xa7, 0xd3, 0x0d, 0xc4, 0xc2,
+ 0x5a, 0x13, 0xf4, 0xcd, 0x30, 0x15, 0x63, 0x14, 0xe4, 0xdb, 0x05, 0x78, 0x34, 0x7a, 0xed, 0x1d,
+ 0x64, 0x27, 0x8e, 0x7c, 0x90, 0x4d, 0xee, 0xef, 0x4d, 0x3e, 0xda, 0xec, 0xcf, 0x12, 0xef, 0x56,
+ 0x1f, 0xed, 0x25, 0x18, 0x9d, 0xed, 0x06, 0x5b, 0xae, 0x67, 0xbd, 0xc1, 0x95, 0x6e, 0xb2, 0x00,
+ 0x95, 0x80, 0x2b, 0x4f, 0x62, 0x5d, 0x7e, 0x22, 0x4b, 0xd4, 0x42, 0x91, 0xbd, 0x46, 0x77, 0x95,
+ 0x36, 0x20, 0xd6, 0x47, 0xa1, 0x4c, 0x89, 0xe2, 0xda, 0x6f, 0x14, 0xa0, 0xd6, 0xd0, 0x7d, 0xcb,
+ 0x60, 0xf0, 0x64, 0x0e, 0xca, 0x5d, 0x9f, 0x7a, 0x87, 0x03, 0xe5, 0xb3, 0xf4, 0xba, 0x4f, 0x3d,
+ 0xe4, 0x85, 0xc9, 0x0d, 0xa8, 0x76, 0x74, 0xdf, 0xbf, 0xe5, 0x7a, 0xa6, 0x5c, 0x69, 0x0e, 0x08,
+ 0x24, 0xb4, 0x62, 0x59, 0x14, 0x43, 0x10, 0xad, 0x0e, 0xd1, 0x52, 0xab, 0xfd, 0xa8, 0x00, 0xa7,
+ 0x1a, 0xdd, 0xcd, 0x4d, 0xea, 0x49, 0x25, 0x50, 0xaa, 0x57, 0x14, 0x2a, 0x1e, 0x35, 0x2d, 0x5f,
+ 0xd6, 0x7d, 0x7e, 0xe0, 0x4f, 0x87, 0x0c, 0x45, 0x6a, 0x73, 0x5c, 0x5e, 0x3c, 0x01, 0x05, 0x3a,
+ 0xe9, 0x42, 0xed, 0x35, 0x1a, 0xf8, 0x81, 0x47, 0xf5, 0xb6, 0x6c, 0xdd, 0x95, 0x81, 0x59, 0x5d,
+ 0xa5, 0x41, 0x93, 0x23, 0xc5, 0x95, 0xc7, 0x30, 0x11, 0x23, 0x4e, 0xda, 0x77, 0x2a, 0x30, 0x32,
+ 0xe7, 0xb6, 0x37, 0x2c, 0x87, 0x9a, 0x97, 0xcd, 0x16, 0x25, 0xaf, 0x42, 0x99, 0x9a, 0x2d, 0x2a,
+ 0x5b, 0x3b, 0xf8, 0x3a, 0xcb, 0xc0, 0x22, 0x6d, 0x81, 0xbd, 0x21, 0x07, 0x26, 0xcb, 0x30, 0xb6,
+ 0xe9, 0xb9, 0x6d, 0x31, 0x75, 0xad, 0xed, 0x76, 0xa4, 0xaa, 0xd8, 0xf8, 0xa0, 0x9a, 0x0e, 0x16,
+ 0x12, 0xb9, 0x77, 0xf6, 0x26, 0x21, 0x7a, 0xc3, 0x54, 0x59, 0xf2, 0x32, 0x4c, 0x44, 0x29, 0xe1,
+ 0x18, 0x9e, 0x63, 0xda, 0x3b, 0x57, 0x15, 0x2a, 0x8d, 0xf3, 0xfb, 0x7b, 0x93, 0x13, 0x0b, 0x7d,
+ 0x68, 0xb0, 0x6f, 0x69, 0xf2, 0x66, 0x01, 0xc6, 0xa3, 0x4c, 0x31, 0xaf, 0x4a, 0x0d, 0xe1, 0x88,
+ 0x26, 0x6c, 0xbe, 0xcd, 0x59, 0x48, 0xb1, 0xc0, 0x1e, 0xa6, 0x64, 0x01, 0x46, 0x02, 0x37, 0x26,
+ 0xaf, 0x0a, 0x97, 0x97, 0xa6, 0xf6, 0xe5, 0x6b, 0x6e, 0x5f, 0x69, 0x25, 0xca, 0x11, 0x84, 0x33,
+ 0xea, 0x3d, 0x25, 0xa9, 0x21, 0x2e, 0xa9, 0x73, 0xfb, 0x7b, 0x93, 0x67, 0xd6, 0x32, 0x29, 0xb0,
+ 0x4f, 0x49, 0xf2, 0xe5, 0x02, 0x8c, 0xa9, 0x2c, 0x29, 0xa3, 0xe1, 0xa3, 0x94, 0x11, 0x61, 0x3d,
+ 0x62, 0x2d, 0xc1, 0x00, 0x53, 0x0c, 0xb5, 0x9f, 0x94, 0xa1, 0x16, 0xce, 0x6c, 0xe4, 0x71, 0xa8,
+ 0xf0, 0x1d, 0xb7, 0x54, 0x58, 0xc3, 0x25, 0x8b, 0x6f, 0xcc, 0x51, 0xe4, 0x91, 0x27, 0x60, 0xd8,
+ 0x70, 0xdb, 0x6d, 0xdd, 0x31, 0xb9, 0x15, 0xa5, 0xd6, 0xa8, 0xb3, 0x95, 0x7a, 0x4e, 0x24, 0xa1,
+ 0xca, 0x23, 0xe7, 0xa1, 0xac, 0x7b, 0x2d, 0x61, 0xd0, 0xa8, 0x89, 0xf9, 0x68, 0xd6, 0x6b, 0xf9,
+ 0xc8, 0x53, 0xc9, 0xc7, 0xa0, 0x44, 0x9d, 0x9d, 0x89, 0x72, 0x7f, 0x55, 0xe0, 0xb2, 0xb3, 0x73,
+ 0x53, 0xf7, 0x1a, 0x75, 0x59, 0x87, 0xd2, 0x65, 0x67, 0x07, 0x59, 0x19, 0xb2, 0x0c, 0xc3, 0xd4,
+ 0xd9, 0x61, 0xdf, 0x5e, 0x5a, 0x1a, 0x3e, 0xd0, 0xa7, 0x38, 0x23, 0x91, 0x5a, 0x71, 0xa8, 0x50,
+ 0xc8, 0x64, 0x54, 0x10, 0xe4, 0x33, 0x30, 0x22, 0x74, 0x8b, 0x15, 0xf6, 0x4d, 0xfc, 0x89, 0x21,
+ 0x0e, 0x39, 0xd9, 0x5f, 0x39, 0xe1, 0x74, 0x91, 0x65, 0x27, 0x96, 0xe8, 0x63, 0x02, 0x8a, 0x7c,
+ 0x06, 0x6a, 0x6a, 0x23, 0xa8, 0xbe, 0x6c, 0xa6, 0x51, 0x44, 0xed, 0x1e, 0x91, 0xbe, 0xde, 0xb5,
+ 0x3c, 0xda, 0xa6, 0x4e, 0xe0, 0x37, 0x4e, 0xaa, 0x6d, 0xb2, 0xca, 0xf5, 0x31, 0x42, 0x23, 0x1b,
+ 0xbd, 0xd6, 0x1d, 0x61, 0x9a, 0x78, 0xbc, 0xcf, 0xac, 0x3e, 0x80, 0x69, 0xe7, 0xf3, 0x70, 0x22,
+ 0x34, 0xbf, 0xc8, 0x1d, 0xbc, 0x30, 0x56, 0x3c, 0xcd, 0x8a, 0x2f, 0x25, 0xb3, 0xee, 0xec, 0x4d,
+ 0x3e, 0x96, 0xb1, 0x87, 0x8f, 0x08, 0x30, 0x0d, 0xa6, 0xfd, 0x61, 0x09, 0x7a, 0xd5, 0xee, 0xa4,
+ 0xd0, 0x0a, 0x47, 0x2d, 0xb4, 0x74, 0x83, 0xc4, 0xf4, 0xf9, 0xbc, 0x2c, 0x96, 0xbf, 0x51, 0x59,
+ 0x1f, 0xa6, 0x74, 0xd4, 0x1f, 0xe6, 0x41, 0x19, 0x3b, 0xda, 0x5b, 0x65, 0x18, 0x9b, 0xd7, 0x69,
+ 0xdb, 0x75, 0xee, 0xb9, 0x09, 0x29, 0x3c, 0x10, 0x9b, 0x90, 0x4b, 0x50, 0xf5, 0x68, 0xc7, 0xb6,
+ 0x0c, 0xdd, 0xe7, 0x9f, 0x5e, 0x1a, 0xfd, 0x50, 0xa6, 0x61, 0x98, 0xdb, 0x67, 0xf3, 0x59, 0x7a,
+ 0x20, 0x37, 0x9f, 0xe5, 0x77, 0x7f, 0xf3, 0xa9, 0x7d, 0xb9, 0x08, 0x5c, 0x51, 0x21, 0x17, 0xa1,
+ 0xcc, 0x16, 0xe1, 0xb4, 0xc9, 0x83, 0x77, 0x1c, 0x9e, 0x43, 0xce, 0x41, 0x31, 0x70, 0xe5, 0xc8,
+ 0x03, 0x99, 0x5f, 0x5c, 0x73, 0xb1, 0x18, 0xb8, 0xe4, 0x0d, 0x00, 0xc3, 0x75, 0x4c, 0x4b, 0xd9,
+ 0xc2, 0xf3, 0x35, 0x6c, 0xc1, 0xf5, 0x6e, 0xe9, 0x9e, 0x39, 0x17, 0x22, 0x8a, 0xed, 0x47, 0xf4,
+ 0x8e, 0x31, 0x6e, 0xe4, 0x05, 0x18, 0x72, 0x9d, 0x85, 0xae, 0x6d, 0x73, 0x81, 0xd6, 0x1a, 0xff,
+ 0x8d, 0xed, 0x09, 0x6f, 0xf0, 0x94, 0x3b, 0x7b, 0x93, 0x67, 0x85, 0x7e, 0xcb, 0xde, 0x5e, 0xf2,
+ 0xac, 0xc0, 0x72, 0x5a, 0xcd, 0xc0, 0xd3, 0x03, 0xda, 0xda, 0x45, 0x59, 0x4c, 0xfb, 0x5a, 0x01,
+ 0xea, 0x0b, 0xd6, 0x6d, 0x6a, 0xbe, 0x64, 0x39, 0xa6, 0x7b, 0x8b, 0x20, 0x0c, 0xd9, 0xd4, 0x69,
+ 0x05, 0x5b, 0xb2, 0xf7, 0x4f, 0xc5, 0xc6, 0x5a, 0x78, 0x84, 0x12, 0xd5, 0xbf, 0x4d, 0x03, 0x9d,
+ 0x8d, 0xbe, 0xf9, 0xae, 0x34, 0xf2, 0x8b, 0x4d, 0x29, 0x47, 0x40, 0x89, 0x44, 0xa6, 0xa1, 0x26,
+ 0xb4, 0x4f, 0xcb, 0x69, 0x71, 0x19, 0x56, 0xa3, 0x49, 0xaf, 0xa9, 0x32, 0x30, 0xa2, 0xd1, 0x76,
+ 0xe1, 0x64, 0x8f, 0x18, 0x88, 0x09, 0xe5, 0x40, 0x6f, 0xa9, 0xf9, 0x75, 0x61, 0x60, 0x01, 0xaf,
+ 0xe9, 0xad, 0x98, 0x70, 0xf9, 0x1a, 0xbf, 0xa6, 0xb3, 0x35, 0x9e, 0xa1, 0x6b, 0x3f, 0x2b, 0x40,
+ 0x75, 0xa1, 0xeb, 0x18, 0x7c, 0x6f, 0x74, 0x6f, 0x53, 0x98, 0x52, 0x18, 0x8a, 0x99, 0x0a, 0x43,
+ 0x17, 0x86, 0xb6, 0x6f, 0x85, 0x0a, 0x45, 0x7d, 0x66, 0x65, 0xf0, 0x5e, 0x21, 0xab, 0x34, 0x75,
+ 0x8d, 0xe3, 0x89, 0x93, 0x9a, 0x31, 0x59, 0xa1, 0xa1, 0x6b, 0x2f, 0x71, 0xa6, 0x92, 0xd9, 0xb9,
+ 0x8f, 0x41, 0x3d, 0x46, 0x76, 0x28, 0xa3, 0xed, 0xef, 0x96, 0x61, 0x68, 0xb1, 0xd9, 0x9c, 0x5d,
+ 0x5d, 0x22, 0xcf, 0x40, 0x5d, 0x1a, 0xf1, 0xaf, 0x47, 0x32, 0x08, 0xcf, 0x70, 0x9a, 0x51, 0x16,
+ 0xc6, 0xe9, 0x98, 0x3a, 0xe6, 0x51, 0xdd, 0x6e, 0xcb, 0xc1, 0x12, 0xaa, 0x63, 0xc8, 0x12, 0x51,
+ 0xe4, 0x11, 0x1d, 0xc6, 0xd8, 0x0e, 0x8f, 0x89, 0x50, 0xec, 0xde, 0xe4, 0xb0, 0x39, 0xe0, 0xfe,
+ 0x8e, 0x2b, 0x89, 0xeb, 0x09, 0x00, 0x4c, 0x01, 0x92, 0xe7, 0xa1, 0xaa, 0x77, 0x83, 0x2d, 0xae,
+ 0x40, 0x8b, 0xb1, 0x71, 0x9e, 0x9f, 0x71, 0xc8, 0xb4, 0x3b, 0x7b, 0x93, 0x23, 0xd7, 0xb0, 0xf1,
+ 0x8c, 0x7a, 0xc7, 0x90, 0x9a, 0x55, 0x4e, 0xed, 0x18, 0x65, 0xe5, 0x2a, 0x87, 0xae, 0xdc, 0x6a,
+ 0x02, 0x00, 0x53, 0x80, 0xe4, 0x15, 0x18, 0xd9, 0xa6, 0xbb, 0x81, 0xbe, 0x21, 0x19, 0x0c, 0x1d,
+ 0x86, 0xc1, 0x38, 0x53, 0xe1, 0xae, 0xc5, 0x8a, 0x63, 0x02, 0x8c, 0xf8, 0x70, 0x7a, 0x9b, 0x7a,
+ 0x1b, 0xd4, 0x73, 0xe5, 0xee, 0x53, 0x32, 0x19, 0x3e, 0x0c, 0x93, 0x89, 0xfd, 0xbd, 0xc9, 0xd3,
+ 0xd7, 0x32, 0x60, 0x30, 0x13, 0x5c, 0xfb, 0x69, 0x11, 0x4e, 0x2c, 0x8a, 0x53, 0x54, 0xd7, 0x13,
+ 0x8b, 0x30, 0x39, 0x0b, 0x25, 0xaf, 0xd3, 0xe5, 0x3d, 0xa7, 0x24, 0xec, 0xa4, 0xb8, 0xba, 0x8e,
+ 0x2c, 0x8d, 0xbc, 0x0c, 0x55, 0x53, 0x4e, 0x19, 0x72, 0xf3, 0x7b, 0xd8, 0x89, 0x86, 0x2f, 0x82,
+ 0xea, 0x0d, 0x43, 0x34, 0xa6, 0xe9, 0xb7, 0xfd, 0x56, 0xd3, 0x7a, 0x83, 0xca, 0xfd, 0x20, 0xd7,
+ 0xf4, 0x57, 0x44, 0x12, 0xaa, 0x3c, 0xb6, 0xaa, 0x6e, 0xd3, 0x5d, 0xb1, 0x1b, 0x2a, 0x47, 0xab,
+ 0xea, 0x35, 0x99, 0x86, 0x61, 0x2e, 0x99, 0x54, 0x83, 0x85, 0xf5, 0x82, 0xb2, 0xd8, 0xc9, 0xdf,
+ 0x64, 0x09, 0x72, 0xdc, 0xb0, 0x29, 0xf3, 0x35, 0x2b, 0x08, 0xa8, 0x27, 0x3f, 0xe3, 0x40, 0x53,
+ 0xe6, 0x55, 0x8e, 0x80, 0x12, 0x89, 0x7c, 0x18, 0x6a, 0x1c, 0xbc, 0x61, 0xbb, 0x1b, 0xfc, 0xc3,
+ 0xd5, 0xc4, 0x9e, 0xfe, 0xa6, 0x4a, 0xc4, 0x28, 0x5f, 0xfb, 0x79, 0x11, 0xce, 0x2c, 0xd2, 0x40,
+ 0x68, 0x35, 0xf3, 0xb4, 0x63, 0xbb, 0xbb, 0x4c, 0xb5, 0x44, 0xfa, 0x3a, 0x79, 0x11, 0xc0, 0xf2,
+ 0x37, 0x9a, 0x3b, 0x06, 0x1f, 0x07, 0x62, 0x0c, 0x5f, 0x94, 0x43, 0x12, 0x96, 0x9a, 0x0d, 0x99,
+ 0x73, 0x27, 0xf1, 0x86, 0xb1, 0x32, 0xd1, 0xf6, 0xaa, 0x78, 0x97, 0xed, 0x55, 0x13, 0xa0, 0x13,
+ 0x29, 0xa8, 0x25, 0x4e, 0xf9, 0x3f, 0x15, 0x9b, 0xc3, 0xe8, 0xa6, 0x31, 0x98, 0x3c, 0x2a, 0xa3,
+ 0x03, 0xe3, 0x26, 0xdd, 0xd4, 0xbb, 0x76, 0x10, 0x2a, 0xd5, 0x72, 0x10, 0x1f, 0x5c, 0x2f, 0x0f,
+ 0x4f, 0x78, 0xe7, 0x53, 0x48, 0xd8, 0x83, 0xad, 0xfd, 0x5e, 0x09, 0xce, 0x2d, 0xd2, 0x20, 0xb4,
+ 0xb8, 0xc8, 0xd9, 0xb1, 0xd9, 0xa1, 0x06, 0xfb, 0x0a, 0x6f, 0x16, 0x60, 0xc8, 0xd6, 0x37, 0xa8,
+ 0xcd, 0x56, 0x2f, 0xd6, 0x9a, 0x57, 0x07, 0x5e, 0x08, 0xfa, 0x73, 0x99, 0x5a, 0xe6, 0x1c, 0x52,
+ 0x4b, 0x83, 0x48, 0x44, 0xc9, 0x9e, 0x4d, 0xea, 0x86, 0xdd, 0xf5, 0x03, 0xea, 0xad, 0xba, 0x5e,
+ 0x20, 0xf5, 0xc9, 0x70, 0x52, 0x9f, 0x8b, 0xb2, 0x30, 0x4e, 0x47, 0x66, 0x00, 0x0c, 0xdb, 0xa2,
+ 0x4e, 0xc0, 0x4b, 0x89, 0x71, 0x45, 0xd4, 0xf7, 0x9d, 0x0b, 0x73, 0x30, 0x46, 0xc5, 0x58, 0xb5,
+ 0x5d, 0xc7, 0x0a, 0x5c, 0xc1, 0xaa, 0x9c, 0x64, 0xb5, 0x12, 0x65, 0x61, 0x9c, 0x8e, 0x17, 0xa3,
+ 0x81, 0x67, 0x19, 0x3e, 0x2f, 0x56, 0x49, 0x15, 0x8b, 0xb2, 0x30, 0x4e, 0xc7, 0xd6, 0xbc, 0x58,
+ 0xfb, 0x0f, 0xb5, 0xe6, 0x7d, 0xab, 0x06, 0x17, 0x12, 0x62, 0x0d, 0xf4, 0x80, 0x6e, 0x76, 0xed,
+ 0x26, 0x0d, 0xd4, 0x07, 0x1c, 0x70, 0x2d, 0xfc, 0x85, 0xe8, 0xbb, 0x0b, 0xdf, 0x0d, 0xe3, 0x68,
+ 0xbe, 0x7b, 0x4f, 0x05, 0x0f, 0xf4, 0xed, 0xa7, 0xa1, 0xe6, 0xe8, 0x81, 0xcf, 0x07, 0xae, 0x1c,
+ 0xa3, 0xa1, 0x1a, 0x76, 0x5d, 0x65, 0x60, 0x44, 0x43, 0x56, 0xe1, 0xb4, 0x14, 0xf1, 0xe5, 0xdb,
+ 0x1d, 0xd7, 0x0b, 0xa8, 0x27, 0xca, 0xca, 0xe5, 0x54, 0x96, 0x3d, 0xbd, 0x92, 0x41, 0x83, 0x99,
+ 0x25, 0xc9, 0x0a, 0x9c, 0x32, 0xc4, 0x79, 0x36, 0xb5, 0x5d, 0xdd, 0x54, 0x80, 0xc2, 0xc0, 0x15,
+ 0x6e, 0x8d, 0xe6, 0x7a, 0x49, 0x30, 0xab, 0x5c, 0xba, 0x37, 0x0f, 0x0d, 0xd4, 0x9b, 0x87, 0x07,
+ 0xe9, 0xcd, 0xd5, 0xc1, 0x7a, 0x73, 0xed, 0x60, 0xbd, 0x99, 0x49, 0x9e, 0xf5, 0x23, 0xea, 0x31,
+ 0xf5, 0x44, 0xac, 0xb0, 0x31, 0x77, 0x89, 0x50, 0xf2, 0xcd, 0x0c, 0x1a, 0xcc, 0x2c, 0x49, 0x36,
+ 0xe0, 0x9c, 0x48, 0xbf, 0xec, 0x18, 0xde, 0x6e, 0x87, 0x2d, 0x3c, 0x31, 0xdc, 0x7a, 0xc2, 0xc2,
+ 0x78, 0xae, 0xd9, 0x97, 0x12, 0xef, 0x82, 0x42, 0xfe, 0x17, 0x8c, 0x8a, 0xaf, 0xb4, 0xa2, 0x77,
+ 0x38, 0xac, 0x70, 0x9e, 0x78, 0x58, 0xc2, 0x8e, 0xce, 0xc5, 0x33, 0x31, 0x49, 0x4b, 0x66, 0xe1,
+ 0x44, 0x67, 0xc7, 0x60, 0x8f, 0x4b, 0x9b, 0xd7, 0x29, 0x35, 0xa9, 0xc9, 0x4f, 0x6b, 0x6a, 0x8d,
+ 0x47, 0x94, 0xa1, 0x63, 0x35, 0x99, 0x8d, 0x69, 0x7a, 0xf2, 0x3c, 0x8c, 0xf8, 0x81, 0xee, 0x05,
+ 0xd2, 0xac, 0x37, 0x31, 0x26, 0x9c, 0x4b, 0x94, 0xd5, 0xab, 0x19, 0xcb, 0xc3, 0x04, 0x65, 0xe6,
+ 0x7a, 0x71, 0xe2, 0xf8, 0xd6, 0x8b, 0x3c, 0xb3, 0xd5, 0x9f, 0x15, 0xe1, 0xe2, 0x22, 0x0d, 0x56,
+ 0x5c, 0x47, 0x1a, 0x45, 0xb3, 0x96, 0xfd, 0x03, 0xd9, 0x44, 0x93, 0x8b, 0x76, 0xf1, 0x48, 0x17,
+ 0xed, 0xd2, 0x11, 0x2d, 0xda, 0xe5, 0x63, 0x5c, 0xb4, 0xff, 0xa0, 0x08, 0x8f, 0x24, 0x24, 0xb9,
+ 0xea, 0x9a, 0x6a, 0xc2, 0x7f, 0x5f, 0x80, 0x07, 0x10, 0xe0, 0x1d, 0xa1, 0x77, 0xf2, 0x63, 0xad,
+ 0x94, 0xc6, 0xf3, 0xd5, 0xb4, 0xc6, 0xf3, 0x4a, 0x9e, 0x95, 0x2f, 0x83, 0xc3, 0x81, 0x56, 0xbc,
+ 0xab, 0x40, 0x3c, 0x79, 0x08, 0x27, 0x4c, 0x3f, 0x31, 0xa5, 0x27, 0xf4, 0x5e, 0xc3, 0x1e, 0x0a,
+ 0xcc, 0x28, 0x45, 0x9a, 0xf0, 0xb0, 0x4f, 0x9d, 0xc0, 0x72, 0xa8, 0x9d, 0x84, 0x13, 0xda, 0xd0,
+ 0x63, 0x12, 0xee, 0xe1, 0x66, 0x16, 0x11, 0x66, 0x97, 0xcd, 0x33, 0x0f, 0xfc, 0x25, 0x70, 0x95,
+ 0x53, 0x88, 0xe6, 0xc8, 0x34, 0x96, 0x37, 0xd3, 0x1a, 0xcb, 0xab, 0xf9, 0xbf, 0xdb, 0x60, 0xda,
+ 0xca, 0x0c, 0x00, 0xff, 0x0a, 0x71, 0x75, 0x25, 0x5c, 0xa4, 0x31, 0xcc, 0xc1, 0x18, 0x15, 0x5b,
+ 0x80, 0x94, 0x9c, 0xe3, 0x9a, 0x4a, 0xb8, 0x00, 0x35, 0xe3, 0x99, 0x98, 0xa4, 0xed, 0xab, 0xed,
+ 0x54, 0x06, 0xd6, 0x76, 0xae, 0x02, 0x49, 0x18, 0x1e, 0x05, 0xde, 0x50, 0xd2, 0x79, 0x72, 0xa9,
+ 0x87, 0x02, 0x33, 0x4a, 0xf5, 0xe9, 0xca, 0xc3, 0x47, 0xdb, 0x95, 0xab, 0x83, 0x77, 0x65, 0xf2,
+ 0x2a, 0x9c, 0xe5, 0xac, 0xa4, 0x7c, 0x92, 0xc0, 0x42, 0xef, 0xf9, 0x80, 0x04, 0x3e, 0x8b, 0xfd,
+ 0x08, 0xb1, 0x3f, 0x06, 0xfb, 0x3e, 0x86, 0x47, 0x4d, 0xc6, 0x5c, 0xb7, 0xfb, 0xeb, 0x44, 0x73,
+ 0x19, 0x34, 0x98, 0x59, 0x92, 0x75, 0xb1, 0x80, 0x75, 0x43, 0x7d, 0xc3, 0xa6, 0xa6, 0x74, 0x1e,
+ 0x0d, 0xbb, 0xd8, 0xda, 0x72, 0x53, 0xe6, 0x60, 0x8c, 0x2a, 0x4b, 0x4d, 0x19, 0x39, 0xa4, 0x9a,
+ 0xb2, 0xc8, 0xad, 0xf4, 0x9b, 0x09, 0x6d, 0x48, 0xea, 0x3a, 0xa1, 0x3b, 0xf0, 0x5c, 0x9a, 0x00,
+ 0x7b, 0xcb, 0x70, 0x2d, 0xd1, 0xf0, 0xac, 0x4e, 0xe0, 0x27, 0xb1, 0xc6, 0x52, 0x5a, 0x62, 0x06,
+ 0x0d, 0x66, 0x96, 0x64, 0xfa, 0xf9, 0x16, 0xd5, 0xed, 0x60, 0x2b, 0x09, 0x78, 0x22, 0xa9, 0x9f,
+ 0x5f, 0xe9, 0x25, 0xc1, 0xac, 0x72, 0x99, 0x0b, 0xd2, 0xf8, 0x83, 0xa9, 0x56, 0x7d, 0xa5, 0x04,
+ 0x67, 0x17, 0x69, 0x10, 0xfa, 0xd5, 0xbc, 0x6f, 0x46, 0x79, 0x17, 0xcc, 0x28, 0xdf, 0xac, 0xc0,
+ 0xa9, 0x45, 0x1a, 0xf4, 0x68, 0x63, 0xff, 0x45, 0xc5, 0xbf, 0x02, 0xa7, 0x22, 0x57, 0xae, 0x66,
+ 0xe0, 0x7a, 0x62, 0x2d, 0x4f, 0xed, 0x96, 0x9b, 0xbd, 0x24, 0x98, 0x55, 0x8e, 0x7c, 0x06, 0x1e,
+ 0xe1, 0x4b, 0xbd, 0xd3, 0x12, 0xf6, 0x59, 0x61, 0x4c, 0x88, 0x5d, 0x46, 0x98, 0x94, 0x90, 0x8f,
+ 0x34, 0xb3, 0xc9, 0xb0, 0x5f, 0x79, 0xf2, 0x45, 0x18, 0xe9, 0x58, 0x1d, 0x6a, 0x5b, 0x0e, 0xd7,
+ 0xcf, 0x72, 0xbb, 0x84, 0xac, 0xc6, 0xc0, 0xa2, 0x0d, 0x5c, 0x3c, 0x15, 0x13, 0x0c, 0x33, 0x7b,
+ 0x6a, 0xf5, 0x18, 0x7b, 0xea, 0xbf, 0x15, 0x61, 0x78, 0xd1, 0x73, 0xbb, 0x9d, 0xc6, 0x2e, 0x69,
+ 0xc1, 0xd0, 0x2d, 0x7e, 0x78, 0x26, 0x8f, 0xa6, 0x06, 0x77, 0x87, 0x16, 0x67, 0x70, 0x91, 0x4a,
+ 0x24, 0xde, 0x51, 0xc2, 0xb3, 0x4e, 0xbc, 0x4d, 0x77, 0xa9, 0x29, 0xcf, 0xd0, 0xc2, 0x4e, 0x7c,
+ 0x8d, 0x25, 0xa2, 0xc8, 0x23, 0x6d, 0x38, 0xa1, 0xdb, 0xb6, 0x7b, 0x8b, 0x9a, 0xcb, 0x7a, 0x40,
+ 0x1d, 0xea, 0xab, 0x23, 0xc9, 0xc3, 0x9a, 0xa5, 0xf9, 0xb9, 0xfe, 0x6c, 0x12, 0x0a, 0xd3, 0xd8,
+ 0xe4, 0x35, 0x18, 0xf6, 0x03, 0xd7, 0x53, 0xca, 0x56, 0x7d, 0x66, 0x6e, 0xf0, 0x8f, 0xde, 0xf8,
+ 0x74, 0x53, 0x40, 0x09, 0x9b, 0xbd, 0x7c, 0x41, 0xc5, 0x40, 0xfb, 0x46, 0x01, 0xe0, 0xca, 0xda,
+ 0xda, 0xaa, 0x3c, 0x5e, 0x30, 0xa1, 0xac, 0x77, 0xc3, 0x83, 0xca, 0xc1, 0x0f, 0x04, 0x13, 0xfe,
+ 0x90, 0xf2, 0x0c, 0xaf, 0x1b, 0x6c, 0x21, 0x47, 0x27, 0x1f, 0x82, 0x61, 0xa9, 0x20, 0x4b, 0xb1,
+ 0x87, 0xae, 0x05, 0x52, 0x89, 0x46, 0x95, 0xaf, 0xfd, 0x4e, 0x11, 0x60, 0xc9, 0xb4, 0x69, 0x53,
+ 0x79, 0xb0, 0xd7, 0x82, 0x2d, 0x8f, 0xfa, 0x5b, 0xae, 0x6d, 0x0e, 0x78, 0x9a, 0xca, 0x6d, 0xfe,
+ 0x6b, 0x0a, 0x04, 0x23, 0x3c, 0x62, 0xc2, 0x88, 0x1f, 0xd0, 0xce, 0x92, 0x13, 0x50, 0x6f, 0x47,
+ 0xb7, 0x07, 0x3c, 0x44, 0x19, 0x17, 0x76, 0x91, 0x08, 0x07, 0x13, 0xa8, 0x44, 0x87, 0xba, 0xe5,
+ 0x18, 0x62, 0x80, 0x34, 0x76, 0x07, 0xec, 0x48, 0x27, 0xd8, 0x8e, 0x63, 0x29, 0x82, 0xc1, 0x38,
+ 0xa6, 0xf6, 0xc3, 0x22, 0x9c, 0xe1, 0xfc, 0x58, 0x35, 0x12, 0xfe, 0x98, 0xe4, 0xff, 0xf4, 0xdc,
+ 0xb6, 0xfb, 0x1f, 0x07, 0x63, 0x2d, 0x2e, 0x6b, 0xad, 0xd0, 0x40, 0x8f, 0xf4, 0xb9, 0x28, 0x2d,
+ 0x76, 0xc5, 0xae, 0x0b, 0x65, 0x9f, 0xcd, 0x57, 0x42, 0x7a, 0xcd, 0x81, 0xbb, 0x50, 0x76, 0x03,
+ 0xf8, 0xec, 0x15, 0x9e, 0x1a, 0xf3, 0x59, 0x8b, 0xb3, 0x23, 0xff, 0x0f, 0x86, 0xfc, 0x40, 0x0f,
+ 0xba, 0x6a, 0x68, 0xae, 0x1f, 0x35, 0x63, 0x0e, 0x1e, 0xcd, 0x23, 0xe2, 0x1d, 0x25, 0x53, 0xed,
+ 0x87, 0x05, 0x38, 0x97, 0x5d, 0x70, 0xd9, 0xf2, 0x03, 0xf2, 0xbf, 0x7b, 0xc4, 0x7e, 0xc0, 0x2f,
+ 0xce, 0x4a, 0x73, 0xa1, 0x87, 0x0e, 0xd9, 0x2a, 0x25, 0x26, 0xf2, 0x00, 0x2a, 0x56, 0x40, 0xdb,
+ 0x6a, 0x7f, 0x79, 0xe3, 0x88, 0x9b, 0x1e, 0x5b, 0xda, 0x19, 0x17, 0x14, 0xcc, 0xb4, 0xb7, 0x8a,
+ 0xfd, 0x9a, 0xcc, 0x97, 0x0f, 0x3b, 0xe9, 0xf3, 0x7b, 0x2d, 0x9f, 0xcf, 0x6f, 0xb2, 0x42, 0xbd,
+ 0xae, 0xbf, 0xff, 0xb7, 0xd7, 0xf5, 0xf7, 0x46, 0x7e, 0xd7, 0xdf, 0x94, 0x18, 0xfa, 0x7a, 0x00,
+ 0xbf, 0x53, 0x82, 0xf3, 0x77, 0xeb, 0x36, 0x6c, 0x3d, 0x93, 0xbd, 0x33, 0xef, 0x7a, 0x76, 0xf7,
+ 0x7e, 0x48, 0x66, 0xa0, 0xd2, 0xd9, 0xd2, 0x7d, 0xa5, 0x94, 0xa9, 0x0d, 0x4b, 0x65, 0x95, 0x25,
+ 0xde, 0x61, 0x93, 0x06, 0x57, 0xe6, 0xf8, 0x2b, 0x0a, 0x52, 0x36, 0x1d, 0xb7, 0xa9, 0xef, 0x47,
+ 0x36, 0x81, 0x70, 0x3a, 0x5e, 0x11, 0xc9, 0xa8, 0xf2, 0x49, 0x00, 0x43, 0xc2, 0xc4, 0x2c, 0x57,
+ 0xa6, 0xc1, 0x1d, 0xb9, 0x32, 0xdc, 0xc4, 0xa3, 0x46, 0xc9, 0xd3, 0x0a, 0xc9, 0x8b, 0x4c, 0x41,
+ 0x39, 0x88, 0x9c, 0x76, 0xd5, 0xd6, 0xbc, 0x9c, 0xa1, 0x9f, 0x72, 0x3a, 0xb6, 0xb1, 0x77, 0x37,
+ 0xb8, 0x51, 0xdd, 0x94, 0xe7, 0xe7, 0x96, 0xeb, 0x70, 0x85, 0xac, 0x14, 0x6d, 0xec, 0x6f, 0xf4,
+ 0x50, 0x60, 0x46, 0x29, 0xed, 0x6f, 0xaa, 0x70, 0x26, 0xbb, 0x3f, 0x30, 0xb9, 0xed, 0x50, 0xcf,
+ 0x67, 0xd8, 0x85, 0xa4, 0xdc, 0x6e, 0x8a, 0x64, 0x54, 0xf9, 0xef, 0x69, 0x87, 0xb3, 0x6f, 0x16,
+ 0xe0, 0xac, 0x27, 0xcf, 0x88, 0xee, 0x87, 0xd3, 0xd9, 0x63, 0xc2, 0x9c, 0xd1, 0x87, 0x21, 0xf6,
+ 0xaf, 0x0b, 0xf9, 0xad, 0x02, 0x4c, 0xb4, 0x53, 0x76, 0x8e, 0x63, 0xbc, 0x30, 0xc6, 0xbd, 0xe2,
+ 0x57, 0xfa, 0xf0, 0xc3, 0xbe, 0x35, 0x21, 0x5f, 0x84, 0x7a, 0x87, 0xf5, 0x0b, 0x3f, 0xa0, 0x8e,
+ 0xa1, 0xee, 0x8c, 0x0d, 0x3e, 0x92, 0x56, 0x23, 0x2c, 0xe5, 0x8a, 0x26, 0xf4, 0x83, 0x58, 0x06,
+ 0xc6, 0x39, 0x3e, 0xe0, 0x37, 0xc4, 0x2e, 0x41, 0xd5, 0xa7, 0x41, 0x60, 0x39, 0x2d, 0xb1, 0xdf,
+ 0xa8, 0x89, 0xb1, 0xd2, 0x94, 0x69, 0x18, 0xe6, 0x92, 0x0f, 0x43, 0x8d, 0x1f, 0x39, 0xcd, 0x7a,
+ 0x2d, 0x7f, 0xa2, 0xc6, 0xdd, 0xc5, 0x46, 0x85, 0x03, 0x9c, 0x4c, 0xc4, 0x28, 0x9f, 0x3c, 0x0d,
+ 0x23, 0x1b, 0x7c, 0xf8, 0xca, 0x4b, 0xc3, 0xc2, 0xc6, 0xc5, 0xb5, 0xb5, 0x46, 0x2c, 0x1d, 0x13,
+ 0x54, 0x64, 0x06, 0x80, 0x86, 0xe7, 0x72, 0x69, 0x7b, 0x56, 0x74, 0x62, 0x87, 0x31, 0x2a, 0xf2,
+ 0x18, 0x94, 0x02, 0xdb, 0xe7, 0x36, 0xac, 0x6a, 0xb4, 0x05, 0x5d, 0x5b, 0x6e, 0x22, 0x4b, 0xd7,
+ 0x7e, 0x5e, 0x80, 0x13, 0xa9, 0xcb, 0x25, 0xac, 0x48, 0xd7, 0xb3, 0xe5, 0x34, 0x12, 0x16, 0x59,
+ 0xc7, 0x65, 0x64, 0xe9, 0xe4, 0x55, 0xa9, 0x96, 0x17, 0x73, 0xc6, 0x47, 0xb8, 0xae, 0x07, 0x3e,
+ 0xd3, 0xc3, 0x7b, 0x34, 0x72, 0x7e, 0xcc, 0x17, 0xd5, 0x47, 0xae, 0x03, 0xb1, 0x63, 0xbe, 0x28,
+ 0x0f, 0x13, 0x94, 0x29, 0x83, 0x5f, 0xf9, 0x20, 0x06, 0x3f, 0xed, 0x6b, 0xc5, 0x98, 0x04, 0xa4,
+ 0x66, 0x7f, 0x0f, 0x09, 0x3c, 0xc9, 0x16, 0xd0, 0x70, 0x71, 0xaf, 0xc5, 0xd7, 0x3f, 0xbe, 0x18,
+ 0xcb, 0x5c, 0xf2, 0x92, 0x90, 0x7d, 0x29, 0xe7, 0x2d, 0xd4, 0xb5, 0xe5, 0xa6, 0xf0, 0xae, 0x52,
+ 0x5f, 0x2d, 0xfc, 0x04, 0xe5, 0x63, 0xfa, 0x04, 0xda, 0x5f, 0x94, 0xa0, 0x7e, 0xd5, 0xdd, 0x78,
+ 0x8f, 0x78, 0x50, 0x67, 0x2f, 0x53, 0xc5, 0x77, 0x71, 0x99, 0x5a, 0x87, 0x47, 0x82, 0xc0, 0x6e,
+ 0x52, 0xc3, 0x75, 0x4c, 0x7f, 0x76, 0x33, 0xa0, 0xde, 0x82, 0xe5, 0x58, 0xfe, 0x16, 0x35, 0xe5,
+ 0x71, 0xd2, 0xa3, 0xfb, 0x7b, 0x93, 0x8f, 0xac, 0xad, 0x2d, 0x67, 0x91, 0x60, 0xbf, 0xb2, 0x7c,
+ 0xda, 0xd0, 0x8d, 0x6d, 0x77, 0x73, 0x93, 0xdf, 0x94, 0x91, 0x3e, 0x37, 0x62, 0xda, 0x88, 0xa5,
+ 0x63, 0x82, 0x4a, 0x7b, 0xbb, 0x08, 0xb5, 0xf0, 0xe6, 0x3b, 0x79, 0x02, 0x86, 0x37, 0x3c, 0x77,
+ 0x9b, 0x7a, 0xe2, 0xe4, 0x4e, 0xde, 0x94, 0x69, 0x88, 0x24, 0x54, 0x79, 0xe4, 0x71, 0xa8, 0x04,
+ 0x6e, 0xc7, 0x32, 0xd2, 0x06, 0xb5, 0x35, 0x96, 0x88, 0x22, 0xef, 0xf8, 0x3a, 0xf8, 0x93, 0x09,
+ 0xd5, 0xae, 0xd6, 0x57, 0x19, 0x7b, 0x05, 0xca, 0xbe, 0xee, 0xdb, 0x72, 0x3d, 0xcd, 0x71, 0x89,
+ 0x7c, 0xb6, 0xb9, 0x2c, 0x2f, 0x91, 0xcf, 0x36, 0x97, 0x91, 0x83, 0x6a, 0x3f, 0x29, 0x42, 0x5d,
+ 0xc8, 0x4d, 0xcc, 0x0a, 0x47, 0x29, 0xb9, 0x17, 0xb8, 0x2b, 0x85, 0xdf, 0x6d, 0x53, 0x8f, 0x9b,
+ 0x99, 0xe4, 0x24, 0x17, 0x3f, 0x1f, 0x88, 0x32, 0x43, 0x77, 0x8a, 0x28, 0x49, 0x89, 0xbe, 0x7c,
+ 0x8c, 0xa2, 0xaf, 0x1c, 0x48, 0xf4, 0x43, 0xc7, 0x21, 0xfa, 0x37, 0x8b, 0x50, 0x5b, 0xb6, 0x36,
+ 0xa9, 0xb1, 0x6b, 0xd8, 0xfc, 0x4e, 0xa0, 0x49, 0x6d, 0x1a, 0xd0, 0x45, 0x4f, 0x37, 0xe8, 0x2a,
+ 0xf5, 0x2c, 0x1e, 0x19, 0x86, 0x8d, 0x0f, 0x3e, 0x03, 0xc9, 0x3b, 0x81, 0xf3, 0x7d, 0x68, 0xb0,
+ 0x6f, 0x69, 0xb2, 0x04, 0x23, 0x26, 0xf5, 0x2d, 0x8f, 0x9a, 0xab, 0xb1, 0x8d, 0xca, 0x13, 0x6a,
+ 0xa9, 0x99, 0x8f, 0xe5, 0xdd, 0xd9, 0x9b, 0x1c, 0x55, 0x06, 0x4a, 0xb1, 0x63, 0x49, 0x14, 0x65,
+ 0x43, 0xbe, 0xa3, 0x77, 0xfd, 0xac, 0x3a, 0xc6, 0x86, 0xfc, 0x6a, 0x36, 0x09, 0xf6, 0x2b, 0xab,
+ 0x55, 0xa0, 0xb4, 0xec, 0xb6, 0xb4, 0xb7, 0x4a, 0x10, 0x86, 0x10, 0x22, 0xff, 0xbf, 0x00, 0x75,
+ 0xdd, 0x71, 0xdc, 0x40, 0x86, 0xe7, 0x11, 0x27, 0xf0, 0x98, 0x3b, 0x52, 0xd1, 0xd4, 0x6c, 0x04,
+ 0x2a, 0x0e, 0x6f, 0xc3, 0x03, 0xe5, 0x58, 0x0e, 0xc6, 0x79, 0x93, 0x6e, 0xea, 0x3c, 0x79, 0x25,
+ 0x7f, 0x2d, 0x0e, 0x70, 0x7a, 0x7c, 0xee, 0x53, 0x30, 0x9e, 0xae, 0xec, 0x61, 0x8e, 0x83, 0x72,
+ 0x1d, 0xcc, 0x17, 0x01, 0x22, 0x9f, 0x92, 0xfb, 0x60, 0xc4, 0xb2, 0x12, 0x46, 0xac, 0xc5, 0xc1,
+ 0x05, 0x1c, 0x56, 0xba, 0xaf, 0xe1, 0xea, 0xf5, 0x94, 0xe1, 0x6a, 0xe9, 0x28, 0x98, 0xdd, 0xdd,
+ 0x58, 0xf5, 0xdb, 0x05, 0x18, 0x8f, 0x88, 0xe5, 0x0d, 0xd9, 0xe7, 0x60, 0xd4, 0xa3, 0xba, 0xd9,
+ 0xd0, 0x03, 0x63, 0x8b, 0xbb, 0x7a, 0x17, 0xb8, 0x6f, 0xf6, 0xc9, 0xfd, 0xbd, 0xc9, 0x51, 0x8c,
+ 0x67, 0x60, 0x92, 0x8e, 0xe8, 0x50, 0x67, 0x09, 0x6b, 0x56, 0x9b, 0xba, 0xdd, 0x60, 0x40, 0xab,
+ 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c, 0xed, 0x9d, 0x02, 0x8c, 0xc5, 0x2b, 0x7c, 0xec,
+ 0x16, 0xb5, 0xad, 0xa4, 0x45, 0x6d, 0xee, 0x08, 0xbe, 0x49, 0x1f, 0x2b, 0xda, 0x4f, 0xab, 0xf1,
+ 0xa6, 0x71, 0xcb, 0x59, 0xdc, 0x58, 0x50, 0xb8, 0xab, 0xb1, 0xe0, 0xbd, 0x1f, 0x35, 0xa6, 0x9f,
+ 0x96, 0x5b, 0x7e, 0x80, 0xb5, 0xdc, 0x77, 0x33, 0xf4, 0x4c, 0x2c, 0x7c, 0xca, 0x50, 0x8e, 0xf0,
+ 0x29, 0xed, 0x30, 0x7c, 0xca, 0xf0, 0x91, 0x4d, 0x3a, 0x07, 0x09, 0xa1, 0x52, 0xbd, 0xaf, 0x21,
+ 0x54, 0x6a, 0xc7, 0x15, 0x42, 0x05, 0xf2, 0x86, 0x50, 0xf9, 0x6a, 0x01, 0xc6, 0xcc, 0xc4, 0x8d,
+ 0x59, 0x6e, 0x5b, 0xc8, 0xb3, 0xd4, 0x24, 0x2f, 0xe0, 0x8a, 0x2b, 0x53, 0xc9, 0x34, 0x4c, 0xb1,
+ 0xd4, 0x7e, 0x5c, 0x8e, 0xaf, 0x03, 0xf7, 0xdb, 0x54, 0xfd, 0x6c, 0xd2, 0x54, 0x7d, 0x31, 0x6d,
+ 0xaa, 0x3e, 0x11, 0xf3, 0x22, 0x8d, 0x9b, 0xab, 0x3f, 0x12, 0x9b, 0x1e, 0xd9, 0x9c, 0x34, 0x1a,
+ 0x49, 0x3a, 0x63, 0x8a, 0xfc, 0x08, 0x54, 0x7d, 0x15, 0xec, 0x51, 0x6c, 0x6c, 0xa2, 0xef, 0xa2,
+ 0x02, 0x31, 0x86, 0x14, 0x4c, 0x13, 0xf7, 0xa8, 0xee, 0xbb, 0x4e, 0x5a, 0x13, 0x47, 0x9e, 0x8a,
+ 0x32, 0x37, 0x6e, 0x32, 0x1f, 0xba, 0x87, 0xc9, 0x5c, 0x87, 0xba, 0xad, 0xfb, 0xc1, 0x7a, 0xc7,
+ 0xd4, 0x03, 0x6a, 0xca, 0xf1, 0xf6, 0xdf, 0x0f, 0xb6, 0x56, 0xb1, 0xf5, 0x2f, 0x52, 0x08, 0x97,
+ 0x23, 0x18, 0x8c, 0x63, 0x12, 0x13, 0x46, 0xd8, 0x2b, 0x1f, 0x0d, 0xe6, 0xac, 0x0a, 0x01, 0x70,
+ 0x18, 0x1e, 0xa1, 0xa5, 0x67, 0x39, 0x86, 0x83, 0x09, 0xd4, 0x3e, 0x56, 0xf5, 0xda, 0x40, 0x56,
+ 0xf5, 0xaf, 0xd6, 0xa0, 0x7e, 0x5d, 0x0f, 0xac, 0x1d, 0xca, 0x4f, 0x71, 0x8e, 0xc7, 0x94, 0xfe,
+ 0x6b, 0x05, 0x38, 0x93, 0x74, 0xd5, 0x3b, 0x46, 0x7b, 0x3a, 0x0f, 0xfc, 0x81, 0x99, 0xdc, 0xb0,
+ 0x4f, 0x2d, 0xb8, 0x65, 0xbd, 0xc7, 0xf3, 0xef, 0xb8, 0x2d, 0xeb, 0xcd, 0x7e, 0x0c, 0xb1, 0x7f,
+ 0x5d, 0xde, 0x2b, 0x96, 0xf5, 0x07, 0x3b, 0x30, 0x5b, 0xca, 0xee, 0x3f, 0xfc, 0xc0, 0xd8, 0xfd,
+ 0xab, 0x0f, 0x84, 0xb2, 0xd5, 0x89, 0xd9, 0xfd, 0x6b, 0x39, 0xfd, 0x4f, 0xa4, 0x77, 0xbb, 0x40,
+ 0xeb, 0x77, 0x7e, 0xc0, 0x2f, 0xa6, 0x2b, 0x7b, 0x2c, 0xd3, 0x51, 0x36, 0x74, 0xdf, 0x32, 0xe4,
+ 0xb2, 0x97, 0x23, 0x10, 0xa5, 0x8a, 0xd8, 0x25, 0x8e, 0xa9, 0xf9, 0x2b, 0x0a, 0xec, 0x28, 0x32,
+ 0x58, 0x31, 0x57, 0x64, 0x30, 0x32, 0x07, 0x65, 0x87, 0xed, 0x9e, 0x4b, 0x87, 0x8e, 0x05, 0x76,
+ 0xfd, 0x1a, 0xdd, 0x45, 0x5e, 0x58, 0x7b, 0xbb, 0x08, 0xc0, 0x9a, 0x7f, 0x30, 0x0b, 0xfc, 0x87,
+ 0x60, 0xd8, 0xef, 0xf2, 0xbd, 0xb2, 0x5c, 0xb0, 0x23, 0xa7, 0x1d, 0x91, 0x8c, 0x2a, 0x9f, 0x3c,
+ 0x0e, 0x95, 0xd7, 0xbb, 0xb4, 0xab, 0x8e, 0x93, 0x43, 0x75, 0xed, 0xd3, 0x2c, 0x11, 0x45, 0xde,
+ 0xf1, 0x59, 0xd3, 0x94, 0xa5, 0xbe, 0x72, 0x5c, 0x96, 0xfa, 0x1a, 0x0c, 0x5f, 0x77, 0xb9, 0x0f,
+ 0xa0, 0xf6, 0x2f, 0x45, 0x80, 0xc8, 0xc7, 0x8a, 0x7c, 0xa3, 0x00, 0x0f, 0x87, 0x03, 0x2e, 0x10,
+ 0x5a, 0x37, 0x8f, 0xfd, 0x9a, 0xdb, 0x6a, 0x9f, 0x35, 0xd8, 0xf9, 0x0c, 0xb4, 0x9a, 0xc5, 0x0e,
+ 0xb3, 0x6b, 0x41, 0x10, 0xaa, 0xb4, 0xdd, 0x09, 0x76, 0xe7, 0x2d, 0x4f, 0xf6, 0xc0, 0x4c, 0x57,
+ 0xbe, 0xcb, 0x92, 0x46, 0x14, 0x95, 0x5b, 0x43, 0x3e, 0x88, 0x54, 0x0e, 0x86, 0x38, 0x64, 0x0b,
+ 0xaa, 0x8e, 0xfb, 0xaa, 0xcf, 0xc4, 0x21, 0xbb, 0xe3, 0x8b, 0x83, 0x8b, 0x5c, 0x88, 0x55, 0x58,
+ 0x79, 0xe5, 0x0b, 0x0e, 0x3b, 0x52, 0xd8, 0x5f, 0x2f, 0xc2, 0xa9, 0x0c, 0x39, 0x90, 0x17, 0x61,
+ 0x5c, 0xba, 0xb3, 0x45, 0x41, 0x90, 0x0b, 0x51, 0x10, 0xe4, 0x66, 0x2a, 0x0f, 0x7b, 0xa8, 0xc9,
+ 0xab, 0x00, 0xba, 0x61, 0x50, 0xdf, 0x5f, 0x71, 0x4d, 0xa5, 0x8f, 0xbe, 0xb0, 0xbf, 0x37, 0x09,
+ 0xb3, 0x61, 0xea, 0x9d, 0xbd, 0xc9, 0x8f, 0x66, 0x79, 0xa8, 0xa6, 0xe4, 0x1c, 0x15, 0xc0, 0x18,
+ 0x24, 0xf9, 0x3c, 0x80, 0xd8, 0x7a, 0x85, 0x97, 0xe8, 0xef, 0x61, 0xaf, 0x98, 0x52, 0xe1, 0x8a,
+ 0xa6, 0x3e, 0xdd, 0xd5, 0x9d, 0xc0, 0x0a, 0x76, 0x45, 0xcc, 0x92, 0x9b, 0x21, 0x0a, 0xc6, 0x10,
+ 0xb5, 0x3f, 0x2d, 0x42, 0x55, 0x59, 0x4a, 0xef, 0x83, 0x79, 0xac, 0x95, 0x30, 0x8f, 0x1d, 0x91,
+ 0x4f, 0x6a, 0x96, 0x71, 0xcc, 0x4d, 0x19, 0xc7, 0x16, 0xf3, 0xb3, 0xba, 0xbb, 0x69, 0xec, 0xdb,
+ 0x45, 0x18, 0x53, 0xa4, 0x79, 0x0d, 0x63, 0x9f, 0x84, 0x13, 0xe2, 0x2c, 0x79, 0x45, 0xbf, 0x2d,
+ 0xc2, 0xb7, 0x70, 0x81, 0x95, 0x85, 0x1b, 0x68, 0x23, 0x99, 0x85, 0x69, 0x5a, 0xd6, 0xad, 0x45,
+ 0xd2, 0x3a, 0xdb, 0x47, 0x88, 0xd3, 0x27, 0xb1, 0xdf, 0xe1, 0xdd, 0xba, 0x91, 0xca, 0xc3, 0x1e,
+ 0xea, 0xb4, 0x65, 0xae, 0x7c, 0x0c, 0x96, 0xb9, 0xbf, 0x2d, 0xc0, 0x48, 0x24, 0xaf, 0x63, 0xb7,
+ 0xcb, 0x6d, 0x26, 0xed, 0x72, 0xb3, 0xb9, 0xbb, 0x43, 0x1f, 0xab, 0xdc, 0x2f, 0x0d, 0x43, 0xc2,
+ 0x35, 0x9a, 0x6c, 0xc0, 0x39, 0x2b, 0xd3, 0xc1, 0x2b, 0x36, 0xdb, 0x84, 0x77, 0x7d, 0x97, 0xfa,
+ 0x52, 0xe2, 0x5d, 0x50, 0x48, 0x17, 0xaa, 0x3b, 0xd4, 0x0b, 0x2c, 0x83, 0xaa, 0xf6, 0x2d, 0xe6,
+ 0x56, 0xc9, 0xa4, 0xed, 0x31, 0x94, 0xe9, 0x4d, 0xc9, 0x00, 0x43, 0x56, 0x64, 0x03, 0x2a, 0xd4,
+ 0x6c, 0x51, 0x15, 0x50, 0x27, 0x67, 0xb8, 0xca, 0x50, 0x9e, 0xec, 0xcd, 0x47, 0x01, 0x4d, 0x7c,
+ 0xa8, 0xd9, 0xea, 0x6c, 0x49, 0xf6, 0xc3, 0xc1, 0x15, 0xac, 0xf0, 0x94, 0x2a, 0xba, 0x6b, 0x1f,
+ 0x26, 0x61, 0xc4, 0x87, 0x6c, 0x87, 0x46, 0xae, 0xca, 0x11, 0x4d, 0x1e, 0x77, 0x31, 0x71, 0xf9,
+ 0x50, 0xbb, 0xa5, 0x07, 0xd4, 0x6b, 0xeb, 0xde, 0xb6, 0xdc, 0x6d, 0x0c, 0xde, 0xc2, 0x97, 0x14,
+ 0x52, 0xd4, 0xc2, 0x30, 0x09, 0x23, 0x3e, 0xc4, 0x85, 0x5a, 0x20, 0xd5, 0x67, 0x65, 0xc9, 0x1b,
+ 0x9c, 0xa9, 0x52, 0xc4, 0x7d, 0xe9, 0x22, 0xad, 0x5e, 0x31, 0xe2, 0x41, 0x76, 0x12, 0xa1, 0x7c,
+ 0x45, 0x00, 0xe7, 0x46, 0x0e, 0x8b, 0xb0, 0x84, 0x8a, 0x96, 0x9b, 0xec, 0x90, 0xc0, 0xda, 0xdb,
+ 0x95, 0x68, 0x5a, 0xbe, 0xdf, 0x76, 0xaa, 0xa7, 0x93, 0x76, 0xaa, 0x0b, 0x69, 0x3b, 0x55, 0xea,
+ 0x88, 0xf2, 0xf0, 0x4e, 0x95, 0x29, 0x0b, 0x51, 0xf9, 0x18, 0x2c, 0x44, 0x4f, 0x41, 0x7d, 0x87,
+ 0xcf, 0x04, 0x22, 0x3a, 0x4f, 0x85, 0x2f, 0x23, 0x7c, 0x66, 0xbf, 0x19, 0x25, 0x63, 0x9c, 0x86,
+ 0x15, 0x91, 0x3f, 0x2f, 0x08, 0xc3, 0x9b, 0xca, 0x22, 0xcd, 0x28, 0x19, 0xe3, 0x34, 0xdc, 0x1f,
+ 0xcb, 0x72, 0xb6, 0x45, 0x81, 0x61, 0x5e, 0x40, 0xf8, 0x63, 0xa9, 0x44, 0x8c, 0xf2, 0xc9, 0x25,
+ 0xa8, 0x76, 0xcd, 0x4d, 0x41, 0x5b, 0xe5, 0xb4, 0x5c, 0xc3, 0x5c, 0x9f, 0x5f, 0x90, 0xd1, 0x82,
+ 0x54, 0x2e, 0xab, 0x49, 0x5b, 0xef, 0xa8, 0x0c, 0xbe, 0x37, 0x94, 0x35, 0x59, 0x89, 0x92, 0x31,
+ 0x4e, 0x43, 0x3e, 0x0e, 0x63, 0x1e, 0x35, 0xbb, 0x06, 0x0d, 0x4b, 0x01, 0x2f, 0x45, 0xc4, 0x5f,
+ 0x1a, 0xe2, 0x39, 0x98, 0xa2, 0xec, 0x63, 0xe7, 0xaa, 0x0f, 0x64, 0xe7, 0xfa, 0x41, 0x01, 0x48,
+ 0xaf, 0xff, 0x32, 0xd9, 0x82, 0x21, 0x87, 0x5b, 0xbf, 0x72, 0x07, 0x44, 0x8e, 0x19, 0xd1, 0xc4,
+ 0xb4, 0x24, 0x13, 0x24, 0x3e, 0x71, 0xa0, 0x4a, 0x6f, 0x07, 0xd4, 0x73, 0xc2, 0xfb, 0x0c, 0x47,
+ 0x13, 0x7c, 0x59, 0xec, 0x06, 0x24, 0x32, 0x86, 0x3c, 0xb4, 0x1f, 0x15, 0xa1, 0x1e, 0xa3, 0xbb,
+ 0xd7, 0xa6, 0x92, 0x5f, 0xa9, 0x16, 0x46, 0xa7, 0x75, 0xcf, 0x96, 0x23, 0x2c, 0x76, 0xa5, 0x5a,
+ 0x66, 0xe1, 0x32, 0xc6, 0xe9, 0xc8, 0x0c, 0x40, 0x5b, 0xf7, 0x03, 0xea, 0xf1, 0xd5, 0x37, 0x75,
+ 0x91, 0x79, 0x25, 0xcc, 0xc1, 0x18, 0x15, 0xb9, 0x28, 0xc3, 0x67, 0x97, 0x93, 0x81, 0xe7, 0xfa,
+ 0xc4, 0xc6, 0xae, 0x1c, 0x41, 0x6c, 0x6c, 0xd2, 0x82, 0x71, 0x55, 0x6b, 0x95, 0x7b, 0xb8, 0xb0,
+ 0x64, 0x62, 0xff, 0x92, 0x82, 0xc0, 0x1e, 0x50, 0xed, 0xed, 0x02, 0x8c, 0x26, 0x4c, 0x1e, 0x22,
+ 0x64, 0x9c, 0xf2, 0xbe, 0x4f, 0x84, 0x8c, 0x8b, 0x39, 0xcd, 0x3f, 0x09, 0x43, 0x42, 0x40, 0x69,
+ 0xa7, 0x3a, 0x21, 0x42, 0x94, 0xb9, 0x6c, 0x2e, 0x93, 0x46, 0xd5, 0xf4, 0x5c, 0x26, 0xad, 0xae,
+ 0xa8, 0xf2, 0x85, 0xb9, 0x5d, 0xd4, 0xae, 0xd7, 0xdc, 0x2e, 0xd2, 0x31, 0xa4, 0xd0, 0x7e, 0x5c,
+ 0x02, 0xee, 0x82, 0x42, 0x9e, 0x83, 0x5a, 0x9b, 0x1a, 0x5b, 0xba, 0x63, 0xf9, 0x2a, 0x64, 0x24,
+ 0xdb, 0xdd, 0xd6, 0x56, 0x54, 0xe2, 0x1d, 0x06, 0x30, 0xdb, 0x5c, 0xe6, 0x5e, 0xde, 0x11, 0x2d,
+ 0x31, 0x60, 0xa8, 0xe5, 0xfb, 0x7a, 0xc7, 0xca, 0x7d, 0x02, 0x2a, 0x42, 0xf4, 0x89, 0x41, 0x24,
+ 0x9e, 0x51, 0x42, 0x13, 0x03, 0x2a, 0x1d, 0x5b, 0xb7, 0x9c, 0xdc, 0xff, 0x28, 0x61, 0x2d, 0x58,
+ 0x65, 0x48, 0xc2, 0xa4, 0xc3, 0x1f, 0x51, 0x60, 0x93, 0x2e, 0xd4, 0x7d, 0xc3, 0xd3, 0xdb, 0xfe,
+ 0x96, 0x3e, 0xf3, 0xcc, 0xb3, 0xb9, 0x95, 0xa4, 0x88, 0x95, 0x98, 0xb3, 0xe7, 0x70, 0x76, 0xa5,
+ 0x79, 0x65, 0x76, 0xe6, 0x99, 0x67, 0x31, 0xce, 0x27, 0xce, 0xf6, 0x99, 0xa7, 0x66, 0x64, 0xbf,
+ 0x3f, 0x72, 0xb6, 0xcf, 0x3c, 0x35, 0x83, 0x71, 0x3e, 0xda, 0xbf, 0x17, 0xa0, 0x16, 0xd2, 0x92,
+ 0x75, 0x00, 0x36, 0x02, 0x65, 0x50, 0xbd, 0x43, 0x05, 0xb8, 0xe7, 0xbb, 0xe2, 0xf5, 0xb0, 0x30,
+ 0xc6, 0x80, 0x32, 0xa2, 0x0e, 0x16, 0x8f, 0x3a, 0xea, 0xe0, 0x34, 0xd4, 0xb6, 0x74, 0xc7, 0xf4,
+ 0xb7, 0xf4, 0x6d, 0x31, 0x11, 0xc5, 0xe2, 0x70, 0x5e, 0x51, 0x19, 0x18, 0xd1, 0x68, 0x7f, 0x3c,
+ 0x04, 0xe2, 0xd8, 0x92, 0x0d, 0x15, 0xd3, 0xf2, 0x85, 0xdf, 0x6c, 0x81, 0x97, 0x0c, 0x87, 0xca,
+ 0xbc, 0x4c, 0xc7, 0x90, 0x82, 0x9c, 0x85, 0x52, 0xdb, 0x72, 0xe4, 0x89, 0x07, 0x37, 0x78, 0xad,
+ 0x58, 0x0e, 0xb2, 0x34, 0x9e, 0xa5, 0xdf, 0x96, 0x2e, 0x4f, 0x22, 0x4b, 0xbf, 0x8d, 0x2c, 0x8d,
+ 0x6d, 0x41, 0x6d, 0xd7, 0xdd, 0xde, 0xd0, 0x8d, 0x6d, 0xe5, 0x19, 0x55, 0xe6, 0x0b, 0x21, 0xdf,
+ 0x82, 0x2e, 0x27, 0xb3, 0x30, 0x4d, 0x4b, 0xd6, 0xe1, 0x91, 0x37, 0xa8, 0xe7, 0xca, 0x51, 0xde,
+ 0xb4, 0x29, 0xed, 0x28, 0x18, 0xa1, 0x42, 0x70, 0x07, 0xab, 0xcf, 0x66, 0x93, 0x60, 0xbf, 0xb2,
+ 0xdc, 0x55, 0x53, 0xf7, 0x5a, 0x34, 0x58, 0xf5, 0x5c, 0x83, 0xfa, 0xbe, 0xe5, 0xb4, 0x14, 0xec,
+ 0x50, 0x04, 0xbb, 0x96, 0x4d, 0x82, 0xfd, 0xca, 0x92, 0x97, 0x61, 0x42, 0x64, 0x89, 0xc5, 0x76,
+ 0x76, 0x47, 0xb7, 0x6c, 0x7d, 0xc3, 0xb2, 0xd5, 0xaf, 0xbd, 0x46, 0xc5, 0xb9, 0xc2, 0x5a, 0x1f,
+ 0x1a, 0xec, 0x5b, 0x9a, 0x5c, 0x85, 0x71, 0x75, 0xaa, 0xb4, 0x4a, 0xbd, 0x66, 0x78, 0x94, 0x3d,
+ 0xda, 0xb8, 0xc0, 0xf6, 0x7b, 0xf3, 0xb4, 0xe3, 0x51, 0x83, 0x6b, 0x5d, 0x29, 0x2a, 0xec, 0x29,
+ 0x47, 0x10, 0xce, 0xf0, 0xf3, 0xea, 0xf5, 0xce, 0x9c, 0xeb, 0xda, 0xa6, 0x7b, 0xcb, 0x51, 0x6d,
+ 0x17, 0x8a, 0x0d, 0x3f, 0x48, 0x6a, 0x66, 0x52, 0x60, 0x9f, 0x92, 0xac, 0xe5, 0x3c, 0x67, 0xde,
+ 0xbd, 0xe5, 0xa4, 0x51, 0x21, 0x6a, 0x79, 0xb3, 0x0f, 0x0d, 0xf6, 0x2d, 0x4d, 0x16, 0x80, 0xa4,
+ 0x5b, 0xb0, 0xde, 0xe1, 0xca, 0xd0, 0x68, 0xe3, 0x8c, 0x88, 0x8f, 0x91, 0xce, 0xc5, 0x8c, 0x12,
+ 0x64, 0x19, 0x4e, 0xa7, 0x53, 0x19, 0x3b, 0xee, 0x24, 0x3f, 0x2a, 0x22, 0x63, 0x62, 0x46, 0x3e,
+ 0x66, 0x96, 0xd2, 0xfe, 0xa4, 0x08, 0xa3, 0x89, 0x0b, 0xd5, 0x0f, 0xdc, 0xc5, 0x55, 0xa6, 0x81,
+ 0xb6, 0xfd, 0xd6, 0xd2, 0xfc, 0x15, 0xaa, 0x9b, 0xd4, 0xbb, 0x46, 0xd5, 0xe5, 0x77, 0x3e, 0xa9,
+ 0xac, 0x24, 0x72, 0x30, 0x45, 0x49, 0x36, 0xa1, 0x22, 0xec, 0xa9, 0x79, 0x7f, 0x95, 0xa0, 0x64,
+ 0xc4, 0x8d, 0xaa, 0x7c, 0xc9, 0x11, 0x26, 0x55, 0x01, 0xaf, 0x05, 0x30, 0x12, 0xa7, 0x60, 0x13,
+ 0x49, 0xa4, 0xac, 0x0d, 0x27, 0x14, 0xb5, 0x25, 0x28, 0x05, 0xc1, 0xa0, 0x57, 0x62, 0x85, 0x7d,
+ 0x7e, 0x6d, 0x19, 0x19, 0x86, 0xb6, 0xc9, 0xbe, 0x9d, 0xef, 0x5b, 0xae, 0x23, 0xe3, 0x23, 0xaf,
+ 0xc3, 0x70, 0x20, 0x4d, 0x54, 0x83, 0x5d, 0xe9, 0xe5, 0xe6, 0x62, 0x65, 0x9e, 0x52, 0x58, 0xda,
+ 0xdf, 0x15, 0xa1, 0x16, 0x6e, 0x27, 0x0f, 0x10, 0x77, 0xd8, 0x85, 0x5a, 0xe8, 0x6f, 0x93, 0xfb,
+ 0xb7, 0x67, 0x91, 0x1b, 0x08, 0xdf, 0x01, 0x85, 0xaf, 0x18, 0xf1, 0x88, 0xfb, 0xf2, 0x94, 0x72,
+ 0xf8, 0xf2, 0x74, 0x60, 0x38, 0xf0, 0xac, 0x56, 0x4b, 0xea, 0xb6, 0x79, 0x9c, 0x79, 0x42, 0x71,
+ 0xad, 0x09, 0x40, 0x29, 0x59, 0xf1, 0x82, 0x8a, 0x8d, 0xf6, 0x1a, 0x8c, 0xa7, 0x29, 0xb9, 0xe2,
+ 0x67, 0x6c, 0x51, 0xb3, 0x6b, 0x2b, 0x19, 0x47, 0x8a, 0x9f, 0x4c, 0xc7, 0x90, 0x82, 0x6d, 0xfe,
+ 0xd8, 0x67, 0x7a, 0xc3, 0x75, 0xd4, 0xb6, 0x9a, 0xeb, 0xd0, 0x6b, 0x32, 0x0d, 0xc3, 0x5c, 0xed,
+ 0x9f, 0x4b, 0x70, 0x36, 0x32, 0x0a, 0xac, 0xe8, 0x8e, 0xde, 0x3a, 0xc0, 0xbf, 0xae, 0xde, 0xbf,
+ 0x24, 0x71, 0xd8, 0xe0, 0xf1, 0xa5, 0x07, 0x20, 0x78, 0xfc, 0x4f, 0x0a, 0xc0, 0x7d, 0x03, 0xc9,
+ 0x17, 0x61, 0x44, 0x8f, 0xfd, 0xe6, 0x50, 0x7e, 0xce, 0xcb, 0xb9, 0x3f, 0x27, 0x77, 0x41, 0x0c,
+ 0x7d, 0x5d, 0xe2, 0xa9, 0x98, 0x60, 0x48, 0x5c, 0xa8, 0x6e, 0xea, 0xb6, 0xcd, 0x74, 0xa1, 0xdc,
+ 0x87, 0x1c, 0x09, 0xe6, 0xbc, 0x9b, 0x2f, 0x48, 0x68, 0x0c, 0x99, 0x68, 0xff, 0x54, 0x80, 0xd1,
+ 0xa6, 0x6d, 0x99, 0x96, 0xd3, 0x3a, 0xc6, 0xa8, 0xf1, 0x37, 0xa0, 0xe2, 0xdb, 0x96, 0x49, 0x07,
+ 0x9c, 0xc7, 0xc5, 0x0a, 0xc2, 0x00, 0x50, 0xe0, 0x24, 0xc3, 0xd0, 0x97, 0x0e, 0x10, 0x86, 0xfe,
+ 0x67, 0x43, 0x20, 0xfd, 0x4b, 0x49, 0x17, 0x6a, 0x2d, 0x15, 0xdd, 0x5a, 0xb6, 0xf1, 0x4a, 0x8e,
+ 0xc8, 0x68, 0x89, 0x38, 0xd9, 0x62, 0xd6, 0x0d, 0x13, 0x31, 0xe2, 0x44, 0x68, 0xf2, 0xcf, 0x96,
+ 0xf3, 0x39, 0xff, 0x6c, 0x29, 0xd8, 0xf5, 0xfe, 0xdb, 0x52, 0x87, 0xf2, 0x56, 0x10, 0x74, 0xe4,
+ 0xb8, 0x1a, 0xdc, 0x81, 0x38, 0x0a, 0xce, 0x21, 0xb4, 0x11, 0xf6, 0x8e, 0x1c, 0x9a, 0xb1, 0x70,
+ 0xf4, 0xf0, 0x87, 0x4a, 0x73, 0xb9, 0x0e, 0xba, 0xe3, 0x2c, 0xd8, 0x3b, 0x72, 0x68, 0xf2, 0x05,
+ 0xa8, 0x07, 0x9e, 0xee, 0xf8, 0x9b, 0xae, 0xd7, 0xa6, 0x9e, 0xdc, 0x1d, 0x2e, 0xe4, 0xf8, 0xb9,
+ 0xe3, 0x5a, 0x84, 0x26, 0x4e, 0xd0, 0x12, 0x49, 0x18, 0xe7, 0x46, 0xb6, 0xa1, 0xda, 0x35, 0x45,
+ 0xc5, 0xa4, 0xd9, 0x64, 0x36, 0xcf, 0xff, 0x3a, 0x63, 0xc7, 0xd8, 0xea, 0x0d, 0x43, 0x06, 0xc9,
+ 0x7f, 0x87, 0x0d, 0x1f, 0xd5, 0xbf, 0xc3, 0xe2, 0xbd, 0x31, 0x2b, 0x72, 0x00, 0x69, 0x4b, 0x8d,
+ 0xd2, 0x69, 0x49, 0x2f, 0x9c, 0x85, 0xdc, 0xca, 0x9e, 0x60, 0x59, 0x0f, 0xb5, 0x52, 0xa7, 0x85,
+ 0x8a, 0x87, 0xd6, 0x06, 0x69, 0xdd, 0x26, 0x46, 0xe2, 0x0f, 0x1b, 0xe2, 0x3a, 0xcb, 0xf4, 0xc1,
+ 0xe6, 0x83, 0xf0, 0x57, 0x0f, 0xb1, 0x08, 0xbf, 0x99, 0xbf, 0xd2, 0xd0, 0xfe, 0xbe, 0x08, 0xa5,
+ 0xb5, 0xe5, 0xa6, 0x88, 0xda, 0xc7, 0x7f, 0x5f, 0x43, 0x9b, 0xdb, 0x56, 0xe7, 0x26, 0xf5, 0xac,
+ 0xcd, 0x5d, 0xb9, 0xe9, 0x8d, 0x45, 0xed, 0x4b, 0x53, 0x60, 0x46, 0x29, 0xf2, 0x0a, 0x8c, 0x18,
+ 0xfa, 0x1c, 0xf5, 0x82, 0x41, 0xb6, 0xf4, 0xfc, 0xde, 0xde, 0xdc, 0x6c, 0x54, 0x1c, 0x13, 0x60,
+ 0x64, 0x1d, 0xc0, 0x88, 0xa0, 0x4b, 0x87, 0x36, 0x44, 0xc4, 0x80, 0x63, 0x40, 0x04, 0xa1, 0xb6,
+ 0xcd, 0x48, 0x39, 0x6a, 0xf9, 0x30, 0xa8, 0xbc, 0xe7, 0x5c, 0x53, 0x65, 0x31, 0x82, 0xd1, 0x1c,
+ 0x18, 0x4d, 0xfc, 0x76, 0x83, 0x7c, 0x0c, 0xaa, 0x6e, 0x27, 0x36, 0x9d, 0xd6, 0xb8, 0xbf, 0x5f,
+ 0xf5, 0x86, 0x4c, 0xbb, 0xb3, 0x37, 0x39, 0xba, 0xec, 0xb6, 0x2c, 0x43, 0x25, 0x60, 0x48, 0x4e,
+ 0x34, 0x18, 0xe2, 0x97, 0x6d, 0xd4, 0x4f, 0x37, 0xf8, 0xda, 0xc1, 0xe3, 0xe2, 0xfb, 0x28, 0x73,
+ 0xb4, 0x2f, 0x95, 0x21, 0x3a, 0x13, 0x22, 0x3e, 0x0c, 0x09, 0x67, 0x62, 0x39, 0x73, 0x1f, 0xab,
+ 0xdf, 0xb2, 0x64, 0x45, 0x5a, 0x50, 0x7a, 0xcd, 0xdd, 0xc8, 0x3d, 0x71, 0xc7, 0x6e, 0xd9, 0x0a,
+ 0x2b, 0x55, 0x2c, 0x01, 0x19, 0x07, 0xf2, 0xeb, 0x05, 0x38, 0xe9, 0xa7, 0x95, 0x4e, 0xd9, 0x1d,
+ 0x30, 0xbf, 0x76, 0x9d, 0x56, 0x63, 0xa5, 0x63, 0x66, 0xbf, 0x6c, 0xec, 0xad, 0x0b, 0x93, 0xbf,
+ 0x38, 0xac, 0x91, 0xdd, 0x69, 0x31, 0xe7, 0xaf, 0xe2, 0x92, 0xf2, 0x4f, 0xa6, 0xa1, 0x64, 0xa5,
+ 0x7d, 0xa5, 0x08, 0xf5, 0xd8, 0x6c, 0x9d, 0xfb, 0x5f, 0x2e, 0xb7, 0x53, 0xff, 0x72, 0x59, 0x1d,
+ 0xfc, 0xec, 0x32, 0xaa, 0xd5, 0x71, 0xff, 0xce, 0xe5, 0xcf, 0x8b, 0x50, 0x5a, 0x9f, 0x5f, 0x48,
+ 0x6e, 0x17, 0x0b, 0xf7, 0x61, 0xbb, 0xb8, 0x05, 0xc3, 0x1b, 0x5d, 0xcb, 0x0e, 0x2c, 0x27, 0x77,
+ 0x1c, 0x00, 0xf5, 0xeb, 0x1b, 0x79, 0x9d, 0x56, 0xa0, 0xa2, 0x82, 0x27, 0x2d, 0x18, 0x6e, 0x89,
+ 0x40, 0x6c, 0xb9, 0x3d, 0xba, 0x64, 0x40, 0x37, 0xc1, 0x48, 0xbe, 0xa0, 0x42, 0xd7, 0x76, 0x41,
+ 0xfe, 0x3c, 0xfb, 0xbe, 0x4b, 0x53, 0xfb, 0x02, 0x84, 0x5a, 0xc0, 0xfd, 0x67, 0xfe, 0xaf, 0x05,
+ 0x48, 0x2a, 0x3e, 0xf7, 0xbf, 0x37, 0x6d, 0xa7, 0x7b, 0xd3, 0xfc, 0x51, 0x0c, 0xbe, 0xec, 0x0e,
+ 0xa5, 0xfd, 0x51, 0x11, 0x86, 0xee, 0xdb, 0xdd, 0x4d, 0x9a, 0x70, 0x4e, 0x9b, 0xcb, 0x39, 0x31,
+ 0xf6, 0x75, 0x4d, 0x6b, 0xa7, 0x5c, 0xd3, 0xf2, 0xfe, 0xac, 0xf3, 0x1e, 0x8e, 0x69, 0x7f, 0x5d,
+ 0x00, 0x39, 0x2d, 0x2f, 0x39, 0x7e, 0xa0, 0x3b, 0x06, 0xff, 0x67, 0xbc, 0x5c, 0x03, 0xf2, 0x7a,
+ 0x40, 0x48, 0x2f, 0x21, 0xb1, 0xec, 0xf3, 0x67, 0x35, 0xe7, 0x93, 0x8f, 0x40, 0x75, 0xcb, 0xf5,
+ 0x03, 0x3e, 0xcf, 0x17, 0x93, 0x76, 0x9d, 0x2b, 0x32, 0x1d, 0x43, 0x8a, 0xf4, 0x49, 0x61, 0xa5,
+ 0xff, 0x49, 0xa1, 0xf6, 0xad, 0x22, 0x8c, 0xbc, 0x57, 0x2e, 0xa0, 0x66, 0xb9, 0xf2, 0x95, 0x72,
+ 0xba, 0xf2, 0x95, 0x0f, 0xe3, 0xca, 0xa7, 0x7d, 0xaf, 0x00, 0x70, 0xdf, 0x6e, 0xbf, 0x9a, 0x49,
+ 0x2f, 0xbb, 0xdc, 0xfd, 0x2a, 0xdb, 0xc7, 0xee, 0xf7, 0x2b, 0xaa, 0x49, 0xdc, 0xc3, 0xee, 0xcd,
+ 0x02, 0x8c, 0xe9, 0x09, 0xaf, 0xb5, 0xdc, 0xaa, 0x65, 0xca, 0x09, 0x2e, 0xbc, 0xe9, 0x97, 0x4c,
+ 0xc7, 0x14, 0x5b, 0xf2, 0x7c, 0x14, 0x79, 0xf5, 0x7a, 0xd4, 0xed, 0x7b, 0x42, 0xa6, 0x72, 0x35,
+ 0x27, 0x41, 0x79, 0x0f, 0x2f, 0xc1, 0xd2, 0x91, 0x78, 0x09, 0xc6, 0xef, 0x3f, 0x95, 0xef, 0x7a,
+ 0xff, 0x69, 0x07, 0x6a, 0x9b, 0x9e, 0xdb, 0xe6, 0x8e, 0x78, 0xf2, 0x37, 0x9f, 0x97, 0x73, 0xac,
+ 0x29, 0xd1, 0x0f, 0xae, 0x23, 0x1b, 0xcf, 0x82, 0xc2, 0xc7, 0x88, 0x15, 0x37, 0x48, 0xbb, 0x82,
+ 0xeb, 0xd0, 0x51, 0x72, 0x0d, 0xe7, 0x92, 0x35, 0x81, 0x8e, 0x8a, 0x4d, 0xd2, 0xf9, 0x6e, 0xf8,
+ 0xfe, 0x38, 0xdf, 0x69, 0xbf, 0x58, 0x56, 0x13, 0xd8, 0x03, 0x17, 0xe4, 0xef, 0xbd, 0x7f, 0x6b,
+ 0x32, 0x7d, 0xa5, 0x71, 0xf8, 0x3e, 0x5e, 0x69, 0xac, 0x0e, 0xe4, 0xea, 0xb5, 0x57, 0x82, 0xd4,
+ 0xbe, 0xe9, 0xfd, 0xd3, 0x89, 0xff, 0x54, 0xa7, 0x13, 0x6f, 0x15, 0x21, 0x9a, 0x08, 0x0e, 0xe9,
+ 0xbd, 0xf1, 0x32, 0x54, 0xdb, 0xfa, 0xed, 0x79, 0x6a, 0xeb, 0xbb, 0x79, 0xfe, 0xcd, 0xb8, 0x22,
+ 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80, 0x15, 0xc6, 0x47, 0xce, 0x6d, 0x6d, 0x8e, 0x42, 0x2d, 0x0b,
+ 0x7b, 0x56, 0xf4, 0x8e, 0x31, 0x36, 0xda, 0x5f, 0x15, 0x41, 0x06, 0xd2, 0x26, 0x14, 0x2a, 0x9b,
+ 0xd6, 0x6d, 0x6a, 0xe6, 0xf6, 0x64, 0x8c, 0xfd, 0x31, 0x57, 0x98, 0xd3, 0x79, 0x02, 0x0a, 0x74,
+ 0x6e, 0x27, 0x15, 0xc7, 0x23, 0x52, 0x7e, 0x39, 0xec, 0xa4, 0xf1, 0x63, 0x16, 0x69, 0x27, 0x15,
+ 0x49, 0xa8, 0x78, 0x08, 0xb3, 0x2c, 0x3f, 0xa3, 0x96, 0x22, 0xcd, 0x63, 0x96, 0x8d, 0x9d, 0x75,
+ 0x2b, 0xb3, 0xac, 0x2f, 0xee, 0x34, 0x4b, 0x1e, 0x8d, 0xcf, 0x7d, 0xf7, 0xfb, 0x17, 0x1e, 0xfa,
+ 0xde, 0xf7, 0x2f, 0x3c, 0xf4, 0xce, 0xf7, 0x2f, 0x3c, 0xf4, 0xa5, 0xfd, 0x0b, 0x85, 0xef, 0xee,
+ 0x5f, 0x28, 0x7c, 0x6f, 0xff, 0x42, 0xe1, 0x9d, 0xfd, 0x0b, 0x85, 0x7f, 0xd8, 0xbf, 0x50, 0xf8,
+ 0x95, 0x7f, 0xbc, 0xf0, 0xd0, 0x67, 0x9f, 0x8b, 0xaa, 0x30, 0xad, 0xaa, 0x30, 0xad, 0x18, 0x4e,
+ 0x77, 0xb6, 0x5b, 0xd3, 0xac, 0x0a, 0x51, 0x8a, 0xaa, 0xc2, 0x7f, 0x04, 0x00, 0x00, 0xff, 0xff,
+ 0xb2, 0x71, 0xfd, 0x11, 0x25, 0x93, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -3192,6 +3194,20 @@ func (m *AbstractPodTemplate) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ if len(m.ResourceClaims) > 0 {
+ for iNdEx := len(m.ResourceClaims) - 1; iNdEx >= 0; iNdEx-- {
+ {
+ size, err := m.ResourceClaims[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x72
+ }
+ }
if m.DNSConfig != nil {
{
size, err := m.DNSConfig.MarshalToSizedBuffer(dAtA[:i])
@@ -9058,6 +9074,12 @@ func (m *AbstractPodTemplate) Size() (n int) {
l = m.DNSConfig.Size()
n += 1 + l + sovGenerated(uint64(l))
}
+ if len(m.ResourceClaims) > 0 {
+ for _, e := range m.ResourceClaims {
+ l = e.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ }
return n
}
@@ -11162,6 +11184,11 @@ func (this *AbstractPodTemplate) String() string {
repeatedStringForImagePullSecrets += fmt.Sprintf("%v", f) + ","
}
repeatedStringForImagePullSecrets += "}"
+ repeatedStringForResourceClaims := "[]PodResourceClaim{"
+ for _, f := range this.ResourceClaims {
+ repeatedStringForResourceClaims += fmt.Sprintf("%v", f) + ","
+ }
+ repeatedStringForResourceClaims += "}"
keysForNodeSelector := make([]string, 0, len(this.NodeSelector))
for k := range this.NodeSelector {
keysForNodeSelector = append(keysForNodeSelector, k)
@@ -11186,6 +11213,7 @@ func (this *AbstractPodTemplate) String() string {
`AutomountServiceAccountToken:` + valueToStringGenerated(this.AutomountServiceAccountToken) + `,`,
`DNSPolicy:` + fmt.Sprintf("%v", this.DNSPolicy) + `,`,
`DNSConfig:` + strings.Replace(fmt.Sprintf("%v", this.DNSConfig), "PodDNSConfig", "v1.PodDNSConfig", 1) + `,`,
+ `ResourceClaims:` + repeatedStringForResourceClaims + `,`,
`}`,
}, "")
return s
@@ -13157,6 +13185,40 @@ func (m *AbstractPodTemplate) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 14:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ResourceClaims", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.ResourceClaims = append(m.ResourceClaims, v1.PodResourceClaim{})
+ if err := m.ResourceClaims[len(m.ResourceClaims)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 588b26c9af..b8935365e6 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -114,6 +114,14 @@ message AbstractPodTemplate {
// configuration based on DNSPolicy.
// +optional
optional k8s.io.api.core.v1.PodDNSConfig dnsConfig = 13;
+
+ // ResourceClaims defines which ResourceClaims must be allocated and reserved
+ // before the Pod is allowed to start. The resources will be made available to those
+ // containers which consume them by name.
+ // +patchMergeKey=name
+ // +patchStrategy=merge,retainKeys
+ // +optional
+ repeated k8s.io.api.core.v1.PodResourceClaim resourceClaims = 14;
}
message AbstractSink {
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 7189543f3b..0d129cdab1 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -249,11 +249,31 @@ func schema_pkg_apis_numaflow_v1alpha1_AbstractPodTemplate(ref common.ReferenceC
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
},
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
@@ -451,6 +471,26 @@ func schema_pkg_apis_numaflow_v1alpha1_AbstractVertex(ref common.ReferenceCallba
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"volumes": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
@@ -544,7 +584,7 @@ func schema_pkg_apis_numaflow_v1alpha1_AbstractVertex(ref common.ReferenceCallba
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
}
}
@@ -999,6 +1039,26 @@ func schema_pkg_apis_numaflow_v1alpha1_DaemonTemplate(ref common.ReferenceCallba
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"replicas": {
SchemaProps: spec.SchemaProps{
Description: "Replicas is the number of desired replicas of the Deployment. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller",
@@ -1020,7 +1080,7 @@ func schema_pkg_apis_numaflow_v1alpha1_DaemonTemplate(ref common.ReferenceCallba
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
@@ -2369,6 +2429,26 @@ func schema_pkg_apis_numaflow_v1alpha1_JetStreamBufferService(ref common.Referen
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"settings": {
SchemaProps: spec.SchemaProps{
Description: "Nats/JetStream configuration, if not specified, global settings in numaflow-controller-config will be used. See https://docs.nats.io/running-a-nats-service/configuration#limits and https://docs.nats.io/running-a-nats-service/configuration#jetstream. For limits, only \"max_payload\" is supported for configuration, defaults to 1048576 (1MB), not recommended to use values over 8388608 (8MB) but max_payload can be set up to 67108864 (64MB). For jetstream, only \"max_memory_store\" and \"max_file_store\" are supported for configuration, do not set \"store_dir\" as it has been hardcoded.",
@@ -2416,7 +2496,7 @@ func schema_pkg_apis_numaflow_v1alpha1_JetStreamBufferService(ref common.Referen
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.PersistenceStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.PersistenceStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
@@ -2624,6 +2704,26 @@ func schema_pkg_apis_numaflow_v1alpha1_JobTemplate(ref common.ReferenceCallback)
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"containerTemplate": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate"),
@@ -2647,7 +2747,7 @@ func schema_pkg_apis_numaflow_v1alpha1_JobTemplate(ref common.ReferenceCallback)
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
@@ -3110,6 +3210,26 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexSpec(ref common.ReferenceCallba
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"containerTemplate": {
SchemaProps: spec.SchemaProps{
Description: "Container template for the main numa container.",
@@ -3186,7 +3306,7 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexSpec(ref common.ReferenceCallba
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.DaemonTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.DaemonTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
}
}
@@ -3436,6 +3556,26 @@ func schema_pkg_apis_numaflow_v1alpha1_NativeRedis(ref common.ReferenceCallback)
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"settings": {
SchemaProps: spec.SchemaProps{
Description: "Redis configuration, if not specified, global settings in numaflow-controller-config will be used.",
@@ -3446,7 +3586,7 @@ func schema_pkg_apis_numaflow_v1alpha1_NativeRedis(ref common.ReferenceCallback)
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.PersistenceStrategy", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisSettings", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.PersistenceStrategy", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisSettings", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
@@ -4522,6 +4662,26 @@ func schema_pkg_apis_numaflow_v1alpha1_SideInputsManagerTemplate(ref common.Refe
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"containerTemplate": {
SchemaProps: spec.SchemaProps{
Description: "Template for the side inputs manager numa container",
@@ -4538,7 +4698,7 @@ func schema_pkg_apis_numaflow_v1alpha1_SideInputsManagerTemplate(ref common.Refe
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
@@ -5286,6 +5446,26 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexSpec(ref common.ReferenceCallback)
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"volumes": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
@@ -5432,7 +5612,7 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexSpec(ref common.ReferenceCallback)
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.CombinedEdge", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Watermark", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.CombinedEdge", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Watermark", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
}
}
@@ -5636,6 +5816,26 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexTemplate(ref common.ReferenceCallba
Ref: ref("k8s.io/api/core/v1.PodDNSConfig"),
},
},
+ "resourceClaims": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/core/v1.PodResourceClaim"),
+ },
+ },
+ },
+ },
+ },
"containerTemplate": {
SchemaProps: spec.SchemaProps{
Description: "Template for the vertex numa container",
@@ -5652,7 +5852,7 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexTemplate(ref common.ReferenceCallba
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
diff --git a/pkg/apis/numaflow/v1alpha1/pod_template.go b/pkg/apis/numaflow/v1alpha1/pod_template.go
index 442ede947c..553e280efc 100644
--- a/pkg/apis/numaflow/v1alpha1/pod_template.go
+++ b/pkg/apis/numaflow/v1alpha1/pod_template.go
@@ -90,6 +90,13 @@ type AbstractPodTemplate struct {
// configuration based on DNSPolicy.
// +optional
DNSConfig *corev1.PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,13,opt,name=dnsConfig"`
+ // ResourceClaims defines which ResourceClaims must be allocated and reserved
+ // before the Pod is allowed to start. The resources will be made available to those
+ // containers which consume them by name.
+ // +patchMergeKey=name
+ // +patchStrategy=merge,retainKeys
+ // +optional
+ ResourceClaims []corev1.PodResourceClaim `json:"resourceClaims,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,14,rep,name=resourceClaims"`
}
// ApplyToPodSpec updates the PodSpec with the values in the AbstractPodTemplate
@@ -130,6 +137,9 @@ func (apt *AbstractPodTemplate) ApplyToPodSpec(ps *corev1.PodSpec) {
if ps.DNSConfig == nil {
ps.DNSConfig = apt.DNSConfig
}
+ if len(ps.ResourceClaims) == 0 {
+ ps.ResourceClaims = apt.ResourceClaims
+ }
}
// ApplyToPodTemplateSpec updates the PodTemplateSpec with the values in the AbstractPodTemplate
diff --git a/pkg/apis/numaflow/v1alpha1/pod_template_test.go b/pkg/apis/numaflow/v1alpha1/pod_template_test.go
index 4a54f4a288..40d6072f45 100644
--- a/pkg/apis/numaflow/v1alpha1/pod_template_test.go
+++ b/pkg/apis/numaflow/v1alpha1/pod_template_test.go
@@ -71,6 +71,11 @@ func TestApplyToPodSpec(t *testing.T) {
},
},
ServiceAccountName: "template-sa",
+ ResourceClaims: []corev1.PodResourceClaim{
+ {
+ Name: "template-resource-claim",
+ },
+ },
}
podSpec := &corev1.PodSpec{
@@ -92,4 +97,5 @@ func TestApplyToPodSpec(t *testing.T) {
assert.Equal(t, podSpec.Priority, abstractPodTemplate.Priority)
assert.Equal(t, podSpec.Affinity, abstractPodTemplate.Affinity)
assert.Equal(t, podSpec.ServiceAccountName, "spec-sa")
+ assert.Equal(t, podSpec.ResourceClaims[0].Name, "template-resource-claim")
}
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index 81d1a53700..a5ee6d9b6f 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -84,6 +84,13 @@ func (in *AbstractPodTemplate) DeepCopyInto(out *AbstractPodTemplate) {
*out = new(v1.PodDNSConfig)
(*in).DeepCopyInto(*out)
}
+ if in.ResourceClaims != nil {
+ in, out := &in.ResourceClaims, &out.ResourceClaims
+ *out = make([]v1.PodResourceClaim, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
return
}
diff --git a/rust/numaflow-models/Makefile b/rust/numaflow-models/Makefile
index 5ff0bf7e53..3e38249cae 100644
--- a/rust/numaflow-models/Makefile
+++ b/rust/numaflow-models/Makefile
@@ -50,6 +50,7 @@ generate:
--type-mappings CoreV1ContainerResizePolicy="k8s_openapi::api::core::v1::ContainerResizePolicy" \
--type-mappings CoreV1Container="k8s_openapi::api::core::v1::Container" \
--type-mappings CoreV1Volume="k8s_openapi::api::core::v1::Volume" \
+ --type-mappings CoreV1PodResourceClaim="k8s_openapi::api::core::v1::PodResourceClaim" \
--type-mappings CoreV1EmptyDirVolumeSource="k8s_openapi::api::core::v1::EmptyDirVolumeSource" \
--type-mappings MetaV1Duration="kube::core::Duration" \
--type-mappings MetaV1ListMeta="k8s_openapi::apimachinery::pkg::apis::meta::v1::ListMeta" \
diff --git a/rust/numaflow-models/src/models/abstract_pod_template.rs b/rust/numaflow-models/src/models/abstract_pod_template.rs
index 612ade86b6..7315dc4d90 100644
--- a/rust/numaflow-models/src/models/abstract_pod_template.rs
+++ b/rust/numaflow-models/src/models/abstract_pod_template.rs
@@ -47,6 +47,9 @@ pub struct AbstractPodTemplate {
/// If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
#[serde(rename = "priorityClassName", skip_serializing_if = "Option::is_none")]
pub priority_class_name: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -73,6 +76,7 @@ impl AbstractPodTemplate {
node_selector: None,
priority: None,
priority_class_name: None,
+ resource_claims: None,
runtime_class_name: None,
security_context: None,
service_account_name: None,
diff --git a/rust/numaflow-models/src/models/abstract_vertex.rs b/rust/numaflow-models/src/models/abstract_vertex.rs
index a93bcc3ac0..23fb85c813 100644
--- a/rust/numaflow-models/src/models/abstract_vertex.rs
+++ b/rust/numaflow-models/src/models/abstract_vertex.rs
@@ -62,6 +62,9 @@ pub struct AbstractVertex {
/// If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
#[serde(rename = "priorityClassName", skip_serializing_if = "Option::is_none")]
pub priority_class_name: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -114,6 +117,7 @@ impl AbstractVertex {
partitions: None,
priority: None,
priority_class_name: None,
+ resource_claims: None,
runtime_class_name: None,
scale: None,
security_context: None,
diff --git a/rust/numaflow-models/src/models/daemon_template.rs b/rust/numaflow-models/src/models/daemon_template.rs
index 5d03240b86..c6394874fd 100644
--- a/rust/numaflow-models/src/models/daemon_template.rs
+++ b/rust/numaflow-models/src/models/daemon_template.rs
@@ -55,6 +55,9 @@ pub struct DaemonTemplate {
/// Replicas is the number of desired replicas of the Deployment. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller
#[serde(rename = "replicas", skip_serializing_if = "Option::is_none")]
pub replicas: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -83,6 +86,7 @@ impl DaemonTemplate {
priority: None,
priority_class_name: None,
replicas: None,
+ resource_claims: None,
runtime_class_name: None,
security_context: None,
service_account_name: None,
diff --git a/rust/numaflow-models/src/models/jet_stream_buffer_service.rs b/rust/numaflow-models/src/models/jet_stream_buffer_service.rs
index 96dce5855c..9d434864e7 100644
--- a/rust/numaflow-models/src/models/jet_stream_buffer_service.rs
+++ b/rust/numaflow-models/src/models/jet_stream_buffer_service.rs
@@ -68,6 +68,9 @@ pub struct JetStreamBufferService {
/// JetStream StatefulSet size
#[serde(rename = "replicas", skip_serializing_if = "Option::is_none")]
pub replicas: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -112,6 +115,7 @@ impl JetStreamBufferService {
priority_class_name: None,
reloader_container_template: None,
replicas: None,
+ resource_claims: None,
runtime_class_name: None,
security_context: None,
service_account_name: None,
diff --git a/rust/numaflow-models/src/models/job_template.rs b/rust/numaflow-models/src/models/job_template.rs
index 6c273d4db0..d41b0d3340 100644
--- a/rust/numaflow-models/src/models/job_template.rs
+++ b/rust/numaflow-models/src/models/job_template.rs
@@ -50,6 +50,9 @@ pub struct JobTemplate {
/// If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
#[serde(rename = "priorityClassName", skip_serializing_if = "Option::is_none")]
pub priority_class_name: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -83,6 +86,7 @@ impl JobTemplate {
node_selector: None,
priority: None,
priority_class_name: None,
+ resource_claims: None,
runtime_class_name: None,
security_context: None,
service_account_name: None,
diff --git a/rust/numaflow-models/src/models/mono_vertex_spec.rs b/rust/numaflow-models/src/models/mono_vertex_spec.rs
index 1041fbafb7..8eadbbdaab 100644
--- a/rust/numaflow-models/src/models/mono_vertex_spec.rs
+++ b/rust/numaflow-models/src/models/mono_vertex_spec.rs
@@ -56,6 +56,9 @@ pub struct MonoVertexSpec {
pub priority_class_name: Option,
#[serde(rename = "replicas", skip_serializing_if = "Option::is_none")]
pub replicas: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -97,6 +100,7 @@ impl MonoVertexSpec {
priority: None,
priority_class_name: None,
replicas: None,
+ resource_claims: None,
runtime_class_name: None,
scale: None,
security_context: None,
diff --git a/rust/numaflow-models/src/models/native_redis.rs b/rust/numaflow-models/src/models/native_redis.rs
index 3200cd7731..2516bd5e23 100644
--- a/rust/numaflow-models/src/models/native_redis.rs
+++ b/rust/numaflow-models/src/models/native_redis.rs
@@ -65,6 +65,9 @@ pub struct NativeRedis {
/// Redis StatefulSet size
#[serde(rename = "replicas", skip_serializing_if = "Option::is_none")]
pub replicas: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -105,6 +108,7 @@ impl NativeRedis {
priority_class_name: None,
redis_container_template: None,
replicas: None,
+ resource_claims: None,
runtime_class_name: None,
security_context: None,
sentinel_container_template: None,
diff --git a/rust/numaflow-models/src/models/side_inputs_manager_template.rs b/rust/numaflow-models/src/models/side_inputs_manager_template.rs
index d1b4fa8cb4..ec9d3f1073 100644
--- a/rust/numaflow-models/src/models/side_inputs_manager_template.rs
+++ b/rust/numaflow-models/src/models/side_inputs_manager_template.rs
@@ -52,6 +52,9 @@ pub struct SideInputsManagerTemplate {
/// If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
#[serde(rename = "priorityClassName", skip_serializing_if = "Option::is_none")]
pub priority_class_name: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -79,6 +82,7 @@ impl SideInputsManagerTemplate {
node_selector: None,
priority: None,
priority_class_name: None,
+ resource_claims: None,
runtime_class_name: None,
security_context: None,
service_account_name: None,
diff --git a/rust/numaflow-models/src/models/vertex_spec.rs b/rust/numaflow-models/src/models/vertex_spec.rs
index 078879d286..a647ecc7ae 100644
--- a/rust/numaflow-models/src/models/vertex_spec.rs
+++ b/rust/numaflow-models/src/models/vertex_spec.rs
@@ -73,6 +73,9 @@ pub struct VertexSpec {
pub priority_class_name: Option,
#[serde(rename = "replicas", skip_serializing_if = "Option::is_none")]
pub replicas: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -133,6 +136,7 @@ impl VertexSpec {
priority: None,
priority_class_name: None,
replicas: None,
+ resource_claims: None,
runtime_class_name: None,
scale: None,
security_context: None,
diff --git a/rust/numaflow-models/src/models/vertex_template.rs b/rust/numaflow-models/src/models/vertex_template.rs
index 85d743546d..b707cd2ed0 100644
--- a/rust/numaflow-models/src/models/vertex_template.rs
+++ b/rust/numaflow-models/src/models/vertex_template.rs
@@ -52,6 +52,9 @@ pub struct VertexTemplate {
/// If specified, indicates the Redis pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
#[serde(rename = "priorityClassName", skip_serializing_if = "Option::is_none")]
pub priority_class_name: Option,
+ /// ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.
+ #[serde(rename = "resourceClaims", skip_serializing_if = "Option::is_none")]
+ pub resource_claims: Option>,
/// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
#[serde(rename = "runtimeClassName", skip_serializing_if = "Option::is_none")]
pub runtime_class_name: Option,
@@ -79,6 +82,7 @@ impl VertexTemplate {
node_selector: None,
priority: None,
priority_class_name: None,
+ resource_claims: None,
runtime_class_name: None,
security_context: None,
service_account_name: None,
From ee8b83ac649ab0ec4860cf8b03720ac6016aadaa Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Thu, 29 Aug 2024 16:32:21 -0700
Subject: [PATCH 035/188] chore: refactor metric variable names (#2012)
Signed-off-by: Vigith Maurice
---
rust/monovertex/src/forwarder.rs | 10 ++---
rust/monovertex/src/metrics.rs | 68 ++++++++++++++++----------------
2 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index d9f68fc608..8f61b68ff1 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -106,7 +106,7 @@ impl Forwarder {
}
forward_metrics()
- .monovtx_processing_time
+ .e2e_processing_time
.get_or_create(&self.common_labels)
.observe(start_time.elapsed().as_micros() as f64);
}
@@ -135,7 +135,7 @@ impl Forwarder {
let msg_count = messages.len() as u64;
forward_metrics()
- .monovtx_read_total
+ .read_total
.get_or_create(&self.common_labels)
.inc_by(msg_count);
@@ -149,7 +149,7 @@ impl Forwarder {
);
forward_metrics()
- .monovtx_read_bytes_total
+ .read_bytes_total
.get_or_create(&self.common_labels)
.inc_by(bytes_count);
@@ -276,7 +276,7 @@ impl Forwarder {
}
forward_metrics()
- .monovtx_sink_write_total
+ .sink_write_total
.get_or_create(&self.common_labels)
.inc_by(msg_count);
Ok(())
@@ -382,7 +382,7 @@ impl Forwarder {
self.source_client.ack_fn(offsets).await?;
debug!("Ack latency - {}ms", start_time.elapsed().as_millis());
forward_metrics()
- .monovtx_ack_total
+ .ack_total
.get_or_create(&self.common_labels)
.inc_by(n as u64);
Ok(())
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index 7a87b508a4..fd612ba12a 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -29,8 +29,8 @@ use prometheus_client::registry::Registry;
// Define the labels for the metrics
// Note: Please keep consistent with the definitions in MonoVertex daemon
-pub const MONO_VERTEX_NAME_LABEL: &str = "mvtx_name";
-pub const REPLICA_LABEL: &str = "mvtx_replica";
+const VERTEX_NAME_LABEL: &str = "mvtx_name";
+const REPLICA_LABEL: &str = "mvtx_replica";
const PENDING_PERIOD_LABEL: &str = "period";
// Define the metrics
@@ -39,12 +39,12 @@ const PENDING_PERIOD_LABEL: &str = "period";
// refer: https://github.com/prometheus/client_rust/blob/master/src/registry.rs#L102
// Note: Please keep consistent with the definitions in MonoVertex daemon
-const MONOVTX_READ_TOTAL: &str = "monovtx_read";
-const MONOVTX_READ_BYTES_TOTAL: &str = "monovtx_read_bytes";
-const MONOVTX_ACK_TOTAL: &str = "monovtx_ack";
-const MONOVTX_SINK_WRITE_TOTAL: &str = "monovtx_sink_write";
-const MONOVTX_PROCESSING_TIME: &str = "monovtx_processing_time";
-const MONOVTX_PENDING: &str = "monovtx_pending";
+const READ_TOTAL: &str = "monovtx_read";
+const READ_BYTES_TOTAL: &str = "monovtx_read_bytes";
+const ACK_TOTAL: &str = "monovtx_ack";
+const SINK_WRITE_TOTAL: &str = "monovtx_sink_write";
+const E2E_PROCESSING_TIME: &str = "monovtx_processing_time";
+const SOURCE_PENDING: &str = "monovtx_pending";
#[derive(Clone)]
pub(crate) struct MetricsState {
@@ -88,12 +88,12 @@ fn global_registry() -> &'static GlobalRegistry {
// The labels are provided in the form of Vec<(String, String)
// The second argument is the metric kind.
pub struct MonoVtxMetrics {
- pub monovtx_read_total: Family, Counter>,
- pub monovtx_read_bytes_total: Family, Counter>,
- pub monovtx_ack_total: Family, Counter>,
- pub monovtx_sink_write_total: Family, Counter>,
- pub monovtx_processing_time: Family, Histogram>,
- pub monovtx_pending: Family, Gauge>,
+ pub read_total: Family, Counter>,
+ pub read_bytes_total: Family, Counter>,
+ pub ack_total: Family, Counter>,
+ pub sink_write_total: Family, Counter>,
+ pub e2e_processing_time: Family, Histogram>,
+ pub source_pending: Family, Gauge>,
}
/// impl the MonoVtxMetrics struct and create a new object
@@ -111,45 +111,45 @@ impl MonoVtxMetrics {
let monovtx_pending = Family::, Gauge>::default();
let metrics = Self {
- monovtx_read_total,
- monovtx_read_bytes_total,
- monovtx_ack_total,
- monovtx_sink_write_total,
- monovtx_processing_time,
- monovtx_pending,
+ read_total: monovtx_read_total,
+ read_bytes_total: monovtx_read_bytes_total,
+ ack_total: monovtx_ack_total,
+ sink_write_total: monovtx_sink_write_total,
+ e2e_processing_time: monovtx_processing_time,
+ source_pending: monovtx_pending,
};
let mut registry = global_registry().registry.lock();
// Register all the metrics to the global registry
registry.register(
- MONOVTX_READ_TOTAL,
+ READ_TOTAL,
"A Counter to keep track of the total number of messages read from the source",
- metrics.monovtx_read_total.clone(),
+ metrics.read_total.clone(),
);
registry.register(
- MONOVTX_SINK_WRITE_TOTAL,
+ SINK_WRITE_TOTAL,
"A Counter to keep track of the total number of messages written to the sink",
- metrics.monovtx_sink_write_total.clone(),
+ metrics.sink_write_total.clone(),
);
registry.register(
- MONOVTX_ACK_TOTAL,
+ ACK_TOTAL,
"A Counter to keep track of the total number of messages acknowledged by the sink",
- metrics.monovtx_ack_total.clone(),
+ metrics.ack_total.clone(),
);
registry.register(
- MONOVTX_PROCESSING_TIME,
+ E2E_PROCESSING_TIME,
"A Histogram to keep track of the total time taken to forward a chunk, the time is in microseconds",
- metrics.monovtx_processing_time.clone(),
+ metrics.e2e_processing_time.clone(),
);
registry.register(
- MONOVTX_READ_BYTES_TOTAL,
+ READ_BYTES_TOTAL,
"A Counter to keep track of the total number of bytes read from the source",
- metrics.monovtx_read_bytes_total.clone(),
+ metrics.read_bytes_total.clone(),
);
registry.register(
- MONOVTX_PENDING,
+ SOURCE_PENDING,
"A Gauge to keep track of the total number of pending messages for the monovtx",
- metrics.monovtx_pending.clone(),
+ metrics.source_pending.clone(),
);
metrics
@@ -177,7 +177,7 @@ pub(crate) fn forward_metrics_labels() -> &'static Vec<(String, String)> {
MONOVTX_METRICS_LABELS.get_or_init(|| {
let common_labels = vec![
(
- MONO_VERTEX_NAME_LABEL.to_string(),
+ VERTEX_NAME_LABEL.to_string(),
config().mono_vertex_name.clone(),
),
(REPLICA_LABEL.to_string(), config().replica.to_string()),
@@ -435,7 +435,7 @@ async fn expose_pending_metrics(
let mut metric_labels = forward_metrics_labels().clone();
metric_labels.push((PENDING_PERIOD_LABEL.to_string(), label.to_string()));
forward_metrics()
- .monovtx_pending
+ .source_pending
.get_or_create(&metric_labels)
.set(pending);
info!("Pending messages ({}): {}", label, pending);
From 35c6f0991d6821b728c82bee6161e265dc2c1ba6 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Thu, 29 Aug 2024 19:42:49 -0700
Subject: [PATCH 036/188] feat: introduce `readyReplicas` for Vertex and
MonoVertex (#2014)
---
api/json-schema/schema.json | 28 +-
api/openapi-spec/swagger.json | 24 +-
config/advanced-install/minimal-crds.yaml | 6 +
.../numaflow.numaproj.io_monovertices.yaml | 8 +-
.../full/numaflow.numaproj.io_vertices.yaml | 9 +-
.../numaflow.numaproj.io_monovertices.yaml | 3 +
.../numaflow.numaproj.io_vertices.yaml | 3 +
config/install.yaml | 17 +-
config/namespace-install.yaml | 17 +-
docs/APIs.md | 100 ++
pkg/apis/numaflow/v1alpha1/generated.pb.go | 973 +++++++++---------
pkg/apis/numaflow/v1alpha1/generated.proto | 42 +
.../numaflow/v1alpha1/mono_vertex_types.go | 33 +-
.../numaflow/v1alpha1/openapi_generated.go | 51 +-
pkg/apis/numaflow/v1alpha1/pipeline_types.go | 33 +-
pkg/apis/numaflow/v1alpha1/vertex_types.go | 30 +-
pkg/metrics/metrics_server.go | 8 +
pkg/reconciler/monovertex/controller.go | 7 +-
pkg/reconciler/util.go | 27 +-
pkg/reconciler/util_test.go | 82 +-
pkg/reconciler/vertex/controller.go | 7 +-
rust/monovertex/src/metrics.rs | 6 +-
.../src/models/mono_vertex_status.rs | 14 +-
.../src/models/pipeline_status.rs | 1 +
.../src/models/vertex_status.rs | 20 +-
25 files changed, 985 insertions(+), 564 deletions(-)
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index 66b8540090..584220c245 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19278,7 +19278,8 @@
"x-kubernetes-patch-strategy": "merge"
},
"lastScaledAt": {
- "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
+ "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time",
+ "description": "Time of last scaling operation."
},
"lastUpdated": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
@@ -19287,16 +19288,23 @@
"type": "string"
},
"observedGeneration": {
+ "description": "The generation observed by the MonoVertex controller.",
"format": "int64",
"type": "integer"
},
"phase": {
"type": "string"
},
+ "readyReplicas": {
+ "description": "The number of pods targeted by this MonoVertex with a Ready Condition.",
+ "format": "int64",
+ "type": "integer"
+ },
"reason": {
"type": "string"
},
"replicas": {
+ "description": "Total number of non-terminated pods targeted by this MonoVertex (their labels match the selector).",
"format": "int64",
"type": "integer"
},
@@ -19304,9 +19312,6 @@
"type": "string"
}
},
- "required": [
- "replicas"
- ],
"type": "object"
},
"io.numaproj.numaflow.v1alpha1.NativeRedis": {
@@ -19654,6 +19659,7 @@
"type": "string"
},
"observedGeneration": {
+ "description": "The generation observed by the Pipeline controller.",
"format": "int64",
"type": "integer"
},
@@ -20542,22 +20548,30 @@
"x-kubernetes-patch-strategy": "merge"
},
"lastScaledAt": {
- "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
+ "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time",
+ "description": "Time of last scaling operation."
},
"message": {
"type": "string"
},
"observedGeneration": {
+ "description": "The generation observed by the Vertex controller.",
"format": "int64",
"type": "integer"
},
"phase": {
"type": "string"
},
+ "readyReplicas": {
+ "description": "The number of pods targeted by this Vertex with a Ready Condition.",
+ "format": "int64",
+ "type": "integer"
+ },
"reason": {
"type": "string"
},
"replicas": {
+ "description": "Total number of non-terminated pods targeted by this Vertex (their labels match the selector).",
"format": "int64",
"type": "integer"
},
@@ -20565,10 +20579,6 @@
"type": "string"
}
},
- "required": [
- "phase",
- "replicas"
- ],
"type": "object"
},
"io.numaproj.numaflow.v1alpha1.VertexTemplate": {
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index a7eff01898..a326712b6c 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19263,9 +19263,6 @@
},
"io.numaproj.numaflow.v1alpha1.MonoVertexStatus": {
"type": "object",
- "required": [
- "replicas"
- ],
"properties": {
"conditions": {
"description": "Conditions are the latest available observations of a resource's current state.",
@@ -19277,6 +19274,7 @@
"x-kubernetes-patch-strategy": "merge"
},
"lastScaledAt": {
+ "description": "Time of last scaling operation.",
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
},
"lastUpdated": {
@@ -19286,16 +19284,23 @@
"type": "string"
},
"observedGeneration": {
+ "description": "The generation observed by the MonoVertex controller.",
"type": "integer",
"format": "int64"
},
"phase": {
"type": "string"
},
+ "readyReplicas": {
+ "description": "The number of pods targeted by this MonoVertex with a Ready Condition.",
+ "type": "integer",
+ "format": "int64"
+ },
"reason": {
"type": "string"
},
"replicas": {
+ "description": "Total number of non-terminated pods targeted by this MonoVertex (their labels match the selector).",
"type": "integer",
"format": "int64"
},
@@ -19641,6 +19646,7 @@
"type": "string"
},
"observedGeneration": {
+ "description": "The generation observed by the Pipeline controller.",
"type": "integer",
"format": "int64"
},
@@ -20509,10 +20515,6 @@
},
"io.numaproj.numaflow.v1alpha1.VertexStatus": {
"type": "object",
- "required": [
- "phase",
- "replicas"
- ],
"properties": {
"conditions": {
"description": "Conditions are the latest available observations of a resource's current state.",
@@ -20524,22 +20526,30 @@
"x-kubernetes-patch-strategy": "merge"
},
"lastScaledAt": {
+ "description": "Time of last scaling operation.",
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
},
"message": {
"type": "string"
},
"observedGeneration": {
+ "description": "The generation observed by the Vertex controller.",
"type": "integer",
"format": "int64"
},
"phase": {
"type": "string"
},
+ "readyReplicas": {
+ "description": "The number of pods targeted by this Vertex with a Ready Condition.",
+ "type": "integer",
+ "format": "int64"
+ },
"reason": {
"type": "string"
},
"replicas": {
+ "description": "Total number of non-terminated pods targeted by this Vertex (their labels match the selector).",
"type": "integer",
"format": "int64"
},
diff --git a/config/advanced-install/minimal-crds.yaml b/config/advanced-install/minimal-crds.yaml
index 9d27719000..3e647ee3d6 100644
--- a/config/advanced-install/minimal-crds.yaml
+++ b/config/advanced-install/minimal-crds.yaml
@@ -75,6 +75,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -209,6 +212,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index a26426074f..d456bbb249 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -27,6 +27,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -5551,6 +5554,9 @@ spec:
- Paused
- Deleting
type: string
+ readyReplicas:
+ format: int32
+ type: integer
reason:
type: string
replicas:
@@ -5558,8 +5564,6 @@ spec:
type: integer
selector:
type: string
- required:
- - replicas
type: object
required:
- spec
diff --git a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
index d1a77b9375..7973694b88 100644
--- a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
@@ -27,6 +27,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -5480,6 +5483,9 @@ spec:
- Running
- Failed
type: string
+ readyReplicas:
+ format: int32
+ type: integer
reason:
type: string
replicas:
@@ -5487,9 +5493,6 @@ spec:
type: integer
selector:
type: string
- required:
- - phase
- - replicas
type: object
required:
- spec
diff --git a/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
index ac33f527a9..65cb6b2652 100644
--- a/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
@@ -23,6 +23,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml b/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml
index b0f1c1ba0f..68a95ee056 100644
--- a/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml
@@ -23,6 +23,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/install.yaml b/config/install.yaml
index 575bac0cc3..a648c1413e 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -2671,6 +2671,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -8195,6 +8198,9 @@ spec:
- Paused
- Deleting
type: string
+ readyReplicas:
+ format: int32
+ type: integer
reason:
type: string
replicas:
@@ -8202,8 +8208,6 @@ spec:
type: integer
selector:
type: string
- required:
- - replicas
type: object
required:
- spec
@@ -18108,6 +18112,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -23561,6 +23568,9 @@ spec:
- Running
- Failed
type: string
+ readyReplicas:
+ format: int32
+ type: integer
reason:
type: string
replicas:
@@ -23568,9 +23578,6 @@ spec:
type: integer
selector:
type: string
- required:
- - phase
- - replicas
type: object
required:
- spec
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 3b3737b3a9..a922d0f7c5 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -2671,6 +2671,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -8195,6 +8198,9 @@ spec:
- Paused
- Deleting
type: string
+ readyReplicas:
+ format: int32
+ type: integer
reason:
type: string
replicas:
@@ -8202,8 +8208,6 @@ spec:
type: integer
selector:
type: string
- required:
- - replicas
type: object
required:
- spec
@@ -18108,6 +18112,9 @@ spec:
- jsonPath: .status.replicas
name: Current
type: string
+ - jsonPath: .status.readyReplicas
+ name: Ready
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -23561,6 +23568,9 @@ spec:
- Running
- Failed
type: string
+ readyReplicas:
+ format: int32
+ type: integer
reason:
type: string
replicas:
@@ -23568,9 +23578,6 @@ spec:
type: integer
selector:
type: string
- required:
- - phase
- - replicas
type: object
required:
- spec
diff --git a/docs/APIs.md b/docs/APIs.md
index 46a865e83b..b5ba81450d 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -6142,6 +6142,7 @@ MonoVertexPhase
+(Optional)
@@ -6155,6 +6156,13 @@ MonoVertexPhase
+(Optional)
+
+
+Total number of non-terminated pods targeted by this MonoVertex (their
+labels match the selector).
+
+
@@ -6168,6 +6176,7 @@ MonoVertexPhase
+(Optional)
@@ -6181,6 +6190,7 @@ MonoVertexPhase
+(Optional)
@@ -6194,6 +6204,7 @@ MonoVertexPhase
+(Optional)
@@ -6209,6 +6220,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -6224,6 +6236,12 @@ Kubernetes meta/v1.Time
+(Optional)
+
+
+Time of last scaling operation.
+
+
@@ -6237,6 +6255,31 @@ Kubernetes meta/v1.Time
+(Optional)
+
+
+The generation observed by the MonoVertex controller.
+
+
+
+
+
+
+
+
+
+
+readyReplicas
uint32
+
+
+
+
+(Optional)
+
+
+The number of pods targeted by this MonoVertex with a Ready Condition.
+
+
@@ -7579,6 +7622,7 @@ Description
+(Optional)
@@ -7592,6 +7636,7 @@ Description
+(Optional)
@@ -7607,6 +7652,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -7620,6 +7666,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -7633,6 +7680,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -7646,6 +7694,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -7659,6 +7708,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -7672,6 +7722,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -7685,6 +7736,7 @@ Kubernetes meta/v1.Time
+(Optional)
@@ -7698,6 +7750,12 @@ Kubernetes meta/v1.Time
+(Optional)
+
+
+The generation observed by the Pipeline controller.
+
+
@@ -10941,6 +10999,7 @@ Description
+(Optional)
@@ -10954,6 +11013,13 @@ Description
+(Optional)
+
+
+Total number of non-terminated pods targeted by this Vertex (their
+labels match the selector).
+
+
@@ -10967,6 +11033,7 @@ Description
+(Optional)
@@ -10980,6 +11047,7 @@ Description
+(Optional)
@@ -10993,6 +11061,7 @@ Description
+(Optional)
@@ -11008,6 +11077,12 @@ Kubernetes meta/v1.Time
+(Optional)
+
+
+Time of last scaling operation.
+
+
@@ -11021,6 +11096,31 @@ Kubernetes meta/v1.Time
+(Optional)
+
+
+The generation observed by the Vertex controller.
+
+
+
+
+
+
+
+
+
+
+readyReplicas
uint32
+
+
+
+
+(Optional)
+
+
+The number of pods targeted by this Vertex with a Ready Condition.
+
+
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index d905171bec..3a29fb484b 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -2704,474 +2704,475 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7464 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x1d, 0xd7,
- 0x75, 0xae, 0xcf, 0x1f, 0x79, 0xce, 0x3a, 0x24, 0x45, 0x6d, 0xc9, 0x32, 0x25, 0xcb, 0xa2, 0x32,
- 0x8e, 0x7d, 0x95, 0x9b, 0x84, 0xbc, 0xe6, 0xf5, 0x5f, 0x72, 0x93, 0xd8, 0x3c, 0xa4, 0x48, 0x51,
- 0x22, 0x25, 0x66, 0x1d, 0x52, 0x76, 0xe2, 0x9b, 0xf8, 0x0e, 0x67, 0x36, 0x0f, 0xc7, 0x9c, 0x33,
- 0x73, 0x3c, 0x33, 0x87, 0x12, 0x9d, 0x7b, 0x91, 0xbf, 0x07, 0xfb, 0xa2, 0x2d, 0x5a, 0xe4, 0x29,
- 0x40, 0x91, 0x16, 0x2d, 0x0a, 0xe4, 0x21, 0x48, 0x1f, 0x0a, 0xb8, 0x0f, 0x05, 0xfa, 0x93, 0xa2,
- 0x68, 0xd3, 0xa2, 0x3f, 0x79, 0x28, 0x50, 0xf7, 0x85, 0x68, 0x58, 0xf4, 0xa1, 0x05, 0x1a, 0x04,
- 0x0d, 0xd0, 0x24, 0x42, 0x80, 0x14, 0xfb, 0x6f, 0xfe, 0xce, 0x1c, 0x89, 0x3c, 0x43, 0xca, 0x72,
- 0xeb, 0xb7, 0x99, 0xbd, 0xd7, 0xfe, 0xd6, 0xde, 0x6b, 0xf6, 0xcf, 0xda, 0x6b, 0xaf, 0xbd, 0x06,
- 0x16, 0x5b, 0x56, 0xb0, 0xd5, 0xdd, 0x98, 0x32, 0xdc, 0xf6, 0xb4, 0xd3, 0x6d, 0xeb, 0x1d, 0xcf,
- 0x7d, 0x8d, 0x3f, 0x6c, 0xda, 0xee, 0xad, 0xe9, 0xce, 0x76, 0x6b, 0x5a, 0xef, 0x58, 0x7e, 0x94,
- 0xb2, 0xf3, 0x94, 0x6e, 0x77, 0xb6, 0xf4, 0xa7, 0xa6, 0x5b, 0xd4, 0xa1, 0x9e, 0x1e, 0x50, 0x73,
- 0xaa, 0xe3, 0xb9, 0x81, 0x4b, 0x9e, 0x8b, 0x80, 0xa6, 0x14, 0xd0, 0x94, 0x2a, 0x36, 0xd5, 0xd9,
- 0x6e, 0x4d, 0x31, 0xa0, 0x28, 0x45, 0x01, 0x9d, 0xfb, 0x68, 0xac, 0x06, 0x2d, 0xb7, 0xe5, 0x4e,
- 0x73, 0xbc, 0x8d, 0xee, 0x26, 0x7f, 0xe3, 0x2f, 0xfc, 0x49, 0xf0, 0x39, 0xa7, 0x6d, 0x3f, 0xef,
- 0x4f, 0x59, 0x2e, 0xab, 0xd6, 0xb4, 0xe1, 0x7a, 0x74, 0x7a, 0xa7, 0xa7, 0x2e, 0xe7, 0x9e, 0x8e,
- 0x68, 0xda, 0xba, 0xb1, 0x65, 0x39, 0xd4, 0xdb, 0x55, 0x6d, 0x99, 0xf6, 0xa8, 0xef, 0x76, 0x3d,
- 0x83, 0x1e, 0xaa, 0x94, 0x3f, 0xdd, 0xa6, 0x81, 0x9e, 0xc5, 0x6b, 0xba, 0x5f, 0x29, 0xaf, 0xeb,
- 0x04, 0x56, 0xbb, 0x97, 0xcd, 0xb3, 0xf7, 0x2a, 0xe0, 0x1b, 0x5b, 0xb4, 0xad, 0xa7, 0xcb, 0x69,
- 0xdf, 0x01, 0x38, 0x35, 0xbb, 0xe1, 0x07, 0x9e, 0x6e, 0x04, 0xab, 0xae, 0xb9, 0x46, 0xdb, 0x1d,
- 0x5b, 0x0f, 0x28, 0xd9, 0x86, 0x2a, 0xab, 0x9b, 0xa9, 0x07, 0xfa, 0x44, 0xe1, 0x62, 0xe1, 0x52,
- 0x7d, 0x66, 0x76, 0x6a, 0xc0, 0x6f, 0x31, 0xb5, 0x22, 0x81, 0x1a, 0x23, 0xfb, 0x7b, 0x93, 0x55,
- 0xf5, 0x86, 0x21, 0x03, 0xf2, 0xf5, 0x02, 0x8c, 0x38, 0xae, 0x49, 0x9b, 0xd4, 0xa6, 0x46, 0xe0,
- 0x7a, 0x13, 0xc5, 0x8b, 0xa5, 0x4b, 0xf5, 0x99, 0xcf, 0x0f, 0xcc, 0x31, 0xa3, 0x45, 0x53, 0xd7,
- 0x63, 0x0c, 0x2e, 0x3b, 0x81, 0xb7, 0xdb, 0x38, 0xfd, 0xdd, 0xbd, 0xc9, 0x87, 0xf6, 0xf7, 0x26,
- 0x47, 0xe2, 0x59, 0x98, 0xa8, 0x09, 0x59, 0x87, 0x7a, 0xe0, 0xda, 0x4c, 0x64, 0x96, 0xeb, 0xf8,
- 0x13, 0x25, 0x5e, 0xb1, 0x0b, 0x53, 0x42, 0xda, 0x8c, 0xfd, 0x14, 0xeb, 0x2e, 0x53, 0x3b, 0x4f,
- 0x4d, 0xad, 0x85, 0x64, 0x8d, 0x53, 0x12, 0xb8, 0x1e, 0xa5, 0xf9, 0x18, 0xc7, 0x21, 0x14, 0x4e,
- 0xf8, 0xd4, 0xe8, 0x7a, 0x56, 0xb0, 0x3b, 0xe7, 0x3a, 0x01, 0xbd, 0x1d, 0x4c, 0x94, 0xb9, 0x94,
- 0x9f, 0xcc, 0x82, 0x5e, 0x75, 0xcd, 0x66, 0x92, 0xba, 0x71, 0x6a, 0x7f, 0x6f, 0xf2, 0x44, 0x2a,
- 0x11, 0xd3, 0x98, 0xc4, 0x81, 0x71, 0xab, 0xad, 0xb7, 0xe8, 0x6a, 0xd7, 0xb6, 0x9b, 0xd4, 0xf0,
- 0x68, 0xe0, 0x4f, 0x54, 0x78, 0x13, 0x2e, 0x65, 0xf1, 0x59, 0x76, 0x0d, 0xdd, 0xbe, 0xb1, 0xf1,
- 0x1a, 0x35, 0x02, 0xa4, 0x9b, 0xd4, 0xa3, 0x8e, 0x41, 0x1b, 0x13, 0xb2, 0x31, 0xe3, 0x4b, 0x29,
- 0x24, 0xec, 0xc1, 0x26, 0x8b, 0x70, 0xb2, 0xe3, 0x59, 0x2e, 0xaf, 0x82, 0xad, 0xfb, 0xfe, 0x75,
- 0xbd, 0x4d, 0x27, 0x86, 0x2e, 0x16, 0x2e, 0xd5, 0x1a, 0x67, 0x25, 0xcc, 0xc9, 0xd5, 0x34, 0x01,
- 0xf6, 0x96, 0x21, 0x97, 0xa0, 0xaa, 0x12, 0x27, 0x86, 0x2f, 0x16, 0x2e, 0x55, 0x44, 0xdf, 0x51,
- 0x65, 0x31, 0xcc, 0x25, 0x0b, 0x50, 0xd5, 0x37, 0x37, 0x2d, 0x87, 0x51, 0x56, 0xb9, 0x08, 0xcf,
- 0x67, 0x35, 0x6d, 0x56, 0xd2, 0x08, 0x1c, 0xf5, 0x86, 0x61, 0x59, 0x72, 0x15, 0x88, 0x4f, 0xbd,
- 0x1d, 0xcb, 0xa0, 0xb3, 0x86, 0xe1, 0x76, 0x9d, 0x80, 0xd7, 0xbd, 0xc6, 0xeb, 0x7e, 0x4e, 0xd6,
- 0x9d, 0x34, 0x7b, 0x28, 0x30, 0xa3, 0x14, 0x79, 0x11, 0xc6, 0xe5, 0xb0, 0x8b, 0xa4, 0x00, 0x1c,
- 0xe9, 0x34, 0x13, 0x24, 0xa6, 0xf2, 0xb0, 0x87, 0x9a, 0x98, 0x70, 0x5e, 0xef, 0x06, 0x6e, 0x9b,
- 0x41, 0x26, 0x99, 0xae, 0xb9, 0xdb, 0xd4, 0x99, 0xa8, 0x5f, 0x2c, 0x5c, 0xaa, 0x36, 0x2e, 0xee,
- 0xef, 0x4d, 0x9e, 0x9f, 0xbd, 0x0b, 0x1d, 0xde, 0x15, 0x85, 0xdc, 0x80, 0x9a, 0xe9, 0xf8, 0xab,
- 0xae, 0x6d, 0x19, 0xbb, 0x13, 0x23, 0xbc, 0x82, 0x4f, 0xc9, 0xa6, 0xd6, 0xe6, 0xaf, 0x37, 0x45,
- 0xc6, 0x9d, 0xbd, 0xc9, 0xf3, 0xbd, 0xb3, 0xe3, 0x54, 0x98, 0x8f, 0x11, 0x06, 0x59, 0xe1, 0x80,
- 0x73, 0xae, 0xb3, 0x69, 0xb5, 0x26, 0x46, 0xf9, 0xd7, 0xb8, 0xd8, 0xa7, 0x43, 0xcf, 0x5f, 0x6f,
- 0x0a, 0xba, 0xc6, 0xa8, 0x64, 0x27, 0x5e, 0x31, 0x42, 0x20, 0x26, 0x8c, 0xa9, 0x79, 0x75, 0xce,
- 0xd6, 0xad, 0xb6, 0x3f, 0x31, 0xc6, 0x3b, 0xef, 0x07, 0xfb, 0x60, 0x62, 0x9c, 0xb8, 0x71, 0x46,
- 0x36, 0x65, 0x2c, 0x91, 0xec, 0x63, 0x0a, 0xf3, 0xdc, 0x0b, 0x70, 0xb2, 0x67, 0x6e, 0x20, 0xe3,
- 0x50, 0xda, 0xa6, 0xbb, 0x7c, 0xea, 0xab, 0x21, 0x7b, 0x24, 0xa7, 0xa1, 0xb2, 0xa3, 0xdb, 0x5d,
- 0x3a, 0x51, 0xe4, 0x69, 0xe2, 0xe5, 0xe3, 0xc5, 0xe7, 0x0b, 0xda, 0x6f, 0x96, 0x60, 0x44, 0xcd,
- 0x38, 0x4d, 0xcb, 0xd9, 0x26, 0x2f, 0x41, 0xc9, 0x76, 0x5b, 0x72, 0xde, 0xfc, 0xc4, 0xc0, 0xb3,
- 0xd8, 0xb2, 0xdb, 0x6a, 0x0c, 0xef, 0xef, 0x4d, 0x96, 0x96, 0xdd, 0x16, 0x32, 0x44, 0x62, 0x40,
- 0x65, 0x5b, 0xdf, 0xdc, 0xd6, 0x79, 0x1d, 0xea, 0x33, 0x8d, 0x81, 0xa1, 0xaf, 0x31, 0x14, 0x56,
- 0xd7, 0x46, 0x6d, 0x7f, 0x6f, 0xb2, 0xc2, 0x5f, 0x51, 0x60, 0x13, 0x17, 0x6a, 0x1b, 0xb6, 0x6e,
- 0x6c, 0x6f, 0xb9, 0x36, 0x9d, 0x28, 0xe5, 0x64, 0xd4, 0x50, 0x48, 0xe2, 0x33, 0x87, 0xaf, 0x18,
- 0xf1, 0x20, 0x06, 0x0c, 0x75, 0x4d, 0xdf, 0x72, 0xb6, 0xe5, 0x1c, 0xf8, 0xc2, 0xc0, 0xdc, 0xd6,
- 0xe7, 0x79, 0x9b, 0x60, 0x7f, 0x6f, 0x72, 0x48, 0x3c, 0xa3, 0x84, 0xd6, 0x7e, 0x50, 0x87, 0x31,
- 0xf5, 0x91, 0x6e, 0x52, 0x2f, 0xa0, 0xb7, 0xc9, 0x45, 0x28, 0x3b, 0x6c, 0x68, 0xf2, 0x8f, 0xdc,
- 0x18, 0x91, 0xdd, 0xa5, 0xcc, 0x87, 0x24, 0xcf, 0x61, 0x35, 0x13, 0x5d, 0x45, 0x0a, 0x7c, 0xf0,
- 0x9a, 0x35, 0x39, 0x8c, 0xa8, 0x99, 0x78, 0x46, 0x09, 0x4d, 0x5e, 0x81, 0x32, 0x6f, 0xbc, 0x10,
- 0xf5, 0x27, 0x07, 0x67, 0xc1, 0x9a, 0x5e, 0x65, 0x2d, 0xe0, 0x0d, 0xe7, 0xa0, 0xac, 0x2b, 0x76,
- 0xcd, 0x4d, 0x29, 0xd8, 0x4f, 0xe4, 0x10, 0xec, 0x82, 0xe8, 0x8a, 0xeb, 0xf3, 0x0b, 0xc8, 0x10,
- 0xc9, 0x2f, 0x17, 0xe0, 0xa4, 0xe1, 0x3a, 0x81, 0xce, 0x54, 0x0d, 0xb5, 0xc8, 0x4e, 0x54, 0x38,
+ // 7488 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x1d, 0xd7,
+ 0x79, 0xae, 0xf7, 0x8d, 0xdc, 0xfb, 0xdf, 0x24, 0x45, 0x2d, 0xc9, 0x32, 0x25, 0xcb, 0xa2, 0x32,
+ 0x8e, 0x7d, 0x94, 0x93, 0x84, 0x3c, 0xe6, 0xf1, 0x2d, 0x57, 0x9b, 0x9b, 0x14, 0x29, 0x4a, 0xa4,
+ 0xc4, 0xfc, 0x9b, 0x94, 0x9d, 0xf8, 0x24, 0x3e, 0xc3, 0x99, 0xc5, 0xcd, 0x31, 0x67, 0xcf, 0x6c,
+ 0xcf, 0xcc, 0xa6, 0x44, 0xe7, 0x1c, 0xe4, 0xf6, 0x60, 0x1f, 0x1c, 0x1c, 0x9c, 0x22, 0x4f, 0x01,
+ 0x8a, 0xb4, 0x68, 0x51, 0x20, 0x0f, 0x41, 0xfa, 0x50, 0xd4, 0x7d, 0x28, 0xd0, 0x4b, 0x8a, 0xa2,
+ 0x4d, 0x8a, 0x5e, 0xf2, 0x50, 0xa0, 0xee, 0x0b, 0xd1, 0xb0, 0xe8, 0x43, 0x0b, 0x34, 0x08, 0x1a,
+ 0xa0, 0x4d, 0x84, 0x00, 0x29, 0xd6, 0x6d, 0x6e, 0x7b, 0xb6, 0x44, 0xee, 0x21, 0x65, 0xb9, 0xf5,
+ 0xdb, 0xcc, 0x5a, 0xff, 0xfa, 0xfe, 0x35, 0xff, 0xba, 0xfd, 0xeb, 0x5f, 0xff, 0xfa, 0x07, 0x16,
+ 0x5b, 0x56, 0xb0, 0xd5, 0xdd, 0x98, 0x32, 0xdc, 0xf6, 0xb4, 0xd3, 0x6d, 0xeb, 0x1d, 0xcf, 0x7d,
+ 0x8d, 0x3f, 0x6c, 0xda, 0xee, 0xad, 0xe9, 0xce, 0x76, 0x6b, 0x5a, 0xef, 0x58, 0x7e, 0x94, 0xb2,
+ 0xf3, 0x94, 0x6e, 0x77, 0xb6, 0xf4, 0xa7, 0xa6, 0x5b, 0xd4, 0xa1, 0x9e, 0x1e, 0x50, 0x73, 0xaa,
+ 0xe3, 0xb9, 0x81, 0x4b, 0x9e, 0x8b, 0x80, 0xa6, 0x14, 0xd0, 0x94, 0x2a, 0x36, 0xd5, 0xd9, 0x6e,
+ 0x4d, 0x31, 0xa0, 0x28, 0x45, 0x01, 0x9d, 0xfb, 0x68, 0xac, 0x06, 0x2d, 0xb7, 0xe5, 0x4e, 0x73,
+ 0xbc, 0x8d, 0xee, 0x26, 0x7f, 0xe3, 0x2f, 0xfc, 0x49, 0xf0, 0x39, 0xa7, 0x6d, 0x3f, 0xef, 0x4f,
+ 0x59, 0x2e, 0xab, 0xd6, 0xb4, 0xe1, 0x7a, 0x74, 0x7a, 0xa7, 0xa7, 0x2e, 0xe7, 0x9e, 0x8e, 0x68,
+ 0xda, 0xba, 0xb1, 0x65, 0x39, 0xd4, 0xdb, 0x55, 0xdf, 0x32, 0xed, 0x51, 0xdf, 0xed, 0x7a, 0x06,
+ 0x3d, 0x54, 0x29, 0x7f, 0xba, 0x4d, 0x03, 0x3d, 0x8b, 0xd7, 0x74, 0xbf, 0x52, 0x5e, 0xd7, 0x09,
+ 0xac, 0x76, 0x2f, 0x9b, 0x67, 0xef, 0x55, 0xc0, 0x37, 0xb6, 0x68, 0x5b, 0x4f, 0x97, 0xd3, 0xbe,
+ 0x0b, 0x70, 0x6a, 0x76, 0xc3, 0x0f, 0x3c, 0xdd, 0x08, 0x56, 0x5d, 0x73, 0x8d, 0xb6, 0x3b, 0xb6,
+ 0x1e, 0x50, 0xb2, 0x0d, 0x55, 0x56, 0x37, 0x53, 0x0f, 0xf4, 0x89, 0xc2, 0xc5, 0xc2, 0xa5, 0xfa,
+ 0xcc, 0xec, 0xd4, 0x80, 0x6d, 0x31, 0xb5, 0x22, 0x81, 0x1a, 0x23, 0xfb, 0x7b, 0x93, 0x55, 0xf5,
+ 0x86, 0x21, 0x03, 0xf2, 0x8d, 0x02, 0x8c, 0x38, 0xae, 0x49, 0x9b, 0xd4, 0xa6, 0x46, 0xe0, 0x7a,
+ 0x13, 0xc5, 0x8b, 0xa5, 0x4b, 0xf5, 0x99, 0x2f, 0x0c, 0xcc, 0x31, 0xe3, 0x8b, 0xa6, 0xae, 0xc7,
+ 0x18, 0x5c, 0x76, 0x02, 0x6f, 0xb7, 0x71, 0xfa, 0x7b, 0x7b, 0x93, 0x0f, 0xed, 0xef, 0x4d, 0x8e,
+ 0xc4, 0xb3, 0x30, 0x51, 0x13, 0xb2, 0x0e, 0xf5, 0xc0, 0xb5, 0x99, 0xc8, 0x2c, 0xd7, 0xf1, 0x27,
+ 0x4a, 0xbc, 0x62, 0x17, 0xa6, 0x84, 0xb4, 0x19, 0xfb, 0x29, 0xd6, 0x5d, 0xa6, 0x76, 0x9e, 0x9a,
+ 0x5a, 0x0b, 0xc9, 0x1a, 0xa7, 0x24, 0x70, 0x3d, 0x4a, 0xf3, 0x31, 0x8e, 0x43, 0x28, 0x9c, 0xf0,
+ 0xa9, 0xd1, 0xf5, 0xac, 0x60, 0x77, 0xce, 0x75, 0x02, 0x7a, 0x3b, 0x98, 0x28, 0x73, 0x29, 0x3f,
+ 0x99, 0x05, 0xbd, 0xea, 0x9a, 0xcd, 0x24, 0x75, 0xe3, 0xd4, 0xfe, 0xde, 0xe4, 0x89, 0x54, 0x22,
+ 0xa6, 0x31, 0x89, 0x03, 0xe3, 0x56, 0x5b, 0x6f, 0xd1, 0xd5, 0xae, 0x6d, 0x37, 0xa9, 0xe1, 0xd1,
+ 0xc0, 0x9f, 0xa8, 0xf0, 0x4f, 0xb8, 0x94, 0xc5, 0x67, 0xd9, 0x35, 0x74, 0xfb, 0xc6, 0xc6, 0x6b,
+ 0xd4, 0x08, 0x90, 0x6e, 0x52, 0x8f, 0x3a, 0x06, 0x6d, 0x4c, 0xc8, 0x8f, 0x19, 0x5f, 0x4a, 0x21,
+ 0x61, 0x0f, 0x36, 0x59, 0x84, 0x93, 0x1d, 0xcf, 0x72, 0x79, 0x15, 0x6c, 0xdd, 0xf7, 0xaf, 0xeb,
+ 0x6d, 0x3a, 0x31, 0x74, 0xb1, 0x70, 0xa9, 0xd6, 0x38, 0x2b, 0x61, 0x4e, 0xae, 0xa6, 0x09, 0xb0,
+ 0xb7, 0x0c, 0xb9, 0x04, 0x55, 0x95, 0x38, 0x31, 0x7c, 0xb1, 0x70, 0xa9, 0x22, 0xfa, 0x8e, 0x2a,
+ 0x8b, 0x61, 0x2e, 0x59, 0x80, 0xaa, 0xbe, 0xb9, 0x69, 0x39, 0x8c, 0xb2, 0xca, 0x45, 0x78, 0x3e,
+ 0xeb, 0xd3, 0x66, 0x25, 0x8d, 0xc0, 0x51, 0x6f, 0x18, 0x96, 0x25, 0x57, 0x81, 0xf8, 0xd4, 0xdb,
+ 0xb1, 0x0c, 0x3a, 0x6b, 0x18, 0x6e, 0xd7, 0x09, 0x78, 0xdd, 0x6b, 0xbc, 0xee, 0xe7, 0x64, 0xdd,
+ 0x49, 0xb3, 0x87, 0x02, 0x33, 0x4a, 0x91, 0x17, 0x61, 0x5c, 0x0e, 0xbb, 0x48, 0x0a, 0xc0, 0x91,
+ 0x4e, 0x33, 0x41, 0x62, 0x2a, 0x0f, 0x7b, 0xa8, 0x89, 0x09, 0xe7, 0xf5, 0x6e, 0xe0, 0xb6, 0x19,
+ 0x64, 0x92, 0xe9, 0x9a, 0xbb, 0x4d, 0x9d, 0x89, 0xfa, 0xc5, 0xc2, 0xa5, 0x6a, 0xe3, 0xe2, 0xfe,
+ 0xde, 0xe4, 0xf9, 0xd9, 0xbb, 0xd0, 0xe1, 0x5d, 0x51, 0xc8, 0x0d, 0xa8, 0x99, 0x8e, 0xbf, 0xea,
+ 0xda, 0x96, 0xb1, 0x3b, 0x31, 0xc2, 0x2b, 0xf8, 0x94, 0xfc, 0xd4, 0xda, 0xfc, 0xf5, 0xa6, 0xc8,
+ 0xb8, 0xb3, 0x37, 0x79, 0xbe, 0x77, 0x76, 0x9c, 0x0a, 0xf3, 0x31, 0xc2, 0x20, 0x2b, 0x1c, 0x70,
+ 0xce, 0x75, 0x36, 0xad, 0xd6, 0xc4, 0x28, 0x6f, 0x8d, 0x8b, 0x7d, 0x3a, 0xf4, 0xfc, 0xf5, 0xa6,
+ 0xa0, 0x6b, 0x8c, 0x4a, 0x76, 0xe2, 0x15, 0x23, 0x04, 0x62, 0xc2, 0x98, 0x9a, 0x57, 0xe7, 0x6c,
+ 0xdd, 0x6a, 0xfb, 0x13, 0x63, 0xbc, 0xf3, 0x7e, 0xb0, 0x0f, 0x26, 0xc6, 0x89, 0x1b, 0x67, 0xe4,
+ 0xa7, 0x8c, 0x25, 0x92, 0x7d, 0x4c, 0x61, 0x9e, 0x7b, 0x01, 0x4e, 0xf6, 0xcc, 0x0d, 0x64, 0x1c,
+ 0x4a, 0xdb, 0x74, 0x97, 0x4f, 0x7d, 0x35, 0x64, 0x8f, 0xe4, 0x34, 0x54, 0x76, 0x74, 0xbb, 0x4b,
+ 0x27, 0x8a, 0x3c, 0x4d, 0xbc, 0x7c, 0xbc, 0xf8, 0x7c, 0x41, 0xfb, 0xf5, 0x12, 0x8c, 0xa8, 0x19,
+ 0xa7, 0x69, 0x39, 0xdb, 0xe4, 0x25, 0x28, 0xd9, 0x6e, 0x4b, 0xce, 0x9b, 0x9f, 0x1c, 0x78, 0x16,
+ 0x5b, 0x76, 0x5b, 0x8d, 0xe1, 0xfd, 0xbd, 0xc9, 0xd2, 0xb2, 0xdb, 0x42, 0x86, 0x48, 0x0c, 0xa8,
+ 0x6c, 0xeb, 0x9b, 0xdb, 0x3a, 0xaf, 0x43, 0x7d, 0xa6, 0x31, 0x30, 0xf4, 0x35, 0x86, 0xc2, 0xea,
+ 0xda, 0xa8, 0xed, 0xef, 0x4d, 0x56, 0xf8, 0x2b, 0x0a, 0x6c, 0xe2, 0x42, 0x6d, 0xc3, 0xd6, 0x8d,
+ 0xed, 0x2d, 0xd7, 0xa6, 0x13, 0xa5, 0x9c, 0x8c, 0x1a, 0x0a, 0x49, 0x34, 0x73, 0xf8, 0x8a, 0x11,
+ 0x0f, 0x62, 0xc0, 0x50, 0xd7, 0xf4, 0x2d, 0x67, 0x5b, 0xce, 0x81, 0x2f, 0x0c, 0xcc, 0x6d, 0x7d,
+ 0x9e, 0x7f, 0x13, 0xec, 0xef, 0x4d, 0x0e, 0x89, 0x67, 0x94, 0xd0, 0xda, 0x8f, 0xea, 0x30, 0xa6,
+ 0x1a, 0xe9, 0x26, 0xf5, 0x02, 0x7a, 0x9b, 0x5c, 0x84, 0xb2, 0xc3, 0x86, 0x26, 0x6f, 0xe4, 0xc6,
+ 0x88, 0xec, 0x2e, 0x65, 0x3e, 0x24, 0x79, 0x0e, 0xab, 0x99, 0xe8, 0x2a, 0x52, 0xe0, 0x83, 0xd7,
+ 0xac, 0xc9, 0x61, 0x44, 0xcd, 0xc4, 0x33, 0x4a, 0x68, 0xf2, 0x0a, 0x94, 0xf9, 0xc7, 0x0b, 0x51,
+ 0x7f, 0x6a, 0x70, 0x16, 0xec, 0xd3, 0xab, 0xec, 0x0b, 0xf8, 0x87, 0x73, 0x50, 0xd6, 0x15, 0xbb,
+ 0xe6, 0xa6, 0x14, 0xec, 0x27, 0x73, 0x08, 0x76, 0x41, 0x74, 0xc5, 0xf5, 0xf9, 0x05, 0x64, 0x88,
+ 0xe4, 0xff, 0x17, 0xe0, 0xa4, 0xe1, 0x3a, 0x81, 0xce, 0x54, 0x0d, 0xb5, 0xc8, 0x4e, 0x54, 0x38,
0x9f, 0xab, 0x03, 0xf3, 0x99, 0x4b, 0x23, 0x36, 0x1e, 0x66, 0x6b, 0x46, 0x4f, 0x32, 0xf6, 0xf2,
- 0x26, 0xbf, 0x5a, 0x80, 0x87, 0xd9, 0x5c, 0xde, 0x43, 0xcc, 0x57, 0xa0, 0xa3, 0xad, 0xd5, 0xd9,
+ 0x26, 0xbf, 0x5c, 0x80, 0x87, 0xd9, 0x5c, 0xde, 0x43, 0xcc, 0x57, 0xa0, 0xa3, 0xad, 0xd5, 0xd9,
0xfd, 0xbd, 0xc9, 0x87, 0x97, 0xb2, 0x98, 0x61, 0x76, 0x1d, 0x58, 0xed, 0x4e, 0xe9, 0xbd, 0x6a,
0x09, 0x5f, 0xdd, 0xea, 0x33, 0xcb, 0x47, 0xa9, 0xea, 0x34, 0x1e, 0x95, 0x5d, 0x39, 0x4b, 0xb3,
0xc3, 0xac, 0x5a, 0x90, 0xcb, 0x30, 0xbc, 0xe3, 0xda, 0xdd, 0x36, 0xf5, 0x27, 0xaa, 0x7c, 0x8a,
0x3d, 0x97, 0x35, 0xc5, 0xde, 0xe4, 0x24, 0x8d, 0x13, 0x12, 0x7e, 0x58, 0xbc, 0xfb, 0xa8, 0xca,
- 0x12, 0x0b, 0x86, 0x6c, 0xab, 0x6d, 0x05, 0x3e, 0x5f, 0x38, 0xeb, 0x33, 0x97, 0x07, 0x6e, 0x96,
- 0x18, 0xa2, 0xcb, 0x1c, 0x4c, 0x8c, 0x1a, 0xf1, 0x8c, 0x92, 0x01, 0x9b, 0x0a, 0x7d, 0x43, 0xb7,
- 0xc5, 0xc2, 0x5a, 0x9f, 0xf9, 0xd4, 0xe0, 0xc3, 0x86, 0xa1, 0x34, 0x46, 0x65, 0x9b, 0x2a, 0xfc,
- 0x15, 0x05, 0x36, 0xf9, 0x1c, 0x8c, 0x25, 0xbe, 0xa6, 0x3f, 0x51, 0xe7, 0xd2, 0x79, 0x2c, 0x4b,
- 0x3a, 0x21, 0x55, 0xb4, 0xf2, 0x24, 0x7a, 0x88, 0x8f, 0x29, 0x30, 0x72, 0x0d, 0xaa, 0xbe, 0x65,
- 0x52, 0x43, 0xf7, 0xfc, 0x89, 0x91, 0x83, 0x00, 0x8f, 0x4b, 0xe0, 0x6a, 0x53, 0x16, 0xc3, 0x10,
- 0x80, 0x4c, 0x01, 0x74, 0x74, 0x2f, 0xb0, 0x84, 0xa2, 0x3a, 0xca, 0x95, 0xa6, 0xb1, 0xfd, 0xbd,
- 0x49, 0x58, 0x0d, 0x53, 0x31, 0x46, 0xc1, 0xe8, 0x59, 0xd9, 0x25, 0xa7, 0xd3, 0x0d, 0xc4, 0xc2,
- 0x5a, 0x13, 0xf4, 0xcd, 0x30, 0x15, 0x63, 0x14, 0xe4, 0xdb, 0x05, 0x78, 0x34, 0x7a, 0xed, 0x1d,
- 0x64, 0x27, 0x8e, 0x7c, 0x90, 0x4d, 0xee, 0xef, 0x4d, 0x3e, 0xda, 0xec, 0xcf, 0x12, 0xef, 0x56,
- 0x1f, 0xed, 0x25, 0x18, 0x9d, 0xed, 0x06, 0x5b, 0xae, 0x67, 0xbd, 0xc1, 0x95, 0x6e, 0xb2, 0x00,
- 0x95, 0x80, 0x2b, 0x4f, 0x62, 0x5d, 0x7e, 0x22, 0x4b, 0xd4, 0x42, 0x91, 0xbd, 0x46, 0x77, 0x95,
- 0x36, 0x20, 0xd6, 0x47, 0xa1, 0x4c, 0x89, 0xe2, 0xda, 0x6f, 0x14, 0xa0, 0xd6, 0xd0, 0x7d, 0xcb,
- 0x60, 0xf0, 0x64, 0x0e, 0xca, 0x5d, 0x9f, 0x7a, 0x87, 0x03, 0xe5, 0xb3, 0xf4, 0xba, 0x4f, 0x3d,
- 0xe4, 0x85, 0xc9, 0x0d, 0xa8, 0x76, 0x74, 0xdf, 0xbf, 0xe5, 0x7a, 0xa6, 0x5c, 0x69, 0x0e, 0x08,
- 0x24, 0xb4, 0x62, 0x59, 0x14, 0x43, 0x10, 0xad, 0x0e, 0xd1, 0x52, 0xab, 0xfd, 0xa8, 0x00, 0xa7,
- 0x1a, 0xdd, 0xcd, 0x4d, 0xea, 0x49, 0x25, 0x50, 0xaa, 0x57, 0x14, 0x2a, 0x1e, 0x35, 0x2d, 0x5f,
- 0xd6, 0x7d, 0x7e, 0xe0, 0x4f, 0x87, 0x0c, 0x45, 0x6a, 0x73, 0x5c, 0x5e, 0x3c, 0x01, 0x05, 0x3a,
- 0xe9, 0x42, 0xed, 0x35, 0x1a, 0xf8, 0x81, 0x47, 0xf5, 0xb6, 0x6c, 0xdd, 0x95, 0x81, 0x59, 0x5d,
- 0xa5, 0x41, 0x93, 0x23, 0xc5, 0x95, 0xc7, 0x30, 0x11, 0x23, 0x4e, 0xda, 0x77, 0x2a, 0x30, 0x32,
- 0xe7, 0xb6, 0x37, 0x2c, 0x87, 0x9a, 0x97, 0xcd, 0x16, 0x25, 0xaf, 0x42, 0x99, 0x9a, 0x2d, 0x2a,
- 0x5b, 0x3b, 0xf8, 0x3a, 0xcb, 0xc0, 0x22, 0x6d, 0x81, 0xbd, 0x21, 0x07, 0x26, 0xcb, 0x30, 0xb6,
- 0xe9, 0xb9, 0x6d, 0x31, 0x75, 0xad, 0xed, 0x76, 0xa4, 0xaa, 0xd8, 0xf8, 0xa0, 0x9a, 0x0e, 0x16,
- 0x12, 0xb9, 0x77, 0xf6, 0x26, 0x21, 0x7a, 0xc3, 0x54, 0x59, 0xf2, 0x32, 0x4c, 0x44, 0x29, 0xe1,
- 0x18, 0x9e, 0x63, 0xda, 0x3b, 0x57, 0x15, 0x2a, 0x8d, 0xf3, 0xfb, 0x7b, 0x93, 0x13, 0x0b, 0x7d,
- 0x68, 0xb0, 0x6f, 0x69, 0xf2, 0x66, 0x01, 0xc6, 0xa3, 0x4c, 0x31, 0xaf, 0x4a, 0x0d, 0xe1, 0x88,
- 0x26, 0x6c, 0xbe, 0xcd, 0x59, 0x48, 0xb1, 0xc0, 0x1e, 0xa6, 0x64, 0x01, 0x46, 0x02, 0x37, 0x26,
- 0xaf, 0x0a, 0x97, 0x97, 0xa6, 0xf6, 0xe5, 0x6b, 0x6e, 0x5f, 0x69, 0x25, 0xca, 0x11, 0x84, 0x33,
- 0xea, 0x3d, 0x25, 0xa9, 0x21, 0x2e, 0xa9, 0x73, 0xfb, 0x7b, 0x93, 0x67, 0xd6, 0x32, 0x29, 0xb0,
- 0x4f, 0x49, 0xf2, 0xe5, 0x02, 0x8c, 0xa9, 0x2c, 0x29, 0xa3, 0xe1, 0xa3, 0x94, 0x11, 0x61, 0x3d,
- 0x62, 0x2d, 0xc1, 0x00, 0x53, 0x0c, 0xb5, 0x9f, 0x94, 0xa1, 0x16, 0xce, 0x6c, 0xe4, 0x71, 0xa8,
- 0xf0, 0x1d, 0xb7, 0x54, 0x58, 0xc3, 0x25, 0x8b, 0x6f, 0xcc, 0x51, 0xe4, 0x91, 0x27, 0x60, 0xd8,
- 0x70, 0xdb, 0x6d, 0xdd, 0x31, 0xb9, 0x15, 0xa5, 0xd6, 0xa8, 0xb3, 0x95, 0x7a, 0x4e, 0x24, 0xa1,
- 0xca, 0x23, 0xe7, 0xa1, 0xac, 0x7b, 0x2d, 0x61, 0xd0, 0xa8, 0x89, 0xf9, 0x68, 0xd6, 0x6b, 0xf9,
- 0xc8, 0x53, 0xc9, 0xc7, 0xa0, 0x44, 0x9d, 0x9d, 0x89, 0x72, 0x7f, 0x55, 0xe0, 0xb2, 0xb3, 0x73,
- 0x53, 0xf7, 0x1a, 0x75, 0x59, 0x87, 0xd2, 0x65, 0x67, 0x07, 0x59, 0x19, 0xb2, 0x0c, 0xc3, 0xd4,
- 0xd9, 0x61, 0xdf, 0x5e, 0x5a, 0x1a, 0x3e, 0xd0, 0xa7, 0x38, 0x23, 0x91, 0x5a, 0x71, 0xa8, 0x50,
- 0xc8, 0x64, 0x54, 0x10, 0xe4, 0x33, 0x30, 0x22, 0x74, 0x8b, 0x15, 0xf6, 0x4d, 0xfc, 0x89, 0x21,
+ 0x12, 0x0b, 0x86, 0x6c, 0xab, 0x6d, 0x05, 0x3e, 0x5f, 0x38, 0xeb, 0x33, 0x97, 0x07, 0xfe, 0x2c,
+ 0x31, 0x44, 0x97, 0x39, 0x98, 0x18, 0x35, 0xe2, 0x19, 0x25, 0x03, 0x36, 0x15, 0xfa, 0x86, 0x6e,
+ 0x8b, 0x85, 0xb5, 0x3e, 0xf3, 0xe9, 0xc1, 0x87, 0x0d, 0x43, 0x69, 0x8c, 0xca, 0x6f, 0xaa, 0xf0,
+ 0x57, 0x14, 0xd8, 0xe4, 0xf3, 0x30, 0x96, 0x68, 0x4d, 0x7f, 0xa2, 0xce, 0xa5, 0xf3, 0x58, 0x96,
+ 0x74, 0x42, 0xaa, 0x68, 0xe5, 0x49, 0xf4, 0x10, 0x1f, 0x53, 0x60, 0xe4, 0x1a, 0x54, 0x7d, 0xcb,
+ 0xa4, 0x86, 0xee, 0xf9, 0x13, 0x23, 0x07, 0x01, 0x1e, 0x97, 0xc0, 0xd5, 0xa6, 0x2c, 0x86, 0x21,
+ 0x00, 0x99, 0x02, 0xe8, 0xe8, 0x5e, 0x60, 0x09, 0x45, 0x75, 0x94, 0x2b, 0x4d, 0x63, 0xfb, 0x7b,
+ 0x93, 0xb0, 0x1a, 0xa6, 0x62, 0x8c, 0x82, 0xd1, 0xb3, 0xb2, 0x4b, 0x4e, 0xa7, 0x1b, 0x88, 0x85,
+ 0xb5, 0x26, 0xe8, 0x9b, 0x61, 0x2a, 0xc6, 0x28, 0xc8, 0x77, 0x0a, 0xf0, 0x68, 0xf4, 0xda, 0x3b,
+ 0xc8, 0x4e, 0x1c, 0xf9, 0x20, 0x9b, 0xdc, 0xdf, 0x9b, 0x7c, 0xb4, 0xd9, 0x9f, 0x25, 0xde, 0xad,
+ 0x3e, 0xda, 0x4b, 0x30, 0x3a, 0xdb, 0x0d, 0xb6, 0x5c, 0xcf, 0x7a, 0x83, 0x2b, 0xdd, 0x64, 0x01,
+ 0x2a, 0x01, 0x57, 0x9e, 0xc4, 0xba, 0xfc, 0x44, 0x96, 0xa8, 0x85, 0x22, 0x7b, 0x8d, 0xee, 0x2a,
+ 0x6d, 0x40, 0xac, 0x8f, 0x42, 0x99, 0x12, 0xc5, 0xb5, 0x5f, 0x2b, 0x40, 0xad, 0xa1, 0xfb, 0x96,
+ 0xc1, 0xe0, 0xc9, 0x1c, 0x94, 0xbb, 0x3e, 0xf5, 0x0e, 0x07, 0xca, 0x67, 0xe9, 0x75, 0x9f, 0x7a,
+ 0xc8, 0x0b, 0x93, 0x1b, 0x50, 0xed, 0xe8, 0xbe, 0x7f, 0xcb, 0xf5, 0x4c, 0xb9, 0xd2, 0x1c, 0x10,
+ 0x48, 0x68, 0xc5, 0xb2, 0x28, 0x86, 0x20, 0x5a, 0x1d, 0xa2, 0xa5, 0x56, 0xfb, 0x49, 0x01, 0x4e,
+ 0x35, 0xba, 0x9b, 0x9b, 0xd4, 0x93, 0x4a, 0xa0, 0x54, 0xaf, 0x28, 0x54, 0x3c, 0x6a, 0x5a, 0xbe,
+ 0xac, 0xfb, 0xfc, 0xc0, 0x4d, 0x87, 0x0c, 0x45, 0x6a, 0x73, 0x5c, 0x5e, 0x3c, 0x01, 0x05, 0x3a,
+ 0xe9, 0x42, 0xed, 0x35, 0x1a, 0xf8, 0x81, 0x47, 0xf5, 0xb6, 0xfc, 0xba, 0x2b, 0x03, 0xb3, 0xba,
+ 0x4a, 0x83, 0x26, 0x47, 0x8a, 0x2b, 0x8f, 0x61, 0x22, 0x46, 0x9c, 0xb4, 0xef, 0x56, 0x60, 0x64,
+ 0xce, 0x6d, 0x6f, 0x58, 0x0e, 0x35, 0x2f, 0x9b, 0x2d, 0x4a, 0x5e, 0x85, 0x32, 0x35, 0x5b, 0x54,
+ 0x7e, 0xed, 0xe0, 0xeb, 0x2c, 0x03, 0x8b, 0xb4, 0x05, 0xf6, 0x86, 0x1c, 0x98, 0x2c, 0xc3, 0xd8,
+ 0xa6, 0xe7, 0xb6, 0xc5, 0xd4, 0xb5, 0xb6, 0xdb, 0x91, 0xaa, 0x62, 0xe3, 0x83, 0x6a, 0x3a, 0x58,
+ 0x48, 0xe4, 0xde, 0xd9, 0x9b, 0x84, 0xe8, 0x0d, 0x53, 0x65, 0xc9, 0xcb, 0x30, 0x11, 0xa5, 0x84,
+ 0x63, 0x78, 0x8e, 0x69, 0xef, 0x5c, 0x55, 0xa8, 0x34, 0xce, 0xef, 0xef, 0x4d, 0x4e, 0x2c, 0xf4,
+ 0xa1, 0xc1, 0xbe, 0xa5, 0xc9, 0x9b, 0x05, 0x18, 0x8f, 0x32, 0xc5, 0xbc, 0x2a, 0x35, 0x84, 0x23,
+ 0x9a, 0xb0, 0xf9, 0x36, 0x67, 0x21, 0xc5, 0x02, 0x7b, 0x98, 0x92, 0x05, 0x18, 0x09, 0xdc, 0x98,
+ 0xbc, 0x2a, 0x5c, 0x5e, 0x9a, 0xda, 0x97, 0xaf, 0xb9, 0x7d, 0xa5, 0x95, 0x28, 0x47, 0x10, 0xce,
+ 0xa8, 0xf7, 0x94, 0xa4, 0x86, 0xb8, 0xa4, 0xce, 0xed, 0xef, 0x4d, 0x9e, 0x59, 0xcb, 0xa4, 0xc0,
+ 0x3e, 0x25, 0xc9, 0x57, 0x0a, 0x30, 0xa6, 0xb2, 0xa4, 0x8c, 0x86, 0x8f, 0x52, 0x46, 0x84, 0xf5,
+ 0x88, 0xb5, 0x04, 0x03, 0x4c, 0x31, 0xd4, 0x7e, 0x5a, 0x86, 0x5a, 0x38, 0xb3, 0x91, 0xc7, 0xa1,
+ 0xc2, 0x77, 0xdc, 0x52, 0x61, 0x0d, 0x97, 0x2c, 0xbe, 0x31, 0x47, 0x91, 0x47, 0x9e, 0x80, 0x61,
+ 0xc3, 0x6d, 0xb7, 0x75, 0xc7, 0xe4, 0x56, 0x94, 0x5a, 0xa3, 0xce, 0x56, 0xea, 0x39, 0x91, 0x84,
+ 0x2a, 0x8f, 0x9c, 0x87, 0xb2, 0xee, 0xb5, 0x84, 0x41, 0xa3, 0x26, 0xe6, 0xa3, 0x59, 0xaf, 0xe5,
+ 0x23, 0x4f, 0x25, 0x1f, 0x83, 0x12, 0x75, 0x76, 0x26, 0xca, 0xfd, 0x55, 0x81, 0xcb, 0xce, 0xce,
+ 0x4d, 0xdd, 0x6b, 0xd4, 0x65, 0x1d, 0x4a, 0x97, 0x9d, 0x1d, 0x64, 0x65, 0xc8, 0x32, 0x0c, 0x53,
+ 0x67, 0x87, 0xb5, 0xbd, 0xb4, 0x34, 0x7c, 0xa0, 0x4f, 0x71, 0x46, 0x22, 0xb5, 0xe2, 0x50, 0xa1,
+ 0x90, 0xc9, 0xa8, 0x20, 0xc8, 0x67, 0x61, 0x44, 0xe8, 0x16, 0x2b, 0xac, 0x4d, 0xfc, 0x89, 0x21,
0x0e, 0x39, 0xd9, 0x5f, 0x39, 0xe1, 0x74, 0x91, 0x65, 0x27, 0x96, 0xe8, 0x63, 0x02, 0x8a, 0x7c,
- 0x06, 0x6a, 0x6a, 0x23, 0xa8, 0xbe, 0x6c, 0xa6, 0x51, 0x44, 0xed, 0x1e, 0x91, 0xbe, 0xde, 0xb5,
- 0x3c, 0xda, 0xa6, 0x4e, 0xe0, 0x37, 0x4e, 0xaa, 0x6d, 0xb2, 0xca, 0xf5, 0x31, 0x42, 0x23, 0x1b,
- 0xbd, 0xd6, 0x1d, 0x61, 0x9a, 0x78, 0xbc, 0xcf, 0xac, 0x3e, 0x80, 0x69, 0xe7, 0xf3, 0x70, 0x22,
- 0x34, 0xbf, 0xc8, 0x1d, 0xbc, 0x30, 0x56, 0x3c, 0xcd, 0x8a, 0x2f, 0x25, 0xb3, 0xee, 0xec, 0x4d,
- 0x3e, 0x96, 0xb1, 0x87, 0x8f, 0x08, 0x30, 0x0d, 0xa6, 0xfd, 0x61, 0x09, 0x7a, 0xd5, 0xee, 0xa4,
- 0xd0, 0x0a, 0x47, 0x2d, 0xb4, 0x74, 0x83, 0xc4, 0xf4, 0xf9, 0xbc, 0x2c, 0x96, 0xbf, 0x51, 0x59,
- 0x1f, 0xa6, 0x74, 0xd4, 0x1f, 0xe6, 0x41, 0x19, 0x3b, 0xda, 0x5b, 0x65, 0x18, 0x9b, 0xd7, 0x69,
- 0xdb, 0x75, 0xee, 0xb9, 0x09, 0x29, 0x3c, 0x10, 0x9b, 0x90, 0x4b, 0x50, 0xf5, 0x68, 0xc7, 0xb6,
- 0x0c, 0xdd, 0xe7, 0x9f, 0x5e, 0x1a, 0xfd, 0x50, 0xa6, 0x61, 0x98, 0xdb, 0x67, 0xf3, 0x59, 0x7a,
- 0x20, 0x37, 0x9f, 0xe5, 0x77, 0x7f, 0xf3, 0xa9, 0x7d, 0xb9, 0x08, 0x5c, 0x51, 0x21, 0x17, 0xa1,
- 0xcc, 0x16, 0xe1, 0xb4, 0xc9, 0x83, 0x77, 0x1c, 0x9e, 0x43, 0xce, 0x41, 0x31, 0x70, 0xe5, 0xc8,
- 0x03, 0x99, 0x5f, 0x5c, 0x73, 0xb1, 0x18, 0xb8, 0xe4, 0x0d, 0x00, 0xc3, 0x75, 0x4c, 0x4b, 0xd9,
- 0xc2, 0xf3, 0x35, 0x6c, 0xc1, 0xf5, 0x6e, 0xe9, 0x9e, 0x39, 0x17, 0x22, 0x8a, 0xed, 0x47, 0xf4,
- 0x8e, 0x31, 0x6e, 0xe4, 0x05, 0x18, 0x72, 0x9d, 0x85, 0xae, 0x6d, 0x73, 0x81, 0xd6, 0x1a, 0xff,
- 0x8d, 0xed, 0x09, 0x6f, 0xf0, 0x94, 0x3b, 0x7b, 0x93, 0x67, 0x85, 0x7e, 0xcb, 0xde, 0x5e, 0xf2,
- 0xac, 0xc0, 0x72, 0x5a, 0xcd, 0xc0, 0xd3, 0x03, 0xda, 0xda, 0x45, 0x59, 0x4c, 0xfb, 0x5a, 0x01,
- 0xea, 0x0b, 0xd6, 0x6d, 0x6a, 0xbe, 0x64, 0x39, 0xa6, 0x7b, 0x8b, 0x20, 0x0c, 0xd9, 0xd4, 0x69,
- 0x05, 0x5b, 0xb2, 0xf7, 0x4f, 0xc5, 0xc6, 0x5a, 0x78, 0x84, 0x12, 0xd5, 0xbf, 0x4d, 0x03, 0x9d,
- 0x8d, 0xbe, 0xf9, 0xae, 0x34, 0xf2, 0x8b, 0x4d, 0x29, 0x47, 0x40, 0x89, 0x44, 0xa6, 0xa1, 0x26,
- 0xb4, 0x4f, 0xcb, 0x69, 0x71, 0x19, 0x56, 0xa3, 0x49, 0xaf, 0xa9, 0x32, 0x30, 0xa2, 0xd1, 0x76,
- 0xe1, 0x64, 0x8f, 0x18, 0x88, 0x09, 0xe5, 0x40, 0x6f, 0xa9, 0xf9, 0x75, 0x61, 0x60, 0x01, 0xaf,
- 0xe9, 0xad, 0x98, 0x70, 0xf9, 0x1a, 0xbf, 0xa6, 0xb3, 0x35, 0x9e, 0xa1, 0x6b, 0x3f, 0x2b, 0x40,
- 0x75, 0xa1, 0xeb, 0x18, 0x7c, 0x6f, 0x74, 0x6f, 0x53, 0x98, 0x52, 0x18, 0x8a, 0x99, 0x0a, 0x43,
- 0x17, 0x86, 0xb6, 0x6f, 0x85, 0x0a, 0x45, 0x7d, 0x66, 0x65, 0xf0, 0x5e, 0x21, 0xab, 0x34, 0x75,
- 0x8d, 0xe3, 0x89, 0x93, 0x9a, 0x31, 0x59, 0xa1, 0xa1, 0x6b, 0x2f, 0x71, 0xa6, 0x92, 0xd9, 0xb9,
- 0x8f, 0x41, 0x3d, 0x46, 0x76, 0x28, 0xa3, 0xed, 0xef, 0x96, 0x61, 0x68, 0xb1, 0xd9, 0x9c, 0x5d,
- 0x5d, 0x22, 0xcf, 0x40, 0x5d, 0x1a, 0xf1, 0xaf, 0x47, 0x32, 0x08, 0xcf, 0x70, 0x9a, 0x51, 0x16,
- 0xc6, 0xe9, 0x98, 0x3a, 0xe6, 0x51, 0xdd, 0x6e, 0xcb, 0xc1, 0x12, 0xaa, 0x63, 0xc8, 0x12, 0x51,
- 0xe4, 0x11, 0x1d, 0xc6, 0xd8, 0x0e, 0x8f, 0x89, 0x50, 0xec, 0xde, 0xe4, 0xb0, 0x39, 0xe0, 0xfe,
- 0x8e, 0x2b, 0x89, 0xeb, 0x09, 0x00, 0x4c, 0x01, 0x92, 0xe7, 0xa1, 0xaa, 0x77, 0x83, 0x2d, 0xae,
- 0x40, 0x8b, 0xb1, 0x71, 0x9e, 0x9f, 0x71, 0xc8, 0xb4, 0x3b, 0x7b, 0x93, 0x23, 0xd7, 0xb0, 0xf1,
- 0x8c, 0x7a, 0xc7, 0x90, 0x9a, 0x55, 0x4e, 0xed, 0x18, 0x65, 0xe5, 0x2a, 0x87, 0xae, 0xdc, 0x6a,
- 0x02, 0x00, 0x53, 0x80, 0xe4, 0x15, 0x18, 0xd9, 0xa6, 0xbb, 0x81, 0xbe, 0x21, 0x19, 0x0c, 0x1d,
- 0x86, 0xc1, 0x38, 0x53, 0xe1, 0xae, 0xc5, 0x8a, 0x63, 0x02, 0x8c, 0xf8, 0x70, 0x7a, 0x9b, 0x7a,
- 0x1b, 0xd4, 0x73, 0xe5, 0xee, 0x53, 0x32, 0x19, 0x3e, 0x0c, 0x93, 0x89, 0xfd, 0xbd, 0xc9, 0xd3,
- 0xd7, 0x32, 0x60, 0x30, 0x13, 0x5c, 0xfb, 0x69, 0x11, 0x4e, 0x2c, 0x8a, 0x53, 0x54, 0xd7, 0x13,
- 0x8b, 0x30, 0x39, 0x0b, 0x25, 0xaf, 0xd3, 0xe5, 0x3d, 0xa7, 0x24, 0xec, 0xa4, 0xb8, 0xba, 0x8e,
- 0x2c, 0x8d, 0xbc, 0x0c, 0x55, 0x53, 0x4e, 0x19, 0x72, 0xf3, 0x7b, 0xd8, 0x89, 0x86, 0x2f, 0x82,
- 0xea, 0x0d, 0x43, 0x34, 0xa6, 0xe9, 0xb7, 0xfd, 0x56, 0xd3, 0x7a, 0x83, 0xca, 0xfd, 0x20, 0xd7,
- 0xf4, 0x57, 0x44, 0x12, 0xaa, 0x3c, 0xb6, 0xaa, 0x6e, 0xd3, 0x5d, 0xb1, 0x1b, 0x2a, 0x47, 0xab,
- 0xea, 0x35, 0x99, 0x86, 0x61, 0x2e, 0x99, 0x54, 0x83, 0x85, 0xf5, 0x82, 0xb2, 0xd8, 0xc9, 0xdf,
- 0x64, 0x09, 0x72, 0xdc, 0xb0, 0x29, 0xf3, 0x35, 0x2b, 0x08, 0xa8, 0x27, 0x3f, 0xe3, 0x40, 0x53,
- 0xe6, 0x55, 0x8e, 0x80, 0x12, 0x89, 0x7c, 0x18, 0x6a, 0x1c, 0xbc, 0x61, 0xbb, 0x1b, 0xfc, 0xc3,
- 0xd5, 0xc4, 0x9e, 0xfe, 0xa6, 0x4a, 0xc4, 0x28, 0x5f, 0xfb, 0x79, 0x11, 0xce, 0x2c, 0xd2, 0x40,
- 0x68, 0x35, 0xf3, 0xb4, 0x63, 0xbb, 0xbb, 0x4c, 0xb5, 0x44, 0xfa, 0x3a, 0x79, 0x11, 0xc0, 0xf2,
- 0x37, 0x9a, 0x3b, 0x06, 0x1f, 0x07, 0x62, 0x0c, 0x5f, 0x94, 0x43, 0x12, 0x96, 0x9a, 0x0d, 0x99,
- 0x73, 0x27, 0xf1, 0x86, 0xb1, 0x32, 0xd1, 0xf6, 0xaa, 0x78, 0x97, 0xed, 0x55, 0x13, 0xa0, 0x13,
- 0x29, 0xa8, 0x25, 0x4e, 0xf9, 0x3f, 0x15, 0x9b, 0xc3, 0xe8, 0xa6, 0x31, 0x98, 0x3c, 0x2a, 0xa3,
- 0x03, 0xe3, 0x26, 0xdd, 0xd4, 0xbb, 0x76, 0x10, 0x2a, 0xd5, 0x72, 0x10, 0x1f, 0x5c, 0x2f, 0x0f,
- 0x4f, 0x78, 0xe7, 0x53, 0x48, 0xd8, 0x83, 0xad, 0xfd, 0x5e, 0x09, 0xce, 0x2d, 0xd2, 0x20, 0xb4,
- 0xb8, 0xc8, 0xd9, 0xb1, 0xd9, 0xa1, 0x06, 0xfb, 0x0a, 0x6f, 0x16, 0x60, 0xc8, 0xd6, 0x37, 0xa8,
- 0xcd, 0x56, 0x2f, 0xd6, 0x9a, 0x57, 0x07, 0x5e, 0x08, 0xfa, 0x73, 0x99, 0x5a, 0xe6, 0x1c, 0x52,
- 0x4b, 0x83, 0x48, 0x44, 0xc9, 0x9e, 0x4d, 0xea, 0x86, 0xdd, 0xf5, 0x03, 0xea, 0xad, 0xba, 0x5e,
- 0x20, 0xf5, 0xc9, 0x70, 0x52, 0x9f, 0x8b, 0xb2, 0x30, 0x4e, 0x47, 0x66, 0x00, 0x0c, 0xdb, 0xa2,
- 0x4e, 0xc0, 0x4b, 0x89, 0x71, 0x45, 0xd4, 0xf7, 0x9d, 0x0b, 0x73, 0x30, 0x46, 0xc5, 0x58, 0xb5,
- 0x5d, 0xc7, 0x0a, 0x5c, 0xc1, 0xaa, 0x9c, 0x64, 0xb5, 0x12, 0x65, 0x61, 0x9c, 0x8e, 0x17, 0xa3,
- 0x81, 0x67, 0x19, 0x3e, 0x2f, 0x56, 0x49, 0x15, 0x8b, 0xb2, 0x30, 0x4e, 0xc7, 0xd6, 0xbc, 0x58,
- 0xfb, 0x0f, 0xb5, 0xe6, 0x7d, 0xab, 0x06, 0x17, 0x12, 0x62, 0x0d, 0xf4, 0x80, 0x6e, 0x76, 0xed,
- 0x26, 0x0d, 0xd4, 0x07, 0x1c, 0x70, 0x2d, 0xfc, 0x85, 0xe8, 0xbb, 0x0b, 0xdf, 0x0d, 0xe3, 0x68,
- 0xbe, 0x7b, 0x4f, 0x05, 0x0f, 0xf4, 0xed, 0xa7, 0xa1, 0xe6, 0xe8, 0x81, 0xcf, 0x07, 0xae, 0x1c,
- 0xa3, 0xa1, 0x1a, 0x76, 0x5d, 0x65, 0x60, 0x44, 0x43, 0x56, 0xe1, 0xb4, 0x14, 0xf1, 0xe5, 0xdb,
- 0x1d, 0xd7, 0x0b, 0xa8, 0x27, 0xca, 0xca, 0xe5, 0x54, 0x96, 0x3d, 0xbd, 0x92, 0x41, 0x83, 0x99,
- 0x25, 0xc9, 0x0a, 0x9c, 0x32, 0xc4, 0x79, 0x36, 0xb5, 0x5d, 0xdd, 0x54, 0x80, 0xc2, 0xc0, 0x15,
- 0x6e, 0x8d, 0xe6, 0x7a, 0x49, 0x30, 0xab, 0x5c, 0xba, 0x37, 0x0f, 0x0d, 0xd4, 0x9b, 0x87, 0x07,
- 0xe9, 0xcd, 0xd5, 0xc1, 0x7a, 0x73, 0xed, 0x60, 0xbd, 0x99, 0x49, 0x9e, 0xf5, 0x23, 0xea, 0x31,
- 0xf5, 0x44, 0xac, 0xb0, 0x31, 0x77, 0x89, 0x50, 0xf2, 0xcd, 0x0c, 0x1a, 0xcc, 0x2c, 0x49, 0x36,
- 0xe0, 0x9c, 0x48, 0xbf, 0xec, 0x18, 0xde, 0x6e, 0x87, 0x2d, 0x3c, 0x31, 0xdc, 0x7a, 0xc2, 0xc2,
- 0x78, 0xae, 0xd9, 0x97, 0x12, 0xef, 0x82, 0x42, 0xfe, 0x17, 0x8c, 0x8a, 0xaf, 0xb4, 0xa2, 0x77,
- 0x38, 0xac, 0x70, 0x9e, 0x78, 0x58, 0xc2, 0x8e, 0xce, 0xc5, 0x33, 0x31, 0x49, 0x4b, 0x66, 0xe1,
- 0x44, 0x67, 0xc7, 0x60, 0x8f, 0x4b, 0x9b, 0xd7, 0x29, 0x35, 0xa9, 0xc9, 0x4f, 0x6b, 0x6a, 0x8d,
- 0x47, 0x94, 0xa1, 0x63, 0x35, 0x99, 0x8d, 0x69, 0x7a, 0xf2, 0x3c, 0x8c, 0xf8, 0x81, 0xee, 0x05,
- 0xd2, 0xac, 0x37, 0x31, 0x26, 0x9c, 0x4b, 0x94, 0xd5, 0xab, 0x19, 0xcb, 0xc3, 0x04, 0x65, 0xe6,
- 0x7a, 0x71, 0xe2, 0xf8, 0xd6, 0x8b, 0x3c, 0xb3, 0xd5, 0x9f, 0x15, 0xe1, 0xe2, 0x22, 0x0d, 0x56,
- 0x5c, 0x47, 0x1a, 0x45, 0xb3, 0x96, 0xfd, 0x03, 0xd9, 0x44, 0x93, 0x8b, 0x76, 0xf1, 0x48, 0x17,
- 0xed, 0xd2, 0x11, 0x2d, 0xda, 0xe5, 0x63, 0x5c, 0xb4, 0xff, 0xa0, 0x08, 0x8f, 0x24, 0x24, 0xb9,
- 0xea, 0x9a, 0x6a, 0xc2, 0x7f, 0x5f, 0x80, 0x07, 0x10, 0xe0, 0x1d, 0xa1, 0x77, 0xf2, 0x63, 0xad,
- 0x94, 0xc6, 0xf3, 0xd5, 0xb4, 0xc6, 0xf3, 0x4a, 0x9e, 0x95, 0x2f, 0x83, 0xc3, 0x81, 0x56, 0xbc,
- 0xab, 0x40, 0x3c, 0x79, 0x08, 0x27, 0x4c, 0x3f, 0x31, 0xa5, 0x27, 0xf4, 0x5e, 0xc3, 0x1e, 0x0a,
- 0xcc, 0x28, 0x45, 0x9a, 0xf0, 0xb0, 0x4f, 0x9d, 0xc0, 0x72, 0xa8, 0x9d, 0x84, 0x13, 0xda, 0xd0,
- 0x63, 0x12, 0xee, 0xe1, 0x66, 0x16, 0x11, 0x66, 0x97, 0xcd, 0x33, 0x0f, 0xfc, 0x25, 0x70, 0x95,
- 0x53, 0x88, 0xe6, 0xc8, 0x34, 0x96, 0x37, 0xd3, 0x1a, 0xcb, 0xab, 0xf9, 0xbf, 0xdb, 0x60, 0xda,
- 0xca, 0x0c, 0x00, 0xff, 0x0a, 0x71, 0x75, 0x25, 0x5c, 0xa4, 0x31, 0xcc, 0xc1, 0x18, 0x15, 0x5b,
- 0x80, 0x94, 0x9c, 0xe3, 0x9a, 0x4a, 0xb8, 0x00, 0x35, 0xe3, 0x99, 0x98, 0xa4, 0xed, 0xab, 0xed,
- 0x54, 0x06, 0xd6, 0x76, 0xae, 0x02, 0x49, 0x18, 0x1e, 0x05, 0xde, 0x50, 0xd2, 0x79, 0x72, 0xa9,
- 0x87, 0x02, 0x33, 0x4a, 0xf5, 0xe9, 0xca, 0xc3, 0x47, 0xdb, 0x95, 0xab, 0x83, 0x77, 0x65, 0xf2,
- 0x2a, 0x9c, 0xe5, 0xac, 0xa4, 0x7c, 0x92, 0xc0, 0x42, 0xef, 0xf9, 0x80, 0x04, 0x3e, 0x8b, 0xfd,
- 0x08, 0xb1, 0x3f, 0x06, 0xfb, 0x3e, 0x86, 0x47, 0x4d, 0xc6, 0x5c, 0xb7, 0xfb, 0xeb, 0x44, 0x73,
- 0x19, 0x34, 0x98, 0x59, 0x92, 0x75, 0xb1, 0x80, 0x75, 0x43, 0x7d, 0xc3, 0xa6, 0xa6, 0x74, 0x1e,
- 0x0d, 0xbb, 0xd8, 0xda, 0x72, 0x53, 0xe6, 0x60, 0x8c, 0x2a, 0x4b, 0x4d, 0x19, 0x39, 0xa4, 0x9a,
- 0xb2, 0xc8, 0xad, 0xf4, 0x9b, 0x09, 0x6d, 0x48, 0xea, 0x3a, 0xa1, 0x3b, 0xf0, 0x5c, 0x9a, 0x00,
- 0x7b, 0xcb, 0x70, 0x2d, 0xd1, 0xf0, 0xac, 0x4e, 0xe0, 0x27, 0xb1, 0xc6, 0x52, 0x5a, 0x62, 0x06,
- 0x0d, 0x66, 0x96, 0x64, 0xfa, 0xf9, 0x16, 0xd5, 0xed, 0x60, 0x2b, 0x09, 0x78, 0x22, 0xa9, 0x9f,
- 0x5f, 0xe9, 0x25, 0xc1, 0xac, 0x72, 0x99, 0x0b, 0xd2, 0xf8, 0x83, 0xa9, 0x56, 0x7d, 0xa5, 0x04,
- 0x67, 0x17, 0x69, 0x10, 0xfa, 0xd5, 0xbc, 0x6f, 0x46, 0x79, 0x17, 0xcc, 0x28, 0xdf, 0xac, 0xc0,
- 0xa9, 0x45, 0x1a, 0xf4, 0x68, 0x63, 0xff, 0x45, 0xc5, 0xbf, 0x02, 0xa7, 0x22, 0x57, 0xae, 0x66,
- 0xe0, 0x7a, 0x62, 0x2d, 0x4f, 0xed, 0x96, 0x9b, 0xbd, 0x24, 0x98, 0x55, 0x8e, 0x7c, 0x06, 0x1e,
- 0xe1, 0x4b, 0xbd, 0xd3, 0x12, 0xf6, 0x59, 0x61, 0x4c, 0x88, 0x5d, 0x46, 0x98, 0x94, 0x90, 0x8f,
- 0x34, 0xb3, 0xc9, 0xb0, 0x5f, 0x79, 0xf2, 0x45, 0x18, 0xe9, 0x58, 0x1d, 0x6a, 0x5b, 0x0e, 0xd7,
- 0xcf, 0x72, 0xbb, 0x84, 0xac, 0xc6, 0xc0, 0xa2, 0x0d, 0x5c, 0x3c, 0x15, 0x13, 0x0c, 0x33, 0x7b,
- 0x6a, 0xf5, 0x18, 0x7b, 0xea, 0xbf, 0x15, 0x61, 0x78, 0xd1, 0x73, 0xbb, 0x9d, 0xc6, 0x2e, 0x69,
- 0xc1, 0xd0, 0x2d, 0x7e, 0x78, 0x26, 0x8f, 0xa6, 0x06, 0x77, 0x87, 0x16, 0x67, 0x70, 0x91, 0x4a,
- 0x24, 0xde, 0x51, 0xc2, 0xb3, 0x4e, 0xbc, 0x4d, 0x77, 0xa9, 0x29, 0xcf, 0xd0, 0xc2, 0x4e, 0x7c,
- 0x8d, 0x25, 0xa2, 0xc8, 0x23, 0x6d, 0x38, 0xa1, 0xdb, 0xb6, 0x7b, 0x8b, 0x9a, 0xcb, 0x7a, 0x40,
- 0x1d, 0xea, 0xab, 0x23, 0xc9, 0xc3, 0x9a, 0xa5, 0xf9, 0xb9, 0xfe, 0x6c, 0x12, 0x0a, 0xd3, 0xd8,
- 0xe4, 0x35, 0x18, 0xf6, 0x03, 0xd7, 0x53, 0xca, 0x56, 0x7d, 0x66, 0x6e, 0xf0, 0x8f, 0xde, 0xf8,
- 0x74, 0x53, 0x40, 0x09, 0x9b, 0xbd, 0x7c, 0x41, 0xc5, 0x40, 0xfb, 0x46, 0x01, 0xe0, 0xca, 0xda,
- 0xda, 0xaa, 0x3c, 0x5e, 0x30, 0xa1, 0xac, 0x77, 0xc3, 0x83, 0xca, 0xc1, 0x0f, 0x04, 0x13, 0xfe,
- 0x90, 0xf2, 0x0c, 0xaf, 0x1b, 0x6c, 0x21, 0x47, 0x27, 0x1f, 0x82, 0x61, 0xa9, 0x20, 0x4b, 0xb1,
- 0x87, 0xae, 0x05, 0x52, 0x89, 0x46, 0x95, 0xaf, 0xfd, 0x4e, 0x11, 0x60, 0xc9, 0xb4, 0x69, 0x53,
- 0x79, 0xb0, 0xd7, 0x82, 0x2d, 0x8f, 0xfa, 0x5b, 0xae, 0x6d, 0x0e, 0x78, 0x9a, 0xca, 0x6d, 0xfe,
- 0x6b, 0x0a, 0x04, 0x23, 0x3c, 0x62, 0xc2, 0x88, 0x1f, 0xd0, 0xce, 0x92, 0x13, 0x50, 0x6f, 0x47,
- 0xb7, 0x07, 0x3c, 0x44, 0x19, 0x17, 0x76, 0x91, 0x08, 0x07, 0x13, 0xa8, 0x44, 0x87, 0xba, 0xe5,
- 0x18, 0x62, 0x80, 0x34, 0x76, 0x07, 0xec, 0x48, 0x27, 0xd8, 0x8e, 0x63, 0x29, 0x82, 0xc1, 0x38,
- 0xa6, 0xf6, 0xc3, 0x22, 0x9c, 0xe1, 0xfc, 0x58, 0x35, 0x12, 0xfe, 0x98, 0xe4, 0xff, 0xf4, 0xdc,
- 0xb6, 0xfb, 0x1f, 0x07, 0x63, 0x2d, 0x2e, 0x6b, 0xad, 0xd0, 0x40, 0x8f, 0xf4, 0xb9, 0x28, 0x2d,
- 0x76, 0xc5, 0xae, 0x0b, 0x65, 0x9f, 0xcd, 0x57, 0x42, 0x7a, 0xcd, 0x81, 0xbb, 0x50, 0x76, 0x03,
- 0xf8, 0xec, 0x15, 0x9e, 0x1a, 0xf3, 0x59, 0x8b, 0xb3, 0x23, 0xff, 0x0f, 0x86, 0xfc, 0x40, 0x0f,
- 0xba, 0x6a, 0x68, 0xae, 0x1f, 0x35, 0x63, 0x0e, 0x1e, 0xcd, 0x23, 0xe2, 0x1d, 0x25, 0x53, 0xed,
- 0x87, 0x05, 0x38, 0x97, 0x5d, 0x70, 0xd9, 0xf2, 0x03, 0xf2, 0xbf, 0x7b, 0xc4, 0x7e, 0xc0, 0x2f,
- 0xce, 0x4a, 0x73, 0xa1, 0x87, 0x0e, 0xd9, 0x2a, 0x25, 0x26, 0xf2, 0x00, 0x2a, 0x56, 0x40, 0xdb,
- 0x6a, 0x7f, 0x79, 0xe3, 0x88, 0x9b, 0x1e, 0x5b, 0xda, 0x19, 0x17, 0x14, 0xcc, 0xb4, 0xb7, 0x8a,
- 0xfd, 0x9a, 0xcc, 0x97, 0x0f, 0x3b, 0xe9, 0xf3, 0x7b, 0x2d, 0x9f, 0xcf, 0x6f, 0xb2, 0x42, 0xbd,
- 0xae, 0xbf, 0xff, 0xb7, 0xd7, 0xf5, 0xf7, 0x46, 0x7e, 0xd7, 0xdf, 0x94, 0x18, 0xfa, 0x7a, 0x00,
- 0xbf, 0x53, 0x82, 0xf3, 0x77, 0xeb, 0x36, 0x6c, 0x3d, 0x93, 0xbd, 0x33, 0xef, 0x7a, 0x76, 0xf7,
- 0x7e, 0x48, 0x66, 0xa0, 0xd2, 0xd9, 0xd2, 0x7d, 0xa5, 0x94, 0xa9, 0x0d, 0x4b, 0x65, 0x95, 0x25,
- 0xde, 0x61, 0x93, 0x06, 0x57, 0xe6, 0xf8, 0x2b, 0x0a, 0x52, 0x36, 0x1d, 0xb7, 0xa9, 0xef, 0x47,
- 0x36, 0x81, 0x70, 0x3a, 0x5e, 0x11, 0xc9, 0xa8, 0xf2, 0x49, 0x00, 0x43, 0xc2, 0xc4, 0x2c, 0x57,
- 0xa6, 0xc1, 0x1d, 0xb9, 0x32, 0xdc, 0xc4, 0xa3, 0x46, 0xc9, 0xd3, 0x0a, 0xc9, 0x8b, 0x4c, 0x41,
- 0x39, 0x88, 0x9c, 0x76, 0xd5, 0xd6, 0xbc, 0x9c, 0xa1, 0x9f, 0x72, 0x3a, 0xb6, 0xb1, 0x77, 0x37,
- 0xb8, 0x51, 0xdd, 0x94, 0xe7, 0xe7, 0x96, 0xeb, 0x70, 0x85, 0xac, 0x14, 0x6d, 0xec, 0x6f, 0xf4,
- 0x50, 0x60, 0x46, 0x29, 0xed, 0x6f, 0xaa, 0x70, 0x26, 0xbb, 0x3f, 0x30, 0xb9, 0xed, 0x50, 0xcf,
- 0x67, 0xd8, 0x85, 0xa4, 0xdc, 0x6e, 0x8a, 0x64, 0x54, 0xf9, 0xef, 0x69, 0x87, 0xb3, 0x6f, 0x16,
- 0xe0, 0xac, 0x27, 0xcf, 0x88, 0xee, 0x87, 0xd3, 0xd9, 0x63, 0xc2, 0x9c, 0xd1, 0x87, 0x21, 0xf6,
- 0xaf, 0x0b, 0xf9, 0xad, 0x02, 0x4c, 0xb4, 0x53, 0x76, 0x8e, 0x63, 0xbc, 0x30, 0xc6, 0xbd, 0xe2,
- 0x57, 0xfa, 0xf0, 0xc3, 0xbe, 0x35, 0x21, 0x5f, 0x84, 0x7a, 0x87, 0xf5, 0x0b, 0x3f, 0xa0, 0x8e,
- 0xa1, 0xee, 0x8c, 0x0d, 0x3e, 0x92, 0x56, 0x23, 0x2c, 0xe5, 0x8a, 0x26, 0xf4, 0x83, 0x58, 0x06,
- 0xc6, 0x39, 0x3e, 0xe0, 0x37, 0xc4, 0x2e, 0x41, 0xd5, 0xa7, 0x41, 0x60, 0x39, 0x2d, 0xb1, 0xdf,
- 0xa8, 0x89, 0xb1, 0xd2, 0x94, 0x69, 0x18, 0xe6, 0x92, 0x0f, 0x43, 0x8d, 0x1f, 0x39, 0xcd, 0x7a,
- 0x2d, 0x7f, 0xa2, 0xc6, 0xdd, 0xc5, 0x46, 0x85, 0x03, 0x9c, 0x4c, 0xc4, 0x28, 0x9f, 0x3c, 0x0d,
- 0x23, 0x1b, 0x7c, 0xf8, 0xca, 0x4b, 0xc3, 0xc2, 0xc6, 0xc5, 0xb5, 0xb5, 0x46, 0x2c, 0x1d, 0x13,
- 0x54, 0x64, 0x06, 0x80, 0x86, 0xe7, 0x72, 0x69, 0x7b, 0x56, 0x74, 0x62, 0x87, 0x31, 0x2a, 0xf2,
- 0x18, 0x94, 0x02, 0xdb, 0xe7, 0x36, 0xac, 0x6a, 0xb4, 0x05, 0x5d, 0x5b, 0x6e, 0x22, 0x4b, 0xd7,
- 0x7e, 0x5e, 0x80, 0x13, 0xa9, 0xcb, 0x25, 0xac, 0x48, 0xd7, 0xb3, 0xe5, 0x34, 0x12, 0x16, 0x59,
- 0xc7, 0x65, 0x64, 0xe9, 0xe4, 0x55, 0xa9, 0x96, 0x17, 0x73, 0xc6, 0x47, 0xb8, 0xae, 0x07, 0x3e,
- 0xd3, 0xc3, 0x7b, 0x34, 0x72, 0x7e, 0xcc, 0x17, 0xd5, 0x47, 0xae, 0x03, 0xb1, 0x63, 0xbe, 0x28,
- 0x0f, 0x13, 0x94, 0x29, 0x83, 0x5f, 0xf9, 0x20, 0x06, 0x3f, 0xed, 0x6b, 0xc5, 0x98, 0x04, 0xa4,
- 0x66, 0x7f, 0x0f, 0x09, 0x3c, 0xc9, 0x16, 0xd0, 0x70, 0x71, 0xaf, 0xc5, 0xd7, 0x3f, 0xbe, 0x18,
- 0xcb, 0x5c, 0xf2, 0x92, 0x90, 0x7d, 0x29, 0xe7, 0x2d, 0xd4, 0xb5, 0xe5, 0xa6, 0xf0, 0xae, 0x52,
- 0x5f, 0x2d, 0xfc, 0x04, 0xe5, 0x63, 0xfa, 0x04, 0xda, 0x5f, 0x94, 0xa0, 0x7e, 0xd5, 0xdd, 0x78,
- 0x8f, 0x78, 0x50, 0x67, 0x2f, 0x53, 0xc5, 0x77, 0x71, 0x99, 0x5a, 0x87, 0x47, 0x82, 0xc0, 0x6e,
- 0x52, 0xc3, 0x75, 0x4c, 0x7f, 0x76, 0x33, 0xa0, 0xde, 0x82, 0xe5, 0x58, 0xfe, 0x16, 0x35, 0xe5,
- 0x71, 0xd2, 0xa3, 0xfb, 0x7b, 0x93, 0x8f, 0xac, 0xad, 0x2d, 0x67, 0x91, 0x60, 0xbf, 0xb2, 0x7c,
- 0xda, 0xd0, 0x8d, 0x6d, 0x77, 0x73, 0x93, 0xdf, 0x94, 0x91, 0x3e, 0x37, 0x62, 0xda, 0x88, 0xa5,
- 0x63, 0x82, 0x4a, 0x7b, 0xbb, 0x08, 0xb5, 0xf0, 0xe6, 0x3b, 0x79, 0x02, 0x86, 0x37, 0x3c, 0x77,
- 0x9b, 0x7a, 0xe2, 0xe4, 0x4e, 0xde, 0x94, 0x69, 0x88, 0x24, 0x54, 0x79, 0xe4, 0x71, 0xa8, 0x04,
- 0x6e, 0xc7, 0x32, 0xd2, 0x06, 0xb5, 0x35, 0x96, 0x88, 0x22, 0xef, 0xf8, 0x3a, 0xf8, 0x93, 0x09,
- 0xd5, 0xae, 0xd6, 0x57, 0x19, 0x7b, 0x05, 0xca, 0xbe, 0xee, 0xdb, 0x72, 0x3d, 0xcd, 0x71, 0x89,
- 0x7c, 0xb6, 0xb9, 0x2c, 0x2f, 0x91, 0xcf, 0x36, 0x97, 0x91, 0x83, 0x6a, 0x3f, 0x29, 0x42, 0x5d,
- 0xc8, 0x4d, 0xcc, 0x0a, 0x47, 0x29, 0xb9, 0x17, 0xb8, 0x2b, 0x85, 0xdf, 0x6d, 0x53, 0x8f, 0x9b,
- 0x99, 0xe4, 0x24, 0x17, 0x3f, 0x1f, 0x88, 0x32, 0x43, 0x77, 0x8a, 0x28, 0x49, 0x89, 0xbe, 0x7c,
- 0x8c, 0xa2, 0xaf, 0x1c, 0x48, 0xf4, 0x43, 0xc7, 0x21, 0xfa, 0x37, 0x8b, 0x50, 0x5b, 0xb6, 0x36,
- 0xa9, 0xb1, 0x6b, 0xd8, 0xfc, 0x4e, 0xa0, 0x49, 0x6d, 0x1a, 0xd0, 0x45, 0x4f, 0x37, 0xe8, 0x2a,
- 0xf5, 0x2c, 0x1e, 0x19, 0x86, 0x8d, 0x0f, 0x3e, 0x03, 0xc9, 0x3b, 0x81, 0xf3, 0x7d, 0x68, 0xb0,
- 0x6f, 0x69, 0xb2, 0x04, 0x23, 0x26, 0xf5, 0x2d, 0x8f, 0x9a, 0xab, 0xb1, 0x8d, 0xca, 0x13, 0x6a,
- 0xa9, 0x99, 0x8f, 0xe5, 0xdd, 0xd9, 0x9b, 0x1c, 0x55, 0x06, 0x4a, 0xb1, 0x63, 0x49, 0x14, 0x65,
- 0x43, 0xbe, 0xa3, 0x77, 0xfd, 0xac, 0x3a, 0xc6, 0x86, 0xfc, 0x6a, 0x36, 0x09, 0xf6, 0x2b, 0xab,
- 0x55, 0xa0, 0xb4, 0xec, 0xb6, 0xb4, 0xb7, 0x4a, 0x10, 0x86, 0x10, 0x22, 0xff, 0xbf, 0x00, 0x75,
- 0xdd, 0x71, 0xdc, 0x40, 0x86, 0xe7, 0x11, 0x27, 0xf0, 0x98, 0x3b, 0x52, 0xd1, 0xd4, 0x6c, 0x04,
- 0x2a, 0x0e, 0x6f, 0xc3, 0x03, 0xe5, 0x58, 0x0e, 0xc6, 0x79, 0x93, 0x6e, 0xea, 0x3c, 0x79, 0x25,
- 0x7f, 0x2d, 0x0e, 0x70, 0x7a, 0x7c, 0xee, 0x53, 0x30, 0x9e, 0xae, 0xec, 0x61, 0x8e, 0x83, 0x72,
- 0x1d, 0xcc, 0x17, 0x01, 0x22, 0x9f, 0x92, 0xfb, 0x60, 0xc4, 0xb2, 0x12, 0x46, 0xac, 0xc5, 0xc1,
- 0x05, 0x1c, 0x56, 0xba, 0xaf, 0xe1, 0xea, 0xf5, 0x94, 0xe1, 0x6a, 0xe9, 0x28, 0x98, 0xdd, 0xdd,
- 0x58, 0xf5, 0xdb, 0x05, 0x18, 0x8f, 0x88, 0xe5, 0x0d, 0xd9, 0xe7, 0x60, 0xd4, 0xa3, 0xba, 0xd9,
- 0xd0, 0x03, 0x63, 0x8b, 0xbb, 0x7a, 0x17, 0xb8, 0x6f, 0xf6, 0xc9, 0xfd, 0xbd, 0xc9, 0x51, 0x8c,
- 0x67, 0x60, 0x92, 0x8e, 0xe8, 0x50, 0x67, 0x09, 0x6b, 0x56, 0x9b, 0xba, 0xdd, 0x60, 0x40, 0xab,
- 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c, 0xed, 0x9d, 0x02, 0x8c, 0xc5, 0x2b, 0x7c, 0xec,
- 0x16, 0xb5, 0xad, 0xa4, 0x45, 0x6d, 0xee, 0x08, 0xbe, 0x49, 0x1f, 0x2b, 0xda, 0x4f, 0xab, 0xf1,
- 0xa6, 0x71, 0xcb, 0x59, 0xdc, 0x58, 0x50, 0xb8, 0xab, 0xb1, 0xe0, 0xbd, 0x1f, 0x35, 0xa6, 0x9f,
- 0x96, 0x5b, 0x7e, 0x80, 0xb5, 0xdc, 0x77, 0x33, 0xf4, 0x4c, 0x2c, 0x7c, 0xca, 0x50, 0x8e, 0xf0,
- 0x29, 0xed, 0x30, 0x7c, 0xca, 0xf0, 0x91, 0x4d, 0x3a, 0x07, 0x09, 0xa1, 0x52, 0xbd, 0xaf, 0x21,
- 0x54, 0x6a, 0xc7, 0x15, 0x42, 0x05, 0xf2, 0x86, 0x50, 0xf9, 0x6a, 0x01, 0xc6, 0xcc, 0xc4, 0x8d,
- 0x59, 0x6e, 0x5b, 0xc8, 0xb3, 0xd4, 0x24, 0x2f, 0xe0, 0x8a, 0x2b, 0x53, 0xc9, 0x34, 0x4c, 0xb1,
- 0xd4, 0x7e, 0x5c, 0x8e, 0xaf, 0x03, 0xf7, 0xdb, 0x54, 0xfd, 0x6c, 0xd2, 0x54, 0x7d, 0x31, 0x6d,
- 0xaa, 0x3e, 0x11, 0xf3, 0x22, 0x8d, 0x9b, 0xab, 0x3f, 0x12, 0x9b, 0x1e, 0xd9, 0x9c, 0x34, 0x1a,
- 0x49, 0x3a, 0x63, 0x8a, 0xfc, 0x08, 0x54, 0x7d, 0x15, 0xec, 0x51, 0x6c, 0x6c, 0xa2, 0xef, 0xa2,
- 0x02, 0x31, 0x86, 0x14, 0x4c, 0x13, 0xf7, 0xa8, 0xee, 0xbb, 0x4e, 0x5a, 0x13, 0x47, 0x9e, 0x8a,
- 0x32, 0x37, 0x6e, 0x32, 0x1f, 0xba, 0x87, 0xc9, 0x5c, 0x87, 0xba, 0xad, 0xfb, 0xc1, 0x7a, 0xc7,
- 0xd4, 0x03, 0x6a, 0xca, 0xf1, 0xf6, 0xdf, 0x0f, 0xb6, 0x56, 0xb1, 0xf5, 0x2f, 0x52, 0x08, 0x97,
- 0x23, 0x18, 0x8c, 0x63, 0x12, 0x13, 0x46, 0xd8, 0x2b, 0x1f, 0x0d, 0xe6, 0xac, 0x0a, 0x01, 0x70,
- 0x18, 0x1e, 0xa1, 0xa5, 0x67, 0x39, 0x86, 0x83, 0x09, 0xd4, 0x3e, 0x56, 0xf5, 0xda, 0x40, 0x56,
- 0xf5, 0xaf, 0xd6, 0xa0, 0x7e, 0x5d, 0x0f, 0xac, 0x1d, 0xca, 0x4f, 0x71, 0x8e, 0xc7, 0x94, 0xfe,
- 0x6b, 0x05, 0x38, 0x93, 0x74, 0xd5, 0x3b, 0x46, 0x7b, 0x3a, 0x0f, 0xfc, 0x81, 0x99, 0xdc, 0xb0,
- 0x4f, 0x2d, 0xb8, 0x65, 0xbd, 0xc7, 0xf3, 0xef, 0xb8, 0x2d, 0xeb, 0xcd, 0x7e, 0x0c, 0xb1, 0x7f,
- 0x5d, 0xde, 0x2b, 0x96, 0xf5, 0x07, 0x3b, 0x30, 0x5b, 0xca, 0xee, 0x3f, 0xfc, 0xc0, 0xd8, 0xfd,
- 0xab, 0x0f, 0x84, 0xb2, 0xd5, 0x89, 0xd9, 0xfd, 0x6b, 0x39, 0xfd, 0x4f, 0xa4, 0x77, 0xbb, 0x40,
- 0xeb, 0x77, 0x7e, 0xc0, 0x2f, 0xa6, 0x2b, 0x7b, 0x2c, 0xd3, 0x51, 0x36, 0x74, 0xdf, 0x32, 0xe4,
- 0xb2, 0x97, 0x23, 0x10, 0xa5, 0x8a, 0xd8, 0x25, 0x8e, 0xa9, 0xf9, 0x2b, 0x0a, 0xec, 0x28, 0x32,
- 0x58, 0x31, 0x57, 0x64, 0x30, 0x32, 0x07, 0x65, 0x87, 0xed, 0x9e, 0x4b, 0x87, 0x8e, 0x05, 0x76,
- 0xfd, 0x1a, 0xdd, 0x45, 0x5e, 0x58, 0x7b, 0xbb, 0x08, 0xc0, 0x9a, 0x7f, 0x30, 0x0b, 0xfc, 0x87,
- 0x60, 0xd8, 0xef, 0xf2, 0xbd, 0xb2, 0x5c, 0xb0, 0x23, 0xa7, 0x1d, 0x91, 0x8c, 0x2a, 0x9f, 0x3c,
- 0x0e, 0x95, 0xd7, 0xbb, 0xb4, 0xab, 0x8e, 0x93, 0x43, 0x75, 0xed, 0xd3, 0x2c, 0x11, 0x45, 0xde,
- 0xf1, 0x59, 0xd3, 0x94, 0xa5, 0xbe, 0x72, 0x5c, 0x96, 0xfa, 0x1a, 0x0c, 0x5f, 0x77, 0xb9, 0x0f,
- 0xa0, 0xf6, 0x2f, 0x45, 0x80, 0xc8, 0xc7, 0x8a, 0x7c, 0xa3, 0x00, 0x0f, 0x87, 0x03, 0x2e, 0x10,
- 0x5a, 0x37, 0x8f, 0xfd, 0x9a, 0xdb, 0x6a, 0x9f, 0x35, 0xd8, 0xf9, 0x0c, 0xb4, 0x9a, 0xc5, 0x0e,
- 0xb3, 0x6b, 0x41, 0x10, 0xaa, 0xb4, 0xdd, 0x09, 0x76, 0xe7, 0x2d, 0x4f, 0xf6, 0xc0, 0x4c, 0x57,
- 0xbe, 0xcb, 0x92, 0x46, 0x14, 0x95, 0x5b, 0x43, 0x3e, 0x88, 0x54, 0x0e, 0x86, 0x38, 0x64, 0x0b,
- 0xaa, 0x8e, 0xfb, 0xaa, 0xcf, 0xc4, 0x21, 0xbb, 0xe3, 0x8b, 0x83, 0x8b, 0x5c, 0x88, 0x55, 0x58,
- 0x79, 0xe5, 0x0b, 0x0e, 0x3b, 0x52, 0xd8, 0x5f, 0x2f, 0xc2, 0xa9, 0x0c, 0x39, 0x90, 0x17, 0x61,
- 0x5c, 0xba, 0xb3, 0x45, 0x41, 0x90, 0x0b, 0x51, 0x10, 0xe4, 0x66, 0x2a, 0x0f, 0x7b, 0xa8, 0xc9,
- 0xab, 0x00, 0xba, 0x61, 0x50, 0xdf, 0x5f, 0x71, 0x4d, 0xa5, 0x8f, 0xbe, 0xb0, 0xbf, 0x37, 0x09,
- 0xb3, 0x61, 0xea, 0x9d, 0xbd, 0xc9, 0x8f, 0x66, 0x79, 0xa8, 0xa6, 0xe4, 0x1c, 0x15, 0xc0, 0x18,
- 0x24, 0xf9, 0x3c, 0x80, 0xd8, 0x7a, 0x85, 0x97, 0xe8, 0xef, 0x61, 0xaf, 0x98, 0x52, 0xe1, 0x8a,
- 0xa6, 0x3e, 0xdd, 0xd5, 0x9d, 0xc0, 0x0a, 0x76, 0x45, 0xcc, 0x92, 0x9b, 0x21, 0x0a, 0xc6, 0x10,
- 0xb5, 0x3f, 0x2d, 0x42, 0x55, 0x59, 0x4a, 0xef, 0x83, 0x79, 0xac, 0x95, 0x30, 0x8f, 0x1d, 0x91,
- 0x4f, 0x6a, 0x96, 0x71, 0xcc, 0x4d, 0x19, 0xc7, 0x16, 0xf3, 0xb3, 0xba, 0xbb, 0x69, 0xec, 0xdb,
- 0x45, 0x18, 0x53, 0xa4, 0x79, 0x0d, 0x63, 0x9f, 0x84, 0x13, 0xe2, 0x2c, 0x79, 0x45, 0xbf, 0x2d,
- 0xc2, 0xb7, 0x70, 0x81, 0x95, 0x85, 0x1b, 0x68, 0x23, 0x99, 0x85, 0x69, 0x5a, 0xd6, 0xad, 0x45,
- 0xd2, 0x3a, 0xdb, 0x47, 0x88, 0xd3, 0x27, 0xb1, 0xdf, 0xe1, 0xdd, 0xba, 0x91, 0xca, 0xc3, 0x1e,
- 0xea, 0xb4, 0x65, 0xae, 0x7c, 0x0c, 0x96, 0xb9, 0xbf, 0x2d, 0xc0, 0x48, 0x24, 0xaf, 0x63, 0xb7,
- 0xcb, 0x6d, 0x26, 0xed, 0x72, 0xb3, 0xb9, 0xbb, 0x43, 0x1f, 0xab, 0xdc, 0x2f, 0x0d, 0x43, 0xc2,
- 0x35, 0x9a, 0x6c, 0xc0, 0x39, 0x2b, 0xd3, 0xc1, 0x2b, 0x36, 0xdb, 0x84, 0x77, 0x7d, 0x97, 0xfa,
- 0x52, 0xe2, 0x5d, 0x50, 0x48, 0x17, 0xaa, 0x3b, 0xd4, 0x0b, 0x2c, 0x83, 0xaa, 0xf6, 0x2d, 0xe6,
- 0x56, 0xc9, 0xa4, 0xed, 0x31, 0x94, 0xe9, 0x4d, 0xc9, 0x00, 0x43, 0x56, 0x64, 0x03, 0x2a, 0xd4,
- 0x6c, 0x51, 0x15, 0x50, 0x27, 0x67, 0xb8, 0xca, 0x50, 0x9e, 0xec, 0xcd, 0x47, 0x01, 0x4d, 0x7c,
- 0xa8, 0xd9, 0xea, 0x6c, 0x49, 0xf6, 0xc3, 0xc1, 0x15, 0xac, 0xf0, 0x94, 0x2a, 0xba, 0x6b, 0x1f,
- 0x26, 0x61, 0xc4, 0x87, 0x6c, 0x87, 0x46, 0xae, 0xca, 0x11, 0x4d, 0x1e, 0x77, 0x31, 0x71, 0xf9,
- 0x50, 0xbb, 0xa5, 0x07, 0xd4, 0x6b, 0xeb, 0xde, 0xb6, 0xdc, 0x6d, 0x0c, 0xde, 0xc2, 0x97, 0x14,
- 0x52, 0xd4, 0xc2, 0x30, 0x09, 0x23, 0x3e, 0xc4, 0x85, 0x5a, 0x20, 0xd5, 0x67, 0x65, 0xc9, 0x1b,
- 0x9c, 0xa9, 0x52, 0xc4, 0x7d, 0xe9, 0x22, 0xad, 0x5e, 0x31, 0xe2, 0x41, 0x76, 0x12, 0xa1, 0x7c,
- 0x45, 0x00, 0xe7, 0x46, 0x0e, 0x8b, 0xb0, 0x84, 0x8a, 0x96, 0x9b, 0xec, 0x90, 0xc0, 0xda, 0xdb,
- 0x95, 0x68, 0x5a, 0xbe, 0xdf, 0x76, 0xaa, 0xa7, 0x93, 0x76, 0xaa, 0x0b, 0x69, 0x3b, 0x55, 0xea,
- 0x88, 0xf2, 0xf0, 0x4e, 0x95, 0x29, 0x0b, 0x51, 0xf9, 0x18, 0x2c, 0x44, 0x4f, 0x41, 0x7d, 0x87,
- 0xcf, 0x04, 0x22, 0x3a, 0x4f, 0x85, 0x2f, 0x23, 0x7c, 0x66, 0xbf, 0x19, 0x25, 0x63, 0x9c, 0x86,
- 0x15, 0x91, 0x3f, 0x2f, 0x08, 0xc3, 0x9b, 0xca, 0x22, 0xcd, 0x28, 0x19, 0xe3, 0x34, 0xdc, 0x1f,
- 0xcb, 0x72, 0xb6, 0x45, 0x81, 0x61, 0x5e, 0x40, 0xf8, 0x63, 0xa9, 0x44, 0x8c, 0xf2, 0xc9, 0x25,
- 0xa8, 0x76, 0xcd, 0x4d, 0x41, 0x5b, 0xe5, 0xb4, 0x5c, 0xc3, 0x5c, 0x9f, 0x5f, 0x90, 0xd1, 0x82,
- 0x54, 0x2e, 0xab, 0x49, 0x5b, 0xef, 0xa8, 0x0c, 0xbe, 0x37, 0x94, 0x35, 0x59, 0x89, 0x92, 0x31,
- 0x4e, 0x43, 0x3e, 0x0e, 0x63, 0x1e, 0x35, 0xbb, 0x06, 0x0d, 0x4b, 0x01, 0x2f, 0x45, 0xc4, 0x5f,
- 0x1a, 0xe2, 0x39, 0x98, 0xa2, 0xec, 0x63, 0xe7, 0xaa, 0x0f, 0x64, 0xe7, 0xfa, 0x41, 0x01, 0x48,
- 0xaf, 0xff, 0x32, 0xd9, 0x82, 0x21, 0x87, 0x5b, 0xbf, 0x72, 0x07, 0x44, 0x8e, 0x19, 0xd1, 0xc4,
- 0xb4, 0x24, 0x13, 0x24, 0x3e, 0x71, 0xa0, 0x4a, 0x6f, 0x07, 0xd4, 0x73, 0xc2, 0xfb, 0x0c, 0x47,
- 0x13, 0x7c, 0x59, 0xec, 0x06, 0x24, 0x32, 0x86, 0x3c, 0xb4, 0x1f, 0x15, 0xa1, 0x1e, 0xa3, 0xbb,
- 0xd7, 0xa6, 0x92, 0x5f, 0xa9, 0x16, 0x46, 0xa7, 0x75, 0xcf, 0x96, 0x23, 0x2c, 0x76, 0xa5, 0x5a,
- 0x66, 0xe1, 0x32, 0xc6, 0xe9, 0xc8, 0x0c, 0x40, 0x5b, 0xf7, 0x03, 0xea, 0xf1, 0xd5, 0x37, 0x75,
- 0x91, 0x79, 0x25, 0xcc, 0xc1, 0x18, 0x15, 0xb9, 0x28, 0xc3, 0x67, 0x97, 0x93, 0x81, 0xe7, 0xfa,
- 0xc4, 0xc6, 0xae, 0x1c, 0x41, 0x6c, 0x6c, 0xd2, 0x82, 0x71, 0x55, 0x6b, 0x95, 0x7b, 0xb8, 0xb0,
- 0x64, 0x62, 0xff, 0x92, 0x82, 0xc0, 0x1e, 0x50, 0xed, 0xed, 0x02, 0x8c, 0x26, 0x4c, 0x1e, 0x22,
- 0x64, 0x9c, 0xf2, 0xbe, 0x4f, 0x84, 0x8c, 0x8b, 0x39, 0xcd, 0x3f, 0x09, 0x43, 0x42, 0x40, 0x69,
- 0xa7, 0x3a, 0x21, 0x42, 0x94, 0xb9, 0x6c, 0x2e, 0x93, 0x46, 0xd5, 0xf4, 0x5c, 0x26, 0xad, 0xae,
- 0xa8, 0xf2, 0x85, 0xb9, 0x5d, 0xd4, 0xae, 0xd7, 0xdc, 0x2e, 0xd2, 0x31, 0xa4, 0xd0, 0x7e, 0x5c,
- 0x02, 0xee, 0x82, 0x42, 0x9e, 0x83, 0x5a, 0x9b, 0x1a, 0x5b, 0xba, 0x63, 0xf9, 0x2a, 0x64, 0x24,
- 0xdb, 0xdd, 0xd6, 0x56, 0x54, 0xe2, 0x1d, 0x06, 0x30, 0xdb, 0x5c, 0xe6, 0x5e, 0xde, 0x11, 0x2d,
- 0x31, 0x60, 0xa8, 0xe5, 0xfb, 0x7a, 0xc7, 0xca, 0x7d, 0x02, 0x2a, 0x42, 0xf4, 0x89, 0x41, 0x24,
- 0x9e, 0x51, 0x42, 0x13, 0x03, 0x2a, 0x1d, 0x5b, 0xb7, 0x9c, 0xdc, 0xff, 0x28, 0x61, 0x2d, 0x58,
- 0x65, 0x48, 0xc2, 0xa4, 0xc3, 0x1f, 0x51, 0x60, 0x93, 0x2e, 0xd4, 0x7d, 0xc3, 0xd3, 0xdb, 0xfe,
- 0x96, 0x3e, 0xf3, 0xcc, 0xb3, 0xb9, 0x95, 0xa4, 0x88, 0x95, 0x98, 0xb3, 0xe7, 0x70, 0x76, 0xa5,
- 0x79, 0x65, 0x76, 0xe6, 0x99, 0x67, 0x31, 0xce, 0x27, 0xce, 0xf6, 0x99, 0xa7, 0x66, 0x64, 0xbf,
- 0x3f, 0x72, 0xb6, 0xcf, 0x3c, 0x35, 0x83, 0x71, 0x3e, 0xda, 0xbf, 0x17, 0xa0, 0x16, 0xd2, 0x92,
- 0x75, 0x00, 0x36, 0x02, 0x65, 0x50, 0xbd, 0x43, 0x05, 0xb8, 0xe7, 0xbb, 0xe2, 0xf5, 0xb0, 0x30,
- 0xc6, 0x80, 0x32, 0xa2, 0x0e, 0x16, 0x8f, 0x3a, 0xea, 0xe0, 0x34, 0xd4, 0xb6, 0x74, 0xc7, 0xf4,
- 0xb7, 0xf4, 0x6d, 0x31, 0x11, 0xc5, 0xe2, 0x70, 0x5e, 0x51, 0x19, 0x18, 0xd1, 0x68, 0x7f, 0x3c,
- 0x04, 0xe2, 0xd8, 0x92, 0x0d, 0x15, 0xd3, 0xf2, 0x85, 0xdf, 0x6c, 0x81, 0x97, 0x0c, 0x87, 0xca,
- 0xbc, 0x4c, 0xc7, 0x90, 0x82, 0x9c, 0x85, 0x52, 0xdb, 0x72, 0xe4, 0x89, 0x07, 0x37, 0x78, 0xad,
- 0x58, 0x0e, 0xb2, 0x34, 0x9e, 0xa5, 0xdf, 0x96, 0x2e, 0x4f, 0x22, 0x4b, 0xbf, 0x8d, 0x2c, 0x8d,
- 0x6d, 0x41, 0x6d, 0xd7, 0xdd, 0xde, 0xd0, 0x8d, 0x6d, 0xe5, 0x19, 0x55, 0xe6, 0x0b, 0x21, 0xdf,
- 0x82, 0x2e, 0x27, 0xb3, 0x30, 0x4d, 0x4b, 0xd6, 0xe1, 0x91, 0x37, 0xa8, 0xe7, 0xca, 0x51, 0xde,
- 0xb4, 0x29, 0xed, 0x28, 0x18, 0xa1, 0x42, 0x70, 0x07, 0xab, 0xcf, 0x66, 0x93, 0x60, 0xbf, 0xb2,
- 0xdc, 0x55, 0x53, 0xf7, 0x5a, 0x34, 0x58, 0xf5, 0x5c, 0x83, 0xfa, 0xbe, 0xe5, 0xb4, 0x14, 0xec,
- 0x50, 0x04, 0xbb, 0x96, 0x4d, 0x82, 0xfd, 0xca, 0x92, 0x97, 0x61, 0x42, 0x64, 0x89, 0xc5, 0x76,
- 0x76, 0x47, 0xb7, 0x6c, 0x7d, 0xc3, 0xb2, 0xd5, 0xaf, 0xbd, 0x46, 0xc5, 0xb9, 0xc2, 0x5a, 0x1f,
- 0x1a, 0xec, 0x5b, 0x9a, 0x5c, 0x85, 0x71, 0x75, 0xaa, 0xb4, 0x4a, 0xbd, 0x66, 0x78, 0x94, 0x3d,
- 0xda, 0xb8, 0xc0, 0xf6, 0x7b, 0xf3, 0xb4, 0xe3, 0x51, 0x83, 0x6b, 0x5d, 0x29, 0x2a, 0xec, 0x29,
- 0x47, 0x10, 0xce, 0xf0, 0xf3, 0xea, 0xf5, 0xce, 0x9c, 0xeb, 0xda, 0xa6, 0x7b, 0xcb, 0x51, 0x6d,
- 0x17, 0x8a, 0x0d, 0x3f, 0x48, 0x6a, 0x66, 0x52, 0x60, 0x9f, 0x92, 0xac, 0xe5, 0x3c, 0x67, 0xde,
- 0xbd, 0xe5, 0xa4, 0x51, 0x21, 0x6a, 0x79, 0xb3, 0x0f, 0x0d, 0xf6, 0x2d, 0x4d, 0x16, 0x80, 0xa4,
- 0x5b, 0xb0, 0xde, 0xe1, 0xca, 0xd0, 0x68, 0xe3, 0x8c, 0x88, 0x8f, 0x91, 0xce, 0xc5, 0x8c, 0x12,
- 0x64, 0x19, 0x4e, 0xa7, 0x53, 0x19, 0x3b, 0xee, 0x24, 0x3f, 0x2a, 0x22, 0x63, 0x62, 0x46, 0x3e,
- 0x66, 0x96, 0xd2, 0xfe, 0xa4, 0x08, 0xa3, 0x89, 0x0b, 0xd5, 0x0f, 0xdc, 0xc5, 0x55, 0xa6, 0x81,
- 0xb6, 0xfd, 0xd6, 0xd2, 0xfc, 0x15, 0xaa, 0x9b, 0xd4, 0xbb, 0x46, 0xd5, 0xe5, 0x77, 0x3e, 0xa9,
- 0xac, 0x24, 0x72, 0x30, 0x45, 0x49, 0x36, 0xa1, 0x22, 0xec, 0xa9, 0x79, 0x7f, 0x95, 0xa0, 0x64,
- 0xc4, 0x8d, 0xaa, 0x7c, 0xc9, 0x11, 0x26, 0x55, 0x01, 0xaf, 0x05, 0x30, 0x12, 0xa7, 0x60, 0x13,
- 0x49, 0xa4, 0xac, 0x0d, 0x27, 0x14, 0xb5, 0x25, 0x28, 0x05, 0xc1, 0xa0, 0x57, 0x62, 0x85, 0x7d,
- 0x7e, 0x6d, 0x19, 0x19, 0x86, 0xb6, 0xc9, 0xbe, 0x9d, 0xef, 0x5b, 0xae, 0x23, 0xe3, 0x23, 0xaf,
- 0xc3, 0x70, 0x20, 0x4d, 0x54, 0x83, 0x5d, 0xe9, 0xe5, 0xe6, 0x62, 0x65, 0x9e, 0x52, 0x58, 0xda,
- 0xdf, 0x15, 0xa1, 0x16, 0x6e, 0x27, 0x0f, 0x10, 0x77, 0xd8, 0x85, 0x5a, 0xe8, 0x6f, 0x93, 0xfb,
- 0xb7, 0x67, 0x91, 0x1b, 0x08, 0xdf, 0x01, 0x85, 0xaf, 0x18, 0xf1, 0x88, 0xfb, 0xf2, 0x94, 0x72,
- 0xf8, 0xf2, 0x74, 0x60, 0x38, 0xf0, 0xac, 0x56, 0x4b, 0xea, 0xb6, 0x79, 0x9c, 0x79, 0x42, 0x71,
- 0xad, 0x09, 0x40, 0x29, 0x59, 0xf1, 0x82, 0x8a, 0x8d, 0xf6, 0x1a, 0x8c, 0xa7, 0x29, 0xb9, 0xe2,
- 0x67, 0x6c, 0x51, 0xb3, 0x6b, 0x2b, 0x19, 0x47, 0x8a, 0x9f, 0x4c, 0xc7, 0x90, 0x82, 0x6d, 0xfe,
- 0xd8, 0x67, 0x7a, 0xc3, 0x75, 0xd4, 0xb6, 0x9a, 0xeb, 0xd0, 0x6b, 0x32, 0x0d, 0xc3, 0x5c, 0xed,
- 0x9f, 0x4b, 0x70, 0x36, 0x32, 0x0a, 0xac, 0xe8, 0x8e, 0xde, 0x3a, 0xc0, 0xbf, 0xae, 0xde, 0xbf,
- 0x24, 0x71, 0xd8, 0xe0, 0xf1, 0xa5, 0x07, 0x20, 0x78, 0xfc, 0x4f, 0x0a, 0xc0, 0x7d, 0x03, 0xc9,
- 0x17, 0x61, 0x44, 0x8f, 0xfd, 0xe6, 0x50, 0x7e, 0xce, 0xcb, 0xb9, 0x3f, 0x27, 0x77, 0x41, 0x0c,
- 0x7d, 0x5d, 0xe2, 0xa9, 0x98, 0x60, 0x48, 0x5c, 0xa8, 0x6e, 0xea, 0xb6, 0xcd, 0x74, 0xa1, 0xdc,
- 0x87, 0x1c, 0x09, 0xe6, 0xbc, 0x9b, 0x2f, 0x48, 0x68, 0x0c, 0x99, 0x68, 0xff, 0x54, 0x80, 0xd1,
- 0xa6, 0x6d, 0x99, 0x96, 0xd3, 0x3a, 0xc6, 0xa8, 0xf1, 0x37, 0xa0, 0xe2, 0xdb, 0x96, 0x49, 0x07,
- 0x9c, 0xc7, 0xc5, 0x0a, 0xc2, 0x00, 0x50, 0xe0, 0x24, 0xc3, 0xd0, 0x97, 0x0e, 0x10, 0x86, 0xfe,
- 0x67, 0x43, 0x20, 0xfd, 0x4b, 0x49, 0x17, 0x6a, 0x2d, 0x15, 0xdd, 0x5a, 0xb6, 0xf1, 0x4a, 0x8e,
- 0xc8, 0x68, 0x89, 0x38, 0xd9, 0x62, 0xd6, 0x0d, 0x13, 0x31, 0xe2, 0x44, 0x68, 0xf2, 0xcf, 0x96,
- 0xf3, 0x39, 0xff, 0x6c, 0x29, 0xd8, 0xf5, 0xfe, 0xdb, 0x52, 0x87, 0xf2, 0x56, 0x10, 0x74, 0xe4,
- 0xb8, 0x1a, 0xdc, 0x81, 0x38, 0x0a, 0xce, 0x21, 0xb4, 0x11, 0xf6, 0x8e, 0x1c, 0x9a, 0xb1, 0x70,
- 0xf4, 0xf0, 0x87, 0x4a, 0x73, 0xb9, 0x0e, 0xba, 0xe3, 0x2c, 0xd8, 0x3b, 0x72, 0x68, 0xf2, 0x05,
- 0xa8, 0x07, 0x9e, 0xee, 0xf8, 0x9b, 0xae, 0xd7, 0xa6, 0x9e, 0xdc, 0x1d, 0x2e, 0xe4, 0xf8, 0xb9,
- 0xe3, 0x5a, 0x84, 0x26, 0x4e, 0xd0, 0x12, 0x49, 0x18, 0xe7, 0x46, 0xb6, 0xa1, 0xda, 0x35, 0x45,
- 0xc5, 0xa4, 0xd9, 0x64, 0x36, 0xcf, 0xff, 0x3a, 0x63, 0xc7, 0xd8, 0xea, 0x0d, 0x43, 0x06, 0xc9,
- 0x7f, 0x87, 0x0d, 0x1f, 0xd5, 0xbf, 0xc3, 0xe2, 0xbd, 0x31, 0x2b, 0x72, 0x00, 0x69, 0x4b, 0x8d,
- 0xd2, 0x69, 0x49, 0x2f, 0x9c, 0x85, 0xdc, 0xca, 0x9e, 0x60, 0x59, 0x0f, 0xb5, 0x52, 0xa7, 0x85,
- 0x8a, 0x87, 0xd6, 0x06, 0x69, 0xdd, 0x26, 0x46, 0xe2, 0x0f, 0x1b, 0xe2, 0x3a, 0xcb, 0xf4, 0xc1,
- 0xe6, 0x83, 0xf0, 0x57, 0x0f, 0xb1, 0x08, 0xbf, 0x99, 0xbf, 0xd2, 0xd0, 0xfe, 0xbe, 0x08, 0xa5,
- 0xb5, 0xe5, 0xa6, 0x88, 0xda, 0xc7, 0x7f, 0x5f, 0x43, 0x9b, 0xdb, 0x56, 0xe7, 0x26, 0xf5, 0xac,
- 0xcd, 0x5d, 0xb9, 0xe9, 0x8d, 0x45, 0xed, 0x4b, 0x53, 0x60, 0x46, 0x29, 0xf2, 0x0a, 0x8c, 0x18,
- 0xfa, 0x1c, 0xf5, 0x82, 0x41, 0xb6, 0xf4, 0xfc, 0xde, 0xde, 0xdc, 0x6c, 0x54, 0x1c, 0x13, 0x60,
- 0x64, 0x1d, 0xc0, 0x88, 0xa0, 0x4b, 0x87, 0x36, 0x44, 0xc4, 0x80, 0x63, 0x40, 0x04, 0xa1, 0xb6,
- 0xcd, 0x48, 0x39, 0x6a, 0xf9, 0x30, 0xa8, 0xbc, 0xe7, 0x5c, 0x53, 0x65, 0x31, 0x82, 0xd1, 0x1c,
- 0x18, 0x4d, 0xfc, 0x76, 0x83, 0x7c, 0x0c, 0xaa, 0x6e, 0x27, 0x36, 0x9d, 0xd6, 0xb8, 0xbf, 0x5f,
- 0xf5, 0x86, 0x4c, 0xbb, 0xb3, 0x37, 0x39, 0xba, 0xec, 0xb6, 0x2c, 0x43, 0x25, 0x60, 0x48, 0x4e,
- 0x34, 0x18, 0xe2, 0x97, 0x6d, 0xd4, 0x4f, 0x37, 0xf8, 0xda, 0xc1, 0xe3, 0xe2, 0xfb, 0x28, 0x73,
- 0xb4, 0x2f, 0x95, 0x21, 0x3a, 0x13, 0x22, 0x3e, 0x0c, 0x09, 0x67, 0x62, 0x39, 0x73, 0x1f, 0xab,
- 0xdf, 0xb2, 0x64, 0x45, 0x5a, 0x50, 0x7a, 0xcd, 0xdd, 0xc8, 0x3d, 0x71, 0xc7, 0x6e, 0xd9, 0x0a,
- 0x2b, 0x55, 0x2c, 0x01, 0x19, 0x07, 0xf2, 0xeb, 0x05, 0x38, 0xe9, 0xa7, 0x95, 0x4e, 0xd9, 0x1d,
- 0x30, 0xbf, 0x76, 0x9d, 0x56, 0x63, 0xa5, 0x63, 0x66, 0xbf, 0x6c, 0xec, 0xad, 0x0b, 0x93, 0xbf,
- 0x38, 0xac, 0x91, 0xdd, 0x69, 0x31, 0xe7, 0xaf, 0xe2, 0x92, 0xf2, 0x4f, 0xa6, 0xa1, 0x64, 0xa5,
- 0x7d, 0xa5, 0x08, 0xf5, 0xd8, 0x6c, 0x9d, 0xfb, 0x5f, 0x2e, 0xb7, 0x53, 0xff, 0x72, 0x59, 0x1d,
- 0xfc, 0xec, 0x32, 0xaa, 0xd5, 0x71, 0xff, 0xce, 0xe5, 0xcf, 0x8b, 0x50, 0x5a, 0x9f, 0x5f, 0x48,
- 0x6e, 0x17, 0x0b, 0xf7, 0x61, 0xbb, 0xb8, 0x05, 0xc3, 0x1b, 0x5d, 0xcb, 0x0e, 0x2c, 0x27, 0x77,
- 0x1c, 0x00, 0xf5, 0xeb, 0x1b, 0x79, 0x9d, 0x56, 0xa0, 0xa2, 0x82, 0x27, 0x2d, 0x18, 0x6e, 0x89,
- 0x40, 0x6c, 0xb9, 0x3d, 0xba, 0x64, 0x40, 0x37, 0xc1, 0x48, 0xbe, 0xa0, 0x42, 0xd7, 0x76, 0x41,
- 0xfe, 0x3c, 0xfb, 0xbe, 0x4b, 0x53, 0xfb, 0x02, 0x84, 0x5a, 0xc0, 0xfd, 0x67, 0xfe, 0xaf, 0x05,
- 0x48, 0x2a, 0x3e, 0xf7, 0xbf, 0x37, 0x6d, 0xa7, 0x7b, 0xd3, 0xfc, 0x51, 0x0c, 0xbe, 0xec, 0x0e,
- 0xa5, 0xfd, 0x51, 0x11, 0x86, 0xee, 0xdb, 0xdd, 0x4d, 0x9a, 0x70, 0x4e, 0x9b, 0xcb, 0x39, 0x31,
- 0xf6, 0x75, 0x4d, 0x6b, 0xa7, 0x5c, 0xd3, 0xf2, 0xfe, 0xac, 0xf3, 0x1e, 0x8e, 0x69, 0x7f, 0x5d,
- 0x00, 0x39, 0x2d, 0x2f, 0x39, 0x7e, 0xa0, 0x3b, 0x06, 0xff, 0x67, 0xbc, 0x5c, 0x03, 0xf2, 0x7a,
- 0x40, 0x48, 0x2f, 0x21, 0xb1, 0xec, 0xf3, 0x67, 0x35, 0xe7, 0x93, 0x8f, 0x40, 0x75, 0xcb, 0xf5,
- 0x03, 0x3e, 0xcf, 0x17, 0x93, 0x76, 0x9d, 0x2b, 0x32, 0x1d, 0x43, 0x8a, 0xf4, 0x49, 0x61, 0xa5,
- 0xff, 0x49, 0xa1, 0xf6, 0xad, 0x22, 0x8c, 0xbc, 0x57, 0x2e, 0xa0, 0x66, 0xb9, 0xf2, 0x95, 0x72,
- 0xba, 0xf2, 0x95, 0x0f, 0xe3, 0xca, 0xa7, 0x7d, 0xaf, 0x00, 0x70, 0xdf, 0x6e, 0xbf, 0x9a, 0x49,
- 0x2f, 0xbb, 0xdc, 0xfd, 0x2a, 0xdb, 0xc7, 0xee, 0xf7, 0x2b, 0xaa, 0x49, 0xdc, 0xc3, 0xee, 0xcd,
- 0x02, 0x8c, 0xe9, 0x09, 0xaf, 0xb5, 0xdc, 0xaa, 0x65, 0xca, 0x09, 0x2e, 0xbc, 0xe9, 0x97, 0x4c,
- 0xc7, 0x14, 0x5b, 0xf2, 0x7c, 0x14, 0x79, 0xf5, 0x7a, 0xd4, 0xed, 0x7b, 0x42, 0xa6, 0x72, 0x35,
- 0x27, 0x41, 0x79, 0x0f, 0x2f, 0xc1, 0xd2, 0x91, 0x78, 0x09, 0xc6, 0xef, 0x3f, 0x95, 0xef, 0x7a,
- 0xff, 0x69, 0x07, 0x6a, 0x9b, 0x9e, 0xdb, 0xe6, 0x8e, 0x78, 0xf2, 0x37, 0x9f, 0x97, 0x73, 0xac,
- 0x29, 0xd1, 0x0f, 0xae, 0x23, 0x1b, 0xcf, 0x82, 0xc2, 0xc7, 0x88, 0x15, 0x37, 0x48, 0xbb, 0x82,
- 0xeb, 0xd0, 0x51, 0x72, 0x0d, 0xe7, 0x92, 0x35, 0x81, 0x8e, 0x8a, 0x4d, 0xd2, 0xf9, 0x6e, 0xf8,
- 0xfe, 0x38, 0xdf, 0x69, 0xbf, 0x58, 0x56, 0x13, 0xd8, 0x03, 0x17, 0xe4, 0xef, 0xbd, 0x7f, 0x6b,
- 0x32, 0x7d, 0xa5, 0x71, 0xf8, 0x3e, 0x5e, 0x69, 0xac, 0x0e, 0xe4, 0xea, 0xb5, 0x57, 0x82, 0xd4,
- 0xbe, 0xe9, 0xfd, 0xd3, 0x89, 0xff, 0x54, 0xa7, 0x13, 0x6f, 0x15, 0x21, 0x9a, 0x08, 0x0e, 0xe9,
- 0xbd, 0xf1, 0x32, 0x54, 0xdb, 0xfa, 0xed, 0x79, 0x6a, 0xeb, 0xbb, 0x79, 0xfe, 0xcd, 0xb8, 0x22,
- 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80, 0x15, 0xc6, 0x47, 0xce, 0x6d, 0x6d, 0x8e, 0x42, 0x2d, 0x0b,
- 0x7b, 0x56, 0xf4, 0x8e, 0x31, 0x36, 0xda, 0x5f, 0x15, 0x41, 0x06, 0xd2, 0x26, 0x14, 0x2a, 0x9b,
- 0xd6, 0x6d, 0x6a, 0xe6, 0xf6, 0x64, 0x8c, 0xfd, 0x31, 0x57, 0x98, 0xd3, 0x79, 0x02, 0x0a, 0x74,
- 0x6e, 0x27, 0x15, 0xc7, 0x23, 0x52, 0x7e, 0x39, 0xec, 0xa4, 0xf1, 0x63, 0x16, 0x69, 0x27, 0x15,
- 0x49, 0xa8, 0x78, 0x08, 0xb3, 0x2c, 0x3f, 0xa3, 0x96, 0x22, 0xcd, 0x63, 0x96, 0x8d, 0x9d, 0x75,
- 0x2b, 0xb3, 0xac, 0x2f, 0xee, 0x34, 0x4b, 0x1e, 0x8d, 0xcf, 0x7d, 0xf7, 0xfb, 0x17, 0x1e, 0xfa,
- 0xde, 0xf7, 0x2f, 0x3c, 0xf4, 0xce, 0xf7, 0x2f, 0x3c, 0xf4, 0xa5, 0xfd, 0x0b, 0x85, 0xef, 0xee,
- 0x5f, 0x28, 0x7c, 0x6f, 0xff, 0x42, 0xe1, 0x9d, 0xfd, 0x0b, 0x85, 0x7f, 0xd8, 0xbf, 0x50, 0xf8,
- 0x95, 0x7f, 0xbc, 0xf0, 0xd0, 0x67, 0x9f, 0x8b, 0xaa, 0x30, 0xad, 0xaa, 0x30, 0xad, 0x18, 0x4e,
- 0x77, 0xb6, 0x5b, 0xd3, 0xac, 0x0a, 0x51, 0x8a, 0xaa, 0xc2, 0x7f, 0x04, 0x00, 0x00, 0xff, 0xff,
- 0xb2, 0x71, 0xfd, 0x11, 0x25, 0x93, 0x00, 0x00,
+ 0x16, 0x6a, 0x6a, 0x23, 0xa8, 0x5a, 0x36, 0xd3, 0x28, 0xa2, 0x76, 0x8f, 0x48, 0x5f, 0xef, 0x5a,
+ 0x1e, 0x6d, 0x53, 0x27, 0xf0, 0x1b, 0x27, 0xd5, 0x36, 0x59, 0xe5, 0xfa, 0x18, 0xa1, 0x91, 0x8d,
+ 0x5e, 0xeb, 0x8e, 0x30, 0x4d, 0x3c, 0xde, 0x67, 0x56, 0x1f, 0xc0, 0xb4, 0xf3, 0x05, 0x38, 0x11,
+ 0x9a, 0x5f, 0xe4, 0x0e, 0x5e, 0x18, 0x2b, 0x9e, 0x66, 0xc5, 0x97, 0x92, 0x59, 0x77, 0xf6, 0x26,
+ 0x1f, 0xcb, 0xd8, 0xc3, 0x47, 0x04, 0x98, 0x06, 0xd3, 0xfe, 0xa0, 0x04, 0xbd, 0x6a, 0x77, 0x52,
+ 0x68, 0x85, 0xa3, 0x16, 0x5a, 0xfa, 0x83, 0xc4, 0xf4, 0xf9, 0xbc, 0x2c, 0x96, 0xff, 0xa3, 0xb2,
+ 0x1a, 0xa6, 0x74, 0xd4, 0x0d, 0xf3, 0xa0, 0x8c, 0x1d, 0xed, 0xad, 0x32, 0x8c, 0xcd, 0xeb, 0xb4,
+ 0xed, 0x3a, 0xf7, 0xdc, 0x84, 0x14, 0x1e, 0x88, 0x4d, 0xc8, 0x25, 0xa8, 0x7a, 0xb4, 0x63, 0x5b,
+ 0x86, 0xee, 0xf3, 0xa6, 0x97, 0x46, 0x3f, 0x94, 0x69, 0x18, 0xe6, 0xf6, 0xd9, 0x7c, 0x96, 0x1e,
+ 0xc8, 0xcd, 0x67, 0xf9, 0xdd, 0xdf, 0x7c, 0x6a, 0x5f, 0x29, 0x02, 0x57, 0x54, 0xc8, 0x45, 0x28,
+ 0xb3, 0x45, 0x38, 0x6d, 0xf2, 0xe0, 0x1d, 0x87, 0xe7, 0x90, 0x73, 0x50, 0x0c, 0x5c, 0x39, 0xf2,
+ 0x40, 0xe6, 0x17, 0xd7, 0x5c, 0x2c, 0x06, 0x2e, 0x79, 0x03, 0xc0, 0x70, 0x1d, 0xd3, 0x52, 0xb6,
+ 0xf0, 0x7c, 0x1f, 0xb6, 0xe0, 0x7a, 0xb7, 0x74, 0xcf, 0x9c, 0x0b, 0x11, 0xc5, 0xf6, 0x23, 0x7a,
+ 0xc7, 0x18, 0x37, 0xf2, 0x02, 0x0c, 0xb9, 0xce, 0x42, 0xd7, 0xb6, 0xb9, 0x40, 0x6b, 0x8d, 0xff,
+ 0xc2, 0xf6, 0x84, 0x37, 0x78, 0xca, 0x9d, 0xbd, 0xc9, 0xb3, 0x42, 0xbf, 0x65, 0x6f, 0x2f, 0x79,
+ 0x56, 0x60, 0x39, 0xad, 0x66, 0xe0, 0xe9, 0x01, 0x6d, 0xed, 0xa2, 0x2c, 0xa6, 0x7d, 0xbd, 0x00,
+ 0xf5, 0x05, 0xeb, 0x36, 0x35, 0x5f, 0xb2, 0x1c, 0xd3, 0xbd, 0x45, 0x10, 0x86, 0x6c, 0xea, 0xb4,
+ 0x82, 0x2d, 0xd9, 0xfb, 0xa7, 0x62, 0x63, 0x2d, 0x3c, 0x42, 0x89, 0xea, 0xdf, 0xa6, 0x81, 0xce,
+ 0x46, 0xdf, 0x7c, 0x57, 0x1a, 0xf9, 0xc5, 0xa6, 0x94, 0x23, 0xa0, 0x44, 0x22, 0xd3, 0x50, 0x13,
+ 0xda, 0xa7, 0xe5, 0xb4, 0xb8, 0x0c, 0xab, 0xd1, 0xa4, 0xd7, 0x54, 0x19, 0x18, 0xd1, 0x68, 0xbb,
+ 0x70, 0xb2, 0x47, 0x0c, 0xc4, 0x84, 0x72, 0xa0, 0xb7, 0xd4, 0xfc, 0xba, 0x30, 0xb0, 0x80, 0xd7,
+ 0xf4, 0x56, 0x4c, 0xb8, 0x7c, 0x8d, 0x5f, 0xd3, 0xd9, 0x1a, 0xcf, 0xd0, 0xb5, 0x9f, 0x17, 0xa0,
+ 0xba, 0xd0, 0x75, 0x0c, 0xbe, 0x37, 0xba, 0xb7, 0x29, 0x4c, 0x29, 0x0c, 0xc5, 0x4c, 0x85, 0xa1,
+ 0x0b, 0x43, 0xdb, 0xb7, 0x42, 0x85, 0xa2, 0x3e, 0xb3, 0x32, 0x78, 0xaf, 0x90, 0x55, 0x9a, 0xba,
+ 0xc6, 0xf1, 0xc4, 0x49, 0xcd, 0x98, 0xac, 0xd0, 0xd0, 0xb5, 0x97, 0x38, 0x53, 0xc9, 0xec, 0xdc,
+ 0xc7, 0xa0, 0x1e, 0x23, 0x3b, 0x94, 0xd1, 0xf6, 0x77, 0xca, 0x30, 0xb4, 0xd8, 0x6c, 0xce, 0xae,
+ 0x2e, 0x91, 0x67, 0xa0, 0x2e, 0x8d, 0xf8, 0xd7, 0x23, 0x19, 0x84, 0x67, 0x38, 0xcd, 0x28, 0x0b,
+ 0xe3, 0x74, 0x4c, 0x1d, 0xf3, 0xa8, 0x6e, 0xb7, 0xe5, 0x60, 0x09, 0xd5, 0x31, 0x64, 0x89, 0x28,
+ 0xf2, 0x88, 0x0e, 0x63, 0x6c, 0x87, 0xc7, 0x44, 0x28, 0x76, 0x6f, 0x72, 0xd8, 0x1c, 0x70, 0x7f,
+ 0xc7, 0x95, 0xc4, 0xf5, 0x04, 0x00, 0xa6, 0x00, 0xc9, 0xf3, 0x50, 0xd5, 0xbb, 0xc1, 0x16, 0x57,
+ 0xa0, 0xc5, 0xd8, 0x38, 0xcf, 0xcf, 0x38, 0x64, 0xda, 0x9d, 0xbd, 0xc9, 0x91, 0x6b, 0xd8, 0x78,
+ 0x46, 0xbd, 0x63, 0x48, 0xcd, 0x2a, 0xa7, 0x76, 0x8c, 0xb2, 0x72, 0x95, 0x43, 0x57, 0x6e, 0x35,
+ 0x01, 0x80, 0x29, 0x40, 0xf2, 0x0a, 0x8c, 0x6c, 0xd3, 0xdd, 0x40, 0xdf, 0x90, 0x0c, 0x86, 0x0e,
+ 0xc3, 0x60, 0x9c, 0xa9, 0x70, 0xd7, 0x62, 0xc5, 0x31, 0x01, 0x46, 0x7c, 0x38, 0xbd, 0x4d, 0xbd,
+ 0x0d, 0xea, 0xb9, 0x72, 0xf7, 0x29, 0x99, 0x0c, 0x1f, 0x86, 0xc9, 0xc4, 0xfe, 0xde, 0xe4, 0xe9,
+ 0x6b, 0x19, 0x30, 0x98, 0x09, 0xae, 0xfd, 0xac, 0x08, 0x27, 0x16, 0xc5, 0x29, 0xaa, 0xeb, 0x89,
+ 0x45, 0x98, 0x9c, 0x85, 0x92, 0xd7, 0xe9, 0xf2, 0x9e, 0x53, 0x12, 0x76, 0x52, 0x5c, 0x5d, 0x47,
+ 0x96, 0x46, 0x5e, 0x86, 0xaa, 0x29, 0xa7, 0x0c, 0xb9, 0xf9, 0x3d, 0xec, 0x44, 0xc3, 0x17, 0x41,
+ 0xf5, 0x86, 0x21, 0x1a, 0xd3, 0xf4, 0xdb, 0x7e, 0xab, 0x69, 0xbd, 0x41, 0xe5, 0x7e, 0x90, 0x6b,
+ 0xfa, 0x2b, 0x22, 0x09, 0x55, 0x1e, 0x5b, 0x55, 0xb7, 0xe9, 0xae, 0xd8, 0x0d, 0x95, 0xa3, 0x55,
+ 0xf5, 0x9a, 0x4c, 0xc3, 0x30, 0x97, 0x4c, 0xaa, 0xc1, 0xc2, 0x7a, 0x41, 0x59, 0xec, 0xe4, 0x6f,
+ 0xb2, 0x04, 0x39, 0x6e, 0xd8, 0x94, 0xf9, 0x9a, 0x15, 0x04, 0xd4, 0x93, 0xcd, 0x38, 0xd0, 0x94,
+ 0x79, 0x95, 0x23, 0xa0, 0x44, 0x22, 0x1f, 0x86, 0x1a, 0x07, 0x6f, 0xd8, 0xee, 0x06, 0x6f, 0xb8,
+ 0x9a, 0xd8, 0xd3, 0xdf, 0x54, 0x89, 0x18, 0xe5, 0x6b, 0xbf, 0x28, 0xc2, 0x99, 0x45, 0x1a, 0x08,
+ 0xad, 0x66, 0x9e, 0x76, 0x6c, 0x77, 0x97, 0xa9, 0x96, 0x48, 0x5f, 0x27, 0x2f, 0x02, 0x58, 0xfe,
+ 0x46, 0x73, 0xc7, 0xe0, 0xe3, 0x40, 0x8c, 0xe1, 0x8b, 0x72, 0x48, 0xc2, 0x52, 0xb3, 0x21, 0x73,
+ 0xee, 0x24, 0xde, 0x30, 0x56, 0x26, 0xda, 0x5e, 0x15, 0xef, 0xb2, 0xbd, 0x6a, 0x02, 0x74, 0x22,
+ 0x05, 0xb5, 0xc4, 0x29, 0xff, 0xbb, 0x62, 0x73, 0x18, 0xdd, 0x34, 0x06, 0x93, 0x47, 0x65, 0x74,
+ 0x60, 0xdc, 0xa4, 0x9b, 0x7a, 0xd7, 0x0e, 0x42, 0xa5, 0x5a, 0x0e, 0xe2, 0x83, 0xeb, 0xe5, 0xe1,
+ 0x09, 0xef, 0x7c, 0x0a, 0x09, 0x7b, 0xb0, 0xb5, 0xdf, 0x2d, 0xc1, 0xb9, 0x45, 0x1a, 0x84, 0x16,
+ 0x17, 0x39, 0x3b, 0x36, 0x3b, 0xd4, 0x60, 0xad, 0xf0, 0x66, 0x01, 0x86, 0x6c, 0x7d, 0x83, 0xda,
+ 0x6c, 0xf5, 0x62, 0x5f, 0xf3, 0xea, 0xc0, 0x0b, 0x41, 0x7f, 0x2e, 0x53, 0xcb, 0x9c, 0x43, 0x6a,
+ 0x69, 0x10, 0x89, 0x28, 0xd9, 0xb3, 0x49, 0xdd, 0xb0, 0xbb, 0x7e, 0x40, 0xbd, 0x55, 0xd7, 0x0b,
+ 0xa4, 0x3e, 0x19, 0x4e, 0xea, 0x73, 0x51, 0x16, 0xc6, 0xe9, 0xc8, 0x0c, 0x80, 0x61, 0x5b, 0xd4,
+ 0x09, 0x78, 0x29, 0x31, 0xae, 0x88, 0x6a, 0xdf, 0xb9, 0x30, 0x07, 0x63, 0x54, 0x8c, 0x55, 0xdb,
+ 0x75, 0xac, 0xc0, 0x15, 0xac, 0xca, 0x49, 0x56, 0x2b, 0x51, 0x16, 0xc6, 0xe9, 0x78, 0x31, 0x1a,
+ 0x78, 0x96, 0xe1, 0xf3, 0x62, 0x95, 0x54, 0xb1, 0x28, 0x0b, 0xe3, 0x74, 0x6c, 0xcd, 0x8b, 0x7d,
+ 0xff, 0xa1, 0xd6, 0xbc, 0x6f, 0xd7, 0xe0, 0x42, 0x42, 0xac, 0x81, 0x1e, 0xd0, 0xcd, 0xae, 0xdd,
+ 0xa4, 0x81, 0x6a, 0xc0, 0x01, 0xd7, 0xc2, 0xff, 0x1b, 0xb5, 0xbb, 0xf0, 0xdd, 0x30, 0x8e, 0xa6,
+ 0xdd, 0x7b, 0x2a, 0x78, 0xa0, 0xb6, 0x9f, 0x86, 0x9a, 0xa3, 0x07, 0x3e, 0x1f, 0xb8, 0x72, 0x8c,
+ 0x86, 0x6a, 0xd8, 0x75, 0x95, 0x81, 0x11, 0x0d, 0x59, 0x85, 0xd3, 0x52, 0xc4, 0x97, 0x6f, 0x77,
+ 0x5c, 0x2f, 0xa0, 0x9e, 0x28, 0x2b, 0x97, 0x53, 0x59, 0xf6, 0xf4, 0x4a, 0x06, 0x0d, 0x66, 0x96,
+ 0x24, 0x2b, 0x70, 0xca, 0x10, 0xe7, 0xd9, 0xd4, 0x76, 0x75, 0x53, 0x01, 0x0a, 0x03, 0x57, 0xb8,
+ 0x35, 0x9a, 0xeb, 0x25, 0xc1, 0xac, 0x72, 0xe9, 0xde, 0x3c, 0x34, 0x50, 0x6f, 0x1e, 0x1e, 0xa4,
+ 0x37, 0x57, 0x07, 0xeb, 0xcd, 0xb5, 0x83, 0xf5, 0x66, 0x26, 0x79, 0xd6, 0x8f, 0xa8, 0xc7, 0xd4,
+ 0x13, 0xb1, 0xc2, 0xc6, 0xdc, 0x25, 0x42, 0xc9, 0x37, 0x33, 0x68, 0x30, 0xb3, 0x24, 0xd9, 0x80,
+ 0x73, 0x22, 0xfd, 0xb2, 0x63, 0x78, 0xbb, 0x1d, 0xb6, 0xf0, 0xc4, 0x70, 0xeb, 0x09, 0x0b, 0xe3,
+ 0xb9, 0x66, 0x5f, 0x4a, 0xbc, 0x0b, 0x0a, 0xf9, 0x04, 0x8c, 0x8a, 0x56, 0x5a, 0xd1, 0x3b, 0x1c,
+ 0x56, 0x38, 0x4f, 0x3c, 0x2c, 0x61, 0x47, 0xe7, 0xe2, 0x99, 0x98, 0xa4, 0x25, 0xb3, 0x70, 0xa2,
+ 0xb3, 0x63, 0xb0, 0xc7, 0xa5, 0xcd, 0xeb, 0x94, 0x9a, 0xd4, 0xe4, 0xa7, 0x35, 0xb5, 0xc6, 0x23,
+ 0xca, 0xd0, 0xb1, 0x9a, 0xcc, 0xc6, 0x34, 0x3d, 0x79, 0x1e, 0x46, 0xfc, 0x40, 0xf7, 0x02, 0x69,
+ 0xd6, 0x9b, 0x18, 0x13, 0xce, 0x25, 0xca, 0xea, 0xd5, 0x8c, 0xe5, 0x61, 0x82, 0x32, 0x73, 0xbd,
+ 0x38, 0x71, 0x7c, 0xeb, 0x45, 0x9e, 0xd9, 0xea, 0x4f, 0x8b, 0x70, 0x71, 0x91, 0x06, 0x2b, 0xae,
+ 0x23, 0x8d, 0xa2, 0x59, 0xcb, 0xfe, 0x81, 0x6c, 0xa2, 0xc9, 0x45, 0xbb, 0x78, 0xa4, 0x8b, 0x76,
+ 0xe9, 0x88, 0x16, 0xed, 0xf2, 0x31, 0x2e, 0xda, 0xbf, 0x5f, 0x84, 0x47, 0x12, 0x92, 0x5c, 0x75,
+ 0x4d, 0x35, 0xe1, 0xbf, 0x2f, 0xc0, 0x03, 0x08, 0xf0, 0x8e, 0xd0, 0x3b, 0xf9, 0xb1, 0x56, 0x4a,
+ 0xe3, 0xf9, 0x5a, 0x5a, 0xe3, 0x79, 0x25, 0xcf, 0xca, 0x97, 0xc1, 0xe1, 0x40, 0x2b, 0xde, 0x55,
+ 0x20, 0x9e, 0x3c, 0x84, 0x13, 0xa6, 0x9f, 0x98, 0xd2, 0x13, 0x7a, 0xaf, 0x61, 0x0f, 0x05, 0x66,
+ 0x94, 0x22, 0x4d, 0x78, 0xd8, 0xa7, 0x4e, 0x60, 0x39, 0xd4, 0x4e, 0xc2, 0x09, 0x6d, 0xe8, 0x31,
+ 0x09, 0xf7, 0x70, 0x33, 0x8b, 0x08, 0xb3, 0xcb, 0xe6, 0x99, 0x07, 0xfe, 0x1c, 0xb8, 0xca, 0x29,
+ 0x44, 0x73, 0x64, 0x1a, 0xcb, 0x9b, 0x69, 0x8d, 0xe5, 0xd5, 0xfc, 0xed, 0x36, 0x98, 0xb6, 0x32,
+ 0x03, 0xc0, 0x5b, 0x21, 0xae, 0xae, 0x84, 0x8b, 0x34, 0x86, 0x39, 0x18, 0xa3, 0x62, 0x0b, 0x90,
+ 0x92, 0x73, 0x5c, 0x53, 0x09, 0x17, 0xa0, 0x66, 0x3c, 0x13, 0x93, 0xb4, 0x7d, 0xb5, 0x9d, 0xca,
+ 0xc0, 0xda, 0xce, 0x55, 0x20, 0x09, 0xc3, 0xa3, 0xc0, 0x1b, 0x4a, 0x3a, 0x4f, 0x2e, 0xf5, 0x50,
+ 0x60, 0x46, 0xa9, 0x3e, 0x5d, 0x79, 0xf8, 0x68, 0xbb, 0x72, 0x75, 0xf0, 0xae, 0x4c, 0x5e, 0x85,
+ 0xb3, 0x9c, 0x95, 0x94, 0x4f, 0x12, 0x58, 0xe8, 0x3d, 0x1f, 0x90, 0xc0, 0x67, 0xb1, 0x1f, 0x21,
+ 0xf6, 0xc7, 0x60, 0xed, 0x63, 0x78, 0xd4, 0x64, 0xcc, 0x75, 0xbb, 0xbf, 0x4e, 0x34, 0x97, 0x41,
+ 0x83, 0x99, 0x25, 0x59, 0x17, 0x0b, 0x58, 0x37, 0xd4, 0x37, 0x6c, 0x6a, 0x4a, 0xe7, 0xd1, 0xb0,
+ 0x8b, 0xad, 0x2d, 0x37, 0x65, 0x0e, 0xc6, 0xa8, 0xb2, 0xd4, 0x94, 0x91, 0x43, 0xaa, 0x29, 0x8b,
+ 0xdc, 0x4a, 0xbf, 0x99, 0xd0, 0x86, 0xa4, 0xae, 0x13, 0xba, 0x03, 0xcf, 0xa5, 0x09, 0xb0, 0xb7,
+ 0x0c, 0xd7, 0x12, 0x0d, 0xcf, 0xea, 0x04, 0x7e, 0x12, 0x6b, 0x2c, 0xa5, 0x25, 0x66, 0xd0, 0x60,
+ 0x66, 0x49, 0xa6, 0x9f, 0x6f, 0x51, 0xdd, 0x0e, 0xb6, 0x92, 0x80, 0x27, 0x92, 0xfa, 0xf9, 0x95,
+ 0x5e, 0x12, 0xcc, 0x2a, 0x97, 0xb9, 0x20, 0x8d, 0x3f, 0x98, 0x6a, 0xd5, 0x57, 0x4b, 0x70, 0x76,
+ 0x91, 0x06, 0xa1, 0x5f, 0xcd, 0xfb, 0x66, 0x94, 0x77, 0xc1, 0x8c, 0xf2, 0xad, 0x0a, 0x9c, 0x5a,
+ 0xa4, 0x41, 0x8f, 0x36, 0xf6, 0x9f, 0x54, 0xfc, 0x2b, 0x70, 0x2a, 0x72, 0xe5, 0x6a, 0x06, 0xae,
+ 0x27, 0xd6, 0xf2, 0xd4, 0x6e, 0xb9, 0xd9, 0x4b, 0x82, 0x59, 0xe5, 0xc8, 0x67, 0xe1, 0x11, 0xbe,
+ 0xd4, 0x3b, 0x2d, 0x61, 0x9f, 0x15, 0xc6, 0x84, 0xd8, 0x65, 0x84, 0x49, 0x09, 0xf9, 0x48, 0x33,
+ 0x9b, 0x0c, 0xfb, 0x95, 0x27, 0x5f, 0x82, 0x91, 0x8e, 0xd5, 0xa1, 0xb6, 0xe5, 0x70, 0xfd, 0x2c,
+ 0xb7, 0x4b, 0xc8, 0x6a, 0x0c, 0x2c, 0xda, 0xc0, 0xc5, 0x53, 0x31, 0xc1, 0x30, 0xb3, 0xa7, 0x56,
+ 0x8f, 0xb1, 0xa7, 0xfe, 0x4b, 0x11, 0x86, 0x17, 0x3d, 0xb7, 0xdb, 0x69, 0xec, 0x92, 0x16, 0x0c,
+ 0xdd, 0xe2, 0x87, 0x67, 0xf2, 0x68, 0x6a, 0x70, 0x77, 0x68, 0x71, 0x06, 0x17, 0xa9, 0x44, 0xe2,
+ 0x1d, 0x25, 0x3c, 0xeb, 0xc4, 0xdb, 0x74, 0x97, 0x9a, 0xf2, 0x0c, 0x2d, 0xec, 0xc4, 0xd7, 0x58,
+ 0x22, 0x8a, 0x3c, 0xd2, 0x86, 0x13, 0xba, 0x6d, 0xbb, 0xb7, 0xa8, 0xb9, 0xac, 0x07, 0xd4, 0xa1,
+ 0xbe, 0x3a, 0x92, 0x3c, 0xac, 0x59, 0x9a, 0x9f, 0xeb, 0xcf, 0x26, 0xa1, 0x30, 0x8d, 0x4d, 0x5e,
+ 0x83, 0x61, 0x3f, 0x70, 0x3d, 0xa5, 0x6c, 0xd5, 0x67, 0xe6, 0x06, 0x6f, 0xf4, 0xc6, 0x67, 0x9a,
+ 0x02, 0x4a, 0xd8, 0xec, 0xe5, 0x0b, 0x2a, 0x06, 0xda, 0x37, 0x0b, 0x00, 0x57, 0xd6, 0xd6, 0x56,
+ 0xe5, 0xf1, 0x82, 0x09, 0x65, 0xbd, 0x1b, 0x1e, 0x54, 0x0e, 0x7e, 0x20, 0x98, 0xf0, 0x87, 0x94,
+ 0x67, 0x78, 0xdd, 0x60, 0x0b, 0x39, 0x3a, 0xf9, 0x10, 0x0c, 0x4b, 0x05, 0x59, 0x8a, 0x3d, 0x74,
+ 0x2d, 0x90, 0x4a, 0x34, 0xaa, 0x7c, 0xed, 0xb7, 0x8a, 0x00, 0x4b, 0xa6, 0x4d, 0x9b, 0xca, 0x83,
+ 0xbd, 0x16, 0x6c, 0x79, 0xd4, 0xdf, 0x72, 0x6d, 0x73, 0xc0, 0xd3, 0x54, 0x6e, 0xf3, 0x5f, 0x53,
+ 0x20, 0x18, 0xe1, 0x11, 0x13, 0x46, 0xfc, 0x80, 0x76, 0x96, 0x9c, 0x80, 0x7a, 0x3b, 0xba, 0x3d,
+ 0xe0, 0x21, 0xca, 0xb8, 0xb0, 0x8b, 0x44, 0x38, 0x98, 0x40, 0x25, 0x3a, 0xd4, 0x2d, 0xc7, 0x10,
+ 0x03, 0xa4, 0xb1, 0x3b, 0x60, 0x47, 0x3a, 0xc1, 0x76, 0x1c, 0x4b, 0x11, 0x0c, 0xc6, 0x31, 0xb5,
+ 0x1f, 0x17, 0xe1, 0x0c, 0xe7, 0xc7, 0xaa, 0x91, 0xf0, 0xc7, 0x24, 0xff, 0xb3, 0xe7, 0xb6, 0xdd,
+ 0x7f, 0x3b, 0x18, 0x6b, 0x71, 0x59, 0x6b, 0x85, 0x06, 0x7a, 0xa4, 0xcf, 0x45, 0x69, 0xb1, 0x2b,
+ 0x76, 0x5d, 0x28, 0xfb, 0x6c, 0xbe, 0x12, 0xd2, 0x6b, 0x0e, 0xdc, 0x85, 0xb2, 0x3f, 0x80, 0xcf,
+ 0x5e, 0xe1, 0xa9, 0x31, 0x9f, 0xb5, 0x38, 0x3b, 0xf2, 0xbf, 0x61, 0xc8, 0x0f, 0xf4, 0xa0, 0xab,
+ 0x86, 0xe6, 0xfa, 0x51, 0x33, 0xe6, 0xe0, 0xd1, 0x3c, 0x22, 0xde, 0x51, 0x32, 0xd5, 0x7e, 0x5c,
+ 0x80, 0x73, 0xd9, 0x05, 0x97, 0x2d, 0x3f, 0x20, 0xff, 0xa3, 0x47, 0xec, 0x07, 0x6c, 0x71, 0x56,
+ 0x9a, 0x0b, 0x3d, 0x74, 0xc8, 0x56, 0x29, 0x31, 0x91, 0x07, 0x50, 0xb1, 0x02, 0xda, 0x56, 0xfb,
+ 0xcb, 0x1b, 0x47, 0xfc, 0xe9, 0xb1, 0xa5, 0x9d, 0x71, 0x41, 0xc1, 0x4c, 0x7b, 0xab, 0xd8, 0xef,
+ 0x93, 0xf9, 0xf2, 0x61, 0x27, 0x7d, 0x7e, 0xaf, 0xe5, 0xf3, 0xf9, 0x4d, 0x56, 0xa8, 0xd7, 0xf5,
+ 0xf7, 0x7f, 0xf5, 0xba, 0xfe, 0xde, 0xc8, 0xef, 0xfa, 0x9b, 0x12, 0x43, 0x5f, 0x0f, 0xe0, 0x77,
+ 0x4a, 0x70, 0xfe, 0x6e, 0xdd, 0x86, 0xad, 0x67, 0xb2, 0x77, 0xe6, 0x5d, 0xcf, 0xee, 0xde, 0x0f,
+ 0xc9, 0x0c, 0x54, 0x3a, 0x5b, 0xba, 0xaf, 0x94, 0x32, 0xb5, 0x61, 0xa9, 0xac, 0xb2, 0xc4, 0x3b,
+ 0x6c, 0xd2, 0xe0, 0xca, 0x1c, 0x7f, 0x45, 0x41, 0xca, 0xa6, 0xe3, 0x36, 0xf5, 0xfd, 0xc8, 0x26,
+ 0x10, 0x4e, 0xc7, 0x2b, 0x22, 0x19, 0x55, 0x3e, 0x09, 0x60, 0x48, 0x98, 0x98, 0xe5, 0xca, 0x34,
+ 0xb8, 0x23, 0x57, 0x86, 0x9b, 0x78, 0xf4, 0x51, 0xf2, 0xb4, 0x42, 0xf2, 0x22, 0x53, 0x50, 0x0e,
+ 0x22, 0xa7, 0x5d, 0xb5, 0x35, 0x2f, 0x67, 0xe8, 0xa7, 0x9c, 0x8e, 0x6d, 0xec, 0xdd, 0x0d, 0x6e,
+ 0x54, 0x37, 0xe5, 0xf9, 0xb9, 0xe5, 0x3a, 0x5c, 0x21, 0x2b, 0x45, 0x1b, 0xfb, 0x1b, 0x3d, 0x14,
+ 0x98, 0x51, 0x4a, 0xfb, 0xab, 0x2a, 0x9c, 0xc9, 0xee, 0x0f, 0x4c, 0x6e, 0x3b, 0xd4, 0xf3, 0x19,
+ 0x76, 0x21, 0x29, 0xb7, 0x9b, 0x22, 0x19, 0x55, 0xfe, 0x7b, 0xda, 0xe1, 0xec, 0x5b, 0x05, 0x38,
+ 0xeb, 0xc9, 0x33, 0xa2, 0xfb, 0xe1, 0x74, 0xf6, 0x98, 0x30, 0x67, 0xf4, 0x61, 0x88, 0xfd, 0xeb,
+ 0x42, 0x7e, 0xa3, 0x00, 0x13, 0xed, 0x94, 0x9d, 0xe3, 0x18, 0x2f, 0x8c, 0x71, 0xaf, 0xf8, 0x95,
+ 0x3e, 0xfc, 0xb0, 0x6f, 0x4d, 0xc8, 0x97, 0xa0, 0xde, 0x61, 0xfd, 0xc2, 0x0f, 0xa8, 0x63, 0xa8,
+ 0x3b, 0x63, 0x83, 0x8f, 0xa4, 0xd5, 0x08, 0x4b, 0xb9, 0xa2, 0x09, 0xfd, 0x20, 0x96, 0x81, 0x71,
+ 0x8e, 0x0f, 0xf8, 0x0d, 0xb1, 0x4b, 0x50, 0xf5, 0x69, 0x10, 0x58, 0x4e, 0x4b, 0xec, 0x37, 0x6a,
+ 0x62, 0xac, 0x34, 0x65, 0x1a, 0x86, 0xb9, 0xe4, 0xc3, 0x50, 0xe3, 0x47, 0x4e, 0xb3, 0x5e, 0xcb,
+ 0x9f, 0xa8, 0x71, 0x77, 0xb1, 0x51, 0xe1, 0x00, 0x27, 0x13, 0x31, 0xca, 0x27, 0x4f, 0xc3, 0xc8,
+ 0x06, 0x1f, 0xbe, 0xf2, 0xd2, 0xb0, 0xb0, 0x71, 0x71, 0x6d, 0xad, 0x11, 0x4b, 0xc7, 0x04, 0x15,
+ 0x99, 0x01, 0xa0, 0xe1, 0xb9, 0x5c, 0xda, 0x9e, 0x15, 0x9d, 0xd8, 0x61, 0x8c, 0x8a, 0x3c, 0x06,
+ 0xa5, 0xc0, 0xf6, 0xb9, 0x0d, 0xab, 0x1a, 0x6d, 0x41, 0xd7, 0x96, 0x9b, 0xc8, 0xd2, 0xb5, 0x5f,
+ 0x14, 0xe0, 0x44, 0xea, 0x72, 0x09, 0x2b, 0xd2, 0xf5, 0x6c, 0x39, 0x8d, 0x84, 0x45, 0xd6, 0x71,
+ 0x19, 0x59, 0x3a, 0x79, 0x55, 0xaa, 0xe5, 0xc5, 0x9c, 0xf1, 0x11, 0xae, 0xeb, 0x81, 0xcf, 0xf4,
+ 0xf0, 0x1e, 0x8d, 0x9c, 0x1f, 0xf3, 0x45, 0xf5, 0x91, 0xeb, 0x40, 0xec, 0x98, 0x2f, 0xca, 0xc3,
+ 0x04, 0x65, 0xca, 0xe0, 0x57, 0x3e, 0x88, 0xc1, 0x4f, 0xfb, 0x7a, 0x31, 0x26, 0x01, 0xa9, 0xd9,
+ 0xdf, 0x43, 0x02, 0x4f, 0xb2, 0x05, 0x34, 0x5c, 0xdc, 0x6b, 0xf1, 0xf5, 0x8f, 0x2f, 0xc6, 0x32,
+ 0x97, 0xbc, 0x24, 0x64, 0x5f, 0xca, 0x79, 0x0b, 0x75, 0x6d, 0xb9, 0x29, 0xbc, 0xab, 0x54, 0xab,
+ 0x85, 0x4d, 0x50, 0x3e, 0xa6, 0x26, 0xd0, 0xfe, 0xac, 0x04, 0xf5, 0xab, 0xee, 0xc6, 0x7b, 0xc4,
+ 0x83, 0x3a, 0x7b, 0x99, 0x2a, 0xbe, 0x8b, 0xcb, 0xd4, 0x3a, 0x3c, 0x12, 0x04, 0x76, 0x93, 0x1a,
+ 0xae, 0x63, 0xfa, 0xb3, 0x9b, 0x01, 0xf5, 0x16, 0x2c, 0xc7, 0xf2, 0xb7, 0xa8, 0x29, 0x8f, 0x93,
+ 0x1e, 0xdd, 0xdf, 0x9b, 0x7c, 0x64, 0x6d, 0x6d, 0x39, 0x8b, 0x04, 0xfb, 0x95, 0xe5, 0xd3, 0x86,
+ 0x6e, 0x6c, 0xbb, 0x9b, 0x9b, 0xfc, 0xa6, 0x8c, 0xf4, 0xb9, 0x11, 0xd3, 0x46, 0x2c, 0x1d, 0x13,
+ 0x54, 0xda, 0xdb, 0x45, 0xa8, 0x85, 0x37, 0xdf, 0xc9, 0x13, 0x30, 0xbc, 0xe1, 0xb9, 0xdb, 0xd4,
+ 0x13, 0x27, 0x77, 0xf2, 0xa6, 0x4c, 0x43, 0x24, 0xa1, 0xca, 0x23, 0x8f, 0x43, 0x25, 0x70, 0x3b,
+ 0x96, 0x91, 0x36, 0xa8, 0xad, 0xb1, 0x44, 0x14, 0x79, 0xc7, 0xd7, 0xc1, 0x9f, 0x4c, 0xa8, 0x76,
+ 0xb5, 0xbe, 0xca, 0xd8, 0x2b, 0x50, 0xf6, 0x75, 0xdf, 0x96, 0xeb, 0x69, 0x8e, 0x4b, 0xe4, 0xb3,
+ 0xcd, 0x65, 0x79, 0x89, 0x7c, 0xb6, 0xb9, 0x8c, 0x1c, 0x54, 0xfb, 0x69, 0x11, 0xea, 0x42, 0x6e,
+ 0x62, 0x56, 0x38, 0x4a, 0xc9, 0xbd, 0xc0, 0x5d, 0x29, 0xfc, 0x6e, 0x9b, 0x7a, 0xdc, 0xcc, 0x24,
+ 0x27, 0xb9, 0xf8, 0xf9, 0x40, 0x94, 0x19, 0xba, 0x53, 0x44, 0x49, 0x4a, 0xf4, 0xe5, 0x63, 0x14,
+ 0x7d, 0xe5, 0x40, 0xa2, 0x1f, 0x3a, 0x0e, 0xd1, 0xbf, 0x59, 0x84, 0xda, 0xb2, 0xb5, 0x49, 0x8d,
+ 0x5d, 0xc3, 0xe6, 0x77, 0x02, 0x4d, 0x6a, 0xd3, 0x80, 0x2e, 0x7a, 0xba, 0x41, 0x57, 0xa9, 0x67,
+ 0xf1, 0xc8, 0x30, 0x6c, 0x7c, 0xf0, 0x19, 0x48, 0xde, 0x09, 0x9c, 0xef, 0x43, 0x83, 0x7d, 0x4b,
+ 0x93, 0x25, 0x18, 0x31, 0xa9, 0x6f, 0x79, 0xd4, 0x5c, 0x8d, 0x6d, 0x54, 0x9e, 0x50, 0x4b, 0xcd,
+ 0x7c, 0x2c, 0xef, 0xce, 0xde, 0xe4, 0xa8, 0x32, 0x50, 0x8a, 0x1d, 0x4b, 0xa2, 0x28, 0x1b, 0xf2,
+ 0x1d, 0xbd, 0xeb, 0x67, 0xd5, 0x31, 0x36, 0xe4, 0x57, 0xb3, 0x49, 0xb0, 0x5f, 0x59, 0xad, 0x02,
+ 0xa5, 0x65, 0xb7, 0xa5, 0xbd, 0x55, 0x82, 0x30, 0x84, 0x10, 0xf9, 0x3f, 0x05, 0xa8, 0xeb, 0x8e,
+ 0xe3, 0x06, 0x32, 0x3c, 0x8f, 0x38, 0x81, 0xc7, 0xdc, 0x91, 0x8a, 0xa6, 0x66, 0x23, 0x50, 0x71,
+ 0x78, 0x1b, 0x1e, 0x28, 0xc7, 0x72, 0x30, 0xce, 0x9b, 0x74, 0x53, 0xe7, 0xc9, 0x2b, 0xf9, 0x6b,
+ 0x71, 0x80, 0xd3, 0xe3, 0x73, 0x9f, 0x86, 0xf1, 0x74, 0x65, 0x0f, 0x73, 0x1c, 0x94, 0xeb, 0x60,
+ 0xbe, 0x08, 0x10, 0xf9, 0x94, 0xdc, 0x07, 0x23, 0x96, 0x95, 0x30, 0x62, 0x2d, 0x0e, 0x2e, 0xe0,
+ 0xb0, 0xd2, 0x7d, 0x0d, 0x57, 0xaf, 0xa7, 0x0c, 0x57, 0x4b, 0x47, 0xc1, 0xec, 0xee, 0xc6, 0xaa,
+ 0xdf, 0x2c, 0xc0, 0x78, 0x44, 0x2c, 0x6f, 0xc8, 0x3e, 0x07, 0xa3, 0x1e, 0xd5, 0xcd, 0x86, 0x1e,
+ 0x18, 0x5b, 0xdc, 0xd5, 0xbb, 0xc0, 0x7d, 0xb3, 0x4f, 0xee, 0xef, 0x4d, 0x8e, 0x62, 0x3c, 0x03,
+ 0x93, 0x74, 0x44, 0x87, 0x3a, 0x4b, 0x58, 0xb3, 0xda, 0xd4, 0xed, 0x06, 0x03, 0x5a, 0x4d, 0xf9,
+ 0x86, 0x05, 0x23, 0x18, 0x8c, 0x63, 0x6a, 0xef, 0x14, 0x60, 0x2c, 0x5e, 0xe1, 0x63, 0xb7, 0xa8,
+ 0x6d, 0x25, 0x2d, 0x6a, 0x73, 0x47, 0xd0, 0x26, 0x7d, 0xac, 0x68, 0x3f, 0xab, 0xc6, 0x3f, 0x8d,
+ 0x5b, 0xce, 0xe2, 0xc6, 0x82, 0xc2, 0x5d, 0x8d, 0x05, 0xef, 0xfd, 0xa8, 0x31, 0xfd, 0xb4, 0xdc,
+ 0xf2, 0x03, 0xac, 0xe5, 0xbe, 0x9b, 0xa1, 0x67, 0x62, 0xe1, 0x53, 0x86, 0x72, 0x84, 0x4f, 0x69,
+ 0x87, 0xe1, 0x53, 0x86, 0x8f, 0x6c, 0xd2, 0x39, 0x48, 0x08, 0x95, 0xea, 0x7d, 0x0d, 0xa1, 0x52,
+ 0x3b, 0xae, 0x10, 0x2a, 0x90, 0x37, 0x84, 0xca, 0xd7, 0x0a, 0x30, 0x66, 0x26, 0x6e, 0xcc, 0x72,
+ 0xdb, 0x42, 0x9e, 0xa5, 0x26, 0x79, 0x01, 0x57, 0x5c, 0x99, 0x4a, 0xa6, 0x61, 0x8a, 0xa5, 0xf6,
+ 0xdb, 0x95, 0xf8, 0x3a, 0x70, 0xbf, 0x4d, 0xd5, 0xcf, 0x26, 0x4d, 0xd5, 0x17, 0xd3, 0xa6, 0xea,
+ 0x13, 0x31, 0x2f, 0xd2, 0xb8, 0xb9, 0xfa, 0x23, 0xb1, 0xe9, 0x91, 0xcd, 0x49, 0xa3, 0x91, 0xa4,
+ 0x33, 0xa6, 0xc8, 0x8f, 0x40, 0xd5, 0x57, 0xc1, 0x1e, 0xc5, 0xc6, 0x26, 0x6a, 0x17, 0x15, 0x88,
+ 0x31, 0xa4, 0x60, 0x9a, 0xb8, 0x47, 0x75, 0xdf, 0x75, 0xd2, 0x9a, 0x38, 0xf2, 0x54, 0x94, 0xb9,
+ 0x71, 0x93, 0xf9, 0xd0, 0x3d, 0x4c, 0xe6, 0x3a, 0xd4, 0x6d, 0xdd, 0x0f, 0xd6, 0x3b, 0xa6, 0x1e,
+ 0x50, 0x53, 0x8e, 0xb7, 0xff, 0x7a, 0xb0, 0xb5, 0x8a, 0xad, 0x7f, 0x91, 0x42, 0xb8, 0x1c, 0xc1,
+ 0x60, 0x1c, 0x93, 0x98, 0x30, 0xc2, 0x5e, 0xf9, 0x68, 0x30, 0x67, 0x55, 0x08, 0x80, 0xc3, 0xf0,
+ 0x08, 0x2d, 0x3d, 0xcb, 0x31, 0x1c, 0x4c, 0xa0, 0xf6, 0xb1, 0xaa, 0xd7, 0x06, 0xb1, 0xaa, 0x93,
+ 0x4f, 0x08, 0x65, 0x63, 0x57, 0x35, 0x18, 0xb7, 0xc6, 0x8d, 0x46, 0x5e, 0x85, 0x18, 0xcf, 0xc4,
+ 0x24, 0xad, 0xf6, 0xb5, 0x1a, 0xd4, 0xaf, 0xeb, 0x81, 0xb5, 0x43, 0xf9, 0x11, 0xd0, 0xf1, 0xd8,
+ 0xe1, 0x7f, 0xa5, 0x00, 0x67, 0x92, 0x7e, 0x7e, 0xc7, 0x68, 0x8c, 0xe7, 0x51, 0x43, 0x30, 0x93,
+ 0x1b, 0xf6, 0xa9, 0x05, 0x37, 0xcb, 0xf7, 0xb8, 0x0d, 0x1e, 0xb7, 0x59, 0xbe, 0xd9, 0x8f, 0x21,
+ 0xf6, 0xaf, 0xcb, 0x7b, 0xc5, 0x2c, 0xff, 0x60, 0x47, 0x75, 0x4b, 0x1d, 0x1a, 0x0c, 0x3f, 0x30,
+ 0x87, 0x06, 0xd5, 0x07, 0x42, 0x53, 0xeb, 0xc4, 0x0e, 0x0d, 0x6a, 0x39, 0x9d, 0x57, 0xa4, 0x6b,
+ 0xbc, 0x40, 0xeb, 0x77, 0xf8, 0xc0, 0x6f, 0xb5, 0x2b, 0x63, 0x2e, 0x53, 0x70, 0x36, 0x74, 0xdf,
+ 0x32, 0xe4, 0x9a, 0x99, 0x23, 0x8a, 0xa5, 0x0a, 0xf7, 0x25, 0xce, 0xb8, 0xf9, 0x2b, 0x0a, 0xec,
+ 0x28, 0xac, 0x58, 0x31, 0x57, 0x58, 0x31, 0x32, 0x07, 0x65, 0x87, 0x6d, 0xbd, 0x4b, 0x87, 0x0e,
+ 0x24, 0x76, 0xfd, 0x1a, 0xdd, 0x45, 0x5e, 0x58, 0x7b, 0xbb, 0x08, 0xc0, 0x3e, 0xff, 0x60, 0xe6,
+ 0xfb, 0x0f, 0xc1, 0xb0, 0xdf, 0xe5, 0x1b, 0x6d, 0xb9, 0xda, 0x47, 0x1e, 0x3f, 0x22, 0x19, 0x55,
+ 0x3e, 0x79, 0x1c, 0x2a, 0xaf, 0x77, 0x69, 0x57, 0x9d, 0x45, 0x87, 0xba, 0xde, 0x67, 0x58, 0x22,
+ 0x8a, 0xbc, 0xe3, 0x33, 0xc5, 0x29, 0x33, 0x7f, 0xe5, 0xb8, 0xcc, 0xfc, 0x35, 0x18, 0xbe, 0xee,
+ 0x72, 0x07, 0x42, 0xed, 0x9f, 0x8a, 0x00, 0x91, 0x83, 0x16, 0xf9, 0x66, 0x01, 0x1e, 0x0e, 0x07,
+ 0x5c, 0x20, 0x54, 0x76, 0x1e, 0x38, 0x36, 0xb7, 0xc9, 0x3f, 0x6b, 0xb0, 0xf3, 0x19, 0x68, 0x35,
+ 0x8b, 0x1d, 0x66, 0xd7, 0x82, 0x20, 0x54, 0x69, 0xbb, 0x13, 0xec, 0xce, 0x5b, 0x9e, 0xec, 0x81,
+ 0x99, 0x7e, 0x80, 0x97, 0x25, 0x8d, 0x28, 0x2a, 0xf7, 0x95, 0x7c, 0x10, 0xa9, 0x1c, 0x0c, 0x71,
+ 0xc8, 0x16, 0x54, 0x1d, 0xf7, 0x55, 0x9f, 0x89, 0x43, 0x76, 0xc7, 0x17, 0x07, 0x17, 0xb9, 0x10,
+ 0xab, 0x30, 0x11, 0xcb, 0x17, 0x1c, 0x76, 0xa4, 0xb0, 0xbf, 0x51, 0x84, 0x53, 0x19, 0x72, 0x20,
+ 0x2f, 0xc2, 0xb8, 0xf4, 0x85, 0x8b, 0x22, 0x28, 0x17, 0xa2, 0x08, 0xca, 0xcd, 0x54, 0x1e, 0xf6,
+ 0x50, 0x93, 0x57, 0x01, 0x74, 0xc3, 0xa0, 0xbe, 0xbf, 0xe2, 0x9a, 0x4a, 0x99, 0x7d, 0x61, 0x7f,
+ 0x6f, 0x12, 0x66, 0xc3, 0xd4, 0x3b, 0x7b, 0x93, 0x1f, 0xcd, 0x72, 0x6f, 0x4d, 0xc9, 0x39, 0x2a,
+ 0x80, 0x31, 0x48, 0xf2, 0x05, 0x00, 0xb1, 0x6f, 0x0b, 0x6f, 0xe0, 0xdf, 0xc3, 0xd8, 0x31, 0xa5,
+ 0x62, 0x1d, 0x4d, 0x7d, 0xa6, 0xab, 0x3b, 0x81, 0x15, 0xec, 0x8a, 0x80, 0x27, 0x37, 0x43, 0x14,
+ 0x8c, 0x21, 0x6a, 0x7f, 0x52, 0x84, 0xaa, 0x32, 0xb3, 0xde, 0x07, 0xdb, 0x5a, 0x2b, 0x61, 0x5b,
+ 0x3b, 0x22, 0x87, 0xd6, 0x2c, 0xcb, 0x9a, 0x9b, 0xb2, 0xac, 0x2d, 0xe6, 0x67, 0x75, 0x77, 0xbb,
+ 0xda, 0x77, 0x8a, 0x30, 0xa6, 0x48, 0xf3, 0x5a, 0xd5, 0x3e, 0x05, 0x27, 0xc4, 0x41, 0xf4, 0x8a,
+ 0x7e, 0x5b, 0xc4, 0x7e, 0xe1, 0x02, 0x2b, 0x0b, 0x1f, 0xd2, 0x46, 0x32, 0x0b, 0xd3, 0xb4, 0xac,
+ 0x5b, 0x8b, 0xa4, 0x75, 0xb6, 0x09, 0x11, 0x47, 0x57, 0x62, 0xb3, 0xc4, 0xbb, 0x75, 0x23, 0x95,
+ 0x87, 0x3d, 0xd4, 0x69, 0xb3, 0x5e, 0xf9, 0x18, 0xcc, 0x7a, 0x7f, 0x5d, 0x80, 0x91, 0x48, 0x5e,
+ 0xc7, 0x6e, 0xd4, 0xdb, 0x4c, 0x1a, 0xf5, 0x66, 0x73, 0x77, 0x87, 0x3e, 0x26, 0xbd, 0xff, 0x37,
+ 0x0c, 0x09, 0xbf, 0x6a, 0xb2, 0x01, 0xe7, 0xac, 0x4c, 0xef, 0xb0, 0xd8, 0x6c, 0x13, 0x5e, 0x14,
+ 0x5e, 0xea, 0x4b, 0x89, 0x77, 0x41, 0x21, 0x5d, 0xa8, 0xee, 0x50, 0x2f, 0xb0, 0x0c, 0xaa, 0xbe,
+ 0x6f, 0x31, 0xb7, 0x4a, 0x26, 0x0d, 0x97, 0xa1, 0x4c, 0x6f, 0x4a, 0x06, 0x18, 0xb2, 0x22, 0x1b,
+ 0x50, 0xa1, 0x66, 0x8b, 0xaa, 0x68, 0x3c, 0x39, 0x63, 0x5d, 0x86, 0xf2, 0x64, 0x6f, 0x3e, 0x0a,
+ 0x68, 0xe2, 0x43, 0xcd, 0x56, 0x07, 0x53, 0xb2, 0x1f, 0x0e, 0xae, 0x60, 0x85, 0x47, 0x5c, 0xd1,
+ 0x45, 0xfd, 0x30, 0x09, 0x23, 0x3e, 0x64, 0x3b, 0xb4, 0x90, 0x55, 0x8e, 0x68, 0xf2, 0xb8, 0x8b,
+ 0x7d, 0xcc, 0x87, 0xda, 0x2d, 0x3d, 0xa0, 0x5e, 0x5b, 0xf7, 0xb6, 0xe5, 0x6e, 0x63, 0xf0, 0x2f,
+ 0x7c, 0x49, 0x21, 0x45, 0x5f, 0x18, 0x26, 0x61, 0xc4, 0x87, 0xb8, 0x50, 0x0b, 0xa4, 0xfa, 0xac,
+ 0xcc, 0x80, 0x83, 0x33, 0x55, 0x8a, 0xb8, 0x2f, 0xfd, 0xab, 0xd5, 0x2b, 0x46, 0x3c, 0xc8, 0x4e,
+ 0x22, 0x0e, 0xb0, 0x88, 0xfe, 0xdc, 0xc8, 0x61, 0x4e, 0x96, 0x50, 0xd1, 0x72, 0x93, 0x1d, 0x4f,
+ 0x58, 0x7b, 0xbb, 0x12, 0x4d, 0xcb, 0xf7, 0xdb, 0xc8, 0xf5, 0x74, 0xd2, 0xc8, 0x75, 0x21, 0x6d,
+ 0xe4, 0x4a, 0x9d, 0x6f, 0x1e, 0xde, 0x23, 0x33, 0x65, 0x5e, 0x2a, 0x1f, 0x83, 0x79, 0xe9, 0x29,
+ 0xa8, 0xef, 0xf0, 0x99, 0x40, 0x84, 0xf6, 0xa9, 0xf0, 0x65, 0x84, 0xcf, 0xec, 0x37, 0xa3, 0x64,
+ 0x8c, 0xd3, 0xb0, 0x22, 0xf2, 0xcf, 0x07, 0x61, 0x6c, 0x54, 0x59, 0xa4, 0x19, 0x25, 0x63, 0x9c,
+ 0x86, 0x3b, 0x73, 0x59, 0xce, 0xb6, 0x28, 0x30, 0xcc, 0x0b, 0x08, 0x67, 0x2e, 0x95, 0x88, 0x51,
+ 0x3e, 0xb9, 0x04, 0xd5, 0xae, 0xb9, 0x29, 0x68, 0xab, 0x9c, 0x96, 0x6b, 0x98, 0xeb, 0xf3, 0x0b,
+ 0x32, 0xd4, 0x90, 0xca, 0x65, 0x35, 0x69, 0xeb, 0x1d, 0x95, 0xc1, 0xf7, 0x86, 0xb2, 0x26, 0x2b,
+ 0x51, 0x32, 0xc6, 0x69, 0xc8, 0xc7, 0x61, 0xcc, 0xa3, 0x66, 0xd7, 0xa0, 0x61, 0x29, 0x61, 0x9d,
+ 0x22, 0xe2, 0x17, 0x0f, 0xf1, 0x1c, 0x4c, 0x51, 0xf6, 0x31, 0x92, 0xd5, 0x07, 0x72, 0x3d, 0xfd,
+ 0x51, 0x01, 0x48, 0xaf, 0xf3, 0x33, 0xd9, 0x82, 0x21, 0x87, 0x5b, 0xbf, 0x72, 0x47, 0x53, 0x8e,
+ 0x19, 0xd1, 0xc4, 0xb4, 0x24, 0x13, 0x24, 0x3e, 0x71, 0xa0, 0x4a, 0x6f, 0x07, 0xd4, 0x73, 0xc2,
+ 0xcb, 0x10, 0x47, 0x13, 0xb9, 0x59, 0xec, 0x06, 0x24, 0x32, 0x86, 0x3c, 0xb4, 0x9f, 0x14, 0xa1,
+ 0x1e, 0xa3, 0xbb, 0xd7, 0xa6, 0x92, 0xdf, 0xc7, 0x16, 0x46, 0xa7, 0x75, 0xcf, 0x96, 0x23, 0x2c,
+ 0x76, 0x1f, 0x5b, 0x66, 0xe1, 0x32, 0xc6, 0xe9, 0xc8, 0x0c, 0x40, 0x5b, 0xf7, 0x03, 0xea, 0xf1,
+ 0xd5, 0x37, 0x75, 0x0b, 0x7a, 0x25, 0xcc, 0xc1, 0x18, 0x15, 0xb9, 0x28, 0x63, 0x6f, 0x97, 0x93,
+ 0x51, 0xeb, 0xfa, 0x04, 0xd6, 0xae, 0x1c, 0x41, 0x60, 0x6d, 0xd2, 0x82, 0x71, 0x55, 0x6b, 0x95,
+ 0x7b, 0xb8, 0x98, 0x66, 0x62, 0xff, 0x92, 0x82, 0xc0, 0x1e, 0x50, 0xed, 0xed, 0x02, 0x8c, 0x26,
+ 0x4c, 0x1e, 0x22, 0xde, 0x9c, 0x72, 0xdd, 0x4f, 0xc4, 0x9b, 0x8b, 0x79, 0xdc, 0x3f, 0x09, 0x43,
+ 0x42, 0x40, 0x69, 0x8f, 0x3c, 0x21, 0x42, 0x94, 0xb9, 0x6c, 0x2e, 0x93, 0x46, 0xd5, 0xf4, 0x5c,
+ 0x26, 0xad, 0xae, 0xa8, 0xf2, 0x85, 0xad, 0x5e, 0xd4, 0xae, 0xd7, 0x56, 0x2f, 0xd2, 0x31, 0xa4,
+ 0xd0, 0xfe, 0xad, 0x04, 0xdc, 0x7f, 0x85, 0x3c, 0x07, 0xb5, 0x36, 0x35, 0xb6, 0x74, 0xc7, 0xf2,
+ 0x55, 0xbc, 0x49, 0xb6, 0xbb, 0xad, 0xad, 0xa8, 0xc4, 0x3b, 0x0c, 0x60, 0xb6, 0xb9, 0xcc, 0x5d,
+ 0xc4, 0x23, 0x5a, 0x62, 0xc0, 0x50, 0xcb, 0xf7, 0xf5, 0x8e, 0x95, 0xfb, 0xf8, 0x54, 0xc4, 0xf7,
+ 0x13, 0x83, 0x48, 0x3c, 0xa3, 0x84, 0x26, 0x06, 0x54, 0x3a, 0xb6, 0x6e, 0x39, 0xb9, 0x7f, 0x70,
+ 0xc2, 0xbe, 0x60, 0x95, 0x21, 0x09, 0x93, 0x0e, 0x7f, 0x44, 0x81, 0x4d, 0xba, 0x50, 0xf7, 0x0d,
+ 0x4f, 0x6f, 0xfb, 0x5b, 0xfa, 0xcc, 0x33, 0xcf, 0xe6, 0x56, 0x92, 0x22, 0x56, 0x62, 0xce, 0x9e,
+ 0xc3, 0xd9, 0x95, 0xe6, 0x95, 0xd9, 0x99, 0x67, 0x9e, 0xc5, 0x38, 0x9f, 0x38, 0xdb, 0x67, 0x9e,
+ 0x9a, 0x91, 0xfd, 0xfe, 0xc8, 0xd9, 0x3e, 0xf3, 0xd4, 0x0c, 0xc6, 0xf9, 0x68, 0xff, 0x5a, 0x80,
+ 0x5a, 0x48, 0x4b, 0xd6, 0x01, 0xd8, 0x08, 0x94, 0x11, 0xf9, 0x0e, 0x15, 0x1d, 0x9f, 0xef, 0x8a,
+ 0xd7, 0xc3, 0xc2, 0x18, 0x03, 0xca, 0x08, 0x59, 0x58, 0x3c, 0xea, 0x90, 0x85, 0xd3, 0x50, 0xdb,
+ 0xd2, 0x1d, 0xd3, 0xdf, 0xd2, 0xb7, 0xc5, 0x44, 0x14, 0x0b, 0xe2, 0x79, 0x45, 0x65, 0x60, 0x44,
+ 0xa3, 0xfd, 0xd1, 0x10, 0x88, 0x33, 0x4f, 0x36, 0x54, 0x4c, 0xcb, 0x17, 0x4e, 0xb7, 0x05, 0x5e,
+ 0x32, 0x1c, 0x2a, 0xf3, 0x32, 0x1d, 0x43, 0x0a, 0x72, 0x16, 0x4a, 0x6d, 0xcb, 0x91, 0x27, 0x1e,
+ 0xdc, 0xe0, 0xb5, 0x62, 0x39, 0xc8, 0xd2, 0x78, 0x96, 0x7e, 0x5b, 0xfa, 0x4b, 0x89, 0x2c, 0xfd,
+ 0x36, 0xb2, 0x34, 0xb6, 0x05, 0xb5, 0x5d, 0x77, 0x7b, 0x43, 0x37, 0xb6, 0x95, 0x5b, 0x55, 0x99,
+ 0x2f, 0x84, 0x7c, 0x0b, 0xba, 0x9c, 0xcc, 0xc2, 0x34, 0x2d, 0x59, 0x87, 0x47, 0xde, 0xa0, 0x9e,
+ 0x2b, 0x47, 0x79, 0xd3, 0xa6, 0xb4, 0xa3, 0x60, 0x84, 0x0a, 0xc1, 0xbd, 0xb3, 0x3e, 0x97, 0x4d,
+ 0x82, 0xfd, 0xca, 0x72, 0x3f, 0x4f, 0xdd, 0x6b, 0xd1, 0x60, 0xd5, 0x73, 0x0d, 0xea, 0xfb, 0x96,
+ 0xd3, 0x52, 0xb0, 0x43, 0x11, 0xec, 0x5a, 0x36, 0x09, 0xf6, 0x2b, 0x4b, 0x5e, 0x86, 0x09, 0x91,
+ 0x25, 0x16, 0xdb, 0xd9, 0x1d, 0xdd, 0xb2, 0xf5, 0x0d, 0xcb, 0x56, 0xff, 0x05, 0x1b, 0x15, 0xe7,
+ 0x0a, 0x6b, 0x7d, 0x68, 0xb0, 0x6f, 0x69, 0x72, 0x15, 0xc6, 0xd5, 0xa9, 0xd2, 0x2a, 0xf5, 0x9a,
+ 0xe1, 0x39, 0xf8, 0x68, 0xe3, 0x02, 0xdb, 0xef, 0xcd, 0xd3, 0x8e, 0x47, 0x0d, 0xae, 0x75, 0xa5,
+ 0xa8, 0xb0, 0xa7, 0x1c, 0x41, 0x38, 0xc3, 0x0f, 0xbb, 0xd7, 0x3b, 0x73, 0xae, 0x6b, 0x9b, 0xee,
+ 0x2d, 0x47, 0x7d, 0xbb, 0x50, 0x6c, 0xf8, 0x41, 0x52, 0x33, 0x93, 0x02, 0xfb, 0x94, 0x64, 0x5f,
+ 0xce, 0x73, 0xe6, 0xdd, 0x5b, 0x4e, 0x1a, 0x15, 0xa2, 0x2f, 0x6f, 0xf6, 0xa1, 0xc1, 0xbe, 0xa5,
+ 0xc9, 0x02, 0x90, 0xf4, 0x17, 0xac, 0x77, 0xb8, 0x32, 0x34, 0xda, 0x38, 0x23, 0x82, 0x6b, 0xa4,
+ 0x73, 0x31, 0xa3, 0x04, 0x59, 0x86, 0xd3, 0xe9, 0x54, 0xc6, 0x8e, 0x7b, 0xd8, 0x8f, 0x8a, 0xb0,
+ 0x9a, 0x98, 0x91, 0x8f, 0x99, 0xa5, 0xb4, 0x3f, 0x2e, 0xc2, 0x68, 0xe2, 0x36, 0xf6, 0x03, 0x77,
+ 0xeb, 0x95, 0x69, 0xa0, 0x6d, 0xbf, 0xb5, 0x34, 0x7f, 0x85, 0xea, 0x26, 0xf5, 0xae, 0x51, 0x75,
+ 0x73, 0x9e, 0x4f, 0x2a, 0x2b, 0x89, 0x1c, 0x4c, 0x51, 0x92, 0x4d, 0xa8, 0x08, 0x7b, 0x6a, 0xde,
+ 0xff, 0x2c, 0x28, 0x19, 0x71, 0xa3, 0x2a, 0x5f, 0x72, 0x84, 0x49, 0x55, 0xc0, 0x6b, 0x01, 0x8c,
+ 0xc4, 0x29, 0xd8, 0x44, 0x12, 0x29, 0x6b, 0xc3, 0x09, 0x45, 0x6d, 0x09, 0x4a, 0x41, 0x30, 0xe8,
+ 0x7d, 0x5a, 0x61, 0x9f, 0x5f, 0x5b, 0x46, 0x86, 0xa1, 0x6d, 0xb2, 0xb6, 0xf3, 0x7d, 0xcb, 0x75,
+ 0x64, 0x70, 0xe5, 0x75, 0x18, 0x0e, 0xa4, 0x89, 0x6a, 0xb0, 0xfb, 0xc0, 0xdc, 0x5c, 0xac, 0xcc,
+ 0x53, 0x0a, 0x4b, 0xfb, 0x9b, 0x22, 0xd4, 0xc2, 0xed, 0xe4, 0x01, 0x82, 0x16, 0xbb, 0x50, 0x0b,
+ 0x9d, 0x75, 0x72, 0xff, 0x33, 0x2d, 0xf2, 0x21, 0xe1, 0x3b, 0xa0, 0xf0, 0x15, 0x23, 0x1e, 0x71,
+ 0x47, 0xa0, 0x52, 0x0e, 0x47, 0xa0, 0x0e, 0x0c, 0x07, 0x9e, 0xd5, 0x6a, 0x49, 0xdd, 0x36, 0x8f,
+ 0x27, 0x50, 0x28, 0xae, 0x35, 0x01, 0x28, 0x25, 0x2b, 0x5e, 0x50, 0xb1, 0xd1, 0x5e, 0x83, 0xf1,
+ 0x34, 0x25, 0x57, 0xfc, 0x8c, 0x2d, 0x6a, 0x76, 0x6d, 0x25, 0xe3, 0x48, 0xf1, 0x93, 0xe9, 0x18,
+ 0x52, 0xb0, 0xcd, 0x1f, 0x6b, 0xa6, 0x37, 0x5c, 0x47, 0x6d, 0xab, 0xb9, 0x0e, 0xbd, 0x26, 0xd3,
+ 0x30, 0xcc, 0xd5, 0xfe, 0xb1, 0x04, 0x67, 0x23, 0xa3, 0xc0, 0x8a, 0xee, 0xe8, 0xad, 0x03, 0xfc,
+ 0x28, 0xeb, 0xfd, 0x1b, 0x16, 0x87, 0x8d, 0x3c, 0x5f, 0x7a, 0x00, 0x22, 0xcf, 0xff, 0xb4, 0x00,
+ 0xdc, 0xb1, 0x90, 0x7c, 0x09, 0x46, 0xf4, 0xd8, 0x3f, 0x12, 0x65, 0x73, 0x5e, 0xce, 0xdd, 0x9c,
+ 0xdc, 0x7f, 0x31, 0x74, 0x94, 0x89, 0xa7, 0x62, 0x82, 0x21, 0x71, 0xa1, 0xba, 0xa9, 0xdb, 0x36,
+ 0xd3, 0x85, 0x72, 0x1f, 0x72, 0x24, 0x98, 0xf3, 0x6e, 0xbe, 0x20, 0xa1, 0x31, 0x64, 0xa2, 0xfd,
+ 0x43, 0x01, 0x46, 0x9b, 0xb6, 0x65, 0x5a, 0x4e, 0xeb, 0x18, 0x43, 0xce, 0xdf, 0x80, 0x8a, 0x6f,
+ 0x5b, 0x26, 0x1d, 0x70, 0x1e, 0x17, 0x2b, 0x08, 0x03, 0x40, 0x81, 0x93, 0x8c, 0x61, 0x5f, 0x3a,
+ 0x40, 0x0c, 0xfb, 0x9f, 0x0f, 0x81, 0x74, 0x4e, 0x25, 0x5d, 0xa8, 0xb5, 0x54, 0x68, 0x6c, 0xf9,
+ 0x8d, 0x57, 0x72, 0x84, 0x55, 0x4b, 0x04, 0xd9, 0x16, 0xb3, 0x6e, 0x98, 0x88, 0x11, 0x27, 0x42,
+ 0x93, 0xbf, 0xc5, 0x9c, 0xcf, 0xf9, 0x5b, 0x4c, 0xc1, 0xae, 0xf7, 0xc7, 0x98, 0x3a, 0x94, 0xb7,
+ 0x82, 0xa0, 0x23, 0xc7, 0xd5, 0xe0, 0xde, 0xc7, 0x51, 0x64, 0x0f, 0xa1, 0x8d, 0xb0, 0x77, 0xe4,
+ 0xd0, 0x8c, 0x85, 0xa3, 0x87, 0x7f, 0x63, 0x9a, 0xcb, 0x75, 0xd0, 0x1d, 0x67, 0xc1, 0xde, 0x91,
+ 0x43, 0x93, 0x2f, 0x42, 0x3d, 0xf0, 0x74, 0xc7, 0xdf, 0x74, 0xbd, 0x36, 0xf5, 0xe4, 0xee, 0x70,
+ 0x21, 0xc7, 0x9f, 0x21, 0xd7, 0x22, 0x34, 0x71, 0x82, 0x96, 0x48, 0xc2, 0x38, 0x37, 0xb2, 0x0d,
+ 0xd5, 0xae, 0x29, 0x2a, 0x26, 0xcd, 0x26, 0xb3, 0x79, 0x7e, 0xf6, 0x19, 0x3b, 0xc6, 0x56, 0x6f,
+ 0x18, 0x32, 0x48, 0xfe, 0x78, 0x6c, 0xf8, 0xa8, 0x7e, 0x3c, 0x16, 0xef, 0x8d, 0x59, 0x61, 0x07,
+ 0x48, 0x5b, 0x6a, 0x94, 0x4e, 0x4b, 0x7a, 0xe1, 0x2c, 0xe4, 0x56, 0xf6, 0x04, 0xcb, 0x7a, 0xa8,
+ 0x95, 0x3a, 0x2d, 0x54, 0x3c, 0xb4, 0x36, 0x48, 0xeb, 0x36, 0x31, 0x12, 0xbf, 0xe7, 0x10, 0x77,
+ 0x61, 0xa6, 0x0f, 0x36, 0x1f, 0x84, 0xff, 0x89, 0x88, 0x85, 0x07, 0xce, 0xfc, 0x0f, 0x87, 0xf6,
+ 0xb7, 0x45, 0x28, 0xad, 0x2d, 0x37, 0x45, 0xc8, 0x3f, 0xfe, 0xef, 0x1b, 0xda, 0xdc, 0xb6, 0x3a,
+ 0x37, 0xa9, 0x67, 0x6d, 0xee, 0xca, 0x4d, 0x6f, 0x2c, 0xe4, 0x5f, 0x9a, 0x02, 0x33, 0x4a, 0x91,
+ 0x57, 0x60, 0xc4, 0xd0, 0xe7, 0xa8, 0x17, 0x0c, 0xb2, 0xa5, 0xe7, 0x97, 0xfe, 0xe6, 0x66, 0xa3,
+ 0xe2, 0x98, 0x00, 0x23, 0xeb, 0x00, 0x46, 0x04, 0x5d, 0x3a, 0xb4, 0x21, 0x22, 0x06, 0x1c, 0x03,
+ 0x22, 0x08, 0xb5, 0x6d, 0x46, 0xca, 0x51, 0xcb, 0x87, 0x41, 0xe5, 0x3d, 0xe7, 0x9a, 0x2a, 0x8b,
+ 0x11, 0x8c, 0xe6, 0xc0, 0x68, 0xe2, 0x9f, 0x1d, 0xe4, 0x63, 0x50, 0x75, 0x3b, 0xb1, 0xe9, 0xb4,
+ 0xc6, 0xfd, 0xfd, 0xaa, 0x37, 0x64, 0xda, 0x9d, 0xbd, 0xc9, 0xd1, 0x65, 0xb7, 0x65, 0x19, 0x2a,
+ 0x01, 0x43, 0x72, 0xa2, 0xc1, 0x10, 0xbf, 0xa9, 0xa3, 0xfe, 0xd8, 0xc1, 0xd7, 0x0e, 0x1e, 0x54,
+ 0xdf, 0x47, 0x99, 0xa3, 0x7d, 0xb9, 0x0c, 0xd1, 0x99, 0x10, 0xf1, 0x61, 0x48, 0x78, 0x22, 0xcb,
+ 0x99, 0xfb, 0x58, 0x9d, 0x9e, 0x25, 0x2b, 0xd2, 0x82, 0xd2, 0x6b, 0xee, 0x46, 0xee, 0x89, 0x3b,
+ 0x76, 0x45, 0x57, 0x58, 0xa9, 0x62, 0x09, 0xc8, 0x38, 0x90, 0x5f, 0x2d, 0xc0, 0x49, 0x3f, 0xad,
+ 0x74, 0xca, 0xee, 0x80, 0xf9, 0xb5, 0xeb, 0xb4, 0x1a, 0x2b, 0x1d, 0x33, 0xfb, 0x65, 0x63, 0x6f,
+ 0x5d, 0x98, 0xfc, 0xc5, 0x61, 0x8d, 0xec, 0x4e, 0x8b, 0x39, 0xff, 0x33, 0x97, 0x94, 0x7f, 0x32,
+ 0x0d, 0x25, 0x2b, 0xed, 0xab, 0x45, 0xa8, 0xc7, 0x66, 0xeb, 0xdc, 0x3f, 0x82, 0xb9, 0x9d, 0xfa,
+ 0x11, 0xcc, 0xea, 0xe0, 0x67, 0x97, 0x51, 0xad, 0x8e, 0xfb, 0x5f, 0x30, 0xdf, 0x2f, 0x42, 0x69,
+ 0x7d, 0x7e, 0x21, 0xb9, 0x5d, 0x2c, 0xdc, 0x87, 0xed, 0xe2, 0x16, 0x0c, 0x6f, 0x74, 0x2d, 0x3b,
+ 0xb0, 0x9c, 0xdc, 0x41, 0x04, 0xd4, 0x7f, 0x73, 0xe4, 0x5d, 0x5c, 0x81, 0x8a, 0x0a, 0x9e, 0xb4,
+ 0x60, 0xb8, 0x25, 0xa2, 0xb8, 0xe5, 0xf6, 0xe8, 0x92, 0xd1, 0xe0, 0x04, 0x23, 0xf9, 0x82, 0x0a,
+ 0x5d, 0xdb, 0x05, 0xf9, 0xe7, 0xed, 0xfb, 0x2e, 0x4d, 0xed, 0x8b, 0x10, 0x6a, 0x01, 0xf7, 0x9f,
+ 0xf9, 0x3f, 0x17, 0x20, 0xa9, 0xf8, 0xdc, 0xff, 0xde, 0xb4, 0x9d, 0xee, 0x4d, 0xf3, 0x47, 0x31,
+ 0xf8, 0xb2, 0x3b, 0x94, 0xf6, 0x87, 0x45, 0x18, 0xba, 0x6f, 0x17, 0x3f, 0x69, 0xc2, 0x39, 0x6d,
+ 0x2e, 0xe7, 0xc4, 0xd8, 0xd7, 0x35, 0xad, 0x9d, 0x72, 0x4d, 0xcb, 0xfb, 0xa7, 0xcf, 0x7b, 0x38,
+ 0xa6, 0xfd, 0x65, 0x01, 0xe4, 0xb4, 0xbc, 0xe4, 0xf8, 0x81, 0xee, 0x18, 0xfc, 0x87, 0xf3, 0x72,
+ 0x0d, 0xc8, 0xeb, 0x01, 0x21, 0xbd, 0x84, 0xc4, 0xb2, 0xcf, 0x9f, 0xd5, 0x9c, 0x4f, 0x3e, 0x02,
+ 0xd5, 0x2d, 0xd7, 0x0f, 0xf8, 0x3c, 0x5f, 0x4c, 0xda, 0x75, 0xae, 0xc8, 0x74, 0x0c, 0x29, 0xd2,
+ 0x27, 0x85, 0x95, 0xfe, 0x27, 0x85, 0xda, 0xb7, 0x8b, 0x30, 0xf2, 0x5e, 0xb9, 0xbd, 0x9a, 0xe5,
+ 0xca, 0x57, 0xca, 0xe9, 0xca, 0x57, 0x3e, 0x8c, 0x2b, 0x9f, 0xf6, 0x83, 0x02, 0xc0, 0x7d, 0xbb,
+ 0x3a, 0x6b, 0x26, 0xbd, 0xec, 0x72, 0xf7, 0xab, 0x6c, 0x1f, 0xbb, 0xdf, 0xab, 0xa8, 0x4f, 0xe2,
+ 0x1e, 0x76, 0x6f, 0x16, 0x60, 0x4c, 0x4f, 0x78, 0xad, 0xe5, 0x56, 0x2d, 0x53, 0x4e, 0x70, 0xe1,
+ 0x35, 0xc1, 0x64, 0x3a, 0xa6, 0xd8, 0x92, 0xe7, 0xa3, 0xb0, 0xad, 0xd7, 0xa3, 0x6e, 0xdf, 0x13,
+ 0x6f, 0x95, 0xab, 0x39, 0x09, 0xca, 0x7b, 0x78, 0x09, 0x96, 0x8e, 0xc4, 0x4b, 0x30, 0x7e, 0xff,
+ 0xa9, 0x7c, 0xd7, 0xfb, 0x4f, 0x3b, 0x50, 0xdb, 0xf4, 0xdc, 0x36, 0x77, 0xc4, 0x93, 0xff, 0x08,
+ 0xbd, 0x9c, 0x63, 0x4d, 0x89, 0xfe, 0x8e, 0x1d, 0xd9, 0x78, 0x16, 0x14, 0x3e, 0x46, 0xac, 0xb8,
+ 0x41, 0xda, 0x15, 0x5c, 0x87, 0x8e, 0x92, 0x6b, 0x38, 0x97, 0xac, 0x09, 0x74, 0x54, 0x6c, 0x92,
+ 0xce, 0x77, 0xc3, 0xf7, 0xc7, 0xf9, 0x4e, 0xfb, 0x7e, 0x59, 0x4d, 0x60, 0x0f, 0x5c, 0x84, 0xc0,
+ 0xf7, 0xfe, 0x95, 0xcb, 0xf4, 0x7d, 0xc8, 0xe1, 0xfb, 0x78, 0x1f, 0xb2, 0x7a, 0x34, 0xf7, 0x21,
+ 0x6b, 0x87, 0xb8, 0x0f, 0xb9, 0x57, 0x82, 0xd4, 0xa6, 0xeb, 0xfd, 0xa3, 0x8d, 0xff, 0x50, 0x47,
+ 0x1b, 0x6f, 0x15, 0x21, 0x9a, 0x45, 0x0e, 0xe9, 0xfa, 0xf1, 0x32, 0x54, 0xdb, 0xfa, 0xed, 0x79,
+ 0x6a, 0xeb, 0xbb, 0x79, 0xfe, 0x0a, 0xb9, 0x22, 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80, 0x15, 0x46,
+ 0x66, 0xce, 0x6d, 0xaa, 0x8e, 0x82, 0x3c, 0x0b, 0x63, 0x58, 0xf4, 0x8e, 0x31, 0x36, 0xda, 0x5f,
+ 0x14, 0x41, 0x86, 0xf0, 0x26, 0x14, 0x2a, 0x9b, 0xd6, 0x6d, 0x6a, 0xe6, 0x76, 0x83, 0x8c, 0xfd,
+ 0xab, 0x57, 0xd8, 0xe2, 0x79, 0x02, 0x0a, 0x74, 0x6e, 0x64, 0x15, 0x67, 0x2b, 0x52, 0x7e, 0x39,
+ 0x8c, 0xac, 0xf1, 0x33, 0x1a, 0x69, 0x64, 0x15, 0x49, 0xa8, 0x78, 0x08, 0x9b, 0x2e, 0x3f, 0xe0,
+ 0x96, 0x22, 0xcd, 0x63, 0xd3, 0x8d, 0x1d, 0x94, 0x2b, 0x9b, 0xae, 0x2f, 0x2e, 0x44, 0x4b, 0x1e,
+ 0x8d, 0xcf, 0x7f, 0xef, 0x87, 0x17, 0x1e, 0xfa, 0xc1, 0x0f, 0x2f, 0x3c, 0xf4, 0xce, 0x0f, 0x2f,
+ 0x3c, 0xf4, 0xe5, 0xfd, 0x0b, 0x85, 0xef, 0xed, 0x5f, 0x28, 0xfc, 0x60, 0xff, 0x42, 0xe1, 0x9d,
+ 0xfd, 0x0b, 0x85, 0xbf, 0xdb, 0xbf, 0x50, 0xf8, 0xa5, 0xbf, 0xbf, 0xf0, 0xd0, 0xe7, 0x9e, 0x8b,
+ 0xaa, 0x30, 0xad, 0xaa, 0x30, 0xad, 0x18, 0x4e, 0x77, 0xb6, 0x5b, 0xd3, 0xac, 0x0a, 0x51, 0x8a,
+ 0xaa, 0xc2, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x80, 0x4f, 0xc1, 0x9f, 0x93, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -6367,6 +6368,9 @@ func (m *MonoVertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas))
+ i--
+ dAtA[i] = 0x50
i = encodeVarintGenerated(dAtA, i, uint64(m.ObservedGeneration))
i--
dAtA[i] = 0x48
@@ -8785,6 +8789,9 @@ func (m *VertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas))
+ i--
+ dAtA[i] = 0x48
i = encodeVarintGenerated(dAtA, i, uint64(m.ObservedGeneration))
i--
dAtA[i] = 0x40
@@ -10208,6 +10215,7 @@ func (m *MonoVertexStatus) Size() (n int) {
l = m.LastScaledAt.Size()
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.ObservedGeneration))
+ n += 1 + sovGenerated(uint64(m.ReadyReplicas))
return n
}
@@ -11103,6 +11111,7 @@ func (m *VertexStatus) Size() (n int) {
l = m.LastScaledAt.Size()
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.ObservedGeneration))
+ n += 1 + sovGenerated(uint64(m.ReadyReplicas))
return n
}
@@ -12015,6 +12024,7 @@ func (this *MonoVertexStatus) String() string {
`LastUpdated:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.LastUpdated), "Time", "v11.Time", 1), `&`, ``, 1) + `,`,
`LastScaledAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.LastScaledAt), "Time", "v11.Time", 1), `&`, ``, 1) + `,`,
`ObservedGeneration:` + fmt.Sprintf("%v", this.ObservedGeneration) + `,`,
+ `ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`,
`}`,
}, "")
return s
@@ -12599,6 +12609,7 @@ func (this *VertexStatus) String() string {
`Message:` + fmt.Sprintf("%v", this.Message) + `,`,
`LastScaledAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.LastScaledAt), "Time", "v11.Time", 1), `&`, ``, 1) + `,`,
`ObservedGeneration:` + fmt.Sprintf("%v", this.ObservedGeneration) + `,`,
+ `ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`,
`}`,
}, "")
return s
@@ -23278,6 +23289,25 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
+ case 10:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ReadyReplicas", wireType)
+ }
+ m.ReadyReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.ReadyReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -30427,6 +30457,25 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
+ case 9:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ReadyReplicas", wireType)
+ }
+ m.ReadyReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.ReadyReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index b8935365e6..8c5a6db0f2 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -854,6 +854,7 @@ message Metadata {
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
+// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason`
// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.message`
@@ -934,21 +935,36 @@ message MonoVertexSpec {
message MonoVertexStatus {
optional Status status = 1;
+ // +optional
optional string phase = 2;
+ // Total number of non-terminated pods targeted by this MonoVertex (their labels match the selector).
+ // +optional
optional uint32 replicas = 3;
+ // +optional
optional string selector = 4;
+ // +optional
optional string reason = 5;
+ // +optional
optional string message = 6;
+ // +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastUpdated = 7;
+ // Time of last scaling operation.
+ // +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastScaledAt = 8;
+ // The generation observed by the MonoVertex controller.
+ // +optional
optional int64 observedGeneration = 9;
+
+ // The number of pods targeted by this MonoVertex with a Ready Condition.
+ // +optional
+ optional uint32 readyReplicas = 10;
}
message NativeRedis {
@@ -1148,24 +1164,35 @@ message PipelineSpec {
message PipelineStatus {
optional Status status = 1;
+ // +optional
optional string phase = 2;
+ // +optional
optional string message = 3;
+ // +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastUpdated = 4;
+ // +optional
optional uint32 vertexCount = 5;
+ // +optional
optional uint32 sourceCount = 6;
+ // +optional
optional uint32 sinkCount = 7;
+ // +optional
optional uint32 udfCount = 8;
+ // +optional
optional uint32 mapUDFCount = 9;
+ // +optional
optional uint32 reduceUDFCount = 10;
+ // The generation observed by the Pipeline controller.
+ // +optional
optional int64 observedGeneration = 11;
}
@@ -1533,6 +1560,7 @@ message UDTransformer {
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
+// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason`
// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.message`
@@ -1613,19 +1641,33 @@ message VertexSpec {
message VertexStatus {
optional Status status = 1;
+ // +optional
optional string phase = 2;
+ // Total number of non-terminated pods targeted by this Vertex (their labels match the selector).
+ // +optional
optional uint32 replicas = 3;
+ // +optional
optional string selector = 4;
+ // +optional
optional string reason = 5;
+ // +optional
optional string message = 6;
+ // Time of last scaling operation.
+ // +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastScaledAt = 7;
+ // The generation observed by the Vertex controller.
+ // +optional
optional int64 observedGeneration = 8;
+
+ // The number of pods targeted by this Vertex with a Ready Condition.
+ // +optional
+ optional uint32 readyReplicas = 9;
}
message VertexTemplate {
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index 81c9cf56e6..15544b81b6 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -56,6 +56,7 @@ const (
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
+// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason`
// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.message`
@@ -463,15 +464,29 @@ func (mvl MonoVertexLimits) GetReadTimeout() time.Duration {
}
type MonoVertexStatus struct {
- Status `json:",inline" protobuf:"bytes,1,opt,name=status"`
- Phase MonoVertexPhase `json:"phase,omitempty" protobuf:"bytes,2,opt,name=phase,casttype=MonoVertexPhase"`
- Replicas uint32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
- Selector string `json:"selector,omitempty" protobuf:"bytes,4,opt,name=selector"`
- Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
- Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
- LastUpdated metav1.Time `json:"lastUpdated,omitempty" protobuf:"bytes,7,opt,name=lastUpdated"`
- LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,8,opt,name=lastScaledAt"`
- ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,9,opt,name=observedGeneration"`
+ Status `json:",inline" protobuf:"bytes,1,opt,name=status"`
+ // +optional
+ Phase MonoVertexPhase `json:"phase,omitempty" protobuf:"bytes,2,opt,name=phase,casttype=MonoVertexPhase"`
+ // Total number of non-terminated pods targeted by this MonoVertex (their labels match the selector).
+ // +optional
+ Replicas uint32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
+ // +optional
+ Selector string `json:"selector,omitempty" protobuf:"bytes,4,opt,name=selector"`
+ // +optional
+ Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
+ // +optional
+ Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
+ // +optional
+ LastUpdated metav1.Time `json:"lastUpdated,omitempty" protobuf:"bytes,7,opt,name=lastUpdated"`
+ // Time of last scaling operation.
+ // +optional
+ LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,8,opt,name=lastScaledAt"`
+ // The generation observed by the MonoVertex controller.
+ // +optional
+ ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,9,opt,name=observedGeneration"`
+ // The number of pods targeted by this MonoVertex with a Ready Condition.
+ // +optional
+ ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,10,opt,name=readyReplicas"`
}
// SetObservedGeneration sets the Status ObservedGeneration
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 0d129cdab1..8ed0f228c0 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -3344,9 +3344,10 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexStatus(ref common.ReferenceCall
},
"replicas": {
SchemaProps: spec.SchemaProps{
- Default: 0,
- Type: []string{"integer"},
- Format: "int64",
+ Description: "Total number of non-terminated pods targeted by this MonoVertex (their labels match the selector).",
+ Default: 0,
+ Type: []string{"integer"},
+ Format: "int64",
},
},
"selector": {
@@ -3374,17 +3375,25 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexStatus(ref common.ReferenceCall
},
"lastScaledAt": {
SchemaProps: spec.SchemaProps{
- Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
+ Description: "Time of last scaling operation.",
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
},
},
"observedGeneration": {
SchemaProps: spec.SchemaProps{
- Type: []string{"integer"},
- Format: "int64",
+ Description: "The generation observed by the MonoVertex controller.",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ "readyReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of pods targeted by this MonoVertex with a Ready Condition.",
+ Type: []string{"integer"},
+ Format: "int64",
},
},
},
- Required: []string{"replicas"},
},
},
Dependencies: []string{
@@ -4060,8 +4069,9 @@ func schema_pkg_apis_numaflow_v1alpha1_PipelineStatus(ref common.ReferenceCallba
},
"observedGeneration": {
SchemaProps: spec.SchemaProps{
- Type: []string{"integer"},
- Format: "int64",
+ Description: "The generation observed by the Pipeline controller.",
+ Type: []string{"integer"},
+ Format: "int64",
},
},
},
@@ -5651,9 +5661,10 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexStatus(ref common.ReferenceCallback
},
"replicas": {
SchemaProps: spec.SchemaProps{
- Default: 0,
- Type: []string{"integer"},
- Format: "int64",
+ Description: "Total number of non-terminated pods targeted by this Vertex (their labels match the selector).",
+ Default: 0,
+ Type: []string{"integer"},
+ Format: "int64",
},
},
"selector": {
@@ -5676,17 +5687,25 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexStatus(ref common.ReferenceCallback
},
"lastScaledAt": {
SchemaProps: spec.SchemaProps{
- Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
+ Description: "Time of last scaling operation.",
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
},
},
"observedGeneration": {
SchemaProps: spec.SchemaProps{
- Type: []string{"integer"},
- Format: "int64",
+ Description: "The generation observed by the Vertex controller.",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ "readyReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of pods targeted by this Vertex with a Ready Condition.",
+ Type: []string{"integer"},
+ Format: "int64",
},
},
},
- Required: []string{"phase", "replicas"},
},
},
Dependencies: []string{
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types.go b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
index 07d62f673d..4604239674 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
@@ -611,17 +611,28 @@ type PipelineLimits struct {
}
type PipelineStatus struct {
- Status `json:",inline" protobuf:"bytes,1,opt,name=status"`
- Phase PipelinePhase `json:"phase,omitempty" protobuf:"bytes,2,opt,name=phase,casttype=PipelinePhase"`
- Message string `json:"message,omitempty" protobuf:"bytes,3,opt,name=message"`
- LastUpdated metav1.Time `json:"lastUpdated,omitempty" protobuf:"bytes,4,opt,name=lastUpdated"`
- VertexCount *uint32 `json:"vertexCount,omitempty" protobuf:"varint,5,opt,name=vertexCount"`
- SourceCount *uint32 `json:"sourceCount,omitempty" protobuf:"varint,6,opt,name=sourceCount"`
- SinkCount *uint32 `json:"sinkCount,omitempty" protobuf:"varint,7,opt,name=sinkCount"`
- UDFCount *uint32 `json:"udfCount,omitempty" protobuf:"varint,8,opt,name=udfCount"`
- MapUDFCount *uint32 `json:"mapUDFCount,omitempty" protobuf:"varint,9,opt,name=mapUDFCount"`
- ReduceUDFCount *uint32 `json:"reduceUDFCount,omitempty" protobuf:"varint,10,opt,name=reduceUDFCount"`
- ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,11,opt,name=observedGeneration"`
+ Status `json:",inline" protobuf:"bytes,1,opt,name=status"`
+ // +optional
+ Phase PipelinePhase `json:"phase,omitempty" protobuf:"bytes,2,opt,name=phase,casttype=PipelinePhase"`
+ // +optional
+ Message string `json:"message,omitempty" protobuf:"bytes,3,opt,name=message"`
+ // +optional
+ LastUpdated metav1.Time `json:"lastUpdated,omitempty" protobuf:"bytes,4,opt,name=lastUpdated"`
+ // +optional
+ VertexCount *uint32 `json:"vertexCount,omitempty" protobuf:"varint,5,opt,name=vertexCount"`
+ // +optional
+ SourceCount *uint32 `json:"sourceCount,omitempty" protobuf:"varint,6,opt,name=sourceCount"`
+ // +optional
+ SinkCount *uint32 `json:"sinkCount,omitempty" protobuf:"varint,7,opt,name=sinkCount"`
+ // +optional
+ UDFCount *uint32 `json:"udfCount,omitempty" protobuf:"varint,8,opt,name=udfCount"`
+ // +optional
+ MapUDFCount *uint32 `json:"mapUDFCount,omitempty" protobuf:"varint,9,opt,name=mapUDFCount"`
+ // +optional
+ ReduceUDFCount *uint32 `json:"reduceUDFCount,omitempty" protobuf:"varint,10,opt,name=reduceUDFCount"`
+ // The generation observed by the Pipeline controller.
+ // +optional
+ ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,11,opt,name=observedGeneration"`
}
// SetVertexCounts sets the counts of vertices.
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types.go b/pkg/apis/numaflow/v1alpha1/vertex_types.go
index 5b97700944..2e78357bb9 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types.go
@@ -60,6 +60,7 @@ const NumaflowRustBinary = "/bin/numaflow-rs"
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
+// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason`
// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.message`
@@ -708,14 +709,27 @@ func (v VertexSpec) getType() containerSupplier {
}
type VertexStatus struct {
- Status `json:",inline" protobuf:"bytes,1,opt,name=status"`
- Phase VertexPhase `json:"phase" protobuf:"bytes,2,opt,name=phase,casttype=VertexPhase"`
- Replicas uint32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
- Selector string `json:"selector,omitempty" protobuf:"bytes,4,opt,name=selector"`
- Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
- Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
- LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,7,opt,name=lastScaledAt"`
- ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,8,opt,name=observedGeneration"`
+ Status `json:",inline" protobuf:"bytes,1,opt,name=status"`
+ // +optional
+ Phase VertexPhase `json:"phase" protobuf:"bytes,2,opt,name=phase,casttype=VertexPhase"`
+ // Total number of non-terminated pods targeted by this Vertex (their labels match the selector).
+ // +optional
+ Replicas uint32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
+ // +optional
+ Selector string `json:"selector,omitempty" protobuf:"bytes,4,opt,name=selector"`
+ // +optional
+ Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
+ // +optional
+ Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
+ // Time of last scaling operation.
+ // +optional
+ LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,7,opt,name=lastScaledAt"`
+ // The generation observed by the Vertex controller.
+ // +optional
+ ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,8,opt,name=observedGeneration"`
+ // The number of pods targeted by this Vertex with a Ready Condition.
+ // +optional
+ ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,9,opt,name=readyReplicas"`
}
func (vs *VertexStatus) MarkPhase(phase VertexPhase, reason, message string) {
diff --git a/pkg/metrics/metrics_server.go b/pkg/metrics/metrics_server.go
index 7cafaa9070..651ffa52b6 100644
--- a/pkg/metrics/metrics_server.go
+++ b/pkg/metrics/metrics_server.go
@@ -241,6 +241,14 @@ func (ms *metricsServer) Start(ctx context.Context) (func(ctx context.Context) e
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
mux.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) {
+ for _, ex := range ms.healthCheckExecutors {
+ if err := ex(); err != nil {
+ log.Errorw("Failed to execute sidecar health check", zap.Error(err))
+ w.WriteHeader(http.StatusInternalServerError)
+ _, _ = w.Write([]byte(err.Error()))
+ return
+ }
+ }
w.WriteHeader(http.StatusNoContent)
})
mux.HandleFunc("/livez", func(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index 3bec61394e..104b7d9728 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -484,7 +484,12 @@ func (mr *monoVertexReconciler) checkChildrenResourceStatus(ctx context.Context,
monoVtx.Status.MarkPodNotHealthy("ListMonoVerticesPodsFailed", err.Error())
return fmt.Errorf("failed to get pods of a vertex: %w", err)
}
- if healthy, reason, msg := reconciler.CheckVertexPodsStatus(&podList); healthy {
+ readyPods := reconciler.NumOfReadyPods(podList)
+ if readyPods > int(monoVtx.Status.Replicas) { // It might happen in some corner cases, such as during rollout
+ readyPods = int(monoVtx.Status.Replicas)
+ }
+ monoVtx.Status.ReadyReplicas = uint32(readyPods)
+ if healthy, reason, msg := reconciler.CheckPodsStatus(&podList); healthy {
monoVtx.Status.MarkPodHealthy(reason, msg)
} else {
// Do not need to explicitly requeue, since the it keeps watching the status change of the pods
diff --git a/pkg/reconciler/util.go b/pkg/reconciler/util.go
index 70f0fe7376..6d27a749bb 100644
--- a/pkg/reconciler/util.go
+++ b/pkg/reconciler/util.go
@@ -30,13 +30,13 @@ import (
// which should be considered as unhealthy
var unhealthyWaitingStatus = []string{"CrashLoopBackOff", "ImagePullBackOff"}
-// CheckVertexPodsStatus checks the status by iterating over pods objects
-func CheckVertexPodsStatus(vertexPods *corev1.PodList) (healthy bool, reason string, message string) {
+// CheckPodsStatus checks the status by iterating over pods objects
+func CheckPodsStatus(pods *corev1.PodList) (healthy bool, reason string, message string) {
// TODO: Need to revisit later.
- if len(vertexPods.Items) == 0 {
+ if len(pods.Items) == 0 {
return true, "NoPodsFound", "No Pods found"
} else {
- for _, pod := range vertexPods.Items {
+ for _, pod := range pods.Items {
if podHealthy, msg := isPodHealthy(&pod); !podHealthy {
message = fmt.Sprintf("Pod %s is unhealthy", pod.Name)
reason = "Pod" + msg
@@ -45,7 +45,7 @@ func CheckVertexPodsStatus(vertexPods *corev1.PodList) (healthy bool, reason str
}
}
}
- return true, "Running", "All vertex pods are healthy"
+ return true, "Running", "All pods are healthy"
}
func isPodHealthy(pod *corev1.Pod) (healthy bool, reason string) {
@@ -60,6 +60,23 @@ func isPodHealthy(pod *corev1.Pod) (healthy bool, reason string) {
return true, ""
}
+func NumOfReadyPods(pods corev1.PodList) int {
+ result := 0
+ for _, pod := range pods.Items {
+ ready := true
+ for _, s := range pod.Status.ContainerStatuses {
+ if !s.Ready {
+ ready = false
+ break
+ }
+ }
+ if ready {
+ result++
+ }
+ }
+ return result
+}
+
// CheckVertexStatus will calculate the status of the vertices and return the status and reason
func CheckVertexStatus(vertices *dfv1.VertexList) (healthy bool, reason string, message string) {
for _, vertex := range vertices.Items {
diff --git a/pkg/reconciler/util_test.go b/pkg/reconciler/util_test.go
index 36fbbfa5c5..51348f5a32 100644
--- a/pkg/reconciler/util_test.go
+++ b/pkg/reconciler/util_test.go
@@ -36,8 +36,8 @@ func TestCheckVertexPodsStatus(t *testing.T) {
}},
}},
}
- done, reason, message := CheckVertexPodsStatus(&pods)
- assert.Equal(t, "All vertex pods are healthy", message)
+ done, reason, message := CheckPodsStatus(&pods)
+ assert.Equal(t, "All pods are healthy", message)
assert.Equal(t, "Running", reason)
assert.True(t, done)
})
@@ -52,7 +52,7 @@ func TestCheckVertexPodsStatus(t *testing.T) {
},
},
}
- done, reason, message := CheckVertexPodsStatus(&pods)
+ done, reason, message := CheckPodsStatus(&pods)
assert.Equal(t, "Pod test-pod is unhealthy", message)
assert.Equal(t, "PodCrashLoopBackOff", reason)
assert.False(t, done)
@@ -62,7 +62,7 @@ func TestCheckVertexPodsStatus(t *testing.T) {
pods := corev1.PodList{
Items: []corev1.Pod{},
}
- done, reason, message := CheckVertexPodsStatus(&pods)
+ done, reason, message := CheckPodsStatus(&pods)
assert.Equal(t, "No Pods found", message)
assert.Equal(t, "NoPodsFound", reason)
assert.True(t, done)
@@ -254,3 +254,77 @@ func TestGetStatefulSetStatus(t *testing.T) {
assert.Equal(t, "Waiting for statefulset spec update to be observed...", msg)
})
}
+
+func TestNumOfReadyPods(t *testing.T) {
+ pods := corev1.PodList{
+ Items: []corev1.Pod{
+ {
+ Status: corev1.PodStatus{
+ ContainerStatuses: []corev1.ContainerStatus{
+ {
+ Ready: true,
+ },
+ {
+ Ready: true,
+ },
+ },
+ },
+ },
+ {
+ Status: corev1.PodStatus{
+ ContainerStatuses: []corev1.ContainerStatus{
+ {
+ Ready: false,
+ },
+ {
+ Ready: true,
+ },
+ },
+ },
+ },
+ {
+ Status: corev1.PodStatus{
+ ContainerStatuses: []corev1.ContainerStatus{
+ {
+ Ready: true,
+ },
+ {
+ Ready: false,
+ },
+ },
+ },
+ },
+ {
+ Status: corev1.PodStatus{
+ ContainerStatuses: []corev1.ContainerStatus{
+ {
+ Ready: true,
+ },
+ {
+ Ready: true,
+ },
+ {
+ Ready: true,
+ },
+ },
+ },
+ },
+ {
+ Status: corev1.PodStatus{
+ ContainerStatuses: []corev1.ContainerStatus{
+ {
+ Ready: false,
+ },
+ {
+ Ready: false,
+ },
+ {
+ Ready: false,
+ },
+ },
+ },
+ },
+ },
+ }
+ assert.Equal(t, 2, NumOfReadyPods(pods))
+}
diff --git a/pkg/reconciler/vertex/controller.go b/pkg/reconciler/vertex/controller.go
index f9e82436d1..c1e6b8febb 100644
--- a/pkg/reconciler/vertex/controller.go
+++ b/pkg/reconciler/vertex/controller.go
@@ -330,7 +330,12 @@ func (r *vertexReconciler) reconcile(ctx context.Context, vertex *dfv1.Vertex) (
vertex.Status.MarkPodNotHealthy("ListVerticesPodsFailed", err.Error())
return ctrl.Result{}, fmt.Errorf("failed to get pods of a vertex: %w", err)
}
- if healthy, reason, msg := reconciler.CheckVertexPodsStatus(&podList); healthy {
+ readyPods := reconciler.NumOfReadyPods(podList)
+ if readyPods > desiredReplicas { // It might happen in some corner cases, such as during rollout
+ readyPods = desiredReplicas
+ }
+ vertex.Status.ReadyReplicas = uint32(readyPods)
+ if healthy, reason, msg := reconciler.CheckPodsStatus(&podList); healthy {
vertex.Status.MarkPodHealthy(reason, msg)
} else {
// Do not need to explicitly requeue, since the it keeps watching the status change of the pods
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index fd612ba12a..3befdf04b7 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -253,7 +253,7 @@ fn metrics_router(metrics_state: MetricsState) -> Router {
Router::new()
.route("/metrics", get(metrics_handler))
.route("/livez", get(livez))
- .route("/readyz", get(readyz))
+ .route("/readyz", get(sidecar_livez))
.route("/sidecar-livez", get(sidecar_livez))
.with_state(metrics_state)
}
@@ -262,10 +262,6 @@ async fn livez() -> impl IntoResponse {
StatusCode::NO_CONTENT
}
-async fn readyz() -> impl IntoResponse {
- StatusCode::NO_CONTENT
-}
-
async fn sidecar_livez(State(mut state): State) -> impl IntoResponse {
if !state.source_client.is_ready().await {
error!("Source client is not available");
diff --git a/rust/numaflow-models/src/models/mono_vertex_status.rs b/rust/numaflow-models/src/models/mono_vertex_status.rs
index 86c0cb489f..b0773192e8 100644
--- a/rust/numaflow-models/src/models/mono_vertex_status.rs
+++ b/rust/numaflow-models/src/models/mono_vertex_status.rs
@@ -27,20 +27,25 @@ pub struct MonoVertexStatus {
pub last_updated: Option,
#[serde(rename = "message", skip_serializing_if = "Option::is_none")]
pub message: Option,
+ /// The generation observed by the MonoVertex controller.
#[serde(rename = "observedGeneration", skip_serializing_if = "Option::is_none")]
pub observed_generation: Option,
#[serde(rename = "phase", skip_serializing_if = "Option::is_none")]
pub phase: Option,
+ /// The number of pods targeted by this MonoVertex with a Ready Condition.
+ #[serde(rename = "readyReplicas", skip_serializing_if = "Option::is_none")]
+ pub ready_replicas: Option,
#[serde(rename = "reason", skip_serializing_if = "Option::is_none")]
pub reason: Option,
- #[serde(rename = "replicas")]
- pub replicas: i64,
+ /// Total number of non-terminated pods targeted by this MonoVertex (their labels match the selector).
+ #[serde(rename = "replicas", skip_serializing_if = "Option::is_none")]
+ pub replicas: Option,
#[serde(rename = "selector", skip_serializing_if = "Option::is_none")]
pub selector: Option,
}
impl MonoVertexStatus {
- pub fn new(replicas: i64) -> MonoVertexStatus {
+ pub fn new() -> MonoVertexStatus {
MonoVertexStatus {
conditions: None,
last_scaled_at: None,
@@ -48,8 +53,9 @@ impl MonoVertexStatus {
message: None,
observed_generation: None,
phase: None,
+ ready_replicas: None,
reason: None,
- replicas,
+ replicas: None,
selector: None,
}
}
diff --git a/rust/numaflow-models/src/models/pipeline_status.rs b/rust/numaflow-models/src/models/pipeline_status.rs
index 6e061bbdbe..27d6049658 100644
--- a/rust/numaflow-models/src/models/pipeline_status.rs
+++ b/rust/numaflow-models/src/models/pipeline_status.rs
@@ -27,6 +27,7 @@ pub struct PipelineStatus {
pub map_udf_count: Option,
#[serde(rename = "message", skip_serializing_if = "Option::is_none")]
pub message: Option,
+ /// The generation observed by the Pipeline controller.
#[serde(rename = "observedGeneration", skip_serializing_if = "Option::is_none")]
pub observed_generation: Option,
#[serde(rename = "phase", skip_serializing_if = "Option::is_none")]
diff --git a/rust/numaflow-models/src/models/vertex_status.rs b/rust/numaflow-models/src/models/vertex_status.rs
index b0c9cd0e9a..326a83c9b7 100644
--- a/rust/numaflow-models/src/models/vertex_status.rs
+++ b/rust/numaflow-models/src/models/vertex_status.rs
@@ -25,28 +25,34 @@ pub struct VertexStatus {
pub last_scaled_at: Option,
#[serde(rename = "message", skip_serializing_if = "Option::is_none")]
pub message: Option,
+ /// The generation observed by the Vertex controller.
#[serde(rename = "observedGeneration", skip_serializing_if = "Option::is_none")]
pub observed_generation: Option,
- #[serde(rename = "phase")]
- pub phase: String,
+ #[serde(rename = "phase", skip_serializing_if = "Option::is_none")]
+ pub phase: Option,
+ /// The number of pods targeted by this Vertex with a Ready Condition.
+ #[serde(rename = "readyReplicas", skip_serializing_if = "Option::is_none")]
+ pub ready_replicas: Option,
#[serde(rename = "reason", skip_serializing_if = "Option::is_none")]
pub reason: Option,
- #[serde(rename = "replicas")]
- pub replicas: i64,
+ /// Total number of non-terminated pods targeted by this Vertex (their labels match the selector).
+ #[serde(rename = "replicas", skip_serializing_if = "Option::is_none")]
+ pub replicas: Option,
#[serde(rename = "selector", skip_serializing_if = "Option::is_none")]
pub selector: Option,
}
impl VertexStatus {
- pub fn new(phase: String, replicas: i64) -> VertexStatus {
+ pub fn new() -> VertexStatus {
VertexStatus {
conditions: None,
last_scaled_at: None,
message: None,
observed_generation: None,
- phase,
+ phase: None,
+ ready_replicas: None,
reason: None,
- replicas,
+ replicas: None,
selector: None,
}
}
From a77c9391e9e6dbdd00cbc50376b90b99eebc6cc5 Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Thu, 29 Aug 2024 20:01:10 -0700
Subject: [PATCH 037/188] fix: add latency metrics for mvtx (#2013)
Signed-off-by: Vigith Maurice
---
rust/monovertex/src/forwarder.rs | 31 ++++++++++-
rust/monovertex/src/metrics.rs | 96 +++++++++++++++++++++++---------
2 files changed, 98 insertions(+), 29 deletions(-)
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index 8f61b68ff1..e3b3c5285f 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -106,7 +106,7 @@ impl Forwarder {
}
forward_metrics()
- .e2e_processing_time
+ .e2e_time
.get_or_create(&self.common_labels)
.observe(start_time.elapsed().as_micros() as f64);
}
@@ -127,6 +127,10 @@ impl Forwarder {
messages.len(),
start_time.elapsed().as_millis()
);
+ forward_metrics()
+ .read_time
+ .get_or_create(&self.common_labels)
+ .observe(start_time.elapsed().as_micros() as f64);
// read returned 0 messages, nothing more to be done.
if messages.is_empty() {
@@ -192,6 +196,10 @@ impl Forwarder {
"Transformer latency - {}ms",
start_time.elapsed().as_millis()
);
+ forward_metrics()
+ .transform_time
+ .get_or_create(&self.common_labels)
+ .observe(start_time.elapsed().as_micros() as f64);
Ok(results)
}
@@ -204,6 +212,9 @@ impl Forwarder {
return Ok(());
}
+ // this start time is for tracking the total time taken
+ let start_time_e2e = tokio::time::Instant::now();
+
let mut attempts = 0;
let mut error_map = HashMap::new();
let mut fallback_msgs = Vec::new();
@@ -215,7 +226,11 @@ impl Forwarder {
let start_time = tokio::time::Instant::now();
match self.sink_client.sink_fn(messages_to_send.clone()).await {
Ok(response) => {
- debug!("Sink latency - {}ms", start_time.elapsed().as_millis());
+ debug!(
+ attempts=attempts,
+ "Sink latency - {}ms",
+ start_time.elapsed().as_millis()
+ );
attempts += 1;
// create a map of id to result, since there is no strict requirement
@@ -275,6 +290,10 @@ impl Forwarder {
self.handle_fallback_messages(fallback_msgs).await?;
}
+ forward_metrics()
+ .sink_time
+ .get_or_create(&self.common_labels)
+ .observe(start_time_e2e.elapsed().as_micros() as f64);
forward_metrics()
.sink_write_total
.get_or_create(&self.common_labels)
@@ -379,8 +398,16 @@ impl Forwarder {
async fn acknowledge_messages(&mut self, offsets: Vec) -> Result<()> {
let n = offsets.len();
let start_time = tokio::time::Instant::now();
+
self.source_client.ack_fn(offsets).await?;
+
debug!("Ack latency - {}ms", start_time.elapsed().as_millis());
+
+ forward_metrics()
+ .ack_time
+ .get_or_create(&self.common_labels)
+ .observe(start_time.elapsed().as_micros() as f64);
+
forward_metrics()
.ack_total
.get_or_create(&self.common_labels)
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index 3befdf04b7..712118166b 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -29,7 +29,7 @@ use prometheus_client::registry::Registry;
// Define the labels for the metrics
// Note: Please keep consistent with the definitions in MonoVertex daemon
-const VERTEX_NAME_LABEL: &str = "mvtx_name";
+const MVTX_NAME_LABEL: &str = "mvtx_name";
const REPLICA_LABEL: &str = "mvtx_replica";
const PENDING_PERIOD_LABEL: &str = "period";
@@ -37,14 +37,22 @@ const PENDING_PERIOD_LABEL: &str = "period";
// Note: We do not add a suffix to the metric name, as the suffix is inferred through the metric type
// by the prometheus client library
// refer: https://github.com/prometheus/client_rust/blob/master/src/registry.rs#L102
-
// Note: Please keep consistent with the definitions in MonoVertex daemon
+
+// counters (please note the prefix _total, and read above link)
const READ_TOTAL: &str = "monovtx_read";
const READ_BYTES_TOTAL: &str = "monovtx_read_bytes";
const ACK_TOTAL: &str = "monovtx_ack";
const SINK_WRITE_TOTAL: &str = "monovtx_sink_write";
-const E2E_PROCESSING_TIME: &str = "monovtx_processing_time";
+// pending as gauge
const SOURCE_PENDING: &str = "monovtx_pending";
+// processing times as timers
+const E2E_TIME: &str = "monovtx_processing_time";
+const READ_TIME: &str = "monovtx_read_time";
+const TRANSFORM_TIME: &str = "monovtx_transformer_time";
+const ACK_TIME: &str = "monovtx_ack_time";
+const SINK_TIME: &str = "monovtx_sink_time";
+
#[derive(Clone)]
pub(crate) struct MetricsState {
@@ -88,35 +96,47 @@ fn global_registry() -> &'static GlobalRegistry {
// The labels are provided in the form of Vec<(String, String)
// The second argument is the metric kind.
pub struct MonoVtxMetrics {
+ // counters
pub read_total: Family, Counter>,
pub read_bytes_total: Family, Counter>,
pub ack_total: Family, Counter>,
pub sink_write_total: Family, Counter>,
- pub e2e_processing_time: Family, Histogram>,
+ // gauge
pub source_pending: Family, Gauge>,
+ // timers
+ pub e2e_time: Family, Histogram>,
+ pub read_time: Family, Histogram>,
+ pub transform_time: Family, Histogram>,
+ pub ack_time: Family, Histogram>,
+ pub sink_time: Family, Histogram>,
}
/// impl the MonoVtxMetrics struct and create a new object
impl MonoVtxMetrics {
fn new() -> Self {
- let monovtx_read_total = Family::, Counter>::default();
- let monovtx_ack_total = Family::, Counter>::default();
- let monovtx_read_bytes_total = Family::, Counter>::default();
- let monovtx_sink_write_total = Family::, Counter>::default();
-
- let monovtx_processing_time =
- Family::, Histogram>::new_with_constructor(|| {
- Histogram::new(exponential_buckets(100.0, 60000000.0 * 15.0, 10))
- });
- let monovtx_pending = Family::, Gauge>::default();
-
let metrics = Self {
- read_total: monovtx_read_total,
- read_bytes_total: monovtx_read_bytes_total,
- ack_total: monovtx_ack_total,
- sink_write_total: monovtx_sink_write_total,
- e2e_processing_time: monovtx_processing_time,
- source_pending: monovtx_pending,
+ read_total: Family::, Counter>::default(),
+ read_bytes_total: Family::, Counter>::default(),
+ ack_total: Family::, Counter>::default(),
+ sink_write_total: Family::, Counter>::default(),
+ // gauge
+ source_pending: Family::, Gauge>::default(),
+ // timers
+ e2e_time: Family::, Histogram>::new_with_constructor(|| {
+ Histogram::new(exponential_buckets(100.0, 60000000.0 * 15.0, 10))
+ }),
+ read_time: Family::, Histogram>::new_with_constructor(|| {
+ Histogram::new(exponential_buckets(100.0, 60000000.0 * 15.0, 10))
+ }),
+ transform_time: Family::, Histogram>::new_with_constructor(
+ || Histogram::new(exponential_buckets(100.0, 60000000.0 * 15.0, 10)),
+ ),
+ ack_time: Family::, Histogram>::new_with_constructor(|| {
+ Histogram::new(exponential_buckets(100.0, 60000000.0 * 15.0, 10))
+ }),
+ sink_time: Family::, Histogram>::new_with_constructor(|| {
+ Histogram::new(exponential_buckets(100.0, 60000000.0 * 15.0, 10))
+ }),
};
let mut registry = global_registry().registry.lock();
@@ -136,21 +156,43 @@ impl MonoVtxMetrics {
"A Counter to keep track of the total number of messages acknowledged by the sink",
metrics.ack_total.clone(),
);
- registry.register(
- E2E_PROCESSING_TIME,
- "A Histogram to keep track of the total time taken to forward a chunk, the time is in microseconds",
- metrics.e2e_processing_time.clone(),
- );
registry.register(
READ_BYTES_TOTAL,
"A Counter to keep track of the total number of bytes read from the source",
metrics.read_bytes_total.clone(),
);
+ // gauges
registry.register(
SOURCE_PENDING,
"A Gauge to keep track of the total number of pending messages for the monovtx",
metrics.source_pending.clone(),
);
+ // timers
+ registry.register(
+ E2E_TIME,
+ "A Histogram to keep track of the total time taken to forward a chunk, in microseconds",
+ metrics.e2e_time.clone(),
+ );
+ registry.register(
+ READ_TIME,
+ "A Histogram to keep track of the total time taken to Read from the Source, in microseconds",
+ metrics.read_time.clone(),
+ );
+ registry.register(
+ TRANSFORM_TIME,
+ "A Histogram to keep track of the total time taken to Transform, in microseconds",
+ metrics.transform_time.clone(),
+ );
+ registry.register(
+ ACK_TIME,
+ "A Histogram to keep track of the total time taken to Ack to the Source, in microseconds",
+ metrics.ack_time.clone(),
+ );
+ registry.register(
+ SINK_TIME,
+ "A Histogram to keep track of the total time taken to Write to the Sink, in microseconds",
+ metrics.sink_time.clone(),
+ );
metrics
}
@@ -177,7 +219,7 @@ pub(crate) fn forward_metrics_labels() -> &'static Vec<(String, String)> {
MONOVTX_METRICS_LABELS.get_or_init(|| {
let common_labels = vec![
(
- VERTEX_NAME_LABEL.to_string(),
+ MVTX_NAME_LABEL.to_string(),
config().mono_vertex_name.clone(),
),
(REPLICA_LABEL.to_string(), config().replica.to_string()),
From 39e6ddd47cb6d2a012d7463a66ff667993197df8 Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Sat, 31 Aug 2024 00:21:46 +0530
Subject: [PATCH 038/188] chore: use RUST_LOG env to control logging in rust
binary (#2017)
Signed-off-by: Yashash H L
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
rust/Cargo.lock | 1 +
rust/Cargo.toml | 1 +
rust/monovertex/Cargo.toml | 5 ++---
rust/monovertex/src/config.rs | 10 ----------
rust/monovertex/src/forwarder.rs | 2 +-
rust/monovertex/src/lib.rs | 14 +-------------
rust/monovertex/src/metrics.rs | 6 +-----
rust/servesink/src/lib.rs | 9 ---------
rust/serving/src/lib.rs | 12 ------------
rust/src/bin/main.rs | 15 +++++++++++++++
10 files changed, 22 insertions(+), 53 deletions(-)
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index c269c5e60d..4e3ba1085b 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1594,6 +1594,7 @@ dependencies = [
"serving",
"tokio",
"tracing",
+ "tracing-subscriber",
]
[[package]]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 45a630b732..d4c5152f12 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -17,3 +17,4 @@ servesink = { path = "servesink" }
serving = { path = "serving" }
monovertex = { path = "monovertex" }
tracing = "0.1.40"
+tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index d56a502541..00c51e6e9a 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -23,7 +23,7 @@ tower = "0.4.13"
uuid = { version = "1.10.0", features = ["v4"] }
once_cell = "1.19.0"
serde_json = "1.0.122"
-numaflow-models = { path = "../numaflow-models"}
+numaflow-models = { path = "../numaflow-models" }
trait-variant = "0.1.2"
rcgen = "0.13.1"
rustls = { version = "0.23.12", features = ["aws_lc_rs"] }
@@ -35,9 +35,8 @@ parking_lot = "0.12.3"
prometheus-client = "0.22.3"
[dev-dependencies]
-tower = "0.4.13"
tempfile = "3.11.0"
-numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch="main" }
+numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
[build-dependencies]
tonic-build = "0.12.1"
diff --git a/rust/monovertex/src/config.rs b/rust/monovertex/src/config.rs
index 5a3121e862..00f966b9bc 100644
--- a/rust/monovertex/src/config.rs
+++ b/rust/monovertex/src/config.rs
@@ -3,7 +3,6 @@ use std::sync::OnceLock;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
-use tracing::level_filters::LevelFilter;
use numaflow_models::models::MonoVertex;
@@ -14,7 +13,6 @@ const ENV_GRPC_MAX_MESSAGE_SIZE: &str = "NUMAFLOW_GRPC_MAX_MESSAGE_SIZE";
const ENV_POD_REPLICA: &str = "NUMAFLOW_REPLICA";
const DEFAULT_GRPC_MAX_MESSAGE_SIZE: usize = 64 * 1024 * 1024; // 64 MB
const DEFAULT_METRICS_PORT: u16 = 2469;
-const ENV_LOG_LEVEL: &str = "NUMAFLOW_DEBUG";
const DEFAULT_LAG_CHECK_INTERVAL_IN_SECS: u16 = 5;
const DEFAULT_LAG_REFRESH_INTERVAL_IN_SECS: u16 = 3;
const DEFAULT_BATCH_SIZE: u64 = 500;
@@ -38,7 +36,6 @@ pub struct Settings {
pub batch_size: u64,
pub timeout_in_ms: u32,
pub metrics_server_listen_port: u16,
- pub log_level: String,
pub grpc_max_message_size: usize,
pub is_transformer_enabled: bool,
pub is_fallback_enabled: bool,
@@ -56,7 +53,6 @@ impl Default for Settings {
batch_size: DEFAULT_BATCH_SIZE,
timeout_in_ms: DEFAULT_TIMEOUT_IN_MS,
metrics_server_listen_port: DEFAULT_METRICS_PORT,
- log_level: LevelFilter::INFO.to_string(),
grpc_max_message_size: DEFAULT_GRPC_MAX_MESSAGE_SIZE,
is_transformer_enabled: false,
is_fallback_enabled: false,
@@ -122,9 +118,6 @@ impl Settings {
.is_some();
}
- settings.log_level =
- env::var(ENV_LOG_LEVEL).unwrap_or_else(|_| LevelFilter::INFO.to_string());
-
settings.grpc_max_message_size = env::var(ENV_GRPC_MAX_MESSAGE_SIZE)
.unwrap_or_else(|_| DEFAULT_GRPC_MAX_MESSAGE_SIZE.to_string())
.parse()
@@ -152,7 +145,6 @@ mod tests {
// Set up environment variables
unsafe {
env::set_var(ENV_MONO_VERTEX_OBJ, "eyJtZXRhZGF0YSI6eyJuYW1lIjoic2ltcGxlLW1vbm8tdmVydGV4IiwibmFtZXNwYWNlIjoiZGVmYXVsdCIsImNyZWF0aW9uVGltZXN0YW1wIjpudWxsfSwic3BlYyI6eyJyZXBsaWNhcyI6MCwic291cmNlIjp7InRyYW5zZm9ybWVyIjp7ImNvbnRhaW5lciI6eyJpbWFnZSI6InF1YXkuaW8vbnVtYWlvL251bWFmbG93LXJzL21hcHQtZXZlbnQtdGltZS1maWx0ZXI6c3RhYmxlIiwicmVzb3VyY2VzIjp7fX0sImJ1aWx0aW4iOm51bGx9LCJ1ZHNvdXJjZSI6eyJjb250YWluZXIiOnsiaW1hZ2UiOiJkb2NrZXIuaW50dWl0LmNvbS9wZXJzb25hbC95aGwwMS9zaW1wbGUtc291cmNlOnN0YWJsZSIsInJlc291cmNlcyI6e319fX0sInNpbmsiOnsidWRzaW5rIjp7ImNvbnRhaW5lciI6eyJpbWFnZSI6ImRvY2tlci5pbnR1aXQuY29tL3BlcnNvbmFsL3lobDAxL2JsYWNraG9sZS1zaW5rOnN0YWJsZSIsInJlc291cmNlcyI6e319fX0sImxpbWl0cyI6eyJyZWFkQmF0Y2hTaXplIjo1MDAsInJlYWRUaW1lb3V0IjoiMXMifSwic2NhbGUiOnt9fSwic3RhdHVzIjp7InJlcGxpY2FzIjowLCJsYXN0VXBkYXRlZCI6bnVsbCwibGFzdFNjYWxlZEF0IjpudWxsfX0=");
- env::set_var(ENV_LOG_LEVEL, "debug");
env::set_var(ENV_GRPC_MAX_MESSAGE_SIZE, "128000000");
};
@@ -163,13 +155,11 @@ mod tests {
assert_eq!(settings.mono_vertex_name, "simple-mono-vertex");
assert_eq!(settings.batch_size, 500);
assert_eq!(settings.timeout_in_ms, 1000);
- assert_eq!(settings.log_level, "debug");
assert_eq!(settings.grpc_max_message_size, 128000000);
// Clean up environment variables
unsafe {
env::remove_var(ENV_MONO_VERTEX_OBJ);
- env::remove_var(ENV_LOG_LEVEL);
env::remove_var(ENV_GRPC_MAX_MESSAGE_SIZE);
};
}
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index e3b3c5285f..8efe16c844 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -227,7 +227,7 @@ impl Forwarder {
match self.sink_client.sink_fn(messages_to_send.clone()).await {
Ok(response) => {
debug!(
- attempts=attempts,
+ attempts = attempts,
"Sink latency - {}ms",
start_time.elapsed().as_millis()
);
diff --git a/rust/monovertex/src/lib.rs b/rust/monovertex/src/lib.rs
index 9acabc95ea..c1d172adf9 100644
--- a/rust/monovertex/src/lib.rs
+++ b/rust/monovertex/src/lib.rs
@@ -12,9 +12,7 @@ use tokio::signal;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use tokio_util::sync::CancellationToken;
-use tracing::level_filters::LevelFilter;
use tracing::{error, info, warn};
-use tracing_subscriber::EnvFilter;
/// SourcerSinker orchestrates data movement from the Source to the Sink via the optional SourceTransformer.
/// The forward-a-chunk executes the following in an infinite loop till a shutdown signal is received:
@@ -43,17 +41,6 @@ mod server_info;
mod metrics;
pub async fn mono_vertex() {
- // Initialize the logger
- tracing_subscriber::fmt()
- .with_env_filter(
- EnvFilter::builder()
- .with_default_directive(LevelFilter::INFO.into())
- .parse_lossy(&config().log_level),
- )
- .with_target(false)
- .with_ansi(false)
- .init();
-
// Initialize the source, sink and transformer configurations
// We are using the default configurations for now.
let source_config = SourceConfig {
@@ -83,6 +70,7 @@ pub async fn mono_vertex() {
let cln_token = CancellationToken::new();
let shutdown_cln_token = cln_token.clone();
+
// wait for SIG{INT,TERM} and invoke cancellation token.
let shutdown_handle: JoinHandle> = tokio::spawn(async move {
shutdown_signal().await;
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index 712118166b..dc5dfd6dc9 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -53,7 +53,6 @@ const TRANSFORM_TIME: &str = "monovtx_transformer_time";
const ACK_TIME: &str = "monovtx_ack_time";
const SINK_TIME: &str = "monovtx_sink_time";
-
#[derive(Clone)]
pub(crate) struct MetricsState {
pub source_client: SourceClient,
@@ -204,10 +203,7 @@ static MONOVTX_METRICS: OnceLock = OnceLock::new();
// forward_metrics is a helper function used to fetch the
// MonoVtxMetrics object
pub(crate) fn forward_metrics() -> &'static MonoVtxMetrics {
- MONOVTX_METRICS.get_or_init(|| {
- let metrics = MonoVtxMetrics::new();
- metrics
- })
+ MONOVTX_METRICS.get_or_init(MonoVtxMetrics::new)
}
/// MONOVTX_METRICS_LABELS are used to store the common labels used in the metrics
diff --git a/rust/servesink/src/lib.rs b/rust/servesink/src/lib.rs
index b54ed8b580..3c384b657f 100644
--- a/rust/servesink/src/lib.rs
+++ b/rust/servesink/src/lib.rs
@@ -3,21 +3,12 @@ use std::error::Error;
use numaflow::sink::{self, Response, SinkRequest};
use reqwest::Client;
use tracing::{error, warn};
-use tracing_subscriber::prelude::*;
const NUMAFLOW_CALLBACK_URL_HEADER: &str = "X-Numaflow-Callback-Url";
const NUMAFLOW_ID_HEADER: &str = "X-Numaflow-Id";
/// servesink is a Numaflow Sink which forwards the payload to the Numaflow serving URL.
pub async fn servesink() -> Result<(), Box> {
- tracing_subscriber::registry()
- .with(
- tracing_subscriber::EnvFilter::try_from_default_env()
- .unwrap_or_else(|_| "servesink=debug".into()),
- )
- .with(tracing_subscriber::fmt::layer().with_ansi(false))
- .init();
-
sink::Server::new(ServeSink::new()).start().await
}
diff --git a/rust/serving/src/lib.rs b/rust/serving/src/lib.rs
index 86265a5ed0..1838fdb77c 100644
--- a/rust/serving/src/lib.rs
+++ b/rust/serving/src/lib.rs
@@ -6,8 +6,6 @@ use crate::pipeline::min_pipeline_spec;
use axum_server::tls_rustls::RustlsConfig;
use std::net::SocketAddr;
use tracing::info;
-use tracing_subscriber::layer::SubscriberExt;
-use tracing_subscriber::util::SubscriberInitExt;
mod app;
mod config;
@@ -23,16 +21,6 @@ pub async fn serve() -> std::result::Result<(), Box = env::args().collect();
+ // Set up the tracing subscriber. RUST_LOG can be used to set the log level.
+ // The default log level is `info`. The `axum::rejection=trace` enables showing
+ // rejections from built-in extractors at `TRACE` level.
+ tracing_subscriber::registry()
+ .with(
+ tracing_subscriber::EnvFilter::try_from_default_env()
+ // TODO: add a better default based on entry point invocation
+ // e.g., serving/monovertex might need a different default
+ .unwrap_or_else(|_| "info".into()),
+ )
+ .with(tracing_subscriber::fmt::layer().with_ansi(false))
+ .init();
+
// Based on the argument, run the appropriate component.
if args.contains(&"--serving".to_string()) {
if let Err(e) = serving::serve().await {
From 55230e84fd86f05bcac96dd4b42afe73aa1b2e4a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 30 Aug 2024 13:28:00 -0700
Subject: [PATCH 039/188] chore(deps): bump webpack from 5.93.0 to 5.94.0 in
/ui (#2018)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
ui/yarn.lock | 27 +++++----------------------
1 file changed, 5 insertions(+), 22 deletions(-)
diff --git a/ui/yarn.lock b/ui/yarn.lock
index eda56fac2e..36a04c8dd9 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -2640,22 +2640,6 @@
dependencies:
"@types/ms" "*"
-"@types/eslint-scope@^3.7.3":
- version "3.7.7"
- resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5"
- integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==
- dependencies:
- "@types/eslint" "*"
- "@types/estree" "*"
-
-"@types/eslint@*":
- version "9.6.0"
- resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.0.tgz#51d4fe4d0316da9e9f2c80884f2c20ed5fb022ff"
- integrity sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==
- dependencies:
- "@types/estree" "*"
- "@types/json-schema" "*"
-
"@types/eslint@^7.29.0 || ^8.4.1":
version "8.56.11"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.11.tgz#e2ff61510a3b9454b3329fe7731e3b4c6f780041"
@@ -5192,7 +5176,7 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
-enhanced-resolve@^5.17.0:
+enhanced-resolve@^5.17.1:
version "5.17.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
@@ -11419,11 +11403,10 @@ webpack-sources@^3.2.3:
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack@^5.64.4:
- version "5.93.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.93.0.tgz#2e89ec7035579bdfba9760d26c63ac5c3462a5e5"
- integrity sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==
+ version "5.94.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f"
+ integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==
dependencies:
- "@types/eslint-scope" "^3.7.3"
"@types/estree" "^1.0.5"
"@webassemblyjs/ast" "^1.12.1"
"@webassemblyjs/wasm-edit" "^1.12.1"
@@ -11432,7 +11415,7 @@ webpack@^5.64.4:
acorn-import-attributes "^1.9.5"
browserslist "^4.21.10"
chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.17.0"
+ enhanced-resolve "^5.17.1"
es-module-lexer "^1.2.1"
eslint-scope "5.1.1"
events "^3.2.0"
From 40a3d2f5bd3ac57e075bc23b076c1e5df8436fc8 Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Fri, 30 Aug 2024 16:37:07 -0700
Subject: [PATCH 040/188] feat: allow configurable retryStrategy (#2010)
Signed-off-by: Sidhant Kohli
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
api/json-schema/schema.json | 33 +
api/openapi-spec/swagger.json | 33 +
.../numaflow.numaproj.io_monovertices.yaml | 15 +
.../full/numaflow.numaproj.io_pipelines.yaml | 15 +
.../full/numaflow.numaproj.io_vertices.yaml | 15 +
config/install.yaml | 45 +
config/namespace-install.yaml | 45 +
docs/APIs.md | 218 +++
pkg/apis/numaflow/v1alpha1/const.go | 24 +
pkg/apis/numaflow/v1alpha1/generated.pb.go | 1586 +++++++++++------
pkg/apis/numaflow/v1alpha1/generated.proto | 29 +
.../numaflow/v1alpha1/openapi_generated.go | 67 +-
pkg/apis/numaflow/v1alpha1/retry_strategy.go | 102 ++
.../numaflow/v1alpha1/retry_strategy_test.go | 115 ++
pkg/apis/numaflow/v1alpha1/sink.go | 3 +
.../v1alpha1/zz_generated.deepcopy.go | 53 +
pkg/metrics/metrics.go | 26 +-
pkg/reconciler/pipeline/validate.go | 34 +
pkg/reconciler/pipeline/validate_test.go | 122 ++
pkg/sinks/forward/forward.go | 238 ++-
pkg/sinks/forward/options.go | 13 -
rust/Cargo.lock | 1 +
rust/monovertex/Cargo.toml | 1 +
rust/monovertex/src/config.rs | 286 ++-
rust/monovertex/src/forwarder.rs | 250 ++-
rust/monovertex/src/metrics.rs | 24 +-
rust/numaflow-models/src/models/backoff.rs | 38 +
rust/numaflow-models/src/models/mod.rs | 4 +
.../src/models/retry_strategy.rs | 38 +
rust/numaflow-models/src/models/sink.rs | 3 +
30 files changed, 2752 insertions(+), 724 deletions(-)
create mode 100644 pkg/apis/numaflow/v1alpha1/retry_strategy.go
create mode 100644 pkg/apis/numaflow/v1alpha1/retry_strategy_test.go
create mode 100644 rust/numaflow-models/src/models/backoff.rs
create mode 100644 rust/numaflow-models/src/models/retry_strategy.rs
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index 584220c245..496b6a963f 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -17808,6 +17808,21 @@
},
"type": "object"
},
+ "io.numaproj.numaflow.v1alpha1.Backoff": {
+ "description": "Backoff defines parameters used to systematically configure the retry strategy.",
+ "properties": {
+ "interval": {
+ "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Duration",
+ "description": "Interval sets the delay to wait before retry, after a failure occurs."
+ },
+ "steps": {
+ "description": "Steps defines the number of times to try writing to a sink including retries",
+ "format": "int64",
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
"io.numaproj.numaflow.v1alpha1.BasicAuth": {
"description": "BasicAuth represents the basic authentication approach which contains a user name and a password.",
"properties": {
@@ -19752,6 +19767,20 @@
},
"type": "object"
},
+ "io.numaproj.numaflow.v1alpha1.RetryStrategy": {
+ "description": "RetryStrategy struct encapsulates the settings for retrying operations in the event of failures. It includes a BackOff strategy to manage the timing of retries and defines the action to take upon failure.",
+ "properties": {
+ "backoff": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Backoff",
+ "description": "BackOff specifies the parameters for the backoff strategy, controlling how delays between retries should increase."
+ },
+ "onFailure": {
+ "description": "OnFailure specifies the action to take when a retry fails. The default action is to retry.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
"io.numaproj.numaflow.v1alpha1.SASL": {
"properties": {
"gssapi": {
@@ -20064,6 +20093,10 @@
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Log",
"description": "Log sink is used to write the data to the log."
},
+ "retryStrategy": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.RetryStrategy",
+ "description": "RetryStrategy struct encapsulates the settings for retrying operations in the event of failures."
+ },
"udsink": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UDSink",
"description": "UDSink sink is used to write the data to the user-defined sink."
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index a326712b6c..41d81a0efb 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -17812,6 +17812,21 @@
}
}
},
+ "io.numaproj.numaflow.v1alpha1.Backoff": {
+ "description": "Backoff defines parameters used to systematically configure the retry strategy.",
+ "type": "object",
+ "properties": {
+ "interval": {
+ "description": "Interval sets the delay to wait before retry, after a failure occurs.",
+ "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Duration"
+ },
+ "steps": {
+ "description": "Steps defines the number of times to try writing to a sink including retries",
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ },
"io.numaproj.numaflow.v1alpha1.BasicAuth": {
"description": "BasicAuth represents the basic authentication approach which contains a user name and a password.",
"type": "object",
@@ -19738,6 +19753,20 @@
}
}
},
+ "io.numaproj.numaflow.v1alpha1.RetryStrategy": {
+ "description": "RetryStrategy struct encapsulates the settings for retrying operations in the event of failures. It includes a BackOff strategy to manage the timing of retries and defines the action to take upon failure.",
+ "type": "object",
+ "properties": {
+ "backoff": {
+ "description": "BackOff specifies the parameters for the backoff strategy, controlling how delays between retries should increase.",
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Backoff"
+ },
+ "onFailure": {
+ "description": "OnFailure specifies the action to take when a retry fails. The default action is to retry.",
+ "type": "string"
+ }
+ }
+ },
"io.numaproj.numaflow.v1alpha1.SASL": {
"type": "object",
"required": [
@@ -20051,6 +20080,10 @@
"description": "Log sink is used to write the data to the log.",
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Log"
},
+ "retryStrategy": {
+ "description": "RetryStrategy struct encapsulates the settings for retrying operations in the event of failures.",
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.RetryStrategy"
+ },
"udsink": {
"description": "UDSink sink is used to write the data to the user-defined sink.",
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UDSink"
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index d456bbb249..c07e927f5b 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -3615,6 +3615,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
diff --git a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
index 070ba0b033..4ddc954f61 100644
--- a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
@@ -7571,6 +7571,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
diff --git a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
index 7973694b88..8f0d150360 100644
--- a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
@@ -3158,6 +3158,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
diff --git a/config/install.yaml b/config/install.yaml
index a648c1413e..d2e76c26b3 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -6259,6 +6259,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
@@ -15799,6 +15814,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
@@ -21243,6 +21273,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index a922d0f7c5..13afdb3228 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -6259,6 +6259,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
@@ -15799,6 +15814,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
@@ -21243,6 +21273,21 @@ spec:
type: object
log:
type: object
+ retryStrategy:
+ properties:
+ backoff:
+ properties:
+ interval:
+ default: 1ms
+ type: string
+ steps:
+ format: int32
+ type: integer
+ type: object
+ onFailure:
+ default: retry
+ type: string
+ type: object
udsink:
properties:
container:
diff --git a/docs/APIs.md b/docs/APIs.md
index b5ba81450d..3e631f3870 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -908,6 +908,94 @@ needs to add “Authorization: Bearer ” in the header
+
+
+Backoff
+
+
+
+
+(Appears on:
+RetryStrategy )
+
+
+
+
+
+
+Backoff defines parameters used to systematically configure the retry
+strategy.
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+
+
+
+Description
+
+
+
+
+
+
+
+
+
+
+
+
+interval
+
+Kubernetes meta/v1.Duration
+
+
+
+
+(Optional)
+
+
+Interval sets the delay to wait before retry, after a failure occurs.
+
+
+
+
+
+
+
+
+
+
+steps
uint32
+
+
+
+
+(Optional)
+
+
+Steps defines the number of times to try writing to a sink including
+retries
+
+
+
+
+
+
+
+
+
+
BasicAuth
@@ -6773,6 +6861,23 @@ etc.).
+
+
+OnFailureRetryStrategy (string
alias)
+
+
+
+
+
+
+(Appears on:
+RetryStrategy )
+
+
+
+
+
+
PBQStorage
@@ -8126,6 +8231,97 @@ config
+
+
+RetryStrategy
+
+
+
+
+(Appears on:
+Sink )
+
+
+
+
+
+
+RetryStrategy struct encapsulates the settings for retrying operations
+in the event of failures. It includes a BackOff strategy to manage the
+timing of retries and defines the action to take upon failure.
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+
+
+
+Description
+
+
+
+
+
+
+
+
+
+
+
+
+backoff
+ Backoff
+
+
+
+
+(Optional)
+
+
+BackOff specifies the parameters for the backoff strategy, controlling
+how delays between retries should increase.
+
+
+
+
+
+
+
+
+
+
+onFailure
+
+OnFailureRetryStrategy
+
+
+
+
+(Optional)
+
+
+OnFailure specifies the action to take when a retry fails. The default
+action is to retry.
+
+
+
+
+
+
+
+
+
+
SASL
@@ -9318,6 +9514,28 @@ it.
+
+
+
+
+retryStrategy
+ RetryStrategy
+
+
+
+
+
+(Optional)
+
+
+RetryStrategy struct encapsulates the settings for retrying operations
+in the event of failures.
+
+
+
+
+
+
diff --git a/pkg/apis/numaflow/v1alpha1/const.go b/pkg/apis/numaflow/v1alpha1/const.go
index 8677101378..d0e9eb62f0 100644
--- a/pkg/apis/numaflow/v1alpha1/const.go
+++ b/pkg/apis/numaflow/v1alpha1/const.go
@@ -18,6 +18,7 @@ package v1alpha1
import (
"fmt"
+ "math"
"time"
)
@@ -243,6 +244,29 @@ const (
// Serving source
DefaultServingTTL = 24 * time.Hour
+
+ // Retry Strategy
+
+ // DefaultRetryInterval specifies the default time interval between retry attempts.
+ // This value can be adjusted depending on the specific requirements
+ // for responsiveness and system load considerations.
+ DefaultRetryInterval = 1 * time.Millisecond
+
+ // DefaultRetrySteps is defined to dictate how many times the platform should attempt to retry
+ // a write operation to a sink following a failure. The value is set to math.MaxInt32 - 1,
+ // effectively indicating an almost indefinite number of retries. This large default is chosen
+ // to ensure that the system will try persistently to carry out the operation unless explicitly
+ // configured otherwise. This approach can be useful in environments where loss of data
+ // due to intermittent failures is unacceptable.
+ DefaultRetrySteps = math.MaxInt32 - 1
+
+ // DefaultOnFailureRetryStrategy specifies the strategy to be used when the write to a sink fails and
+ // the retries count specified are exhausted.
+ // Setting this to 'OnFailureRetry' means the system is configured by default
+ // to retry the failed operation until successful completion.
+ // This strategy argues for robustness in operations, aiming
+ // to minimize the chances of data loss or failed deliveries in transient failure scenarios.
+ DefaultOnFailureRetryStrategy = OnFailureRetry
)
var (
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index 3a29fb484b..3514c89361 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -160,10 +160,38 @@ func (m *Authorization) XXX_DiscardUnknown() {
var xxx_messageInfo_Authorization proto.InternalMessageInfo
+func (m *Backoff) Reset() { *m = Backoff{} }
+func (*Backoff) ProtoMessage() {}
+func (*Backoff) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0d1b17d3865563, []int{4}
+}
+func (m *Backoff) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *Backoff) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+}
+func (m *Backoff) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Backoff.Merge(m, src)
+}
+func (m *Backoff) XXX_Size() int {
+ return m.Size()
+}
+func (m *Backoff) XXX_DiscardUnknown() {
+ xxx_messageInfo_Backoff.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Backoff proto.InternalMessageInfo
+
func (m *BasicAuth) Reset() { *m = BasicAuth{} }
func (*BasicAuth) ProtoMessage() {}
func (*BasicAuth) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{4}
+ return fileDescriptor_9d0d1b17d3865563, []int{5}
}
func (m *BasicAuth) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -191,7 +219,7 @@ var xxx_messageInfo_BasicAuth proto.InternalMessageInfo
func (m *Blackhole) Reset() { *m = Blackhole{} }
func (*Blackhole) ProtoMessage() {}
func (*Blackhole) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{5}
+ return fileDescriptor_9d0d1b17d3865563, []int{6}
}
func (m *Blackhole) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -219,7 +247,7 @@ var xxx_messageInfo_Blackhole proto.InternalMessageInfo
func (m *BufferServiceConfig) Reset() { *m = BufferServiceConfig{} }
func (*BufferServiceConfig) ProtoMessage() {}
func (*BufferServiceConfig) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{6}
+ return fileDescriptor_9d0d1b17d3865563, []int{7}
}
func (m *BufferServiceConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -247,7 +275,7 @@ var xxx_messageInfo_BufferServiceConfig proto.InternalMessageInfo
func (m *CombinedEdge) Reset() { *m = CombinedEdge{} }
func (*CombinedEdge) ProtoMessage() {}
func (*CombinedEdge) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{7}
+ return fileDescriptor_9d0d1b17d3865563, []int{8}
}
func (m *CombinedEdge) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -275,7 +303,7 @@ var xxx_messageInfo_CombinedEdge proto.InternalMessageInfo
func (m *Container) Reset() { *m = Container{} }
func (*Container) ProtoMessage() {}
func (*Container) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{8}
+ return fileDescriptor_9d0d1b17d3865563, []int{9}
}
func (m *Container) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -303,7 +331,7 @@ var xxx_messageInfo_Container proto.InternalMessageInfo
func (m *ContainerTemplate) Reset() { *m = ContainerTemplate{} }
func (*ContainerTemplate) ProtoMessage() {}
func (*ContainerTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{9}
+ return fileDescriptor_9d0d1b17d3865563, []int{10}
}
func (m *ContainerTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -331,7 +359,7 @@ var xxx_messageInfo_ContainerTemplate proto.InternalMessageInfo
func (m *DaemonTemplate) Reset() { *m = DaemonTemplate{} }
func (*DaemonTemplate) ProtoMessage() {}
func (*DaemonTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{10}
+ return fileDescriptor_9d0d1b17d3865563, []int{11}
}
func (m *DaemonTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -359,7 +387,7 @@ var xxx_messageInfo_DaemonTemplate proto.InternalMessageInfo
func (m *Edge) Reset() { *m = Edge{} }
func (*Edge) ProtoMessage() {}
func (*Edge) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{11}
+ return fileDescriptor_9d0d1b17d3865563, []int{12}
}
func (m *Edge) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -387,7 +415,7 @@ var xxx_messageInfo_Edge proto.InternalMessageInfo
func (m *FixedWindow) Reset() { *m = FixedWindow{} }
func (*FixedWindow) ProtoMessage() {}
func (*FixedWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{12}
+ return fileDescriptor_9d0d1b17d3865563, []int{13}
}
func (m *FixedWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -415,7 +443,7 @@ var xxx_messageInfo_FixedWindow proto.InternalMessageInfo
func (m *ForwardConditions) Reset() { *m = ForwardConditions{} }
func (*ForwardConditions) ProtoMessage() {}
func (*ForwardConditions) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{13}
+ return fileDescriptor_9d0d1b17d3865563, []int{14}
}
func (m *ForwardConditions) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -443,7 +471,7 @@ var xxx_messageInfo_ForwardConditions proto.InternalMessageInfo
func (m *Function) Reset() { *m = Function{} }
func (*Function) ProtoMessage() {}
func (*Function) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{14}
+ return fileDescriptor_9d0d1b17d3865563, []int{15}
}
func (m *Function) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -471,7 +499,7 @@ var xxx_messageInfo_Function proto.InternalMessageInfo
func (m *GSSAPI) Reset() { *m = GSSAPI{} }
func (*GSSAPI) ProtoMessage() {}
func (*GSSAPI) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{15}
+ return fileDescriptor_9d0d1b17d3865563, []int{16}
}
func (m *GSSAPI) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -499,7 +527,7 @@ var xxx_messageInfo_GSSAPI proto.InternalMessageInfo
func (m *GeneratorSource) Reset() { *m = GeneratorSource{} }
func (*GeneratorSource) ProtoMessage() {}
func (*GeneratorSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{16}
+ return fileDescriptor_9d0d1b17d3865563, []int{17}
}
func (m *GeneratorSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -527,7 +555,7 @@ var xxx_messageInfo_GeneratorSource proto.InternalMessageInfo
func (m *GetDaemonDeploymentReq) Reset() { *m = GetDaemonDeploymentReq{} }
func (*GetDaemonDeploymentReq) ProtoMessage() {}
func (*GetDaemonDeploymentReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{17}
+ return fileDescriptor_9d0d1b17d3865563, []int{18}
}
func (m *GetDaemonDeploymentReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -555,7 +583,7 @@ var xxx_messageInfo_GetDaemonDeploymentReq proto.InternalMessageInfo
func (m *GetJetStreamServiceSpecReq) Reset() { *m = GetJetStreamServiceSpecReq{} }
func (*GetJetStreamServiceSpecReq) ProtoMessage() {}
func (*GetJetStreamServiceSpecReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{18}
+ return fileDescriptor_9d0d1b17d3865563, []int{19}
}
func (m *GetJetStreamServiceSpecReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -583,7 +611,7 @@ var xxx_messageInfo_GetJetStreamServiceSpecReq proto.InternalMessageInfo
func (m *GetJetStreamStatefulSetSpecReq) Reset() { *m = GetJetStreamStatefulSetSpecReq{} }
func (*GetJetStreamStatefulSetSpecReq) ProtoMessage() {}
func (*GetJetStreamStatefulSetSpecReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{19}
+ return fileDescriptor_9d0d1b17d3865563, []int{20}
}
func (m *GetJetStreamStatefulSetSpecReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -611,7 +639,7 @@ var xxx_messageInfo_GetJetStreamStatefulSetSpecReq proto.InternalMessageInfo
func (m *GetMonoVertexDaemonDeploymentReq) Reset() { *m = GetMonoVertexDaemonDeploymentReq{} }
func (*GetMonoVertexDaemonDeploymentReq) ProtoMessage() {}
func (*GetMonoVertexDaemonDeploymentReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{20}
+ return fileDescriptor_9d0d1b17d3865563, []int{21}
}
func (m *GetMonoVertexDaemonDeploymentReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -639,7 +667,7 @@ var xxx_messageInfo_GetMonoVertexDaemonDeploymentReq proto.InternalMessageInfo
func (m *GetMonoVertexPodSpecReq) Reset() { *m = GetMonoVertexPodSpecReq{} }
func (*GetMonoVertexPodSpecReq) ProtoMessage() {}
func (*GetMonoVertexPodSpecReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{21}
+ return fileDescriptor_9d0d1b17d3865563, []int{22}
}
func (m *GetMonoVertexPodSpecReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -667,7 +695,7 @@ var xxx_messageInfo_GetMonoVertexPodSpecReq proto.InternalMessageInfo
func (m *GetRedisServiceSpecReq) Reset() { *m = GetRedisServiceSpecReq{} }
func (*GetRedisServiceSpecReq) ProtoMessage() {}
func (*GetRedisServiceSpecReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{22}
+ return fileDescriptor_9d0d1b17d3865563, []int{23}
}
func (m *GetRedisServiceSpecReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -695,7 +723,7 @@ var xxx_messageInfo_GetRedisServiceSpecReq proto.InternalMessageInfo
func (m *GetRedisStatefulSetSpecReq) Reset() { *m = GetRedisStatefulSetSpecReq{} }
func (*GetRedisStatefulSetSpecReq) ProtoMessage() {}
func (*GetRedisStatefulSetSpecReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{23}
+ return fileDescriptor_9d0d1b17d3865563, []int{24}
}
func (m *GetRedisStatefulSetSpecReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -723,7 +751,7 @@ var xxx_messageInfo_GetRedisStatefulSetSpecReq proto.InternalMessageInfo
func (m *GetSideInputDeploymentReq) Reset() { *m = GetSideInputDeploymentReq{} }
func (*GetSideInputDeploymentReq) ProtoMessage() {}
func (*GetSideInputDeploymentReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{24}
+ return fileDescriptor_9d0d1b17d3865563, []int{25}
}
func (m *GetSideInputDeploymentReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -751,7 +779,7 @@ var xxx_messageInfo_GetSideInputDeploymentReq proto.InternalMessageInfo
func (m *GetVertexPodSpecReq) Reset() { *m = GetVertexPodSpecReq{} }
func (*GetVertexPodSpecReq) ProtoMessage() {}
func (*GetVertexPodSpecReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{25}
+ return fileDescriptor_9d0d1b17d3865563, []int{26}
}
func (m *GetVertexPodSpecReq) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -779,7 +807,7 @@ var xxx_messageInfo_GetVertexPodSpecReq proto.InternalMessageInfo
func (m *GroupBy) Reset() { *m = GroupBy{} }
func (*GroupBy) ProtoMessage() {}
func (*GroupBy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{26}
+ return fileDescriptor_9d0d1b17d3865563, []int{27}
}
func (m *GroupBy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -807,7 +835,7 @@ var xxx_messageInfo_GroupBy proto.InternalMessageInfo
func (m *HTTPSource) Reset() { *m = HTTPSource{} }
func (*HTTPSource) ProtoMessage() {}
func (*HTTPSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{27}
+ return fileDescriptor_9d0d1b17d3865563, []int{28}
}
func (m *HTTPSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -835,7 +863,7 @@ var xxx_messageInfo_HTTPSource proto.InternalMessageInfo
func (m *IdleSource) Reset() { *m = IdleSource{} }
func (*IdleSource) ProtoMessage() {}
func (*IdleSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{28}
+ return fileDescriptor_9d0d1b17d3865563, []int{29}
}
func (m *IdleSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -863,7 +891,7 @@ var xxx_messageInfo_IdleSource proto.InternalMessageInfo
func (m *InterStepBufferService) Reset() { *m = InterStepBufferService{} }
func (*InterStepBufferService) ProtoMessage() {}
func (*InterStepBufferService) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{29}
+ return fileDescriptor_9d0d1b17d3865563, []int{30}
}
func (m *InterStepBufferService) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -891,7 +919,7 @@ var xxx_messageInfo_InterStepBufferService proto.InternalMessageInfo
func (m *InterStepBufferServiceList) Reset() { *m = InterStepBufferServiceList{} }
func (*InterStepBufferServiceList) ProtoMessage() {}
func (*InterStepBufferServiceList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{30}
+ return fileDescriptor_9d0d1b17d3865563, []int{31}
}
func (m *InterStepBufferServiceList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -919,7 +947,7 @@ var xxx_messageInfo_InterStepBufferServiceList proto.InternalMessageInfo
func (m *InterStepBufferServiceSpec) Reset() { *m = InterStepBufferServiceSpec{} }
func (*InterStepBufferServiceSpec) ProtoMessage() {}
func (*InterStepBufferServiceSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{31}
+ return fileDescriptor_9d0d1b17d3865563, []int{32}
}
func (m *InterStepBufferServiceSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -947,7 +975,7 @@ var xxx_messageInfo_InterStepBufferServiceSpec proto.InternalMessageInfo
func (m *InterStepBufferServiceStatus) Reset() { *m = InterStepBufferServiceStatus{} }
func (*InterStepBufferServiceStatus) ProtoMessage() {}
func (*InterStepBufferServiceStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{32}
+ return fileDescriptor_9d0d1b17d3865563, []int{33}
}
func (m *InterStepBufferServiceStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -975,7 +1003,7 @@ var xxx_messageInfo_InterStepBufferServiceStatus proto.InternalMessageInfo
func (m *JetStreamBufferService) Reset() { *m = JetStreamBufferService{} }
func (*JetStreamBufferService) ProtoMessage() {}
func (*JetStreamBufferService) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{33}
+ return fileDescriptor_9d0d1b17d3865563, []int{34}
}
func (m *JetStreamBufferService) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1003,7 +1031,7 @@ var xxx_messageInfo_JetStreamBufferService proto.InternalMessageInfo
func (m *JetStreamConfig) Reset() { *m = JetStreamConfig{} }
func (*JetStreamConfig) ProtoMessage() {}
func (*JetStreamConfig) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{34}
+ return fileDescriptor_9d0d1b17d3865563, []int{35}
}
func (m *JetStreamConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1031,7 +1059,7 @@ var xxx_messageInfo_JetStreamConfig proto.InternalMessageInfo
func (m *JetStreamSource) Reset() { *m = JetStreamSource{} }
func (*JetStreamSource) ProtoMessage() {}
func (*JetStreamSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{35}
+ return fileDescriptor_9d0d1b17d3865563, []int{36}
}
func (m *JetStreamSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1059,7 +1087,7 @@ var xxx_messageInfo_JetStreamSource proto.InternalMessageInfo
func (m *JobTemplate) Reset() { *m = JobTemplate{} }
func (*JobTemplate) ProtoMessage() {}
func (*JobTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{36}
+ return fileDescriptor_9d0d1b17d3865563, []int{37}
}
func (m *JobTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1087,7 +1115,7 @@ var xxx_messageInfo_JobTemplate proto.InternalMessageInfo
func (m *KafkaSink) Reset() { *m = KafkaSink{} }
func (*KafkaSink) ProtoMessage() {}
func (*KafkaSink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{37}
+ return fileDescriptor_9d0d1b17d3865563, []int{38}
}
func (m *KafkaSink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1115,7 +1143,7 @@ var xxx_messageInfo_KafkaSink proto.InternalMessageInfo
func (m *KafkaSource) Reset() { *m = KafkaSource{} }
func (*KafkaSource) ProtoMessage() {}
func (*KafkaSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{38}
+ return fileDescriptor_9d0d1b17d3865563, []int{39}
}
func (m *KafkaSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1143,7 +1171,7 @@ var xxx_messageInfo_KafkaSource proto.InternalMessageInfo
func (m *Lifecycle) Reset() { *m = Lifecycle{} }
func (*Lifecycle) ProtoMessage() {}
func (*Lifecycle) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{39}
+ return fileDescriptor_9d0d1b17d3865563, []int{40}
}
func (m *Lifecycle) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1171,7 +1199,7 @@ var xxx_messageInfo_Lifecycle proto.InternalMessageInfo
func (m *Log) Reset() { *m = Log{} }
func (*Log) ProtoMessage() {}
func (*Log) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{40}
+ return fileDescriptor_9d0d1b17d3865563, []int{41}
}
func (m *Log) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1199,7 +1227,7 @@ var xxx_messageInfo_Log proto.InternalMessageInfo
func (m *Metadata) Reset() { *m = Metadata{} }
func (*Metadata) ProtoMessage() {}
func (*Metadata) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{41}
+ return fileDescriptor_9d0d1b17d3865563, []int{42}
}
func (m *Metadata) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1227,7 +1255,7 @@ var xxx_messageInfo_Metadata proto.InternalMessageInfo
func (m *MonoVertex) Reset() { *m = MonoVertex{} }
func (*MonoVertex) ProtoMessage() {}
func (*MonoVertex) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{42}
+ return fileDescriptor_9d0d1b17d3865563, []int{43}
}
func (m *MonoVertex) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1255,7 +1283,7 @@ var xxx_messageInfo_MonoVertex proto.InternalMessageInfo
func (m *MonoVertexLimits) Reset() { *m = MonoVertexLimits{} }
func (*MonoVertexLimits) ProtoMessage() {}
func (*MonoVertexLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{43}
+ return fileDescriptor_9d0d1b17d3865563, []int{44}
}
func (m *MonoVertexLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1283,7 +1311,7 @@ var xxx_messageInfo_MonoVertexLimits proto.InternalMessageInfo
func (m *MonoVertexList) Reset() { *m = MonoVertexList{} }
func (*MonoVertexList) ProtoMessage() {}
func (*MonoVertexList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{44}
+ return fileDescriptor_9d0d1b17d3865563, []int{45}
}
func (m *MonoVertexList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1311,7 +1339,7 @@ var xxx_messageInfo_MonoVertexList proto.InternalMessageInfo
func (m *MonoVertexSpec) Reset() { *m = MonoVertexSpec{} }
func (*MonoVertexSpec) ProtoMessage() {}
func (*MonoVertexSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{45}
+ return fileDescriptor_9d0d1b17d3865563, []int{46}
}
func (m *MonoVertexSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1339,7 +1367,7 @@ var xxx_messageInfo_MonoVertexSpec proto.InternalMessageInfo
func (m *MonoVertexStatus) Reset() { *m = MonoVertexStatus{} }
func (*MonoVertexStatus) ProtoMessage() {}
func (*MonoVertexStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{46}
+ return fileDescriptor_9d0d1b17d3865563, []int{47}
}
func (m *MonoVertexStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1367,7 +1395,7 @@ var xxx_messageInfo_MonoVertexStatus proto.InternalMessageInfo
func (m *NativeRedis) Reset() { *m = NativeRedis{} }
func (*NativeRedis) ProtoMessage() {}
func (*NativeRedis) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{47}
+ return fileDescriptor_9d0d1b17d3865563, []int{48}
}
func (m *NativeRedis) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1395,7 +1423,7 @@ var xxx_messageInfo_NativeRedis proto.InternalMessageInfo
func (m *NatsAuth) Reset() { *m = NatsAuth{} }
func (*NatsAuth) ProtoMessage() {}
func (*NatsAuth) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{48}
+ return fileDescriptor_9d0d1b17d3865563, []int{49}
}
func (m *NatsAuth) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1423,7 +1451,7 @@ var xxx_messageInfo_NatsAuth proto.InternalMessageInfo
func (m *NatsSource) Reset() { *m = NatsSource{} }
func (*NatsSource) ProtoMessage() {}
func (*NatsSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{49}
+ return fileDescriptor_9d0d1b17d3865563, []int{50}
}
func (m *NatsSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1451,7 +1479,7 @@ var xxx_messageInfo_NatsSource proto.InternalMessageInfo
func (m *NoStore) Reset() { *m = NoStore{} }
func (*NoStore) ProtoMessage() {}
func (*NoStore) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{50}
+ return fileDescriptor_9d0d1b17d3865563, []int{51}
}
func (m *NoStore) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1479,7 +1507,7 @@ var xxx_messageInfo_NoStore proto.InternalMessageInfo
func (m *PBQStorage) Reset() { *m = PBQStorage{} }
func (*PBQStorage) ProtoMessage() {}
func (*PBQStorage) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{51}
+ return fileDescriptor_9d0d1b17d3865563, []int{52}
}
func (m *PBQStorage) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1507,7 +1535,7 @@ var xxx_messageInfo_PBQStorage proto.InternalMessageInfo
func (m *PersistenceStrategy) Reset() { *m = PersistenceStrategy{} }
func (*PersistenceStrategy) ProtoMessage() {}
func (*PersistenceStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{52}
+ return fileDescriptor_9d0d1b17d3865563, []int{53}
}
func (m *PersistenceStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1535,7 +1563,7 @@ var xxx_messageInfo_PersistenceStrategy proto.InternalMessageInfo
func (m *Pipeline) Reset() { *m = Pipeline{} }
func (*Pipeline) ProtoMessage() {}
func (*Pipeline) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{53}
+ return fileDescriptor_9d0d1b17d3865563, []int{54}
}
func (m *Pipeline) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1563,7 +1591,7 @@ var xxx_messageInfo_Pipeline proto.InternalMessageInfo
func (m *PipelineLimits) Reset() { *m = PipelineLimits{} }
func (*PipelineLimits) ProtoMessage() {}
func (*PipelineLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{54}
+ return fileDescriptor_9d0d1b17d3865563, []int{55}
}
func (m *PipelineLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1591,7 +1619,7 @@ var xxx_messageInfo_PipelineLimits proto.InternalMessageInfo
func (m *PipelineList) Reset() { *m = PipelineList{} }
func (*PipelineList) ProtoMessage() {}
func (*PipelineList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{55}
+ return fileDescriptor_9d0d1b17d3865563, []int{56}
}
func (m *PipelineList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1619,7 +1647,7 @@ var xxx_messageInfo_PipelineList proto.InternalMessageInfo
func (m *PipelineSpec) Reset() { *m = PipelineSpec{} }
func (*PipelineSpec) ProtoMessage() {}
func (*PipelineSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{56}
+ return fileDescriptor_9d0d1b17d3865563, []int{57}
}
func (m *PipelineSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1647,7 +1675,7 @@ var xxx_messageInfo_PipelineSpec proto.InternalMessageInfo
func (m *PipelineStatus) Reset() { *m = PipelineStatus{} }
func (*PipelineStatus) ProtoMessage() {}
func (*PipelineStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{57}
+ return fileDescriptor_9d0d1b17d3865563, []int{58}
}
func (m *PipelineStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1675,7 +1703,7 @@ var xxx_messageInfo_PipelineStatus proto.InternalMessageInfo
func (m *RedisBufferService) Reset() { *m = RedisBufferService{} }
func (*RedisBufferService) ProtoMessage() {}
func (*RedisBufferService) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{58}
+ return fileDescriptor_9d0d1b17d3865563, []int{59}
}
func (m *RedisBufferService) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1703,7 +1731,7 @@ var xxx_messageInfo_RedisBufferService proto.InternalMessageInfo
func (m *RedisConfig) Reset() { *m = RedisConfig{} }
func (*RedisConfig) ProtoMessage() {}
func (*RedisConfig) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{59}
+ return fileDescriptor_9d0d1b17d3865563, []int{60}
}
func (m *RedisConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1731,7 +1759,7 @@ var xxx_messageInfo_RedisConfig proto.InternalMessageInfo
func (m *RedisSettings) Reset() { *m = RedisSettings{} }
func (*RedisSettings) ProtoMessage() {}
func (*RedisSettings) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{60}
+ return fileDescriptor_9d0d1b17d3865563, []int{61}
}
func (m *RedisSettings) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1756,10 +1784,38 @@ func (m *RedisSettings) XXX_DiscardUnknown() {
var xxx_messageInfo_RedisSettings proto.InternalMessageInfo
+func (m *RetryStrategy) Reset() { *m = RetryStrategy{} }
+func (*RetryStrategy) ProtoMessage() {}
+func (*RetryStrategy) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0d1b17d3865563, []int{62}
+}
+func (m *RetryStrategy) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *RetryStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+}
+func (m *RetryStrategy) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_RetryStrategy.Merge(m, src)
+}
+func (m *RetryStrategy) XXX_Size() int {
+ return m.Size()
+}
+func (m *RetryStrategy) XXX_DiscardUnknown() {
+ xxx_messageInfo_RetryStrategy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RetryStrategy proto.InternalMessageInfo
+
func (m *SASL) Reset() { *m = SASL{} }
func (*SASL) ProtoMessage() {}
func (*SASL) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{61}
+ return fileDescriptor_9d0d1b17d3865563, []int{63}
}
func (m *SASL) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1787,7 +1843,7 @@ var xxx_messageInfo_SASL proto.InternalMessageInfo
func (m *SASLPlain) Reset() { *m = SASLPlain{} }
func (*SASLPlain) ProtoMessage() {}
func (*SASLPlain) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{62}
+ return fileDescriptor_9d0d1b17d3865563, []int{64}
}
func (m *SASLPlain) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1815,7 +1871,7 @@ var xxx_messageInfo_SASLPlain proto.InternalMessageInfo
func (m *Scale) Reset() { *m = Scale{} }
func (*Scale) ProtoMessage() {}
func (*Scale) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{63}
+ return fileDescriptor_9d0d1b17d3865563, []int{65}
}
func (m *Scale) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1843,7 +1899,7 @@ var xxx_messageInfo_Scale proto.InternalMessageInfo
func (m *ServingSource) Reset() { *m = ServingSource{} }
func (*ServingSource) ProtoMessage() {}
func (*ServingSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{64}
+ return fileDescriptor_9d0d1b17d3865563, []int{66}
}
func (m *ServingSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1871,7 +1927,7 @@ var xxx_messageInfo_ServingSource proto.InternalMessageInfo
func (m *ServingStore) Reset() { *m = ServingStore{} }
func (*ServingStore) ProtoMessage() {}
func (*ServingStore) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{65}
+ return fileDescriptor_9d0d1b17d3865563, []int{67}
}
func (m *ServingStore) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1899,7 +1955,7 @@ var xxx_messageInfo_ServingStore proto.InternalMessageInfo
func (m *SessionWindow) Reset() { *m = SessionWindow{} }
func (*SessionWindow) ProtoMessage() {}
func (*SessionWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{66}
+ return fileDescriptor_9d0d1b17d3865563, []int{68}
}
func (m *SessionWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1927,7 +1983,7 @@ var xxx_messageInfo_SessionWindow proto.InternalMessageInfo
func (m *SideInput) Reset() { *m = SideInput{} }
func (*SideInput) ProtoMessage() {}
func (*SideInput) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{67}
+ return fileDescriptor_9d0d1b17d3865563, []int{69}
}
func (m *SideInput) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1955,7 +2011,7 @@ var xxx_messageInfo_SideInput proto.InternalMessageInfo
func (m *SideInputTrigger) Reset() { *m = SideInputTrigger{} }
func (*SideInputTrigger) ProtoMessage() {}
func (*SideInputTrigger) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{68}
+ return fileDescriptor_9d0d1b17d3865563, []int{70}
}
func (m *SideInputTrigger) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1983,7 +2039,7 @@ var xxx_messageInfo_SideInputTrigger proto.InternalMessageInfo
func (m *SideInputsManagerTemplate) Reset() { *m = SideInputsManagerTemplate{} }
func (*SideInputsManagerTemplate) ProtoMessage() {}
func (*SideInputsManagerTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{69}
+ return fileDescriptor_9d0d1b17d3865563, []int{71}
}
func (m *SideInputsManagerTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2011,7 +2067,7 @@ var xxx_messageInfo_SideInputsManagerTemplate proto.InternalMessageInfo
func (m *Sink) Reset() { *m = Sink{} }
func (*Sink) ProtoMessage() {}
func (*Sink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{70}
+ return fileDescriptor_9d0d1b17d3865563, []int{72}
}
func (m *Sink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2039,7 +2095,7 @@ var xxx_messageInfo_Sink proto.InternalMessageInfo
func (m *SlidingWindow) Reset() { *m = SlidingWindow{} }
func (*SlidingWindow) ProtoMessage() {}
func (*SlidingWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{71}
+ return fileDescriptor_9d0d1b17d3865563, []int{73}
}
func (m *SlidingWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2067,7 +2123,7 @@ var xxx_messageInfo_SlidingWindow proto.InternalMessageInfo
func (m *Source) Reset() { *m = Source{} }
func (*Source) ProtoMessage() {}
func (*Source) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{72}
+ return fileDescriptor_9d0d1b17d3865563, []int{74}
}
func (m *Source) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2095,7 +2151,7 @@ var xxx_messageInfo_Source proto.InternalMessageInfo
func (m *Status) Reset() { *m = Status{} }
func (*Status) ProtoMessage() {}
func (*Status) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{73}
+ return fileDescriptor_9d0d1b17d3865563, []int{75}
}
func (m *Status) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2123,7 +2179,7 @@ var xxx_messageInfo_Status proto.InternalMessageInfo
func (m *TLS) Reset() { *m = TLS{} }
func (*TLS) ProtoMessage() {}
func (*TLS) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{74}
+ return fileDescriptor_9d0d1b17d3865563, []int{76}
}
func (m *TLS) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2151,7 +2207,7 @@ var xxx_messageInfo_TLS proto.InternalMessageInfo
func (m *TagConditions) Reset() { *m = TagConditions{} }
func (*TagConditions) ProtoMessage() {}
func (*TagConditions) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{75}
+ return fileDescriptor_9d0d1b17d3865563, []int{77}
}
func (m *TagConditions) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2179,7 +2235,7 @@ var xxx_messageInfo_TagConditions proto.InternalMessageInfo
func (m *Templates) Reset() { *m = Templates{} }
func (*Templates) ProtoMessage() {}
func (*Templates) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{76}
+ return fileDescriptor_9d0d1b17d3865563, []int{78}
}
func (m *Templates) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2207,7 +2263,7 @@ var xxx_messageInfo_Templates proto.InternalMessageInfo
func (m *Transformer) Reset() { *m = Transformer{} }
func (*Transformer) ProtoMessage() {}
func (*Transformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{77}
+ return fileDescriptor_9d0d1b17d3865563, []int{79}
}
func (m *Transformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2235,7 +2291,7 @@ var xxx_messageInfo_Transformer proto.InternalMessageInfo
func (m *UDF) Reset() { *m = UDF{} }
func (*UDF) ProtoMessage() {}
func (*UDF) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{78}
+ return fileDescriptor_9d0d1b17d3865563, []int{80}
}
func (m *UDF) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2263,7 +2319,7 @@ var xxx_messageInfo_UDF proto.InternalMessageInfo
func (m *UDSink) Reset() { *m = UDSink{} }
func (*UDSink) ProtoMessage() {}
func (*UDSink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{79}
+ return fileDescriptor_9d0d1b17d3865563, []int{81}
}
func (m *UDSink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2291,7 +2347,7 @@ var xxx_messageInfo_UDSink proto.InternalMessageInfo
func (m *UDSource) Reset() { *m = UDSource{} }
func (*UDSource) ProtoMessage() {}
func (*UDSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{80}
+ return fileDescriptor_9d0d1b17d3865563, []int{82}
}
func (m *UDSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2319,7 +2375,7 @@ var xxx_messageInfo_UDSource proto.InternalMessageInfo
func (m *UDTransformer) Reset() { *m = UDTransformer{} }
func (*UDTransformer) ProtoMessage() {}
func (*UDTransformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{81}
+ return fileDescriptor_9d0d1b17d3865563, []int{83}
}
func (m *UDTransformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2347,7 +2403,7 @@ var xxx_messageInfo_UDTransformer proto.InternalMessageInfo
func (m *Vertex) Reset() { *m = Vertex{} }
func (*Vertex) ProtoMessage() {}
func (*Vertex) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{82}
+ return fileDescriptor_9d0d1b17d3865563, []int{84}
}
func (m *Vertex) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2375,7 +2431,7 @@ var xxx_messageInfo_Vertex proto.InternalMessageInfo
func (m *VertexInstance) Reset() { *m = VertexInstance{} }
func (*VertexInstance) ProtoMessage() {}
func (*VertexInstance) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{83}
+ return fileDescriptor_9d0d1b17d3865563, []int{85}
}
func (m *VertexInstance) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2403,7 +2459,7 @@ var xxx_messageInfo_VertexInstance proto.InternalMessageInfo
func (m *VertexLimits) Reset() { *m = VertexLimits{} }
func (*VertexLimits) ProtoMessage() {}
func (*VertexLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{84}
+ return fileDescriptor_9d0d1b17d3865563, []int{86}
}
func (m *VertexLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2431,7 +2487,7 @@ var xxx_messageInfo_VertexLimits proto.InternalMessageInfo
func (m *VertexList) Reset() { *m = VertexList{} }
func (*VertexList) ProtoMessage() {}
func (*VertexList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{85}
+ return fileDescriptor_9d0d1b17d3865563, []int{87}
}
func (m *VertexList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2459,7 +2515,7 @@ var xxx_messageInfo_VertexList proto.InternalMessageInfo
func (m *VertexSpec) Reset() { *m = VertexSpec{} }
func (*VertexSpec) ProtoMessage() {}
func (*VertexSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{86}
+ return fileDescriptor_9d0d1b17d3865563, []int{88}
}
func (m *VertexSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2487,7 +2543,7 @@ var xxx_messageInfo_VertexSpec proto.InternalMessageInfo
func (m *VertexStatus) Reset() { *m = VertexStatus{} }
func (*VertexStatus) ProtoMessage() {}
func (*VertexStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{87}
+ return fileDescriptor_9d0d1b17d3865563, []int{89}
}
func (m *VertexStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2515,7 +2571,7 @@ var xxx_messageInfo_VertexStatus proto.InternalMessageInfo
func (m *VertexTemplate) Reset() { *m = VertexTemplate{} }
func (*VertexTemplate) ProtoMessage() {}
func (*VertexTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{88}
+ return fileDescriptor_9d0d1b17d3865563, []int{90}
}
func (m *VertexTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2543,7 +2599,7 @@ var xxx_messageInfo_VertexTemplate proto.InternalMessageInfo
func (m *Watermark) Reset() { *m = Watermark{} }
func (*Watermark) ProtoMessage() {}
func (*Watermark) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{89}
+ return fileDescriptor_9d0d1b17d3865563, []int{91}
}
func (m *Watermark) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2571,7 +2627,7 @@ var xxx_messageInfo_Watermark proto.InternalMessageInfo
func (m *Window) Reset() { *m = Window{} }
func (*Window) ProtoMessage() {}
func (*Window) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{90}
+ return fileDescriptor_9d0d1b17d3865563, []int{92}
}
func (m *Window) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2602,6 +2658,7 @@ func init() {
proto.RegisterType((*AbstractSink)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.AbstractSink")
proto.RegisterType((*AbstractVertex)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.AbstractVertex")
proto.RegisterType((*Authorization)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Authorization")
+ proto.RegisterType((*Backoff)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Backoff")
proto.RegisterType((*BasicAuth)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.BasicAuth")
proto.RegisterType((*Blackhole)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Blackhole")
proto.RegisterType((*BufferServiceConfig)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.BufferServiceConfig")
@@ -2666,6 +2723,7 @@ func init() {
proto.RegisterType((*RedisBufferService)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisBufferService")
proto.RegisterType((*RedisConfig)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisConfig")
proto.RegisterType((*RedisSettings)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisSettings")
+ proto.RegisterType((*RetryStrategy)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RetryStrategy")
proto.RegisterType((*SASL)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.SASL")
proto.RegisterType((*SASLPlain)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.SASLPlain")
proto.RegisterType((*Scale)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Scale")
@@ -2704,475 +2762,483 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7488 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x1d, 0xd7,
- 0x79, 0xae, 0xf7, 0x8d, 0xdc, 0xfb, 0xdf, 0x24, 0x45, 0x2d, 0xc9, 0x32, 0x25, 0xcb, 0xa2, 0x32,
- 0x8e, 0x7d, 0x94, 0x93, 0x84, 0x3c, 0xe6, 0xf1, 0x2d, 0x57, 0x9b, 0x9b, 0x14, 0x29, 0x4a, 0xa4,
- 0xc4, 0xfc, 0x9b, 0x94, 0x9d, 0xf8, 0x24, 0x3e, 0xc3, 0x99, 0xc5, 0xcd, 0x31, 0x67, 0xcf, 0x6c,
- 0xcf, 0xcc, 0xa6, 0x44, 0xe7, 0x1c, 0xe4, 0xf6, 0x60, 0x1f, 0x1c, 0x1c, 0x9c, 0x22, 0x4f, 0x01,
- 0x8a, 0xb4, 0x68, 0x51, 0x20, 0x0f, 0x41, 0xfa, 0x50, 0xd4, 0x7d, 0x28, 0xd0, 0x4b, 0x8a, 0xa2,
- 0x4d, 0x8a, 0x5e, 0xf2, 0x50, 0xa0, 0xee, 0x0b, 0xd1, 0xb0, 0xe8, 0x43, 0x0b, 0x34, 0x08, 0x1a,
- 0xa0, 0x4d, 0x84, 0x00, 0x29, 0xd6, 0x6d, 0x6e, 0x7b, 0xb6, 0x44, 0xee, 0x21, 0x65, 0xb9, 0xf5,
- 0xdb, 0xcc, 0x5a, 0xff, 0xfa, 0xfe, 0x35, 0xff, 0xba, 0xfd, 0xeb, 0x5f, 0xff, 0xfa, 0x07, 0x16,
- 0x5b, 0x56, 0xb0, 0xd5, 0xdd, 0x98, 0x32, 0xdc, 0xf6, 0xb4, 0xd3, 0x6d, 0xeb, 0x1d, 0xcf, 0x7d,
- 0x8d, 0x3f, 0x6c, 0xda, 0xee, 0xad, 0xe9, 0xce, 0x76, 0x6b, 0x5a, 0xef, 0x58, 0x7e, 0x94, 0xb2,
- 0xf3, 0x94, 0x6e, 0x77, 0xb6, 0xf4, 0xa7, 0xa6, 0x5b, 0xd4, 0xa1, 0x9e, 0x1e, 0x50, 0x73, 0xaa,
- 0xe3, 0xb9, 0x81, 0x4b, 0x9e, 0x8b, 0x80, 0xa6, 0x14, 0xd0, 0x94, 0x2a, 0x36, 0xd5, 0xd9, 0x6e,
- 0x4d, 0x31, 0xa0, 0x28, 0x45, 0x01, 0x9d, 0xfb, 0x68, 0xac, 0x06, 0x2d, 0xb7, 0xe5, 0x4e, 0x73,
- 0xbc, 0x8d, 0xee, 0x26, 0x7f, 0xe3, 0x2f, 0xfc, 0x49, 0xf0, 0x39, 0xa7, 0x6d, 0x3f, 0xef, 0x4f,
- 0x59, 0x2e, 0xab, 0xd6, 0xb4, 0xe1, 0x7a, 0x74, 0x7a, 0xa7, 0xa7, 0x2e, 0xe7, 0x9e, 0x8e, 0x68,
- 0xda, 0xba, 0xb1, 0x65, 0x39, 0xd4, 0xdb, 0x55, 0xdf, 0x32, 0xed, 0x51, 0xdf, 0xed, 0x7a, 0x06,
- 0x3d, 0x54, 0x29, 0x7f, 0xba, 0x4d, 0x03, 0x3d, 0x8b, 0xd7, 0x74, 0xbf, 0x52, 0x5e, 0xd7, 0x09,
- 0xac, 0x76, 0x2f, 0x9b, 0x67, 0xef, 0x55, 0xc0, 0x37, 0xb6, 0x68, 0x5b, 0x4f, 0x97, 0xd3, 0xbe,
- 0x0b, 0x70, 0x6a, 0x76, 0xc3, 0x0f, 0x3c, 0xdd, 0x08, 0x56, 0x5d, 0x73, 0x8d, 0xb6, 0x3b, 0xb6,
- 0x1e, 0x50, 0xb2, 0x0d, 0x55, 0x56, 0x37, 0x53, 0x0f, 0xf4, 0x89, 0xc2, 0xc5, 0xc2, 0xa5, 0xfa,
- 0xcc, 0xec, 0xd4, 0x80, 0x6d, 0x31, 0xb5, 0x22, 0x81, 0x1a, 0x23, 0xfb, 0x7b, 0x93, 0x55, 0xf5,
- 0x86, 0x21, 0x03, 0xf2, 0x8d, 0x02, 0x8c, 0x38, 0xae, 0x49, 0x9b, 0xd4, 0xa6, 0x46, 0xe0, 0x7a,
- 0x13, 0xc5, 0x8b, 0xa5, 0x4b, 0xf5, 0x99, 0x2f, 0x0c, 0xcc, 0x31, 0xe3, 0x8b, 0xa6, 0xae, 0xc7,
- 0x18, 0x5c, 0x76, 0x02, 0x6f, 0xb7, 0x71, 0xfa, 0x7b, 0x7b, 0x93, 0x0f, 0xed, 0xef, 0x4d, 0x8e,
- 0xc4, 0xb3, 0x30, 0x51, 0x13, 0xb2, 0x0e, 0xf5, 0xc0, 0xb5, 0x99, 0xc8, 0x2c, 0xd7, 0xf1, 0x27,
- 0x4a, 0xbc, 0x62, 0x17, 0xa6, 0x84, 0xb4, 0x19, 0xfb, 0x29, 0xd6, 0x5d, 0xa6, 0x76, 0x9e, 0x9a,
- 0x5a, 0x0b, 0xc9, 0x1a, 0xa7, 0x24, 0x70, 0x3d, 0x4a, 0xf3, 0x31, 0x8e, 0x43, 0x28, 0x9c, 0xf0,
- 0xa9, 0xd1, 0xf5, 0xac, 0x60, 0x77, 0xce, 0x75, 0x02, 0x7a, 0x3b, 0x98, 0x28, 0x73, 0x29, 0x3f,
- 0x99, 0x05, 0xbd, 0xea, 0x9a, 0xcd, 0x24, 0x75, 0xe3, 0xd4, 0xfe, 0xde, 0xe4, 0x89, 0x54, 0x22,
- 0xa6, 0x31, 0x89, 0x03, 0xe3, 0x56, 0x5b, 0x6f, 0xd1, 0xd5, 0xae, 0x6d, 0x37, 0xa9, 0xe1, 0xd1,
- 0xc0, 0x9f, 0xa8, 0xf0, 0x4f, 0xb8, 0x94, 0xc5, 0x67, 0xd9, 0x35, 0x74, 0xfb, 0xc6, 0xc6, 0x6b,
- 0xd4, 0x08, 0x90, 0x6e, 0x52, 0x8f, 0x3a, 0x06, 0x6d, 0x4c, 0xc8, 0x8f, 0x19, 0x5f, 0x4a, 0x21,
- 0x61, 0x0f, 0x36, 0x59, 0x84, 0x93, 0x1d, 0xcf, 0x72, 0x79, 0x15, 0x6c, 0xdd, 0xf7, 0xaf, 0xeb,
- 0x6d, 0x3a, 0x31, 0x74, 0xb1, 0x70, 0xa9, 0xd6, 0x38, 0x2b, 0x61, 0x4e, 0xae, 0xa6, 0x09, 0xb0,
- 0xb7, 0x0c, 0xb9, 0x04, 0x55, 0x95, 0x38, 0x31, 0x7c, 0xb1, 0x70, 0xa9, 0x22, 0xfa, 0x8e, 0x2a,
- 0x8b, 0x61, 0x2e, 0x59, 0x80, 0xaa, 0xbe, 0xb9, 0x69, 0x39, 0x8c, 0xb2, 0xca, 0x45, 0x78, 0x3e,
- 0xeb, 0xd3, 0x66, 0x25, 0x8d, 0xc0, 0x51, 0x6f, 0x18, 0x96, 0x25, 0x57, 0x81, 0xf8, 0xd4, 0xdb,
- 0xb1, 0x0c, 0x3a, 0x6b, 0x18, 0x6e, 0xd7, 0x09, 0x78, 0xdd, 0x6b, 0xbc, 0xee, 0xe7, 0x64, 0xdd,
- 0x49, 0xb3, 0x87, 0x02, 0x33, 0x4a, 0x91, 0x17, 0x61, 0x5c, 0x0e, 0xbb, 0x48, 0x0a, 0xc0, 0x91,
- 0x4e, 0x33, 0x41, 0x62, 0x2a, 0x0f, 0x7b, 0xa8, 0x89, 0x09, 0xe7, 0xf5, 0x6e, 0xe0, 0xb6, 0x19,
- 0x64, 0x92, 0xe9, 0x9a, 0xbb, 0x4d, 0x9d, 0x89, 0xfa, 0xc5, 0xc2, 0xa5, 0x6a, 0xe3, 0xe2, 0xfe,
- 0xde, 0xe4, 0xf9, 0xd9, 0xbb, 0xd0, 0xe1, 0x5d, 0x51, 0xc8, 0x0d, 0xa8, 0x99, 0x8e, 0xbf, 0xea,
- 0xda, 0x96, 0xb1, 0x3b, 0x31, 0xc2, 0x2b, 0xf8, 0x94, 0xfc, 0xd4, 0xda, 0xfc, 0xf5, 0xa6, 0xc8,
- 0xb8, 0xb3, 0x37, 0x79, 0xbe, 0x77, 0x76, 0x9c, 0x0a, 0xf3, 0x31, 0xc2, 0x20, 0x2b, 0x1c, 0x70,
- 0xce, 0x75, 0x36, 0xad, 0xd6, 0xc4, 0x28, 0x6f, 0x8d, 0x8b, 0x7d, 0x3a, 0xf4, 0xfc, 0xf5, 0xa6,
- 0xa0, 0x6b, 0x8c, 0x4a, 0x76, 0xe2, 0x15, 0x23, 0x04, 0x62, 0xc2, 0x98, 0x9a, 0x57, 0xe7, 0x6c,
- 0xdd, 0x6a, 0xfb, 0x13, 0x63, 0xbc, 0xf3, 0x7e, 0xb0, 0x0f, 0x26, 0xc6, 0x89, 0x1b, 0x67, 0xe4,
- 0xa7, 0x8c, 0x25, 0x92, 0x7d, 0x4c, 0x61, 0x9e, 0x7b, 0x01, 0x4e, 0xf6, 0xcc, 0x0d, 0x64, 0x1c,
- 0x4a, 0xdb, 0x74, 0x97, 0x4f, 0x7d, 0x35, 0x64, 0x8f, 0xe4, 0x34, 0x54, 0x76, 0x74, 0xbb, 0x4b,
- 0x27, 0x8a, 0x3c, 0x4d, 0xbc, 0x7c, 0xbc, 0xf8, 0x7c, 0x41, 0xfb, 0xf5, 0x12, 0x8c, 0xa8, 0x19,
- 0xa7, 0x69, 0x39, 0xdb, 0xe4, 0x25, 0x28, 0xd9, 0x6e, 0x4b, 0xce, 0x9b, 0x9f, 0x1c, 0x78, 0x16,
- 0x5b, 0x76, 0x5b, 0x8d, 0xe1, 0xfd, 0xbd, 0xc9, 0xd2, 0xb2, 0xdb, 0x42, 0x86, 0x48, 0x0c, 0xa8,
- 0x6c, 0xeb, 0x9b, 0xdb, 0x3a, 0xaf, 0x43, 0x7d, 0xa6, 0x31, 0x30, 0xf4, 0x35, 0x86, 0xc2, 0xea,
- 0xda, 0xa8, 0xed, 0xef, 0x4d, 0x56, 0xf8, 0x2b, 0x0a, 0x6c, 0xe2, 0x42, 0x6d, 0xc3, 0xd6, 0x8d,
- 0xed, 0x2d, 0xd7, 0xa6, 0x13, 0xa5, 0x9c, 0x8c, 0x1a, 0x0a, 0x49, 0x34, 0x73, 0xf8, 0x8a, 0x11,
- 0x0f, 0x62, 0xc0, 0x50, 0xd7, 0xf4, 0x2d, 0x67, 0x5b, 0xce, 0x81, 0x2f, 0x0c, 0xcc, 0x6d, 0x7d,
- 0x9e, 0x7f, 0x13, 0xec, 0xef, 0x4d, 0x0e, 0x89, 0x67, 0x94, 0xd0, 0xda, 0x8f, 0xea, 0x30, 0xa6,
- 0x1a, 0xe9, 0x26, 0xf5, 0x02, 0x7a, 0x9b, 0x5c, 0x84, 0xb2, 0xc3, 0x86, 0x26, 0x6f, 0xe4, 0xc6,
- 0x88, 0xec, 0x2e, 0x65, 0x3e, 0x24, 0x79, 0x0e, 0xab, 0x99, 0xe8, 0x2a, 0x52, 0xe0, 0x83, 0xd7,
- 0xac, 0xc9, 0x61, 0x44, 0xcd, 0xc4, 0x33, 0x4a, 0x68, 0xf2, 0x0a, 0x94, 0xf9, 0xc7, 0x0b, 0x51,
- 0x7f, 0x6a, 0x70, 0x16, 0xec, 0xd3, 0xab, 0xec, 0x0b, 0xf8, 0x87, 0x73, 0x50, 0xd6, 0x15, 0xbb,
- 0xe6, 0xa6, 0x14, 0xec, 0x27, 0x73, 0x08, 0x76, 0x41, 0x74, 0xc5, 0xf5, 0xf9, 0x05, 0x64, 0x88,
- 0xe4, 0xff, 0x17, 0xe0, 0xa4, 0xe1, 0x3a, 0x81, 0xce, 0x54, 0x0d, 0xb5, 0xc8, 0x4e, 0x54, 0x38,
- 0x9f, 0xab, 0x03, 0xf3, 0x99, 0x4b, 0x23, 0x36, 0x1e, 0x66, 0x6b, 0x46, 0x4f, 0x32, 0xf6, 0xf2,
- 0x26, 0xbf, 0x5c, 0x80, 0x87, 0xd9, 0x5c, 0xde, 0x43, 0xcc, 0x57, 0xa0, 0xa3, 0xad, 0xd5, 0xd9,
- 0xfd, 0xbd, 0xc9, 0x87, 0x97, 0xb2, 0x98, 0x61, 0x76, 0x1d, 0x58, 0xed, 0x4e, 0xe9, 0xbd, 0x6a,
- 0x09, 0x5f, 0xdd, 0xea, 0x33, 0xcb, 0x47, 0xa9, 0xea, 0x34, 0x1e, 0x95, 0x5d, 0x39, 0x4b, 0xb3,
- 0xc3, 0xac, 0x5a, 0x90, 0xcb, 0x30, 0xbc, 0xe3, 0xda, 0xdd, 0x36, 0xf5, 0x27, 0xaa, 0x7c, 0x8a,
- 0x3d, 0x97, 0x35, 0xc5, 0xde, 0xe4, 0x24, 0x8d, 0x13, 0x12, 0x7e, 0x58, 0xbc, 0xfb, 0xa8, 0xca,
- 0x12, 0x0b, 0x86, 0x6c, 0xab, 0x6d, 0x05, 0x3e, 0x5f, 0x38, 0xeb, 0x33, 0x97, 0x07, 0xfe, 0x2c,
- 0x31, 0x44, 0x97, 0x39, 0x98, 0x18, 0x35, 0xe2, 0x19, 0x25, 0x03, 0x36, 0x15, 0xfa, 0x86, 0x6e,
- 0x8b, 0x85, 0xb5, 0x3e, 0xf3, 0xe9, 0xc1, 0x87, 0x0d, 0x43, 0x69, 0x8c, 0xca, 0x6f, 0xaa, 0xf0,
- 0x57, 0x14, 0xd8, 0xe4, 0xf3, 0x30, 0x96, 0x68, 0x4d, 0x7f, 0xa2, 0xce, 0xa5, 0xf3, 0x58, 0x96,
- 0x74, 0x42, 0xaa, 0x68, 0xe5, 0x49, 0xf4, 0x10, 0x1f, 0x53, 0x60, 0xe4, 0x1a, 0x54, 0x7d, 0xcb,
- 0xa4, 0x86, 0xee, 0xf9, 0x13, 0x23, 0x07, 0x01, 0x1e, 0x97, 0xc0, 0xd5, 0xa6, 0x2c, 0x86, 0x21,
- 0x00, 0x99, 0x02, 0xe8, 0xe8, 0x5e, 0x60, 0x09, 0x45, 0x75, 0x94, 0x2b, 0x4d, 0x63, 0xfb, 0x7b,
- 0x93, 0xb0, 0x1a, 0xa6, 0x62, 0x8c, 0x82, 0xd1, 0xb3, 0xb2, 0x4b, 0x4e, 0xa7, 0x1b, 0x88, 0x85,
- 0xb5, 0x26, 0xe8, 0x9b, 0x61, 0x2a, 0xc6, 0x28, 0xc8, 0x77, 0x0a, 0xf0, 0x68, 0xf4, 0xda, 0x3b,
- 0xc8, 0x4e, 0x1c, 0xf9, 0x20, 0x9b, 0xdc, 0xdf, 0x9b, 0x7c, 0xb4, 0xd9, 0x9f, 0x25, 0xde, 0xad,
- 0x3e, 0xda, 0x4b, 0x30, 0x3a, 0xdb, 0x0d, 0xb6, 0x5c, 0xcf, 0x7a, 0x83, 0x2b, 0xdd, 0x64, 0x01,
- 0x2a, 0x01, 0x57, 0x9e, 0xc4, 0xba, 0xfc, 0x44, 0x96, 0xa8, 0x85, 0x22, 0x7b, 0x8d, 0xee, 0x2a,
- 0x6d, 0x40, 0xac, 0x8f, 0x42, 0x99, 0x12, 0xc5, 0xb5, 0x5f, 0x2b, 0x40, 0xad, 0xa1, 0xfb, 0x96,
- 0xc1, 0xe0, 0xc9, 0x1c, 0x94, 0xbb, 0x3e, 0xf5, 0x0e, 0x07, 0xca, 0x67, 0xe9, 0x75, 0x9f, 0x7a,
- 0xc8, 0x0b, 0x93, 0x1b, 0x50, 0xed, 0xe8, 0xbe, 0x7f, 0xcb, 0xf5, 0x4c, 0xb9, 0xd2, 0x1c, 0x10,
- 0x48, 0x68, 0xc5, 0xb2, 0x28, 0x86, 0x20, 0x5a, 0x1d, 0xa2, 0xa5, 0x56, 0xfb, 0x49, 0x01, 0x4e,
- 0x35, 0xba, 0x9b, 0x9b, 0xd4, 0x93, 0x4a, 0xa0, 0x54, 0xaf, 0x28, 0x54, 0x3c, 0x6a, 0x5a, 0xbe,
- 0xac, 0xfb, 0xfc, 0xc0, 0x4d, 0x87, 0x0c, 0x45, 0x6a, 0x73, 0x5c, 0x5e, 0x3c, 0x01, 0x05, 0x3a,
- 0xe9, 0x42, 0xed, 0x35, 0x1a, 0xf8, 0x81, 0x47, 0xf5, 0xb6, 0xfc, 0xba, 0x2b, 0x03, 0xb3, 0xba,
- 0x4a, 0x83, 0x26, 0x47, 0x8a, 0x2b, 0x8f, 0x61, 0x22, 0x46, 0x9c, 0xb4, 0xef, 0x56, 0x60, 0x64,
- 0xce, 0x6d, 0x6f, 0x58, 0x0e, 0x35, 0x2f, 0x9b, 0x2d, 0x4a, 0x5e, 0x85, 0x32, 0x35, 0x5b, 0x54,
- 0x7e, 0xed, 0xe0, 0xeb, 0x2c, 0x03, 0x8b, 0xb4, 0x05, 0xf6, 0x86, 0x1c, 0x98, 0x2c, 0xc3, 0xd8,
- 0xa6, 0xe7, 0xb6, 0xc5, 0xd4, 0xb5, 0xb6, 0xdb, 0x91, 0xaa, 0x62, 0xe3, 0x83, 0x6a, 0x3a, 0x58,
- 0x48, 0xe4, 0xde, 0xd9, 0x9b, 0x84, 0xe8, 0x0d, 0x53, 0x65, 0xc9, 0xcb, 0x30, 0x11, 0xa5, 0x84,
- 0x63, 0x78, 0x8e, 0x69, 0xef, 0x5c, 0x55, 0xa8, 0x34, 0xce, 0xef, 0xef, 0x4d, 0x4e, 0x2c, 0xf4,
- 0xa1, 0xc1, 0xbe, 0xa5, 0xc9, 0x9b, 0x05, 0x18, 0x8f, 0x32, 0xc5, 0xbc, 0x2a, 0x35, 0x84, 0x23,
- 0x9a, 0xb0, 0xf9, 0x36, 0x67, 0x21, 0xc5, 0x02, 0x7b, 0x98, 0x92, 0x05, 0x18, 0x09, 0xdc, 0x98,
- 0xbc, 0x2a, 0x5c, 0x5e, 0x9a, 0xda, 0x97, 0xaf, 0xb9, 0x7d, 0xa5, 0x95, 0x28, 0x47, 0x10, 0xce,
- 0xa8, 0xf7, 0x94, 0xa4, 0x86, 0xb8, 0xa4, 0xce, 0xed, 0xef, 0x4d, 0x9e, 0x59, 0xcb, 0xa4, 0xc0,
- 0x3e, 0x25, 0xc9, 0x57, 0x0a, 0x30, 0xa6, 0xb2, 0xa4, 0x8c, 0x86, 0x8f, 0x52, 0x46, 0x84, 0xf5,
- 0x88, 0xb5, 0x04, 0x03, 0x4c, 0x31, 0xd4, 0x7e, 0x5a, 0x86, 0x5a, 0x38, 0xb3, 0x91, 0xc7, 0xa1,
- 0xc2, 0x77, 0xdc, 0x52, 0x61, 0x0d, 0x97, 0x2c, 0xbe, 0x31, 0x47, 0x91, 0x47, 0x9e, 0x80, 0x61,
- 0xc3, 0x6d, 0xb7, 0x75, 0xc7, 0xe4, 0x56, 0x94, 0x5a, 0xa3, 0xce, 0x56, 0xea, 0x39, 0x91, 0x84,
- 0x2a, 0x8f, 0x9c, 0x87, 0xb2, 0xee, 0xb5, 0x84, 0x41, 0xa3, 0x26, 0xe6, 0xa3, 0x59, 0xaf, 0xe5,
- 0x23, 0x4f, 0x25, 0x1f, 0x83, 0x12, 0x75, 0x76, 0x26, 0xca, 0xfd, 0x55, 0x81, 0xcb, 0xce, 0xce,
- 0x4d, 0xdd, 0x6b, 0xd4, 0x65, 0x1d, 0x4a, 0x97, 0x9d, 0x1d, 0x64, 0x65, 0xc8, 0x32, 0x0c, 0x53,
- 0x67, 0x87, 0xb5, 0xbd, 0xb4, 0x34, 0x7c, 0xa0, 0x4f, 0x71, 0x46, 0x22, 0xb5, 0xe2, 0x50, 0xa1,
- 0x90, 0xc9, 0xa8, 0x20, 0xc8, 0x67, 0x61, 0x44, 0xe8, 0x16, 0x2b, 0xac, 0x4d, 0xfc, 0x89, 0x21,
- 0x0e, 0x39, 0xd9, 0x5f, 0x39, 0xe1, 0x74, 0x91, 0x65, 0x27, 0x96, 0xe8, 0x63, 0x02, 0x8a, 0x7c,
- 0x16, 0x6a, 0x6a, 0x23, 0xa8, 0x5a, 0x36, 0xd3, 0x28, 0xa2, 0x76, 0x8f, 0x48, 0x5f, 0xef, 0x5a,
- 0x1e, 0x6d, 0x53, 0x27, 0xf0, 0x1b, 0x27, 0xd5, 0x36, 0x59, 0xe5, 0xfa, 0x18, 0xa1, 0x91, 0x8d,
- 0x5e, 0xeb, 0x8e, 0x30, 0x4d, 0x3c, 0xde, 0x67, 0x56, 0x1f, 0xc0, 0xb4, 0xf3, 0x05, 0x38, 0x11,
- 0x9a, 0x5f, 0xe4, 0x0e, 0x5e, 0x18, 0x2b, 0x9e, 0x66, 0xc5, 0x97, 0x92, 0x59, 0x77, 0xf6, 0x26,
- 0x1f, 0xcb, 0xd8, 0xc3, 0x47, 0x04, 0x98, 0x06, 0xd3, 0xfe, 0xa0, 0x04, 0xbd, 0x6a, 0x77, 0x52,
- 0x68, 0x85, 0xa3, 0x16, 0x5a, 0xfa, 0x83, 0xc4, 0xf4, 0xf9, 0xbc, 0x2c, 0x96, 0xff, 0xa3, 0xb2,
- 0x1a, 0xa6, 0x74, 0xd4, 0x0d, 0xf3, 0xa0, 0x8c, 0x1d, 0xed, 0xad, 0x32, 0x8c, 0xcd, 0xeb, 0xb4,
- 0xed, 0x3a, 0xf7, 0xdc, 0x84, 0x14, 0x1e, 0x88, 0x4d, 0xc8, 0x25, 0xa8, 0x7a, 0xb4, 0x63, 0x5b,
- 0x86, 0xee, 0xf3, 0xa6, 0x97, 0x46, 0x3f, 0x94, 0x69, 0x18, 0xe6, 0xf6, 0xd9, 0x7c, 0x96, 0x1e,
- 0xc8, 0xcd, 0x67, 0xf9, 0xdd, 0xdf, 0x7c, 0x6a, 0x5f, 0x29, 0x02, 0x57, 0x54, 0xc8, 0x45, 0x28,
- 0xb3, 0x45, 0x38, 0x6d, 0xf2, 0xe0, 0x1d, 0x87, 0xe7, 0x90, 0x73, 0x50, 0x0c, 0x5c, 0x39, 0xf2,
- 0x40, 0xe6, 0x17, 0xd7, 0x5c, 0x2c, 0x06, 0x2e, 0x79, 0x03, 0xc0, 0x70, 0x1d, 0xd3, 0x52, 0xb6,
- 0xf0, 0x7c, 0x1f, 0xb6, 0xe0, 0x7a, 0xb7, 0x74, 0xcf, 0x9c, 0x0b, 0x11, 0xc5, 0xf6, 0x23, 0x7a,
- 0xc7, 0x18, 0x37, 0xf2, 0x02, 0x0c, 0xb9, 0xce, 0x42, 0xd7, 0xb6, 0xb9, 0x40, 0x6b, 0x8d, 0xff,
- 0xc2, 0xf6, 0x84, 0x37, 0x78, 0xca, 0x9d, 0xbd, 0xc9, 0xb3, 0x42, 0xbf, 0x65, 0x6f, 0x2f, 0x79,
- 0x56, 0x60, 0x39, 0xad, 0x66, 0xe0, 0xe9, 0x01, 0x6d, 0xed, 0xa2, 0x2c, 0xa6, 0x7d, 0xbd, 0x00,
- 0xf5, 0x05, 0xeb, 0x36, 0x35, 0x5f, 0xb2, 0x1c, 0xd3, 0xbd, 0x45, 0x10, 0x86, 0x6c, 0xea, 0xb4,
- 0x82, 0x2d, 0xd9, 0xfb, 0xa7, 0x62, 0x63, 0x2d, 0x3c, 0x42, 0x89, 0xea, 0xdf, 0xa6, 0x81, 0xce,
- 0x46, 0xdf, 0x7c, 0x57, 0x1a, 0xf9, 0xc5, 0xa6, 0x94, 0x23, 0xa0, 0x44, 0x22, 0xd3, 0x50, 0x13,
- 0xda, 0xa7, 0xe5, 0xb4, 0xb8, 0x0c, 0xab, 0xd1, 0xa4, 0xd7, 0x54, 0x19, 0x18, 0xd1, 0x68, 0xbb,
- 0x70, 0xb2, 0x47, 0x0c, 0xc4, 0x84, 0x72, 0xa0, 0xb7, 0xd4, 0xfc, 0xba, 0x30, 0xb0, 0x80, 0xd7,
- 0xf4, 0x56, 0x4c, 0xb8, 0x7c, 0x8d, 0x5f, 0xd3, 0xd9, 0x1a, 0xcf, 0xd0, 0xb5, 0x9f, 0x17, 0xa0,
- 0xba, 0xd0, 0x75, 0x0c, 0xbe, 0x37, 0xba, 0xb7, 0x29, 0x4c, 0x29, 0x0c, 0xc5, 0x4c, 0x85, 0xa1,
- 0x0b, 0x43, 0xdb, 0xb7, 0x42, 0x85, 0xa2, 0x3e, 0xb3, 0x32, 0x78, 0xaf, 0x90, 0x55, 0x9a, 0xba,
- 0xc6, 0xf1, 0xc4, 0x49, 0xcd, 0x98, 0xac, 0xd0, 0xd0, 0xb5, 0x97, 0x38, 0x53, 0xc9, 0xec, 0xdc,
- 0xc7, 0xa0, 0x1e, 0x23, 0x3b, 0x94, 0xd1, 0xf6, 0x77, 0xca, 0x30, 0xb4, 0xd8, 0x6c, 0xce, 0xae,
- 0x2e, 0x91, 0x67, 0xa0, 0x2e, 0x8d, 0xf8, 0xd7, 0x23, 0x19, 0x84, 0x67, 0x38, 0xcd, 0x28, 0x0b,
- 0xe3, 0x74, 0x4c, 0x1d, 0xf3, 0xa8, 0x6e, 0xb7, 0xe5, 0x60, 0x09, 0xd5, 0x31, 0x64, 0x89, 0x28,
- 0xf2, 0x88, 0x0e, 0x63, 0x6c, 0x87, 0xc7, 0x44, 0x28, 0x76, 0x6f, 0x72, 0xd8, 0x1c, 0x70, 0x7f,
- 0xc7, 0x95, 0xc4, 0xf5, 0x04, 0x00, 0xa6, 0x00, 0xc9, 0xf3, 0x50, 0xd5, 0xbb, 0xc1, 0x16, 0x57,
- 0xa0, 0xc5, 0xd8, 0x38, 0xcf, 0xcf, 0x38, 0x64, 0xda, 0x9d, 0xbd, 0xc9, 0x91, 0x6b, 0xd8, 0x78,
- 0x46, 0xbd, 0x63, 0x48, 0xcd, 0x2a, 0xa7, 0x76, 0x8c, 0xb2, 0x72, 0x95, 0x43, 0x57, 0x6e, 0x35,
- 0x01, 0x80, 0x29, 0x40, 0xf2, 0x0a, 0x8c, 0x6c, 0xd3, 0xdd, 0x40, 0xdf, 0x90, 0x0c, 0x86, 0x0e,
- 0xc3, 0x60, 0x9c, 0xa9, 0x70, 0xd7, 0x62, 0xc5, 0x31, 0x01, 0x46, 0x7c, 0x38, 0xbd, 0x4d, 0xbd,
- 0x0d, 0xea, 0xb9, 0x72, 0xf7, 0x29, 0x99, 0x0c, 0x1f, 0x86, 0xc9, 0xc4, 0xfe, 0xde, 0xe4, 0xe9,
- 0x6b, 0x19, 0x30, 0x98, 0x09, 0xae, 0xfd, 0xac, 0x08, 0x27, 0x16, 0xc5, 0x29, 0xaa, 0xeb, 0x89,
- 0x45, 0x98, 0x9c, 0x85, 0x92, 0xd7, 0xe9, 0xf2, 0x9e, 0x53, 0x12, 0x76, 0x52, 0x5c, 0x5d, 0x47,
- 0x96, 0x46, 0x5e, 0x86, 0xaa, 0x29, 0xa7, 0x0c, 0xb9, 0xf9, 0x3d, 0xec, 0x44, 0xc3, 0x17, 0x41,
- 0xf5, 0x86, 0x21, 0x1a, 0xd3, 0xf4, 0xdb, 0x7e, 0xab, 0x69, 0xbd, 0x41, 0xe5, 0x7e, 0x90, 0x6b,
- 0xfa, 0x2b, 0x22, 0x09, 0x55, 0x1e, 0x5b, 0x55, 0xb7, 0xe9, 0xae, 0xd8, 0x0d, 0x95, 0xa3, 0x55,
- 0xf5, 0x9a, 0x4c, 0xc3, 0x30, 0x97, 0x4c, 0xaa, 0xc1, 0xc2, 0x7a, 0x41, 0x59, 0xec, 0xe4, 0x6f,
- 0xb2, 0x04, 0x39, 0x6e, 0xd8, 0x94, 0xf9, 0x9a, 0x15, 0x04, 0xd4, 0x93, 0xcd, 0x38, 0xd0, 0x94,
- 0x79, 0x95, 0x23, 0xa0, 0x44, 0x22, 0x1f, 0x86, 0x1a, 0x07, 0x6f, 0xd8, 0xee, 0x06, 0x6f, 0xb8,
- 0x9a, 0xd8, 0xd3, 0xdf, 0x54, 0x89, 0x18, 0xe5, 0x6b, 0xbf, 0x28, 0xc2, 0x99, 0x45, 0x1a, 0x08,
- 0xad, 0x66, 0x9e, 0x76, 0x6c, 0x77, 0x97, 0xa9, 0x96, 0x48, 0x5f, 0x27, 0x2f, 0x02, 0x58, 0xfe,
- 0x46, 0x73, 0xc7, 0xe0, 0xe3, 0x40, 0x8c, 0xe1, 0x8b, 0x72, 0x48, 0xc2, 0x52, 0xb3, 0x21, 0x73,
- 0xee, 0x24, 0xde, 0x30, 0x56, 0x26, 0xda, 0x5e, 0x15, 0xef, 0xb2, 0xbd, 0x6a, 0x02, 0x74, 0x22,
- 0x05, 0xb5, 0xc4, 0x29, 0xff, 0xbb, 0x62, 0x73, 0x18, 0xdd, 0x34, 0x06, 0x93, 0x47, 0x65, 0x74,
- 0x60, 0xdc, 0xa4, 0x9b, 0x7a, 0xd7, 0x0e, 0x42, 0xa5, 0x5a, 0x0e, 0xe2, 0x83, 0xeb, 0xe5, 0xe1,
- 0x09, 0xef, 0x7c, 0x0a, 0x09, 0x7b, 0xb0, 0xb5, 0xdf, 0x2d, 0xc1, 0xb9, 0x45, 0x1a, 0x84, 0x16,
- 0x17, 0x39, 0x3b, 0x36, 0x3b, 0xd4, 0x60, 0xad, 0xf0, 0x66, 0x01, 0x86, 0x6c, 0x7d, 0x83, 0xda,
- 0x6c, 0xf5, 0x62, 0x5f, 0xf3, 0xea, 0xc0, 0x0b, 0x41, 0x7f, 0x2e, 0x53, 0xcb, 0x9c, 0x43, 0x6a,
- 0x69, 0x10, 0x89, 0x28, 0xd9, 0xb3, 0x49, 0xdd, 0xb0, 0xbb, 0x7e, 0x40, 0xbd, 0x55, 0xd7, 0x0b,
- 0xa4, 0x3e, 0x19, 0x4e, 0xea, 0x73, 0x51, 0x16, 0xc6, 0xe9, 0xc8, 0x0c, 0x80, 0x61, 0x5b, 0xd4,
- 0x09, 0x78, 0x29, 0x31, 0xae, 0x88, 0x6a, 0xdf, 0xb9, 0x30, 0x07, 0x63, 0x54, 0x8c, 0x55, 0xdb,
- 0x75, 0xac, 0xc0, 0x15, 0xac, 0xca, 0x49, 0x56, 0x2b, 0x51, 0x16, 0xc6, 0xe9, 0x78, 0x31, 0x1a,
- 0x78, 0x96, 0xe1, 0xf3, 0x62, 0x95, 0x54, 0xb1, 0x28, 0x0b, 0xe3, 0x74, 0x6c, 0xcd, 0x8b, 0x7d,
- 0xff, 0xa1, 0xd6, 0xbc, 0x6f, 0xd7, 0xe0, 0x42, 0x42, 0xac, 0x81, 0x1e, 0xd0, 0xcd, 0xae, 0xdd,
- 0xa4, 0x81, 0x6a, 0xc0, 0x01, 0xd7, 0xc2, 0xff, 0x1b, 0xb5, 0xbb, 0xf0, 0xdd, 0x30, 0x8e, 0xa6,
- 0xdd, 0x7b, 0x2a, 0x78, 0xa0, 0xb6, 0x9f, 0x86, 0x9a, 0xa3, 0x07, 0x3e, 0x1f, 0xb8, 0x72, 0x8c,
- 0x86, 0x6a, 0xd8, 0x75, 0x95, 0x81, 0x11, 0x0d, 0x59, 0x85, 0xd3, 0x52, 0xc4, 0x97, 0x6f, 0x77,
- 0x5c, 0x2f, 0xa0, 0x9e, 0x28, 0x2b, 0x97, 0x53, 0x59, 0xf6, 0xf4, 0x4a, 0x06, 0x0d, 0x66, 0x96,
- 0x24, 0x2b, 0x70, 0xca, 0x10, 0xe7, 0xd9, 0xd4, 0x76, 0x75, 0x53, 0x01, 0x0a, 0x03, 0x57, 0xb8,
- 0x35, 0x9a, 0xeb, 0x25, 0xc1, 0xac, 0x72, 0xe9, 0xde, 0x3c, 0x34, 0x50, 0x6f, 0x1e, 0x1e, 0xa4,
- 0x37, 0x57, 0x07, 0xeb, 0xcd, 0xb5, 0x83, 0xf5, 0x66, 0x26, 0x79, 0xd6, 0x8f, 0xa8, 0xc7, 0xd4,
- 0x13, 0xb1, 0xc2, 0xc6, 0xdc, 0x25, 0x42, 0xc9, 0x37, 0x33, 0x68, 0x30, 0xb3, 0x24, 0xd9, 0x80,
- 0x73, 0x22, 0xfd, 0xb2, 0x63, 0x78, 0xbb, 0x1d, 0xb6, 0xf0, 0xc4, 0x70, 0xeb, 0x09, 0x0b, 0xe3,
- 0xb9, 0x66, 0x5f, 0x4a, 0xbc, 0x0b, 0x0a, 0xf9, 0x04, 0x8c, 0x8a, 0x56, 0x5a, 0xd1, 0x3b, 0x1c,
- 0x56, 0x38, 0x4f, 0x3c, 0x2c, 0x61, 0x47, 0xe7, 0xe2, 0x99, 0x98, 0xa4, 0x25, 0xb3, 0x70, 0xa2,
- 0xb3, 0x63, 0xb0, 0xc7, 0xa5, 0xcd, 0xeb, 0x94, 0x9a, 0xd4, 0xe4, 0xa7, 0x35, 0xb5, 0xc6, 0x23,
- 0xca, 0xd0, 0xb1, 0x9a, 0xcc, 0xc6, 0x34, 0x3d, 0x79, 0x1e, 0x46, 0xfc, 0x40, 0xf7, 0x02, 0x69,
- 0xd6, 0x9b, 0x18, 0x13, 0xce, 0x25, 0xca, 0xea, 0xd5, 0x8c, 0xe5, 0x61, 0x82, 0x32, 0x73, 0xbd,
- 0x38, 0x71, 0x7c, 0xeb, 0x45, 0x9e, 0xd9, 0xea, 0x4f, 0x8b, 0x70, 0x71, 0x91, 0x06, 0x2b, 0xae,
- 0x23, 0x8d, 0xa2, 0x59, 0xcb, 0xfe, 0x81, 0x6c, 0xa2, 0xc9, 0x45, 0xbb, 0x78, 0xa4, 0x8b, 0x76,
- 0xe9, 0x88, 0x16, 0xed, 0xf2, 0x31, 0x2e, 0xda, 0xbf, 0x5f, 0x84, 0x47, 0x12, 0x92, 0x5c, 0x75,
- 0x4d, 0x35, 0xe1, 0xbf, 0x2f, 0xc0, 0x03, 0x08, 0xf0, 0x8e, 0xd0, 0x3b, 0xf9, 0xb1, 0x56, 0x4a,
- 0xe3, 0xf9, 0x5a, 0x5a, 0xe3, 0x79, 0x25, 0xcf, 0xca, 0x97, 0xc1, 0xe1, 0x40, 0x2b, 0xde, 0x55,
- 0x20, 0x9e, 0x3c, 0x84, 0x13, 0xa6, 0x9f, 0x98, 0xd2, 0x13, 0x7a, 0xaf, 0x61, 0x0f, 0x05, 0x66,
- 0x94, 0x22, 0x4d, 0x78, 0xd8, 0xa7, 0x4e, 0x60, 0x39, 0xd4, 0x4e, 0xc2, 0x09, 0x6d, 0xe8, 0x31,
- 0x09, 0xf7, 0x70, 0x33, 0x8b, 0x08, 0xb3, 0xcb, 0xe6, 0x99, 0x07, 0xfe, 0x1c, 0xb8, 0xca, 0x29,
- 0x44, 0x73, 0x64, 0x1a, 0xcb, 0x9b, 0x69, 0x8d, 0xe5, 0xd5, 0xfc, 0xed, 0x36, 0x98, 0xb6, 0x32,
- 0x03, 0xc0, 0x5b, 0x21, 0xae, 0xae, 0x84, 0x8b, 0x34, 0x86, 0x39, 0x18, 0xa3, 0x62, 0x0b, 0x90,
- 0x92, 0x73, 0x5c, 0x53, 0x09, 0x17, 0xa0, 0x66, 0x3c, 0x13, 0x93, 0xb4, 0x7d, 0xb5, 0x9d, 0xca,
- 0xc0, 0xda, 0xce, 0x55, 0x20, 0x09, 0xc3, 0xa3, 0xc0, 0x1b, 0x4a, 0x3a, 0x4f, 0x2e, 0xf5, 0x50,
- 0x60, 0x46, 0xa9, 0x3e, 0x5d, 0x79, 0xf8, 0x68, 0xbb, 0x72, 0x75, 0xf0, 0xae, 0x4c, 0x5e, 0x85,
- 0xb3, 0x9c, 0x95, 0x94, 0x4f, 0x12, 0x58, 0xe8, 0x3d, 0x1f, 0x90, 0xc0, 0x67, 0xb1, 0x1f, 0x21,
- 0xf6, 0xc7, 0x60, 0xed, 0x63, 0x78, 0xd4, 0x64, 0xcc, 0x75, 0xbb, 0xbf, 0x4e, 0x34, 0x97, 0x41,
- 0x83, 0x99, 0x25, 0x59, 0x17, 0x0b, 0x58, 0x37, 0xd4, 0x37, 0x6c, 0x6a, 0x4a, 0xe7, 0xd1, 0xb0,
- 0x8b, 0xad, 0x2d, 0x37, 0x65, 0x0e, 0xc6, 0xa8, 0xb2, 0xd4, 0x94, 0x91, 0x43, 0xaa, 0x29, 0x8b,
- 0xdc, 0x4a, 0xbf, 0x99, 0xd0, 0x86, 0xa4, 0xae, 0x13, 0xba, 0x03, 0xcf, 0xa5, 0x09, 0xb0, 0xb7,
- 0x0c, 0xd7, 0x12, 0x0d, 0xcf, 0xea, 0x04, 0x7e, 0x12, 0x6b, 0x2c, 0xa5, 0x25, 0x66, 0xd0, 0x60,
- 0x66, 0x49, 0xa6, 0x9f, 0x6f, 0x51, 0xdd, 0x0e, 0xb6, 0x92, 0x80, 0x27, 0x92, 0xfa, 0xf9, 0x95,
- 0x5e, 0x12, 0xcc, 0x2a, 0x97, 0xb9, 0x20, 0x8d, 0x3f, 0x98, 0x6a, 0xd5, 0x57, 0x4b, 0x70, 0x76,
- 0x91, 0x06, 0xa1, 0x5f, 0xcd, 0xfb, 0x66, 0x94, 0x77, 0xc1, 0x8c, 0xf2, 0xad, 0x0a, 0x9c, 0x5a,
- 0xa4, 0x41, 0x8f, 0x36, 0xf6, 0x9f, 0x54, 0xfc, 0x2b, 0x70, 0x2a, 0x72, 0xe5, 0x6a, 0x06, 0xae,
- 0x27, 0xd6, 0xf2, 0xd4, 0x6e, 0xb9, 0xd9, 0x4b, 0x82, 0x59, 0xe5, 0xc8, 0x67, 0xe1, 0x11, 0xbe,
- 0xd4, 0x3b, 0x2d, 0x61, 0x9f, 0x15, 0xc6, 0x84, 0xd8, 0x65, 0x84, 0x49, 0x09, 0xf9, 0x48, 0x33,
- 0x9b, 0x0c, 0xfb, 0x95, 0x27, 0x5f, 0x82, 0x91, 0x8e, 0xd5, 0xa1, 0xb6, 0xe5, 0x70, 0xfd, 0x2c,
- 0xb7, 0x4b, 0xc8, 0x6a, 0x0c, 0x2c, 0xda, 0xc0, 0xc5, 0x53, 0x31, 0xc1, 0x30, 0xb3, 0xa7, 0x56,
- 0x8f, 0xb1, 0xa7, 0xfe, 0x4b, 0x11, 0x86, 0x17, 0x3d, 0xb7, 0xdb, 0x69, 0xec, 0x92, 0x16, 0x0c,
- 0xdd, 0xe2, 0x87, 0x67, 0xf2, 0x68, 0x6a, 0x70, 0x77, 0x68, 0x71, 0x06, 0x17, 0xa9, 0x44, 0xe2,
- 0x1d, 0x25, 0x3c, 0xeb, 0xc4, 0xdb, 0x74, 0x97, 0x9a, 0xf2, 0x0c, 0x2d, 0xec, 0xc4, 0xd7, 0x58,
- 0x22, 0x8a, 0x3c, 0xd2, 0x86, 0x13, 0xba, 0x6d, 0xbb, 0xb7, 0xa8, 0xb9, 0xac, 0x07, 0xd4, 0xa1,
- 0xbe, 0x3a, 0x92, 0x3c, 0xac, 0x59, 0x9a, 0x9f, 0xeb, 0xcf, 0x26, 0xa1, 0x30, 0x8d, 0x4d, 0x5e,
- 0x83, 0x61, 0x3f, 0x70, 0x3d, 0xa5, 0x6c, 0xd5, 0x67, 0xe6, 0x06, 0x6f, 0xf4, 0xc6, 0x67, 0x9a,
- 0x02, 0x4a, 0xd8, 0xec, 0xe5, 0x0b, 0x2a, 0x06, 0xda, 0x37, 0x0b, 0x00, 0x57, 0xd6, 0xd6, 0x56,
- 0xe5, 0xf1, 0x82, 0x09, 0x65, 0xbd, 0x1b, 0x1e, 0x54, 0x0e, 0x7e, 0x20, 0x98, 0xf0, 0x87, 0x94,
- 0x67, 0x78, 0xdd, 0x60, 0x0b, 0x39, 0x3a, 0xf9, 0x10, 0x0c, 0x4b, 0x05, 0x59, 0x8a, 0x3d, 0x74,
- 0x2d, 0x90, 0x4a, 0x34, 0xaa, 0x7c, 0xed, 0xb7, 0x8a, 0x00, 0x4b, 0xa6, 0x4d, 0x9b, 0xca, 0x83,
- 0xbd, 0x16, 0x6c, 0x79, 0xd4, 0xdf, 0x72, 0x6d, 0x73, 0xc0, 0xd3, 0x54, 0x6e, 0xf3, 0x5f, 0x53,
- 0x20, 0x18, 0xe1, 0x11, 0x13, 0x46, 0xfc, 0x80, 0x76, 0x96, 0x9c, 0x80, 0x7a, 0x3b, 0xba, 0x3d,
- 0xe0, 0x21, 0xca, 0xb8, 0xb0, 0x8b, 0x44, 0x38, 0x98, 0x40, 0x25, 0x3a, 0xd4, 0x2d, 0xc7, 0x10,
- 0x03, 0xa4, 0xb1, 0x3b, 0x60, 0x47, 0x3a, 0xc1, 0x76, 0x1c, 0x4b, 0x11, 0x0c, 0xc6, 0x31, 0xb5,
- 0x1f, 0x17, 0xe1, 0x0c, 0xe7, 0xc7, 0xaa, 0x91, 0xf0, 0xc7, 0x24, 0xff, 0xb3, 0xe7, 0xb6, 0xdd,
- 0x7f, 0x3b, 0x18, 0x6b, 0x71, 0x59, 0x6b, 0x85, 0x06, 0x7a, 0xa4, 0xcf, 0x45, 0x69, 0xb1, 0x2b,
- 0x76, 0x5d, 0x28, 0xfb, 0x6c, 0xbe, 0x12, 0xd2, 0x6b, 0x0e, 0xdc, 0x85, 0xb2, 0x3f, 0x80, 0xcf,
- 0x5e, 0xe1, 0xa9, 0x31, 0x9f, 0xb5, 0x38, 0x3b, 0xf2, 0xbf, 0x61, 0xc8, 0x0f, 0xf4, 0xa0, 0xab,
- 0x86, 0xe6, 0xfa, 0x51, 0x33, 0xe6, 0xe0, 0xd1, 0x3c, 0x22, 0xde, 0x51, 0x32, 0xd5, 0x7e, 0x5c,
- 0x80, 0x73, 0xd9, 0x05, 0x97, 0x2d, 0x3f, 0x20, 0xff, 0xa3, 0x47, 0xec, 0x07, 0x6c, 0x71, 0x56,
- 0x9a, 0x0b, 0x3d, 0x74, 0xc8, 0x56, 0x29, 0x31, 0x91, 0x07, 0x50, 0xb1, 0x02, 0xda, 0x56, 0xfb,
- 0xcb, 0x1b, 0x47, 0xfc, 0xe9, 0xb1, 0xa5, 0x9d, 0x71, 0x41, 0xc1, 0x4c, 0x7b, 0xab, 0xd8, 0xef,
- 0x93, 0xf9, 0xf2, 0x61, 0x27, 0x7d, 0x7e, 0xaf, 0xe5, 0xf3, 0xf9, 0x4d, 0x56, 0xa8, 0xd7, 0xf5,
- 0xf7, 0x7f, 0xf5, 0xba, 0xfe, 0xde, 0xc8, 0xef, 0xfa, 0x9b, 0x12, 0x43, 0x5f, 0x0f, 0xe0, 0x77,
- 0x4a, 0x70, 0xfe, 0x6e, 0xdd, 0x86, 0xad, 0x67, 0xb2, 0x77, 0xe6, 0x5d, 0xcf, 0xee, 0xde, 0x0f,
- 0xc9, 0x0c, 0x54, 0x3a, 0x5b, 0xba, 0xaf, 0x94, 0x32, 0xb5, 0x61, 0xa9, 0xac, 0xb2, 0xc4, 0x3b,
- 0x6c, 0xd2, 0xe0, 0xca, 0x1c, 0x7f, 0x45, 0x41, 0xca, 0xa6, 0xe3, 0x36, 0xf5, 0xfd, 0xc8, 0x26,
- 0x10, 0x4e, 0xc7, 0x2b, 0x22, 0x19, 0x55, 0x3e, 0x09, 0x60, 0x48, 0x98, 0x98, 0xe5, 0xca, 0x34,
- 0xb8, 0x23, 0x57, 0x86, 0x9b, 0x78, 0xf4, 0x51, 0xf2, 0xb4, 0x42, 0xf2, 0x22, 0x53, 0x50, 0x0e,
- 0x22, 0xa7, 0x5d, 0xb5, 0x35, 0x2f, 0x67, 0xe8, 0xa7, 0x9c, 0x8e, 0x6d, 0xec, 0xdd, 0x0d, 0x6e,
- 0x54, 0x37, 0xe5, 0xf9, 0xb9, 0xe5, 0x3a, 0x5c, 0x21, 0x2b, 0x45, 0x1b, 0xfb, 0x1b, 0x3d, 0x14,
- 0x98, 0x51, 0x4a, 0xfb, 0xab, 0x2a, 0x9c, 0xc9, 0xee, 0x0f, 0x4c, 0x6e, 0x3b, 0xd4, 0xf3, 0x19,
- 0x76, 0x21, 0x29, 0xb7, 0x9b, 0x22, 0x19, 0x55, 0xfe, 0x7b, 0xda, 0xe1, 0xec, 0x5b, 0x05, 0x38,
- 0xeb, 0xc9, 0x33, 0xa2, 0xfb, 0xe1, 0x74, 0xf6, 0x98, 0x30, 0x67, 0xf4, 0x61, 0x88, 0xfd, 0xeb,
- 0x42, 0x7e, 0xa3, 0x00, 0x13, 0xed, 0x94, 0x9d, 0xe3, 0x18, 0x2f, 0x8c, 0x71, 0xaf, 0xf8, 0x95,
- 0x3e, 0xfc, 0xb0, 0x6f, 0x4d, 0xc8, 0x97, 0xa0, 0xde, 0x61, 0xfd, 0xc2, 0x0f, 0xa8, 0x63, 0xa8,
- 0x3b, 0x63, 0x83, 0x8f, 0xa4, 0xd5, 0x08, 0x4b, 0xb9, 0xa2, 0x09, 0xfd, 0x20, 0x96, 0x81, 0x71,
- 0x8e, 0x0f, 0xf8, 0x0d, 0xb1, 0x4b, 0x50, 0xf5, 0x69, 0x10, 0x58, 0x4e, 0x4b, 0xec, 0x37, 0x6a,
- 0x62, 0xac, 0x34, 0x65, 0x1a, 0x86, 0xb9, 0xe4, 0xc3, 0x50, 0xe3, 0x47, 0x4e, 0xb3, 0x5e, 0xcb,
- 0x9f, 0xa8, 0x71, 0x77, 0xb1, 0x51, 0xe1, 0x00, 0x27, 0x13, 0x31, 0xca, 0x27, 0x4f, 0xc3, 0xc8,
- 0x06, 0x1f, 0xbe, 0xf2, 0xd2, 0xb0, 0xb0, 0x71, 0x71, 0x6d, 0xad, 0x11, 0x4b, 0xc7, 0x04, 0x15,
- 0x99, 0x01, 0xa0, 0xe1, 0xb9, 0x5c, 0xda, 0x9e, 0x15, 0x9d, 0xd8, 0x61, 0x8c, 0x8a, 0x3c, 0x06,
- 0xa5, 0xc0, 0xf6, 0xb9, 0x0d, 0xab, 0x1a, 0x6d, 0x41, 0xd7, 0x96, 0x9b, 0xc8, 0xd2, 0xb5, 0x5f,
- 0x14, 0xe0, 0x44, 0xea, 0x72, 0x09, 0x2b, 0xd2, 0xf5, 0x6c, 0x39, 0x8d, 0x84, 0x45, 0xd6, 0x71,
- 0x19, 0x59, 0x3a, 0x79, 0x55, 0xaa, 0xe5, 0xc5, 0x9c, 0xf1, 0x11, 0xae, 0xeb, 0x81, 0xcf, 0xf4,
- 0xf0, 0x1e, 0x8d, 0x9c, 0x1f, 0xf3, 0x45, 0xf5, 0x91, 0xeb, 0x40, 0xec, 0x98, 0x2f, 0xca, 0xc3,
- 0x04, 0x65, 0xca, 0xe0, 0x57, 0x3e, 0x88, 0xc1, 0x4f, 0xfb, 0x7a, 0x31, 0x26, 0x01, 0xa9, 0xd9,
- 0xdf, 0x43, 0x02, 0x4f, 0xb2, 0x05, 0x34, 0x5c, 0xdc, 0x6b, 0xf1, 0xf5, 0x8f, 0x2f, 0xc6, 0x32,
- 0x97, 0xbc, 0x24, 0x64, 0x5f, 0xca, 0x79, 0x0b, 0x75, 0x6d, 0xb9, 0x29, 0xbc, 0xab, 0x54, 0xab,
- 0x85, 0x4d, 0x50, 0x3e, 0xa6, 0x26, 0xd0, 0xfe, 0xac, 0x04, 0xf5, 0xab, 0xee, 0xc6, 0x7b, 0xc4,
- 0x83, 0x3a, 0x7b, 0x99, 0x2a, 0xbe, 0x8b, 0xcb, 0xd4, 0x3a, 0x3c, 0x12, 0x04, 0x76, 0x93, 0x1a,
- 0xae, 0x63, 0xfa, 0xb3, 0x9b, 0x01, 0xf5, 0x16, 0x2c, 0xc7, 0xf2, 0xb7, 0xa8, 0x29, 0x8f, 0x93,
- 0x1e, 0xdd, 0xdf, 0x9b, 0x7c, 0x64, 0x6d, 0x6d, 0x39, 0x8b, 0x04, 0xfb, 0x95, 0xe5, 0xd3, 0x86,
- 0x6e, 0x6c, 0xbb, 0x9b, 0x9b, 0xfc, 0xa6, 0x8c, 0xf4, 0xb9, 0x11, 0xd3, 0x46, 0x2c, 0x1d, 0x13,
- 0x54, 0xda, 0xdb, 0x45, 0xa8, 0x85, 0x37, 0xdf, 0xc9, 0x13, 0x30, 0xbc, 0xe1, 0xb9, 0xdb, 0xd4,
- 0x13, 0x27, 0x77, 0xf2, 0xa6, 0x4c, 0x43, 0x24, 0xa1, 0xca, 0x23, 0x8f, 0x43, 0x25, 0x70, 0x3b,
- 0x96, 0x91, 0x36, 0xa8, 0xad, 0xb1, 0x44, 0x14, 0x79, 0xc7, 0xd7, 0xc1, 0x9f, 0x4c, 0xa8, 0x76,
- 0xb5, 0xbe, 0xca, 0xd8, 0x2b, 0x50, 0xf6, 0x75, 0xdf, 0x96, 0xeb, 0x69, 0x8e, 0x4b, 0xe4, 0xb3,
- 0xcd, 0x65, 0x79, 0x89, 0x7c, 0xb6, 0xb9, 0x8c, 0x1c, 0x54, 0xfb, 0x69, 0x11, 0xea, 0x42, 0x6e,
- 0x62, 0x56, 0x38, 0x4a, 0xc9, 0xbd, 0xc0, 0x5d, 0x29, 0xfc, 0x6e, 0x9b, 0x7a, 0xdc, 0xcc, 0x24,
- 0x27, 0xb9, 0xf8, 0xf9, 0x40, 0x94, 0x19, 0xba, 0x53, 0x44, 0x49, 0x4a, 0xf4, 0xe5, 0x63, 0x14,
- 0x7d, 0xe5, 0x40, 0xa2, 0x1f, 0x3a, 0x0e, 0xd1, 0xbf, 0x59, 0x84, 0xda, 0xb2, 0xb5, 0x49, 0x8d,
- 0x5d, 0xc3, 0xe6, 0x77, 0x02, 0x4d, 0x6a, 0xd3, 0x80, 0x2e, 0x7a, 0xba, 0x41, 0x57, 0xa9, 0x67,
- 0xf1, 0xc8, 0x30, 0x6c, 0x7c, 0xf0, 0x19, 0x48, 0xde, 0x09, 0x9c, 0xef, 0x43, 0x83, 0x7d, 0x4b,
- 0x93, 0x25, 0x18, 0x31, 0xa9, 0x6f, 0x79, 0xd4, 0x5c, 0x8d, 0x6d, 0x54, 0x9e, 0x50, 0x4b, 0xcd,
- 0x7c, 0x2c, 0xef, 0xce, 0xde, 0xe4, 0xa8, 0x32, 0x50, 0x8a, 0x1d, 0x4b, 0xa2, 0x28, 0x1b, 0xf2,
- 0x1d, 0xbd, 0xeb, 0x67, 0xd5, 0x31, 0x36, 0xe4, 0x57, 0xb3, 0x49, 0xb0, 0x5f, 0x59, 0xad, 0x02,
- 0xa5, 0x65, 0xb7, 0xa5, 0xbd, 0x55, 0x82, 0x30, 0x84, 0x10, 0xf9, 0x3f, 0x05, 0xa8, 0xeb, 0x8e,
- 0xe3, 0x06, 0x32, 0x3c, 0x8f, 0x38, 0x81, 0xc7, 0xdc, 0x91, 0x8a, 0xa6, 0x66, 0x23, 0x50, 0x71,
- 0x78, 0x1b, 0x1e, 0x28, 0xc7, 0x72, 0x30, 0xce, 0x9b, 0x74, 0x53, 0xe7, 0xc9, 0x2b, 0xf9, 0x6b,
- 0x71, 0x80, 0xd3, 0xe3, 0x73, 0x9f, 0x86, 0xf1, 0x74, 0x65, 0x0f, 0x73, 0x1c, 0x94, 0xeb, 0x60,
- 0xbe, 0x08, 0x10, 0xf9, 0x94, 0xdc, 0x07, 0x23, 0x96, 0x95, 0x30, 0x62, 0x2d, 0x0e, 0x2e, 0xe0,
- 0xb0, 0xd2, 0x7d, 0x0d, 0x57, 0xaf, 0xa7, 0x0c, 0x57, 0x4b, 0x47, 0xc1, 0xec, 0xee, 0xc6, 0xaa,
- 0xdf, 0x2c, 0xc0, 0x78, 0x44, 0x2c, 0x6f, 0xc8, 0x3e, 0x07, 0xa3, 0x1e, 0xd5, 0xcd, 0x86, 0x1e,
- 0x18, 0x5b, 0xdc, 0xd5, 0xbb, 0xc0, 0x7d, 0xb3, 0x4f, 0xee, 0xef, 0x4d, 0x8e, 0x62, 0x3c, 0x03,
- 0x93, 0x74, 0x44, 0x87, 0x3a, 0x4b, 0x58, 0xb3, 0xda, 0xd4, 0xed, 0x06, 0x03, 0x5a, 0x4d, 0xf9,
- 0x86, 0x05, 0x23, 0x18, 0x8c, 0x63, 0x6a, 0xef, 0x14, 0x60, 0x2c, 0x5e, 0xe1, 0x63, 0xb7, 0xa8,
- 0x6d, 0x25, 0x2d, 0x6a, 0x73, 0x47, 0xd0, 0x26, 0x7d, 0xac, 0x68, 0x3f, 0xab, 0xc6, 0x3f, 0x8d,
- 0x5b, 0xce, 0xe2, 0xc6, 0x82, 0xc2, 0x5d, 0x8d, 0x05, 0xef, 0xfd, 0xa8, 0x31, 0xfd, 0xb4, 0xdc,
- 0xf2, 0x03, 0xac, 0xe5, 0xbe, 0x9b, 0xa1, 0x67, 0x62, 0xe1, 0x53, 0x86, 0x72, 0x84, 0x4f, 0x69,
- 0x87, 0xe1, 0x53, 0x86, 0x8f, 0x6c, 0xd2, 0x39, 0x48, 0x08, 0x95, 0xea, 0x7d, 0x0d, 0xa1, 0x52,
- 0x3b, 0xae, 0x10, 0x2a, 0x90, 0x37, 0x84, 0xca, 0xd7, 0x0a, 0x30, 0x66, 0x26, 0x6e, 0xcc, 0x72,
- 0xdb, 0x42, 0x9e, 0xa5, 0x26, 0x79, 0x01, 0x57, 0x5c, 0x99, 0x4a, 0xa6, 0x61, 0x8a, 0xa5, 0xf6,
- 0xdb, 0x95, 0xf8, 0x3a, 0x70, 0xbf, 0x4d, 0xd5, 0xcf, 0x26, 0x4d, 0xd5, 0x17, 0xd3, 0xa6, 0xea,
- 0x13, 0x31, 0x2f, 0xd2, 0xb8, 0xb9, 0xfa, 0x23, 0xb1, 0xe9, 0x91, 0xcd, 0x49, 0xa3, 0x91, 0xa4,
- 0x33, 0xa6, 0xc8, 0x8f, 0x40, 0xd5, 0x57, 0xc1, 0x1e, 0xc5, 0xc6, 0x26, 0x6a, 0x17, 0x15, 0x88,
- 0x31, 0xa4, 0x60, 0x9a, 0xb8, 0x47, 0x75, 0xdf, 0x75, 0xd2, 0x9a, 0x38, 0xf2, 0x54, 0x94, 0xb9,
- 0x71, 0x93, 0xf9, 0xd0, 0x3d, 0x4c, 0xe6, 0x3a, 0xd4, 0x6d, 0xdd, 0x0f, 0xd6, 0x3b, 0xa6, 0x1e,
- 0x50, 0x53, 0x8e, 0xb7, 0xff, 0x7a, 0xb0, 0xb5, 0x8a, 0xad, 0x7f, 0x91, 0x42, 0xb8, 0x1c, 0xc1,
- 0x60, 0x1c, 0x93, 0x98, 0x30, 0xc2, 0x5e, 0xf9, 0x68, 0x30, 0x67, 0x55, 0x08, 0x80, 0xc3, 0xf0,
- 0x08, 0x2d, 0x3d, 0xcb, 0x31, 0x1c, 0x4c, 0xa0, 0xf6, 0xb1, 0xaa, 0xd7, 0x06, 0xb1, 0xaa, 0x93,
- 0x4f, 0x08, 0x65, 0x63, 0x57, 0x35, 0x18, 0xb7, 0xc6, 0x8d, 0x46, 0x5e, 0x85, 0x18, 0xcf, 0xc4,
- 0x24, 0xad, 0xf6, 0xb5, 0x1a, 0xd4, 0xaf, 0xeb, 0x81, 0xb5, 0x43, 0xf9, 0x11, 0xd0, 0xf1, 0xd8,
- 0xe1, 0x7f, 0xa5, 0x00, 0x67, 0x92, 0x7e, 0x7e, 0xc7, 0x68, 0x8c, 0xe7, 0x51, 0x43, 0x30, 0x93,
- 0x1b, 0xf6, 0xa9, 0x05, 0x37, 0xcb, 0xf7, 0xb8, 0x0d, 0x1e, 0xb7, 0x59, 0xbe, 0xd9, 0x8f, 0x21,
- 0xf6, 0xaf, 0xcb, 0x7b, 0xc5, 0x2c, 0xff, 0x60, 0x47, 0x75, 0x4b, 0x1d, 0x1a, 0x0c, 0x3f, 0x30,
- 0x87, 0x06, 0xd5, 0x07, 0x42, 0x53, 0xeb, 0xc4, 0x0e, 0x0d, 0x6a, 0x39, 0x9d, 0x57, 0xa4, 0x6b,
- 0xbc, 0x40, 0xeb, 0x77, 0xf8, 0xc0, 0x6f, 0xb5, 0x2b, 0x63, 0x2e, 0x53, 0x70, 0x36, 0x74, 0xdf,
- 0x32, 0xe4, 0x9a, 0x99, 0x23, 0x8a, 0xa5, 0x0a, 0xf7, 0x25, 0xce, 0xb8, 0xf9, 0x2b, 0x0a, 0xec,
- 0x28, 0xac, 0x58, 0x31, 0x57, 0x58, 0x31, 0x32, 0x07, 0x65, 0x87, 0x6d, 0xbd, 0x4b, 0x87, 0x0e,
- 0x24, 0x76, 0xfd, 0x1a, 0xdd, 0x45, 0x5e, 0x58, 0x7b, 0xbb, 0x08, 0xc0, 0x3e, 0xff, 0x60, 0xe6,
- 0xfb, 0x0f, 0xc1, 0xb0, 0xdf, 0xe5, 0x1b, 0x6d, 0xb9, 0xda, 0x47, 0x1e, 0x3f, 0x22, 0x19, 0x55,
- 0x3e, 0x79, 0x1c, 0x2a, 0xaf, 0x77, 0x69, 0x57, 0x9d, 0x45, 0x87, 0xba, 0xde, 0x67, 0x58, 0x22,
- 0x8a, 0xbc, 0xe3, 0x33, 0xc5, 0x29, 0x33, 0x7f, 0xe5, 0xb8, 0xcc, 0xfc, 0x35, 0x18, 0xbe, 0xee,
- 0x72, 0x07, 0x42, 0xed, 0x9f, 0x8a, 0x00, 0x91, 0x83, 0x16, 0xf9, 0x66, 0x01, 0x1e, 0x0e, 0x07,
- 0x5c, 0x20, 0x54, 0x76, 0x1e, 0x38, 0x36, 0xb7, 0xc9, 0x3f, 0x6b, 0xb0, 0xf3, 0x19, 0x68, 0x35,
- 0x8b, 0x1d, 0x66, 0xd7, 0x82, 0x20, 0x54, 0x69, 0xbb, 0x13, 0xec, 0xce, 0x5b, 0x9e, 0xec, 0x81,
- 0x99, 0x7e, 0x80, 0x97, 0x25, 0x8d, 0x28, 0x2a, 0xf7, 0x95, 0x7c, 0x10, 0xa9, 0x1c, 0x0c, 0x71,
- 0xc8, 0x16, 0x54, 0x1d, 0xf7, 0x55, 0x9f, 0x89, 0x43, 0x76, 0xc7, 0x17, 0x07, 0x17, 0xb9, 0x10,
- 0xab, 0x30, 0x11, 0xcb, 0x17, 0x1c, 0x76, 0xa4, 0xb0, 0xbf, 0x51, 0x84, 0x53, 0x19, 0x72, 0x20,
- 0x2f, 0xc2, 0xb8, 0xf4, 0x85, 0x8b, 0x22, 0x28, 0x17, 0xa2, 0x08, 0xca, 0xcd, 0x54, 0x1e, 0xf6,
- 0x50, 0x93, 0x57, 0x01, 0x74, 0xc3, 0xa0, 0xbe, 0xbf, 0xe2, 0x9a, 0x4a, 0x99, 0x7d, 0x61, 0x7f,
- 0x6f, 0x12, 0x66, 0xc3, 0xd4, 0x3b, 0x7b, 0x93, 0x1f, 0xcd, 0x72, 0x6f, 0x4d, 0xc9, 0x39, 0x2a,
- 0x80, 0x31, 0x48, 0xf2, 0x05, 0x00, 0xb1, 0x6f, 0x0b, 0x6f, 0xe0, 0xdf, 0xc3, 0xd8, 0x31, 0xa5,
- 0x62, 0x1d, 0x4d, 0x7d, 0xa6, 0xab, 0x3b, 0x81, 0x15, 0xec, 0x8a, 0x80, 0x27, 0x37, 0x43, 0x14,
- 0x8c, 0x21, 0x6a, 0x7f, 0x52, 0x84, 0xaa, 0x32, 0xb3, 0xde, 0x07, 0xdb, 0x5a, 0x2b, 0x61, 0x5b,
- 0x3b, 0x22, 0x87, 0xd6, 0x2c, 0xcb, 0x9a, 0x9b, 0xb2, 0xac, 0x2d, 0xe6, 0x67, 0x75, 0x77, 0xbb,
- 0xda, 0x77, 0x8a, 0x30, 0xa6, 0x48, 0xf3, 0x5a, 0xd5, 0x3e, 0x05, 0x27, 0xc4, 0x41, 0xf4, 0x8a,
- 0x7e, 0x5b, 0xc4, 0x7e, 0xe1, 0x02, 0x2b, 0x0b, 0x1f, 0xd2, 0x46, 0x32, 0x0b, 0xd3, 0xb4, 0xac,
- 0x5b, 0x8b, 0xa4, 0x75, 0xb6, 0x09, 0x11, 0x47, 0x57, 0x62, 0xb3, 0xc4, 0xbb, 0x75, 0x23, 0x95,
- 0x87, 0x3d, 0xd4, 0x69, 0xb3, 0x5e, 0xf9, 0x18, 0xcc, 0x7a, 0x7f, 0x5d, 0x80, 0x91, 0x48, 0x5e,
- 0xc7, 0x6e, 0xd4, 0xdb, 0x4c, 0x1a, 0xf5, 0x66, 0x73, 0x77, 0x87, 0x3e, 0x26, 0xbd, 0xff, 0x37,
- 0x0c, 0x09, 0xbf, 0x6a, 0xb2, 0x01, 0xe7, 0xac, 0x4c, 0xef, 0xb0, 0xd8, 0x6c, 0x13, 0x5e, 0x14,
- 0x5e, 0xea, 0x4b, 0x89, 0x77, 0x41, 0x21, 0x5d, 0xa8, 0xee, 0x50, 0x2f, 0xb0, 0x0c, 0xaa, 0xbe,
- 0x6f, 0x31, 0xb7, 0x4a, 0x26, 0x0d, 0x97, 0xa1, 0x4c, 0x6f, 0x4a, 0x06, 0x18, 0xb2, 0x22, 0x1b,
- 0x50, 0xa1, 0x66, 0x8b, 0xaa, 0x68, 0x3c, 0x39, 0x63, 0x5d, 0x86, 0xf2, 0x64, 0x6f, 0x3e, 0x0a,
- 0x68, 0xe2, 0x43, 0xcd, 0x56, 0x07, 0x53, 0xb2, 0x1f, 0x0e, 0xae, 0x60, 0x85, 0x47, 0x5c, 0xd1,
- 0x45, 0xfd, 0x30, 0x09, 0x23, 0x3e, 0x64, 0x3b, 0xb4, 0x90, 0x55, 0x8e, 0x68, 0xf2, 0xb8, 0x8b,
- 0x7d, 0xcc, 0x87, 0xda, 0x2d, 0x3d, 0xa0, 0x5e, 0x5b, 0xf7, 0xb6, 0xe5, 0x6e, 0x63, 0xf0, 0x2f,
- 0x7c, 0x49, 0x21, 0x45, 0x5f, 0x18, 0x26, 0x61, 0xc4, 0x87, 0xb8, 0x50, 0x0b, 0xa4, 0xfa, 0xac,
- 0xcc, 0x80, 0x83, 0x33, 0x55, 0x8a, 0xb8, 0x2f, 0xfd, 0xab, 0xd5, 0x2b, 0x46, 0x3c, 0xc8, 0x4e,
- 0x22, 0x0e, 0xb0, 0x88, 0xfe, 0xdc, 0xc8, 0x61, 0x4e, 0x96, 0x50, 0xd1, 0x72, 0x93, 0x1d, 0x4f,
- 0x58, 0x7b, 0xbb, 0x12, 0x4d, 0xcb, 0xf7, 0xdb, 0xc8, 0xf5, 0x74, 0xd2, 0xc8, 0x75, 0x21, 0x6d,
- 0xe4, 0x4a, 0x9d, 0x6f, 0x1e, 0xde, 0x23, 0x33, 0x65, 0x5e, 0x2a, 0x1f, 0x83, 0x79, 0xe9, 0x29,
- 0xa8, 0xef, 0xf0, 0x99, 0x40, 0x84, 0xf6, 0xa9, 0xf0, 0x65, 0x84, 0xcf, 0xec, 0x37, 0xa3, 0x64,
- 0x8c, 0xd3, 0xb0, 0x22, 0xf2, 0xcf, 0x07, 0x61, 0x6c, 0x54, 0x59, 0xa4, 0x19, 0x25, 0x63, 0x9c,
- 0x86, 0x3b, 0x73, 0x59, 0xce, 0xb6, 0x28, 0x30, 0xcc, 0x0b, 0x08, 0x67, 0x2e, 0x95, 0x88, 0x51,
- 0x3e, 0xb9, 0x04, 0xd5, 0xae, 0xb9, 0x29, 0x68, 0xab, 0x9c, 0x96, 0x6b, 0x98, 0xeb, 0xf3, 0x0b,
- 0x32, 0xd4, 0x90, 0xca, 0x65, 0x35, 0x69, 0xeb, 0x1d, 0x95, 0xc1, 0xf7, 0x86, 0xb2, 0x26, 0x2b,
- 0x51, 0x32, 0xc6, 0x69, 0xc8, 0xc7, 0x61, 0xcc, 0xa3, 0x66, 0xd7, 0xa0, 0x61, 0x29, 0x61, 0x9d,
- 0x22, 0xe2, 0x17, 0x0f, 0xf1, 0x1c, 0x4c, 0x51, 0xf6, 0x31, 0x92, 0xd5, 0x07, 0x72, 0x3d, 0xfd,
- 0x51, 0x01, 0x48, 0xaf, 0xf3, 0x33, 0xd9, 0x82, 0x21, 0x87, 0x5b, 0xbf, 0x72, 0x47, 0x53, 0x8e,
- 0x19, 0xd1, 0xc4, 0xb4, 0x24, 0x13, 0x24, 0x3e, 0x71, 0xa0, 0x4a, 0x6f, 0x07, 0xd4, 0x73, 0xc2,
- 0xcb, 0x10, 0x47, 0x13, 0xb9, 0x59, 0xec, 0x06, 0x24, 0x32, 0x86, 0x3c, 0xb4, 0x9f, 0x14, 0xa1,
- 0x1e, 0xa3, 0xbb, 0xd7, 0xa6, 0x92, 0xdf, 0xc7, 0x16, 0x46, 0xa7, 0x75, 0xcf, 0x96, 0x23, 0x2c,
- 0x76, 0x1f, 0x5b, 0x66, 0xe1, 0x32, 0xc6, 0xe9, 0xc8, 0x0c, 0x40, 0x5b, 0xf7, 0x03, 0xea, 0xf1,
- 0xd5, 0x37, 0x75, 0x0b, 0x7a, 0x25, 0xcc, 0xc1, 0x18, 0x15, 0xb9, 0x28, 0x63, 0x6f, 0x97, 0x93,
- 0x51, 0xeb, 0xfa, 0x04, 0xd6, 0xae, 0x1c, 0x41, 0x60, 0x6d, 0xd2, 0x82, 0x71, 0x55, 0x6b, 0x95,
- 0x7b, 0xb8, 0x98, 0x66, 0x62, 0xff, 0x92, 0x82, 0xc0, 0x1e, 0x50, 0xed, 0xed, 0x02, 0x8c, 0x26,
- 0x4c, 0x1e, 0x22, 0xde, 0x9c, 0x72, 0xdd, 0x4f, 0xc4, 0x9b, 0x8b, 0x79, 0xdc, 0x3f, 0x09, 0x43,
- 0x42, 0x40, 0x69, 0x8f, 0x3c, 0x21, 0x42, 0x94, 0xb9, 0x6c, 0x2e, 0x93, 0x46, 0xd5, 0xf4, 0x5c,
- 0x26, 0xad, 0xae, 0xa8, 0xf2, 0x85, 0xad, 0x5e, 0xd4, 0xae, 0xd7, 0x56, 0x2f, 0xd2, 0x31, 0xa4,
- 0xd0, 0xfe, 0xad, 0x04, 0xdc, 0x7f, 0x85, 0x3c, 0x07, 0xb5, 0x36, 0x35, 0xb6, 0x74, 0xc7, 0xf2,
- 0x55, 0xbc, 0x49, 0xb6, 0xbb, 0xad, 0xad, 0xa8, 0xc4, 0x3b, 0x0c, 0x60, 0xb6, 0xb9, 0xcc, 0x5d,
- 0xc4, 0x23, 0x5a, 0x62, 0xc0, 0x50, 0xcb, 0xf7, 0xf5, 0x8e, 0x95, 0xfb, 0xf8, 0x54, 0xc4, 0xf7,
- 0x13, 0x83, 0x48, 0x3c, 0xa3, 0x84, 0x26, 0x06, 0x54, 0x3a, 0xb6, 0x6e, 0x39, 0xb9, 0x7f, 0x70,
- 0xc2, 0xbe, 0x60, 0x95, 0x21, 0x09, 0x93, 0x0e, 0x7f, 0x44, 0x81, 0x4d, 0xba, 0x50, 0xf7, 0x0d,
- 0x4f, 0x6f, 0xfb, 0x5b, 0xfa, 0xcc, 0x33, 0xcf, 0xe6, 0x56, 0x92, 0x22, 0x56, 0x62, 0xce, 0x9e,
- 0xc3, 0xd9, 0x95, 0xe6, 0x95, 0xd9, 0x99, 0x67, 0x9e, 0xc5, 0x38, 0x9f, 0x38, 0xdb, 0x67, 0x9e,
- 0x9a, 0x91, 0xfd, 0xfe, 0xc8, 0xd9, 0x3e, 0xf3, 0xd4, 0x0c, 0xc6, 0xf9, 0x68, 0xff, 0x5a, 0x80,
- 0x5a, 0x48, 0x4b, 0xd6, 0x01, 0xd8, 0x08, 0x94, 0x11, 0xf9, 0x0e, 0x15, 0x1d, 0x9f, 0xef, 0x8a,
- 0xd7, 0xc3, 0xc2, 0x18, 0x03, 0xca, 0x08, 0x59, 0x58, 0x3c, 0xea, 0x90, 0x85, 0xd3, 0x50, 0xdb,
- 0xd2, 0x1d, 0xd3, 0xdf, 0xd2, 0xb7, 0xc5, 0x44, 0x14, 0x0b, 0xe2, 0x79, 0x45, 0x65, 0x60, 0x44,
- 0xa3, 0xfd, 0xd1, 0x10, 0x88, 0x33, 0x4f, 0x36, 0x54, 0x4c, 0xcb, 0x17, 0x4e, 0xb7, 0x05, 0x5e,
- 0x32, 0x1c, 0x2a, 0xf3, 0x32, 0x1d, 0x43, 0x0a, 0x72, 0x16, 0x4a, 0x6d, 0xcb, 0x91, 0x27, 0x1e,
- 0xdc, 0xe0, 0xb5, 0x62, 0x39, 0xc8, 0xd2, 0x78, 0x96, 0x7e, 0x5b, 0xfa, 0x4b, 0x89, 0x2c, 0xfd,
- 0x36, 0xb2, 0x34, 0xb6, 0x05, 0xb5, 0x5d, 0x77, 0x7b, 0x43, 0x37, 0xb6, 0x95, 0x5b, 0x55, 0x99,
- 0x2f, 0x84, 0x7c, 0x0b, 0xba, 0x9c, 0xcc, 0xc2, 0x34, 0x2d, 0x59, 0x87, 0x47, 0xde, 0xa0, 0x9e,
- 0x2b, 0x47, 0x79, 0xd3, 0xa6, 0xb4, 0xa3, 0x60, 0x84, 0x0a, 0xc1, 0xbd, 0xb3, 0x3e, 0x97, 0x4d,
- 0x82, 0xfd, 0xca, 0x72, 0x3f, 0x4f, 0xdd, 0x6b, 0xd1, 0x60, 0xd5, 0x73, 0x0d, 0xea, 0xfb, 0x96,
- 0xd3, 0x52, 0xb0, 0x43, 0x11, 0xec, 0x5a, 0x36, 0x09, 0xf6, 0x2b, 0x4b, 0x5e, 0x86, 0x09, 0x91,
- 0x25, 0x16, 0xdb, 0xd9, 0x1d, 0xdd, 0xb2, 0xf5, 0x0d, 0xcb, 0x56, 0xff, 0x05, 0x1b, 0x15, 0xe7,
- 0x0a, 0x6b, 0x7d, 0x68, 0xb0, 0x6f, 0x69, 0x72, 0x15, 0xc6, 0xd5, 0xa9, 0xd2, 0x2a, 0xf5, 0x9a,
- 0xe1, 0x39, 0xf8, 0x68, 0xe3, 0x02, 0xdb, 0xef, 0xcd, 0xd3, 0x8e, 0x47, 0x0d, 0xae, 0x75, 0xa5,
- 0xa8, 0xb0, 0xa7, 0x1c, 0x41, 0x38, 0xc3, 0x0f, 0xbb, 0xd7, 0x3b, 0x73, 0xae, 0x6b, 0x9b, 0xee,
- 0x2d, 0x47, 0x7d, 0xbb, 0x50, 0x6c, 0xf8, 0x41, 0x52, 0x33, 0x93, 0x02, 0xfb, 0x94, 0x64, 0x5f,
- 0xce, 0x73, 0xe6, 0xdd, 0x5b, 0x4e, 0x1a, 0x15, 0xa2, 0x2f, 0x6f, 0xf6, 0xa1, 0xc1, 0xbe, 0xa5,
- 0xc9, 0x02, 0x90, 0xf4, 0x17, 0xac, 0x77, 0xb8, 0x32, 0x34, 0xda, 0x38, 0x23, 0x82, 0x6b, 0xa4,
- 0x73, 0x31, 0xa3, 0x04, 0x59, 0x86, 0xd3, 0xe9, 0x54, 0xc6, 0x8e, 0x7b, 0xd8, 0x8f, 0x8a, 0xb0,
- 0x9a, 0x98, 0x91, 0x8f, 0x99, 0xa5, 0xb4, 0x3f, 0x2e, 0xc2, 0x68, 0xe2, 0x36, 0xf6, 0x03, 0x77,
- 0xeb, 0x95, 0x69, 0xa0, 0x6d, 0xbf, 0xb5, 0x34, 0x7f, 0x85, 0xea, 0x26, 0xf5, 0xae, 0x51, 0x75,
- 0x73, 0x9e, 0x4f, 0x2a, 0x2b, 0x89, 0x1c, 0x4c, 0x51, 0x92, 0x4d, 0xa8, 0x08, 0x7b, 0x6a, 0xde,
- 0xff, 0x2c, 0x28, 0x19, 0x71, 0xa3, 0x2a, 0x5f, 0x72, 0x84, 0x49, 0x55, 0xc0, 0x6b, 0x01, 0x8c,
- 0xc4, 0x29, 0xd8, 0x44, 0x12, 0x29, 0x6b, 0xc3, 0x09, 0x45, 0x6d, 0x09, 0x4a, 0x41, 0x30, 0xe8,
- 0x7d, 0x5a, 0x61, 0x9f, 0x5f, 0x5b, 0x46, 0x86, 0xa1, 0x6d, 0xb2, 0xb6, 0xf3, 0x7d, 0xcb, 0x75,
- 0x64, 0x70, 0xe5, 0x75, 0x18, 0x0e, 0xa4, 0x89, 0x6a, 0xb0, 0xfb, 0xc0, 0xdc, 0x5c, 0xac, 0xcc,
- 0x53, 0x0a, 0x4b, 0xfb, 0x9b, 0x22, 0xd4, 0xc2, 0xed, 0xe4, 0x01, 0x82, 0x16, 0xbb, 0x50, 0x0b,
- 0x9d, 0x75, 0x72, 0xff, 0x33, 0x2d, 0xf2, 0x21, 0xe1, 0x3b, 0xa0, 0xf0, 0x15, 0x23, 0x1e, 0x71,
- 0x47, 0xa0, 0x52, 0x0e, 0x47, 0xa0, 0x0e, 0x0c, 0x07, 0x9e, 0xd5, 0x6a, 0x49, 0xdd, 0x36, 0x8f,
- 0x27, 0x50, 0x28, 0xae, 0x35, 0x01, 0x28, 0x25, 0x2b, 0x5e, 0x50, 0xb1, 0xd1, 0x5e, 0x83, 0xf1,
- 0x34, 0x25, 0x57, 0xfc, 0x8c, 0x2d, 0x6a, 0x76, 0x6d, 0x25, 0xe3, 0x48, 0xf1, 0x93, 0xe9, 0x18,
- 0x52, 0xb0, 0xcd, 0x1f, 0x6b, 0xa6, 0x37, 0x5c, 0x47, 0x6d, 0xab, 0xb9, 0x0e, 0xbd, 0x26, 0xd3,
- 0x30, 0xcc, 0xd5, 0xfe, 0xb1, 0x04, 0x67, 0x23, 0xa3, 0xc0, 0x8a, 0xee, 0xe8, 0xad, 0x03, 0xfc,
- 0x28, 0xeb, 0xfd, 0x1b, 0x16, 0x87, 0x8d, 0x3c, 0x5f, 0x7a, 0x00, 0x22, 0xcf, 0xff, 0xb4, 0x00,
- 0xdc, 0xb1, 0x90, 0x7c, 0x09, 0x46, 0xf4, 0xd8, 0x3f, 0x12, 0x65, 0x73, 0x5e, 0xce, 0xdd, 0x9c,
- 0xdc, 0x7f, 0x31, 0x74, 0x94, 0x89, 0xa7, 0x62, 0x82, 0x21, 0x71, 0xa1, 0xba, 0xa9, 0xdb, 0x36,
- 0xd3, 0x85, 0x72, 0x1f, 0x72, 0x24, 0x98, 0xf3, 0x6e, 0xbe, 0x20, 0xa1, 0x31, 0x64, 0xa2, 0xfd,
- 0x43, 0x01, 0x46, 0x9b, 0xb6, 0x65, 0x5a, 0x4e, 0xeb, 0x18, 0x43, 0xce, 0xdf, 0x80, 0x8a, 0x6f,
- 0x5b, 0x26, 0x1d, 0x70, 0x1e, 0x17, 0x2b, 0x08, 0x03, 0x40, 0x81, 0x93, 0x8c, 0x61, 0x5f, 0x3a,
- 0x40, 0x0c, 0xfb, 0x9f, 0x0f, 0x81, 0x74, 0x4e, 0x25, 0x5d, 0xa8, 0xb5, 0x54, 0x68, 0x6c, 0xf9,
- 0x8d, 0x57, 0x72, 0x84, 0x55, 0x4b, 0x04, 0xd9, 0x16, 0xb3, 0x6e, 0x98, 0x88, 0x11, 0x27, 0x42,
- 0x93, 0xbf, 0xc5, 0x9c, 0xcf, 0xf9, 0x5b, 0x4c, 0xc1, 0xae, 0xf7, 0xc7, 0x98, 0x3a, 0x94, 0xb7,
- 0x82, 0xa0, 0x23, 0xc7, 0xd5, 0xe0, 0xde, 0xc7, 0x51, 0x64, 0x0f, 0xa1, 0x8d, 0xb0, 0x77, 0xe4,
- 0xd0, 0x8c, 0x85, 0xa3, 0x87, 0x7f, 0x63, 0x9a, 0xcb, 0x75, 0xd0, 0x1d, 0x67, 0xc1, 0xde, 0x91,
- 0x43, 0x93, 0x2f, 0x42, 0x3d, 0xf0, 0x74, 0xc7, 0xdf, 0x74, 0xbd, 0x36, 0xf5, 0xe4, 0xee, 0x70,
- 0x21, 0xc7, 0x9f, 0x21, 0xd7, 0x22, 0x34, 0x71, 0x82, 0x96, 0x48, 0xc2, 0x38, 0x37, 0xb2, 0x0d,
- 0xd5, 0xae, 0x29, 0x2a, 0x26, 0xcd, 0x26, 0xb3, 0x79, 0x7e, 0xf6, 0x19, 0x3b, 0xc6, 0x56, 0x6f,
- 0x18, 0x32, 0x48, 0xfe, 0x78, 0x6c, 0xf8, 0xa8, 0x7e, 0x3c, 0x16, 0xef, 0x8d, 0x59, 0x61, 0x07,
- 0x48, 0x5b, 0x6a, 0x94, 0x4e, 0x4b, 0x7a, 0xe1, 0x2c, 0xe4, 0x56, 0xf6, 0x04, 0xcb, 0x7a, 0xa8,
- 0x95, 0x3a, 0x2d, 0x54, 0x3c, 0xb4, 0x36, 0x48, 0xeb, 0x36, 0x31, 0x12, 0xbf, 0xe7, 0x10, 0x77,
- 0x61, 0xa6, 0x0f, 0x36, 0x1f, 0x84, 0xff, 0x89, 0x88, 0x85, 0x07, 0xce, 0xfc, 0x0f, 0x87, 0xf6,
- 0xb7, 0x45, 0x28, 0xad, 0x2d, 0x37, 0x45, 0xc8, 0x3f, 0xfe, 0xef, 0x1b, 0xda, 0xdc, 0xb6, 0x3a,
- 0x37, 0xa9, 0x67, 0x6d, 0xee, 0xca, 0x4d, 0x6f, 0x2c, 0xe4, 0x5f, 0x9a, 0x02, 0x33, 0x4a, 0x91,
- 0x57, 0x60, 0xc4, 0xd0, 0xe7, 0xa8, 0x17, 0x0c, 0xb2, 0xa5, 0xe7, 0x97, 0xfe, 0xe6, 0x66, 0xa3,
- 0xe2, 0x98, 0x00, 0x23, 0xeb, 0x00, 0x46, 0x04, 0x5d, 0x3a, 0xb4, 0x21, 0x22, 0x06, 0x1c, 0x03,
- 0x22, 0x08, 0xb5, 0x6d, 0x46, 0xca, 0x51, 0xcb, 0x87, 0x41, 0xe5, 0x3d, 0xe7, 0x9a, 0x2a, 0x8b,
- 0x11, 0x8c, 0xe6, 0xc0, 0x68, 0xe2, 0x9f, 0x1d, 0xe4, 0x63, 0x50, 0x75, 0x3b, 0xb1, 0xe9, 0xb4,
- 0xc6, 0xfd, 0xfd, 0xaa, 0x37, 0x64, 0xda, 0x9d, 0xbd, 0xc9, 0xd1, 0x65, 0xb7, 0x65, 0x19, 0x2a,
- 0x01, 0x43, 0x72, 0xa2, 0xc1, 0x10, 0xbf, 0xa9, 0xa3, 0xfe, 0xd8, 0xc1, 0xd7, 0x0e, 0x1e, 0x54,
- 0xdf, 0x47, 0x99, 0xa3, 0x7d, 0xb9, 0x0c, 0xd1, 0x99, 0x10, 0xf1, 0x61, 0x48, 0x78, 0x22, 0xcb,
- 0x99, 0xfb, 0x58, 0x9d, 0x9e, 0x25, 0x2b, 0xd2, 0x82, 0xd2, 0x6b, 0xee, 0x46, 0xee, 0x89, 0x3b,
- 0x76, 0x45, 0x57, 0x58, 0xa9, 0x62, 0x09, 0xc8, 0x38, 0x90, 0x5f, 0x2d, 0xc0, 0x49, 0x3f, 0xad,
- 0x74, 0xca, 0xee, 0x80, 0xf9, 0xb5, 0xeb, 0xb4, 0x1a, 0x2b, 0x1d, 0x33, 0xfb, 0x65, 0x63, 0x6f,
- 0x5d, 0x98, 0xfc, 0xc5, 0x61, 0x8d, 0xec, 0x4e, 0x8b, 0x39, 0xff, 0x33, 0x97, 0x94, 0x7f, 0x32,
- 0x0d, 0x25, 0x2b, 0xed, 0xab, 0x45, 0xa8, 0xc7, 0x66, 0xeb, 0xdc, 0x3f, 0x82, 0xb9, 0x9d, 0xfa,
- 0x11, 0xcc, 0xea, 0xe0, 0x67, 0x97, 0x51, 0xad, 0x8e, 0xfb, 0x5f, 0x30, 0xdf, 0x2f, 0x42, 0x69,
- 0x7d, 0x7e, 0x21, 0xb9, 0x5d, 0x2c, 0xdc, 0x87, 0xed, 0xe2, 0x16, 0x0c, 0x6f, 0x74, 0x2d, 0x3b,
- 0xb0, 0x9c, 0xdc, 0x41, 0x04, 0xd4, 0x7f, 0x73, 0xe4, 0x5d, 0x5c, 0x81, 0x8a, 0x0a, 0x9e, 0xb4,
- 0x60, 0xb8, 0x25, 0xa2, 0xb8, 0xe5, 0xf6, 0xe8, 0x92, 0xd1, 0xe0, 0x04, 0x23, 0xf9, 0x82, 0x0a,
- 0x5d, 0xdb, 0x05, 0xf9, 0xe7, 0xed, 0xfb, 0x2e, 0x4d, 0xed, 0x8b, 0x10, 0x6a, 0x01, 0xf7, 0x9f,
- 0xf9, 0x3f, 0x17, 0x20, 0xa9, 0xf8, 0xdc, 0xff, 0xde, 0xb4, 0x9d, 0xee, 0x4d, 0xf3, 0x47, 0x31,
- 0xf8, 0xb2, 0x3b, 0x94, 0xf6, 0x87, 0x45, 0x18, 0xba, 0x6f, 0x17, 0x3f, 0x69, 0xc2, 0x39, 0x6d,
- 0x2e, 0xe7, 0xc4, 0xd8, 0xd7, 0x35, 0xad, 0x9d, 0x72, 0x4d, 0xcb, 0xfb, 0xa7, 0xcf, 0x7b, 0x38,
- 0xa6, 0xfd, 0x65, 0x01, 0xe4, 0xb4, 0xbc, 0xe4, 0xf8, 0x81, 0xee, 0x18, 0xfc, 0x87, 0xf3, 0x72,
- 0x0d, 0xc8, 0xeb, 0x01, 0x21, 0xbd, 0x84, 0xc4, 0xb2, 0xcf, 0x9f, 0xd5, 0x9c, 0x4f, 0x3e, 0x02,
- 0xd5, 0x2d, 0xd7, 0x0f, 0xf8, 0x3c, 0x5f, 0x4c, 0xda, 0x75, 0xae, 0xc8, 0x74, 0x0c, 0x29, 0xd2,
- 0x27, 0x85, 0x95, 0xfe, 0x27, 0x85, 0xda, 0xb7, 0x8b, 0x30, 0xf2, 0x5e, 0xb9, 0xbd, 0x9a, 0xe5,
- 0xca, 0x57, 0xca, 0xe9, 0xca, 0x57, 0x3e, 0x8c, 0x2b, 0x9f, 0xf6, 0x83, 0x02, 0xc0, 0x7d, 0xbb,
- 0x3a, 0x6b, 0x26, 0xbd, 0xec, 0x72, 0xf7, 0xab, 0x6c, 0x1f, 0xbb, 0xdf, 0xab, 0xa8, 0x4f, 0xe2,
- 0x1e, 0x76, 0x6f, 0x16, 0x60, 0x4c, 0x4f, 0x78, 0xad, 0xe5, 0x56, 0x2d, 0x53, 0x4e, 0x70, 0xe1,
- 0x35, 0xc1, 0x64, 0x3a, 0xa6, 0xd8, 0x92, 0xe7, 0xa3, 0xb0, 0xad, 0xd7, 0xa3, 0x6e, 0xdf, 0x13,
- 0x6f, 0x95, 0xab, 0x39, 0x09, 0xca, 0x7b, 0x78, 0x09, 0x96, 0x8e, 0xc4, 0x4b, 0x30, 0x7e, 0xff,
- 0xa9, 0x7c, 0xd7, 0xfb, 0x4f, 0x3b, 0x50, 0xdb, 0xf4, 0xdc, 0x36, 0x77, 0xc4, 0x93, 0xff, 0x08,
- 0xbd, 0x9c, 0x63, 0x4d, 0x89, 0xfe, 0x8e, 0x1d, 0xd9, 0x78, 0x16, 0x14, 0x3e, 0x46, 0xac, 0xb8,
- 0x41, 0xda, 0x15, 0x5c, 0x87, 0x8e, 0x92, 0x6b, 0x38, 0x97, 0xac, 0x09, 0x74, 0x54, 0x6c, 0x92,
- 0xce, 0x77, 0xc3, 0xf7, 0xc7, 0xf9, 0x4e, 0xfb, 0x7e, 0x59, 0x4d, 0x60, 0x0f, 0x5c, 0x84, 0xc0,
- 0xf7, 0xfe, 0x95, 0xcb, 0xf4, 0x7d, 0xc8, 0xe1, 0xfb, 0x78, 0x1f, 0xb2, 0x7a, 0x34, 0xf7, 0x21,
- 0x6b, 0x87, 0xb8, 0x0f, 0xb9, 0x57, 0x82, 0xd4, 0xa6, 0xeb, 0xfd, 0xa3, 0x8d, 0xff, 0x50, 0x47,
- 0x1b, 0x6f, 0x15, 0x21, 0x9a, 0x45, 0x0e, 0xe9, 0xfa, 0xf1, 0x32, 0x54, 0xdb, 0xfa, 0xed, 0x79,
- 0x6a, 0xeb, 0xbb, 0x79, 0xfe, 0x0a, 0xb9, 0x22, 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80, 0x15, 0x46,
- 0x66, 0xce, 0x6d, 0xaa, 0x8e, 0x82, 0x3c, 0x0b, 0x63, 0x58, 0xf4, 0x8e, 0x31, 0x36, 0xda, 0x5f,
- 0x14, 0x41, 0x86, 0xf0, 0x26, 0x14, 0x2a, 0x9b, 0xd6, 0x6d, 0x6a, 0xe6, 0x76, 0x83, 0x8c, 0xfd,
- 0xab, 0x57, 0xd8, 0xe2, 0x79, 0x02, 0x0a, 0x74, 0x6e, 0x64, 0x15, 0x67, 0x2b, 0x52, 0x7e, 0x39,
- 0x8c, 0xac, 0xf1, 0x33, 0x1a, 0x69, 0x64, 0x15, 0x49, 0xa8, 0x78, 0x08, 0x9b, 0x2e, 0x3f, 0xe0,
- 0x96, 0x22, 0xcd, 0x63, 0xd3, 0x8d, 0x1d, 0x94, 0x2b, 0x9b, 0xae, 0x2f, 0x2e, 0x44, 0x4b, 0x1e,
- 0x8d, 0xcf, 0x7f, 0xef, 0x87, 0x17, 0x1e, 0xfa, 0xc1, 0x0f, 0x2f, 0x3c, 0xf4, 0xce, 0x0f, 0x2f,
- 0x3c, 0xf4, 0xe5, 0xfd, 0x0b, 0x85, 0xef, 0xed, 0x5f, 0x28, 0xfc, 0x60, 0xff, 0x42, 0xe1, 0x9d,
- 0xfd, 0x0b, 0x85, 0xbf, 0xdb, 0xbf, 0x50, 0xf8, 0xa5, 0xbf, 0xbf, 0xf0, 0xd0, 0xe7, 0x9e, 0x8b,
- 0xaa, 0x30, 0xad, 0xaa, 0x30, 0xad, 0x18, 0x4e, 0x77, 0xb6, 0x5b, 0xd3, 0xac, 0x0a, 0x51, 0x8a,
- 0xaa, 0xc2, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x80, 0x4f, 0xc1, 0x9f, 0x93, 0x00, 0x00,
+ // 7611 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0xd7,
+ 0x75, 0xa6, 0xfa, 0x8f, 0xec, 0x3e, 0x4d, 0x72, 0x38, 0x77, 0x46, 0x23, 0xce, 0x68, 0x34, 0x1c,
+ 0x97, 0x2c, 0x79, 0xbc, 0xb6, 0xc9, 0x15, 0x57, 0x7f, 0xfe, 0x95, 0xd8, 0xe4, 0x90, 0xc3, 0x19,
+ 0x72, 0x86, 0x3e, 0x4d, 0x8e, 0x64, 0x6b, 0x6d, 0x6d, 0xb1, 0xea, 0xb2, 0x59, 0x62, 0x75, 0x55,
+ 0xab, 0xaa, 0x9a, 0x33, 0x94, 0x77, 0xe1, 0x1f, 0x2d, 0x20, 0x2d, 0x16, 0x8b, 0x5d, 0xf8, 0xc9,
+ 0xc0, 0xc2, 0xbb, 0xd8, 0xc5, 0x02, 0x7e, 0x30, 0xbc, 0x0f, 0x8b, 0xd5, 0x3e, 0x2c, 0x90, 0x38,
+ 0x0e, 0x82, 0xc4, 0x0e, 0xf2, 0xe3, 0x87, 0x00, 0x51, 0x5e, 0x88, 0x98, 0x41, 0x1e, 0x12, 0x20,
+ 0x86, 0x11, 0x03, 0x89, 0x3d, 0x30, 0xe2, 0xe0, 0xfe, 0xd5, 0x5f, 0x57, 0xcf, 0x90, 0x5d, 0xe4,
+ 0x68, 0x94, 0xe8, 0xad, 0xea, 0xdc, 0x73, 0xbf, 0x73, 0xef, 0xa9, 0xfb, 0x73, 0xee, 0xb9, 0xe7,
+ 0xde, 0x82, 0xc5, 0x96, 0x15, 0x6c, 0x75, 0x37, 0xa6, 0x0c, 0xb7, 0x3d, 0xed, 0x74, 0xdb, 0x7a,
+ 0xc7, 0x73, 0x5f, 0xe3, 0x0f, 0x9b, 0xb6, 0x7b, 0x6b, 0xba, 0xb3, 0xdd, 0x9a, 0xd6, 0x3b, 0x96,
+ 0x1f, 0x51, 0x76, 0x9e, 0xd2, 0xed, 0xce, 0x96, 0xfe, 0xd4, 0x74, 0x8b, 0x3a, 0xd4, 0xd3, 0x03,
+ 0x6a, 0x4e, 0x75, 0x3c, 0x37, 0x70, 0xc9, 0x73, 0x11, 0xd0, 0x94, 0x02, 0x9a, 0x52, 0xd9, 0xa6,
+ 0x3a, 0xdb, 0xad, 0x29, 0x06, 0x14, 0x51, 0x14, 0xd0, 0xb9, 0x4f, 0xc4, 0x4a, 0xd0, 0x72, 0x5b,
+ 0xee, 0x34, 0xc7, 0xdb, 0xe8, 0x6e, 0xf2, 0x37, 0xfe, 0xc2, 0x9f, 0x84, 0x9c, 0x73, 0xda, 0xf6,
+ 0xf3, 0xfe, 0x94, 0xe5, 0xb2, 0x62, 0x4d, 0x1b, 0xae, 0x47, 0xa7, 0x77, 0x7a, 0xca, 0x72, 0xee,
+ 0xe9, 0x88, 0xa7, 0xad, 0x1b, 0x5b, 0x96, 0x43, 0xbd, 0x5d, 0x55, 0x97, 0x69, 0x8f, 0xfa, 0x6e,
+ 0xd7, 0x33, 0xe8, 0xa1, 0x72, 0xf9, 0xd3, 0x6d, 0x1a, 0xe8, 0x59, 0xb2, 0xa6, 0xfb, 0xe5, 0xf2,
+ 0xba, 0x4e, 0x60, 0xb5, 0x7b, 0xc5, 0x3c, 0x7b, 0xaf, 0x0c, 0xbe, 0xb1, 0x45, 0xdb, 0x7a, 0x3a,
+ 0x9f, 0xf6, 0x03, 0x80, 0x53, 0xb3, 0x1b, 0x7e, 0xe0, 0xe9, 0x46, 0xb0, 0xea, 0x9a, 0x6b, 0xb4,
+ 0xdd, 0xb1, 0xf5, 0x80, 0x92, 0x6d, 0xa8, 0xb2, 0xb2, 0x99, 0x7a, 0xa0, 0x4f, 0x14, 0x2e, 0x16,
+ 0x2e, 0xd5, 0x67, 0x66, 0xa7, 0x06, 0xfc, 0x16, 0x53, 0x2b, 0x12, 0xa8, 0x31, 0xb2, 0xbf, 0x37,
+ 0x59, 0x55, 0x6f, 0x18, 0x0a, 0x20, 0xdf, 0x2a, 0xc0, 0x88, 0xe3, 0x9a, 0xb4, 0x49, 0x6d, 0x6a,
+ 0x04, 0xae, 0x37, 0x51, 0xbc, 0x58, 0xba, 0x54, 0x9f, 0xf9, 0xf2, 0xc0, 0x12, 0x33, 0x6a, 0x34,
+ 0x75, 0x3d, 0x26, 0xe0, 0xb2, 0x13, 0x78, 0xbb, 0x8d, 0xd3, 0x3f, 0xdc, 0x9b, 0x7c, 0x68, 0x7f,
+ 0x6f, 0x72, 0x24, 0x9e, 0x84, 0x89, 0x92, 0x90, 0x75, 0xa8, 0x07, 0xae, 0xcd, 0x54, 0x66, 0xb9,
+ 0x8e, 0x3f, 0x51, 0xe2, 0x05, 0xbb, 0x30, 0x25, 0xb4, 0xcd, 0xc4, 0x4f, 0xb1, 0xe6, 0x32, 0xb5,
+ 0xf3, 0xd4, 0xd4, 0x5a, 0xc8, 0xd6, 0x38, 0x25, 0x81, 0xeb, 0x11, 0xcd, 0xc7, 0x38, 0x0e, 0xa1,
+ 0x70, 0xc2, 0xa7, 0x46, 0xd7, 0xb3, 0x82, 0xdd, 0x39, 0xd7, 0x09, 0xe8, 0xed, 0x60, 0xa2, 0xcc,
+ 0xb5, 0xfc, 0x64, 0x16, 0xf4, 0xaa, 0x6b, 0x36, 0x93, 0xdc, 0x8d, 0x53, 0xfb, 0x7b, 0x93, 0x27,
+ 0x52, 0x44, 0x4c, 0x63, 0x12, 0x07, 0xc6, 0xad, 0xb6, 0xde, 0xa2, 0xab, 0x5d, 0xdb, 0x6e, 0x52,
+ 0xc3, 0xa3, 0x81, 0x3f, 0x51, 0xe1, 0x55, 0xb8, 0x94, 0x25, 0x67, 0xd9, 0x35, 0x74, 0xfb, 0xc6,
+ 0xc6, 0x6b, 0xd4, 0x08, 0x90, 0x6e, 0x52, 0x8f, 0x3a, 0x06, 0x6d, 0x4c, 0xc8, 0xca, 0x8c, 0x2f,
+ 0xa5, 0x90, 0xb0, 0x07, 0x9b, 0x2c, 0xc2, 0xc9, 0x8e, 0x67, 0xb9, 0xbc, 0x08, 0xb6, 0xee, 0xfb,
+ 0xd7, 0xf5, 0x36, 0x9d, 0x18, 0xba, 0x58, 0xb8, 0x54, 0x6b, 0x9c, 0x95, 0x30, 0x27, 0x57, 0xd3,
+ 0x0c, 0xd8, 0x9b, 0x87, 0x5c, 0x82, 0xaa, 0x22, 0x4e, 0x0c, 0x5f, 0x2c, 0x5c, 0xaa, 0x88, 0xb6,
+ 0xa3, 0xf2, 0x62, 0x98, 0x4a, 0x16, 0xa0, 0xaa, 0x6f, 0x6e, 0x5a, 0x0e, 0xe3, 0xac, 0x72, 0x15,
+ 0x9e, 0xcf, 0xaa, 0xda, 0xac, 0xe4, 0x11, 0x38, 0xea, 0x0d, 0xc3, 0xbc, 0xe4, 0x2a, 0x10, 0x9f,
+ 0x7a, 0x3b, 0x96, 0x41, 0x67, 0x0d, 0xc3, 0xed, 0x3a, 0x01, 0x2f, 0x7b, 0x8d, 0x97, 0xfd, 0x9c,
+ 0x2c, 0x3b, 0x69, 0xf6, 0x70, 0x60, 0x46, 0x2e, 0xf2, 0x22, 0x8c, 0xcb, 0x6e, 0x17, 0x69, 0x01,
+ 0x38, 0xd2, 0x69, 0xa6, 0x48, 0x4c, 0xa5, 0x61, 0x0f, 0x37, 0x31, 0xe1, 0xbc, 0xde, 0x0d, 0xdc,
+ 0x36, 0x83, 0x4c, 0x0a, 0x5d, 0x73, 0xb7, 0xa9, 0x33, 0x51, 0xbf, 0x58, 0xb8, 0x54, 0x6d, 0x5c,
+ 0xdc, 0xdf, 0x9b, 0x3c, 0x3f, 0x7b, 0x17, 0x3e, 0xbc, 0x2b, 0x0a, 0xb9, 0x01, 0x35, 0xd3, 0xf1,
+ 0x57, 0x5d, 0xdb, 0x32, 0x76, 0x27, 0x46, 0x78, 0x01, 0x9f, 0x92, 0x55, 0xad, 0xcd, 0x5f, 0x6f,
+ 0x8a, 0x84, 0x3b, 0x7b, 0x93, 0xe7, 0x7b, 0x47, 0xc7, 0xa9, 0x30, 0x1d, 0x23, 0x0c, 0xb2, 0xc2,
+ 0x01, 0xe7, 0x5c, 0x67, 0xd3, 0x6a, 0x4d, 0x8c, 0xf2, 0xaf, 0x71, 0xb1, 0x4f, 0x83, 0x9e, 0xbf,
+ 0xde, 0x14, 0x7c, 0x8d, 0x51, 0x29, 0x4e, 0xbc, 0x62, 0x84, 0x40, 0x4c, 0x18, 0x53, 0xe3, 0xea,
+ 0x9c, 0xad, 0x5b, 0x6d, 0x7f, 0x62, 0x8c, 0x37, 0xde, 0x0f, 0xf7, 0xc1, 0xc4, 0x38, 0x73, 0xe3,
+ 0x8c, 0xac, 0xca, 0x58, 0x82, 0xec, 0x63, 0x0a, 0xf3, 0xdc, 0x0b, 0x70, 0xb2, 0x67, 0x6c, 0x20,
+ 0xe3, 0x50, 0xda, 0xa6, 0xbb, 0x7c, 0xe8, 0xab, 0x21, 0x7b, 0x24, 0xa7, 0xa1, 0xb2, 0xa3, 0xdb,
+ 0x5d, 0x3a, 0x51, 0xe4, 0x34, 0xf1, 0xf2, 0xa9, 0xe2, 0xf3, 0x05, 0xed, 0x7f, 0x96, 0x60, 0x44,
+ 0x8d, 0x38, 0x4d, 0xcb, 0xd9, 0x26, 0x2f, 0x41, 0xc9, 0x76, 0x5b, 0x72, 0xdc, 0xfc, 0xcc, 0xc0,
+ 0xa3, 0xd8, 0xb2, 0xdb, 0x6a, 0x0c, 0xef, 0xef, 0x4d, 0x96, 0x96, 0xdd, 0x16, 0x32, 0x44, 0x62,
+ 0x40, 0x65, 0x5b, 0xdf, 0xdc, 0xd6, 0x79, 0x19, 0xea, 0x33, 0x8d, 0x81, 0xa1, 0xaf, 0x31, 0x14,
+ 0x56, 0xd6, 0x46, 0x6d, 0x7f, 0x6f, 0xb2, 0xc2, 0x5f, 0x51, 0x60, 0x13, 0x17, 0x6a, 0x1b, 0xb6,
+ 0x6e, 0x6c, 0x6f, 0xb9, 0x36, 0x9d, 0x28, 0xe5, 0x14, 0xd4, 0x50, 0x48, 0xe2, 0x33, 0x87, 0xaf,
+ 0x18, 0xc9, 0x20, 0x06, 0x0c, 0x75, 0x4d, 0xdf, 0x72, 0xb6, 0xe5, 0x18, 0xf8, 0xc2, 0xc0, 0xd2,
+ 0xd6, 0xe7, 0x79, 0x9d, 0x60, 0x7f, 0x6f, 0x72, 0x48, 0x3c, 0xa3, 0x84, 0xd6, 0x7e, 0x5a, 0x87,
+ 0x31, 0xf5, 0x91, 0x6e, 0x52, 0x2f, 0xa0, 0xb7, 0xc9, 0x45, 0x28, 0x3b, 0xac, 0x6b, 0xf2, 0x8f,
+ 0xdc, 0x18, 0x91, 0xcd, 0xa5, 0xcc, 0xbb, 0x24, 0x4f, 0x61, 0x25, 0x13, 0x4d, 0x45, 0x2a, 0x7c,
+ 0xf0, 0x92, 0x35, 0x39, 0x8c, 0x28, 0x99, 0x78, 0x46, 0x09, 0x4d, 0x5e, 0x81, 0x32, 0xaf, 0xbc,
+ 0x50, 0xf5, 0x67, 0x07, 0x17, 0xc1, 0xaa, 0x5e, 0x65, 0x35, 0xe0, 0x15, 0xe7, 0xa0, 0xac, 0x29,
+ 0x76, 0xcd, 0x4d, 0xa9, 0xd8, 0xcf, 0xe4, 0x50, 0xec, 0x82, 0x68, 0x8a, 0xeb, 0xf3, 0x0b, 0xc8,
+ 0x10, 0xc9, 0x7f, 0x2e, 0xc0, 0x49, 0xc3, 0x75, 0x02, 0x9d, 0x99, 0x1a, 0x6a, 0x92, 0x9d, 0xa8,
+ 0x70, 0x39, 0x57, 0x07, 0x96, 0x33, 0x97, 0x46, 0x6c, 0x3c, 0xcc, 0xe6, 0x8c, 0x1e, 0x32, 0xf6,
+ 0xca, 0x26, 0xff, 0xb5, 0x00, 0x0f, 0xb3, 0xb1, 0xbc, 0x87, 0x99, 0xcf, 0x40, 0x47, 0x5b, 0xaa,
+ 0xb3, 0xfb, 0x7b, 0x93, 0x0f, 0x2f, 0x65, 0x09, 0xc3, 0xec, 0x32, 0xb0, 0xd2, 0x9d, 0xd2, 0x7b,
+ 0xcd, 0x12, 0x3e, 0xbb, 0xd5, 0x67, 0x96, 0x8f, 0xd2, 0xd4, 0x69, 0x3c, 0x2a, 0x9b, 0x72, 0x96,
+ 0x65, 0x87, 0x59, 0xa5, 0x20, 0x97, 0x61, 0x78, 0xc7, 0xb5, 0xbb, 0x6d, 0xea, 0x4f, 0x54, 0xf9,
+ 0x10, 0x7b, 0x2e, 0x6b, 0x88, 0xbd, 0xc9, 0x59, 0x1a, 0x27, 0x24, 0xfc, 0xb0, 0x78, 0xf7, 0x51,
+ 0xe5, 0x25, 0x16, 0x0c, 0xd9, 0x56, 0xdb, 0x0a, 0x7c, 0x3e, 0x71, 0xd6, 0x67, 0x2e, 0x0f, 0x5c,
+ 0x2d, 0xd1, 0x45, 0x97, 0x39, 0x98, 0xe8, 0x35, 0xe2, 0x19, 0xa5, 0x00, 0x36, 0x14, 0xfa, 0x86,
+ 0x6e, 0x8b, 0x89, 0xb5, 0x3e, 0xf3, 0xb9, 0xc1, 0xbb, 0x0d, 0x43, 0x69, 0x8c, 0xca, 0x3a, 0x55,
+ 0xf8, 0x2b, 0x0a, 0x6c, 0xf2, 0x25, 0x18, 0x4b, 0x7c, 0x4d, 0x7f, 0xa2, 0xce, 0xb5, 0xf3, 0x58,
+ 0x96, 0x76, 0x42, 0xae, 0x68, 0xe6, 0x49, 0xb4, 0x10, 0x1f, 0x53, 0x60, 0xe4, 0x1a, 0x54, 0x7d,
+ 0xcb, 0xa4, 0x86, 0xee, 0xf9, 0x13, 0x23, 0x07, 0x01, 0x1e, 0x97, 0xc0, 0xd5, 0xa6, 0xcc, 0x86,
+ 0x21, 0x00, 0x99, 0x02, 0xe8, 0xe8, 0x5e, 0x60, 0x09, 0x43, 0x75, 0x94, 0x1b, 0x4d, 0x63, 0xfb,
+ 0x7b, 0x93, 0xb0, 0x1a, 0x52, 0x31, 0xc6, 0xc1, 0xf8, 0x59, 0xde, 0x25, 0xa7, 0xd3, 0x0d, 0xc4,
+ 0xc4, 0x5a, 0x13, 0xfc, 0xcd, 0x90, 0x8a, 0x31, 0x0e, 0xf2, 0xbd, 0x02, 0x3c, 0x1a, 0xbd, 0xf6,
+ 0x76, 0xb2, 0x13, 0x47, 0xde, 0xc9, 0x26, 0xf7, 0xf7, 0x26, 0x1f, 0x6d, 0xf6, 0x17, 0x89, 0x77,
+ 0x2b, 0x8f, 0xf6, 0x12, 0x8c, 0xce, 0x76, 0x83, 0x2d, 0xd7, 0xb3, 0xde, 0xe0, 0x46, 0x37, 0x59,
+ 0x80, 0x4a, 0xc0, 0x8d, 0x27, 0x31, 0x2f, 0x3f, 0x91, 0xa5, 0x6a, 0x61, 0xc8, 0x5e, 0xa3, 0xbb,
+ 0xca, 0x1a, 0x10, 0xf3, 0xa3, 0x30, 0xa6, 0x44, 0x76, 0xed, 0xdf, 0x17, 0x60, 0xb8, 0xa1, 0x1b,
+ 0xdb, 0xee, 0xe6, 0x26, 0x79, 0x19, 0xaa, 0x96, 0x13, 0x50, 0x6f, 0x47, 0xb7, 0x25, 0xec, 0x54,
+ 0x0c, 0x36, 0x5c, 0x89, 0x45, 0xf5, 0x66, 0x6b, 0x1e, 0x26, 0x68, 0xbe, 0x2b, 0xd7, 0x0a, 0xdc,
+ 0x1e, 0x5d, 0x92, 0x18, 0x18, 0xa2, 0x91, 0x49, 0xa8, 0xf8, 0x01, 0xed, 0xf8, 0x7c, 0xe6, 0x19,
+ 0x15, 0xc5, 0x68, 0x32, 0x02, 0x0a, 0xba, 0xf6, 0x3f, 0x0a, 0x50, 0x6b, 0xe8, 0xbe, 0x65, 0xb0,
+ 0x5a, 0x92, 0x39, 0x28, 0x77, 0x7d, 0xea, 0x1d, 0xae, 0x6e, 0x7c, 0xb2, 0x58, 0xf7, 0xa9, 0x87,
+ 0x3c, 0x33, 0xb9, 0x01, 0xd5, 0x8e, 0xee, 0xfb, 0xb7, 0x5c, 0xcf, 0x94, 0x13, 0xde, 0x01, 0x81,
+ 0x84, 0x71, 0x2e, 0xb3, 0x62, 0x08, 0xa2, 0xd5, 0x21, 0x9a, 0xf1, 0xb5, 0x9f, 0x17, 0xe0, 0x54,
+ 0xa3, 0xbb, 0xb9, 0x49, 0x3d, 0x69, 0x8b, 0x4a, 0x2b, 0x8f, 0x42, 0xc5, 0xa3, 0xa6, 0xe5, 0xcb,
+ 0xb2, 0xcf, 0x0f, 0xdc, 0x82, 0x90, 0xa1, 0x48, 0xa3, 0x92, 0xeb, 0x8b, 0x13, 0x50, 0xa0, 0x93,
+ 0x2e, 0xd4, 0x5e, 0xa3, 0x81, 0x1f, 0x78, 0x54, 0x6f, 0xcb, 0xda, 0x5d, 0x19, 0x58, 0xd4, 0x55,
+ 0x1a, 0x34, 0x39, 0x52, 0xdc, 0x86, 0x0d, 0x89, 0x18, 0x49, 0xd2, 0x7e, 0x50, 0x81, 0x91, 0x39,
+ 0xb7, 0xbd, 0x61, 0x39, 0xd4, 0xbc, 0x6c, 0xb6, 0x28, 0x79, 0x15, 0xca, 0xd4, 0x6c, 0x51, 0x59,
+ 0xdb, 0xc1, 0xa7, 0x7b, 0x06, 0x16, 0x19, 0x2d, 0xec, 0x0d, 0x39, 0x30, 0x59, 0x86, 0xb1, 0x4d,
+ 0xcf, 0x6d, 0x8b, 0x11, 0x74, 0x6d, 0xb7, 0x23, 0x2d, 0xd6, 0xc6, 0x87, 0xd5, 0xa8, 0xb4, 0x90,
+ 0x48, 0xbd, 0xb3, 0x37, 0x09, 0xd1, 0x1b, 0xa6, 0xf2, 0x92, 0x97, 0x61, 0x22, 0xa2, 0x84, 0x43,
+ 0xc9, 0x1c, 0x5b, 0x44, 0x70, 0x8b, 0xa5, 0xd2, 0x38, 0xbf, 0xbf, 0x37, 0x39, 0xb1, 0xd0, 0x87,
+ 0x07, 0xfb, 0xe6, 0x26, 0x6f, 0x15, 0x60, 0x3c, 0x4a, 0x14, 0xc3, 0xbb, 0x34, 0x54, 0x8e, 0x68,
+ 0xde, 0xe0, 0xab, 0xad, 0x85, 0x94, 0x08, 0xec, 0x11, 0x4a, 0x16, 0x60, 0x24, 0x70, 0x63, 0xfa,
+ 0xaa, 0x70, 0x7d, 0x69, 0xca, 0x3d, 0xb0, 0xe6, 0xf6, 0xd5, 0x56, 0x22, 0x1f, 0x41, 0x38, 0xa3,
+ 0xde, 0x53, 0x9a, 0x1a, 0xe2, 0x9a, 0x3a, 0xb7, 0xbf, 0x37, 0x79, 0x66, 0x2d, 0x93, 0x03, 0xfb,
+ 0xe4, 0x24, 0x5f, 0x2f, 0xc0, 0x98, 0x4a, 0x92, 0x3a, 0x1a, 0x3e, 0x4a, 0x1d, 0x11, 0xd6, 0x22,
+ 0xd6, 0x12, 0x02, 0x30, 0x25, 0x50, 0xfb, 0x45, 0x19, 0x6a, 0xe1, 0x00, 0x4b, 0x1e, 0x87, 0x0a,
+ 0x5f, 0xf8, 0x4b, 0xbb, 0x39, 0x9c, 0x39, 0xb9, 0x7f, 0x00, 0x45, 0x1a, 0x79, 0x02, 0x86, 0x0d,
+ 0xb7, 0xdd, 0xd6, 0x1d, 0x93, 0x3b, 0x73, 0x6a, 0x8d, 0x3a, 0x33, 0x18, 0xe6, 0x04, 0x09, 0x55,
+ 0x1a, 0x39, 0x0f, 0x65, 0xdd, 0x6b, 0x09, 0xbf, 0x4a, 0x4d, 0x8c, 0x47, 0xb3, 0x5e, 0xcb, 0x47,
+ 0x4e, 0x25, 0x9f, 0x84, 0x12, 0x75, 0x76, 0x26, 0xca, 0xfd, 0x2d, 0x92, 0xcb, 0xce, 0xce, 0x4d,
+ 0xdd, 0x6b, 0xd4, 0x65, 0x19, 0x4a, 0x97, 0x9d, 0x1d, 0x64, 0x79, 0xc8, 0x32, 0x0c, 0x53, 0x67,
+ 0x87, 0x7d, 0x7b, 0xe9, 0xf0, 0xf8, 0x50, 0x9f, 0xec, 0x8c, 0x45, 0x1a, 0xe7, 0xa1, 0x5d, 0x23,
+ 0xc9, 0xa8, 0x20, 0xc8, 0x17, 0x60, 0x44, 0x98, 0x38, 0x2b, 0xec, 0x9b, 0xf8, 0x13, 0x43, 0x1c,
+ 0x72, 0xb2, 0xbf, 0x8d, 0xc4, 0xf9, 0x22, 0x07, 0x53, 0x8c, 0xe8, 0x63, 0x02, 0x8a, 0x7c, 0x01,
+ 0x6a, 0x6a, 0x3d, 0xaa, 0xbe, 0x6c, 0xa6, 0x6f, 0x46, 0x2d, 0x62, 0x91, 0xbe, 0xde, 0xb5, 0x3c,
+ 0xda, 0xa6, 0x4e, 0xe0, 0x37, 0x4e, 0xaa, 0xd5, 0xba, 0x4a, 0xf5, 0x31, 0x42, 0x23, 0x1b, 0xbd,
+ 0x4e, 0x26, 0xe1, 0x21, 0x79, 0xbc, 0xcf, 0xa8, 0x3e, 0x80, 0x87, 0xe9, 0xcb, 0x70, 0x22, 0xf4,
+ 0x02, 0x49, 0x47, 0x82, 0xf0, 0x99, 0x3c, 0xcd, 0xb2, 0x2f, 0x25, 0x93, 0xee, 0xec, 0x4d, 0x3e,
+ 0x96, 0xe1, 0x4a, 0x88, 0x18, 0x30, 0x0d, 0xa6, 0x7d, 0xbf, 0x04, 0xbd, 0xd6, 0x7f, 0x52, 0x69,
+ 0x85, 0xa3, 0x56, 0x5a, 0xba, 0x42, 0x62, 0xf8, 0x7c, 0x5e, 0x66, 0xcb, 0x5f, 0xa9, 0xac, 0x0f,
+ 0x53, 0x3a, 0xea, 0x0f, 0xf3, 0xa0, 0xf4, 0x1d, 0xed, 0xed, 0x32, 0x8c, 0xcd, 0xeb, 0xb4, 0xed,
+ 0x3a, 0xf7, 0x5c, 0x0b, 0x15, 0x1e, 0x88, 0xb5, 0xd0, 0x25, 0xa8, 0x7a, 0xb4, 0x63, 0x5b, 0x86,
+ 0x2e, 0x8c, 0x2f, 0xe9, 0x7b, 0x44, 0x49, 0xc3, 0x30, 0xb5, 0xcf, 0x1a, 0xb8, 0xf4, 0x40, 0xae,
+ 0x81, 0xcb, 0xef, 0xfd, 0x1a, 0x58, 0xfb, 0x7a, 0x11, 0xb8, 0xa1, 0x42, 0x2e, 0x42, 0x99, 0x4d,
+ 0xc2, 0x69, 0xcf, 0x0b, 0x6f, 0x38, 0x3c, 0x85, 0x9c, 0x83, 0x62, 0xe0, 0xca, 0x9e, 0x07, 0x32,
+ 0xbd, 0xb8, 0xe6, 0x62, 0x31, 0x70, 0xc9, 0x1b, 0x00, 0x86, 0xeb, 0x98, 0x96, 0x72, 0xc9, 0xe7,
+ 0xab, 0xd8, 0x82, 0xeb, 0xdd, 0xd2, 0x3d, 0x73, 0x2e, 0x44, 0x14, 0xab, 0xa0, 0xe8, 0x1d, 0x63,
+ 0xd2, 0xc8, 0x0b, 0x30, 0xe4, 0x3a, 0x0b, 0x5d, 0xdb, 0xe6, 0x0a, 0xad, 0x35, 0x3e, 0xc2, 0x96,
+ 0xa6, 0x37, 0x38, 0xe5, 0xce, 0xde, 0xe4, 0x59, 0x61, 0xdf, 0xb2, 0xb7, 0x97, 0x3c, 0x2b, 0xb0,
+ 0x9c, 0x56, 0x33, 0xf0, 0xf4, 0x80, 0xb6, 0x76, 0x51, 0x66, 0xd3, 0xbe, 0x59, 0x80, 0xfa, 0x82,
+ 0x75, 0x9b, 0x9a, 0x2f, 0x59, 0x8e, 0xe9, 0xde, 0x22, 0x08, 0x43, 0x36, 0x75, 0x5a, 0xc1, 0xd6,
+ 0x80, 0xeb, 0x07, 0xb1, 0x36, 0xe6, 0x08, 0x28, 0x91, 0xc8, 0x34, 0xd4, 0x84, 0xf5, 0x69, 0x39,
+ 0x2d, 0xae, 0xc3, 0x6a, 0x34, 0xe8, 0x35, 0x55, 0x02, 0x46, 0x3c, 0xda, 0x2e, 0x9c, 0xec, 0x51,
+ 0x03, 0x31, 0xa1, 0x1c, 0xe8, 0x2d, 0x35, 0xbe, 0x2e, 0x0c, 0xac, 0xe0, 0x35, 0xbd, 0x15, 0x53,
+ 0x2e, 0x9f, 0xe3, 0xd7, 0x74, 0x36, 0xc7, 0x33, 0x74, 0xed, 0x57, 0x05, 0xa8, 0x2e, 0x74, 0x1d,
+ 0x83, 0x2f, 0xd1, 0xee, 0xed, 0x91, 0x53, 0x06, 0x43, 0x31, 0xd3, 0x60, 0xe8, 0xc2, 0xd0, 0xf6,
+ 0xad, 0xd0, 0xa0, 0xa8, 0xcf, 0xac, 0x0c, 0xde, 0x2a, 0x64, 0x91, 0xa6, 0xae, 0x71, 0x3c, 0xb1,
+ 0x61, 0x34, 0x26, 0x0b, 0x34, 0x74, 0xed, 0x25, 0x2e, 0x54, 0x0a, 0x3b, 0xf7, 0x49, 0xa8, 0xc7,
+ 0xd8, 0x0e, 0xe5, 0x3b, 0xfe, 0x7f, 0x65, 0x18, 0x5a, 0x6c, 0x36, 0x67, 0x57, 0x97, 0xc8, 0x33,
+ 0x50, 0x97, 0x7b, 0x09, 0xd7, 0x23, 0x1d, 0x84, 0x5b, 0x49, 0xcd, 0x28, 0x09, 0xe3, 0x7c, 0xcc,
+ 0x1c, 0xf3, 0xa8, 0x6e, 0xb7, 0x65, 0x67, 0x09, 0xcd, 0x31, 0x64, 0x44, 0x14, 0x69, 0x44, 0x87,
+ 0x31, 0xb6, 0xc2, 0x63, 0x2a, 0x14, 0xab, 0x37, 0xd9, 0x6d, 0x0e, 0xb8, 0xbe, 0xe3, 0x46, 0xe2,
+ 0x7a, 0x02, 0x00, 0x53, 0x80, 0xe4, 0x79, 0xa8, 0xea, 0xdd, 0x60, 0x8b, 0x1b, 0xd0, 0xa2, 0x6f,
+ 0x9c, 0xe7, 0x5b, 0x2d, 0x92, 0x76, 0x67, 0x6f, 0x72, 0xe4, 0x1a, 0x36, 0x9e, 0x51, 0xef, 0x18,
+ 0x72, 0xb3, 0xc2, 0xa9, 0x15, 0xa3, 0x2c, 0x5c, 0xe5, 0xd0, 0x85, 0x5b, 0x4d, 0x00, 0x60, 0x0a,
+ 0x90, 0xbc, 0x02, 0x23, 0xdb, 0x74, 0x37, 0xd0, 0x37, 0xa4, 0x80, 0xa1, 0xc3, 0x08, 0x18, 0x67,
+ 0x26, 0xdc, 0xb5, 0x58, 0x76, 0x4c, 0x80, 0x11, 0x1f, 0x4e, 0x6f, 0x53, 0x6f, 0x83, 0x7a, 0xae,
+ 0x5c, 0x7d, 0x4a, 0x21, 0xc3, 0x87, 0x11, 0x32, 0xb1, 0xbf, 0x37, 0x79, 0xfa, 0x5a, 0x06, 0x0c,
+ 0x66, 0x82, 0x6b, 0xbf, 0x2c, 0xc2, 0x89, 0x45, 0xb1, 0x99, 0xeb, 0x7a, 0x62, 0x12, 0x26, 0x67,
+ 0xa1, 0xe4, 0x75, 0xba, 0xbc, 0xe5, 0x94, 0x84, 0xbb, 0x16, 0x57, 0xd7, 0x91, 0xd1, 0xc8, 0xcb,
+ 0x50, 0x35, 0xe5, 0x90, 0x21, 0x17, 0xbf, 0x03, 0x39, 0x2a, 0xd4, 0x1b, 0x86, 0x68, 0xcc, 0xd2,
+ 0x6f, 0xfb, 0xad, 0xa6, 0xf5, 0x06, 0x95, 0xeb, 0x41, 0x6e, 0xe9, 0xaf, 0x08, 0x12, 0xaa, 0x34,
+ 0x36, 0xab, 0x6e, 0xd3, 0x5d, 0xb1, 0x1a, 0x2a, 0x47, 0xb3, 0xea, 0x35, 0x49, 0xc3, 0x30, 0x95,
+ 0x4c, 0xaa, 0xce, 0xc2, 0x5a, 0x41, 0x59, 0xac, 0xe4, 0x6f, 0x32, 0x82, 0xec, 0x37, 0x6c, 0xc8,
+ 0x7c, 0xcd, 0x0a, 0x02, 0xea, 0xc9, 0xcf, 0x38, 0xd0, 0x90, 0x79, 0x95, 0x23, 0xa0, 0x44, 0x22,
+ 0x1f, 0x83, 0x1a, 0x07, 0x6f, 0xd8, 0xee, 0x06, 0xff, 0x70, 0x35, 0xb1, 0xa6, 0xbf, 0xa9, 0x88,
+ 0x18, 0xa5, 0x6b, 0xbf, 0x2e, 0xc2, 0x99, 0x45, 0x1a, 0x08, 0xab, 0x66, 0x9e, 0x76, 0x6c, 0x77,
+ 0x97, 0x99, 0x96, 0x48, 0x5f, 0x27, 0x2f, 0x02, 0x58, 0xfe, 0x46, 0x73, 0xc7, 0xe0, 0xfd, 0x40,
+ 0xf4, 0xe1, 0x8b, 0xb2, 0x4b, 0xc2, 0x52, 0xb3, 0x21, 0x53, 0xee, 0x24, 0xde, 0x30, 0x96, 0x27,
+ 0x5a, 0x5e, 0x15, 0xef, 0xb2, 0xbc, 0x6a, 0x02, 0x74, 0x22, 0x03, 0xb5, 0xc4, 0x39, 0xff, 0x95,
+ 0x12, 0x73, 0x18, 0xdb, 0x34, 0x06, 0x93, 0xc7, 0x64, 0x74, 0x60, 0xdc, 0xa4, 0x9b, 0x7a, 0xd7,
+ 0x0e, 0x42, 0xa3, 0x5a, 0x76, 0xe2, 0x83, 0xdb, 0xe5, 0xe1, 0x46, 0xf3, 0x7c, 0x0a, 0x09, 0x7b,
+ 0xb0, 0xb5, 0xff, 0x5f, 0x82, 0x73, 0x8b, 0x34, 0x08, 0x3d, 0x2e, 0x72, 0x74, 0x6c, 0x76, 0xa8,
+ 0xc1, 0xbe, 0xc2, 0x5b, 0x05, 0x18, 0xb2, 0xf5, 0x0d, 0x6a, 0xb3, 0xd9, 0x8b, 0xd5, 0xe6, 0xd5,
+ 0x81, 0x27, 0x82, 0xfe, 0x52, 0xa6, 0x96, 0xb9, 0x84, 0xd4, 0xd4, 0x20, 0x88, 0x28, 0xc5, 0xb3,
+ 0x41, 0xdd, 0xb0, 0xbb, 0x7e, 0x40, 0xbd, 0x55, 0xd7, 0x0b, 0xa4, 0x3d, 0x19, 0x0e, 0xea, 0x73,
+ 0x51, 0x12, 0xc6, 0xf9, 0xc8, 0x0c, 0x80, 0x61, 0x5b, 0xd4, 0x09, 0x78, 0x2e, 0xd1, 0xaf, 0x88,
+ 0xfa, 0xbe, 0x73, 0x61, 0x0a, 0xc6, 0xb8, 0x98, 0xa8, 0xb6, 0xeb, 0x58, 0x81, 0x2b, 0x44, 0x95,
+ 0x93, 0xa2, 0x56, 0xa2, 0x24, 0x8c, 0xf3, 0xf1, 0x6c, 0x34, 0xf0, 0x2c, 0xc3, 0xe7, 0xd9, 0x2a,
+ 0xa9, 0x6c, 0x51, 0x12, 0xc6, 0xf9, 0xd8, 0x9c, 0x17, 0xab, 0xff, 0xa1, 0xe6, 0xbc, 0xef, 0xd6,
+ 0xe0, 0x42, 0x42, 0xad, 0x81, 0x1e, 0xd0, 0xcd, 0xae, 0xdd, 0xa4, 0x81, 0xfa, 0x80, 0x03, 0xce,
+ 0x85, 0xff, 0x31, 0xfa, 0xee, 0x22, 0x84, 0xc4, 0x38, 0x9a, 0xef, 0xde, 0x53, 0xc0, 0x03, 0x7d,
+ 0xfb, 0x69, 0xa8, 0x39, 0x7a, 0xe0, 0xf3, 0x8e, 0x2b, 0xfb, 0x68, 0x68, 0x86, 0x5d, 0x57, 0x09,
+ 0x18, 0xf1, 0x90, 0x55, 0x38, 0x2d, 0x55, 0x7c, 0xf9, 0x76, 0xc7, 0xf5, 0x02, 0xea, 0x89, 0xbc,
+ 0x72, 0x3a, 0x95, 0x79, 0x4f, 0xaf, 0x64, 0xf0, 0x60, 0x66, 0x4e, 0xb2, 0x02, 0xa7, 0x0c, 0xb1,
+ 0xad, 0x4e, 0x6d, 0x57, 0x37, 0x15, 0xa0, 0x70, 0x70, 0x85, 0x4b, 0xa3, 0xb9, 0x5e, 0x16, 0xcc,
+ 0xca, 0x97, 0x6e, 0xcd, 0x43, 0x03, 0xb5, 0xe6, 0xe1, 0x41, 0x5a, 0x73, 0x75, 0xb0, 0xd6, 0x5c,
+ 0x3b, 0x58, 0x6b, 0x66, 0x9a, 0x67, 0xed, 0x88, 0x7a, 0xcc, 0x3c, 0x11, 0x33, 0x6c, 0x2c, 0x6a,
+ 0x23, 0xd4, 0x7c, 0x33, 0x83, 0x07, 0x33, 0x73, 0x92, 0x0d, 0x38, 0x27, 0xe8, 0x97, 0x1d, 0xc3,
+ 0xdb, 0xed, 0xb0, 0x89, 0x27, 0x86, 0x5b, 0x4f, 0x78, 0x18, 0xcf, 0x35, 0xfb, 0x72, 0xe2, 0x5d,
+ 0x50, 0xc8, 0xa7, 0x61, 0x54, 0x7c, 0xa5, 0x15, 0xbd, 0xc3, 0x61, 0x45, 0x0c, 0xc7, 0xc3, 0x12,
+ 0x76, 0x74, 0x2e, 0x9e, 0x88, 0x49, 0x5e, 0x32, 0x0b, 0x27, 0x3a, 0x3b, 0x06, 0x7b, 0x5c, 0xda,
+ 0xbc, 0x4e, 0xa9, 0x49, 0x4d, 0xbe, 0x69, 0x54, 0x6b, 0x3c, 0xa2, 0x1c, 0x1d, 0xab, 0xc9, 0x64,
+ 0x4c, 0xf3, 0x93, 0xe7, 0x61, 0xc4, 0x0f, 0x74, 0x2f, 0x90, 0x6e, 0xbd, 0x89, 0x31, 0x11, 0xe3,
+ 0xa2, 0xbc, 0x5e, 0xcd, 0x58, 0x1a, 0x26, 0x38, 0x33, 0xe7, 0x8b, 0x13, 0xc7, 0x37, 0x5f, 0xe4,
+ 0x19, 0xad, 0x7e, 0xaf, 0x08, 0x17, 0x17, 0x69, 0xb0, 0xe2, 0x3a, 0xd2, 0x29, 0x9a, 0x35, 0xed,
+ 0x1f, 0xc8, 0x27, 0x9a, 0x9c, 0xb4, 0x8b, 0x47, 0x3a, 0x69, 0x97, 0x8e, 0x68, 0xd2, 0x2e, 0x1f,
+ 0xe3, 0xa4, 0xfd, 0x9b, 0x45, 0x78, 0x24, 0xa1, 0xc9, 0x55, 0xd7, 0x54, 0x03, 0xfe, 0x07, 0x0a,
+ 0x3c, 0x80, 0x02, 0xef, 0x08, 0xbb, 0x93, 0x6f, 0x6b, 0xa5, 0x2c, 0x9e, 0x37, 0xd3, 0x16, 0xcf,
+ 0x2b, 0x79, 0x66, 0xbe, 0x0c, 0x09, 0x07, 0x9a, 0xf1, 0xae, 0x02, 0xf1, 0xe4, 0x26, 0x9c, 0x70,
+ 0xfd, 0xc4, 0x8c, 0x9e, 0x30, 0x88, 0x0e, 0x7b, 0x38, 0x30, 0x23, 0x17, 0x69, 0xc2, 0xc3, 0x3e,
+ 0x75, 0x02, 0xcb, 0xa1, 0x76, 0x12, 0x4e, 0x58, 0x43, 0x8f, 0x49, 0xb8, 0x87, 0x9b, 0x59, 0x4c,
+ 0x98, 0x9d, 0x37, 0xcf, 0x38, 0xf0, 0x07, 0xc0, 0x4d, 0x4e, 0xa1, 0x9a, 0x23, 0xb3, 0x58, 0xde,
+ 0x4a, 0x5b, 0x2c, 0xaf, 0xe6, 0xff, 0x6e, 0x83, 0x59, 0x2b, 0x33, 0x00, 0xfc, 0x2b, 0xc4, 0xcd,
+ 0x95, 0x70, 0x92, 0xc6, 0x30, 0x05, 0x63, 0x5c, 0x6c, 0x02, 0x52, 0x7a, 0x8e, 0x5b, 0x2a, 0xe1,
+ 0x04, 0xd4, 0x8c, 0x27, 0x62, 0x92, 0xb7, 0xaf, 0xb5, 0x53, 0x19, 0xd8, 0xda, 0xb9, 0x0a, 0x24,
+ 0xe1, 0x78, 0x14, 0x78, 0x43, 0xc9, 0x18, 0xce, 0xa5, 0x1e, 0x0e, 0xcc, 0xc8, 0xd5, 0xa7, 0x29,
+ 0x0f, 0x1f, 0x6d, 0x53, 0xae, 0x0e, 0xde, 0x94, 0xc9, 0xab, 0x70, 0x96, 0x8b, 0x92, 0xfa, 0x49,
+ 0x02, 0x0b, 0xbb, 0xe7, 0x43, 0x12, 0xf8, 0x2c, 0xf6, 0x63, 0xc4, 0xfe, 0x18, 0xec, 0xfb, 0x18,
+ 0x1e, 0x35, 0x99, 0x70, 0xdd, 0xee, 0x6f, 0x13, 0xcd, 0x65, 0xf0, 0x60, 0x66, 0x4e, 0xd6, 0xc4,
+ 0x02, 0xd6, 0x0c, 0xf5, 0x0d, 0x9b, 0x9a, 0x32, 0x86, 0x35, 0x6c, 0x62, 0x6b, 0xcb, 0x4d, 0x99,
+ 0x82, 0x31, 0xae, 0x2c, 0x33, 0x65, 0xe4, 0x90, 0x66, 0xca, 0x22, 0xf7, 0xd2, 0x6f, 0x26, 0xac,
+ 0x21, 0x69, 0xeb, 0x84, 0x51, 0xc9, 0x73, 0x69, 0x06, 0xec, 0xcd, 0xc3, 0xad, 0x44, 0xc3, 0xb3,
+ 0x3a, 0x81, 0x9f, 0xc4, 0x1a, 0x4b, 0x59, 0x89, 0x19, 0x3c, 0x98, 0x99, 0x93, 0xd9, 0xe7, 0x5b,
+ 0x54, 0xb7, 0x83, 0xad, 0x24, 0xe0, 0x89, 0xa4, 0x7d, 0x7e, 0xa5, 0x97, 0x05, 0xb3, 0xf2, 0x65,
+ 0x4e, 0x48, 0xe3, 0x0f, 0xa6, 0x59, 0xf5, 0x8d, 0x12, 0x9c, 0x5d, 0xa4, 0x41, 0x18, 0xde, 0xf3,
+ 0x81, 0x1b, 0xe5, 0x3d, 0x70, 0xa3, 0x7c, 0xa7, 0x02, 0xa7, 0x16, 0x69, 0xd0, 0x63, 0x8d, 0xfd,
+ 0x33, 0x55, 0xff, 0x0a, 0x9c, 0x8a, 0x22, 0xca, 0x9a, 0x81, 0xeb, 0x89, 0xb9, 0x3c, 0xb5, 0x5a,
+ 0x6e, 0xf6, 0xb2, 0x60, 0x56, 0x3e, 0xf2, 0x05, 0x78, 0x84, 0x4f, 0xf5, 0x4e, 0x4b, 0xf8, 0x67,
+ 0x85, 0x33, 0x21, 0x76, 0x26, 0x62, 0x52, 0x42, 0x3e, 0xd2, 0xcc, 0x66, 0xc3, 0x7e, 0xf9, 0xc9,
+ 0x57, 0x61, 0xa4, 0x63, 0x75, 0xa8, 0x6d, 0x39, 0xdc, 0x3e, 0xcb, 0x1d, 0x12, 0xb2, 0x1a, 0x03,
+ 0x8b, 0x16, 0x70, 0x71, 0x2a, 0x26, 0x04, 0x66, 0xb6, 0xd4, 0xea, 0x31, 0xb6, 0xd4, 0xbf, 0x2d,
+ 0xc2, 0xf0, 0xa2, 0xe7, 0x76, 0x3b, 0x8d, 0x5d, 0xd2, 0x82, 0xa1, 0x5b, 0x7c, 0xf3, 0x4c, 0x6e,
+ 0x4d, 0x0d, 0x1e, 0x95, 0x2d, 0xf6, 0xe0, 0x22, 0x93, 0x48, 0xbc, 0xa3, 0x84, 0x67, 0x8d, 0x78,
+ 0x9b, 0xee, 0x52, 0x53, 0xee, 0xa1, 0x85, 0x8d, 0xf8, 0x1a, 0x23, 0xa2, 0x48, 0x23, 0x6d, 0x38,
+ 0xa1, 0xdb, 0xb6, 0x7b, 0x8b, 0x9a, 0xcb, 0x7a, 0x40, 0x1d, 0xea, 0xab, 0x2d, 0xc9, 0xc3, 0xba,
+ 0xa5, 0xf9, 0xbe, 0xfe, 0x6c, 0x12, 0x0a, 0xd3, 0xd8, 0xe4, 0x35, 0x18, 0xf6, 0x03, 0xd7, 0x53,
+ 0xc6, 0x56, 0x7d, 0x66, 0x6e, 0xf0, 0x8f, 0xde, 0xf8, 0x7c, 0x53, 0x40, 0x09, 0x9f, 0xbd, 0x7c,
+ 0x41, 0x25, 0x40, 0xfb, 0x76, 0x01, 0xe0, 0xca, 0xda, 0xda, 0xaa, 0xdc, 0x5e, 0x30, 0xa1, 0xac,
+ 0x77, 0xc3, 0x8d, 0xca, 0xc1, 0x37, 0x04, 0x13, 0x61, 0x99, 0x72, 0x0f, 0xaf, 0x1b, 0x6c, 0x21,
+ 0x47, 0x27, 0x1f, 0x85, 0x61, 0x69, 0x20, 0x4b, 0xb5, 0x87, 0xa1, 0x05, 0xd2, 0x88, 0x46, 0x95,
+ 0xae, 0xfd, 0x9f, 0x22, 0xc0, 0x92, 0x69, 0xd3, 0xa6, 0x0a, 0xa4, 0xaf, 0x05, 0x5b, 0x1e, 0xf5,
+ 0xb7, 0x5c, 0xdb, 0x1c, 0x70, 0x37, 0x95, 0xfb, 0xfc, 0xd7, 0x14, 0x08, 0x46, 0x78, 0xc4, 0x84,
+ 0x11, 0x3f, 0xa0, 0x1d, 0x15, 0xa9, 0x39, 0xe0, 0x26, 0xca, 0xb8, 0xf0, 0x8b, 0x44, 0x38, 0x98,
+ 0x40, 0x25, 0x3a, 0xd4, 0x2d, 0xc7, 0x10, 0x1d, 0xa4, 0xb1, 0x3b, 0x60, 0x43, 0x3a, 0xc1, 0x56,
+ 0x1c, 0x4b, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0x9f, 0x15, 0xe1, 0x0c, 0x97, 0xc7, 0x8a, 0x91, 0x88,
+ 0xc7, 0x24, 0xff, 0xa6, 0xe7, 0xd0, 0xdf, 0xbf, 0x3c, 0x98, 0x68, 0x71, 0x66, 0x6c, 0x85, 0x06,
+ 0x7a, 0x64, 0xcf, 0x45, 0xb4, 0xd8, 0x49, 0xbf, 0x2e, 0x94, 0x7d, 0x36, 0x5e, 0x09, 0xed, 0x35,
+ 0x07, 0x6e, 0x42, 0xd9, 0x15, 0xe0, 0xa3, 0x57, 0xb8, 0x6b, 0xcc, 0x47, 0x2d, 0x2e, 0x8e, 0xfc,
+ 0x3b, 0x18, 0xf2, 0x03, 0x3d, 0xe8, 0xaa, 0xae, 0xb9, 0x7e, 0xd4, 0x82, 0x39, 0x78, 0x34, 0x8e,
+ 0x88, 0x77, 0x94, 0x42, 0xb5, 0x9f, 0x15, 0xe0, 0x5c, 0x76, 0xc6, 0x65, 0xcb, 0x0f, 0xc8, 0xbf,
+ 0xee, 0x51, 0xfb, 0x01, 0xbf, 0x38, 0xcb, 0xcd, 0x95, 0x1e, 0xc6, 0x85, 0x2b, 0x4a, 0x4c, 0xe5,
+ 0x01, 0x54, 0xac, 0x80, 0xb6, 0xd5, 0xfa, 0xf2, 0xc6, 0x11, 0x57, 0x3d, 0x36, 0xb5, 0x33, 0x29,
+ 0x28, 0x84, 0x69, 0x6f, 0x17, 0xfb, 0x55, 0x99, 0x4f, 0x1f, 0x76, 0x32, 0xe6, 0xf7, 0x5a, 0xbe,
+ 0x98, 0xdf, 0x64, 0x81, 0x7a, 0x43, 0x7f, 0xff, 0x6d, 0x6f, 0xe8, 0xef, 0x8d, 0xfc, 0xa1, 0xbf,
+ 0x29, 0x35, 0xf4, 0x8d, 0x00, 0x7e, 0xb7, 0x04, 0xe7, 0xef, 0xd6, 0x6c, 0xd8, 0x7c, 0x26, 0x5b,
+ 0x67, 0xde, 0xf9, 0xec, 0xee, 0xed, 0x90, 0xcc, 0x40, 0xa5, 0xb3, 0xa5, 0xfb, 0xca, 0x28, 0x53,
+ 0x0b, 0x96, 0xca, 0x2a, 0x23, 0xde, 0x61, 0x83, 0x06, 0x37, 0xe6, 0xf8, 0x2b, 0x0a, 0x56, 0x36,
+ 0x1c, 0xb7, 0xa9, 0xef, 0x47, 0x3e, 0x81, 0x70, 0x38, 0x5e, 0x11, 0x64, 0x54, 0xe9, 0x24, 0x80,
+ 0x21, 0xe1, 0x62, 0x96, 0x33, 0xd3, 0xe0, 0x81, 0x5c, 0x19, 0x61, 0xe2, 0x51, 0xa5, 0xe4, 0x6e,
+ 0x85, 0x94, 0x45, 0xa6, 0xa0, 0x1c, 0x44, 0x41, 0xbb, 0x6a, 0x69, 0x5e, 0xce, 0xb0, 0x4f, 0x39,
+ 0x1f, 0x5b, 0xd8, 0xbb, 0x1b, 0xdc, 0xa9, 0x6e, 0xca, 0xfd, 0x73, 0xcb, 0x75, 0xb8, 0x41, 0x56,
+ 0x8a, 0x16, 0xf6, 0x37, 0x7a, 0x38, 0x30, 0x23, 0x97, 0xf6, 0xc7, 0x55, 0x38, 0x93, 0xdd, 0x1e,
+ 0x98, 0xde, 0x76, 0xa8, 0xe7, 0x33, 0xec, 0x42, 0x52, 0x6f, 0x37, 0x05, 0x19, 0x55, 0xfa, 0xfb,
+ 0x3a, 0xe0, 0xec, 0x3b, 0x05, 0x38, 0xeb, 0xc9, 0x3d, 0xa2, 0xfb, 0x11, 0x74, 0xf6, 0x98, 0x70,
+ 0x67, 0xf4, 0x11, 0x88, 0xfd, 0xcb, 0x42, 0xfe, 0x57, 0x01, 0x26, 0xda, 0x29, 0x3f, 0xc7, 0x31,
+ 0x9e, 0x5b, 0xe3, 0x51, 0xf1, 0x2b, 0x7d, 0xe4, 0x61, 0xdf, 0x92, 0x90, 0xaf, 0x42, 0xbd, 0xc3,
+ 0xda, 0x85, 0x1f, 0x50, 0xc7, 0x50, 0x47, 0xd7, 0x06, 0xef, 0x49, 0xab, 0x11, 0x96, 0x0a, 0x45,
+ 0x13, 0xf6, 0x41, 0x2c, 0x01, 0xe3, 0x12, 0x1f, 0xf0, 0x83, 0x6a, 0x97, 0xa0, 0xea, 0xd3, 0x20,
+ 0xb0, 0x9c, 0x96, 0x58, 0x6f, 0xd4, 0x44, 0x5f, 0x69, 0x4a, 0x1a, 0x86, 0xa9, 0xe4, 0x63, 0x50,
+ 0xe3, 0x5b, 0x4e, 0xb3, 0x5e, 0xcb, 0x9f, 0xa8, 0xf1, 0x70, 0xb1, 0x51, 0x11, 0x00, 0x27, 0x89,
+ 0x18, 0xa5, 0x93, 0xa7, 0x61, 0x64, 0x83, 0x77, 0x5f, 0x79, 0x76, 0x59, 0xf8, 0xb8, 0xb8, 0xb5,
+ 0xd6, 0x88, 0xd1, 0x31, 0xc1, 0x45, 0x66, 0x00, 0x68, 0xb8, 0x2f, 0x97, 0xf6, 0x67, 0x45, 0x3b,
+ 0x76, 0x18, 0xe3, 0x22, 0x8f, 0x41, 0x29, 0xb0, 0x7d, 0xee, 0xc3, 0xaa, 0x46, 0x4b, 0xd0, 0xb5,
+ 0xe5, 0x26, 0x32, 0xba, 0xf6, 0xeb, 0x02, 0x9c, 0x48, 0x1d, 0x2e, 0x61, 0x59, 0xba, 0x9e, 0x2d,
+ 0x87, 0x91, 0x30, 0xcb, 0x3a, 0x2e, 0x23, 0xa3, 0x93, 0x57, 0xa5, 0x59, 0x5e, 0xcc, 0x79, 0x4d,
+ 0xc3, 0x75, 0x3d, 0xf0, 0x99, 0x1d, 0xde, 0x63, 0x91, 0xf3, 0x6d, 0xbe, 0xa8, 0x3c, 0x72, 0x1e,
+ 0x88, 0x6d, 0xf3, 0x45, 0x69, 0x98, 0xe0, 0x4c, 0x39, 0xfc, 0xca, 0x07, 0x71, 0xf8, 0x69, 0xdf,
+ 0x2c, 0xc6, 0x34, 0x20, 0x2d, 0xfb, 0x7b, 0x68, 0xe0, 0x49, 0x36, 0x81, 0x86, 0x93, 0x7b, 0x2d,
+ 0x3e, 0xff, 0xf1, 0xc9, 0x58, 0xa6, 0x92, 0x97, 0x84, 0xee, 0x4b, 0x39, 0x0f, 0xc3, 0xae, 0x2d,
+ 0x37, 0x45, 0x74, 0x95, 0xfa, 0x6a, 0xe1, 0x27, 0x28, 0x1f, 0xd3, 0x27, 0xd0, 0x7e, 0xbf, 0x04,
+ 0xf5, 0xab, 0xee, 0xc6, 0xfb, 0x24, 0x82, 0x3a, 0x7b, 0x9a, 0x2a, 0xbe, 0x87, 0xd3, 0xd4, 0x3a,
+ 0x3c, 0x12, 0x04, 0x76, 0x93, 0x1a, 0xae, 0x63, 0xfa, 0xb3, 0x9b, 0x01, 0xf5, 0x16, 0x2c, 0xc7,
+ 0xf2, 0xb7, 0xa8, 0x29, 0xb7, 0x93, 0x1e, 0xdd, 0xdf, 0x9b, 0x7c, 0x64, 0x6d, 0x6d, 0x39, 0x8b,
+ 0x05, 0xfb, 0xe5, 0xe5, 0xc3, 0x86, 0x38, 0x09, 0xc8, 0x4f, 0xca, 0xc8, 0x98, 0x1b, 0x31, 0x6c,
+ 0xc4, 0xe8, 0x98, 0xe0, 0xd2, 0xde, 0x29, 0x42, 0x2d, 0x3c, 0x80, 0x4f, 0x9e, 0x80, 0xe1, 0x0d,
+ 0xcf, 0xdd, 0xa6, 0x9e, 0xd8, 0xb9, 0x93, 0x27, 0x65, 0x1a, 0x82, 0x84, 0x2a, 0x8d, 0x3c, 0x0e,
+ 0x95, 0xc0, 0xed, 0x58, 0x46, 0xda, 0xa1, 0xb6, 0xc6, 0x88, 0x28, 0xd2, 0x8e, 0xaf, 0x81, 0x3f,
+ 0x99, 0x30, 0xed, 0x6a, 0x7d, 0x8d, 0xb1, 0x57, 0xa0, 0xec, 0xeb, 0xbe, 0x2d, 0xe7, 0xd3, 0x1c,
+ 0x67, 0xd9, 0x67, 0x9b, 0xcb, 0xf2, 0x2c, 0xfb, 0x6c, 0x73, 0x19, 0x39, 0xa8, 0xf6, 0x8b, 0x22,
+ 0xd4, 0x85, 0xde, 0xc4, 0xa8, 0x70, 0x94, 0x9a, 0x7b, 0x81, 0x87, 0x52, 0xf8, 0xdd, 0x36, 0xf5,
+ 0xb8, 0x9b, 0x49, 0x0e, 0x72, 0xf1, 0xfd, 0x81, 0x28, 0x31, 0x0c, 0xa7, 0x88, 0x48, 0x4a, 0xf5,
+ 0xe5, 0x63, 0x54, 0x7d, 0xe5, 0x40, 0xaa, 0x1f, 0x3a, 0x0e, 0xd5, 0xbf, 0x55, 0x84, 0xda, 0xb2,
+ 0xb5, 0x49, 0x8d, 0x5d, 0xc3, 0xe6, 0x67, 0x02, 0x4d, 0x6a, 0xd3, 0x80, 0x2e, 0x7a, 0xba, 0x41,
+ 0x57, 0xa9, 0x67, 0xf1, 0x0b, 0x6a, 0x58, 0xff, 0xe0, 0x23, 0x90, 0x3c, 0x13, 0x38, 0xdf, 0x87,
+ 0x07, 0xfb, 0xe6, 0x26, 0x4b, 0x30, 0x62, 0x52, 0xdf, 0xf2, 0xa8, 0xb9, 0x1a, 0x5b, 0xa8, 0x3c,
+ 0xa1, 0xa6, 0x9a, 0xf9, 0x58, 0xda, 0x9d, 0xbd, 0xc9, 0x51, 0xe5, 0xa0, 0x14, 0x2b, 0x96, 0x44,
+ 0x56, 0xd6, 0xe5, 0x3b, 0x7a, 0xd7, 0xcf, 0x2a, 0x63, 0xac, 0xcb, 0xaf, 0x66, 0xb3, 0x60, 0xbf,
+ 0xbc, 0x5a, 0x05, 0x4a, 0xcb, 0x6e, 0x4b, 0x7b, 0xbb, 0x04, 0xe1, 0x4d, 0x46, 0xe4, 0x3f, 0x14,
+ 0xa0, 0xae, 0x3b, 0x8e, 0x1b, 0xc8, 0x5b, 0x82, 0xc4, 0x0e, 0x3c, 0xe6, 0xbe, 0x30, 0x69, 0x6a,
+ 0x36, 0x02, 0x15, 0x9b, 0xb7, 0xe1, 0x86, 0x72, 0x2c, 0x05, 0xe3, 0xb2, 0x49, 0x37, 0xb5, 0x9f,
+ 0xbc, 0x92, 0xbf, 0x14, 0x07, 0xd8, 0x3d, 0x3e, 0xf7, 0x39, 0x18, 0x4f, 0x17, 0xf6, 0x30, 0xdb,
+ 0x41, 0xb9, 0x36, 0xe6, 0x8b, 0x00, 0x51, 0x4c, 0xc9, 0x7d, 0x70, 0x62, 0x59, 0x09, 0x27, 0xd6,
+ 0xe2, 0xe0, 0x0a, 0x0e, 0x0b, 0xdd, 0xd7, 0x71, 0xf5, 0x7a, 0xca, 0x71, 0xb5, 0x74, 0x14, 0xc2,
+ 0xee, 0xee, 0xac, 0xfa, 0xdf, 0x05, 0x18, 0x8f, 0x98, 0xe5, 0x09, 0xd9, 0xe7, 0x60, 0xd4, 0xa3,
+ 0xba, 0xd9, 0xd0, 0x03, 0x63, 0x8b, 0x87, 0x7a, 0x17, 0x78, 0x6c, 0xf6, 0xc9, 0xfd, 0xbd, 0xc9,
+ 0x51, 0x8c, 0x27, 0x60, 0x92, 0x8f, 0xe8, 0x50, 0x67, 0x84, 0x35, 0xab, 0x4d, 0xdd, 0x6e, 0x30,
+ 0xa0, 0xd7, 0x94, 0x2f, 0x58, 0x30, 0x82, 0xc1, 0x38, 0xa6, 0xf6, 0x6e, 0x01, 0xc6, 0xe2, 0x05,
+ 0x3e, 0x76, 0x8f, 0xda, 0x56, 0xd2, 0xa3, 0x36, 0x77, 0x04, 0xdf, 0xa4, 0x8f, 0x17, 0xed, 0x97,
+ 0xd5, 0x78, 0xd5, 0xb8, 0xe7, 0x2c, 0xee, 0x2c, 0x28, 0xdc, 0xd5, 0x59, 0xf0, 0xfe, 0xbf, 0xbc,
+ 0xa6, 0x9f, 0x95, 0x5b, 0x7e, 0x80, 0xad, 0xdc, 0xf7, 0xf2, 0x06, 0x9c, 0xd8, 0x2d, 0x2e, 0x43,
+ 0x39, 0x6e, 0x71, 0x69, 0x87, 0xb7, 0xb8, 0x0c, 0x1f, 0xd9, 0xa0, 0x73, 0x90, 0x9b, 0x5c, 0xaa,
+ 0xf7, 0xf5, 0x26, 0x97, 0xda, 0x71, 0xdd, 0xe4, 0x02, 0x79, 0x6f, 0x72, 0x79, 0xb3, 0x00, 0x63,
+ 0x66, 0xe2, 0xc4, 0x2c, 0xf7, 0x2d, 0xe4, 0x99, 0x6a, 0x92, 0x07, 0x70, 0xc5, 0x91, 0xa9, 0x24,
+ 0x0d, 0x53, 0x22, 0xb5, 0xff, 0x5b, 0x89, 0xcf, 0x03, 0xf7, 0xdb, 0x55, 0xfd, 0x6c, 0xd2, 0x55,
+ 0x7d, 0x31, 0xed, 0xaa, 0x3e, 0x11, 0x8b, 0x22, 0x8d, 0xbb, 0xab, 0x3f, 0x1e, 0x1b, 0x1e, 0x4b,
+ 0xfc, 0xe6, 0x94, 0x50, 0xd3, 0x19, 0x43, 0xe4, 0xc7, 0xa1, 0xea, 0xab, 0x3b, 0x27, 0xc5, 0xc2,
+ 0x26, 0xfa, 0x2e, 0xea, 0x3e, 0xc8, 0x90, 0x83, 0x59, 0xe2, 0x1e, 0xd5, 0x7d, 0xd7, 0x49, 0x5b,
+ 0xe2, 0xc8, 0xa9, 0x28, 0x53, 0xe3, 0x2e, 0xf3, 0xa1, 0x7b, 0xb8, 0xcc, 0x75, 0xa8, 0xdb, 0xba,
+ 0x1f, 0xac, 0x77, 0x4c, 0x3d, 0xa0, 0xa6, 0xec, 0x6f, 0xff, 0xe2, 0x60, 0x73, 0x15, 0x9b, 0xff,
+ 0x22, 0x83, 0x70, 0x39, 0x82, 0xc1, 0x38, 0x26, 0x31, 0x61, 0x84, 0xbd, 0xf2, 0xde, 0x60, 0xce,
+ 0xaa, 0x2b, 0x00, 0x0e, 0x23, 0x23, 0xf4, 0xf4, 0x2c, 0xc7, 0x70, 0x30, 0x81, 0xda, 0xc7, 0xab,
+ 0x5e, 0x1b, 0xc4, 0xab, 0x4e, 0x3e, 0x2d, 0x8c, 0x8d, 0x5d, 0xf5, 0xc1, 0xb8, 0x37, 0x6e, 0x34,
+ 0x8a, 0x2a, 0xc4, 0x78, 0x22, 0x26, 0x79, 0xb5, 0x37, 0x6b, 0x50, 0xbf, 0xae, 0x07, 0xd6, 0x0e,
+ 0xe5, 0x5b, 0x40, 0xc7, 0xe3, 0x87, 0xff, 0x6f, 0x05, 0x38, 0x93, 0x8c, 0xf3, 0x3b, 0x46, 0x67,
+ 0x3c, 0xbf, 0x35, 0x04, 0x33, 0xa5, 0x61, 0x9f, 0x52, 0x70, 0xb7, 0x7c, 0x4f, 0xd8, 0xe0, 0x71,
+ 0xbb, 0xe5, 0x9b, 0xfd, 0x04, 0x62, 0xff, 0xb2, 0xbc, 0x5f, 0xdc, 0xf2, 0x0f, 0xf6, 0xe5, 0x72,
+ 0xa9, 0x4d, 0x83, 0xe1, 0x07, 0x66, 0xd3, 0xa0, 0xfa, 0x40, 0x58, 0x6a, 0x9d, 0xd8, 0xa6, 0x41,
+ 0x2d, 0x67, 0xf0, 0x8a, 0x0c, 0x8d, 0x17, 0x68, 0xfd, 0x36, 0x1f, 0xf8, 0xa9, 0x76, 0xe5, 0xcc,
+ 0x65, 0x06, 0xce, 0x86, 0xee, 0x5b, 0x86, 0x9c, 0x33, 0x73, 0x5c, 0xa6, 0xa9, 0xae, 0xfb, 0x12,
+ 0x7b, 0xdc, 0xfc, 0x15, 0x05, 0x76, 0x74, 0xbb, 0x59, 0x31, 0xd7, 0xed, 0x66, 0x64, 0x0e, 0xca,
+ 0x0e, 0x5b, 0x7a, 0x97, 0x0e, 0x7d, 0x91, 0xd8, 0xf5, 0x6b, 0x74, 0x17, 0x79, 0x66, 0xed, 0x9d,
+ 0x22, 0x00, 0xab, 0xfe, 0xc1, 0xdc, 0xf7, 0x1f, 0x85, 0x61, 0xbf, 0xcb, 0x17, 0xda, 0x72, 0xb6,
+ 0x8f, 0x22, 0x7e, 0x04, 0x19, 0x55, 0x3a, 0x79, 0x1c, 0x2a, 0xaf, 0x77, 0x69, 0x57, 0xed, 0x45,
+ 0x87, 0xb6, 0xde, 0xe7, 0x19, 0x11, 0x45, 0xda, 0xf1, 0xb9, 0xe2, 0x94, 0x9b, 0xbf, 0x72, 0x5c,
+ 0x6e, 0xfe, 0x1a, 0x0c, 0x5f, 0x77, 0x79, 0x00, 0xa1, 0xf6, 0xd7, 0x45, 0x80, 0x28, 0x40, 0x8b,
+ 0x7c, 0xbb, 0x00, 0x0f, 0x87, 0x1d, 0x2e, 0x10, 0x26, 0x3b, 0xbf, 0xbf, 0x36, 0xb7, 0xcb, 0x3f,
+ 0xab, 0xb3, 0xf3, 0x11, 0x68, 0x35, 0x4b, 0x1c, 0x66, 0x97, 0x82, 0x20, 0x54, 0x69, 0xbb, 0x13,
+ 0xec, 0xce, 0x5b, 0x9e, 0x6c, 0x81, 0x99, 0x71, 0x80, 0x97, 0x25, 0x8f, 0xc8, 0x2a, 0xd7, 0x95,
+ 0xbc, 0x13, 0xa9, 0x14, 0x0c, 0x71, 0xc8, 0x16, 0x54, 0x1d, 0xf7, 0x55, 0x9f, 0xa9, 0x43, 0x36,
+ 0xc7, 0x17, 0x07, 0x57, 0xb9, 0x50, 0xab, 0x70, 0x11, 0xcb, 0x17, 0x1c, 0x76, 0xa4, 0xb2, 0xbf,
+ 0x55, 0x84, 0x53, 0x19, 0x7a, 0x20, 0x2f, 0xc2, 0xb8, 0x8c, 0x85, 0x8b, 0x2e, 0x72, 0x2e, 0x44,
+ 0x17, 0x39, 0x37, 0x53, 0x69, 0xd8, 0xc3, 0x4d, 0x5e, 0x05, 0xd0, 0x0d, 0x83, 0xfa, 0xfe, 0x8a,
+ 0x6b, 0x2a, 0x63, 0xf6, 0x85, 0xfd, 0xbd, 0x49, 0x98, 0x0d, 0xa9, 0x77, 0xf6, 0x26, 0x3f, 0x91,
+ 0x15, 0xde, 0x9a, 0xd2, 0x73, 0x94, 0x01, 0x63, 0x90, 0xe4, 0xcb, 0x00, 0x62, 0xdd, 0x16, 0x9e,
+ 0xc0, 0xbf, 0x87, 0xb3, 0x63, 0x4a, 0xdd, 0x75, 0x34, 0xf5, 0xf9, 0xae, 0xee, 0x04, 0x56, 0xb0,
+ 0x2b, 0x2e, 0x3c, 0xb9, 0x19, 0xa2, 0x60, 0x0c, 0x51, 0xfb, 0xdd, 0x22, 0x54, 0x95, 0x9b, 0xf5,
+ 0x3e, 0xf8, 0xd6, 0x5a, 0x09, 0xdf, 0xda, 0x11, 0x05, 0xb4, 0x66, 0x79, 0xd6, 0xdc, 0x94, 0x67,
+ 0x6d, 0x31, 0xbf, 0xa8, 0xbb, 0xfb, 0xd5, 0xbe, 0x57, 0x84, 0x31, 0xc5, 0x9a, 0xd7, 0xab, 0xf6,
+ 0x59, 0x38, 0x21, 0x36, 0xa2, 0x57, 0xf4, 0xdb, 0xe2, 0xee, 0x17, 0xae, 0xb0, 0xb2, 0x88, 0x21,
+ 0x6d, 0x24, 0x93, 0x30, 0xcd, 0xcb, 0x9a, 0xb5, 0x20, 0xad, 0xb3, 0x45, 0x88, 0xd8, 0xba, 0x12,
+ 0x8b, 0x25, 0xde, 0xac, 0x1b, 0xa9, 0x34, 0xec, 0xe1, 0x4e, 0xbb, 0xf5, 0xca, 0xc7, 0xe0, 0xd6,
+ 0xfb, 0x93, 0x02, 0x8c, 0x44, 0xfa, 0x3a, 0x76, 0xa7, 0xde, 0x66, 0xd2, 0xa9, 0x37, 0x9b, 0xbb,
+ 0x39, 0xf4, 0x71, 0xe9, 0xfd, 0xa7, 0x61, 0x48, 0xc4, 0x55, 0x93, 0x0d, 0x38, 0x67, 0x65, 0x46,
+ 0x87, 0xc5, 0x46, 0x9b, 0xf0, 0xa0, 0xf0, 0x52, 0x5f, 0x4e, 0xbc, 0x0b, 0x0a, 0xe9, 0x42, 0x75,
+ 0x87, 0x7a, 0x81, 0x65, 0x50, 0x55, 0xbf, 0xc5, 0xdc, 0x26, 0x99, 0x74, 0x5c, 0x86, 0x3a, 0xbd,
+ 0x29, 0x05, 0x60, 0x28, 0x8a, 0x6c, 0x40, 0x85, 0x9a, 0x2d, 0xaa, 0x6e, 0xe3, 0xc9, 0x79, 0xd7,
+ 0x65, 0xa8, 0x4f, 0xf6, 0xe6, 0xa3, 0x80, 0x26, 0x3e, 0xd4, 0x6c, 0xb5, 0x31, 0x25, 0xdb, 0xe1,
+ 0xe0, 0x06, 0x56, 0xb8, 0xc5, 0x15, 0x1d, 0xd4, 0x0f, 0x49, 0x18, 0xc9, 0x21, 0xdb, 0xa1, 0x87,
+ 0xac, 0x72, 0x44, 0x83, 0xc7, 0x5d, 0xfc, 0x63, 0x3e, 0xd4, 0x6e, 0xe9, 0x01, 0xf5, 0xda, 0xba,
+ 0xb7, 0x2d, 0x57, 0x1b, 0x83, 0xd7, 0xf0, 0x25, 0x85, 0x14, 0xd5, 0x30, 0x24, 0x61, 0x24, 0x87,
+ 0xb8, 0x50, 0x0b, 0xa4, 0xf9, 0xac, 0xdc, 0x80, 0x83, 0x0b, 0x55, 0x86, 0xb8, 0x2f, 0xe3, 0xab,
+ 0xd5, 0x2b, 0x46, 0x32, 0xc8, 0x4e, 0xe2, 0x3a, 0x62, 0x71, 0x09, 0x75, 0x23, 0x87, 0x3b, 0x59,
+ 0x42, 0x45, 0xd3, 0x4d, 0xf6, 0xb5, 0xc6, 0xda, 0x3b, 0x95, 0x68, 0x58, 0xbe, 0xdf, 0x4e, 0xae,
+ 0xa7, 0x93, 0x4e, 0xae, 0x0b, 0x69, 0x27, 0x57, 0x6a, 0x7f, 0xf3, 0xf0, 0x11, 0x99, 0x29, 0xf7,
+ 0x52, 0xf9, 0x18, 0xdc, 0x4b, 0x4f, 0x41, 0x7d, 0x87, 0x8f, 0x04, 0xe2, 0x6a, 0x9f, 0x0a, 0x9f,
+ 0x46, 0xf8, 0xc8, 0x7e, 0x33, 0x22, 0x63, 0x9c, 0x87, 0x65, 0x91, 0x3f, 0x60, 0x08, 0xef, 0x46,
+ 0x95, 0x59, 0x9a, 0x11, 0x19, 0xe3, 0x3c, 0x3c, 0x98, 0xcb, 0x72, 0xb6, 0x45, 0x86, 0x61, 0x9e,
+ 0x41, 0x04, 0x73, 0x29, 0x22, 0x46, 0xe9, 0xe4, 0x12, 0x54, 0xbb, 0xe6, 0xa6, 0xe0, 0xad, 0x72,
+ 0x5e, 0x6e, 0x61, 0xae, 0xcf, 0x2f, 0xc8, 0xab, 0x86, 0x54, 0x2a, 0x2b, 0x49, 0x5b, 0xef, 0xa8,
+ 0x04, 0xbe, 0x36, 0x94, 0x25, 0x59, 0x89, 0xc8, 0x18, 0xe7, 0x21, 0x9f, 0x82, 0x31, 0x8f, 0x9a,
+ 0x5d, 0x83, 0x86, 0xb9, 0x84, 0x77, 0x8a, 0x88, 0x3f, 0x4d, 0xc4, 0x53, 0x30, 0xc5, 0xd9, 0xc7,
+ 0x49, 0x56, 0x1f, 0x28, 0xf4, 0xf4, 0xa7, 0x05, 0x20, 0xbd, 0xc1, 0xcf, 0x64, 0x0b, 0x86, 0x1c,
+ 0xee, 0xfd, 0xca, 0x7d, 0x9b, 0x72, 0xcc, 0x89, 0x26, 0x86, 0x25, 0x49, 0x90, 0xf8, 0xc4, 0x81,
+ 0x2a, 0xbd, 0x1d, 0x50, 0xcf, 0x09, 0x0f, 0x43, 0x1c, 0xcd, 0xcd, 0xcd, 0x62, 0x35, 0x20, 0x91,
+ 0x31, 0x94, 0xa1, 0xfd, 0xbc, 0x08, 0xf5, 0x18, 0xdf, 0xbd, 0x16, 0x95, 0xfc, 0x3c, 0xb6, 0x70,
+ 0x3a, 0xad, 0x7b, 0xb6, 0xec, 0x61, 0xb1, 0xf3, 0xd8, 0x32, 0x09, 0x97, 0x31, 0xce, 0x47, 0x66,
+ 0x00, 0xda, 0xba, 0x1f, 0x50, 0x8f, 0xcf, 0xbe, 0xa9, 0x53, 0xd0, 0x2b, 0x61, 0x0a, 0xc6, 0xb8,
+ 0xc8, 0x45, 0x79, 0xf7, 0x76, 0x39, 0x79, 0x6b, 0x5d, 0x9f, 0x8b, 0xb5, 0x2b, 0x47, 0x70, 0xb1,
+ 0x36, 0x69, 0xc1, 0xb8, 0x2a, 0xb5, 0x4a, 0x3d, 0xdc, 0x9d, 0x66, 0x62, 0xfd, 0x92, 0x82, 0xc0,
+ 0x1e, 0x50, 0xed, 0x9d, 0x02, 0x8c, 0x26, 0x5c, 0x1e, 0xe2, 0xbe, 0x39, 0x15, 0xba, 0x9f, 0xb8,
+ 0x6f, 0x2e, 0x16, 0x71, 0xff, 0x24, 0x0c, 0x09, 0x05, 0xa5, 0x23, 0xf2, 0x84, 0x0a, 0x51, 0xa6,
+ 0xb2, 0xb1, 0x4c, 0x3a, 0x55, 0xd3, 0x63, 0x99, 0xf4, 0xba, 0xa2, 0x4a, 0x17, 0xbe, 0x7a, 0x51,
+ 0xba, 0x5e, 0x5f, 0xbd, 0xa0, 0x63, 0xc8, 0xa1, 0x7d, 0x9f, 0x97, 0x3b, 0xf0, 0x76, 0xc3, 0xb5,
+ 0x5c, 0x0b, 0x86, 0x65, 0x14, 0x96, 0xec, 0x1a, 0x2f, 0xe6, 0xf0, 0xc3, 0x70, 0x1c, 0x19, 0x6f,
+ 0xa4, 0x1b, 0xdb, 0x37, 0x36, 0x37, 0x51, 0xa1, 0x93, 0xcb, 0x50, 0x73, 0x9d, 0x05, 0xdd, 0xb2,
+ 0xbb, 0x9e, 0x1a, 0xd9, 0x3f, 0xc2, 0xc6, 0xaa, 0x1b, 0x8a, 0x78, 0x67, 0x6f, 0xf2, 0x4c, 0xf8,
+ 0x92, 0x28, 0x24, 0x46, 0x39, 0xb5, 0xbf, 0x2f, 0x01, 0x8f, 0xc0, 0x21, 0xcf, 0x41, 0xad, 0x4d,
+ 0x8d, 0x2d, 0xdd, 0xb1, 0x7c, 0x75, 0x63, 0x26, 0x5b, 0x9f, 0xd7, 0x56, 0x14, 0xf1, 0x0e, 0x53,
+ 0xc1, 0x6c, 0x73, 0x99, 0x07, 0xb9, 0x47, 0xbc, 0xc4, 0x80, 0xa1, 0x96, 0xef, 0xeb, 0x1d, 0x2b,
+ 0xf7, 0x06, 0xb0, 0xb8, 0xa1, 0x50, 0x0c, 0x03, 0xe2, 0x19, 0x25, 0x34, 0x31, 0xa0, 0xd2, 0xb1,
+ 0x75, 0xcb, 0xc9, 0xfd, 0xa7, 0x18, 0x56, 0x83, 0x55, 0x86, 0x24, 0x9c, 0x52, 0xfc, 0x11, 0x05,
+ 0x36, 0xe9, 0x42, 0xdd, 0x37, 0x3c, 0xbd, 0xed, 0x6f, 0xe9, 0x33, 0xcf, 0x3c, 0x9b, 0xdb, 0xcc,
+ 0x8b, 0x44, 0x89, 0x59, 0x67, 0x0e, 0x67, 0x57, 0x9a, 0x57, 0x66, 0x67, 0x9e, 0x79, 0x16, 0xe3,
+ 0x72, 0xe2, 0x62, 0x9f, 0x79, 0x6a, 0x46, 0xf6, 0xdc, 0x23, 0x17, 0xfb, 0xcc, 0x53, 0x33, 0x18,
+ 0x97, 0xa3, 0xfd, 0x5d, 0x01, 0x6a, 0x21, 0x2f, 0x59, 0x07, 0x60, 0x63, 0x88, 0xbc, 0x53, 0xf0,
+ 0x50, 0xf7, 0xfb, 0xf3, 0x75, 0xfd, 0x7a, 0x98, 0x19, 0x63, 0x40, 0x19, 0x97, 0x2e, 0x16, 0x8f,
+ 0xfa, 0xd2, 0xc5, 0x69, 0xa8, 0x6d, 0xe9, 0x8e, 0xe9, 0x6f, 0xe9, 0xdb, 0x62, 0x28, 0x8d, 0x5d,
+ 0x43, 0x7a, 0x45, 0x25, 0x60, 0xc4, 0xa3, 0xfd, 0xf6, 0x10, 0x88, 0x5d, 0x5b, 0xd6, 0xd9, 0x4d,
+ 0xcb, 0x17, 0x61, 0xc3, 0x05, 0x9e, 0x33, 0xec, 0xec, 0xf3, 0x92, 0x8e, 0x21, 0x07, 0x39, 0x0b,
+ 0xa5, 0xb6, 0xe5, 0xc8, 0x3d, 0x1b, 0xee, 0xb2, 0x5b, 0xb1, 0x1c, 0x64, 0x34, 0x9e, 0xa4, 0xdf,
+ 0x96, 0x11, 0x5f, 0x22, 0x49, 0xbf, 0x8d, 0x8c, 0xc6, 0x16, 0xd1, 0xb6, 0xeb, 0x6e, 0xb3, 0x6e,
+ 0xab, 0x02, 0xc3, 0xca, 0x7c, 0x2a, 0xe7, 0x8b, 0xe8, 0xe5, 0x64, 0x12, 0xa6, 0x79, 0xc9, 0x3a,
+ 0x3c, 0xf2, 0x06, 0xf5, 0x5c, 0x39, 0x4e, 0x35, 0x6d, 0x4a, 0x3b, 0x0a, 0x46, 0x18, 0x41, 0x3c,
+ 0xbe, 0xec, 0x8b, 0xd9, 0x2c, 0xd8, 0x2f, 0x2f, 0x8f, 0x54, 0xd5, 0xbd, 0x16, 0x0d, 0x56, 0x3d,
+ 0xd7, 0xa0, 0xbe, 0x6f, 0x39, 0x2d, 0x05, 0x3b, 0x14, 0xc1, 0xae, 0x65, 0xb3, 0x60, 0xbf, 0xbc,
+ 0xe4, 0x65, 0x98, 0x10, 0x49, 0xc2, 0x5c, 0x98, 0xdd, 0xd1, 0x2d, 0x5b, 0xdf, 0xb0, 0x6c, 0xf5,
+ 0x83, 0xb5, 0x51, 0xb1, 0x33, 0xb2, 0xd6, 0x87, 0x07, 0xfb, 0xe6, 0x26, 0x57, 0x61, 0x5c, 0xed,
+ 0x8b, 0xad, 0x52, 0xaf, 0x19, 0xee, 0xe4, 0x8f, 0x36, 0x2e, 0xb0, 0x15, 0xeb, 0x3c, 0xed, 0x78,
+ 0xd4, 0xe0, 0x76, 0x63, 0x8a, 0x0b, 0x7b, 0xf2, 0x11, 0x84, 0x33, 0x7c, 0xbb, 0x7e, 0xbd, 0x33,
+ 0xe7, 0xba, 0xb6, 0xe9, 0xde, 0x72, 0x54, 0xdd, 0x85, 0x69, 0xc6, 0xb7, 0xc2, 0x9a, 0x99, 0x1c,
+ 0xd8, 0x27, 0x27, 0xab, 0x39, 0x4f, 0x99, 0x77, 0x6f, 0x39, 0x69, 0x54, 0x88, 0x6a, 0xde, 0xec,
+ 0xc3, 0x83, 0x7d, 0x73, 0x93, 0x05, 0x20, 0xe9, 0x1a, 0xac, 0x77, 0xb8, 0x39, 0x37, 0xda, 0x38,
+ 0x23, 0xae, 0x07, 0x49, 0xa7, 0x62, 0x46, 0x0e, 0xb2, 0x0c, 0xa7, 0xd3, 0x54, 0x26, 0x8e, 0x9f,
+ 0x11, 0x18, 0x15, 0x17, 0x83, 0x62, 0x46, 0x3a, 0x66, 0xe6, 0xd2, 0x7e, 0xa7, 0x08, 0xa3, 0x89,
+ 0xf3, 0xe4, 0x0f, 0xdc, 0xb9, 0x5d, 0x66, 0x43, 0xb7, 0xfd, 0xd6, 0xd2, 0xfc, 0x15, 0xaa, 0x9b,
+ 0xd4, 0xbb, 0x46, 0xd5, 0xd9, 0x7f, 0x3e, 0xa8, 0xac, 0x24, 0x52, 0x30, 0xc5, 0x49, 0x36, 0xa1,
+ 0x22, 0x3c, 0xc2, 0x79, 0xff, 0x14, 0xa1, 0x74, 0xc4, 0xdd, 0xc2, 0xf2, 0xf7, 0x2a, 0xae, 0x47,
+ 0x51, 0xc0, 0x6b, 0x01, 0x8c, 0xc4, 0x39, 0xd8, 0x40, 0x12, 0x99, 0x9b, 0xc3, 0x09, 0x53, 0x73,
+ 0x09, 0x4a, 0x41, 0x30, 0xe8, 0x89, 0x60, 0xb1, 0xc3, 0xb0, 0xb6, 0x8c, 0x0c, 0x43, 0xdb, 0x64,
+ 0xdf, 0xce, 0xf7, 0x2d, 0xd7, 0x91, 0xd7, 0x43, 0xaf, 0xc3, 0x70, 0x20, 0x9d, 0x6c, 0x83, 0x9d,
+ 0x68, 0xe6, 0x36, 0x8a, 0x72, 0xb0, 0x29, 0x2c, 0xed, 0x4f, 0x8b, 0x50, 0x0b, 0x17, 0xc4, 0x07,
+ 0xb8, 0x76, 0xd9, 0x85, 0x5a, 0x18, 0x6e, 0x94, 0xfb, 0xe7, 0x73, 0x51, 0x14, 0x0c, 0x5f, 0xc3,
+ 0x85, 0xaf, 0x18, 0xc9, 0x88, 0x87, 0x32, 0x95, 0x72, 0x84, 0x32, 0x75, 0x60, 0x38, 0xf0, 0xac,
+ 0x56, 0x4b, 0x5a, 0xe7, 0x79, 0x62, 0x99, 0x42, 0x75, 0xad, 0x09, 0x40, 0xa9, 0x59, 0xf1, 0x82,
+ 0x4a, 0x8c, 0xf6, 0x1a, 0x8c, 0xa7, 0x39, 0xb9, 0xe9, 0x6a, 0x6c, 0x51, 0xb3, 0x6b, 0x2b, 0x1d,
+ 0x47, 0xa6, 0xab, 0xa4, 0x63, 0xc8, 0xc1, 0x96, 0xaf, 0xec, 0x33, 0xbd, 0xe1, 0x3a, 0xca, 0x7c,
+ 0xe4, 0xab, 0x80, 0x35, 0x49, 0xc3, 0x30, 0x55, 0xfb, 0xab, 0x12, 0x9c, 0x8d, 0xdc, 0x1a, 0x2b,
+ 0xba, 0xa3, 0xb7, 0x0e, 0xf0, 0xc7, 0xb1, 0x0f, 0xce, 0x88, 0x1c, 0xf6, 0xee, 0xfc, 0xd2, 0x03,
+ 0x70, 0x77, 0xfe, 0x3f, 0x14, 0x81, 0x87, 0x46, 0x92, 0xaf, 0xc2, 0x88, 0x1e, 0xfb, 0xd9, 0xa4,
+ 0xfc, 0x9c, 0x97, 0x73, 0x7f, 0x4e, 0x1e, 0x81, 0x19, 0x86, 0xfa, 0xc4, 0xa9, 0x98, 0x10, 0x48,
+ 0x5c, 0xa8, 0x6e, 0xea, 0xb6, 0xcd, 0x6c, 0xa1, 0xdc, 0xdb, 0x34, 0x09, 0xe1, 0xbc, 0x99, 0x2f,
+ 0x48, 0x68, 0x0c, 0x85, 0x90, 0x37, 0x0b, 0x30, 0xea, 0xc5, 0x97, 0x49, 0xf2, 0x83, 0xe4, 0xd9,
+ 0xc4, 0x8f, 0xa1, 0xc5, 0x03, 0x8b, 0xe2, 0x6b, 0xb1, 0xa4, 0x4c, 0xed, 0x2f, 0x0b, 0x30, 0xda,
+ 0xb4, 0x2d, 0xd3, 0x72, 0x5a, 0xc7, 0x78, 0x75, 0xff, 0x0d, 0xa8, 0xf8, 0xb6, 0x65, 0xd2, 0x01,
+ 0x67, 0x13, 0x31, 0x8f, 0x31, 0x00, 0x14, 0x38, 0xc9, 0x7f, 0x01, 0x94, 0x0e, 0xf0, 0x2f, 0x80,
+ 0x5f, 0x0d, 0x81, 0x0c, 0xf2, 0x25, 0x5d, 0xa8, 0xb5, 0xd4, 0x15, 0xe3, 0xb2, 0x8e, 0x57, 0x72,
+ 0x5c, 0x4f, 0x97, 0xb8, 0xac, 0x5c, 0x8c, 0xfd, 0x21, 0x11, 0x23, 0x49, 0x84, 0x26, 0xff, 0x72,
+ 0x3a, 0x9f, 0xf3, 0x2f, 0xa7, 0x42, 0x5c, 0xef, 0x7f, 0x4e, 0x75, 0x28, 0x6f, 0x05, 0x41, 0x47,
+ 0x36, 0xa6, 0xc1, 0xa3, 0xb8, 0xa3, 0x1b, 0x52, 0x84, 0x4d, 0xc4, 0xde, 0x91, 0x43, 0x33, 0x11,
+ 0x8e, 0x1e, 0xfe, 0xd5, 0x6a, 0x2e, 0x57, 0xc0, 0x40, 0x5c, 0x04, 0x7b, 0x47, 0x0e, 0x4d, 0xbe,
+ 0x02, 0xf5, 0xc0, 0xd3, 0x1d, 0x7f, 0xd3, 0xf5, 0xda, 0xd4, 0x93, 0x6b, 0xd4, 0x85, 0x1c, 0x3f,
+ 0xfa, 0x5c, 0x8b, 0xd0, 0xc4, 0x4e, 0x64, 0x82, 0x84, 0x71, 0x69, 0x64, 0x1b, 0xaa, 0x5d, 0x53,
+ 0x14, 0x4c, 0xba, 0x9f, 0x66, 0xf3, 0xfc, 0xbb, 0x35, 0x16, 0x0e, 0xa0, 0xde, 0x30, 0x14, 0x90,
+ 0xfc, 0x81, 0xdb, 0xf0, 0x51, 0xfd, 0xc0, 0x2d, 0xde, 0x1a, 0xb3, 0xae, 0x6f, 0x20, 0x6d, 0x69,
+ 0xd7, 0x3a, 0x2d, 0x19, 0xcd, 0xb4, 0x90, 0xdb, 0xe4, 0x14, 0x22, 0xeb, 0xa1, 0x6d, 0xec, 0xb4,
+ 0x50, 0xc9, 0xd0, 0xda, 0x20, 0x77, 0x09, 0x88, 0x91, 0xf8, 0xcd, 0x89, 0x38, 0x53, 0x34, 0x7d,
+ 0xb0, 0xf1, 0x20, 0xfc, 0xdf, 0x46, 0xec, 0x9a, 0xe5, 0xcc, 0xff, 0x99, 0x68, 0x7f, 0x56, 0x84,
+ 0xd2, 0xda, 0x72, 0x53, 0x5c, 0x9d, 0xc8, 0xff, 0x21, 0x44, 0x9b, 0xdb, 0x56, 0xe7, 0x26, 0xf5,
+ 0xac, 0xcd, 0x5d, 0xb9, 0xf4, 0x8e, 0x5d, 0x9d, 0x98, 0xe6, 0xc0, 0x8c, 0x5c, 0xe4, 0x15, 0x18,
+ 0x31, 0xf4, 0x39, 0xea, 0x05, 0x83, 0x38, 0x16, 0xf8, 0xe1, 0xc9, 0xb9, 0xd9, 0x28, 0x3b, 0x26,
+ 0xc0, 0xc8, 0x3a, 0x80, 0x11, 0x41, 0x97, 0x0e, 0xed, 0x0e, 0x89, 0x01, 0xc7, 0x80, 0x08, 0x42,
+ 0x6d, 0x9b, 0xb1, 0x72, 0xd4, 0xf2, 0x61, 0x50, 0x79, 0xcb, 0xb9, 0xa6, 0xf2, 0x62, 0x04, 0xa3,
+ 0x39, 0x30, 0x9a, 0xf8, 0xf7, 0x09, 0xf9, 0x24, 0x54, 0xdd, 0x4e, 0x6c, 0x38, 0xad, 0xf1, 0xb8,
+ 0xc9, 0xea, 0x0d, 0x49, 0xbb, 0xb3, 0x37, 0x39, 0xba, 0xec, 0xb6, 0x2c, 0x43, 0x11, 0x30, 0x64,
+ 0x27, 0x1a, 0x0c, 0xf1, 0x13, 0x4f, 0xea, 0xcf, 0x27, 0x7c, 0xee, 0xe0, 0x3f, 0x27, 0xf0, 0x51,
+ 0xa6, 0x68, 0x5f, 0x2b, 0x43, 0xb4, 0xb7, 0x46, 0x7c, 0x18, 0x12, 0x11, 0xdd, 0x72, 0xe4, 0x3e,
+ 0xd6, 0xe0, 0x71, 0x29, 0x8a, 0xb4, 0xa0, 0xf4, 0x9a, 0xbb, 0x91, 0x7b, 0xe0, 0x8e, 0x1d, 0x75,
+ 0x16, 0xbe, 0xb2, 0x18, 0x01, 0x99, 0x04, 0xf2, 0xdf, 0x0b, 0x70, 0xd2, 0x4f, 0x9b, 0xbe, 0xb2,
+ 0x39, 0x60, 0x7e, 0x1b, 0x3f, 0x6d, 0x4c, 0xcb, 0x00, 0xd7, 0x7e, 0xc9, 0xd8, 0x5b, 0x16, 0xa6,
+ 0x7f, 0xb1, 0xe9, 0x25, 0x9b, 0xd3, 0x62, 0xce, 0xff, 0xf5, 0x25, 0xf5, 0x9f, 0xa4, 0xa1, 0x14,
+ 0xa5, 0x7d, 0xa3, 0x08, 0xf5, 0xd8, 0x68, 0x9d, 0xfb, 0x87, 0x3a, 0xb7, 0x53, 0x3f, 0xd4, 0x59,
+ 0x1d, 0x7c, 0x0f, 0x38, 0x2a, 0xd5, 0x71, 0xff, 0x53, 0xe7, 0x47, 0x45, 0x28, 0xad, 0xcf, 0x2f,
+ 0x24, 0x17, 0xad, 0x85, 0xfb, 0xb0, 0x68, 0xdd, 0x82, 0xe1, 0x8d, 0xae, 0x65, 0x07, 0x96, 0x93,
+ 0xfb, 0x32, 0x06, 0xf5, 0xff, 0x21, 0xb9, 0xc7, 0x20, 0x50, 0x51, 0xc1, 0x93, 0x16, 0x0c, 0xb7,
+ 0xc4, 0x6d, 0x78, 0xb9, 0x23, 0xe3, 0xe4, 0xad, 0x7a, 0x42, 0x90, 0x7c, 0x41, 0x85, 0xae, 0xed,
+ 0x82, 0xfc, 0x91, 0xfa, 0x7d, 0xd7, 0xa6, 0xf6, 0x15, 0x08, 0xad, 0x80, 0xfb, 0x2f, 0xfc, 0x6f,
+ 0x0a, 0x90, 0x34, 0x7c, 0xee, 0x7f, 0x6b, 0xda, 0x4e, 0xb7, 0xa6, 0xf9, 0xa3, 0xe8, 0x7c, 0xd9,
+ 0x0d, 0x4a, 0xfb, 0xad, 0x22, 0x0c, 0xdd, 0xb7, 0x03, 0xb4, 0x34, 0x11, 0xe4, 0x37, 0x97, 0x73,
+ 0x60, 0xec, 0x1b, 0xe2, 0xd7, 0x4e, 0x85, 0xf8, 0xe5, 0xfd, 0x63, 0xea, 0x3d, 0x02, 0xfc, 0xfe,
+ 0xa8, 0x00, 0x72, 0x58, 0x5e, 0x72, 0xfc, 0x40, 0x77, 0x0c, 0x4a, 0x8c, 0x70, 0x0e, 0xc8, 0x1b,
+ 0x49, 0x22, 0xa3, 0xad, 0xc4, 0xb4, 0xcf, 0x9f, 0xd5, 0x98, 0x4f, 0x3e, 0x0e, 0xd5, 0x2d, 0xd7,
+ 0x0f, 0xf8, 0x38, 0x5f, 0x4c, 0x7a, 0x97, 0xae, 0x48, 0x3a, 0x86, 0x1c, 0xe9, 0x1d, 0xd7, 0x4a,
+ 0xff, 0x1d, 0x57, 0xed, 0xbb, 0x45, 0x18, 0x79, 0xbf, 0x9c, 0x02, 0xce, 0x0a, 0x89, 0x2c, 0xe5,
+ 0x0c, 0x89, 0x2c, 0x1f, 0x26, 0x24, 0x52, 0xfb, 0x71, 0x01, 0xe0, 0xbe, 0x1d, 0x41, 0x36, 0x93,
+ 0xd1, 0x8a, 0xb9, 0xdb, 0x55, 0x76, 0xac, 0xe2, 0x6f, 0x54, 0x54, 0x95, 0x78, 0xa4, 0xe2, 0x5b,
+ 0x05, 0x18, 0xd3, 0x13, 0xd1, 0x7f, 0xb9, 0x4d, 0xcb, 0x54, 0x30, 0x61, 0x78, 0xdc, 0x32, 0x49,
+ 0xc7, 0x94, 0x58, 0xf2, 0x7c, 0x74, 0xfd, 0xed, 0xf5, 0xa8, 0xd9, 0xf7, 0xdc, 0x5b, 0xcb, 0xcd,
+ 0x9c, 0x04, 0xe7, 0x3d, 0xa2, 0x2d, 0x4b, 0x47, 0x12, 0x6d, 0x19, 0x3f, 0x47, 0x56, 0xbe, 0xeb,
+ 0x39, 0xb2, 0x1d, 0xa8, 0x6d, 0x7a, 0x6e, 0x9b, 0x07, 0x34, 0xca, 0x7f, 0xad, 0x5e, 0xce, 0x31,
+ 0xa7, 0x44, 0x7f, 0x19, 0x8f, 0x7c, 0x3c, 0x0b, 0x0a, 0x1f, 0x23, 0x51, 0xdc, 0x2d, 0xee, 0x0a,
+ 0xa9, 0x43, 0x47, 0x29, 0x35, 0x1c, 0x4b, 0xd6, 0x04, 0x3a, 0x2a, 0x31, 0xc9, 0x20, 0xc6, 0xe1,
+ 0xfb, 0x13, 0xc4, 0xa8, 0xfd, 0xa8, 0xac, 0x06, 0xb0, 0x07, 0xee, 0xa6, 0xc5, 0xf7, 0xff, 0xd1,
+ 0xd5, 0xf4, 0xb9, 0xd2, 0xe1, 0xfb, 0x78, 0xae, 0xb4, 0x7a, 0x34, 0xe7, 0x4a, 0x6b, 0x87, 0x38,
+ 0x57, 0xba, 0x57, 0x82, 0xd4, 0xa2, 0xeb, 0x83, 0x0d, 0x96, 0x7f, 0x52, 0x1b, 0x2c, 0x6f, 0x17,
+ 0x21, 0x1a, 0x45, 0x0e, 0x19, 0x80, 0xf2, 0x32, 0x54, 0xdb, 0xfa, 0xed, 0x79, 0x6a, 0xeb, 0xbb,
+ 0x79, 0xfe, 0xae, 0xb9, 0x22, 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80, 0x15, 0xde, 0x70, 0x9d, 0xdb,
+ 0x55, 0x1d, 0x5d, 0x96, 0x2d, 0x9c, 0x61, 0xd1, 0x3b, 0xc6, 0xc4, 0x68, 0x7f, 0x58, 0x04, 0x79,
+ 0x15, 0x3a, 0xa1, 0x50, 0xd9, 0xb4, 0x6e, 0x53, 0x33, 0x77, 0x38, 0x69, 0xec, 0x9f, 0xc7, 0xc2,
+ 0x17, 0xcf, 0x09, 0x28, 0xd0, 0xb9, 0x93, 0x55, 0xec, 0xad, 0x48, 0xfd, 0xe5, 0x70, 0xb2, 0xc6,
+ 0xf7, 0x68, 0xa4, 0x93, 0x55, 0x90, 0x50, 0xc9, 0x10, 0x3e, 0x5d, 0xbe, 0xcd, 0x9e, 0x7b, 0x2b,
+ 0x29, 0xb1, 0x5d, 0xaf, 0x7c, 0xba, 0xbe, 0x38, 0x58, 0x2e, 0x65, 0x34, 0xbe, 0xf4, 0xc3, 0x9f,
+ 0x5c, 0x78, 0xe8, 0xc7, 0x3f, 0xb9, 0xf0, 0xd0, 0xbb, 0x3f, 0xb9, 0xf0, 0xd0, 0xd7, 0xf6, 0x2f,
+ 0x14, 0x7e, 0xb8, 0x7f, 0xa1, 0xf0, 0xe3, 0xfd, 0x0b, 0x85, 0x77, 0xf7, 0x2f, 0x14, 0xfe, 0x7c,
+ 0xff, 0x42, 0xe1, 0xbf, 0xfc, 0xc5, 0x85, 0x87, 0xbe, 0xf8, 0x5c, 0x54, 0x84, 0x69, 0x55, 0x84,
+ 0x69, 0x25, 0x70, 0xba, 0xb3, 0xdd, 0x9a, 0x66, 0x45, 0x88, 0x28, 0xaa, 0x08, 0xff, 0x18, 0x00,
+ 0x00, 0xff, 0xff, 0xa1, 0xe2, 0x38, 0xfd, 0x6e, 0x95, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -3643,6 +3709,46 @@ func (m *Authorization) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *Backoff) 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 *Backoff) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Backoff) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if m.Steps != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.Steps))
+ i--
+ dAtA[i] = 0x10
+ }
+ if m.Interval != nil {
+ {
+ size, err := m.Interval.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
func (m *BasicAuth) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -7304,6 +7410,48 @@ func (m *RedisSettings) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *RetryStrategy) 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 *RetryStrategy) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *RetryStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if m.OnFailure != nil {
+ i -= len(*m.OnFailure)
+ copy(dAtA[i:], *m.OnFailure)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(*m.OnFailure)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if m.BackOff != nil {
+ {
+ size, err := m.BackOff.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
func (m *SASL) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -7840,6 +7988,16 @@ func (m *Sink) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ {
+ size, err := m.RetryStrategy.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
if m.Fallback != nil {
{
size, err := m.Fallback.MarshalToSizedBuffer(dAtA[:i])
@@ -9198,6 +9356,22 @@ func (m *Authorization) Size() (n int) {
return n
}
+func (m *Backoff) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.Interval != nil {
+ l = m.Interval.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ if m.Steps != nil {
+ n += 1 + sovGenerated(uint64(*m.Steps))
+ }
+ return n
+}
+
func (m *BasicAuth) Size() (n int) {
if m == nil {
return 0
@@ -10544,6 +10718,23 @@ func (m *RedisSettings) Size() (n int) {
return n
}
+func (m *RetryStrategy) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.BackOff != nil {
+ l = m.BackOff.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ if m.OnFailure != nil {
+ l = len(*m.OnFailure)
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ return n
+}
+
func (m *SASL) Size() (n int) {
if m == nil {
return 0
@@ -10757,6 +10948,8 @@ func (m *Sink) Size() (n int) {
l = m.Fallback.Size()
n += 1 + l + sovGenerated(uint64(l))
}
+ l = m.RetryStrategy.Size()
+ n += 1 + l + sovGenerated(uint64(l))
return n
}
@@ -11289,6 +11482,17 @@ func (this *Authorization) String() string {
}, "")
return s
}
+func (this *Backoff) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&Backoff{`,
+ `Interval:` + strings.Replace(fmt.Sprintf("%v", this.Interval), "Duration", "v11.Duration", 1) + `,`,
+ `Steps:` + valueToStringGenerated(this.Steps) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func (this *BasicAuth) String() string {
if this == nil {
return "nil"
@@ -12238,6 +12442,17 @@ func (this *RedisSettings) String() string {
}, "")
return s
}
+func (this *RetryStrategy) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&RetryStrategy{`,
+ `BackOff:` + strings.Replace(this.BackOff.String(), "Backoff", "Backoff", 1) + `,`,
+ `OnFailure:` + valueToStringGenerated(this.OnFailure) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func (this *SASL) String() string {
if this == nil {
return "nil"
@@ -12367,6 +12582,7 @@ func (this *Sink) String() string {
s := strings.Join([]string{`&Sink{`,
`AbstractSink:` + strings.Replace(strings.Replace(this.AbstractSink.String(), "AbstractSink", "AbstractSink", 1), `&`, ``, 1) + `,`,
`Fallback:` + strings.Replace(this.Fallback.String(), "AbstractSink", "AbstractSink", 1) + `,`,
+ `RetryStrategy:` + strings.Replace(strings.Replace(this.RetryStrategy.String(), "RetryStrategy", "RetryStrategy", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
@@ -14085,6 +14301,112 @@ func (m *Authorization) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *Backoff) 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 ErrIntOverflowGenerated
+ }
+ 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: Backoff: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: Backoff: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Interval", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Interval == nil {
+ m.Interval = &v11.Duration{}
+ }
+ if err := m.Interval.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Steps", wireType)
+ }
+ var v uint32
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Steps = &v
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *BasicAuth) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -26019,6 +26341,125 @@ func (m *RedisSettings) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *RetryStrategy) 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 ErrIntOverflowGenerated
+ }
+ 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: RetryStrategy: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: RetryStrategy: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field BackOff", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.BackOff == nil {
+ m.BackOff = &Backoff{}
+ }
+ if err := m.BackOff.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field OnFailure", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ 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 ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ s := OnFailureRetryStrategy(dAtA[iNdEx:postIndex])
+ m.OnFailure = &s
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *SASL) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -27614,6 +28055,39 @@ func (m *Sink) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RetryStrategy", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.RetryStrategy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 8c5a6db0f2..9c513d6fde 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -209,6 +209,18 @@ message Authorization {
optional k8s.io.api.core.v1.SecretKeySelector token = 1;
}
+// Backoff defines parameters used to systematically configure the retry strategy.
+message Backoff {
+ // Interval sets the delay to wait before retry, after a failure occurs.
+ // +kubebuilder:default="1ms"
+ // +optional
+ optional k8s.io.apimachinery.pkg.apis.meta.v1.Duration interval = 1;
+
+ // Steps defines the number of times to try writing to a sink including retries
+ // +optional
+ optional uint32 steps = 2;
+}
+
// BasicAuth represents the basic authentication approach which contains a user name and a password.
message BasicAuth {
// Secret for auth user
@@ -1248,6 +1260,19 @@ message RedisSettings {
optional string sentinel = 4;
}
+// RetryStrategy struct encapsulates the settings for retrying operations in the event of failures.
+// It includes a BackOff strategy to manage the timing of retries and defines the action to take upon failure.
+message RetryStrategy {
+ // BackOff specifies the parameters for the backoff strategy, controlling how delays between retries should increase.
+ // +optional
+ optional Backoff backoff = 1;
+
+ // OnFailure specifies the action to take when a retry fails. The default action is to retry.
+ // +optional
+ // +kubebuilder:default="retry"
+ optional string onFailure = 2;
+}
+
message SASL {
// SASL mechanism to use
optional string mechanism = 1;
@@ -1418,6 +1443,10 @@ message Sink {
// initiated if the ud-sink response field sets it.
// +optional
optional AbstractSink fallback = 2;
+
+ // RetryStrategy struct encapsulates the settings for retrying operations in the event of failures.
+ // +optional
+ optional RetryStrategy retryStrategy = 3;
}
// SlidingWindow describes a sliding window
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 8ed0f228c0..79aab81c33 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -34,6 +34,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.AbstractSink": schema_pkg_apis_numaflow_v1alpha1_AbstractSink(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.AbstractVertex": schema_pkg_apis_numaflow_v1alpha1_AbstractVertex(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Authorization": schema_pkg_apis_numaflow_v1alpha1_Authorization(ref),
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Backoff": schema_pkg_apis_numaflow_v1alpha1_Backoff(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.BasicAuth": schema_pkg_apis_numaflow_v1alpha1_BasicAuth(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Blackhole": schema_pkg_apis_numaflow_v1alpha1_Blackhole(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.BufferServiceConfig": schema_pkg_apis_numaflow_v1alpha1_BufferServiceConfig(ref),
@@ -91,6 +92,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisBufferService": schema_pkg_apis_numaflow_v1alpha1_RedisBufferService(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisConfig": schema_pkg_apis_numaflow_v1alpha1_RedisConfig(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisSettings": schema_pkg_apis_numaflow_v1alpha1_RedisSettings(ref),
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RetryStrategy": schema_pkg_apis_numaflow_v1alpha1_RetryStrategy(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.SASL": schema_pkg_apis_numaflow_v1alpha1_SASL(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.SASLPlain": schema_pkg_apis_numaflow_v1alpha1_SASLPlain(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale": schema_pkg_apis_numaflow_v1alpha1_Scale(ref),
@@ -608,6 +610,34 @@ func schema_pkg_apis_numaflow_v1alpha1_Authorization(ref common.ReferenceCallbac
}
}
+func schema_pkg_apis_numaflow_v1alpha1_Backoff(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Backoff defines parameters used to systematically configure the retry strategy.",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "interval": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Interval sets the delay to wait before retry, after a failure occurs.",
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+ },
+ },
+ "steps": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Steps defines the number of times to try writing to a sink including retries",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"},
+ }
+}
+
func schema_pkg_apis_numaflow_v1alpha1_BasicAuth(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -4202,6 +4232,34 @@ func schema_pkg_apis_numaflow_v1alpha1_RedisSettings(ref common.ReferenceCallbac
}
}
+func schema_pkg_apis_numaflow_v1alpha1_RetryStrategy(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "RetryStrategy struct encapsulates the settings for retrying operations in the event of failures. It includes a BackOff strategy to manage the timing of retries and defines the action to take upon failure.",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "backoff": {
+ SchemaProps: spec.SchemaProps{
+ Description: "BackOff specifies the parameters for the backoff strategy, controlling how delays between retries should increase.",
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Backoff"),
+ },
+ },
+ "onFailure": {
+ SchemaProps: spec.SchemaProps{
+ Description: "OnFailure specifies the action to take when a retry fails. The default action is to retry.",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Backoff"},
+ }
+}
+
func schema_pkg_apis_numaflow_v1alpha1_SASL(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -4748,11 +4806,18 @@ func schema_pkg_apis_numaflow_v1alpha1_Sink(ref common.ReferenceCallback) common
Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.AbstractSink"),
},
},
+ "retryStrategy": {
+ SchemaProps: spec.SchemaProps{
+ Description: "RetryStrategy struct encapsulates the settings for retrying operations in the event of failures.",
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RetryStrategy"),
+ },
+ },
},
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.AbstractSink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Blackhole", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.KafkaSink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Log", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDSink"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.AbstractSink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Blackhole", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.KafkaSink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Log", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RetryStrategy", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDSink"},
}
}
diff --git a/pkg/apis/numaflow/v1alpha1/retry_strategy.go b/pkg/apis/numaflow/v1alpha1/retry_strategy.go
new file mode 100644
index 0000000000..12c9daab4b
--- /dev/null
+++ b/pkg/apis/numaflow/v1alpha1/retry_strategy.go
@@ -0,0 +1,102 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/wait"
+)
+
+type OnFailureRetryStrategy string
+
+// Constants representing the possible actions that can be taken when a failure occurs during an operation.
+const (
+ OnFailureRetry OnFailureRetryStrategy = "retry" // Retry the operation.
+ OnFailureFallback OnFailureRetryStrategy = "fallback" // Reroute the operation to a fallback mechanism.
+ OnFailureDrop OnFailureRetryStrategy = "drop" // Drop the operation and perform no further action.
+)
+
+// RetryStrategy struct encapsulates the settings for retrying operations in the event of failures.
+// It includes a BackOff strategy to manage the timing of retries and defines the action to take upon failure.
+type RetryStrategy struct {
+ // BackOff specifies the parameters for the backoff strategy, controlling how delays between retries should increase.
+ // +optional
+ BackOff *Backoff `json:"backoff,omitempty" protobuf:"bytes,1,opt,name=backoff"`
+ // OnFailure specifies the action to take when a retry fails. The default action is to retry.
+ // +optional
+ // +kubebuilder:default="retry"
+ OnFailure *OnFailureRetryStrategy `json:"onFailure,omitempty" protobuf:"bytes,2,opt,name=onFailure"`
+}
+
+// Backoff defines parameters used to systematically configure the retry strategy.
+type Backoff struct {
+ // Interval sets the delay to wait before retry, after a failure occurs.
+ // +kubebuilder:default="1ms"
+ // +optional
+ Interval *metav1.Duration `json:"interval,omitempty" protobuf:"bytes,1,opt,name=interval"`
+ // Steps defines the number of times to try writing to a sink including retries
+ // +optional
+ Steps *uint32 `json:"steps,omitempty" protobuf:"bytes,2,opt,name=steps"`
+ // TODO(Retry): Enable after we add support for exponential backoff
+ //// +optional
+ //Cap *metav1.Duration `json:"cap,omitempty" protobuf:"bytes,3,opt,name=cap"`
+ //// +optional
+ //Factor *floatstr `json:"factor,omitempty" protobuf:"bytes,2,opt,name=factor"`
+ //// +optional
+ //Jitter *floatstr `json:"jitter,omitempty" protobuf:"bytes,3,opt,name=jitter"`
+}
+
+// GetBackoff constructs a wait.Backoff configuration using default values and optionally overrides
+// these defaults with custom settings specified in the RetryStrategy.
+func (r RetryStrategy) GetBackoff() wait.Backoff {
+ // Initialize the Backoff structure with default values.
+ wt := wait.Backoff{
+ Duration: DefaultRetryInterval,
+ Steps: DefaultRetrySteps,
+ }
+
+ // If a custom back-off configuration is present, check and substitute the respective parts.
+ if r.BackOff != nil {
+ // If a custom Interval is specified, override the default Duration.
+ if r.BackOff.Interval != nil {
+ wt.Duration = r.BackOff.Interval.Duration
+ }
+ // If custom Steps are specified, override the default Steps.
+ if r.BackOff.Steps != nil {
+ wt.Steps = int(*r.BackOff.Steps)
+ }
+ }
+
+ // Returns the fully configured Backoff structure, which is either default or overridden by custom settings.
+ return wt
+}
+
+// GetOnFailureRetryStrategy retrieves the currently set strategy for handling failures upon retrying.
+// This method uses a default strategy which can be overridden by a custom strategy defined in RetryStrategy.
+func (r RetryStrategy) GetOnFailureRetryStrategy() OnFailureRetryStrategy {
+ // If the OnFailure is not defined initialize with the Default value
+ if r.OnFailure == nil {
+ return DefaultOnFailureRetryStrategy
+ }
+ switch *r.OnFailure {
+ case OnFailureRetry, OnFailureFallback, OnFailureDrop:
+ // If a custom on-failure behavior is specified
+ return *r.OnFailure
+ default:
+ return DefaultOnFailureRetryStrategy
+ }
+}
diff --git a/pkg/apis/numaflow/v1alpha1/retry_strategy_test.go b/pkg/apis/numaflow/v1alpha1/retry_strategy_test.go
new file mode 100644
index 0000000000..e8968b9d36
--- /dev/null
+++ b/pkg/apis/numaflow/v1alpha1/retry_strategy_test.go
@@ -0,0 +1,115 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "testing"
+ "time"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/wait"
+)
+
+func TestGetBackoff(t *testing.T) {
+ steps := uint32(10)
+ tests := []struct {
+ name string
+ strategy RetryStrategy
+ expectedBackoff wait.Backoff
+ steps uint32
+ }{
+ {
+ name: "default backoff",
+ strategy: RetryStrategy{},
+ expectedBackoff: wait.Backoff{
+ Duration: DefaultRetryInterval,
+ Steps: DefaultRetrySteps,
+ },
+ },
+ {
+ name: "custom backoff",
+ strategy: RetryStrategy{
+ BackOff: &Backoff{
+ Interval: &metav1.Duration{Duration: 10 * time.Second},
+ Steps: &steps,
+ },
+ },
+ expectedBackoff: wait.Backoff{
+ Duration: 10 * time.Second,
+ Steps: 10,
+ },
+ },
+ {
+ name: "custom backoff - 2",
+ strategy: RetryStrategy{
+ BackOff: &Backoff{
+ Interval: &metav1.Duration{Duration: 10 * time.Second},
+ },
+ },
+ expectedBackoff: wait.Backoff{
+ Duration: 10 * time.Second,
+ Steps: DefaultRetrySteps,
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := tt.strategy.GetBackoff()
+ if got.Duration != tt.expectedBackoff.Duration || got.Steps != tt.expectedBackoff.Steps {
+ t.Errorf("GetBackoff() = %v, want %v", got, tt.expectedBackoff)
+ }
+ })
+ }
+}
+
+func TestGetOnFailureRetryStrategy(t *testing.T) {
+ tests := []struct {
+ name string
+ strategy RetryStrategy
+ expectedOnFailure OnFailureRetryStrategy
+ }{
+ {
+ name: "default strategy",
+ strategy: RetryStrategy{},
+ expectedOnFailure: DefaultOnFailureRetryStrategy,
+ },
+ {
+ name: "custom strategy",
+ strategy: RetryStrategy{
+ OnFailure: func() *OnFailureRetryStrategy { s := OnFailureDrop; return &s }(),
+ },
+ expectedOnFailure: OnFailureDrop,
+ },
+ {
+ name: "incorrect strategy - use default",
+ strategy: RetryStrategy{
+ OnFailure: func() *OnFailureRetryStrategy { s := "xxxx"; return (*OnFailureRetryStrategy)(&s) }(),
+ },
+ expectedOnFailure: DefaultOnFailureRetryStrategy,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := tt.strategy.GetOnFailureRetryStrategy()
+ if got != tt.expectedOnFailure {
+ t.Errorf("GetOnFailureRetryStrategy() = %v, want %v", got, tt.expectedOnFailure)
+ }
+ })
+ }
+}
diff --git a/pkg/apis/numaflow/v1alpha1/sink.go b/pkg/apis/numaflow/v1alpha1/sink.go
index 8530259214..d596a079e6 100644
--- a/pkg/apis/numaflow/v1alpha1/sink.go
+++ b/pkg/apis/numaflow/v1alpha1/sink.go
@@ -27,6 +27,9 @@ type Sink struct {
// initiated if the ud-sink response field sets it.
// +optional
Fallback *AbstractSink `json:"fallback,omitempty" protobuf:"bytes,2,opt,name=fallback"`
+ // RetryStrategy struct encapsulates the settings for retrying operations in the event of failures.
+ // +optional
+ RetryStrategy RetryStrategy `json:"retryStrategy,omitempty" protobuf:"bytes,3,opt,name=retryStrategy"`
}
type AbstractSink struct {
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index a5ee6d9b6f..6033c0302f 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -245,6 +245,32 @@ func (in *Authorization) DeepCopy() *Authorization {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Backoff) DeepCopyInto(out *Backoff) {
+ *out = *in
+ if in.Interval != nil {
+ in, out := &in.Interval, &out.Interval
+ *out = new(metav1.Duration)
+ **out = **in
+ }
+ if in.Steps != nil {
+ in, out := &in.Steps, &out.Steps
+ *out = new(uint32)
+ **out = **in
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backoff.
+func (in *Backoff) DeepCopy() *Backoff {
+ if in == nil {
+ return nil
+ }
+ out := new(Backoff)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BasicAuth) DeepCopyInto(out *BasicAuth) {
*out = *in
@@ -1957,6 +1983,32 @@ func (in *RedisSettings) DeepCopy() *RedisSettings {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RetryStrategy) DeepCopyInto(out *RetryStrategy) {
+ *out = *in
+ if in.BackOff != nil {
+ in, out := &in.BackOff, &out.BackOff
+ *out = new(Backoff)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.OnFailure != nil {
+ in, out := &in.OnFailure, &out.OnFailure
+ *out = new(OnFailureRetryStrategy)
+ **out = **in
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryStrategy.
+func (in *RetryStrategy) DeepCopy() *RetryStrategy {
+ if in == nil {
+ return nil
+ }
+ out := new(RetryStrategy)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SASL) DeepCopyInto(out *SASL) {
*out = *in
@@ -2263,6 +2315,7 @@ func (in *Sink) DeepCopyInto(out *Sink) {
*out = new(AbstractSink)
(*in).DeepCopyInto(*out)
}
+ in.RetryStrategy.DeepCopyInto(&out.RetryStrategy)
return
}
diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go
index f4e394004e..95ce17b984 100644
--- a/pkg/metrics/metrics.go
+++ b/pkg/metrics/metrics.go
@@ -86,7 +86,7 @@ var (
Help: "Total number of bytes written",
}, []string{LabelVertex, LabelPipeline, LabelVertexType, LabelVertexReplicaIndex, LabelPartitionName})
- // WriteMessagesError is used to indicate the number of errors messages written
+ // WriteMessagesError is used to indicate the number of errors encountered while writing messages
WriteMessagesError = promauto.NewCounterVec(prometheus.CounterOpts{
Subsystem: "forwarder",
Name: "write_error_total",
@@ -298,3 +298,27 @@ var (
Help: "Total number of ctrl Messages sent",
}, []string{LabelVertex, LabelPipeline, LabelVertexType, LabelVertexReplicaIndex, LabelPartitionName})
)
+
+// Sink forwarder metrics
+var (
+ // FbSinkWriteMessagesCount is used to indicate the number of messages written to a fallback sink
+ FbSinkWriteMessagesCount = promauto.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: "forwarder",
+ Name: "fbsink_write_total",
+ Help: "Total number of Messages written to a fallback sink",
+ }, []string{LabelVertex, LabelPipeline, LabelVertexType, LabelVertexReplicaIndex, LabelPartitionName})
+
+ // FbSinkWriteBytesCount is to indicate the number of bytes written to a fallback sink
+ FbSinkWriteBytesCount = promauto.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: "forwarder",
+ Name: "fbsink_write_bytes_total",
+ Help: "Total number of bytes written to a fallback sink",
+ }, []string{LabelVertex, LabelPipeline, LabelVertexType, LabelVertexReplicaIndex, LabelPartitionName})
+
+ // FbSinkWriteMessagesError is used to indicate the number of errors while writing to a fallback sink
+ FbSinkWriteMessagesError = promauto.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: "forwarder",
+ Name: "fbsink_write_error_total",
+ Help: "Total number of Write Errors while writing to a fallback sink",
+ }, []string{LabelVertex, LabelPipeline, LabelVertexType, LabelVertexReplicaIndex, LabelPartitionName})
+)
diff --git a/pkg/reconciler/pipeline/validate.go b/pkg/reconciler/pipeline/validate.go
index 0fc443b6b5..2a98c2e665 100644
--- a/pkg/reconciler/pipeline/validate.go
+++ b/pkg/reconciler/pipeline/validate.go
@@ -272,6 +272,10 @@ func validateVertex(v dfv1.AbstractVertex) error {
if v.UDF != nil {
return validateUDF(*v.UDF)
}
+
+ if v.Sink != nil {
+ return validateSink(*v.Sink)
+ }
return nil
}
@@ -553,3 +557,33 @@ func buildVisitedMap(vtxName string, visited map[string]struct{}, pl *dfv1.Pipel
}
}
+
+// validateSink initiates the validation of the sink spec for a pipeline
+func validateSink(sink dfv1.Sink) error {
+ // check the sinks retry strategy validity.
+ if ok := HasValidSinkRetryStrategy(sink); !ok {
+ return fmt.Errorf("given OnFailure strategy is fallback but fallback sink is not provided")
+ }
+ return nil
+}
+
+// HasValidSinkRetryStrategy checks if the provided RetryStrategy is valid based on the sink's configuration.
+// This validation ensures that the retry strategy is compatible with the sink's current setup
+func HasValidSinkRetryStrategy(s dfv1.Sink) bool {
+ // If the OnFailure strategy is set to fallback, but no fallback sink is provided in the Sink struct,
+ // we return an error
+ if s.RetryStrategy.OnFailure != nil && *s.RetryStrategy.OnFailure == dfv1.OnFailureFallback && !hasValidFallbackSink(&s) {
+ return false
+ }
+ // If steps are provided in the strategy they cannot be 0, as we do not allow no tries for writing
+ if s.RetryStrategy.BackOff != nil && s.RetryStrategy.BackOff.Steps != nil && *s.RetryStrategy.BackOff.Steps == 0 {
+ return false
+ }
+ // If no errors are found, the function returns true indicating the validation passed.
+ return true
+}
+
+// HasValidFallbackSink checks if the Sink vertex has a valid fallback sink configured
+func hasValidFallbackSink(s *dfv1.Sink) bool {
+ return s.Fallback != nil && s.Fallback.UDSink != nil
+}
diff --git a/pkg/reconciler/pipeline/validate_test.go b/pkg/reconciler/pipeline/validate_test.go
index 486b36c9be..f116e04825 100644
--- a/pkg/reconciler/pipeline/validate_test.go
+++ b/pkg/reconciler/pipeline/validate_test.go
@@ -1056,3 +1056,125 @@ func Test_validateIdleSource(t *testing.T) {
assert.Error(t, err)
assert.Contains(t, err.Error(), `invalid idle source watermark config, threshold should be greater than or equal to incrementBy`)
}
+
+// TestValidateSink tests the validateSink function with different sink configurations.
+func TestValidateSink(t *testing.T) {
+ onFailFallback := dfv1.OnFailureFallback
+ tests := []struct {
+ name string
+ sink dfv1.Sink
+ expectedError bool
+ }{
+ {
+ name: "Valid configuration without needing fallback",
+ sink: dfv1.Sink{
+ RetryStrategy: dfv1.RetryStrategy{OnFailure: nil},
+ },
+ expectedError: false,
+ },
+ {
+ name: "Valid configuration with valid fallback",
+ sink: dfv1.Sink{
+ RetryStrategy: dfv1.RetryStrategy{OnFailure: &onFailFallback},
+ // represents a valid fallback sink
+ Fallback: &dfv1.AbstractSink{
+ UDSink: &dfv1.UDSink{},
+ },
+ },
+ expectedError: false,
+ },
+ {
+ name: "Valid configuration with invalid fallback - no UDSink",
+ sink: dfv1.Sink{
+ RetryStrategy: dfv1.RetryStrategy{OnFailure: &onFailFallback},
+ Fallback: &dfv1.AbstractSink{}, // represents a valid fallback sink
+ },
+ expectedError: true,
+ },
+ {
+ name: "Invalid configuration, fallback needed but not provided",
+ sink: dfv1.Sink{
+ RetryStrategy: dfv1.RetryStrategy{OnFailure: &onFailFallback},
+ Fallback: nil,
+ },
+ expectedError: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Run the validation function
+ err := validateSink(tt.sink)
+ // Check if an error was expected or not
+ if (err != nil) != tt.expectedError {
+ t.Errorf("%s: validateSink() error = %v, wantErr %v", tt.name, err, tt.expectedError)
+ }
+ })
+ }
+}
+
+func TestIsValidSinkRetryStrategy(t *testing.T) {
+ zeroSteps := uint32(0)
+ tests := []struct {
+ name string
+ sink dfv1.Sink
+ strategy dfv1.RetryStrategy
+ wantErr bool
+ }{
+ {
+ name: "valid strategy with fallback configured",
+ sink: dfv1.Sink{Fallback: &dfv1.AbstractSink{
+ UDSink: &dfv1.UDSink{},
+ }},
+ strategy: dfv1.RetryStrategy{
+ OnFailure: func() *dfv1.OnFailureRetryStrategy { str := dfv1.OnFailureFallback; return &str }(),
+ },
+ wantErr: false,
+ },
+ {
+ name: "invalid valid strategy with fallback not configured properly",
+ sink: dfv1.Sink{Fallback: &dfv1.AbstractSink{}},
+ strategy: dfv1.RetryStrategy{
+ OnFailure: func() *dfv1.OnFailureRetryStrategy { str := dfv1.OnFailureFallback; return &str }(),
+ },
+ wantErr: true,
+ },
+ {
+ name: "invalid strategy with no fallback configured",
+ sink: dfv1.Sink{},
+ strategy: dfv1.RetryStrategy{
+ OnFailure: func() *dfv1.OnFailureRetryStrategy { str := dfv1.OnFailureFallback; return &str }(),
+ },
+ wantErr: true,
+ },
+ {
+ name: "valid strategy with drop and no fallback needed",
+ sink: dfv1.Sink{},
+ strategy: dfv1.RetryStrategy{
+ OnFailure: func() *dfv1.OnFailureRetryStrategy { str := dfv1.OnFailureDrop; return &str }(),
+ },
+ wantErr: false,
+ },
+ {
+ name: "invalid strategy with 0 steps",
+ sink: dfv1.Sink{},
+ strategy: dfv1.RetryStrategy{
+ BackOff: &dfv1.Backoff{
+ Steps: &zeroSteps,
+ },
+ OnFailure: func() *dfv1.OnFailureRetryStrategy { str := dfv1.OnFailureDrop; return &str }(),
+ },
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tt.sink.RetryStrategy = tt.strategy
+ ok := HasValidSinkRetryStrategy(tt.sink)
+ if (!ok) != tt.wantErr {
+ t.Errorf("isValidSinkRetryStrategy() got = %v, want %v", ok, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/pkg/sinks/forward/forward.go b/pkg/sinks/forward/forward.go
index acddb40567..ad53b5fa51 100644
--- a/pkg/sinks/forward/forward.go
+++ b/pkg/sinks/forward/forward.go
@@ -54,6 +54,7 @@ type DataForward struct {
vertexName string
pipelineName string
vertexReplica int32
+ sinkRetryStrategy dfv1.RetryStrategy
// idleManager manages the idle watermark status.
idleManager wmb.IdleManager
// wmbChecker checks if the idle watermark is valid.
@@ -98,6 +99,10 @@ func NewDataForward(
},
opts: *dOpts,
}
+ // add the sink retry strategy to the forward
+ if vertexInstance.Vertex.Spec.Sink != nil {
+ df.sinkRetryStrategy = vertexInstance.Vertex.Spec.Sink.RetryStrategy
+ }
// Add logger from parent ctx to child context.
df.ctx = logging.WithLogger(ctx, dOpts.logger)
@@ -357,82 +362,184 @@ func (df *DataForward) ackFromBuffer(ctx context.Context, offsets []isb.Offset)
return ctxClosedErr
}
-// writeToSink forwards an array of messages to a sink and it is a blocking call it keeps retrying until shutdown has been initiated.
-func (df *DataForward) writeToSink(ctx context.Context, sinkWriter sinker.SinkWriter, messages []isb.Message, isFbSinkWriter bool) ([]isb.Offset, []isb.Message, error) {
+// writeToSink forwards an array of messages to a sink and it is a blocking call it keeps retrying
+// until shutdown has been initiated. The function also evaluates whether to use a fallback sink based
+// on the error and configuration.
+func (df *DataForward) writeToSink(ctx context.Context, sinkWriter sinker.SinkWriter, messagesToTry []isb.Message, isFbSinkWriter bool) ([]isb.Offset, []isb.Message, error) {
var (
- err error
- writeCount int
- writeBytes float64
+ err error
+ writeCount int
+ writeBytes float64
+ fallbackMessages []isb.Message
)
- writeOffsets := make([]isb.Offset, 0, len(messages))
- var fallbackMessages []isb.Message
+ // slice to store the successful offsets returned by the sink
+ writeOffsets := make([]isb.Offset, 0, len(messagesToTry))
+
+ // extract the backOff conditions and failStrategy for the retry logic,
+ // when the isFbSinkWriter is true, we use an infinite retry
+ backoffCond, failStrategy := df.getBackOffConditions(isFbSinkWriter)
+ // The loop will continue trying to write messages until they are all processed
+ // or an unrecoverable error occurs.
for {
- _writeOffsets, errs := sinkWriter.Write(ctx, messages)
- // Note: this is an unwanted memory allocation during a happy path. We want only minimal allocation since using failedMessages is an unlikely path.
- var failedMessages []isb.Message
- needRetry := false
- for idx, msg := range messages {
-
- if err = errs[idx]; err != nil {
- // if we are asked to write to fallback sink, check if the fallback sink is configured,
- // and we are not already in the fallback sink write path.
- if errors.Is(err, &udsink.WriteToFallbackErr) && df.opts.fbSinkWriter != nil && !isFbSinkWriter {
- fallbackMessages = append(fallbackMessages, msg)
- continue
- }
+ err = wait.ExponentialBackoffWithContext(ctx, backoffCond, func(_ context.Context) (done bool, err error) {
+ // Note: this is an unwanted memory allocation during a happy path. We want only minimal allocation
+ // since using failedMessages is an unlikely path.
+ var failedMessages []isb.Message
+ needRetry := false
+ _writeOffsets, errs := sinkWriter.Write(ctx, messagesToTry)
+ for idx, msg := range messagesToTry {
+ if err = errs[idx]; err != nil {
+ // if we are asked to write to fallback sink, check if the fallback sink is configured,
+ // and we are not already in the fallback sink write path.
+ if errors.Is(err, &udsink.WriteToFallbackErr) && df.opts.fbSinkWriter != nil && !isFbSinkWriter {
+ fallbackMessages = append(fallbackMessages, msg)
+ continue
+ }
- // if we are asked to write to fallback but no fallback sink is configured, we will retry the messages to the same sink
- if errors.Is(err, &udsink.WriteToFallbackErr) && df.opts.fbSinkWriter == nil {
- df.opts.logger.Error("Asked to write to fallback but no fallback sink is configured, retrying the message to the same sink")
- }
+ // if we are asked to write to fallback but no fallback sink is configured, we will retry the messages to the same sink
+ if errors.Is(err, &udsink.WriteToFallbackErr) && df.opts.fbSinkWriter == nil {
+ df.opts.logger.Error("Asked to write to fallback but no fallback sink is configured, retrying the message to the same sink")
+ }
- // if we are asked to write to fallback sink inside the fallback sink, we will retry the messages to the fallback sink
- if errors.Is(err, &udsink.WriteToFallbackErr) && isFbSinkWriter {
- df.opts.logger.Error("Asked to write to fallback sink inside the fallback sink, retrying the message to fallback sink")
- }
+ // if we are asked to write to fallback sink inside the fallback sink, we will retry the messages to the fallback sink
+ if errors.Is(err, &udsink.WriteToFallbackErr) && isFbSinkWriter {
+ df.opts.logger.Error("Asked to write to fallback sink inside the fallback sink, retrying the message to fallback sink")
+ }
- needRetry = true
- // we retry only failed messages
- failedMessages = append(failedMessages, msg)
- metrics.WriteMessagesError.With(map[string]string{metrics.LabelVertex: df.vertexName, metrics.LabelPipeline: df.pipelineName, metrics.LabelVertexType: string(dfv1.VertexTypeSink), metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica)), metrics.LabelPartitionName: sinkWriter.GetName()}).Inc()
- // a shutdown can break the blocking loop caused due to InternalErr
- if ok, _ := df.IsShuttingDown(); ok {
- metrics.PlatformError.With(map[string]string{metrics.LabelVertex: df.vertexName, metrics.LabelPipeline: df.pipelineName, metrics.LabelVertexType: string(dfv1.VertexTypeSink), metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica))}).Inc()
- return nil, nil, fmt.Errorf("writeToSink failed, Stop called while stuck on an internal error with failed messages:%d, %v", len(failedMessages), errs)
- }
- } else {
- writeCount++
- writeBytes += float64(len(msg.Payload))
- // we support write offsets only for jetstream
- if _writeOffsets != nil {
- writeOffsets = append(writeOffsets, _writeOffsets[idx])
+ needRetry = true
+
+ // TODO(Retry-Sink) : Propagate the retry-count?
+ // we retry only failed message
+ failedMessages = append(failedMessages, msg)
+
+ // increment the error metric
+ df.incrementErrorMetric(sinkWriter.GetName(), isFbSinkWriter)
+
+ // a shutdown can break the blocking loop caused due to InternalErr
+ if ok, _ := df.IsShuttingDown(); ok {
+ metrics.PlatformError.With(map[string]string{metrics.LabelVertex: df.vertexName, metrics.LabelPipeline: df.pipelineName, metrics.LabelVertexType: string(dfv1.VertexTypeSink), metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica))}).Inc()
+ return true, fmt.Errorf("writeToSink failed, Stop called while stuck on an internal error with failed messages:%d, %v", len(failedMessages), errs)
+ }
+ } else {
+ writeCount++
+ writeBytes += float64(len(msg.Payload))
+ // we support write offsets only for jetstream
+ if _writeOffsets != nil {
+ writeOffsets = append(writeOffsets, _writeOffsets[idx])
+ }
}
}
+ // set messages to failedMessages, in case of success this should be empty
+ // While checking for retry we see the length of the messages left
+ messagesToTry = failedMessages
+ if needRetry {
+ df.opts.logger.Errorw("Retrying failed messages",
+ zap.Any("errors", errorArrayToMap(errs)),
+ zap.String(metrics.LabelPipeline, df.pipelineName),
+ zap.String(metrics.LabelVertex, df.vertexName),
+ zap.String(metrics.LabelPartitionName, sinkWriter.GetName()),
+ )
+ return false, nil
+ }
+ return true, nil
+ })
+ // If we exited out of the loop and it was due to a forced shutdown we should exit
+ // TODO(Retry-Sink): Check for ctx done separately? That should be covered in shutdown
+ if ok, _ := df.IsShuttingDown(); err != nil && ok {
+ return nil, nil, err
}
-
- if needRetry {
- df.opts.logger.Errorw("Retrying failed messages",
- zap.Any("errors", errorArrayToMap(errs)),
- zap.String(metrics.LabelPipeline, df.pipelineName),
- zap.String(metrics.LabelVertex, df.vertexName),
- zap.String(metrics.LabelPartitionName, sinkWriter.GetName()),
- )
- // set messages to failed for the retry
- messages = failedMessages
- // TODO: implement retry with backoff etc.
- time.Sleep(df.opts.retryInterval)
- } else {
+ // Check what actions are required once the writing loop is completed
+ // Break if no further action is required
+ if !df.handlePostRetryFailures(&messagesToTry, failStrategy, &fallbackMessages, sinkWriter) {
break
}
}
-
- metrics.WriteMessagesCount.With(map[string]string{metrics.LabelVertex: df.vertexName, metrics.LabelPipeline: df.pipelineName, metrics.LabelVertexType: string(dfv1.VertexTypeSink), metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica)), metrics.LabelPartitionName: sinkWriter.GetName()}).Add(float64(writeCount))
- metrics.WriteBytesCount.With(map[string]string{metrics.LabelVertex: df.vertexName, metrics.LabelPipeline: df.pipelineName, metrics.LabelVertexType: string(dfv1.VertexTypeSink), metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica)), metrics.LabelPartitionName: sinkWriter.GetName()}).Add(writeBytes)
+ // update the write metrics for sink
+ df.updateSinkWriteMetrics(writeCount, writeBytes, sinkWriter.GetName(), isFbSinkWriter)
return writeOffsets, fallbackMessages, nil
}
+// handlePostRetryFailures deals with the scenarios after retries are exhausted.
+// It returns true if we need to continue retrying else returns false when no further writes are required
+func (df *DataForward) handlePostRetryFailures(messagesToTry *[]isb.Message, failStrategy dfv1.OnFailureRetryStrategy, fallbackMessages *[]isb.Message,
+ sinkWriter sinker.SinkWriter) bool {
+
+ // Check if we still have messages left to be processed
+ if len(*messagesToTry) > 0 {
+
+ df.opts.logger.Infof("Retries exhausted in sink, messagesLeft %d, Next strategy %s",
+ len(*messagesToTry), failStrategy)
+
+ // Check what is the failure strategy to be followed after retry exhaustion
+ switch failStrategy {
+ case dfv1.OnFailureRetry:
+ // If on failure, we keep on retrying then lets continue the loop and try all again
+ return true
+ case dfv1.OnFailureFallback:
+ // If onFail we have to divert messages to fallback, lets add all failed messages to fallback slice
+ *fallbackMessages = append(*fallbackMessages, *messagesToTry...)
+ case dfv1.OnFailureDrop:
+ // If on fail we want to Drop in that case lets not retry further
+ df.opts.logger.Info("Dropping the failed messages after retry in the Sink")
+ // Update the drop metric count with the messages left
+ metrics.DropMessagesCount.With(map[string]string{
+ metrics.LabelVertex: df.vertexName,
+ metrics.LabelPipeline: df.pipelineName,
+ metrics.LabelVertexType: string(dfv1.VertexTypeSink),
+ metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica)),
+ metrics.LabelPartitionName: sinkWriter.GetName(),
+ metrics.LabelReason: "retries exhausted in the Sink",
+ }).Add(float64(len(*messagesToTry)))
+ }
+ }
+ return false
+}
+
+// updateSinkWriteMetrics updates metrics related to data writes to a sink.
+// Metrics are updated based on whether the operation involves the primary or fallback sink.
+func (df *DataForward) updateSinkWriteMetrics(writeCount int, writeBytes float64, sinkWriterName string, isFallback bool) {
+ // Define labels to keep track of the data related to the specific operation
+ labels := map[string]string{
+ metrics.LabelVertex: df.vertexName,
+ metrics.LabelPipeline: df.pipelineName,
+ metrics.LabelVertexType: string(dfv1.VertexTypeSink),
+ metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica)),
+ metrics.LabelPartitionName: sinkWriterName,
+ }
+
+ // Increment the metrics for message count and bytes
+ metrics.WriteMessagesCount.With(labels).Add(float64(writeCount))
+ metrics.WriteBytesCount.With(labels).Add(writeBytes)
+
+ // if this is for Fallback Sink, increment specific metrics as well
+ if isFallback {
+ metrics.FbSinkWriteMessagesCount.With(labels).Add(float64(writeCount))
+ metrics.FbSinkWriteBytesCount.With(labels).Add(writeBytes)
+ }
+}
+
+// incrementErrorMetric updates the appropriate error metric based on whether the operation involves a fallback sink.
+func (df *DataForward) incrementErrorMetric(sinkWriter string, isFbSinkWriter bool) {
+ // Define labels to keep track of the data related to the specific operation
+ labels := map[string]string{
+ metrics.LabelVertex: df.vertexName,
+ metrics.LabelPipeline: df.pipelineName,
+ metrics.LabelVertexType: string(dfv1.VertexTypeSink),
+ metrics.LabelVertexReplicaIndex: strconv.Itoa(int(df.vertexReplica)),
+ metrics.LabelPartitionName: sinkWriter,
+ }
+
+ // Increment the selected metric and attach labels to provide detailed context:
+ metrics.WriteMessagesError.With(labels).Inc()
+
+ // Increment fallback specific metric if fallback mode
+ if isFbSinkWriter {
+ metrics.FbSinkWriteMessagesError.With(labels).Inc()
+ }
+}
+
// errorArrayToMap summarizes an error array to map
func errorArrayToMap(errs []error) map[string]int64 {
result := make(map[string]int64)
@@ -443,3 +550,16 @@ func errorArrayToMap(errs []error) map[string]int64 {
}
return result
}
+
+// getBackOffConditions configures the retry backoff strategy based on whether its a fallbackSink or primary sink.
+func (df *DataForward) getBackOffConditions(isFallbackSink bool) (wait.Backoff, dfv1.OnFailureRetryStrategy) {
+ // If we want for isFallbackSink we will return an infinite retry which will keep retrying post exhaustion till it succeeds
+ if isFallbackSink {
+ return wait.Backoff{
+ Duration: dfv1.DefaultRetryInterval,
+ Steps: dfv1.DefaultRetrySteps,
+ }, dfv1.OnFailureRetry
+ }
+ // Initial interval duration and number of retries are taken from DataForward settings.
+ return df.sinkRetryStrategy.GetBackoff(), df.sinkRetryStrategy.GetOnFailureRetryStrategy()
+}
diff --git a/pkg/sinks/forward/options.go b/pkg/sinks/forward/options.go
index f18dbe2620..afb6d1f37b 100644
--- a/pkg/sinks/forward/options.go
+++ b/pkg/sinks/forward/options.go
@@ -17,8 +17,6 @@ limitations under the License.
package forward
import (
- "time"
-
"go.uber.org/zap"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
@@ -33,8 +31,6 @@ type options struct {
readBatchSize int64
// sinkConcurrency sets the concurrency for concurrent processing
sinkConcurrency int
- // retryInterval is the time.Duration to sleep before retrying
- retryInterval time.Duration
// fbSinkWriter is the writer for the fallback sink
fbSinkWriter sinker.SinkWriter
// logger is used to pass the logger variable
@@ -49,7 +45,6 @@ func DefaultOptions() *options {
return &options{
readBatchSize: dfv1.DefaultReadBatchSize,
sinkConcurrency: dfv1.DefaultReadBatchSize,
- retryInterval: time.Millisecond,
logger: logging.NewLogger(),
}
}
@@ -70,14 +65,6 @@ func WithSinkConcurrency(f int) Option {
}
}
-// WithRetryInterval sets the retry interval
-func WithRetryInterval(f time.Duration) Option {
- return func(o *options) error {
- o.retryInterval = time.Duration(f)
- return nil
- }
-}
-
// WithLogger is used to return logger information
func WithLogger(l *zap.SugaredLogger) Option {
return func(o *options) error {
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 4e3ba1085b..6b34934210 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1473,6 +1473,7 @@ dependencies = [
"bytes",
"chrono",
"hyper-util",
+ "kube",
"numaflow 0.1.0 (git+https://github.com/numaproj/numaflow-rs.git?branch=main)",
"numaflow-models",
"once_cell",
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index 00c51e6e9a..90ddc5cbec 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -33,6 +33,7 @@ pep440_rs = "0.6.6"
backoff = { path = "../backoff" }
parking_lot = "0.12.3"
prometheus-client = "0.22.3"
+kube = "0.93.1"
[dev-dependencies]
tempfile = "3.11.0"
diff --git a/rust/monovertex/src/config.rs b/rust/monovertex/src/config.rs
index 00f966b9bc..d1450500be 100644
--- a/rust/monovertex/src/config.rs
+++ b/rust/monovertex/src/config.rs
@@ -4,7 +4,7 @@ use std::sync::OnceLock;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
-use numaflow_models::models::MonoVertex;
+use numaflow_models::models::{Backoff, MonoVertex, RetryStrategy};
use crate::error::Error;
@@ -17,8 +17,9 @@ const DEFAULT_LAG_CHECK_INTERVAL_IN_SECS: u16 = 5;
const DEFAULT_LAG_REFRESH_INTERVAL_IN_SECS: u16 = 3;
const DEFAULT_BATCH_SIZE: u64 = 500;
const DEFAULT_TIMEOUT_IN_MS: u32 = 1000;
-const DEFAULT_MAX_SINK_RETRY_ATTEMPTS: u16 = 10;
+const DEFAULT_MAX_SINK_RETRY_ATTEMPTS: u16 = u16::MAX;
const DEFAULT_SINK_RETRY_INTERVAL_IN_MS: u32 = 1;
+const DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY: &str = "retry";
pub fn config() -> &'static Settings {
static CONF: OnceLock = OnceLock::new();
@@ -43,10 +44,22 @@ pub struct Settings {
pub lag_refresh_interval_in_secs: u16,
pub sink_max_retry_attempts: u16,
pub sink_retry_interval_in_ms: u32,
+ pub sink_retry_on_fail_strategy: String,
+ pub sink_default_retry_strategy: RetryStrategy,
}
impl Default for Settings {
fn default() -> Self {
+ // Create a default retry strategy from defined constants
+ let default_retry_strategy = RetryStrategy {
+ backoff: Option::from(Box::from(Backoff {
+ interval: Option::from(kube::core::Duration::from(
+ std::time::Duration::from_millis(DEFAULT_SINK_RETRY_INTERVAL_IN_MS as u64),
+ )),
+ steps: Option::from(DEFAULT_MAX_SINK_RETRY_ATTEMPTS as i64),
+ })),
+ on_failure: Option::from(DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY.to_string()),
+ };
Self {
mono_vertex_name: "default".to_string(),
replica: 0,
@@ -60,6 +73,8 @@ impl Default for Settings {
lag_refresh_interval_in_secs: DEFAULT_LAG_REFRESH_INTERVAL_IN_SECS,
sink_max_retry_attempts: DEFAULT_MAX_SINK_RETRY_ATTEMPTS,
sink_retry_interval_in_ms: DEFAULT_SINK_RETRY_INTERVAL_IN_MS,
+ sink_retry_on_fail_strategy: DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY.to_string(),
+ sink_default_retry_strategy: default_retry_strategy,
}
}
}
@@ -113,9 +128,56 @@ impl Settings {
settings.is_fallback_enabled = mono_vertex_obj
.spec
.sink
+ .as_deref()
.ok_or(Error::ConfigError("Sink not found".to_string()))?
.fallback
.is_some();
+
+ if let Some(retry_strategy) = mono_vertex_obj
+ .spec
+ .sink
+ .expect("sink should not be empty")
+ .retry_strategy
+ {
+ if let Some(sink_backoff) = retry_strategy.clone().backoff {
+ // Set the max retry attempts and retry interval using direct reference
+ settings.sink_retry_interval_in_ms = sink_backoff
+ .clone()
+ .interval
+ .map(|x| std::time::Duration::from(x).as_millis() as u32)
+ .unwrap_or(DEFAULT_SINK_RETRY_INTERVAL_IN_MS);
+
+ settings.sink_max_retry_attempts = sink_backoff
+ .clone()
+ .steps
+ .map(|x| x as u16)
+ .unwrap_or(DEFAULT_MAX_SINK_RETRY_ATTEMPTS);
+
+ // We do not allow 0 attempts to write to sink
+ if settings.sink_max_retry_attempts == 0 {
+ return Err(Error::ConfigError(
+ "Retry Strategy given with 0 retry attempts".to_string(),
+ ));
+ }
+ }
+
+ // Set the retry strategy using a direct reference whenever possible
+ settings.sink_retry_on_fail_strategy = retry_strategy
+ .on_failure
+ .clone()
+ .unwrap_or_else(|| DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY.to_string());
+
+ // check if the sink retry strategy is set to fallback and there is no fallback sink configured
+ // then we should return an error
+ if settings.sink_retry_on_fail_strategy == "fallback"
+ && !settings.is_fallback_enabled
+ {
+ return Err(Error::ConfigError(
+ "Retry Strategy given as fallback but Fallback sink not configured"
+ .to_string(),
+ ));
+ }
+ }
}
settings.grpc_max_message_size = env::var(ENV_GRPC_MAX_MESSAGE_SIZE)
@@ -136,31 +198,213 @@ impl Settings {
#[cfg(test)]
mod tests {
- use std::env;
-
use super::*;
+ use serde_json::json;
+ use std::env;
#[test]
- fn test_settings_load() {
- // Set up environment variables
- unsafe {
- env::set_var(ENV_MONO_VERTEX_OBJ, "eyJtZXRhZGF0YSI6eyJuYW1lIjoic2ltcGxlLW1vbm8tdmVydGV4IiwibmFtZXNwYWNlIjoiZGVmYXVsdCIsImNyZWF0aW9uVGltZXN0YW1wIjpudWxsfSwic3BlYyI6eyJyZXBsaWNhcyI6MCwic291cmNlIjp7InRyYW5zZm9ybWVyIjp7ImNvbnRhaW5lciI6eyJpbWFnZSI6InF1YXkuaW8vbnVtYWlvL251bWFmbG93LXJzL21hcHQtZXZlbnQtdGltZS1maWx0ZXI6c3RhYmxlIiwicmVzb3VyY2VzIjp7fX0sImJ1aWx0aW4iOm51bGx9LCJ1ZHNvdXJjZSI6eyJjb250YWluZXIiOnsiaW1hZ2UiOiJkb2NrZXIuaW50dWl0LmNvbS9wZXJzb25hbC95aGwwMS9zaW1wbGUtc291cmNlOnN0YWJsZSIsInJlc291cmNlcyI6e319fX0sInNpbmsiOnsidWRzaW5rIjp7ImNvbnRhaW5lciI6eyJpbWFnZSI6ImRvY2tlci5pbnR1aXQuY29tL3BlcnNvbmFsL3lobDAxL2JsYWNraG9sZS1zaW5rOnN0YWJsZSIsInJlc291cmNlcyI6e319fX0sImxpbWl0cyI6eyJyZWFkQmF0Y2hTaXplIjo1MDAsInJlYWRUaW1lb3V0IjoiMXMifSwic2NhbGUiOnt9fSwic3RhdHVzIjp7InJlcGxpY2FzIjowLCJsYXN0VXBkYXRlZCI6bnVsbCwibGFzdFNjYWxlZEF0IjpudWxsfX0=");
- env::set_var(ENV_GRPC_MAX_MESSAGE_SIZE, "128000000");
- };
+ fn test_settings_load_combined() {
+ // Define all JSON test configurations in separate scopes to use them distinctively
+ {
+ let json_data = json!({
+ "metadata": {
+ "name": "simple-mono-vertex",
+ "namespace": "default",
+ "creationTimestamp": null
+ },
+ "spec": {
+ "replicas": 0,
+ "source": {
+ "transformer": {
+ "container": {
+ "image": "xxxxxxx",
+ "resources": {}
+ },
+ "builtin": null
+ },
+ "udsource": {
+ "container": {
+ "image": "xxxxxxx",
+ "resources": {}
+ }
+ }
+ },
+ "sink": {
+ "udsink": {
+ "container": {
+ "image": "xxxxxx",
+ "resources": {}
+ }
+ }
+ },
+ "limits": {
+ "readBatchSize": 500,
+ "readTimeout": "1s"
+ },
+ "scale": {},
+ "status": {
+ "replicas": 0,
+ "lastUpdated": null,
+ "lastScaledAt": null
+ }
+ }
+ });
+ let json_str = json_data.to_string();
+ let encoded_json = BASE64_STANDARD.encode(json_str);
+ env::set_var(ENV_MONO_VERTEX_OBJ, encoded_json);
- // Load settings
- let settings = Settings::load().unwrap();
+ // Execute and verify
+ let settings = Settings::load().unwrap();
+ assert_eq!(settings.mono_vertex_name, "simple-mono-vertex");
+ env::remove_var(ENV_MONO_VERTEX_OBJ);
+ }
- // Verify settings
- assert_eq!(settings.mono_vertex_name, "simple-mono-vertex");
- assert_eq!(settings.batch_size, 500);
- assert_eq!(settings.timeout_in_ms, 1000);
- assert_eq!(settings.grpc_max_message_size, 128000000);
+ {
+ // Test Retry Strategy Load
+ let json_data = json!({
+ "metadata": {
+ "name": "simple-mono-vertex",
+ "namespace": "default",
+ "creationTimestamp": null
+ },
+ "spec": {
+ "replicas": 0,
+ "source": {
+ "udsource": {
+ "container": {
+ "image": "xxxxxxx",
+ "resources": {}
+ }
+ }
+ },
+ "sink": {
+ "udsink": {
+ "container": {
+ "image": "xxxxxx",
+ "resources": {}
+ }
+ },
+ "retryStrategy": {
+ "backoff": {
+ "interval": "1s",
+ "steps": 5
+ },
+ },
+ },
+ "limits": {
+ "readBatchSize": 500,
+ "readTimeout": "1s"
+ },
+ }
+ });
+ let json_str = json_data.to_string();
+ let encoded_json = BASE64_STANDARD.encode(json_str);
+ env::set_var(ENV_MONO_VERTEX_OBJ, encoded_json);
- // Clean up environment variables
- unsafe {
+ // Execute and verify
+ let settings = Settings::load().unwrap();
+ assert_eq!(settings.sink_retry_on_fail_strategy, "retry");
+ assert_eq!(settings.sink_max_retry_attempts, 5);
+ assert_eq!(settings.sink_retry_interval_in_ms, 1000);
env::remove_var(ENV_MONO_VERTEX_OBJ);
- env::remove_var(ENV_GRPC_MAX_MESSAGE_SIZE);
- };
+ }
+
+ {
+ // Test Error Case: Retry Strategy Fallback without Fallback Sink
+ let json_data = json!({
+ "metadata": {
+ "name": "simple-mono-vertex",
+ "namespace": "default",
+ "creationTimestamp": null
+ },
+ "spec": {
+ "replicas": 0,
+ "source": {
+ "udsource": {
+ "container": {
+ "image": "xxxxxxx",
+ "resources": {}
+ }
+ }
+ },
+ "sink": {
+ "udsink": {
+ "container": {
+ "image": "xxxxxx",
+ "resources": {}
+ }
+ },
+ "retryStrategy": {
+ "backoff": {
+ "interval": "1s",
+ "steps": 5
+ },
+ "onFailure": "fallback"
+ },
+ },
+ "limits": {
+ "readBatchSize": 500,
+ "readTimeout": "1s"
+ },
+ }
+ });
+ let json_str = json_data.to_string();
+ let encoded_json = BASE64_STANDARD.encode(json_str);
+ env::set_var(ENV_MONO_VERTEX_OBJ, encoded_json);
+
+ // Execute and verify
+ assert!(Settings::load().is_err());
+ env::remove_var(ENV_MONO_VERTEX_OBJ);
+ }
+
+ {
+ // Test Error Case: Retry Strategy with 0 Retry Attempts
+ let json_data = json!({
+ "metadata": {
+ "name": "simple-mono-vertex",
+ "namespace": "default",
+ "creationTimestamp": null
+ },
+ "spec": {
+ "replicas": 0,
+ "source": {
+ "udsource": {
+ "container": {
+ "image": "xxxxxxx",
+ "resources": {}
+ }
+ }
+ },
+ "sink": {
+ "udsink": {
+ "container": {
+ "image": "xxxxxx",
+ "resources": {}
+ }
+ },
+ "retryStrategy": {
+ "backoff": {
+ "interval": "1s",
+ "steps": 0
+ },
+ "onFailure": "retry"
+ },
+ },
+ "limits": {
+ "readBatchSize": 500,
+ "readTimeout": "1s"
+ },
+ }
+ });
+ let json_str = json_data.to_string();
+ let encoded_json = BASE64_STANDARD.encode(json_str);
+ env::set_var(ENV_MONO_VERTEX_OBJ, encoded_json);
+
+ // Execute and verify
+ assert!(Settings::load().is_err());
+ env::remove_var(ENV_MONO_VERTEX_OBJ);
+ }
+ // General cleanup
+ env::remove_var(ENV_GRPC_MAX_MESSAGE_SIZE);
}
}
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index 8efe16c844..0f55268576 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
use crate::config::config;
use crate::error::{Error, Result};
use crate::message::{Message, Offset};
@@ -7,7 +9,6 @@ use crate::sink::{proto, SinkClient};
use crate::source::SourceClient;
use crate::transformer::TransformerClient;
use chrono::Utc;
-use std::collections::HashMap;
use tokio::task::JoinSet;
use tokio::time::sleep;
use tokio_util::sync::CancellationToken;
@@ -222,67 +223,43 @@ impl Forwarder {
// we will overwrite this vec with failed messages and will keep retrying.
let mut messages_to_send = messages;
- while attempts <= config().sink_max_retry_attempts {
- let start_time = tokio::time::Instant::now();
- match self.sink_client.sink_fn(messages_to_send.clone()).await {
- Ok(response) => {
- debug!(
- attempts = attempts,
- "Sink latency - {}ms",
- start_time.elapsed().as_millis()
- );
- attempts += 1;
-
- // create a map of id to result, since there is no strict requirement
- // for the udsink to return the results in the same order as the requests
- let result_map: HashMap<_, _> = response
- .results
- .iter()
- .map(|result| (result.id.clone(), result))
- .collect();
-
- error_map.clear();
- // drain all the messages that were successfully written
- // and keep only the failed messages to send again
- // construct the error map for the failed messages
- messages_to_send.retain(|msg| {
- if let Some(result) = result_map.get(&msg.id) {
- return if result.status == proto::Status::Success as i32 {
- false
- } else if result.status == proto::Status::Fallback as i32 {
- fallback_msgs.push(msg.clone()); // add to fallback messages
- false
- } else {
- *error_map.entry(result.err_msg.clone()).or_insert(0) += 1;
- true
- };
- }
- false
- });
-
- // if all messages are successfully written, break the loop
- if messages_to_send.is_empty() {
- break;
- }
-
- warn!(
- "Retry attempt {} due to retryable error. Errors: {:?}",
- attempts, error_map
- );
- sleep(tokio::time::Duration::from_millis(
- config().sink_retry_interval_in_ms as u64,
- ))
+ // only breaks out of this loop based on the retry strategy unless all the messages have been written to sink
+ // successfully.
+ loop {
+ while attempts < config().sink_max_retry_attempts {
+ let status = self
+ .write_to_sink_once(&mut error_map, &mut fallback_msgs, &mut messages_to_send)
.await;
+ match status {
+ Ok(true) => break,
+ Ok(false) => {
+ attempts += 1;
+ warn!(
+ "Retry attempt {} due to retryable error. Errors: {:?}",
+ attempts, error_map
+ );
+ }
+ Err(e) => Err(e)?,
}
- Err(e) => return Err(e),
}
- }
- if !messages_to_send.is_empty() {
- return Err(Error::SinkError(format!(
- "Failed to sink messages after {} attempts. Errors: {:?}",
- attempts, error_map
- )));
+ // If after the retries we still have messages to process, handle the post retry failures
+ let need_retry = self.handle_sink_post_retry(
+ &mut attempts,
+ &mut error_map,
+ &mut fallback_msgs,
+ &mut messages_to_send,
+ );
+ match need_retry {
+ // if we are done with the messages, break the loop
+ Ok(false) => break,
+ // if we need to retry, reset the attempts and error_map
+ Ok(true) => {
+ attempts = 0;
+ error_map.clear();
+ }
+ Err(e) => Err(e)?,
+ }
}
// If there are fallback messages, write them to the fallback sink
@@ -294,6 +271,9 @@ impl Forwarder {
.sink_time
.get_or_create(&self.common_labels)
.observe(start_time_e2e.elapsed().as_micros() as f64);
+
+ // update the metric for number of messages written to the sink
+ // this included primary and fallback sink
forward_metrics()
.sink_write_total
.get_or_create(&self.common_labels)
@@ -301,6 +281,122 @@ impl Forwarder {
Ok(())
}
+ /// Handles the post retry failures based on the configured strategy,
+ /// returns true if we need to retry, else false.
+ fn handle_sink_post_retry(
+ &mut self,
+ attempts: &mut u16,
+ error_map: &mut HashMap,
+ fallback_msgs: &mut Vec,
+ messages_to_send: &mut Vec,
+ ) -> Result {
+ // if we are done with the messages, break the loop
+ if messages_to_send.is_empty() {
+ return Ok(false);
+ }
+ // check what is the failure strategy in the config
+ let strategy = config().sink_retry_on_fail_strategy.clone();
+ match strategy.as_str() {
+ // if we need to retry, return true
+ "retry" => {
+ warn!(
+ "Using onFailure Retry, Retry attempts {} completed",
+ attempts
+ );
+ return Ok(true);
+ }
+ // if we need to drop the messages, log and return false
+ "drop" => {
+ // log that we are dropping the messages as requested
+ warn!(
+ "Dropping messages after {} attempts. Errors: {:?}",
+ attempts, error_map
+ );
+ // update the metrics
+ forward_metrics()
+ .dropped_total
+ .get_or_create(&self.common_labels)
+ .inc_by(messages_to_send.len() as u64);
+ }
+ // if we need to move the messages to the fallback, return false
+ "fallback" => {
+ // log that we are moving the messages to the fallback as requested
+ warn!(
+ "Moving messages to fallback after {} attempts. Errors: {:?}",
+ attempts, error_map
+ );
+ // move the messages to the fallback messages
+ fallback_msgs.append(messages_to_send);
+ }
+ // if the strategy is invalid, return an error
+ _ => {
+ return Err(Error::SinkError(format!(
+ "Invalid sink retry on fail strategy: {}",
+ strategy
+ )));
+ }
+ }
+ // if we are done with the messages, break the loop
+ Ok(false)
+ }
+
+ /// Writes to sink once and will return true if successful, else false. Please note that it
+ /// mutates is incoming fields.
+ async fn write_to_sink_once(
+ &mut self,
+ error_map: &mut HashMap,
+ fallback_msgs: &mut Vec,
+ messages_to_send: &mut Vec,
+ ) -> Result {
+ let start_time = tokio::time::Instant::now();
+ match self.sink_client.sink_fn(messages_to_send.clone()).await {
+ Ok(response) => {
+ debug!("Sink latency - {}ms", start_time.elapsed().as_millis());
+
+ // create a map of id to result, since there is no strict requirement
+ // for the udsink to return the results in the same order as the requests
+ let result_map: HashMap<_, _> = response
+ .results
+ .iter()
+ .map(|result| (result.id.clone(), result))
+ .collect();
+
+ error_map.clear();
+ // drain all the messages that were successfully written
+ // and keep only the failed messages to send again
+ // construct the error map for the failed messages
+ messages_to_send.retain(|msg| {
+ if let Some(result) = result_map.get(&msg.id) {
+ return if result.status == proto::Status::Success as i32 {
+ false
+ } else if result.status == proto::Status::Fallback as i32 {
+ fallback_msgs.push(msg.clone()); // add to fallback messages
+ false
+ } else {
+ *error_map.entry(result.err_msg.clone()).or_insert(0) += 1;
+ true
+ };
+ }
+ false
+ });
+
+ // if all messages are successfully written, break the loop
+ if messages_to_send.is_empty() {
+ return Ok(true);
+ }
+
+ sleep(tokio::time::Duration::from_millis(
+ config().sink_retry_interval_in_ms as u64,
+ ))
+ .await;
+
+ // we need to retry
+ return Ok(false);
+ }
+ Err(e) => return Err(e),
+ }
+ }
+
// Writes the fallback messages to the fallback sink
async fn handle_fallback_messages(&mut self, fallback_msgs: Vec) -> Result<()> {
if self.fallback_client.is_none() {
@@ -316,8 +412,17 @@ impl Forwarder {
// start with the original set of message to be sent.
// we will overwrite this vec with failed messages and will keep retrying.
let mut messages_to_send = fallback_msgs;
+ let fb_msg_count = messages_to_send.len() as u64;
+
+ let default_retry = config()
+ .sink_default_retry_strategy
+ .clone()
+ .backoff
+ .unwrap();
+ let max_attempts = default_retry.steps.unwrap();
+ let sleep_interval = default_retry.interval.unwrap();
- while attempts <= config().sink_max_retry_attempts {
+ while attempts < max_attempts {
let start_time = tokio::time::Instant::now();
match fallback_client.sink_fn(messages_to_send.clone()).await {
Ok(fb_response) => {
@@ -375,22 +480,22 @@ impl Forwarder {
"Retry attempt {} due to retryable error. Errors: {:?}",
attempts, fallback_error_map
);
- sleep(tokio::time::Duration::from_millis(
- config().sink_retry_interval_in_ms as u64,
- ))
- .await;
+ sleep(tokio::time::Duration::from(sleep_interval)).await;
}
Err(e) => return Err(e),
}
}
-
if !messages_to_send.is_empty() {
return Err(Error::SinkError(format!(
"Failed to write messages to fallback sink after {} attempts. Errors: {:?}",
attempts, fallback_error_map
)));
}
-
+ // increment the metric for the fallback sink write
+ forward_metrics()
+ .fbsink_write_total
+ .get_or_create(&self.common_labels)
+ .inc_by(fb_msg_count);
Ok(())
}
@@ -420,17 +525,18 @@ impl Forwarder {
mod tests {
use std::collections::HashSet;
- use crate::error::Result;
- use crate::forwarder::ForwarderBuilder;
- use crate::sink::{SinkClient, SinkConfig};
- use crate::source::{SourceClient, SourceConfig};
- use crate::transformer::{TransformerClient, TransformerConfig};
use chrono::Utc;
use numaflow::source::{Message, Offset, SourceReadRequest};
use numaflow::{sink, source, sourcetransform};
use tokio::sync::mpsc::Sender;
use tokio_util::sync::CancellationToken;
+ use crate::error::Result;
+ use crate::forwarder::ForwarderBuilder;
+ use crate::sink::{SinkClient, SinkConfig};
+ use crate::source::{SourceClient, SourceConfig};
+ use crate::transformer::{TransformerClient, TransformerConfig};
+
struct SimpleSource {
yet_to_be_acked: std::sync::RwLock>,
}
@@ -768,13 +874,13 @@ mod tests {
let forwarder_handle = tokio::spawn(async move {
forwarder.start().await?;
- Ok(())
+ Result::<()>::Ok(())
});
// Set a timeout for the forwarder
let timeout_duration = tokio::time::Duration::from_secs(1);
+ // The future should not complete as we should be retrying
let result = tokio::time::timeout(timeout_duration, forwarder_handle).await;
- let result: Result<()> = result.expect("forwarder_handle timed out").unwrap();
assert!(result.is_err());
// stop the servers
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index dc5dfd6dc9..f6f5519765 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -44,8 +44,12 @@ const READ_TOTAL: &str = "monovtx_read";
const READ_BYTES_TOTAL: &str = "monovtx_read_bytes";
const ACK_TOTAL: &str = "monovtx_ack";
const SINK_WRITE_TOTAL: &str = "monovtx_sink_write";
+const DROPPED_TOTAL: &str = "monovtx_dropped";
+const FALLBACK_SINK_WRITE_TOTAL: &str = "monovtx_fallback_sink_write";
+
// pending as gauge
const SOURCE_PENDING: &str = "monovtx_pending";
+
// processing times as timers
const E2E_TIME: &str = "monovtx_processing_time";
const READ_TIME: &str = "monovtx_read_time";
@@ -100,8 +104,12 @@ pub struct MonoVtxMetrics {
pub read_bytes_total: Family, Counter>,
pub ack_total: Family, Counter>,
pub sink_write_total: Family, Counter>,
+ pub dropped_total: Family, Counter>,
+ pub fbsink_write_total: Family, Counter>,
+
// gauge
pub source_pending: Family, Gauge>,
+
// timers
pub e2e_time: Family, Histogram>,
pub read_time: Family, Histogram>,
@@ -118,6 +126,8 @@ impl MonoVtxMetrics {
read_bytes_total: Family::, Counter>::default(),
ack_total: Family::, Counter>::default(),
sink_write_total: Family::, Counter>::default(),
+ dropped_total: Family::, Counter>::default(),
+ fbsink_write_total: Family::, Counter>::default(),
// gauge
source_pending: Family::, Gauge>::default(),
// timers
@@ -160,6 +170,19 @@ impl MonoVtxMetrics {
"A Counter to keep track of the total number of bytes read from the source",
metrics.read_bytes_total.clone(),
);
+
+ registry.register(
+ DROPPED_TOTAL,
+ "A Counter to keep track of the total number of messages dropped by the monovtx",
+ metrics.dropped_total.clone(),
+ );
+
+ registry.register(
+ FALLBACK_SINK_WRITE_TOTAL,
+ "A Counter to keep track of the total number of messages written to the fallback sink",
+ metrics.fbsink_write_total.clone(),
+ );
+
// gauges
registry.register(
SOURCE_PENDING,
@@ -192,7 +215,6 @@ impl MonoVtxMetrics {
"A Histogram to keep track of the total time taken to Write to the Sink, in microseconds",
metrics.sink_time.clone(),
);
-
metrics
}
}
diff --git a/rust/numaflow-models/src/models/backoff.rs b/rust/numaflow-models/src/models/backoff.rs
new file mode 100644
index 0000000000..7d2baddcd7
--- /dev/null
+++ b/rust/numaflow-models/src/models/backoff.rs
@@ -0,0 +1,38 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by Openapi Generator. DO NOT EDIT.
+
+/// Backoff : Backoff defines parameters used to systematically configure the retry strategy.
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct Backoff {
+ #[serde(rename = "interval", skip_serializing_if = "Option::is_none")]
+ pub interval: Option,
+ /// Steps defines the number of times to try writing to a sink including retries
+ #[serde(rename = "steps", skip_serializing_if = "Option::is_none")]
+ pub steps: Option,
+}
+
+impl Backoff {
+ /// Backoff defines parameters used to systematically configure the retry strategy.
+ pub fn new() -> Backoff {
+ Backoff {
+ interval: None,
+ steps: None,
+ }
+ }
+}
diff --git a/rust/numaflow-models/src/models/mod.rs b/rust/numaflow-models/src/models/mod.rs
index 648964f907..bbb11ca261 100644
--- a/rust/numaflow-models/src/models/mod.rs
+++ b/rust/numaflow-models/src/models/mod.rs
@@ -6,6 +6,8 @@ pub mod abstract_vertex;
pub use self::abstract_vertex::AbstractVertex;
pub mod authorization;
pub use self::authorization::Authorization;
+pub mod backoff;
+pub use self::backoff::Backoff;
pub mod basic_auth;
pub use self::basic_auth::BasicAuth;
pub mod blackhole;
@@ -124,6 +126,8 @@ pub mod redis_config;
pub use self::redis_config::RedisConfig;
pub mod redis_settings;
pub use self::redis_settings::RedisSettings;
+pub mod retry_strategy;
+pub use self::retry_strategy::RetryStrategy;
pub mod sasl;
pub use self::sasl::Sasl;
pub mod sasl_plain;
diff --git a/rust/numaflow-models/src/models/retry_strategy.rs b/rust/numaflow-models/src/models/retry_strategy.rs
new file mode 100644
index 0000000000..0b1a52a654
--- /dev/null
+++ b/rust/numaflow-models/src/models/retry_strategy.rs
@@ -0,0 +1,38 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by Openapi Generator. DO NOT EDIT.
+
+/// RetryStrategy : RetryStrategy struct encapsulates the settings for retrying operations in the event of failures. It includes a BackOff strategy to manage the timing of retries and defines the action to take upon failure.
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct RetryStrategy {
+ #[serde(rename = "backoff", skip_serializing_if = "Option::is_none")]
+ pub backoff: Option>,
+ /// OnFailure specifies the action to take when a retry fails. The default action is to retry.
+ #[serde(rename = "onFailure", skip_serializing_if = "Option::is_none")]
+ pub on_failure: Option,
+}
+
+impl RetryStrategy {
+ /// RetryStrategy struct encapsulates the settings for retrying operations in the event of failures. It includes a BackOff strategy to manage the timing of retries and defines the action to take upon failure.
+ pub fn new() -> RetryStrategy {
+ RetryStrategy {
+ backoff: None,
+ on_failure: None,
+ }
+ }
+}
diff --git a/rust/numaflow-models/src/models/sink.rs b/rust/numaflow-models/src/models/sink.rs
index aa3402f42e..91f18340eb 100644
--- a/rust/numaflow-models/src/models/sink.rs
+++ b/rust/numaflow-models/src/models/sink.rs
@@ -26,6 +26,8 @@ pub struct Sink {
pub kafka: Option>,
#[serde(rename = "log", skip_serializing_if = "Option::is_none")]
pub log: Option>,
+ #[serde(rename = "retryStrategy", skip_serializing_if = "Option::is_none")]
+ pub retry_strategy: Option>,
#[serde(rename = "udsink", skip_serializing_if = "Option::is_none")]
pub udsink: Option>,
}
@@ -37,6 +39,7 @@ impl Sink {
fallback: None,
kafka: None,
log: None,
+ retry_strategy: None,
udsink: None,
}
}
From 97ccc7fb5b312c13b4e23c5ff5b4b33053d81ad4 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Fri, 30 Aug 2024 19:48:21 -0700
Subject: [PATCH 041/188] chore: placeholders for rolling update (#2019)
---
api/json-schema/schema.json | 36 +
api/openapi-spec/swagger.json | 36 +
.../numaflow.numaproj.io_monovertices.yaml | 10 +
.../full/numaflow.numaproj.io_vertices.yaml | 10 +
config/install.yaml | 20 +
config/namespace-install.yaml | 20 +
docs/APIs.md | 152 +++
pkg/apis/numaflow/v1alpha1/generated.pb.go | 1213 ++++++++++-------
pkg/apis/numaflow/v1alpha1/generated.proto | 24 +
.../numaflow/v1alpha1/mono_vertex_types.go | 8 +
.../numaflow/v1alpha1/openapi_generated.go | 56 +
pkg/apis/numaflow/v1alpha1/vertex_types.go | 8 +
.../src/models/mono_vertex_status.rs | 16 +
.../src/models/vertex_status.rs | 16 +
14 files changed, 1149 insertions(+), 476 deletions(-)
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index 496b6a963f..e594c79167 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19292,6 +19292,15 @@
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
},
+ "currentHash": {
+ "description": "If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).",
+ "type": "string"
+ },
+ "currentReplicas": {
+ "description": "The number of Pods created by the controller from the MonoVertex version indicated by currentHash.",
+ "format": "int64",
+ "type": "integer"
+ },
"lastScaledAt": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time",
"description": "Time of last scaling operation."
@@ -19325,6 +19334,15 @@
},
"selector": {
"type": "string"
+ },
+ "updateHash": {
+ "description": "If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "type": "string"
+ },
+ "updatedReplicas": {
+ "description": "The number of Pods created by the controller from the MonoVertex version indicated by updateHash.",
+ "format": "int64",
+ "type": "integer"
}
},
"type": "object"
@@ -20580,6 +20598,15 @@
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
},
+ "currentHash": {
+ "description": "If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).",
+ "type": "string"
+ },
+ "currentReplicas": {
+ "description": "The number of Pods created by the controller from the Vertex version indicated by currentHash.",
+ "format": "int64",
+ "type": "integer"
+ },
"lastScaledAt": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time",
"description": "Time of last scaling operation."
@@ -20610,6 +20637,15 @@
},
"selector": {
"type": "string"
+ },
+ "updateHash": {
+ "description": "If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "type": "string"
+ },
+ "updatedReplicas": {
+ "description": "The number of Pods created by the controller from the Vertex version indicated by updateHash.",
+ "format": "int64",
+ "type": "integer"
}
},
"type": "object"
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index 41d81a0efb..8f47047db6 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19288,6 +19288,15 @@
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
},
+ "currentHash": {
+ "description": "If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).",
+ "type": "string"
+ },
+ "currentReplicas": {
+ "description": "The number of Pods created by the controller from the MonoVertex version indicated by currentHash.",
+ "type": "integer",
+ "format": "int64"
+ },
"lastScaledAt": {
"description": "Time of last scaling operation.",
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
@@ -19321,6 +19330,15 @@
},
"selector": {
"type": "string"
+ },
+ "updateHash": {
+ "description": "If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "type": "string"
+ },
+ "updatedReplicas": {
+ "description": "The number of Pods created by the controller from the MonoVertex version indicated by updateHash.",
+ "type": "integer",
+ "format": "int64"
}
}
},
@@ -20558,6 +20576,15 @@
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
},
+ "currentHash": {
+ "description": "If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).",
+ "type": "string"
+ },
+ "currentReplicas": {
+ "description": "The number of Pods created by the controller from the Vertex version indicated by currentHash.",
+ "type": "integer",
+ "format": "int64"
+ },
"lastScaledAt": {
"description": "Time of last scaling operation.",
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
@@ -20588,6 +20615,15 @@
},
"selector": {
"type": "string"
+ },
+ "updateHash": {
+ "description": "If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "type": "string"
+ },
+ "updatedReplicas": {
+ "description": "The number of Pods created by the controller from the Vertex version indicated by updateHash.",
+ "type": "integer",
+ "format": "int64"
}
}
},
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index c07e927f5b..8e503f47d6 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -5549,6 +5549,11 @@ spec:
- type
type: object
type: array
+ currentHash:
+ type: string
+ currentReplicas:
+ format: int32
+ type: integer
lastScaledAt:
format: date-time
type: string
@@ -5579,6 +5584,11 @@ spec:
type: integer
selector:
type: string
+ updateHash:
+ type: string
+ updatedReplicas:
+ format: int32
+ type: integer
type: object
required:
- spec
diff --git a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
index 8f0d150360..e7bb52a8d8 100644
--- a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
@@ -5484,6 +5484,11 @@ spec:
- type
type: object
type: array
+ currentHash:
+ type: string
+ currentReplicas:
+ format: int32
+ type: integer
lastScaledAt:
format: date-time
type: string
@@ -5508,6 +5513,11 @@ spec:
type: integer
selector:
type: string
+ updateHash:
+ type: string
+ updatedReplicas:
+ format: int32
+ type: integer
type: object
required:
- spec
diff --git a/config/install.yaml b/config/install.yaml
index d2e76c26b3..849958226d 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -8193,6 +8193,11 @@ spec:
- type
type: object
type: array
+ currentHash:
+ type: string
+ currentReplicas:
+ format: int32
+ type: integer
lastScaledAt:
format: date-time
type: string
@@ -8223,6 +8228,11 @@ spec:
type: integer
selector:
type: string
+ updateHash:
+ type: string
+ updatedReplicas:
+ format: int32
+ type: integer
type: object
required:
- spec
@@ -23599,6 +23609,11 @@ spec:
- type
type: object
type: array
+ currentHash:
+ type: string
+ currentReplicas:
+ format: int32
+ type: integer
lastScaledAt:
format: date-time
type: string
@@ -23623,6 +23638,11 @@ spec:
type: integer
selector:
type: string
+ updateHash:
+ type: string
+ updatedReplicas:
+ format: int32
+ type: integer
type: object
required:
- spec
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 13afdb3228..3b65e083b6 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -8193,6 +8193,11 @@ spec:
- type
type: object
type: array
+ currentHash:
+ type: string
+ currentReplicas:
+ format: int32
+ type: integer
lastScaledAt:
format: date-time
type: string
@@ -8223,6 +8228,11 @@ spec:
type: integer
selector:
type: string
+ updateHash:
+ type: string
+ updatedReplicas:
+ format: int32
+ type: integer
type: object
required:
- spec
@@ -23599,6 +23609,11 @@ spec:
- type
type: object
type: array
+ currentHash:
+ type: string
+ currentReplicas:
+ format: int32
+ type: integer
lastScaledAt:
format: date-time
type: string
@@ -23623,6 +23638,11 @@ spec:
type: integer
selector:
type: string
+ updateHash:
+ type: string
+ updatedReplicas:
+ format: int32
+ type: integer
type: object
required:
- spec
diff --git a/docs/APIs.md b/docs/APIs.md
index 3e631f3870..a311d7ee07 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -6372,6 +6372,82 @@ The number of pods targeted by this MonoVertex with a Ready Condition.
+
+
+
+
+currentReplicas
uint32
+
+
+
+
+
+
+The number of Pods created by the controller from the MonoVertex version
+indicated by currentHash.
+
+
+
+
+
+
+
+
+
+
+updatedReplicas
uint32
+
+
+
+
+
+
+The number of Pods created by the controller from the MonoVertex version
+indicated by updateHash.
+
+
+
+
+
+
+
+
+
+
+currentHash
string
+
+
+
+
+
+
+If not empty, indicates the version of the MonoVertex used to generate
+Pods in the sequence \[0,currentReplicas).
+
+
+
+
+
+
+
+
+
+
+updateHash
string
+
+
+
+
+
+
+If not empty, indicates the version of the MonoVertx used to generate
+Pods in the sequence \[replicas-updatedReplicas,replicas)
+
+
+
+
+
+
@@ -11343,6 +11419,82 @@ The number of pods targeted by this Vertex with a Ready Condition.
+
+
+
+
+currentReplicas
uint32
+
+
+
+
+
+
+The number of Pods created by the controller from the Vertex version
+indicated by currentHash.
+
+
+
+
+
+
+
+
+
+
+updatedReplicas
uint32
+
+
+
+
+
+
+The number of Pods created by the controller from the Vertex version
+indicated by updateHash.
+
+
+
+
+
+
+
+
+
+
+currentHash
string
+
+
+
+
+
+
+If not empty, indicates the version of the Vertex used to generate Pods
+in the sequence \[0,currentReplicas).
+
+
+
+
+
+
+
+
+
+
+updateHash
string
+
+
+
+
+
+
+If not empty, indicates the version of the Vertx used to generate Pods
+in the sequence \[replicas-updatedReplicas,replicas)
+
+
+
+
+
+
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index 3514c89361..f9bbc352a1 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -2762,483 +2762,488 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7611 bytes of a gzipped FileDescriptorProto
+ // 7695 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0xd7,
- 0x75, 0xa6, 0xfa, 0x8f, 0xec, 0x3e, 0x4d, 0x72, 0x38, 0x77, 0x46, 0x23, 0xce, 0x68, 0x34, 0x1c,
- 0x97, 0x2c, 0x79, 0xbc, 0xb6, 0xc9, 0x15, 0x57, 0x7f, 0xfe, 0x95, 0xd8, 0xe4, 0x90, 0xc3, 0x19,
- 0x72, 0x86, 0x3e, 0x4d, 0x8e, 0x64, 0x6b, 0x6d, 0x6d, 0xb1, 0xea, 0xb2, 0x59, 0x62, 0x75, 0x55,
- 0xab, 0xaa, 0x9a, 0x33, 0x94, 0x77, 0xe1, 0x1f, 0x2d, 0x20, 0x2d, 0x16, 0x8b, 0x5d, 0xf8, 0xc9,
- 0xc0, 0xc2, 0xbb, 0xd8, 0xc5, 0x02, 0x7e, 0x30, 0xbc, 0x0f, 0x8b, 0xd5, 0x3e, 0x2c, 0x90, 0x38,
- 0x0e, 0x82, 0xc4, 0x0e, 0xf2, 0xe3, 0x87, 0x00, 0x51, 0x5e, 0x88, 0x98, 0x41, 0x1e, 0x12, 0x20,
- 0x86, 0x11, 0x03, 0x89, 0x3d, 0x30, 0xe2, 0xe0, 0xfe, 0xd5, 0x5f, 0x57, 0xcf, 0x90, 0x5d, 0xe4,
- 0x68, 0x94, 0xe8, 0xad, 0xea, 0xdc, 0x73, 0xbf, 0x73, 0xef, 0xa9, 0xfb, 0x73, 0xee, 0xb9, 0xe7,
- 0xde, 0x82, 0xc5, 0x96, 0x15, 0x6c, 0x75, 0x37, 0xa6, 0x0c, 0xb7, 0x3d, 0xed, 0x74, 0xdb, 0x7a,
- 0xc7, 0x73, 0x5f, 0xe3, 0x0f, 0x9b, 0xb6, 0x7b, 0x6b, 0xba, 0xb3, 0xdd, 0x9a, 0xd6, 0x3b, 0x96,
- 0x1f, 0x51, 0x76, 0x9e, 0xd2, 0xed, 0xce, 0x96, 0xfe, 0xd4, 0x74, 0x8b, 0x3a, 0xd4, 0xd3, 0x03,
- 0x6a, 0x4e, 0x75, 0x3c, 0x37, 0x70, 0xc9, 0x73, 0x11, 0xd0, 0x94, 0x02, 0x9a, 0x52, 0xd9, 0xa6,
- 0x3a, 0xdb, 0xad, 0x29, 0x06, 0x14, 0x51, 0x14, 0xd0, 0xb9, 0x4f, 0xc4, 0x4a, 0xd0, 0x72, 0x5b,
- 0xee, 0x34, 0xc7, 0xdb, 0xe8, 0x6e, 0xf2, 0x37, 0xfe, 0xc2, 0x9f, 0x84, 0x9c, 0x73, 0xda, 0xf6,
- 0xf3, 0xfe, 0x94, 0xe5, 0xb2, 0x62, 0x4d, 0x1b, 0xae, 0x47, 0xa7, 0x77, 0x7a, 0xca, 0x72, 0xee,
- 0xe9, 0x88, 0xa7, 0xad, 0x1b, 0x5b, 0x96, 0x43, 0xbd, 0x5d, 0x55, 0x97, 0x69, 0x8f, 0xfa, 0x6e,
- 0xd7, 0x33, 0xe8, 0xa1, 0x72, 0xf9, 0xd3, 0x6d, 0x1a, 0xe8, 0x59, 0xb2, 0xa6, 0xfb, 0xe5, 0xf2,
- 0xba, 0x4e, 0x60, 0xb5, 0x7b, 0xc5, 0x3c, 0x7b, 0xaf, 0x0c, 0xbe, 0xb1, 0x45, 0xdb, 0x7a, 0x3a,
- 0x9f, 0xf6, 0x03, 0x80, 0x53, 0xb3, 0x1b, 0x7e, 0xe0, 0xe9, 0x46, 0xb0, 0xea, 0x9a, 0x6b, 0xb4,
- 0xdd, 0xb1, 0xf5, 0x80, 0x92, 0x6d, 0xa8, 0xb2, 0xb2, 0x99, 0x7a, 0xa0, 0x4f, 0x14, 0x2e, 0x16,
- 0x2e, 0xd5, 0x67, 0x66, 0xa7, 0x06, 0xfc, 0x16, 0x53, 0x2b, 0x12, 0xa8, 0x31, 0xb2, 0xbf, 0x37,
- 0x59, 0x55, 0x6f, 0x18, 0x0a, 0x20, 0xdf, 0x2a, 0xc0, 0x88, 0xe3, 0x9a, 0xb4, 0x49, 0x6d, 0x6a,
- 0x04, 0xae, 0x37, 0x51, 0xbc, 0x58, 0xba, 0x54, 0x9f, 0xf9, 0xf2, 0xc0, 0x12, 0x33, 0x6a, 0x34,
- 0x75, 0x3d, 0x26, 0xe0, 0xb2, 0x13, 0x78, 0xbb, 0x8d, 0xd3, 0x3f, 0xdc, 0x9b, 0x7c, 0x68, 0x7f,
- 0x6f, 0x72, 0x24, 0x9e, 0x84, 0x89, 0x92, 0x90, 0x75, 0xa8, 0x07, 0xae, 0xcd, 0x54, 0x66, 0xb9,
- 0x8e, 0x3f, 0x51, 0xe2, 0x05, 0xbb, 0x30, 0x25, 0xb4, 0xcd, 0xc4, 0x4f, 0xb1, 0xe6, 0x32, 0xb5,
- 0xf3, 0xd4, 0xd4, 0x5a, 0xc8, 0xd6, 0x38, 0x25, 0x81, 0xeb, 0x11, 0xcd, 0xc7, 0x38, 0x0e, 0xa1,
- 0x70, 0xc2, 0xa7, 0x46, 0xd7, 0xb3, 0x82, 0xdd, 0x39, 0xd7, 0x09, 0xe8, 0xed, 0x60, 0xa2, 0xcc,
- 0xb5, 0xfc, 0x64, 0x16, 0xf4, 0xaa, 0x6b, 0x36, 0x93, 0xdc, 0x8d, 0x53, 0xfb, 0x7b, 0x93, 0x27,
- 0x52, 0x44, 0x4c, 0x63, 0x12, 0x07, 0xc6, 0xad, 0xb6, 0xde, 0xa2, 0xab, 0x5d, 0xdb, 0x6e, 0x52,
- 0xc3, 0xa3, 0x81, 0x3f, 0x51, 0xe1, 0x55, 0xb8, 0x94, 0x25, 0x67, 0xd9, 0x35, 0x74, 0xfb, 0xc6,
- 0xc6, 0x6b, 0xd4, 0x08, 0x90, 0x6e, 0x52, 0x8f, 0x3a, 0x06, 0x6d, 0x4c, 0xc8, 0xca, 0x8c, 0x2f,
- 0xa5, 0x90, 0xb0, 0x07, 0x9b, 0x2c, 0xc2, 0xc9, 0x8e, 0x67, 0xb9, 0xbc, 0x08, 0xb6, 0xee, 0xfb,
- 0xd7, 0xf5, 0x36, 0x9d, 0x18, 0xba, 0x58, 0xb8, 0x54, 0x6b, 0x9c, 0x95, 0x30, 0x27, 0x57, 0xd3,
- 0x0c, 0xd8, 0x9b, 0x87, 0x5c, 0x82, 0xaa, 0x22, 0x4e, 0x0c, 0x5f, 0x2c, 0x5c, 0xaa, 0x88, 0xb6,
- 0xa3, 0xf2, 0x62, 0x98, 0x4a, 0x16, 0xa0, 0xaa, 0x6f, 0x6e, 0x5a, 0x0e, 0xe3, 0xac, 0x72, 0x15,
- 0x9e, 0xcf, 0xaa, 0xda, 0xac, 0xe4, 0x11, 0x38, 0xea, 0x0d, 0xc3, 0xbc, 0xe4, 0x2a, 0x10, 0x9f,
- 0x7a, 0x3b, 0x96, 0x41, 0x67, 0x0d, 0xc3, 0xed, 0x3a, 0x01, 0x2f, 0x7b, 0x8d, 0x97, 0xfd, 0x9c,
- 0x2c, 0x3b, 0x69, 0xf6, 0x70, 0x60, 0x46, 0x2e, 0xf2, 0x22, 0x8c, 0xcb, 0x6e, 0x17, 0x69, 0x01,
- 0x38, 0xd2, 0x69, 0xa6, 0x48, 0x4c, 0xa5, 0x61, 0x0f, 0x37, 0x31, 0xe1, 0xbc, 0xde, 0x0d, 0xdc,
- 0x36, 0x83, 0x4c, 0x0a, 0x5d, 0x73, 0xb7, 0xa9, 0x33, 0x51, 0xbf, 0x58, 0xb8, 0x54, 0x6d, 0x5c,
- 0xdc, 0xdf, 0x9b, 0x3c, 0x3f, 0x7b, 0x17, 0x3e, 0xbc, 0x2b, 0x0a, 0xb9, 0x01, 0x35, 0xd3, 0xf1,
- 0x57, 0x5d, 0xdb, 0x32, 0x76, 0x27, 0x46, 0x78, 0x01, 0x9f, 0x92, 0x55, 0xad, 0xcd, 0x5f, 0x6f,
- 0x8a, 0x84, 0x3b, 0x7b, 0x93, 0xe7, 0x7b, 0x47, 0xc7, 0xa9, 0x30, 0x1d, 0x23, 0x0c, 0xb2, 0xc2,
- 0x01, 0xe7, 0x5c, 0x67, 0xd3, 0x6a, 0x4d, 0x8c, 0xf2, 0xaf, 0x71, 0xb1, 0x4f, 0x83, 0x9e, 0xbf,
- 0xde, 0x14, 0x7c, 0x8d, 0x51, 0x29, 0x4e, 0xbc, 0x62, 0x84, 0x40, 0x4c, 0x18, 0x53, 0xe3, 0xea,
- 0x9c, 0xad, 0x5b, 0x6d, 0x7f, 0x62, 0x8c, 0x37, 0xde, 0x0f, 0xf7, 0xc1, 0xc4, 0x38, 0x73, 0xe3,
- 0x8c, 0xac, 0xca, 0x58, 0x82, 0xec, 0x63, 0x0a, 0xf3, 0xdc, 0x0b, 0x70, 0xb2, 0x67, 0x6c, 0x20,
- 0xe3, 0x50, 0xda, 0xa6, 0xbb, 0x7c, 0xe8, 0xab, 0x21, 0x7b, 0x24, 0xa7, 0xa1, 0xb2, 0xa3, 0xdb,
- 0x5d, 0x3a, 0x51, 0xe4, 0x34, 0xf1, 0xf2, 0xa9, 0xe2, 0xf3, 0x05, 0xed, 0x7f, 0x96, 0x60, 0x44,
- 0x8d, 0x38, 0x4d, 0xcb, 0xd9, 0x26, 0x2f, 0x41, 0xc9, 0x76, 0x5b, 0x72, 0xdc, 0xfc, 0xcc, 0xc0,
- 0xa3, 0xd8, 0xb2, 0xdb, 0x6a, 0x0c, 0xef, 0xef, 0x4d, 0x96, 0x96, 0xdd, 0x16, 0x32, 0x44, 0x62,
- 0x40, 0x65, 0x5b, 0xdf, 0xdc, 0xd6, 0x79, 0x19, 0xea, 0x33, 0x8d, 0x81, 0xa1, 0xaf, 0x31, 0x14,
- 0x56, 0xd6, 0x46, 0x6d, 0x7f, 0x6f, 0xb2, 0xc2, 0x5f, 0x51, 0x60, 0x13, 0x17, 0x6a, 0x1b, 0xb6,
- 0x6e, 0x6c, 0x6f, 0xb9, 0x36, 0x9d, 0x28, 0xe5, 0x14, 0xd4, 0x50, 0x48, 0xe2, 0x33, 0x87, 0xaf,
- 0x18, 0xc9, 0x20, 0x06, 0x0c, 0x75, 0x4d, 0xdf, 0x72, 0xb6, 0xe5, 0x18, 0xf8, 0xc2, 0xc0, 0xd2,
- 0xd6, 0xe7, 0x79, 0x9d, 0x60, 0x7f, 0x6f, 0x72, 0x48, 0x3c, 0xa3, 0x84, 0xd6, 0x7e, 0x5a, 0x87,
- 0x31, 0xf5, 0x91, 0x6e, 0x52, 0x2f, 0xa0, 0xb7, 0xc9, 0x45, 0x28, 0x3b, 0xac, 0x6b, 0xf2, 0x8f,
- 0xdc, 0x18, 0x91, 0xcd, 0xa5, 0xcc, 0xbb, 0x24, 0x4f, 0x61, 0x25, 0x13, 0x4d, 0x45, 0x2a, 0x7c,
- 0xf0, 0x92, 0x35, 0x39, 0x8c, 0x28, 0x99, 0x78, 0x46, 0x09, 0x4d, 0x5e, 0x81, 0x32, 0xaf, 0xbc,
- 0x50, 0xf5, 0x67, 0x07, 0x17, 0xc1, 0xaa, 0x5e, 0x65, 0x35, 0xe0, 0x15, 0xe7, 0xa0, 0xac, 0x29,
- 0x76, 0xcd, 0x4d, 0xa9, 0xd8, 0xcf, 0xe4, 0x50, 0xec, 0x82, 0x68, 0x8a, 0xeb, 0xf3, 0x0b, 0xc8,
- 0x10, 0xc9, 0x7f, 0x2e, 0xc0, 0x49, 0xc3, 0x75, 0x02, 0x9d, 0x99, 0x1a, 0x6a, 0x92, 0x9d, 0xa8,
- 0x70, 0x39, 0x57, 0x07, 0x96, 0x33, 0x97, 0x46, 0x6c, 0x3c, 0xcc, 0xe6, 0x8c, 0x1e, 0x32, 0xf6,
- 0xca, 0x26, 0xff, 0xb5, 0x00, 0x0f, 0xb3, 0xb1, 0xbc, 0x87, 0x99, 0xcf, 0x40, 0x47, 0x5b, 0xaa,
- 0xb3, 0xfb, 0x7b, 0x93, 0x0f, 0x2f, 0x65, 0x09, 0xc3, 0xec, 0x32, 0xb0, 0xd2, 0x9d, 0xd2, 0x7b,
- 0xcd, 0x12, 0x3e, 0xbb, 0xd5, 0x67, 0x96, 0x8f, 0xd2, 0xd4, 0x69, 0x3c, 0x2a, 0x9b, 0x72, 0x96,
- 0x65, 0x87, 0x59, 0xa5, 0x20, 0x97, 0x61, 0x78, 0xc7, 0xb5, 0xbb, 0x6d, 0xea, 0x4f, 0x54, 0xf9,
- 0x10, 0x7b, 0x2e, 0x6b, 0x88, 0xbd, 0xc9, 0x59, 0x1a, 0x27, 0x24, 0xfc, 0xb0, 0x78, 0xf7, 0x51,
- 0xe5, 0x25, 0x16, 0x0c, 0xd9, 0x56, 0xdb, 0x0a, 0x7c, 0x3e, 0x71, 0xd6, 0x67, 0x2e, 0x0f, 0x5c,
- 0x2d, 0xd1, 0x45, 0x97, 0x39, 0x98, 0xe8, 0x35, 0xe2, 0x19, 0xa5, 0x00, 0x36, 0x14, 0xfa, 0x86,
- 0x6e, 0x8b, 0x89, 0xb5, 0x3e, 0xf3, 0xb9, 0xc1, 0xbb, 0x0d, 0x43, 0x69, 0x8c, 0xca, 0x3a, 0x55,
- 0xf8, 0x2b, 0x0a, 0x6c, 0xf2, 0x25, 0x18, 0x4b, 0x7c, 0x4d, 0x7f, 0xa2, 0xce, 0xb5, 0xf3, 0x58,
- 0x96, 0x76, 0x42, 0xae, 0x68, 0xe6, 0x49, 0xb4, 0x10, 0x1f, 0x53, 0x60, 0xe4, 0x1a, 0x54, 0x7d,
- 0xcb, 0xa4, 0x86, 0xee, 0xf9, 0x13, 0x23, 0x07, 0x01, 0x1e, 0x97, 0xc0, 0xd5, 0xa6, 0xcc, 0x86,
- 0x21, 0x00, 0x99, 0x02, 0xe8, 0xe8, 0x5e, 0x60, 0x09, 0x43, 0x75, 0x94, 0x1b, 0x4d, 0x63, 0xfb,
- 0x7b, 0x93, 0xb0, 0x1a, 0x52, 0x31, 0xc6, 0xc1, 0xf8, 0x59, 0xde, 0x25, 0xa7, 0xd3, 0x0d, 0xc4,
- 0xc4, 0x5a, 0x13, 0xfc, 0xcd, 0x90, 0x8a, 0x31, 0x0e, 0xf2, 0xbd, 0x02, 0x3c, 0x1a, 0xbd, 0xf6,
- 0x76, 0xb2, 0x13, 0x47, 0xde, 0xc9, 0x26, 0xf7, 0xf7, 0x26, 0x1f, 0x6d, 0xf6, 0x17, 0x89, 0x77,
- 0x2b, 0x8f, 0xf6, 0x12, 0x8c, 0xce, 0x76, 0x83, 0x2d, 0xd7, 0xb3, 0xde, 0xe0, 0x46, 0x37, 0x59,
- 0x80, 0x4a, 0xc0, 0x8d, 0x27, 0x31, 0x2f, 0x3f, 0x91, 0xa5, 0x6a, 0x61, 0xc8, 0x5e, 0xa3, 0xbb,
- 0xca, 0x1a, 0x10, 0xf3, 0xa3, 0x30, 0xa6, 0x44, 0x76, 0xed, 0xdf, 0x17, 0x60, 0xb8, 0xa1, 0x1b,
- 0xdb, 0xee, 0xe6, 0x26, 0x79, 0x19, 0xaa, 0x96, 0x13, 0x50, 0x6f, 0x47, 0xb7, 0x25, 0xec, 0x54,
- 0x0c, 0x36, 0x5c, 0x89, 0x45, 0xf5, 0x66, 0x6b, 0x1e, 0x26, 0x68, 0xbe, 0x2b, 0xd7, 0x0a, 0xdc,
- 0x1e, 0x5d, 0x92, 0x18, 0x18, 0xa2, 0x91, 0x49, 0xa8, 0xf8, 0x01, 0xed, 0xf8, 0x7c, 0xe6, 0x19,
- 0x15, 0xc5, 0x68, 0x32, 0x02, 0x0a, 0xba, 0xf6, 0x3f, 0x0a, 0x50, 0x6b, 0xe8, 0xbe, 0x65, 0xb0,
- 0x5a, 0x92, 0x39, 0x28, 0x77, 0x7d, 0xea, 0x1d, 0xae, 0x6e, 0x7c, 0xb2, 0x58, 0xf7, 0xa9, 0x87,
- 0x3c, 0x33, 0xb9, 0x01, 0xd5, 0x8e, 0xee, 0xfb, 0xb7, 0x5c, 0xcf, 0x94, 0x13, 0xde, 0x01, 0x81,
- 0x84, 0x71, 0x2e, 0xb3, 0x62, 0x08, 0xa2, 0xd5, 0x21, 0x9a, 0xf1, 0xb5, 0x9f, 0x17, 0xe0, 0x54,
- 0xa3, 0xbb, 0xb9, 0x49, 0x3d, 0x69, 0x8b, 0x4a, 0x2b, 0x8f, 0x42, 0xc5, 0xa3, 0xa6, 0xe5, 0xcb,
- 0xb2, 0xcf, 0x0f, 0xdc, 0x82, 0x90, 0xa1, 0x48, 0xa3, 0x92, 0xeb, 0x8b, 0x13, 0x50, 0xa0, 0x93,
- 0x2e, 0xd4, 0x5e, 0xa3, 0x81, 0x1f, 0x78, 0x54, 0x6f, 0xcb, 0xda, 0x5d, 0x19, 0x58, 0xd4, 0x55,
- 0x1a, 0x34, 0x39, 0x52, 0xdc, 0x86, 0x0d, 0x89, 0x18, 0x49, 0xd2, 0x7e, 0x50, 0x81, 0x91, 0x39,
- 0xb7, 0xbd, 0x61, 0x39, 0xd4, 0xbc, 0x6c, 0xb6, 0x28, 0x79, 0x15, 0xca, 0xd4, 0x6c, 0x51, 0x59,
- 0xdb, 0xc1, 0xa7, 0x7b, 0x06, 0x16, 0x19, 0x2d, 0xec, 0x0d, 0x39, 0x30, 0x59, 0x86, 0xb1, 0x4d,
- 0xcf, 0x6d, 0x8b, 0x11, 0x74, 0x6d, 0xb7, 0x23, 0x2d, 0xd6, 0xc6, 0x87, 0xd5, 0xa8, 0xb4, 0x90,
- 0x48, 0xbd, 0xb3, 0x37, 0x09, 0xd1, 0x1b, 0xa6, 0xf2, 0x92, 0x97, 0x61, 0x22, 0xa2, 0x84, 0x43,
- 0xc9, 0x1c, 0x5b, 0x44, 0x70, 0x8b, 0xa5, 0xd2, 0x38, 0xbf, 0xbf, 0x37, 0x39, 0xb1, 0xd0, 0x87,
- 0x07, 0xfb, 0xe6, 0x26, 0x6f, 0x15, 0x60, 0x3c, 0x4a, 0x14, 0xc3, 0xbb, 0x34, 0x54, 0x8e, 0x68,
- 0xde, 0xe0, 0xab, 0xad, 0x85, 0x94, 0x08, 0xec, 0x11, 0x4a, 0x16, 0x60, 0x24, 0x70, 0x63, 0xfa,
- 0xaa, 0x70, 0x7d, 0x69, 0xca, 0x3d, 0xb0, 0xe6, 0xf6, 0xd5, 0x56, 0x22, 0x1f, 0x41, 0x38, 0xa3,
- 0xde, 0x53, 0x9a, 0x1a, 0xe2, 0x9a, 0x3a, 0xb7, 0xbf, 0x37, 0x79, 0x66, 0x2d, 0x93, 0x03, 0xfb,
- 0xe4, 0x24, 0x5f, 0x2f, 0xc0, 0x98, 0x4a, 0x92, 0x3a, 0x1a, 0x3e, 0x4a, 0x1d, 0x11, 0xd6, 0x22,
- 0xd6, 0x12, 0x02, 0x30, 0x25, 0x50, 0xfb, 0x45, 0x19, 0x6a, 0xe1, 0x00, 0x4b, 0x1e, 0x87, 0x0a,
- 0x5f, 0xf8, 0x4b, 0xbb, 0x39, 0x9c, 0x39, 0xb9, 0x7f, 0x00, 0x45, 0x1a, 0x79, 0x02, 0x86, 0x0d,
- 0xb7, 0xdd, 0xd6, 0x1d, 0x93, 0x3b, 0x73, 0x6a, 0x8d, 0x3a, 0x33, 0x18, 0xe6, 0x04, 0x09, 0x55,
- 0x1a, 0x39, 0x0f, 0x65, 0xdd, 0x6b, 0x09, 0xbf, 0x4a, 0x4d, 0x8c, 0x47, 0xb3, 0x5e, 0xcb, 0x47,
- 0x4e, 0x25, 0x9f, 0x84, 0x12, 0x75, 0x76, 0x26, 0xca, 0xfd, 0x2d, 0x92, 0xcb, 0xce, 0xce, 0x4d,
- 0xdd, 0x6b, 0xd4, 0x65, 0x19, 0x4a, 0x97, 0x9d, 0x1d, 0x64, 0x79, 0xc8, 0x32, 0x0c, 0x53, 0x67,
- 0x87, 0x7d, 0x7b, 0xe9, 0xf0, 0xf8, 0x50, 0x9f, 0xec, 0x8c, 0x45, 0x1a, 0xe7, 0xa1, 0x5d, 0x23,
- 0xc9, 0xa8, 0x20, 0xc8, 0x17, 0x60, 0x44, 0x98, 0x38, 0x2b, 0xec, 0x9b, 0xf8, 0x13, 0x43, 0x1c,
- 0x72, 0xb2, 0xbf, 0x8d, 0xc4, 0xf9, 0x22, 0x07, 0x53, 0x8c, 0xe8, 0x63, 0x02, 0x8a, 0x7c, 0x01,
- 0x6a, 0x6a, 0x3d, 0xaa, 0xbe, 0x6c, 0xa6, 0x6f, 0x46, 0x2d, 0x62, 0x91, 0xbe, 0xde, 0xb5, 0x3c,
- 0xda, 0xa6, 0x4e, 0xe0, 0x37, 0x4e, 0xaa, 0xd5, 0xba, 0x4a, 0xf5, 0x31, 0x42, 0x23, 0x1b, 0xbd,
- 0x4e, 0x26, 0xe1, 0x21, 0x79, 0xbc, 0xcf, 0xa8, 0x3e, 0x80, 0x87, 0xe9, 0xcb, 0x70, 0x22, 0xf4,
- 0x02, 0x49, 0x47, 0x82, 0xf0, 0x99, 0x3c, 0xcd, 0xb2, 0x2f, 0x25, 0x93, 0xee, 0xec, 0x4d, 0x3e,
- 0x96, 0xe1, 0x4a, 0x88, 0x18, 0x30, 0x0d, 0xa6, 0x7d, 0xbf, 0x04, 0xbd, 0xd6, 0x7f, 0x52, 0x69,
- 0x85, 0xa3, 0x56, 0x5a, 0xba, 0x42, 0x62, 0xf8, 0x7c, 0x5e, 0x66, 0xcb, 0x5f, 0xa9, 0xac, 0x0f,
- 0x53, 0x3a, 0xea, 0x0f, 0xf3, 0xa0, 0xf4, 0x1d, 0xed, 0xed, 0x32, 0x8c, 0xcd, 0xeb, 0xb4, 0xed,
- 0x3a, 0xf7, 0x5c, 0x0b, 0x15, 0x1e, 0x88, 0xb5, 0xd0, 0x25, 0xa8, 0x7a, 0xb4, 0x63, 0x5b, 0x86,
- 0x2e, 0x8c, 0x2f, 0xe9, 0x7b, 0x44, 0x49, 0xc3, 0x30, 0xb5, 0xcf, 0x1a, 0xb8, 0xf4, 0x40, 0xae,
- 0x81, 0xcb, 0xef, 0xfd, 0x1a, 0x58, 0xfb, 0x7a, 0x11, 0xb8, 0xa1, 0x42, 0x2e, 0x42, 0x99, 0x4d,
- 0xc2, 0x69, 0xcf, 0x0b, 0x6f, 0x38, 0x3c, 0x85, 0x9c, 0x83, 0x62, 0xe0, 0xca, 0x9e, 0x07, 0x32,
- 0xbd, 0xb8, 0xe6, 0x62, 0x31, 0x70, 0xc9, 0x1b, 0x00, 0x86, 0xeb, 0x98, 0x96, 0x72, 0xc9, 0xe7,
- 0xab, 0xd8, 0x82, 0xeb, 0xdd, 0xd2, 0x3d, 0x73, 0x2e, 0x44, 0x14, 0xab, 0xa0, 0xe8, 0x1d, 0x63,
- 0xd2, 0xc8, 0x0b, 0x30, 0xe4, 0x3a, 0x0b, 0x5d, 0xdb, 0xe6, 0x0a, 0xad, 0x35, 0x3e, 0xc2, 0x96,
- 0xa6, 0x37, 0x38, 0xe5, 0xce, 0xde, 0xe4, 0x59, 0x61, 0xdf, 0xb2, 0xb7, 0x97, 0x3c, 0x2b, 0xb0,
- 0x9c, 0x56, 0x33, 0xf0, 0xf4, 0x80, 0xb6, 0x76, 0x51, 0x66, 0xd3, 0xbe, 0x59, 0x80, 0xfa, 0x82,
- 0x75, 0x9b, 0x9a, 0x2f, 0x59, 0x8e, 0xe9, 0xde, 0x22, 0x08, 0x43, 0x36, 0x75, 0x5a, 0xc1, 0xd6,
- 0x80, 0xeb, 0x07, 0xb1, 0x36, 0xe6, 0x08, 0x28, 0x91, 0xc8, 0x34, 0xd4, 0x84, 0xf5, 0x69, 0x39,
- 0x2d, 0xae, 0xc3, 0x6a, 0x34, 0xe8, 0x35, 0x55, 0x02, 0x46, 0x3c, 0xda, 0x2e, 0x9c, 0xec, 0x51,
- 0x03, 0x31, 0xa1, 0x1c, 0xe8, 0x2d, 0x35, 0xbe, 0x2e, 0x0c, 0xac, 0xe0, 0x35, 0xbd, 0x15, 0x53,
- 0x2e, 0x9f, 0xe3, 0xd7, 0x74, 0x36, 0xc7, 0x33, 0x74, 0xed, 0x57, 0x05, 0xa8, 0x2e, 0x74, 0x1d,
- 0x83, 0x2f, 0xd1, 0xee, 0xed, 0x91, 0x53, 0x06, 0x43, 0x31, 0xd3, 0x60, 0xe8, 0xc2, 0xd0, 0xf6,
- 0xad, 0xd0, 0xa0, 0xa8, 0xcf, 0xac, 0x0c, 0xde, 0x2a, 0x64, 0x91, 0xa6, 0xae, 0x71, 0x3c, 0xb1,
- 0x61, 0x34, 0x26, 0x0b, 0x34, 0x74, 0xed, 0x25, 0x2e, 0x54, 0x0a, 0x3b, 0xf7, 0x49, 0xa8, 0xc7,
- 0xd8, 0x0e, 0xe5, 0x3b, 0xfe, 0x7f, 0x65, 0x18, 0x5a, 0x6c, 0x36, 0x67, 0x57, 0x97, 0xc8, 0x33,
- 0x50, 0x97, 0x7b, 0x09, 0xd7, 0x23, 0x1d, 0x84, 0x5b, 0x49, 0xcd, 0x28, 0x09, 0xe3, 0x7c, 0xcc,
- 0x1c, 0xf3, 0xa8, 0x6e, 0xb7, 0x65, 0x67, 0x09, 0xcd, 0x31, 0x64, 0x44, 0x14, 0x69, 0x44, 0x87,
- 0x31, 0xb6, 0xc2, 0x63, 0x2a, 0x14, 0xab, 0x37, 0xd9, 0x6d, 0x0e, 0xb8, 0xbe, 0xe3, 0x46, 0xe2,
- 0x7a, 0x02, 0x00, 0x53, 0x80, 0xe4, 0x79, 0xa8, 0xea, 0xdd, 0x60, 0x8b, 0x1b, 0xd0, 0xa2, 0x6f,
- 0x9c, 0xe7, 0x5b, 0x2d, 0x92, 0x76, 0x67, 0x6f, 0x72, 0xe4, 0x1a, 0x36, 0x9e, 0x51, 0xef, 0x18,
- 0x72, 0xb3, 0xc2, 0xa9, 0x15, 0xa3, 0x2c, 0x5c, 0xe5, 0xd0, 0x85, 0x5b, 0x4d, 0x00, 0x60, 0x0a,
- 0x90, 0xbc, 0x02, 0x23, 0xdb, 0x74, 0x37, 0xd0, 0x37, 0xa4, 0x80, 0xa1, 0xc3, 0x08, 0x18, 0x67,
- 0x26, 0xdc, 0xb5, 0x58, 0x76, 0x4c, 0x80, 0x11, 0x1f, 0x4e, 0x6f, 0x53, 0x6f, 0x83, 0x7a, 0xae,
- 0x5c, 0x7d, 0x4a, 0x21, 0xc3, 0x87, 0x11, 0x32, 0xb1, 0xbf, 0x37, 0x79, 0xfa, 0x5a, 0x06, 0x0c,
- 0x66, 0x82, 0x6b, 0xbf, 0x2c, 0xc2, 0x89, 0x45, 0xb1, 0x99, 0xeb, 0x7a, 0x62, 0x12, 0x26, 0x67,
- 0xa1, 0xe4, 0x75, 0xba, 0xbc, 0xe5, 0x94, 0x84, 0xbb, 0x16, 0x57, 0xd7, 0x91, 0xd1, 0xc8, 0xcb,
- 0x50, 0x35, 0xe5, 0x90, 0x21, 0x17, 0xbf, 0x03, 0x39, 0x2a, 0xd4, 0x1b, 0x86, 0x68, 0xcc, 0xd2,
- 0x6f, 0xfb, 0xad, 0xa6, 0xf5, 0x06, 0x95, 0xeb, 0x41, 0x6e, 0xe9, 0xaf, 0x08, 0x12, 0xaa, 0x34,
- 0x36, 0xab, 0x6e, 0xd3, 0x5d, 0xb1, 0x1a, 0x2a, 0x47, 0xb3, 0xea, 0x35, 0x49, 0xc3, 0x30, 0x95,
- 0x4c, 0xaa, 0xce, 0xc2, 0x5a, 0x41, 0x59, 0xac, 0xe4, 0x6f, 0x32, 0x82, 0xec, 0x37, 0x6c, 0xc8,
- 0x7c, 0xcd, 0x0a, 0x02, 0xea, 0xc9, 0xcf, 0x38, 0xd0, 0x90, 0x79, 0x95, 0x23, 0xa0, 0x44, 0x22,
- 0x1f, 0x83, 0x1a, 0x07, 0x6f, 0xd8, 0xee, 0x06, 0xff, 0x70, 0x35, 0xb1, 0xa6, 0xbf, 0xa9, 0x88,
- 0x18, 0xa5, 0x6b, 0xbf, 0x2e, 0xc2, 0x99, 0x45, 0x1a, 0x08, 0xab, 0x66, 0x9e, 0x76, 0x6c, 0x77,
- 0x97, 0x99, 0x96, 0x48, 0x5f, 0x27, 0x2f, 0x02, 0x58, 0xfe, 0x46, 0x73, 0xc7, 0xe0, 0xfd, 0x40,
- 0xf4, 0xe1, 0x8b, 0xb2, 0x4b, 0xc2, 0x52, 0xb3, 0x21, 0x53, 0xee, 0x24, 0xde, 0x30, 0x96, 0x27,
- 0x5a, 0x5e, 0x15, 0xef, 0xb2, 0xbc, 0x6a, 0x02, 0x74, 0x22, 0x03, 0xb5, 0xc4, 0x39, 0xff, 0x95,
- 0x12, 0x73, 0x18, 0xdb, 0x34, 0x06, 0x93, 0xc7, 0x64, 0x74, 0x60, 0xdc, 0xa4, 0x9b, 0x7a, 0xd7,
- 0x0e, 0x42, 0xa3, 0x5a, 0x76, 0xe2, 0x83, 0xdb, 0xe5, 0xe1, 0x46, 0xf3, 0x7c, 0x0a, 0x09, 0x7b,
- 0xb0, 0xb5, 0xff, 0x5f, 0x82, 0x73, 0x8b, 0x34, 0x08, 0x3d, 0x2e, 0x72, 0x74, 0x6c, 0x76, 0xa8,
- 0xc1, 0xbe, 0xc2, 0x5b, 0x05, 0x18, 0xb2, 0xf5, 0x0d, 0x6a, 0xb3, 0xd9, 0x8b, 0xd5, 0xe6, 0xd5,
- 0x81, 0x27, 0x82, 0xfe, 0x52, 0xa6, 0x96, 0xb9, 0x84, 0xd4, 0xd4, 0x20, 0x88, 0x28, 0xc5, 0xb3,
- 0x41, 0xdd, 0xb0, 0xbb, 0x7e, 0x40, 0xbd, 0x55, 0xd7, 0x0b, 0xa4, 0x3d, 0x19, 0x0e, 0xea, 0x73,
- 0x51, 0x12, 0xc6, 0xf9, 0xc8, 0x0c, 0x80, 0x61, 0x5b, 0xd4, 0x09, 0x78, 0x2e, 0xd1, 0xaf, 0x88,
- 0xfa, 0xbe, 0x73, 0x61, 0x0a, 0xc6, 0xb8, 0x98, 0xa8, 0xb6, 0xeb, 0x58, 0x81, 0x2b, 0x44, 0x95,
- 0x93, 0xa2, 0x56, 0xa2, 0x24, 0x8c, 0xf3, 0xf1, 0x6c, 0x34, 0xf0, 0x2c, 0xc3, 0xe7, 0xd9, 0x2a,
- 0xa9, 0x6c, 0x51, 0x12, 0xc6, 0xf9, 0xd8, 0x9c, 0x17, 0xab, 0xff, 0xa1, 0xe6, 0xbc, 0xef, 0xd6,
- 0xe0, 0x42, 0x42, 0xad, 0x81, 0x1e, 0xd0, 0xcd, 0xae, 0xdd, 0xa4, 0x81, 0xfa, 0x80, 0x03, 0xce,
- 0x85, 0xff, 0x31, 0xfa, 0xee, 0x22, 0x84, 0xc4, 0x38, 0x9a, 0xef, 0xde, 0x53, 0xc0, 0x03, 0x7d,
- 0xfb, 0x69, 0xa8, 0x39, 0x7a, 0xe0, 0xf3, 0x8e, 0x2b, 0xfb, 0x68, 0x68, 0x86, 0x5d, 0x57, 0x09,
- 0x18, 0xf1, 0x90, 0x55, 0x38, 0x2d, 0x55, 0x7c, 0xf9, 0x76, 0xc7, 0xf5, 0x02, 0xea, 0x89, 0xbc,
- 0x72, 0x3a, 0x95, 0x79, 0x4f, 0xaf, 0x64, 0xf0, 0x60, 0x66, 0x4e, 0xb2, 0x02, 0xa7, 0x0c, 0xb1,
- 0xad, 0x4e, 0x6d, 0x57, 0x37, 0x15, 0xa0, 0x70, 0x70, 0x85, 0x4b, 0xa3, 0xb9, 0x5e, 0x16, 0xcc,
- 0xca, 0x97, 0x6e, 0xcd, 0x43, 0x03, 0xb5, 0xe6, 0xe1, 0x41, 0x5a, 0x73, 0x75, 0xb0, 0xd6, 0x5c,
- 0x3b, 0x58, 0x6b, 0x66, 0x9a, 0x67, 0xed, 0x88, 0x7a, 0xcc, 0x3c, 0x11, 0x33, 0x6c, 0x2c, 0x6a,
- 0x23, 0xd4, 0x7c, 0x33, 0x83, 0x07, 0x33, 0x73, 0x92, 0x0d, 0x38, 0x27, 0xe8, 0x97, 0x1d, 0xc3,
- 0xdb, 0xed, 0xb0, 0x89, 0x27, 0x86, 0x5b, 0x4f, 0x78, 0x18, 0xcf, 0x35, 0xfb, 0x72, 0xe2, 0x5d,
- 0x50, 0xc8, 0xa7, 0x61, 0x54, 0x7c, 0xa5, 0x15, 0xbd, 0xc3, 0x61, 0x45, 0x0c, 0xc7, 0xc3, 0x12,
- 0x76, 0x74, 0x2e, 0x9e, 0x88, 0x49, 0x5e, 0x32, 0x0b, 0x27, 0x3a, 0x3b, 0x06, 0x7b, 0x5c, 0xda,
- 0xbc, 0x4e, 0xa9, 0x49, 0x4d, 0xbe, 0x69, 0x54, 0x6b, 0x3c, 0xa2, 0x1c, 0x1d, 0xab, 0xc9, 0x64,
- 0x4c, 0xf3, 0x93, 0xe7, 0x61, 0xc4, 0x0f, 0x74, 0x2f, 0x90, 0x6e, 0xbd, 0x89, 0x31, 0x11, 0xe3,
- 0xa2, 0xbc, 0x5e, 0xcd, 0x58, 0x1a, 0x26, 0x38, 0x33, 0xe7, 0x8b, 0x13, 0xc7, 0x37, 0x5f, 0xe4,
- 0x19, 0xad, 0x7e, 0xaf, 0x08, 0x17, 0x17, 0x69, 0xb0, 0xe2, 0x3a, 0xd2, 0x29, 0x9a, 0x35, 0xed,
- 0x1f, 0xc8, 0x27, 0x9a, 0x9c, 0xb4, 0x8b, 0x47, 0x3a, 0x69, 0x97, 0x8e, 0x68, 0xd2, 0x2e, 0x1f,
- 0xe3, 0xa4, 0xfd, 0x9b, 0x45, 0x78, 0x24, 0xa1, 0xc9, 0x55, 0xd7, 0x54, 0x03, 0xfe, 0x07, 0x0a,
- 0x3c, 0x80, 0x02, 0xef, 0x08, 0xbb, 0x93, 0x6f, 0x6b, 0xa5, 0x2c, 0x9e, 0x37, 0xd3, 0x16, 0xcf,
- 0x2b, 0x79, 0x66, 0xbe, 0x0c, 0x09, 0x07, 0x9a, 0xf1, 0xae, 0x02, 0xf1, 0xe4, 0x26, 0x9c, 0x70,
- 0xfd, 0xc4, 0x8c, 0x9e, 0x30, 0x88, 0x0e, 0x7b, 0x38, 0x30, 0x23, 0x17, 0x69, 0xc2, 0xc3, 0x3e,
- 0x75, 0x02, 0xcb, 0xa1, 0x76, 0x12, 0x4e, 0x58, 0x43, 0x8f, 0x49, 0xb8, 0x87, 0x9b, 0x59, 0x4c,
- 0x98, 0x9d, 0x37, 0xcf, 0x38, 0xf0, 0x07, 0xc0, 0x4d, 0x4e, 0xa1, 0x9a, 0x23, 0xb3, 0x58, 0xde,
- 0x4a, 0x5b, 0x2c, 0xaf, 0xe6, 0xff, 0x6e, 0x83, 0x59, 0x2b, 0x33, 0x00, 0xfc, 0x2b, 0xc4, 0xcd,
- 0x95, 0x70, 0x92, 0xc6, 0x30, 0x05, 0x63, 0x5c, 0x6c, 0x02, 0x52, 0x7a, 0x8e, 0x5b, 0x2a, 0xe1,
- 0x04, 0xd4, 0x8c, 0x27, 0x62, 0x92, 0xb7, 0xaf, 0xb5, 0x53, 0x19, 0xd8, 0xda, 0xb9, 0x0a, 0x24,
- 0xe1, 0x78, 0x14, 0x78, 0x43, 0xc9, 0x18, 0xce, 0xa5, 0x1e, 0x0e, 0xcc, 0xc8, 0xd5, 0xa7, 0x29,
- 0x0f, 0x1f, 0x6d, 0x53, 0xae, 0x0e, 0xde, 0x94, 0xc9, 0xab, 0x70, 0x96, 0x8b, 0x92, 0xfa, 0x49,
- 0x02, 0x0b, 0xbb, 0xe7, 0x43, 0x12, 0xf8, 0x2c, 0xf6, 0x63, 0xc4, 0xfe, 0x18, 0xec, 0xfb, 0x18,
- 0x1e, 0x35, 0x99, 0x70, 0xdd, 0xee, 0x6f, 0x13, 0xcd, 0x65, 0xf0, 0x60, 0x66, 0x4e, 0xd6, 0xc4,
- 0x02, 0xd6, 0x0c, 0xf5, 0x0d, 0x9b, 0x9a, 0x32, 0x86, 0x35, 0x6c, 0x62, 0x6b, 0xcb, 0x4d, 0x99,
- 0x82, 0x31, 0xae, 0x2c, 0x33, 0x65, 0xe4, 0x90, 0x66, 0xca, 0x22, 0xf7, 0xd2, 0x6f, 0x26, 0xac,
- 0x21, 0x69, 0xeb, 0x84, 0x51, 0xc9, 0x73, 0x69, 0x06, 0xec, 0xcd, 0xc3, 0xad, 0x44, 0xc3, 0xb3,
- 0x3a, 0x81, 0x9f, 0xc4, 0x1a, 0x4b, 0x59, 0x89, 0x19, 0x3c, 0x98, 0x99, 0x93, 0xd9, 0xe7, 0x5b,
- 0x54, 0xb7, 0x83, 0xad, 0x24, 0xe0, 0x89, 0xa4, 0x7d, 0x7e, 0xa5, 0x97, 0x05, 0xb3, 0xf2, 0x65,
- 0x4e, 0x48, 0xe3, 0x0f, 0xa6, 0x59, 0xf5, 0x8d, 0x12, 0x9c, 0x5d, 0xa4, 0x41, 0x18, 0xde, 0xf3,
- 0x81, 0x1b, 0xe5, 0x3d, 0x70, 0xa3, 0x7c, 0xa7, 0x02, 0xa7, 0x16, 0x69, 0xd0, 0x63, 0x8d, 0xfd,
- 0x33, 0x55, 0xff, 0x0a, 0x9c, 0x8a, 0x22, 0xca, 0x9a, 0x81, 0xeb, 0x89, 0xb9, 0x3c, 0xb5, 0x5a,
- 0x6e, 0xf6, 0xb2, 0x60, 0x56, 0x3e, 0xf2, 0x05, 0x78, 0x84, 0x4f, 0xf5, 0x4e, 0x4b, 0xf8, 0x67,
- 0x85, 0x33, 0x21, 0x76, 0x26, 0x62, 0x52, 0x42, 0x3e, 0xd2, 0xcc, 0x66, 0xc3, 0x7e, 0xf9, 0xc9,
- 0x57, 0x61, 0xa4, 0x63, 0x75, 0xa8, 0x6d, 0x39, 0xdc, 0x3e, 0xcb, 0x1d, 0x12, 0xb2, 0x1a, 0x03,
- 0x8b, 0x16, 0x70, 0x71, 0x2a, 0x26, 0x04, 0x66, 0xb6, 0xd4, 0xea, 0x31, 0xb6, 0xd4, 0xbf, 0x2d,
- 0xc2, 0xf0, 0xa2, 0xe7, 0x76, 0x3b, 0x8d, 0x5d, 0xd2, 0x82, 0xa1, 0x5b, 0x7c, 0xf3, 0x4c, 0x6e,
- 0x4d, 0x0d, 0x1e, 0x95, 0x2d, 0xf6, 0xe0, 0x22, 0x93, 0x48, 0xbc, 0xa3, 0x84, 0x67, 0x8d, 0x78,
- 0x9b, 0xee, 0x52, 0x53, 0xee, 0xa1, 0x85, 0x8d, 0xf8, 0x1a, 0x23, 0xa2, 0x48, 0x23, 0x6d, 0x38,
- 0xa1, 0xdb, 0xb6, 0x7b, 0x8b, 0x9a, 0xcb, 0x7a, 0x40, 0x1d, 0xea, 0xab, 0x2d, 0xc9, 0xc3, 0xba,
- 0xa5, 0xf9, 0xbe, 0xfe, 0x6c, 0x12, 0x0a, 0xd3, 0xd8, 0xe4, 0x35, 0x18, 0xf6, 0x03, 0xd7, 0x53,
- 0xc6, 0x56, 0x7d, 0x66, 0x6e, 0xf0, 0x8f, 0xde, 0xf8, 0x7c, 0x53, 0x40, 0x09, 0x9f, 0xbd, 0x7c,
- 0x41, 0x25, 0x40, 0xfb, 0x76, 0x01, 0xe0, 0xca, 0xda, 0xda, 0xaa, 0xdc, 0x5e, 0x30, 0xa1, 0xac,
- 0x77, 0xc3, 0x8d, 0xca, 0xc1, 0x37, 0x04, 0x13, 0x61, 0x99, 0x72, 0x0f, 0xaf, 0x1b, 0x6c, 0x21,
- 0x47, 0x27, 0x1f, 0x85, 0x61, 0x69, 0x20, 0x4b, 0xb5, 0x87, 0xa1, 0x05, 0xd2, 0x88, 0x46, 0x95,
- 0xae, 0xfd, 0x9f, 0x22, 0xc0, 0x92, 0x69, 0xd3, 0xa6, 0x0a, 0xa4, 0xaf, 0x05, 0x5b, 0x1e, 0xf5,
- 0xb7, 0x5c, 0xdb, 0x1c, 0x70, 0x37, 0x95, 0xfb, 0xfc, 0xd7, 0x14, 0x08, 0x46, 0x78, 0xc4, 0x84,
- 0x11, 0x3f, 0xa0, 0x1d, 0x15, 0xa9, 0x39, 0xe0, 0x26, 0xca, 0xb8, 0xf0, 0x8b, 0x44, 0x38, 0x98,
- 0x40, 0x25, 0x3a, 0xd4, 0x2d, 0xc7, 0x10, 0x1d, 0xa4, 0xb1, 0x3b, 0x60, 0x43, 0x3a, 0xc1, 0x56,
- 0x1c, 0x4b, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0x9f, 0x15, 0xe1, 0x0c, 0x97, 0xc7, 0x8a, 0x91, 0x88,
- 0xc7, 0x24, 0xff, 0xa6, 0xe7, 0xd0, 0xdf, 0xbf, 0x3c, 0x98, 0x68, 0x71, 0x66, 0x6c, 0x85, 0x06,
- 0x7a, 0x64, 0xcf, 0x45, 0xb4, 0xd8, 0x49, 0xbf, 0x2e, 0x94, 0x7d, 0x36, 0x5e, 0x09, 0xed, 0x35,
- 0x07, 0x6e, 0x42, 0xd9, 0x15, 0xe0, 0xa3, 0x57, 0xb8, 0x6b, 0xcc, 0x47, 0x2d, 0x2e, 0x8e, 0xfc,
- 0x3b, 0x18, 0xf2, 0x03, 0x3d, 0xe8, 0xaa, 0xae, 0xb9, 0x7e, 0xd4, 0x82, 0x39, 0x78, 0x34, 0x8e,
- 0x88, 0x77, 0x94, 0x42, 0xb5, 0x9f, 0x15, 0xe0, 0x5c, 0x76, 0xc6, 0x65, 0xcb, 0x0f, 0xc8, 0xbf,
- 0xee, 0x51, 0xfb, 0x01, 0xbf, 0x38, 0xcb, 0xcd, 0x95, 0x1e, 0xc6, 0x85, 0x2b, 0x4a, 0x4c, 0xe5,
- 0x01, 0x54, 0xac, 0x80, 0xb6, 0xd5, 0xfa, 0xf2, 0xc6, 0x11, 0x57, 0x3d, 0x36, 0xb5, 0x33, 0x29,
- 0x28, 0x84, 0x69, 0x6f, 0x17, 0xfb, 0x55, 0x99, 0x4f, 0x1f, 0x76, 0x32, 0xe6, 0xf7, 0x5a, 0xbe,
- 0x98, 0xdf, 0x64, 0x81, 0x7a, 0x43, 0x7f, 0xff, 0x6d, 0x6f, 0xe8, 0xef, 0x8d, 0xfc, 0xa1, 0xbf,
- 0x29, 0x35, 0xf4, 0x8d, 0x00, 0x7e, 0xb7, 0x04, 0xe7, 0xef, 0xd6, 0x6c, 0xd8, 0x7c, 0x26, 0x5b,
- 0x67, 0xde, 0xf9, 0xec, 0xee, 0xed, 0x90, 0xcc, 0x40, 0xa5, 0xb3, 0xa5, 0xfb, 0xca, 0x28, 0x53,
- 0x0b, 0x96, 0xca, 0x2a, 0x23, 0xde, 0x61, 0x83, 0x06, 0x37, 0xe6, 0xf8, 0x2b, 0x0a, 0x56, 0x36,
- 0x1c, 0xb7, 0xa9, 0xef, 0x47, 0x3e, 0x81, 0x70, 0x38, 0x5e, 0x11, 0x64, 0x54, 0xe9, 0x24, 0x80,
- 0x21, 0xe1, 0x62, 0x96, 0x33, 0xd3, 0xe0, 0x81, 0x5c, 0x19, 0x61, 0xe2, 0x51, 0xa5, 0xe4, 0x6e,
- 0x85, 0x94, 0x45, 0xa6, 0xa0, 0x1c, 0x44, 0x41, 0xbb, 0x6a, 0x69, 0x5e, 0xce, 0xb0, 0x4f, 0x39,
- 0x1f, 0x5b, 0xd8, 0xbb, 0x1b, 0xdc, 0xa9, 0x6e, 0xca, 0xfd, 0x73, 0xcb, 0x75, 0xb8, 0x41, 0x56,
- 0x8a, 0x16, 0xf6, 0x37, 0x7a, 0x38, 0x30, 0x23, 0x97, 0xf6, 0xc7, 0x55, 0x38, 0x93, 0xdd, 0x1e,
- 0x98, 0xde, 0x76, 0xa8, 0xe7, 0x33, 0xec, 0x42, 0x52, 0x6f, 0x37, 0x05, 0x19, 0x55, 0xfa, 0xfb,
- 0x3a, 0xe0, 0xec, 0x3b, 0x05, 0x38, 0xeb, 0xc9, 0x3d, 0xa2, 0xfb, 0x11, 0x74, 0xf6, 0x98, 0x70,
- 0x67, 0xf4, 0x11, 0x88, 0xfd, 0xcb, 0x42, 0xfe, 0x57, 0x01, 0x26, 0xda, 0x29, 0x3f, 0xc7, 0x31,
- 0x9e, 0x5b, 0xe3, 0x51, 0xf1, 0x2b, 0x7d, 0xe4, 0x61, 0xdf, 0x92, 0x90, 0xaf, 0x42, 0xbd, 0xc3,
- 0xda, 0x85, 0x1f, 0x50, 0xc7, 0x50, 0x47, 0xd7, 0x06, 0xef, 0x49, 0xab, 0x11, 0x96, 0x0a, 0x45,
- 0x13, 0xf6, 0x41, 0x2c, 0x01, 0xe3, 0x12, 0x1f, 0xf0, 0x83, 0x6a, 0x97, 0xa0, 0xea, 0xd3, 0x20,
- 0xb0, 0x9c, 0x96, 0x58, 0x6f, 0xd4, 0x44, 0x5f, 0x69, 0x4a, 0x1a, 0x86, 0xa9, 0xe4, 0x63, 0x50,
- 0xe3, 0x5b, 0x4e, 0xb3, 0x5e, 0xcb, 0x9f, 0xa8, 0xf1, 0x70, 0xb1, 0x51, 0x11, 0x00, 0x27, 0x89,
- 0x18, 0xa5, 0x93, 0xa7, 0x61, 0x64, 0x83, 0x77, 0x5f, 0x79, 0x76, 0x59, 0xf8, 0xb8, 0xb8, 0xb5,
- 0xd6, 0x88, 0xd1, 0x31, 0xc1, 0x45, 0x66, 0x00, 0x68, 0xb8, 0x2f, 0x97, 0xf6, 0x67, 0x45, 0x3b,
- 0x76, 0x18, 0xe3, 0x22, 0x8f, 0x41, 0x29, 0xb0, 0x7d, 0xee, 0xc3, 0xaa, 0x46, 0x4b, 0xd0, 0xb5,
- 0xe5, 0x26, 0x32, 0xba, 0xf6, 0xeb, 0x02, 0x9c, 0x48, 0x1d, 0x2e, 0x61, 0x59, 0xba, 0x9e, 0x2d,
- 0x87, 0x91, 0x30, 0xcb, 0x3a, 0x2e, 0x23, 0xa3, 0x93, 0x57, 0xa5, 0x59, 0x5e, 0xcc, 0x79, 0x4d,
- 0xc3, 0x75, 0x3d, 0xf0, 0x99, 0x1d, 0xde, 0x63, 0x91, 0xf3, 0x6d, 0xbe, 0xa8, 0x3c, 0x72, 0x1e,
- 0x88, 0x6d, 0xf3, 0x45, 0x69, 0x98, 0xe0, 0x4c, 0x39, 0xfc, 0xca, 0x07, 0x71, 0xf8, 0x69, 0xdf,
- 0x2c, 0xc6, 0x34, 0x20, 0x2d, 0xfb, 0x7b, 0x68, 0xe0, 0x49, 0x36, 0x81, 0x86, 0x93, 0x7b, 0x2d,
- 0x3e, 0xff, 0xf1, 0xc9, 0x58, 0xa6, 0x92, 0x97, 0x84, 0xee, 0x4b, 0x39, 0x0f, 0xc3, 0xae, 0x2d,
- 0x37, 0x45, 0x74, 0x95, 0xfa, 0x6a, 0xe1, 0x27, 0x28, 0x1f, 0xd3, 0x27, 0xd0, 0x7e, 0xbf, 0x04,
- 0xf5, 0xab, 0xee, 0xc6, 0xfb, 0x24, 0x82, 0x3a, 0x7b, 0x9a, 0x2a, 0xbe, 0x87, 0xd3, 0xd4, 0x3a,
- 0x3c, 0x12, 0x04, 0x76, 0x93, 0x1a, 0xae, 0x63, 0xfa, 0xb3, 0x9b, 0x01, 0xf5, 0x16, 0x2c, 0xc7,
- 0xf2, 0xb7, 0xa8, 0x29, 0xb7, 0x93, 0x1e, 0xdd, 0xdf, 0x9b, 0x7c, 0x64, 0x6d, 0x6d, 0x39, 0x8b,
- 0x05, 0xfb, 0xe5, 0xe5, 0xc3, 0x86, 0x38, 0x09, 0xc8, 0x4f, 0xca, 0xc8, 0x98, 0x1b, 0x31, 0x6c,
- 0xc4, 0xe8, 0x98, 0xe0, 0xd2, 0xde, 0x29, 0x42, 0x2d, 0x3c, 0x80, 0x4f, 0x9e, 0x80, 0xe1, 0x0d,
- 0xcf, 0xdd, 0xa6, 0x9e, 0xd8, 0xb9, 0x93, 0x27, 0x65, 0x1a, 0x82, 0x84, 0x2a, 0x8d, 0x3c, 0x0e,
- 0x95, 0xc0, 0xed, 0x58, 0x46, 0xda, 0xa1, 0xb6, 0xc6, 0x88, 0x28, 0xd2, 0x8e, 0xaf, 0x81, 0x3f,
- 0x99, 0x30, 0xed, 0x6a, 0x7d, 0x8d, 0xb1, 0x57, 0xa0, 0xec, 0xeb, 0xbe, 0x2d, 0xe7, 0xd3, 0x1c,
- 0x67, 0xd9, 0x67, 0x9b, 0xcb, 0xf2, 0x2c, 0xfb, 0x6c, 0x73, 0x19, 0x39, 0xa8, 0xf6, 0x8b, 0x22,
- 0xd4, 0x85, 0xde, 0xc4, 0xa8, 0x70, 0x94, 0x9a, 0x7b, 0x81, 0x87, 0x52, 0xf8, 0xdd, 0x36, 0xf5,
- 0xb8, 0x9b, 0x49, 0x0e, 0x72, 0xf1, 0xfd, 0x81, 0x28, 0x31, 0x0c, 0xa7, 0x88, 0x48, 0x4a, 0xf5,
- 0xe5, 0x63, 0x54, 0x7d, 0xe5, 0x40, 0xaa, 0x1f, 0x3a, 0x0e, 0xd5, 0xbf, 0x55, 0x84, 0xda, 0xb2,
- 0xb5, 0x49, 0x8d, 0x5d, 0xc3, 0xe6, 0x67, 0x02, 0x4d, 0x6a, 0xd3, 0x80, 0x2e, 0x7a, 0xba, 0x41,
- 0x57, 0xa9, 0x67, 0xf1, 0x0b, 0x6a, 0x58, 0xff, 0xe0, 0x23, 0x90, 0x3c, 0x13, 0x38, 0xdf, 0x87,
- 0x07, 0xfb, 0xe6, 0x26, 0x4b, 0x30, 0x62, 0x52, 0xdf, 0xf2, 0xa8, 0xb9, 0x1a, 0x5b, 0xa8, 0x3c,
- 0xa1, 0xa6, 0x9a, 0xf9, 0x58, 0xda, 0x9d, 0xbd, 0xc9, 0x51, 0xe5, 0xa0, 0x14, 0x2b, 0x96, 0x44,
- 0x56, 0xd6, 0xe5, 0x3b, 0x7a, 0xd7, 0xcf, 0x2a, 0x63, 0xac, 0xcb, 0xaf, 0x66, 0xb3, 0x60, 0xbf,
- 0xbc, 0x5a, 0x05, 0x4a, 0xcb, 0x6e, 0x4b, 0x7b, 0xbb, 0x04, 0xe1, 0x4d, 0x46, 0xe4, 0x3f, 0x14,
- 0xa0, 0xae, 0x3b, 0x8e, 0x1b, 0xc8, 0x5b, 0x82, 0xc4, 0x0e, 0x3c, 0xe6, 0xbe, 0x30, 0x69, 0x6a,
- 0x36, 0x02, 0x15, 0x9b, 0xb7, 0xe1, 0x86, 0x72, 0x2c, 0x05, 0xe3, 0xb2, 0x49, 0x37, 0xb5, 0x9f,
- 0xbc, 0x92, 0xbf, 0x14, 0x07, 0xd8, 0x3d, 0x3e, 0xf7, 0x39, 0x18, 0x4f, 0x17, 0xf6, 0x30, 0xdb,
- 0x41, 0xb9, 0x36, 0xe6, 0x8b, 0x00, 0x51, 0x4c, 0xc9, 0x7d, 0x70, 0x62, 0x59, 0x09, 0x27, 0xd6,
- 0xe2, 0xe0, 0x0a, 0x0e, 0x0b, 0xdd, 0xd7, 0x71, 0xf5, 0x7a, 0xca, 0x71, 0xb5, 0x74, 0x14, 0xc2,
- 0xee, 0xee, 0xac, 0xfa, 0xdf, 0x05, 0x18, 0x8f, 0x98, 0xe5, 0x09, 0xd9, 0xe7, 0x60, 0xd4, 0xa3,
- 0xba, 0xd9, 0xd0, 0x03, 0x63, 0x8b, 0x87, 0x7a, 0x17, 0x78, 0x6c, 0xf6, 0xc9, 0xfd, 0xbd, 0xc9,
- 0x51, 0x8c, 0x27, 0x60, 0x92, 0x8f, 0xe8, 0x50, 0x67, 0x84, 0x35, 0xab, 0x4d, 0xdd, 0x6e, 0x30,
- 0xa0, 0xd7, 0x94, 0x2f, 0x58, 0x30, 0x82, 0xc1, 0x38, 0xa6, 0xf6, 0x6e, 0x01, 0xc6, 0xe2, 0x05,
- 0x3e, 0x76, 0x8f, 0xda, 0x56, 0xd2, 0xa3, 0x36, 0x77, 0x04, 0xdf, 0xa4, 0x8f, 0x17, 0xed, 0x97,
- 0xd5, 0x78, 0xd5, 0xb8, 0xe7, 0x2c, 0xee, 0x2c, 0x28, 0xdc, 0xd5, 0x59, 0xf0, 0xfe, 0xbf, 0xbc,
- 0xa6, 0x9f, 0x95, 0x5b, 0x7e, 0x80, 0xad, 0xdc, 0xf7, 0xf2, 0x06, 0x9c, 0xd8, 0x2d, 0x2e, 0x43,
- 0x39, 0x6e, 0x71, 0x69, 0x87, 0xb7, 0xb8, 0x0c, 0x1f, 0xd9, 0xa0, 0x73, 0x90, 0x9b, 0x5c, 0xaa,
- 0xf7, 0xf5, 0x26, 0x97, 0xda, 0x71, 0xdd, 0xe4, 0x02, 0x79, 0x6f, 0x72, 0x79, 0xb3, 0x00, 0x63,
- 0x66, 0xe2, 0xc4, 0x2c, 0xf7, 0x2d, 0xe4, 0x99, 0x6a, 0x92, 0x07, 0x70, 0xc5, 0x91, 0xa9, 0x24,
- 0x0d, 0x53, 0x22, 0xb5, 0xff, 0x5b, 0x89, 0xcf, 0x03, 0xf7, 0xdb, 0x55, 0xfd, 0x6c, 0xd2, 0x55,
- 0x7d, 0x31, 0xed, 0xaa, 0x3e, 0x11, 0x8b, 0x22, 0x8d, 0xbb, 0xab, 0x3f, 0x1e, 0x1b, 0x1e, 0x4b,
- 0xfc, 0xe6, 0x94, 0x50, 0xd3, 0x19, 0x43, 0xe4, 0xc7, 0xa1, 0xea, 0xab, 0x3b, 0x27, 0xc5, 0xc2,
- 0x26, 0xfa, 0x2e, 0xea, 0x3e, 0xc8, 0x90, 0x83, 0x59, 0xe2, 0x1e, 0xd5, 0x7d, 0xd7, 0x49, 0x5b,
- 0xe2, 0xc8, 0xa9, 0x28, 0x53, 0xe3, 0x2e, 0xf3, 0xa1, 0x7b, 0xb8, 0xcc, 0x75, 0xa8, 0xdb, 0xba,
- 0x1f, 0xac, 0x77, 0x4c, 0x3d, 0xa0, 0xa6, 0xec, 0x6f, 0xff, 0xe2, 0x60, 0x73, 0x15, 0x9b, 0xff,
- 0x22, 0x83, 0x70, 0x39, 0x82, 0xc1, 0x38, 0x26, 0x31, 0x61, 0x84, 0xbd, 0xf2, 0xde, 0x60, 0xce,
- 0xaa, 0x2b, 0x00, 0x0e, 0x23, 0x23, 0xf4, 0xf4, 0x2c, 0xc7, 0x70, 0x30, 0x81, 0xda, 0xc7, 0xab,
- 0x5e, 0x1b, 0xc4, 0xab, 0x4e, 0x3e, 0x2d, 0x8c, 0x8d, 0x5d, 0xf5, 0xc1, 0xb8, 0x37, 0x6e, 0x34,
- 0x8a, 0x2a, 0xc4, 0x78, 0x22, 0x26, 0x79, 0xb5, 0x37, 0x6b, 0x50, 0xbf, 0xae, 0x07, 0xd6, 0x0e,
- 0xe5, 0x5b, 0x40, 0xc7, 0xe3, 0x87, 0xff, 0x6f, 0x05, 0x38, 0x93, 0x8c, 0xf3, 0x3b, 0x46, 0x67,
- 0x3c, 0xbf, 0x35, 0x04, 0x33, 0xa5, 0x61, 0x9f, 0x52, 0x70, 0xb7, 0x7c, 0x4f, 0xd8, 0xe0, 0x71,
- 0xbb, 0xe5, 0x9b, 0xfd, 0x04, 0x62, 0xff, 0xb2, 0xbc, 0x5f, 0xdc, 0xf2, 0x0f, 0xf6, 0xe5, 0x72,
- 0xa9, 0x4d, 0x83, 0xe1, 0x07, 0x66, 0xd3, 0xa0, 0xfa, 0x40, 0x58, 0x6a, 0x9d, 0xd8, 0xa6, 0x41,
- 0x2d, 0x67, 0xf0, 0x8a, 0x0c, 0x8d, 0x17, 0x68, 0xfd, 0x36, 0x1f, 0xf8, 0xa9, 0x76, 0xe5, 0xcc,
- 0x65, 0x06, 0xce, 0x86, 0xee, 0x5b, 0x86, 0x9c, 0x33, 0x73, 0x5c, 0xa6, 0xa9, 0xae, 0xfb, 0x12,
- 0x7b, 0xdc, 0xfc, 0x15, 0x05, 0x76, 0x74, 0xbb, 0x59, 0x31, 0xd7, 0xed, 0x66, 0x64, 0x0e, 0xca,
- 0x0e, 0x5b, 0x7a, 0x97, 0x0e, 0x7d, 0x91, 0xd8, 0xf5, 0x6b, 0x74, 0x17, 0x79, 0x66, 0xed, 0x9d,
- 0x22, 0x00, 0xab, 0xfe, 0xc1, 0xdc, 0xf7, 0x1f, 0x85, 0x61, 0xbf, 0xcb, 0x17, 0xda, 0x72, 0xb6,
- 0x8f, 0x22, 0x7e, 0x04, 0x19, 0x55, 0x3a, 0x79, 0x1c, 0x2a, 0xaf, 0x77, 0x69, 0x57, 0xed, 0x45,
- 0x87, 0xb6, 0xde, 0xe7, 0x19, 0x11, 0x45, 0xda, 0xf1, 0xb9, 0xe2, 0x94, 0x9b, 0xbf, 0x72, 0x5c,
- 0x6e, 0xfe, 0x1a, 0x0c, 0x5f, 0x77, 0x79, 0x00, 0xa1, 0xf6, 0xd7, 0x45, 0x80, 0x28, 0x40, 0x8b,
- 0x7c, 0xbb, 0x00, 0x0f, 0x87, 0x1d, 0x2e, 0x10, 0x26, 0x3b, 0xbf, 0xbf, 0x36, 0xb7, 0xcb, 0x3f,
- 0xab, 0xb3, 0xf3, 0x11, 0x68, 0x35, 0x4b, 0x1c, 0x66, 0x97, 0x82, 0x20, 0x54, 0x69, 0xbb, 0x13,
- 0xec, 0xce, 0x5b, 0x9e, 0x6c, 0x81, 0x99, 0x71, 0x80, 0x97, 0x25, 0x8f, 0xc8, 0x2a, 0xd7, 0x95,
- 0xbc, 0x13, 0xa9, 0x14, 0x0c, 0x71, 0xc8, 0x16, 0x54, 0x1d, 0xf7, 0x55, 0x9f, 0xa9, 0x43, 0x36,
- 0xc7, 0x17, 0x07, 0x57, 0xb9, 0x50, 0xab, 0x70, 0x11, 0xcb, 0x17, 0x1c, 0x76, 0xa4, 0xb2, 0xbf,
- 0x55, 0x84, 0x53, 0x19, 0x7a, 0x20, 0x2f, 0xc2, 0xb8, 0x8c, 0x85, 0x8b, 0x2e, 0x72, 0x2e, 0x44,
- 0x17, 0x39, 0x37, 0x53, 0x69, 0xd8, 0xc3, 0x4d, 0x5e, 0x05, 0xd0, 0x0d, 0x83, 0xfa, 0xfe, 0x8a,
- 0x6b, 0x2a, 0x63, 0xf6, 0x85, 0xfd, 0xbd, 0x49, 0x98, 0x0d, 0xa9, 0x77, 0xf6, 0x26, 0x3f, 0x91,
- 0x15, 0xde, 0x9a, 0xd2, 0x73, 0x94, 0x01, 0x63, 0x90, 0xe4, 0xcb, 0x00, 0x62, 0xdd, 0x16, 0x9e,
- 0xc0, 0xbf, 0x87, 0xb3, 0x63, 0x4a, 0xdd, 0x75, 0x34, 0xf5, 0xf9, 0xae, 0xee, 0x04, 0x56, 0xb0,
- 0x2b, 0x2e, 0x3c, 0xb9, 0x19, 0xa2, 0x60, 0x0c, 0x51, 0xfb, 0xdd, 0x22, 0x54, 0x95, 0x9b, 0xf5,
- 0x3e, 0xf8, 0xd6, 0x5a, 0x09, 0xdf, 0xda, 0x11, 0x05, 0xb4, 0x66, 0x79, 0xd6, 0xdc, 0x94, 0x67,
- 0x6d, 0x31, 0xbf, 0xa8, 0xbb, 0xfb, 0xd5, 0xbe, 0x57, 0x84, 0x31, 0xc5, 0x9a, 0xd7, 0xab, 0xf6,
- 0x59, 0x38, 0x21, 0x36, 0xa2, 0x57, 0xf4, 0xdb, 0xe2, 0xee, 0x17, 0xae, 0xb0, 0xb2, 0x88, 0x21,
- 0x6d, 0x24, 0x93, 0x30, 0xcd, 0xcb, 0x9a, 0xb5, 0x20, 0xad, 0xb3, 0x45, 0x88, 0xd8, 0xba, 0x12,
- 0x8b, 0x25, 0xde, 0xac, 0x1b, 0xa9, 0x34, 0xec, 0xe1, 0x4e, 0xbb, 0xf5, 0xca, 0xc7, 0xe0, 0xd6,
- 0xfb, 0x93, 0x02, 0x8c, 0x44, 0xfa, 0x3a, 0x76, 0xa7, 0xde, 0x66, 0xd2, 0xa9, 0x37, 0x9b, 0xbb,
- 0x39, 0xf4, 0x71, 0xe9, 0xfd, 0xa7, 0x61, 0x48, 0xc4, 0x55, 0x93, 0x0d, 0x38, 0x67, 0x65, 0x46,
- 0x87, 0xc5, 0x46, 0x9b, 0xf0, 0xa0, 0xf0, 0x52, 0x5f, 0x4e, 0xbc, 0x0b, 0x0a, 0xe9, 0x42, 0x75,
- 0x87, 0x7a, 0x81, 0x65, 0x50, 0x55, 0xbf, 0xc5, 0xdc, 0x26, 0x99, 0x74, 0x5c, 0x86, 0x3a, 0xbd,
- 0x29, 0x05, 0x60, 0x28, 0x8a, 0x6c, 0x40, 0x85, 0x9a, 0x2d, 0xaa, 0x6e, 0xe3, 0xc9, 0x79, 0xd7,
- 0x65, 0xa8, 0x4f, 0xf6, 0xe6, 0xa3, 0x80, 0x26, 0x3e, 0xd4, 0x6c, 0xb5, 0x31, 0x25, 0xdb, 0xe1,
- 0xe0, 0x06, 0x56, 0xb8, 0xc5, 0x15, 0x1d, 0xd4, 0x0f, 0x49, 0x18, 0xc9, 0x21, 0xdb, 0xa1, 0x87,
- 0xac, 0x72, 0x44, 0x83, 0xc7, 0x5d, 0xfc, 0x63, 0x3e, 0xd4, 0x6e, 0xe9, 0x01, 0xf5, 0xda, 0xba,
- 0xb7, 0x2d, 0x57, 0x1b, 0x83, 0xd7, 0xf0, 0x25, 0x85, 0x14, 0xd5, 0x30, 0x24, 0x61, 0x24, 0x87,
- 0xb8, 0x50, 0x0b, 0xa4, 0xf9, 0xac, 0xdc, 0x80, 0x83, 0x0b, 0x55, 0x86, 0xb8, 0x2f, 0xe3, 0xab,
- 0xd5, 0x2b, 0x46, 0x32, 0xc8, 0x4e, 0xe2, 0x3a, 0x62, 0x71, 0x09, 0x75, 0x23, 0x87, 0x3b, 0x59,
- 0x42, 0x45, 0xd3, 0x4d, 0xf6, 0xb5, 0xc6, 0xda, 0x3b, 0x95, 0x68, 0x58, 0xbe, 0xdf, 0x4e, 0xae,
- 0xa7, 0x93, 0x4e, 0xae, 0x0b, 0x69, 0x27, 0x57, 0x6a, 0x7f, 0xf3, 0xf0, 0x11, 0x99, 0x29, 0xf7,
- 0x52, 0xf9, 0x18, 0xdc, 0x4b, 0x4f, 0x41, 0x7d, 0x87, 0x8f, 0x04, 0xe2, 0x6a, 0x9f, 0x0a, 0x9f,
- 0x46, 0xf8, 0xc8, 0x7e, 0x33, 0x22, 0x63, 0x9c, 0x87, 0x65, 0x91, 0x3f, 0x60, 0x08, 0xef, 0x46,
- 0x95, 0x59, 0x9a, 0x11, 0x19, 0xe3, 0x3c, 0x3c, 0x98, 0xcb, 0x72, 0xb6, 0x45, 0x86, 0x61, 0x9e,
- 0x41, 0x04, 0x73, 0x29, 0x22, 0x46, 0xe9, 0xe4, 0x12, 0x54, 0xbb, 0xe6, 0xa6, 0xe0, 0xad, 0x72,
- 0x5e, 0x6e, 0x61, 0xae, 0xcf, 0x2f, 0xc8, 0xab, 0x86, 0x54, 0x2a, 0x2b, 0x49, 0x5b, 0xef, 0xa8,
- 0x04, 0xbe, 0x36, 0x94, 0x25, 0x59, 0x89, 0xc8, 0x18, 0xe7, 0x21, 0x9f, 0x82, 0x31, 0x8f, 0x9a,
- 0x5d, 0x83, 0x86, 0xb9, 0x84, 0x77, 0x8a, 0x88, 0x3f, 0x4d, 0xc4, 0x53, 0x30, 0xc5, 0xd9, 0xc7,
- 0x49, 0x56, 0x1f, 0x28, 0xf4, 0xf4, 0xa7, 0x05, 0x20, 0xbd, 0xc1, 0xcf, 0x64, 0x0b, 0x86, 0x1c,
- 0xee, 0xfd, 0xca, 0x7d, 0x9b, 0x72, 0xcc, 0x89, 0x26, 0x86, 0x25, 0x49, 0x90, 0xf8, 0xc4, 0x81,
- 0x2a, 0xbd, 0x1d, 0x50, 0xcf, 0x09, 0x0f, 0x43, 0x1c, 0xcd, 0xcd, 0xcd, 0x62, 0x35, 0x20, 0x91,
- 0x31, 0x94, 0xa1, 0xfd, 0xbc, 0x08, 0xf5, 0x18, 0xdf, 0xbd, 0x16, 0x95, 0xfc, 0x3c, 0xb6, 0x70,
- 0x3a, 0xad, 0x7b, 0xb6, 0xec, 0x61, 0xb1, 0xf3, 0xd8, 0x32, 0x09, 0x97, 0x31, 0xce, 0x47, 0x66,
- 0x00, 0xda, 0xba, 0x1f, 0x50, 0x8f, 0xcf, 0xbe, 0xa9, 0x53, 0xd0, 0x2b, 0x61, 0x0a, 0xc6, 0xb8,
- 0xc8, 0x45, 0x79, 0xf7, 0x76, 0x39, 0x79, 0x6b, 0x5d, 0x9f, 0x8b, 0xb5, 0x2b, 0x47, 0x70, 0xb1,
- 0x36, 0x69, 0xc1, 0xb8, 0x2a, 0xb5, 0x4a, 0x3d, 0xdc, 0x9d, 0x66, 0x62, 0xfd, 0x92, 0x82, 0xc0,
- 0x1e, 0x50, 0xed, 0x9d, 0x02, 0x8c, 0x26, 0x5c, 0x1e, 0xe2, 0xbe, 0x39, 0x15, 0xba, 0x9f, 0xb8,
- 0x6f, 0x2e, 0x16, 0x71, 0xff, 0x24, 0x0c, 0x09, 0x05, 0xa5, 0x23, 0xf2, 0x84, 0x0a, 0x51, 0xa6,
- 0xb2, 0xb1, 0x4c, 0x3a, 0x55, 0xd3, 0x63, 0x99, 0xf4, 0xba, 0xa2, 0x4a, 0x17, 0xbe, 0x7a, 0x51,
- 0xba, 0x5e, 0x5f, 0xbd, 0xa0, 0x63, 0xc8, 0xa1, 0x7d, 0x9f, 0x97, 0x3b, 0xf0, 0x76, 0xc3, 0xb5,
- 0x5c, 0x0b, 0x86, 0x65, 0x14, 0x96, 0xec, 0x1a, 0x2f, 0xe6, 0xf0, 0xc3, 0x70, 0x1c, 0x19, 0x6f,
- 0xa4, 0x1b, 0xdb, 0x37, 0x36, 0x37, 0x51, 0xa1, 0x93, 0xcb, 0x50, 0x73, 0x9d, 0x05, 0xdd, 0xb2,
- 0xbb, 0x9e, 0x1a, 0xd9, 0x3f, 0xc2, 0xc6, 0xaa, 0x1b, 0x8a, 0x78, 0x67, 0x6f, 0xf2, 0x4c, 0xf8,
- 0x92, 0x28, 0x24, 0x46, 0x39, 0xb5, 0xbf, 0x2f, 0x01, 0x8f, 0xc0, 0x21, 0xcf, 0x41, 0xad, 0x4d,
- 0x8d, 0x2d, 0xdd, 0xb1, 0x7c, 0x75, 0x63, 0x26, 0x5b, 0x9f, 0xd7, 0x56, 0x14, 0xf1, 0x0e, 0x53,
- 0xc1, 0x6c, 0x73, 0x99, 0x07, 0xb9, 0x47, 0xbc, 0xc4, 0x80, 0xa1, 0x96, 0xef, 0xeb, 0x1d, 0x2b,
- 0xf7, 0x06, 0xb0, 0xb8, 0xa1, 0x50, 0x0c, 0x03, 0xe2, 0x19, 0x25, 0x34, 0x31, 0xa0, 0xd2, 0xb1,
- 0x75, 0xcb, 0xc9, 0xfd, 0xa7, 0x18, 0x56, 0x83, 0x55, 0x86, 0x24, 0x9c, 0x52, 0xfc, 0x11, 0x05,
- 0x36, 0xe9, 0x42, 0xdd, 0x37, 0x3c, 0xbd, 0xed, 0x6f, 0xe9, 0x33, 0xcf, 0x3c, 0x9b, 0xdb, 0xcc,
- 0x8b, 0x44, 0x89, 0x59, 0x67, 0x0e, 0x67, 0x57, 0x9a, 0x57, 0x66, 0x67, 0x9e, 0x79, 0x16, 0xe3,
- 0x72, 0xe2, 0x62, 0x9f, 0x79, 0x6a, 0x46, 0xf6, 0xdc, 0x23, 0x17, 0xfb, 0xcc, 0x53, 0x33, 0x18,
- 0x97, 0xa3, 0xfd, 0x5d, 0x01, 0x6a, 0x21, 0x2f, 0x59, 0x07, 0x60, 0x63, 0x88, 0xbc, 0x53, 0xf0,
- 0x50, 0xf7, 0xfb, 0xf3, 0x75, 0xfd, 0x7a, 0x98, 0x19, 0x63, 0x40, 0x19, 0x97, 0x2e, 0x16, 0x8f,
- 0xfa, 0xd2, 0xc5, 0x69, 0xa8, 0x6d, 0xe9, 0x8e, 0xe9, 0x6f, 0xe9, 0xdb, 0x62, 0x28, 0x8d, 0x5d,
- 0x43, 0x7a, 0x45, 0x25, 0x60, 0xc4, 0xa3, 0xfd, 0xf6, 0x10, 0x88, 0x5d, 0x5b, 0xd6, 0xd9, 0x4d,
- 0xcb, 0x17, 0x61, 0xc3, 0x05, 0x9e, 0x33, 0xec, 0xec, 0xf3, 0x92, 0x8e, 0x21, 0x07, 0x39, 0x0b,
- 0xa5, 0xb6, 0xe5, 0xc8, 0x3d, 0x1b, 0xee, 0xb2, 0x5b, 0xb1, 0x1c, 0x64, 0x34, 0x9e, 0xa4, 0xdf,
- 0x96, 0x11, 0x5f, 0x22, 0x49, 0xbf, 0x8d, 0x8c, 0xc6, 0x16, 0xd1, 0xb6, 0xeb, 0x6e, 0xb3, 0x6e,
- 0xab, 0x02, 0xc3, 0xca, 0x7c, 0x2a, 0xe7, 0x8b, 0xe8, 0xe5, 0x64, 0x12, 0xa6, 0x79, 0xc9, 0x3a,
- 0x3c, 0xf2, 0x06, 0xf5, 0x5c, 0x39, 0x4e, 0x35, 0x6d, 0x4a, 0x3b, 0x0a, 0x46, 0x18, 0x41, 0x3c,
- 0xbe, 0xec, 0x8b, 0xd9, 0x2c, 0xd8, 0x2f, 0x2f, 0x8f, 0x54, 0xd5, 0xbd, 0x16, 0x0d, 0x56, 0x3d,
- 0xd7, 0xa0, 0xbe, 0x6f, 0x39, 0x2d, 0x05, 0x3b, 0x14, 0xc1, 0xae, 0x65, 0xb3, 0x60, 0xbf, 0xbc,
- 0xe4, 0x65, 0x98, 0x10, 0x49, 0xc2, 0x5c, 0x98, 0xdd, 0xd1, 0x2d, 0x5b, 0xdf, 0xb0, 0x6c, 0xf5,
- 0x83, 0xb5, 0x51, 0xb1, 0x33, 0xb2, 0xd6, 0x87, 0x07, 0xfb, 0xe6, 0x26, 0x57, 0x61, 0x5c, 0xed,
- 0x8b, 0xad, 0x52, 0xaf, 0x19, 0xee, 0xe4, 0x8f, 0x36, 0x2e, 0xb0, 0x15, 0xeb, 0x3c, 0xed, 0x78,
- 0xd4, 0xe0, 0x76, 0x63, 0x8a, 0x0b, 0x7b, 0xf2, 0x11, 0x84, 0x33, 0x7c, 0xbb, 0x7e, 0xbd, 0x33,
- 0xe7, 0xba, 0xb6, 0xe9, 0xde, 0x72, 0x54, 0xdd, 0x85, 0x69, 0xc6, 0xb7, 0xc2, 0x9a, 0x99, 0x1c,
- 0xd8, 0x27, 0x27, 0xab, 0x39, 0x4f, 0x99, 0x77, 0x6f, 0x39, 0x69, 0x54, 0x88, 0x6a, 0xde, 0xec,
- 0xc3, 0x83, 0x7d, 0x73, 0x93, 0x05, 0x20, 0xe9, 0x1a, 0xac, 0x77, 0xb8, 0x39, 0x37, 0xda, 0x38,
- 0x23, 0xae, 0x07, 0x49, 0xa7, 0x62, 0x46, 0x0e, 0xb2, 0x0c, 0xa7, 0xd3, 0x54, 0x26, 0x8e, 0x9f,
- 0x11, 0x18, 0x15, 0x17, 0x83, 0x62, 0x46, 0x3a, 0x66, 0xe6, 0xd2, 0x7e, 0xa7, 0x08, 0xa3, 0x89,
- 0xf3, 0xe4, 0x0f, 0xdc, 0xb9, 0x5d, 0x66, 0x43, 0xb7, 0xfd, 0xd6, 0xd2, 0xfc, 0x15, 0xaa, 0x9b,
- 0xd4, 0xbb, 0x46, 0xd5, 0xd9, 0x7f, 0x3e, 0xa8, 0xac, 0x24, 0x52, 0x30, 0xc5, 0x49, 0x36, 0xa1,
- 0x22, 0x3c, 0xc2, 0x79, 0xff, 0x14, 0xa1, 0x74, 0xc4, 0xdd, 0xc2, 0xf2, 0xf7, 0x2a, 0xae, 0x47,
- 0x51, 0xc0, 0x6b, 0x01, 0x8c, 0xc4, 0x39, 0xd8, 0x40, 0x12, 0x99, 0x9b, 0xc3, 0x09, 0x53, 0x73,
- 0x09, 0x4a, 0x41, 0x30, 0xe8, 0x89, 0x60, 0xb1, 0xc3, 0xb0, 0xb6, 0x8c, 0x0c, 0x43, 0xdb, 0x64,
- 0xdf, 0xce, 0xf7, 0x2d, 0xd7, 0x91, 0xd7, 0x43, 0xaf, 0xc3, 0x70, 0x20, 0x9d, 0x6c, 0x83, 0x9d,
- 0x68, 0xe6, 0x36, 0x8a, 0x72, 0xb0, 0x29, 0x2c, 0xed, 0x4f, 0x8b, 0x50, 0x0b, 0x17, 0xc4, 0x07,
- 0xb8, 0x76, 0xd9, 0x85, 0x5a, 0x18, 0x6e, 0x94, 0xfb, 0xe7, 0x73, 0x51, 0x14, 0x0c, 0x5f, 0xc3,
- 0x85, 0xaf, 0x18, 0xc9, 0x88, 0x87, 0x32, 0x95, 0x72, 0x84, 0x32, 0x75, 0x60, 0x38, 0xf0, 0xac,
- 0x56, 0x4b, 0x5a, 0xe7, 0x79, 0x62, 0x99, 0x42, 0x75, 0xad, 0x09, 0x40, 0xa9, 0x59, 0xf1, 0x82,
- 0x4a, 0x8c, 0xf6, 0x1a, 0x8c, 0xa7, 0x39, 0xb9, 0xe9, 0x6a, 0x6c, 0x51, 0xb3, 0x6b, 0x2b, 0x1d,
- 0x47, 0xa6, 0xab, 0xa4, 0x63, 0xc8, 0xc1, 0x96, 0xaf, 0xec, 0x33, 0xbd, 0xe1, 0x3a, 0xca, 0x7c,
- 0xe4, 0xab, 0x80, 0x35, 0x49, 0xc3, 0x30, 0x55, 0xfb, 0xab, 0x12, 0x9c, 0x8d, 0xdc, 0x1a, 0x2b,
- 0xba, 0xa3, 0xb7, 0x0e, 0xf0, 0xc7, 0xb1, 0x0f, 0xce, 0x88, 0x1c, 0xf6, 0xee, 0xfc, 0xd2, 0x03,
- 0x70, 0x77, 0xfe, 0x3f, 0x14, 0x81, 0x87, 0x46, 0x92, 0xaf, 0xc2, 0x88, 0x1e, 0xfb, 0xd9, 0xa4,
- 0xfc, 0x9c, 0x97, 0x73, 0x7f, 0x4e, 0x1e, 0x81, 0x19, 0x86, 0xfa, 0xc4, 0xa9, 0x98, 0x10, 0x48,
- 0x5c, 0xa8, 0x6e, 0xea, 0xb6, 0xcd, 0x6c, 0xa1, 0xdc, 0xdb, 0x34, 0x09, 0xe1, 0xbc, 0x99, 0x2f,
- 0x48, 0x68, 0x0c, 0x85, 0x90, 0x37, 0x0b, 0x30, 0xea, 0xc5, 0x97, 0x49, 0xf2, 0x83, 0xe4, 0xd9,
- 0xc4, 0x8f, 0xa1, 0xc5, 0x03, 0x8b, 0xe2, 0x6b, 0xb1, 0xa4, 0x4c, 0xed, 0x2f, 0x0b, 0x30, 0xda,
- 0xb4, 0x2d, 0xd3, 0x72, 0x5a, 0xc7, 0x78, 0x75, 0xff, 0x0d, 0xa8, 0xf8, 0xb6, 0x65, 0xd2, 0x01,
- 0x67, 0x13, 0x31, 0x8f, 0x31, 0x00, 0x14, 0x38, 0xc9, 0x7f, 0x01, 0x94, 0x0e, 0xf0, 0x2f, 0x80,
- 0x5f, 0x0d, 0x81, 0x0c, 0xf2, 0x25, 0x5d, 0xa8, 0xb5, 0xd4, 0x15, 0xe3, 0xb2, 0x8e, 0x57, 0x72,
- 0x5c, 0x4f, 0x97, 0xb8, 0xac, 0x5c, 0x8c, 0xfd, 0x21, 0x11, 0x23, 0x49, 0x84, 0x26, 0xff, 0x72,
- 0x3a, 0x9f, 0xf3, 0x2f, 0xa7, 0x42, 0x5c, 0xef, 0x7f, 0x4e, 0x75, 0x28, 0x6f, 0x05, 0x41, 0x47,
- 0x36, 0xa6, 0xc1, 0xa3, 0xb8, 0xa3, 0x1b, 0x52, 0x84, 0x4d, 0xc4, 0xde, 0x91, 0x43, 0x33, 0x11,
- 0x8e, 0x1e, 0xfe, 0xd5, 0x6a, 0x2e, 0x57, 0xc0, 0x40, 0x5c, 0x04, 0x7b, 0x47, 0x0e, 0x4d, 0xbe,
- 0x02, 0xf5, 0xc0, 0xd3, 0x1d, 0x7f, 0xd3, 0xf5, 0xda, 0xd4, 0x93, 0x6b, 0xd4, 0x85, 0x1c, 0x3f,
- 0xfa, 0x5c, 0x8b, 0xd0, 0xc4, 0x4e, 0x64, 0x82, 0x84, 0x71, 0x69, 0x64, 0x1b, 0xaa, 0x5d, 0x53,
- 0x14, 0x4c, 0xba, 0x9f, 0x66, 0xf3, 0xfc, 0xbb, 0x35, 0x16, 0x0e, 0xa0, 0xde, 0x30, 0x14, 0x90,
- 0xfc, 0x81, 0xdb, 0xf0, 0x51, 0xfd, 0xc0, 0x2d, 0xde, 0x1a, 0xb3, 0xae, 0x6f, 0x20, 0x6d, 0x69,
- 0xd7, 0x3a, 0x2d, 0x19, 0xcd, 0xb4, 0x90, 0xdb, 0xe4, 0x14, 0x22, 0xeb, 0xa1, 0x6d, 0xec, 0xb4,
- 0x50, 0xc9, 0xd0, 0xda, 0x20, 0x77, 0x09, 0x88, 0x91, 0xf8, 0xcd, 0x89, 0x38, 0x53, 0x34, 0x7d,
- 0xb0, 0xf1, 0x20, 0xfc, 0xdf, 0x46, 0xec, 0x9a, 0xe5, 0xcc, 0xff, 0x99, 0x68, 0x7f, 0x56, 0x84,
- 0xd2, 0xda, 0x72, 0x53, 0x5c, 0x9d, 0xc8, 0xff, 0x21, 0x44, 0x9b, 0xdb, 0x56, 0xe7, 0x26, 0xf5,
- 0xac, 0xcd, 0x5d, 0xb9, 0xf4, 0x8e, 0x5d, 0x9d, 0x98, 0xe6, 0xc0, 0x8c, 0x5c, 0xe4, 0x15, 0x18,
- 0x31, 0xf4, 0x39, 0xea, 0x05, 0x83, 0x38, 0x16, 0xf8, 0xe1, 0xc9, 0xb9, 0xd9, 0x28, 0x3b, 0x26,
- 0xc0, 0xc8, 0x3a, 0x80, 0x11, 0x41, 0x97, 0x0e, 0xed, 0x0e, 0x89, 0x01, 0xc7, 0x80, 0x08, 0x42,
- 0x6d, 0x9b, 0xb1, 0x72, 0xd4, 0xf2, 0x61, 0x50, 0x79, 0xcb, 0xb9, 0xa6, 0xf2, 0x62, 0x04, 0xa3,
- 0x39, 0x30, 0x9a, 0xf8, 0xf7, 0x09, 0xf9, 0x24, 0x54, 0xdd, 0x4e, 0x6c, 0x38, 0xad, 0xf1, 0xb8,
- 0xc9, 0xea, 0x0d, 0x49, 0xbb, 0xb3, 0x37, 0x39, 0xba, 0xec, 0xb6, 0x2c, 0x43, 0x11, 0x30, 0x64,
- 0x27, 0x1a, 0x0c, 0xf1, 0x13, 0x4f, 0xea, 0xcf, 0x27, 0x7c, 0xee, 0xe0, 0x3f, 0x27, 0xf0, 0x51,
- 0xa6, 0x68, 0x5f, 0x2b, 0x43, 0xb4, 0xb7, 0x46, 0x7c, 0x18, 0x12, 0x11, 0xdd, 0x72, 0xe4, 0x3e,
- 0xd6, 0xe0, 0x71, 0x29, 0x8a, 0xb4, 0xa0, 0xf4, 0x9a, 0xbb, 0x91, 0x7b, 0xe0, 0x8e, 0x1d, 0x75,
- 0x16, 0xbe, 0xb2, 0x18, 0x01, 0x99, 0x04, 0xf2, 0xdf, 0x0b, 0x70, 0xd2, 0x4f, 0x9b, 0xbe, 0xb2,
- 0x39, 0x60, 0x7e, 0x1b, 0x3f, 0x6d, 0x4c, 0xcb, 0x00, 0xd7, 0x7e, 0xc9, 0xd8, 0x5b, 0x16, 0xa6,
- 0x7f, 0xb1, 0xe9, 0x25, 0x9b, 0xd3, 0x62, 0xce, 0xff, 0xf5, 0x25, 0xf5, 0x9f, 0xa4, 0xa1, 0x14,
- 0xa5, 0x7d, 0xa3, 0x08, 0xf5, 0xd8, 0x68, 0x9d, 0xfb, 0x87, 0x3a, 0xb7, 0x53, 0x3f, 0xd4, 0x59,
- 0x1d, 0x7c, 0x0f, 0x38, 0x2a, 0xd5, 0x71, 0xff, 0x53, 0xe7, 0x47, 0x45, 0x28, 0xad, 0xcf, 0x2f,
- 0x24, 0x17, 0xad, 0x85, 0xfb, 0xb0, 0x68, 0xdd, 0x82, 0xe1, 0x8d, 0xae, 0x65, 0x07, 0x96, 0x93,
- 0xfb, 0x32, 0x06, 0xf5, 0xff, 0x21, 0xb9, 0xc7, 0x20, 0x50, 0x51, 0xc1, 0x93, 0x16, 0x0c, 0xb7,
- 0xc4, 0x6d, 0x78, 0xb9, 0x23, 0xe3, 0xe4, 0xad, 0x7a, 0x42, 0x90, 0x7c, 0x41, 0x85, 0xae, 0xed,
- 0x82, 0xfc, 0x91, 0xfa, 0x7d, 0xd7, 0xa6, 0xf6, 0x15, 0x08, 0xad, 0x80, 0xfb, 0x2f, 0xfc, 0x6f,
- 0x0a, 0x90, 0x34, 0x7c, 0xee, 0x7f, 0x6b, 0xda, 0x4e, 0xb7, 0xa6, 0xf9, 0xa3, 0xe8, 0x7c, 0xd9,
- 0x0d, 0x4a, 0xfb, 0xad, 0x22, 0x0c, 0xdd, 0xb7, 0x03, 0xb4, 0x34, 0x11, 0xe4, 0x37, 0x97, 0x73,
- 0x60, 0xec, 0x1b, 0xe2, 0xd7, 0x4e, 0x85, 0xf8, 0xe5, 0xfd, 0x63, 0xea, 0x3d, 0x02, 0xfc, 0xfe,
- 0xa8, 0x00, 0x72, 0x58, 0x5e, 0x72, 0xfc, 0x40, 0x77, 0x0c, 0x4a, 0x8c, 0x70, 0x0e, 0xc8, 0x1b,
- 0x49, 0x22, 0xa3, 0xad, 0xc4, 0xb4, 0xcf, 0x9f, 0xd5, 0x98, 0x4f, 0x3e, 0x0e, 0xd5, 0x2d, 0xd7,
- 0x0f, 0xf8, 0x38, 0x5f, 0x4c, 0x7a, 0x97, 0xae, 0x48, 0x3a, 0x86, 0x1c, 0xe9, 0x1d, 0xd7, 0x4a,
- 0xff, 0x1d, 0x57, 0xed, 0xbb, 0x45, 0x18, 0x79, 0xbf, 0x9c, 0x02, 0xce, 0x0a, 0x89, 0x2c, 0xe5,
- 0x0c, 0x89, 0x2c, 0x1f, 0x26, 0x24, 0x52, 0xfb, 0x71, 0x01, 0xe0, 0xbe, 0x1d, 0x41, 0x36, 0x93,
- 0xd1, 0x8a, 0xb9, 0xdb, 0x55, 0x76, 0xac, 0xe2, 0x6f, 0x54, 0x54, 0x95, 0x78, 0xa4, 0xe2, 0x5b,
- 0x05, 0x18, 0xd3, 0x13, 0xd1, 0x7f, 0xb9, 0x4d, 0xcb, 0x54, 0x30, 0x61, 0x78, 0xdc, 0x32, 0x49,
- 0xc7, 0x94, 0x58, 0xf2, 0x7c, 0x74, 0xfd, 0xed, 0xf5, 0xa8, 0xd9, 0xf7, 0xdc, 0x5b, 0xcb, 0xcd,
- 0x9c, 0x04, 0xe7, 0x3d, 0xa2, 0x2d, 0x4b, 0x47, 0x12, 0x6d, 0x19, 0x3f, 0x47, 0x56, 0xbe, 0xeb,
- 0x39, 0xb2, 0x1d, 0xa8, 0x6d, 0x7a, 0x6e, 0x9b, 0x07, 0x34, 0xca, 0x7f, 0xad, 0x5e, 0xce, 0x31,
- 0xa7, 0x44, 0x7f, 0x19, 0x8f, 0x7c, 0x3c, 0x0b, 0x0a, 0x1f, 0x23, 0x51, 0xdc, 0x2d, 0xee, 0x0a,
- 0xa9, 0x43, 0x47, 0x29, 0x35, 0x1c, 0x4b, 0xd6, 0x04, 0x3a, 0x2a, 0x31, 0xc9, 0x20, 0xc6, 0xe1,
- 0xfb, 0x13, 0xc4, 0xa8, 0xfd, 0xa8, 0xac, 0x06, 0xb0, 0x07, 0xee, 0xa6, 0xc5, 0xf7, 0xff, 0xd1,
- 0xd5, 0xf4, 0xb9, 0xd2, 0xe1, 0xfb, 0x78, 0xae, 0xb4, 0x7a, 0x34, 0xe7, 0x4a, 0x6b, 0x87, 0x38,
- 0x57, 0xba, 0x57, 0x82, 0xd4, 0xa2, 0xeb, 0x83, 0x0d, 0x96, 0x7f, 0x52, 0x1b, 0x2c, 0x6f, 0x17,
- 0x21, 0x1a, 0x45, 0x0e, 0x19, 0x80, 0xf2, 0x32, 0x54, 0xdb, 0xfa, 0xed, 0x79, 0x6a, 0xeb, 0xbb,
- 0x79, 0xfe, 0xae, 0xb9, 0x22, 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80, 0x15, 0xde, 0x70, 0x9d, 0xdb,
- 0x55, 0x1d, 0x5d, 0x96, 0x2d, 0x9c, 0x61, 0xd1, 0x3b, 0xc6, 0xc4, 0x68, 0x7f, 0x58, 0x04, 0x79,
- 0x15, 0x3a, 0xa1, 0x50, 0xd9, 0xb4, 0x6e, 0x53, 0x33, 0x77, 0x38, 0x69, 0xec, 0x9f, 0xc7, 0xc2,
- 0x17, 0xcf, 0x09, 0x28, 0xd0, 0xb9, 0x93, 0x55, 0xec, 0xad, 0x48, 0xfd, 0xe5, 0x70, 0xb2, 0xc6,
- 0xf7, 0x68, 0xa4, 0x93, 0x55, 0x90, 0x50, 0xc9, 0x10, 0x3e, 0x5d, 0xbe, 0xcd, 0x9e, 0x7b, 0x2b,
- 0x29, 0xb1, 0x5d, 0xaf, 0x7c, 0xba, 0xbe, 0x38, 0x58, 0x2e, 0x65, 0x34, 0xbe, 0xf4, 0xc3, 0x9f,
- 0x5c, 0x78, 0xe8, 0xc7, 0x3f, 0xb9, 0xf0, 0xd0, 0xbb, 0x3f, 0xb9, 0xf0, 0xd0, 0xd7, 0xf6, 0x2f,
- 0x14, 0x7e, 0xb8, 0x7f, 0xa1, 0xf0, 0xe3, 0xfd, 0x0b, 0x85, 0x77, 0xf7, 0x2f, 0x14, 0xfe, 0x7c,
- 0xff, 0x42, 0xe1, 0xbf, 0xfc, 0xc5, 0x85, 0x87, 0xbe, 0xf8, 0x5c, 0x54, 0x84, 0x69, 0x55, 0x84,
- 0x69, 0x25, 0x70, 0xba, 0xb3, 0xdd, 0x9a, 0x66, 0x45, 0x88, 0x28, 0xaa, 0x08, 0xff, 0x18, 0x00,
- 0x00, 0xff, 0xff, 0xa1, 0xe2, 0x38, 0xfd, 0x6e, 0x95, 0x00, 0x00,
+ 0x75, 0xa6, 0xfa, 0xbf, 0xfb, 0x34, 0xc9, 0xe1, 0xdc, 0x19, 0x8d, 0x38, 0xa3, 0xd1, 0x70, 0x5c,
+ 0xb2, 0xe4, 0xf1, 0xda, 0x26, 0x57, 0x5c, 0xfd, 0xf9, 0x57, 0x62, 0x93, 0x43, 0x0e, 0x67, 0xc8,
+ 0x19, 0xfa, 0x34, 0x39, 0x92, 0xad, 0xb5, 0xb5, 0xc5, 0xea, 0xcb, 0x66, 0x89, 0xd5, 0x55, 0xad,
+ 0xaa, 0x6a, 0xce, 0x50, 0xde, 0x85, 0x7f, 0xb4, 0x80, 0xb4, 0x58, 0x2c, 0x76, 0xe1, 0x27, 0x03,
+ 0x0b, 0xef, 0x62, 0x17, 0xbb, 0xf0, 0x83, 0xe1, 0x7d, 0x58, 0x40, 0xfb, 0x60, 0x60, 0xe3, 0x38,
+ 0x08, 0x12, 0x27, 0xc8, 0x8f, 0x1f, 0x02, 0x44, 0x79, 0x21, 0x62, 0x06, 0x79, 0x48, 0x80, 0x18,
+ 0x46, 0x0c, 0x24, 0xf6, 0xc0, 0x88, 0x83, 0xfb, 0x57, 0x7f, 0x5d, 0x3d, 0x43, 0x76, 0x91, 0xa3,
+ 0x51, 0xa2, 0xb7, 0xaa, 0x7b, 0xcf, 0xfd, 0xce, 0xad, 0x53, 0xf7, 0xe7, 0xdc, 0x73, 0xce, 0xbd,
+ 0x17, 0x16, 0xdb, 0xa6, 0xbf, 0xd5, 0xdb, 0x98, 0x32, 0x9c, 0xce, 0xb4, 0xdd, 0xeb, 0xe8, 0x5d,
+ 0xd7, 0x79, 0x8d, 0x3f, 0x6c, 0x5a, 0xce, 0xad, 0xe9, 0xee, 0x76, 0x7b, 0x5a, 0xef, 0x9a, 0x5e,
+ 0x98, 0xb2, 0xf3, 0x94, 0x6e, 0x75, 0xb7, 0xf4, 0xa7, 0xa6, 0xdb, 0xd4, 0xa6, 0xae, 0xee, 0xd3,
+ 0xd6, 0x54, 0xd7, 0x75, 0x7c, 0x87, 0x3c, 0x17, 0x02, 0x4d, 0x29, 0xa0, 0x29, 0x55, 0x6c, 0xaa,
+ 0xbb, 0xdd, 0x9e, 0x62, 0x40, 0x61, 0x8a, 0x02, 0x3a, 0xf7, 0x89, 0x48, 0x0d, 0xda, 0x4e, 0xdb,
+ 0x99, 0xe6, 0x78, 0x1b, 0xbd, 0x4d, 0xfe, 0xc6, 0x5f, 0xf8, 0x93, 0xe0, 0x73, 0x4e, 0xdb, 0x7e,
+ 0xde, 0x9b, 0x32, 0x1d, 0x56, 0xad, 0x69, 0xc3, 0x71, 0xe9, 0xf4, 0x4e, 0x5f, 0x5d, 0xce, 0x3d,
+ 0x1d, 0xd2, 0x74, 0x74, 0x63, 0xcb, 0xb4, 0xa9, 0xbb, 0xab, 0xbe, 0x65, 0xda, 0xa5, 0x9e, 0xd3,
+ 0x73, 0x0d, 0x7a, 0xa8, 0x52, 0xde, 0x74, 0x87, 0xfa, 0x7a, 0x1a, 0xaf, 0xe9, 0x41, 0xa5, 0xdc,
+ 0x9e, 0xed, 0x9b, 0x9d, 0x7e, 0x36, 0xcf, 0xde, 0xab, 0x80, 0x67, 0x6c, 0xd1, 0x8e, 0x9e, 0x2c,
+ 0xa7, 0xfd, 0x10, 0xe0, 0xd4, 0xec, 0x86, 0xe7, 0xbb, 0xba, 0xe1, 0xaf, 0x3a, 0xad, 0x35, 0xda,
+ 0xe9, 0x5a, 0xba, 0x4f, 0xc9, 0x36, 0x54, 0x59, 0xdd, 0x5a, 0xba, 0xaf, 0x4f, 0xe4, 0x2e, 0xe6,
+ 0x2e, 0xd5, 0x67, 0x66, 0xa7, 0x86, 0xfc, 0x17, 0x53, 0x2b, 0x12, 0xa8, 0x31, 0xb2, 0xbf, 0x37,
+ 0x59, 0x55, 0x6f, 0x18, 0x30, 0x20, 0xdf, 0xca, 0xc1, 0x88, 0xed, 0xb4, 0x68, 0x93, 0x5a, 0xd4,
+ 0xf0, 0x1d, 0x77, 0x22, 0x7f, 0xb1, 0x70, 0xa9, 0x3e, 0xf3, 0xe5, 0xa1, 0x39, 0xa6, 0x7c, 0xd1,
+ 0xd4, 0xf5, 0x08, 0x83, 0xcb, 0xb6, 0xef, 0xee, 0x36, 0x4e, 0xff, 0x68, 0x6f, 0xf2, 0xa1, 0xfd,
+ 0xbd, 0xc9, 0x91, 0x68, 0x16, 0xc6, 0x6a, 0x42, 0xd6, 0xa1, 0xee, 0x3b, 0x16, 0x13, 0x99, 0xe9,
+ 0xd8, 0xde, 0x44, 0x81, 0x57, 0xec, 0xc2, 0x94, 0x90, 0x36, 0x63, 0x3f, 0xc5, 0x9a, 0xcb, 0xd4,
+ 0xce, 0x53, 0x53, 0x6b, 0x01, 0x59, 0xe3, 0x94, 0x04, 0xae, 0x87, 0x69, 0x1e, 0x46, 0x71, 0x08,
+ 0x85, 0x13, 0x1e, 0x35, 0x7a, 0xae, 0xe9, 0xef, 0xce, 0x39, 0xb6, 0x4f, 0x6f, 0xfb, 0x13, 0x45,
+ 0x2e, 0xe5, 0x27, 0xd3, 0xa0, 0x57, 0x9d, 0x56, 0x33, 0x4e, 0xdd, 0x38, 0xb5, 0xbf, 0x37, 0x79,
+ 0x22, 0x91, 0x88, 0x49, 0x4c, 0x62, 0xc3, 0xb8, 0xd9, 0xd1, 0xdb, 0x74, 0xb5, 0x67, 0x59, 0x4d,
+ 0x6a, 0xb8, 0xd4, 0xf7, 0x26, 0x4a, 0xfc, 0x13, 0x2e, 0xa5, 0xf1, 0x59, 0x76, 0x0c, 0xdd, 0xba,
+ 0xb1, 0xf1, 0x1a, 0x35, 0x7c, 0xa4, 0x9b, 0xd4, 0xa5, 0xb6, 0x41, 0x1b, 0x13, 0xf2, 0x63, 0xc6,
+ 0x97, 0x12, 0x48, 0xd8, 0x87, 0x4d, 0x16, 0xe1, 0x64, 0xd7, 0x35, 0x1d, 0x5e, 0x05, 0x4b, 0xf7,
+ 0xbc, 0xeb, 0x7a, 0x87, 0x4e, 0x94, 0x2f, 0xe6, 0x2e, 0xd5, 0x1a, 0x67, 0x25, 0xcc, 0xc9, 0xd5,
+ 0x24, 0x01, 0xf6, 0x97, 0x21, 0x97, 0xa0, 0xaa, 0x12, 0x27, 0x2a, 0x17, 0x73, 0x97, 0x4a, 0xa2,
+ 0xed, 0xa8, 0xb2, 0x18, 0xe4, 0x92, 0x05, 0xa8, 0xea, 0x9b, 0x9b, 0xa6, 0xcd, 0x28, 0xab, 0x5c,
+ 0x84, 0xe7, 0xd3, 0x3e, 0x6d, 0x56, 0xd2, 0x08, 0x1c, 0xf5, 0x86, 0x41, 0x59, 0x72, 0x15, 0x88,
+ 0x47, 0xdd, 0x1d, 0xd3, 0xa0, 0xb3, 0x86, 0xe1, 0xf4, 0x6c, 0x9f, 0xd7, 0xbd, 0xc6, 0xeb, 0x7e,
+ 0x4e, 0xd6, 0x9d, 0x34, 0xfb, 0x28, 0x30, 0xa5, 0x14, 0x79, 0x11, 0xc6, 0x65, 0xb7, 0x0b, 0xa5,
+ 0x00, 0x1c, 0xe9, 0x34, 0x13, 0x24, 0x26, 0xf2, 0xb0, 0x8f, 0x9a, 0xb4, 0xe0, 0xbc, 0xde, 0xf3,
+ 0x9d, 0x0e, 0x83, 0x8c, 0x33, 0x5d, 0x73, 0xb6, 0xa9, 0x3d, 0x51, 0xbf, 0x98, 0xbb, 0x54, 0x6d,
+ 0x5c, 0xdc, 0xdf, 0x9b, 0x3c, 0x3f, 0x7b, 0x17, 0x3a, 0xbc, 0x2b, 0x0a, 0xb9, 0x01, 0xb5, 0x96,
+ 0xed, 0xad, 0x3a, 0x96, 0x69, 0xec, 0x4e, 0x8c, 0xf0, 0x0a, 0x3e, 0x25, 0x3f, 0xb5, 0x36, 0x7f,
+ 0xbd, 0x29, 0x32, 0xee, 0xec, 0x4d, 0x9e, 0xef, 0x1f, 0x1d, 0xa7, 0x82, 0x7c, 0x0c, 0x31, 0xc8,
+ 0x0a, 0x07, 0x9c, 0x73, 0xec, 0x4d, 0xb3, 0x3d, 0x31, 0xca, 0xff, 0xc6, 0xc5, 0x01, 0x0d, 0x7a,
+ 0xfe, 0x7a, 0x53, 0xd0, 0x35, 0x46, 0x25, 0x3b, 0xf1, 0x8a, 0x21, 0x02, 0x69, 0xc1, 0x98, 0x1a,
+ 0x57, 0xe7, 0x2c, 0xdd, 0xec, 0x78, 0x13, 0x63, 0xbc, 0xf1, 0x7e, 0x78, 0x00, 0x26, 0x46, 0x89,
+ 0x1b, 0x67, 0xe4, 0xa7, 0x8c, 0xc5, 0x92, 0x3d, 0x4c, 0x60, 0x9e, 0x7b, 0x01, 0x4e, 0xf6, 0x8d,
+ 0x0d, 0x64, 0x1c, 0x0a, 0xdb, 0x74, 0x97, 0x0f, 0x7d, 0x35, 0x64, 0x8f, 0xe4, 0x34, 0x94, 0x76,
+ 0x74, 0xab, 0x47, 0x27, 0xf2, 0x3c, 0x4d, 0xbc, 0x7c, 0x2a, 0xff, 0x7c, 0x4e, 0xfb, 0x9f, 0x05,
+ 0x18, 0x51, 0x23, 0x4e, 0xd3, 0xb4, 0xb7, 0xc9, 0x4b, 0x50, 0xb0, 0x9c, 0xb6, 0x1c, 0x37, 0x3f,
+ 0x33, 0xf4, 0x28, 0xb6, 0xec, 0xb4, 0x1b, 0x95, 0xfd, 0xbd, 0xc9, 0xc2, 0xb2, 0xd3, 0x46, 0x86,
+ 0x48, 0x0c, 0x28, 0x6d, 0xeb, 0x9b, 0xdb, 0x3a, 0xaf, 0x43, 0x7d, 0xa6, 0x31, 0x34, 0xf4, 0x35,
+ 0x86, 0xc2, 0xea, 0xda, 0xa8, 0xed, 0xef, 0x4d, 0x96, 0xf8, 0x2b, 0x0a, 0x6c, 0xe2, 0x40, 0x6d,
+ 0xc3, 0xd2, 0x8d, 0xed, 0x2d, 0xc7, 0xa2, 0x13, 0x85, 0x8c, 0x8c, 0x1a, 0x0a, 0x49, 0xfc, 0xe6,
+ 0xe0, 0x15, 0x43, 0x1e, 0xc4, 0x80, 0x72, 0xaf, 0xe5, 0x99, 0xf6, 0xb6, 0x1c, 0x03, 0x5f, 0x18,
+ 0x9a, 0xdb, 0xfa, 0x3c, 0xff, 0x26, 0xd8, 0xdf, 0x9b, 0x2c, 0x8b, 0x67, 0x94, 0xd0, 0xda, 0x4f,
+ 0xeb, 0x30, 0xa6, 0x7e, 0xd2, 0x4d, 0xea, 0xfa, 0xf4, 0x36, 0xb9, 0x08, 0x45, 0x9b, 0x75, 0x4d,
+ 0xfe, 0x93, 0x1b, 0x23, 0xb2, 0xb9, 0x14, 0x79, 0x97, 0xe4, 0x39, 0xac, 0x66, 0xa2, 0xa9, 0x48,
+ 0x81, 0x0f, 0x5f, 0xb3, 0x26, 0x87, 0x11, 0x35, 0x13, 0xcf, 0x28, 0xa1, 0xc9, 0x2b, 0x50, 0xe4,
+ 0x1f, 0x2f, 0x44, 0xfd, 0xd9, 0xe1, 0x59, 0xb0, 0x4f, 0xaf, 0xb2, 0x2f, 0xe0, 0x1f, 0xce, 0x41,
+ 0x59, 0x53, 0xec, 0xb5, 0x36, 0xa5, 0x60, 0x3f, 0x93, 0x41, 0xb0, 0x0b, 0xa2, 0x29, 0xae, 0xcf,
+ 0x2f, 0x20, 0x43, 0x24, 0xff, 0x39, 0x07, 0x27, 0x0d, 0xc7, 0xf6, 0x75, 0xa6, 0x6a, 0xa8, 0x49,
+ 0x76, 0xa2, 0xc4, 0xf9, 0x5c, 0x1d, 0x9a, 0xcf, 0x5c, 0x12, 0xb1, 0xf1, 0x30, 0x9b, 0x33, 0xfa,
+ 0x92, 0xb1, 0x9f, 0x37, 0xf9, 0xaf, 0x39, 0x78, 0x98, 0x8d, 0xe5, 0x7d, 0xc4, 0x7c, 0x06, 0x3a,
+ 0xda, 0x5a, 0x9d, 0xdd, 0xdf, 0x9b, 0x7c, 0x78, 0x29, 0x8d, 0x19, 0xa6, 0xd7, 0x81, 0xd5, 0xee,
+ 0x94, 0xde, 0xaf, 0x96, 0xf0, 0xd9, 0xad, 0x3e, 0xb3, 0x7c, 0x94, 0xaa, 0x4e, 0xe3, 0x51, 0xd9,
+ 0x94, 0xd3, 0x34, 0x3b, 0x4c, 0xab, 0x05, 0xb9, 0x0c, 0x95, 0x1d, 0xc7, 0xea, 0x75, 0xa8, 0x37,
+ 0x51, 0xe5, 0x43, 0xec, 0xb9, 0xb4, 0x21, 0xf6, 0x26, 0x27, 0x69, 0x9c, 0x90, 0xf0, 0x15, 0xf1,
+ 0xee, 0xa1, 0x2a, 0x4b, 0x4c, 0x28, 0x5b, 0x66, 0xc7, 0xf4, 0x3d, 0x3e, 0x71, 0xd6, 0x67, 0x2e,
+ 0x0f, 0xfd, 0x59, 0xa2, 0x8b, 0x2e, 0x73, 0x30, 0xd1, 0x6b, 0xc4, 0x33, 0x4a, 0x06, 0x6c, 0x28,
+ 0xf4, 0x0c, 0xdd, 0x12, 0x13, 0x6b, 0x7d, 0xe6, 0x73, 0xc3, 0x77, 0x1b, 0x86, 0xd2, 0x18, 0x95,
+ 0xdf, 0x54, 0xe2, 0xaf, 0x28, 0xb0, 0xc9, 0x97, 0x60, 0x2c, 0xf6, 0x37, 0xbd, 0x89, 0x3a, 0x97,
+ 0xce, 0x63, 0x69, 0xd2, 0x09, 0xa8, 0xc2, 0x99, 0x27, 0xd6, 0x42, 0x3c, 0x4c, 0x80, 0x91, 0x6b,
+ 0x50, 0xf5, 0xcc, 0x16, 0x35, 0x74, 0xd7, 0x9b, 0x18, 0x39, 0x08, 0xf0, 0xb8, 0x04, 0xae, 0x36,
+ 0x65, 0x31, 0x0c, 0x00, 0xc8, 0x14, 0x40, 0x57, 0x77, 0x7d, 0x53, 0x28, 0xaa, 0xa3, 0x5c, 0x69,
+ 0x1a, 0xdb, 0xdf, 0x9b, 0x84, 0xd5, 0x20, 0x15, 0x23, 0x14, 0x8c, 0x9e, 0x95, 0x5d, 0xb2, 0xbb,
+ 0x3d, 0x5f, 0x4c, 0xac, 0x35, 0x41, 0xdf, 0x0c, 0x52, 0x31, 0x42, 0x41, 0xbe, 0x97, 0x83, 0x47,
+ 0xc3, 0xd7, 0xfe, 0x4e, 0x76, 0xe2, 0xc8, 0x3b, 0xd9, 0xe4, 0xfe, 0xde, 0xe4, 0xa3, 0xcd, 0xc1,
+ 0x2c, 0xf1, 0x6e, 0xf5, 0xd1, 0x5e, 0x82, 0xd1, 0xd9, 0x9e, 0xbf, 0xe5, 0xb8, 0xe6, 0x1b, 0x5c,
+ 0xe9, 0x26, 0x0b, 0x50, 0xf2, 0xb9, 0xf2, 0x24, 0xe6, 0xe5, 0x27, 0xd2, 0x44, 0x2d, 0x14, 0xd9,
+ 0x6b, 0x74, 0x57, 0x69, 0x03, 0x62, 0x7e, 0x14, 0xca, 0x94, 0x28, 0xae, 0xfd, 0xfb, 0x1c, 0x54,
+ 0x1a, 0xba, 0xb1, 0xed, 0x6c, 0x6e, 0x92, 0x97, 0xa1, 0x6a, 0xda, 0x3e, 0x75, 0x77, 0x74, 0x4b,
+ 0xc2, 0x4e, 0x45, 0x60, 0x83, 0x95, 0x58, 0xf8, 0xdd, 0x6c, 0xcd, 0xc3, 0x18, 0xcd, 0xf7, 0xe4,
+ 0x5a, 0x81, 0xeb, 0xa3, 0x4b, 0x12, 0x03, 0x03, 0x34, 0x32, 0x09, 0x25, 0xcf, 0xa7, 0x5d, 0x8f,
+ 0xcf, 0x3c, 0xa3, 0xa2, 0x1a, 0x4d, 0x96, 0x80, 0x22, 0x5d, 0xfb, 0x1f, 0x39, 0xa8, 0x35, 0x74,
+ 0xcf, 0x34, 0xd8, 0x57, 0x92, 0x39, 0x28, 0xf6, 0x3c, 0xea, 0x1e, 0xee, 0xdb, 0xf8, 0x64, 0xb1,
+ 0xee, 0x51, 0x17, 0x79, 0x61, 0x72, 0x03, 0xaa, 0x5d, 0xdd, 0xf3, 0x6e, 0x39, 0x6e, 0x4b, 0x4e,
+ 0x78, 0x07, 0x04, 0x12, 0xca, 0xb9, 0x2c, 0x8a, 0x01, 0x88, 0x56, 0x87, 0x70, 0xc6, 0xd7, 0x7e,
+ 0x9e, 0x83, 0x53, 0x8d, 0xde, 0xe6, 0x26, 0x75, 0xa5, 0x2e, 0x2a, 0xb5, 0x3c, 0x0a, 0x25, 0x97,
+ 0xb6, 0x4c, 0x4f, 0xd6, 0x7d, 0x7e, 0xe8, 0x16, 0x84, 0x0c, 0x45, 0x2a, 0x95, 0x5c, 0x5e, 0x3c,
+ 0x01, 0x05, 0x3a, 0xe9, 0x41, 0xed, 0x35, 0xea, 0x7b, 0xbe, 0x4b, 0xf5, 0x8e, 0xfc, 0xba, 0x2b,
+ 0x43, 0xb3, 0xba, 0x4a, 0xfd, 0x26, 0x47, 0x8a, 0xea, 0xb0, 0x41, 0x22, 0x86, 0x9c, 0xb4, 0x1f,
+ 0x96, 0x60, 0x64, 0xce, 0xe9, 0x6c, 0x98, 0x36, 0x6d, 0x5d, 0x6e, 0xb5, 0x29, 0x79, 0x15, 0x8a,
+ 0xb4, 0xd5, 0xa6, 0xf2, 0x6b, 0x87, 0x9f, 0xee, 0x19, 0x58, 0xa8, 0xb4, 0xb0, 0x37, 0xe4, 0xc0,
+ 0x64, 0x19, 0xc6, 0x36, 0x5d, 0xa7, 0x23, 0x46, 0xd0, 0xb5, 0xdd, 0xae, 0xd4, 0x58, 0x1b, 0x1f,
+ 0x56, 0xa3, 0xd2, 0x42, 0x2c, 0xf7, 0xce, 0xde, 0x24, 0x84, 0x6f, 0x98, 0x28, 0x4b, 0x5e, 0x86,
+ 0x89, 0x30, 0x25, 0x18, 0x4a, 0xe6, 0xd8, 0x22, 0x82, 0x6b, 0x2c, 0xa5, 0xc6, 0xf9, 0xfd, 0xbd,
+ 0xc9, 0x89, 0x85, 0x01, 0x34, 0x38, 0xb0, 0x34, 0x79, 0x2b, 0x07, 0xe3, 0x61, 0xa6, 0x18, 0xde,
+ 0xa5, 0xa2, 0x72, 0x44, 0xf3, 0x06, 0x5f, 0x6d, 0x2d, 0x24, 0x58, 0x60, 0x1f, 0x53, 0xb2, 0x00,
+ 0x23, 0xbe, 0x13, 0x91, 0x57, 0x89, 0xcb, 0x4b, 0x53, 0xe6, 0x81, 0x35, 0x67, 0xa0, 0xb4, 0x62,
+ 0xe5, 0x08, 0xc2, 0x19, 0xf5, 0x9e, 0x90, 0x54, 0x99, 0x4b, 0xea, 0xdc, 0xfe, 0xde, 0xe4, 0x99,
+ 0xb5, 0x54, 0x0a, 0x1c, 0x50, 0x92, 0x7c, 0x3d, 0x07, 0x63, 0x2a, 0x4b, 0xca, 0xa8, 0x72, 0x94,
+ 0x32, 0x22, 0xac, 0x45, 0xac, 0xc5, 0x18, 0x60, 0x82, 0xa1, 0xf6, 0x8b, 0x22, 0xd4, 0x82, 0x01,
+ 0x96, 0x3c, 0x0e, 0x25, 0xbe, 0xf0, 0x97, 0x7a, 0x73, 0x30, 0x73, 0x72, 0xfb, 0x00, 0x8a, 0x3c,
+ 0xf2, 0x04, 0x54, 0x0c, 0xa7, 0xd3, 0xd1, 0xed, 0x16, 0x37, 0xe6, 0xd4, 0x1a, 0x75, 0xa6, 0x30,
+ 0xcc, 0x89, 0x24, 0x54, 0x79, 0xe4, 0x3c, 0x14, 0x75, 0xb7, 0x2d, 0xec, 0x2a, 0x35, 0x31, 0x1e,
+ 0xcd, 0xba, 0x6d, 0x0f, 0x79, 0x2a, 0xf9, 0x24, 0x14, 0xa8, 0xbd, 0x33, 0x51, 0x1c, 0xac, 0x91,
+ 0x5c, 0xb6, 0x77, 0x6e, 0xea, 0x6e, 0xa3, 0x2e, 0xeb, 0x50, 0xb8, 0x6c, 0xef, 0x20, 0x2b, 0x43,
+ 0x96, 0xa1, 0x42, 0xed, 0x1d, 0xf6, 0xef, 0xa5, 0xc1, 0xe3, 0x43, 0x03, 0x8a, 0x33, 0x12, 0xa9,
+ 0x9c, 0x07, 0x7a, 0x8d, 0x4c, 0x46, 0x05, 0x41, 0xbe, 0x00, 0x23, 0x42, 0xc5, 0x59, 0x61, 0xff,
+ 0xc4, 0x9b, 0x28, 0x73, 0xc8, 0xc9, 0xc1, 0x3a, 0x12, 0xa7, 0x0b, 0x0d, 0x4c, 0x91, 0x44, 0x0f,
+ 0x63, 0x50, 0xe4, 0x0b, 0x50, 0x53, 0xeb, 0x51, 0xf5, 0x67, 0x53, 0x6d, 0x33, 0x6a, 0x11, 0x8b,
+ 0xf4, 0xf5, 0x9e, 0xe9, 0xd2, 0x0e, 0xb5, 0x7d, 0xaf, 0x71, 0x52, 0xad, 0xd6, 0x55, 0xae, 0x87,
+ 0x21, 0x1a, 0xd9, 0xe8, 0x37, 0x32, 0x09, 0x0b, 0xc9, 0xe3, 0x03, 0x46, 0xf5, 0x21, 0x2c, 0x4c,
+ 0x5f, 0x86, 0x13, 0x81, 0x15, 0x48, 0x1a, 0x12, 0x84, 0xcd, 0xe4, 0x69, 0x56, 0x7c, 0x29, 0x9e,
+ 0x75, 0x67, 0x6f, 0xf2, 0xb1, 0x14, 0x53, 0x42, 0x48, 0x80, 0x49, 0x30, 0xed, 0x07, 0x05, 0xe8,
+ 0xd7, 0xfe, 0xe3, 0x42, 0xcb, 0x1d, 0xb5, 0xd0, 0x92, 0x1f, 0x24, 0x86, 0xcf, 0xe7, 0x65, 0xb1,
+ 0xec, 0x1f, 0x95, 0xf6, 0x63, 0x0a, 0x47, 0xfd, 0x63, 0x1e, 0x94, 0xbe, 0xa3, 0xbd, 0x5d, 0x84,
+ 0xb1, 0x79, 0x9d, 0x76, 0x1c, 0xfb, 0x9e, 0x6b, 0xa1, 0xdc, 0x03, 0xb1, 0x16, 0xba, 0x04, 0x55,
+ 0x97, 0x76, 0x2d, 0xd3, 0xd0, 0x85, 0xf2, 0x25, 0x6d, 0x8f, 0x28, 0xd3, 0x30, 0xc8, 0x1d, 0xb0,
+ 0x06, 0x2e, 0x3c, 0x90, 0x6b, 0xe0, 0xe2, 0x7b, 0xbf, 0x06, 0xd6, 0xbe, 0x9e, 0x07, 0xae, 0xa8,
+ 0x90, 0x8b, 0x50, 0x64, 0x93, 0x70, 0xd2, 0xf2, 0xc2, 0x1b, 0x0e, 0xcf, 0x21, 0xe7, 0x20, 0xef,
+ 0x3b, 0xb2, 0xe7, 0x81, 0xcc, 0xcf, 0xaf, 0x39, 0x98, 0xf7, 0x1d, 0xf2, 0x06, 0x80, 0xe1, 0xd8,
+ 0x2d, 0x53, 0x99, 0xe4, 0xb3, 0x7d, 0xd8, 0x82, 0xe3, 0xde, 0xd2, 0xdd, 0xd6, 0x5c, 0x80, 0x28,
+ 0x56, 0x41, 0xe1, 0x3b, 0x46, 0xb8, 0x91, 0x17, 0xa0, 0xec, 0xd8, 0x0b, 0x3d, 0xcb, 0xe2, 0x02,
+ 0xad, 0x35, 0x3e, 0xc2, 0x96, 0xa6, 0x37, 0x78, 0xca, 0x9d, 0xbd, 0xc9, 0xb3, 0x42, 0xbf, 0x65,
+ 0x6f, 0x2f, 0xb9, 0xa6, 0x6f, 0xda, 0xed, 0xa6, 0xef, 0xea, 0x3e, 0x6d, 0xef, 0xa2, 0x2c, 0xa6,
+ 0x7d, 0x33, 0x07, 0xf5, 0x05, 0xf3, 0x36, 0x6d, 0xbd, 0x64, 0xda, 0x2d, 0xe7, 0x16, 0x41, 0x28,
+ 0x5b, 0xd4, 0x6e, 0xfb, 0x5b, 0x43, 0xae, 0x1f, 0xc4, 0xda, 0x98, 0x23, 0xa0, 0x44, 0x22, 0xd3,
+ 0x50, 0x13, 0xda, 0xa7, 0x69, 0xb7, 0xb9, 0x0c, 0xab, 0xe1, 0xa0, 0xd7, 0x54, 0x19, 0x18, 0xd2,
+ 0x68, 0xbb, 0x70, 0xb2, 0x4f, 0x0c, 0xa4, 0x05, 0x45, 0x5f, 0x6f, 0xab, 0xf1, 0x75, 0x61, 0x68,
+ 0x01, 0xaf, 0xe9, 0xed, 0x88, 0x70, 0xf9, 0x1c, 0xbf, 0xa6, 0xb3, 0x39, 0x9e, 0xa1, 0x6b, 0xbf,
+ 0xca, 0x41, 0x75, 0xa1, 0x67, 0x1b, 0x7c, 0x89, 0x76, 0x6f, 0x8b, 0x9c, 0x52, 0x18, 0xf2, 0xa9,
+ 0x0a, 0x43, 0x0f, 0xca, 0xdb, 0xb7, 0x02, 0x85, 0xa2, 0x3e, 0xb3, 0x32, 0x7c, 0xab, 0x90, 0x55,
+ 0x9a, 0xba, 0xc6, 0xf1, 0x84, 0xc3, 0x68, 0x4c, 0x56, 0xa8, 0x7c, 0xed, 0x25, 0xce, 0x54, 0x32,
+ 0x3b, 0xf7, 0x49, 0xa8, 0x47, 0xc8, 0x0e, 0x65, 0x3b, 0xfe, 0x7f, 0x45, 0x28, 0x2f, 0x36, 0x9b,
+ 0xb3, 0xab, 0x4b, 0xe4, 0x19, 0xa8, 0x4b, 0x5f, 0xc2, 0xf5, 0x50, 0x06, 0x81, 0x2b, 0xa9, 0x19,
+ 0x66, 0x61, 0x94, 0x8e, 0xa9, 0x63, 0x2e, 0xd5, 0xad, 0x8e, 0xec, 0x2c, 0x81, 0x3a, 0x86, 0x2c,
+ 0x11, 0x45, 0x1e, 0xd1, 0x61, 0x8c, 0xad, 0xf0, 0x98, 0x08, 0xc5, 0xea, 0x4d, 0x76, 0x9b, 0x03,
+ 0xae, 0xef, 0xb8, 0x92, 0xb8, 0x1e, 0x03, 0xc0, 0x04, 0x20, 0x79, 0x1e, 0xaa, 0x7a, 0xcf, 0xdf,
+ 0xe2, 0x0a, 0xb4, 0xe8, 0x1b, 0xe7, 0xb9, 0xab, 0x45, 0xa6, 0xdd, 0xd9, 0x9b, 0x1c, 0xb9, 0x86,
+ 0x8d, 0x67, 0xd4, 0x3b, 0x06, 0xd4, 0xac, 0x72, 0x6a, 0xc5, 0x28, 0x2b, 0x57, 0x3a, 0x74, 0xe5,
+ 0x56, 0x63, 0x00, 0x98, 0x00, 0x24, 0xaf, 0xc0, 0xc8, 0x36, 0xdd, 0xf5, 0xf5, 0x0d, 0xc9, 0xa0,
+ 0x7c, 0x18, 0x06, 0xe3, 0x4c, 0x85, 0xbb, 0x16, 0x29, 0x8e, 0x31, 0x30, 0xe2, 0xc1, 0xe9, 0x6d,
+ 0xea, 0x6e, 0x50, 0xd7, 0x91, 0xab, 0x4f, 0xc9, 0xa4, 0x72, 0x18, 0x26, 0x13, 0xfb, 0x7b, 0x93,
+ 0xa7, 0xaf, 0xa5, 0xc0, 0x60, 0x2a, 0xb8, 0xf6, 0xcb, 0x3c, 0x9c, 0x58, 0x14, 0xce, 0x5c, 0xc7,
+ 0x15, 0x93, 0x30, 0x39, 0x0b, 0x05, 0xb7, 0xdb, 0xe3, 0x2d, 0xa7, 0x20, 0xcc, 0xb5, 0xb8, 0xba,
+ 0x8e, 0x2c, 0x8d, 0xbc, 0x0c, 0xd5, 0x96, 0x1c, 0x32, 0xe4, 0xe2, 0x77, 0x28, 0x43, 0x85, 0x7a,
+ 0xc3, 0x00, 0x8d, 0x69, 0xfa, 0x1d, 0xaf, 0xdd, 0x34, 0xdf, 0xa0, 0x72, 0x3d, 0xc8, 0x35, 0xfd,
+ 0x15, 0x91, 0x84, 0x2a, 0x8f, 0xcd, 0xaa, 0xdb, 0x74, 0x57, 0xac, 0x86, 0x8a, 0xe1, 0xac, 0x7a,
+ 0x4d, 0xa6, 0x61, 0x90, 0x4b, 0x26, 0x55, 0x67, 0x61, 0xad, 0xa0, 0x28, 0x56, 0xf2, 0x37, 0x59,
+ 0x82, 0xec, 0x37, 0x6c, 0xc8, 0x7c, 0xcd, 0xf4, 0x7d, 0xea, 0xca, 0xdf, 0x38, 0xd4, 0x90, 0x79,
+ 0x95, 0x23, 0xa0, 0x44, 0x22, 0x1f, 0x83, 0x1a, 0x07, 0x6f, 0x58, 0xce, 0x06, 0xff, 0x71, 0x35,
+ 0xb1, 0xa6, 0xbf, 0xa9, 0x12, 0x31, 0xcc, 0xd7, 0x7e, 0x9d, 0x87, 0x33, 0x8b, 0xd4, 0x17, 0x5a,
+ 0xcd, 0x3c, 0xed, 0x5a, 0xce, 0x2e, 0x53, 0x2d, 0x91, 0xbe, 0x4e, 0x5e, 0x04, 0x30, 0xbd, 0x8d,
+ 0xe6, 0x8e, 0xc1, 0xfb, 0x81, 0xe8, 0xc3, 0x17, 0x65, 0x97, 0x84, 0xa5, 0x66, 0x43, 0xe6, 0xdc,
+ 0x89, 0xbd, 0x61, 0xa4, 0x4c, 0xb8, 0xbc, 0xca, 0xdf, 0x65, 0x79, 0xd5, 0x04, 0xe8, 0x86, 0x0a,
+ 0x6a, 0x81, 0x53, 0xfe, 0x2b, 0xc5, 0xe6, 0x30, 0xba, 0x69, 0x04, 0x26, 0x8b, 0xca, 0x68, 0xc3,
+ 0x78, 0x8b, 0x6e, 0xea, 0x3d, 0xcb, 0x0f, 0x94, 0x6a, 0xd9, 0x89, 0x0f, 0xae, 0x97, 0x07, 0x8e,
+ 0xe6, 0xf9, 0x04, 0x12, 0xf6, 0x61, 0x6b, 0xdf, 0x2f, 0xc0, 0xb9, 0x45, 0xea, 0x07, 0x16, 0x17,
+ 0x39, 0x3a, 0x36, 0xbb, 0xd4, 0x60, 0x7f, 0xe1, 0xad, 0x1c, 0x94, 0x2d, 0x7d, 0x83, 0x5a, 0x6c,
+ 0xf6, 0x62, 0x5f, 0xf3, 0xea, 0xd0, 0x13, 0xc1, 0x60, 0x2e, 0x53, 0xcb, 0x9c, 0x43, 0x62, 0x6a,
+ 0x10, 0x89, 0x28, 0xd9, 0xb3, 0x41, 0xdd, 0xb0, 0x7a, 0x9e, 0x4f, 0xdd, 0x55, 0xc7, 0xf5, 0xa5,
+ 0x3e, 0x19, 0x0c, 0xea, 0x73, 0x61, 0x16, 0x46, 0xe9, 0xc8, 0x0c, 0x80, 0x61, 0x99, 0xd4, 0xf6,
+ 0x79, 0x29, 0xd1, 0xaf, 0x88, 0xfa, 0xbf, 0x73, 0x41, 0x0e, 0x46, 0xa8, 0x18, 0xab, 0x8e, 0x63,
+ 0x9b, 0xbe, 0x23, 0x58, 0x15, 0xe3, 0xac, 0x56, 0xc2, 0x2c, 0x8c, 0xd2, 0xf1, 0x62, 0xd4, 0x77,
+ 0x4d, 0xc3, 0xe3, 0xc5, 0x4a, 0x89, 0x62, 0x61, 0x16, 0x46, 0xe9, 0xd8, 0x9c, 0x17, 0xf9, 0xfe,
+ 0x43, 0xcd, 0x79, 0xdf, 0xad, 0xc1, 0x85, 0x98, 0x58, 0x7d, 0xdd, 0xa7, 0x9b, 0x3d, 0xab, 0x49,
+ 0x7d, 0xf5, 0x03, 0x87, 0x9c, 0x0b, 0xff, 0x63, 0xf8, 0xdf, 0x45, 0x08, 0x89, 0x71, 0x34, 0xff,
+ 0xbd, 0xaf, 0x82, 0x07, 0xfa, 0xf7, 0xd3, 0x50, 0xb3, 0x75, 0xdf, 0xe3, 0x1d, 0x57, 0xf6, 0xd1,
+ 0x40, 0x0d, 0xbb, 0xae, 0x32, 0x30, 0xa4, 0x21, 0xab, 0x70, 0x5a, 0x8a, 0xf8, 0xf2, 0xed, 0xae,
+ 0xe3, 0xfa, 0xd4, 0x15, 0x65, 0xe5, 0x74, 0x2a, 0xcb, 0x9e, 0x5e, 0x49, 0xa1, 0xc1, 0xd4, 0x92,
+ 0x64, 0x05, 0x4e, 0x19, 0xc2, 0xad, 0x4e, 0x2d, 0x47, 0x6f, 0x29, 0x40, 0x61, 0xe0, 0x0a, 0x96,
+ 0x46, 0x73, 0xfd, 0x24, 0x98, 0x56, 0x2e, 0xd9, 0x9a, 0xcb, 0x43, 0xb5, 0xe6, 0xca, 0x30, 0xad,
+ 0xb9, 0x3a, 0x5c, 0x6b, 0xae, 0x1d, 0xac, 0x35, 0x33, 0xc9, 0xb3, 0x76, 0x44, 0x5d, 0xa6, 0x9e,
+ 0x88, 0x19, 0x36, 0x12, 0xb5, 0x11, 0x48, 0xbe, 0x99, 0x42, 0x83, 0xa9, 0x25, 0xc9, 0x06, 0x9c,
+ 0x13, 0xe9, 0x97, 0x6d, 0xc3, 0xdd, 0xed, 0xb2, 0x89, 0x27, 0x82, 0x5b, 0x8f, 0x59, 0x18, 0xcf,
+ 0x35, 0x07, 0x52, 0xe2, 0x5d, 0x50, 0xc8, 0xa7, 0x61, 0x54, 0xfc, 0xa5, 0x15, 0xbd, 0xcb, 0x61,
+ 0x45, 0x0c, 0xc7, 0xc3, 0x12, 0x76, 0x74, 0x2e, 0x9a, 0x89, 0x71, 0x5a, 0x32, 0x0b, 0x27, 0xba,
+ 0x3b, 0x06, 0x7b, 0x5c, 0xda, 0xbc, 0x4e, 0x69, 0x8b, 0xb6, 0xb8, 0xd3, 0xa8, 0xd6, 0x78, 0x44,
+ 0x19, 0x3a, 0x56, 0xe3, 0xd9, 0x98, 0xa4, 0x27, 0xcf, 0xc3, 0x88, 0xe7, 0xeb, 0xae, 0x2f, 0xcd,
+ 0x7a, 0x13, 0x63, 0x22, 0xc6, 0x45, 0x59, 0xbd, 0x9a, 0x91, 0x3c, 0x8c, 0x51, 0xa6, 0xce, 0x17,
+ 0x27, 0x8e, 0x6f, 0xbe, 0xc8, 0x32, 0x5a, 0xfd, 0x6e, 0x1e, 0x2e, 0x2e, 0x52, 0x7f, 0xc5, 0xb1,
+ 0xa5, 0x51, 0x34, 0x6d, 0xda, 0x3f, 0x90, 0x4d, 0x34, 0x3e, 0x69, 0xe7, 0x8f, 0x74, 0xd2, 0x2e,
+ 0x1c, 0xd1, 0xa4, 0x5d, 0x3c, 0xc6, 0x49, 0xfb, 0x37, 0xf2, 0xf0, 0x48, 0x4c, 0x92, 0xab, 0x4e,
+ 0x4b, 0x0d, 0xf8, 0x1f, 0x08, 0xf0, 0x00, 0x02, 0xbc, 0x23, 0xf4, 0x4e, 0xee, 0xd6, 0x4a, 0x68,
+ 0x3c, 0x6f, 0x26, 0x35, 0x9e, 0x57, 0xb2, 0xcc, 0x7c, 0x29, 0x1c, 0x0e, 0x34, 0xe3, 0x5d, 0x05,
+ 0xe2, 0x4a, 0x27, 0x9c, 0x30, 0xfd, 0x44, 0x94, 0x9e, 0x20, 0x88, 0x0e, 0xfb, 0x28, 0x30, 0xa5,
+ 0x14, 0x69, 0xc2, 0xc3, 0x1e, 0xb5, 0x7d, 0xd3, 0xa6, 0x56, 0x1c, 0x4e, 0x68, 0x43, 0x8f, 0x49,
+ 0xb8, 0x87, 0x9b, 0x69, 0x44, 0x98, 0x5e, 0x36, 0xcb, 0x38, 0xf0, 0x07, 0xc0, 0x55, 0x4e, 0x21,
+ 0x9a, 0x23, 0xd3, 0x58, 0xde, 0x4a, 0x6a, 0x2c, 0xaf, 0x66, 0xff, 0x6f, 0xc3, 0x69, 0x2b, 0x33,
+ 0x00, 0xfc, 0x2f, 0x44, 0xd5, 0x95, 0x60, 0x92, 0xc6, 0x20, 0x07, 0x23, 0x54, 0x6c, 0x02, 0x52,
+ 0x72, 0x8e, 0x6a, 0x2a, 0xc1, 0x04, 0xd4, 0x8c, 0x66, 0x62, 0x9c, 0x76, 0xa0, 0xb6, 0x53, 0x1a,
+ 0x5a, 0xdb, 0xb9, 0x0a, 0x24, 0x66, 0x78, 0x14, 0x78, 0xe5, 0x78, 0x0c, 0xe7, 0x52, 0x1f, 0x05,
+ 0xa6, 0x94, 0x1a, 0xd0, 0x94, 0x2b, 0x47, 0xdb, 0x94, 0xab, 0xc3, 0x37, 0x65, 0xf2, 0x2a, 0x9c,
+ 0xe5, 0xac, 0xa4, 0x7c, 0xe2, 0xc0, 0x42, 0xef, 0xf9, 0x90, 0x04, 0x3e, 0x8b, 0x83, 0x08, 0x71,
+ 0x30, 0x06, 0xfb, 0x3f, 0x86, 0x4b, 0x5b, 0x8c, 0xb9, 0x6e, 0x0d, 0xd6, 0x89, 0xe6, 0x52, 0x68,
+ 0x30, 0xb5, 0x24, 0x6b, 0x62, 0x3e, 0x6b, 0x86, 0xfa, 0x86, 0x45, 0x5b, 0x32, 0x86, 0x35, 0x68,
+ 0x62, 0x6b, 0xcb, 0x4d, 0x99, 0x83, 0x11, 0xaa, 0x34, 0x35, 0x65, 0xe4, 0x90, 0x6a, 0xca, 0x22,
+ 0xb7, 0xd2, 0x6f, 0xc6, 0xb4, 0x21, 0xa9, 0xeb, 0x04, 0x51, 0xc9, 0x73, 0x49, 0x02, 0xec, 0x2f,
+ 0xc3, 0xb5, 0x44, 0xc3, 0x35, 0xbb, 0xbe, 0x17, 0xc7, 0x1a, 0x4b, 0x68, 0x89, 0x29, 0x34, 0x98,
+ 0x5a, 0x92, 0xe9, 0xe7, 0x5b, 0x54, 0xb7, 0xfc, 0xad, 0x38, 0xe0, 0x89, 0xb8, 0x7e, 0x7e, 0xa5,
+ 0x9f, 0x04, 0xd3, 0xca, 0xa5, 0x4e, 0x48, 0xe3, 0x0f, 0xa6, 0x5a, 0xf5, 0x8d, 0x02, 0x9c, 0x5d,
+ 0xa4, 0x7e, 0x10, 0xde, 0xf3, 0x81, 0x19, 0xe5, 0x3d, 0x30, 0xa3, 0x7c, 0xa7, 0x04, 0xa7, 0x16,
+ 0xa9, 0xdf, 0xa7, 0x8d, 0xfd, 0x33, 0x15, 0xff, 0x0a, 0x9c, 0x0a, 0x23, 0xca, 0x9a, 0xbe, 0xe3,
+ 0x8a, 0xb9, 0x3c, 0xb1, 0x5a, 0x6e, 0xf6, 0x93, 0x60, 0x5a, 0x39, 0xf2, 0x05, 0x78, 0x84, 0x4f,
+ 0xf5, 0x76, 0x5b, 0xd8, 0x67, 0x85, 0x31, 0x21, 0xb2, 0x27, 0x62, 0x52, 0x42, 0x3e, 0xd2, 0x4c,
+ 0x27, 0xc3, 0x41, 0xe5, 0xc9, 0x57, 0x61, 0xa4, 0x6b, 0x76, 0xa9, 0x65, 0xda, 0x5c, 0x3f, 0xcb,
+ 0x1c, 0x12, 0xb2, 0x1a, 0x01, 0x0b, 0x17, 0x70, 0xd1, 0x54, 0x8c, 0x31, 0x4c, 0x6d, 0xa9, 0xd5,
+ 0x63, 0x6c, 0xa9, 0x7f, 0x9b, 0x87, 0xca, 0xa2, 0xeb, 0xf4, 0xba, 0x8d, 0x5d, 0xd2, 0x86, 0xf2,
+ 0x2d, 0xee, 0x3c, 0x93, 0xae, 0xa9, 0xe1, 0xa3, 0xb2, 0x85, 0x0f, 0x2e, 0x54, 0x89, 0xc4, 0x3b,
+ 0x4a, 0x78, 0xd6, 0x88, 0xb7, 0xe9, 0x2e, 0x6d, 0x49, 0x1f, 0x5a, 0xd0, 0x88, 0xaf, 0xb1, 0x44,
+ 0x14, 0x79, 0xa4, 0x03, 0x27, 0x74, 0xcb, 0x72, 0x6e, 0xd1, 0xd6, 0xb2, 0xee, 0x53, 0x9b, 0x7a,
+ 0xca, 0x25, 0x79, 0x58, 0xb3, 0x34, 0xf7, 0xeb, 0xcf, 0xc6, 0xa1, 0x30, 0x89, 0x4d, 0x5e, 0x83,
+ 0x8a, 0xe7, 0x3b, 0xae, 0x52, 0xb6, 0xea, 0x33, 0x73, 0xc3, 0xff, 0xf4, 0xc6, 0xe7, 0x9b, 0x02,
+ 0x4a, 0xd8, 0xec, 0xe5, 0x0b, 0x2a, 0x06, 0xda, 0xb7, 0x73, 0x00, 0x57, 0xd6, 0xd6, 0x56, 0xa5,
+ 0x7b, 0xa1, 0x05, 0x45, 0xbd, 0x17, 0x38, 0x2a, 0x87, 0x77, 0x08, 0xc6, 0xc2, 0x32, 0xa5, 0x0f,
+ 0xaf, 0xe7, 0x6f, 0x21, 0x47, 0x27, 0x1f, 0x85, 0x8a, 0x54, 0x90, 0xa5, 0xd8, 0x83, 0xd0, 0x02,
+ 0xa9, 0x44, 0xa3, 0xca, 0xd7, 0xfe, 0x6f, 0x1e, 0x60, 0xa9, 0x65, 0xd1, 0xa6, 0x0a, 0xa4, 0xaf,
+ 0xf9, 0x5b, 0x2e, 0xf5, 0xb6, 0x1c, 0xab, 0x35, 0xa4, 0x37, 0x95, 0xdb, 0xfc, 0xd7, 0x14, 0x08,
+ 0x86, 0x78, 0xa4, 0x05, 0x23, 0x9e, 0x4f, 0xbb, 0x2a, 0x52, 0x73, 0x48, 0x27, 0xca, 0xb8, 0xb0,
+ 0x8b, 0x84, 0x38, 0x18, 0x43, 0x25, 0x3a, 0xd4, 0x4d, 0xdb, 0x10, 0x1d, 0xa4, 0xb1, 0x3b, 0x64,
+ 0x43, 0x3a, 0xc1, 0x56, 0x1c, 0x4b, 0x21, 0x0c, 0x46, 0x31, 0xb5, 0x9f, 0xe5, 0xe1, 0x0c, 0xe7,
+ 0xc7, 0xaa, 0x11, 0x8b, 0xc7, 0x24, 0xff, 0xa6, 0x6f, 0xd3, 0xdf, 0xbf, 0x3c, 0x18, 0x6b, 0xb1,
+ 0x67, 0x6c, 0x85, 0xfa, 0x7a, 0xa8, 0xcf, 0x85, 0x69, 0x91, 0x9d, 0x7e, 0x3d, 0x28, 0x7a, 0x6c,
+ 0xbc, 0x12, 0xd2, 0x6b, 0x0e, 0xdd, 0x84, 0xd2, 0x3f, 0x80, 0x8f, 0x5e, 0x81, 0xd7, 0x98, 0x8f,
+ 0x5a, 0x9c, 0x1d, 0xf9, 0x77, 0x50, 0xf6, 0x7c, 0xdd, 0xef, 0xa9, 0xae, 0xb9, 0x7e, 0xd4, 0x8c,
+ 0x39, 0x78, 0x38, 0x8e, 0x88, 0x77, 0x94, 0x4c, 0xb5, 0x9f, 0xe5, 0xe0, 0x5c, 0x7a, 0xc1, 0x65,
+ 0xd3, 0xf3, 0xc9, 0xbf, 0xee, 0x13, 0xfb, 0x01, 0xff, 0x38, 0x2b, 0xcd, 0x85, 0x1e, 0xc4, 0x85,
+ 0xab, 0x94, 0x88, 0xc8, 0x7d, 0x28, 0x99, 0x3e, 0xed, 0xa8, 0xf5, 0xe5, 0x8d, 0x23, 0xfe, 0xf4,
+ 0xc8, 0xd4, 0xce, 0xb8, 0xa0, 0x60, 0xa6, 0xbd, 0x9d, 0x1f, 0xf4, 0xc9, 0x7c, 0xfa, 0xb0, 0xe2,
+ 0x31, 0xbf, 0xd7, 0xb2, 0xc5, 0xfc, 0xc6, 0x2b, 0xd4, 0x1f, 0xfa, 0xfb, 0x6f, 0xfb, 0x43, 0x7f,
+ 0x6f, 0x64, 0x0f, 0xfd, 0x4d, 0x88, 0x61, 0x60, 0x04, 0xf0, 0xbb, 0x05, 0x38, 0x7f, 0xb7, 0x66,
+ 0xc3, 0xe6, 0x33, 0xd9, 0x3a, 0xb3, 0xce, 0x67, 0x77, 0x6f, 0x87, 0x64, 0x06, 0x4a, 0xdd, 0x2d,
+ 0xdd, 0x53, 0x4a, 0x99, 0x5a, 0xb0, 0x94, 0x56, 0x59, 0xe2, 0x1d, 0x36, 0x68, 0x70, 0x65, 0x8e,
+ 0xbf, 0xa2, 0x20, 0x65, 0xc3, 0x71, 0x87, 0x7a, 0x5e, 0x68, 0x13, 0x08, 0x86, 0xe3, 0x15, 0x91,
+ 0x8c, 0x2a, 0x9f, 0xf8, 0x50, 0x16, 0x26, 0x66, 0x39, 0x33, 0x0d, 0x1f, 0xc8, 0x95, 0x12, 0x26,
+ 0x1e, 0x7e, 0x94, 0xf4, 0x56, 0x48, 0x5e, 0x64, 0x0a, 0x8a, 0x7e, 0x18, 0xb4, 0xab, 0x96, 0xe6,
+ 0xc5, 0x14, 0xfd, 0x94, 0xd3, 0xb1, 0x85, 0xbd, 0xb3, 0xc1, 0x8d, 0xea, 0x2d, 0xe9, 0x3f, 0x37,
+ 0x1d, 0x9b, 0x2b, 0x64, 0x85, 0x70, 0x61, 0x7f, 0xa3, 0x8f, 0x02, 0x53, 0x4a, 0x69, 0x7f, 0x5c,
+ 0x85, 0x33, 0xe9, 0xed, 0x81, 0xc9, 0x6d, 0x87, 0xba, 0x1e, 0xc3, 0xce, 0xc5, 0xe5, 0x76, 0x53,
+ 0x24, 0xa3, 0xca, 0x7f, 0x5f, 0x07, 0x9c, 0x7d, 0x27, 0x07, 0x67, 0x5d, 0xe9, 0x23, 0xba, 0x1f,
+ 0x41, 0x67, 0x8f, 0x09, 0x73, 0xc6, 0x00, 0x86, 0x38, 0xb8, 0x2e, 0xe4, 0x7f, 0xe5, 0x60, 0xa2,
+ 0x93, 0xb0, 0x73, 0x1c, 0xe3, 0xbe, 0x35, 0x1e, 0x15, 0xbf, 0x32, 0x80, 0x1f, 0x0e, 0xac, 0x09,
+ 0xf9, 0x2a, 0xd4, 0xbb, 0xac, 0x5d, 0x78, 0x3e, 0xb5, 0x0d, 0xb5, 0x75, 0x6d, 0xf8, 0x9e, 0xb4,
+ 0x1a, 0x62, 0xa9, 0x50, 0x34, 0xa1, 0x1f, 0x44, 0x32, 0x30, 0xca, 0xf1, 0x01, 0xdf, 0xa8, 0x76,
+ 0x09, 0xaa, 0x1e, 0xf5, 0x7d, 0xd3, 0x6e, 0x8b, 0xf5, 0x46, 0x4d, 0xf4, 0x95, 0xa6, 0x4c, 0xc3,
+ 0x20, 0x97, 0x7c, 0x0c, 0x6a, 0xdc, 0xe5, 0x34, 0xeb, 0xb6, 0xbd, 0x89, 0x1a, 0x0f, 0x17, 0x1b,
+ 0x15, 0x01, 0x70, 0x32, 0x11, 0xc3, 0x7c, 0xf2, 0x34, 0x8c, 0x6c, 0xf0, 0xee, 0x2b, 0xf7, 0x2e,
+ 0x0b, 0x1b, 0x17, 0xd7, 0xd6, 0x1a, 0x91, 0x74, 0x8c, 0x51, 0x91, 0x19, 0x00, 0x1a, 0xf8, 0xe5,
+ 0x92, 0xf6, 0xac, 0xd0, 0x63, 0x87, 0x11, 0x2a, 0xf2, 0x18, 0x14, 0x7c, 0xcb, 0xe3, 0x36, 0xac,
+ 0x6a, 0xb8, 0x04, 0x5d, 0x5b, 0x6e, 0x22, 0x4b, 0xd7, 0x7e, 0x9d, 0x83, 0x13, 0x89, 0xcd, 0x25,
+ 0xac, 0x48, 0xcf, 0xb5, 0xe4, 0x30, 0x12, 0x14, 0x59, 0xc7, 0x65, 0x64, 0xe9, 0xe4, 0x55, 0xa9,
+ 0x96, 0xe7, 0x33, 0x1e, 0xd3, 0x70, 0x5d, 0xf7, 0x3d, 0xa6, 0x87, 0xf7, 0x69, 0xe4, 0xdc, 0xcd,
+ 0x17, 0xd6, 0x47, 0xce, 0x03, 0x11, 0x37, 0x5f, 0x98, 0x87, 0x31, 0xca, 0x84, 0xc1, 0xaf, 0x78,
+ 0x10, 0x83, 0x9f, 0xf6, 0xcd, 0x7c, 0x44, 0x02, 0x52, 0xb3, 0xbf, 0x87, 0x04, 0x9e, 0x64, 0x13,
+ 0x68, 0x30, 0xb9, 0xd7, 0xa2, 0xf3, 0x1f, 0x9f, 0x8c, 0x65, 0x2e, 0x79, 0x49, 0xc8, 0xbe, 0x90,
+ 0x71, 0x33, 0xec, 0xda, 0x72, 0x53, 0x44, 0x57, 0xa9, 0xbf, 0x16, 0xfc, 0x82, 0xe2, 0x31, 0xfd,
+ 0x02, 0xed, 0xf7, 0x0b, 0x50, 0xbf, 0xea, 0x6c, 0xbc, 0x4f, 0x22, 0xa8, 0xd3, 0xa7, 0xa9, 0xfc,
+ 0x7b, 0x38, 0x4d, 0xad, 0xc3, 0x23, 0xbe, 0x6f, 0x35, 0xa9, 0xe1, 0xd8, 0x2d, 0x6f, 0x76, 0xd3,
+ 0xa7, 0xee, 0x82, 0x69, 0x9b, 0xde, 0x16, 0x6d, 0x49, 0x77, 0xd2, 0xa3, 0xfb, 0x7b, 0x93, 0x8f,
+ 0xac, 0xad, 0x2d, 0xa7, 0x91, 0xe0, 0xa0, 0xb2, 0x7c, 0xd8, 0x10, 0x3b, 0x01, 0xf9, 0x4e, 0x19,
+ 0x19, 0x73, 0x23, 0x86, 0x8d, 0x48, 0x3a, 0xc6, 0xa8, 0xb4, 0x77, 0xf2, 0x50, 0x0b, 0x36, 0xe0,
+ 0x93, 0x27, 0xa0, 0xb2, 0xe1, 0x3a, 0xdb, 0xd4, 0x15, 0x9e, 0x3b, 0xb9, 0x53, 0xa6, 0x21, 0x92,
+ 0x50, 0xe5, 0x91, 0xc7, 0xa1, 0xe4, 0x3b, 0x5d, 0xd3, 0x48, 0x1a, 0xd4, 0xd6, 0x58, 0x22, 0x8a,
+ 0xbc, 0xe3, 0x6b, 0xe0, 0x4f, 0xc6, 0x54, 0xbb, 0xda, 0x40, 0x65, 0xec, 0x15, 0x28, 0x7a, 0xba,
+ 0x67, 0xc9, 0xf9, 0x34, 0xc3, 0x5e, 0xf6, 0xd9, 0xe6, 0xb2, 0xdc, 0xcb, 0x3e, 0xdb, 0x5c, 0x46,
+ 0x0e, 0xaa, 0xfd, 0x22, 0x0f, 0x75, 0x21, 0x37, 0x31, 0x2a, 0x1c, 0xa5, 0xe4, 0x5e, 0xe0, 0xa1,
+ 0x14, 0x5e, 0xaf, 0x43, 0x5d, 0x6e, 0x66, 0x92, 0x83, 0x5c, 0xd4, 0x3f, 0x10, 0x66, 0x06, 0xe1,
+ 0x14, 0x61, 0x92, 0x12, 0x7d, 0xf1, 0x18, 0x45, 0x5f, 0x3a, 0x90, 0xe8, 0xcb, 0xc7, 0x21, 0xfa,
+ 0xb7, 0xf2, 0x50, 0x5b, 0x36, 0x37, 0xa9, 0xb1, 0x6b, 0x58, 0x7c, 0x4f, 0x60, 0x8b, 0x5a, 0xd4,
+ 0xa7, 0x8b, 0xae, 0x6e, 0xd0, 0x55, 0xea, 0x9a, 0xfc, 0x80, 0x1a, 0xd6, 0x3f, 0xf8, 0x08, 0x24,
+ 0xf7, 0x04, 0xce, 0x0f, 0xa0, 0xc1, 0x81, 0xa5, 0xc9, 0x12, 0x8c, 0xb4, 0xa8, 0x67, 0xba, 0xb4,
+ 0xb5, 0x1a, 0x59, 0xa8, 0x3c, 0xa1, 0xa6, 0x9a, 0xf9, 0x48, 0xde, 0x9d, 0xbd, 0xc9, 0x51, 0x65,
+ 0xa0, 0x14, 0x2b, 0x96, 0x58, 0x51, 0xd6, 0xe5, 0xbb, 0x7a, 0xcf, 0x4b, 0xab, 0x63, 0xa4, 0xcb,
+ 0xaf, 0xa6, 0x93, 0xe0, 0xa0, 0xb2, 0x5a, 0x09, 0x0a, 0xcb, 0x4e, 0x5b, 0x7b, 0xbb, 0x00, 0xc1,
+ 0x49, 0x46, 0xe4, 0x3f, 0xe4, 0xa0, 0xae, 0xdb, 0xb6, 0xe3, 0xcb, 0x53, 0x82, 0x84, 0x07, 0x1e,
+ 0x33, 0x1f, 0x98, 0x34, 0x35, 0x1b, 0x82, 0x0a, 0xe7, 0x6d, 0xe0, 0x50, 0x8e, 0xe4, 0x60, 0x94,
+ 0x37, 0xe9, 0x25, 0xfc, 0xc9, 0x2b, 0xd9, 0x6b, 0x71, 0x00, 0xef, 0xf1, 0xb9, 0xcf, 0xc1, 0x78,
+ 0xb2, 0xb2, 0x87, 0x71, 0x07, 0x65, 0x72, 0xcc, 0xe7, 0x01, 0xc2, 0x98, 0x92, 0xfb, 0x60, 0xc4,
+ 0x32, 0x63, 0x46, 0xac, 0xc5, 0xe1, 0x05, 0x1c, 0x54, 0x7a, 0xa0, 0xe1, 0xea, 0xf5, 0x84, 0xe1,
+ 0x6a, 0xe9, 0x28, 0x98, 0xdd, 0xdd, 0x58, 0xf5, 0x7f, 0x72, 0x30, 0x1e, 0x12, 0xcb, 0x1d, 0xb2,
+ 0xcf, 0xc1, 0xa8, 0x4b, 0xf5, 0x56, 0x43, 0xf7, 0x8d, 0x2d, 0x1e, 0xea, 0x9d, 0xe3, 0xb1, 0xd9,
+ 0x27, 0xf7, 0xf7, 0x26, 0x47, 0x31, 0x9a, 0x81, 0x71, 0x3a, 0xa2, 0x43, 0x9d, 0x25, 0xac, 0x99,
+ 0x1d, 0xea, 0xf4, 0xfc, 0x21, 0xad, 0xa6, 0x7c, 0xc1, 0x82, 0x21, 0x0c, 0x46, 0x31, 0xb5, 0x77,
+ 0x73, 0x30, 0x16, 0xad, 0xf0, 0xb1, 0x5b, 0xd4, 0xb6, 0xe2, 0x16, 0xb5, 0xb9, 0x23, 0xf8, 0x27,
+ 0x03, 0xac, 0x68, 0xbf, 0xac, 0x46, 0x3f, 0x8d, 0x5b, 0xce, 0xa2, 0xc6, 0x82, 0xdc, 0x5d, 0x8d,
+ 0x05, 0xef, 0xff, 0xc3, 0x6b, 0x06, 0x69, 0xb9, 0xc5, 0x07, 0x58, 0xcb, 0x7d, 0x2f, 0x4f, 0xc0,
+ 0x89, 0x9c, 0xe2, 0x52, 0xce, 0x70, 0x8a, 0x4b, 0x27, 0x38, 0xc5, 0xa5, 0x72, 0x64, 0x83, 0xce,
+ 0x41, 0x4e, 0x72, 0xa9, 0xde, 0xd7, 0x93, 0x5c, 0x6a, 0xc7, 0x75, 0x92, 0x0b, 0x64, 0x3d, 0xc9,
+ 0xe5, 0xcd, 0x1c, 0x8c, 0xb5, 0x62, 0x3b, 0x66, 0xb9, 0x6d, 0x21, 0xcb, 0x54, 0x13, 0xdf, 0x80,
+ 0x2b, 0xb6, 0x4c, 0xc5, 0xd3, 0x30, 0xc1, 0x52, 0xfb, 0xdf, 0x95, 0xe8, 0x3c, 0x70, 0xbf, 0x4d,
+ 0xd5, 0xcf, 0xc6, 0x4d, 0xd5, 0x17, 0x93, 0xa6, 0xea, 0x13, 0x91, 0x28, 0xd2, 0xa8, 0xb9, 0xfa,
+ 0xe3, 0x91, 0xe1, 0xb1, 0xc0, 0x4f, 0x4e, 0x09, 0x24, 0x9d, 0x32, 0x44, 0x7e, 0x1c, 0xaa, 0x9e,
+ 0x3a, 0x73, 0x52, 0x2c, 0x6c, 0xc2, 0xff, 0xa2, 0xce, 0x83, 0x0c, 0x28, 0x98, 0x26, 0xee, 0x52,
+ 0xdd, 0x73, 0xec, 0xa4, 0x26, 0x8e, 0x3c, 0x15, 0x65, 0x6e, 0xd4, 0x64, 0x5e, 0xbe, 0x87, 0xc9,
+ 0x5c, 0x87, 0xba, 0xa5, 0x7b, 0xfe, 0x7a, 0xb7, 0xa5, 0xfb, 0xb4, 0x25, 0xfb, 0xdb, 0xbf, 0x38,
+ 0xd8, 0x5c, 0xc5, 0xe6, 0xbf, 0x50, 0x21, 0x5c, 0x0e, 0x61, 0x30, 0x8a, 0x49, 0x5a, 0x30, 0xc2,
+ 0x5e, 0x79, 0x6f, 0x68, 0xcd, 0xaa, 0x23, 0x00, 0x0e, 0xc3, 0x23, 0xb0, 0xf4, 0x2c, 0x47, 0x70,
+ 0x30, 0x86, 0x3a, 0xc0, 0xaa, 0x5e, 0x1b, 0xc6, 0xaa, 0x4e, 0x3e, 0x2d, 0x94, 0x8d, 0x5d, 0xf5,
+ 0xc3, 0xb8, 0x35, 0x6e, 0x34, 0x8c, 0x2a, 0xc4, 0x68, 0x26, 0xc6, 0x69, 0xc9, 0x2c, 0x9c, 0x30,
+ 0x7a, 0xae, 0xcb, 0xe3, 0x88, 0x64, 0xf1, 0x3a, 0x2f, 0x1e, 0xc4, 0x8b, 0xcd, 0xc5, 0xb3, 0x31,
+ 0x49, 0xcf, 0x20, 0x7a, 0x52, 0x92, 0x0a, 0x62, 0x24, 0x0e, 0xb1, 0x1e, 0xcf, 0xc6, 0x24, 0x3d,
+ 0xdf, 0x28, 0x21, 0x50, 0xaf, 0xe8, 0xde, 0x96, 0x0c, 0x36, 0x0b, 0x37, 0x4a, 0x84, 0x59, 0x18,
+ 0xa5, 0x23, 0x33, 0x00, 0x02, 0x89, 0x97, 0x1a, 0x8b, 0xc7, 0x60, 0xae, 0x07, 0x39, 0x18, 0xa1,
+ 0xd2, 0xde, 0xac, 0x41, 0xfd, 0xba, 0xee, 0x9b, 0x3b, 0x94, 0xfb, 0xbc, 0x8e, 0xc7, 0xf1, 0xf0,
+ 0xdf, 0x72, 0x70, 0x26, 0x1e, 0xd8, 0x78, 0x8c, 0xde, 0x07, 0x7e, 0x4c, 0x0a, 0xa6, 0x72, 0xc3,
+ 0x01, 0xb5, 0xe0, 0x7e, 0x88, 0xbe, 0x38, 0xc9, 0xe3, 0xf6, 0x43, 0x34, 0x07, 0x31, 0xc4, 0xc1,
+ 0x75, 0x79, 0xbf, 0xf8, 0x21, 0x1e, 0xec, 0xd3, 0xf4, 0x12, 0x5e, 0x92, 0xca, 0x03, 0xe3, 0x25,
+ 0xa9, 0x3e, 0x10, 0xaa, 0x69, 0x37, 0xe2, 0x25, 0xa9, 0x65, 0x8c, 0xd6, 0x91, 0x7b, 0x01, 0x04,
+ 0xda, 0x20, 0x6f, 0x0b, 0xdf, 0xc6, 0xaf, 0xac, 0xd7, 0x4c, 0xa3, 0xdb, 0xd0, 0x3d, 0xd3, 0x90,
+ 0x4a, 0x42, 0x86, 0xd3, 0x43, 0xd5, 0xf9, 0x66, 0xc2, 0xa9, 0xcf, 0x5f, 0x51, 0x60, 0x87, 0xc7,
+ 0xb9, 0xe5, 0x33, 0x1d, 0xe7, 0x46, 0xe6, 0xa0, 0x68, 0x6f, 0xd3, 0xdd, 0xc3, 0x6d, 0x88, 0xe7,
+ 0x2b, 0x95, 0xeb, 0xd7, 0xe8, 0x2e, 0xf2, 0xc2, 0xda, 0x3b, 0x79, 0x00, 0xf6, 0xf9, 0x07, 0xf3,
+ 0x57, 0x7c, 0x14, 0x2a, 0x5e, 0x8f, 0x5b, 0x16, 0xa4, 0x7a, 0x13, 0x86, 0x38, 0x89, 0x64, 0x54,
+ 0xf9, 0xe4, 0x71, 0x28, 0xbd, 0xde, 0xa3, 0x3d, 0xe5, 0x7c, 0x0f, 0x94, 0xdb, 0xcf, 0xb3, 0x44,
+ 0x14, 0x79, 0xc7, 0x67, 0x7b, 0x54, 0x7e, 0x8d, 0xd2, 0x71, 0xf9, 0x35, 0x6a, 0x50, 0xb9, 0xee,
+ 0xf0, 0x88, 0x49, 0xed, 0xaf, 0xf3, 0x00, 0x61, 0x44, 0x1a, 0xf9, 0x76, 0x0e, 0x1e, 0x0e, 0x3a,
+ 0x9c, 0x2f, 0xd6, 0x28, 0xfc, 0xc0, 0xde, 0xcc, 0x3e, 0x8e, 0xb4, 0xce, 0xce, 0x47, 0xa0, 0xd5,
+ 0x34, 0x76, 0x98, 0x5e, 0x0b, 0x82, 0x50, 0xa5, 0x9d, 0xae, 0xbf, 0x3b, 0x6f, 0xba, 0xb2, 0x05,
+ 0xa6, 0x06, 0x3e, 0x5e, 0x96, 0x34, 0xa2, 0xa8, 0x5c, 0x48, 0xf3, 0x4e, 0xa4, 0x72, 0x30, 0xc0,
+ 0x21, 0x5b, 0x50, 0xb5, 0x9d, 0x57, 0x3d, 0x26, 0x0e, 0xd9, 0x1c, 0x5f, 0x1c, 0x5e, 0xe4, 0x42,
+ 0xac, 0xc2, 0x26, 0x2e, 0x5f, 0xb0, 0x62, 0x4b, 0x61, 0x7f, 0x2b, 0x0f, 0xa7, 0x52, 0xe4, 0x40,
+ 0x5e, 0x84, 0x71, 0x19, 0xfc, 0x17, 0x9e, 0x5c, 0x9d, 0x0b, 0x4f, 0xae, 0x6e, 0x26, 0xf2, 0xb0,
+ 0x8f, 0x9a, 0xbc, 0x0a, 0xa0, 0x1b, 0x06, 0xf5, 0xbc, 0x15, 0xa7, 0xa5, 0xb4, 0xf7, 0x17, 0x98,
+ 0xfa, 0x32, 0x1b, 0xa4, 0xde, 0xd9, 0x9b, 0xfc, 0x44, 0x5a, 0x3c, 0x6f, 0x42, 0xce, 0x61, 0x01,
+ 0x8c, 0x40, 0x92, 0x2f, 0x03, 0x88, 0x85, 0x6a, 0x70, 0xe4, 0xc0, 0x3d, 0xac, 0x3b, 0x53, 0xea,
+ 0x70, 0xa7, 0xa9, 0xcf, 0xf7, 0x74, 0xdb, 0x37, 0xfd, 0x5d, 0x71, 0xc2, 0xcb, 0xcd, 0x00, 0x05,
+ 0x23, 0x88, 0xda, 0xef, 0xe4, 0xa1, 0xaa, 0xec, 0xca, 0xf7, 0xc1, 0x98, 0xd8, 0x8e, 0x19, 0x13,
+ 0x8f, 0x28, 0x82, 0x37, 0xcd, 0x94, 0xe8, 0x24, 0x4c, 0x89, 0x8b, 0xd9, 0x59, 0xdd, 0xdd, 0x90,
+ 0xf8, 0xbd, 0x3c, 0x8c, 0x29, 0xd2, 0xac, 0x66, 0xc4, 0xcf, 0xc2, 0x09, 0xe1, 0x79, 0x5f, 0xd1,
+ 0x6f, 0x8b, 0xc3, 0x6e, 0xb8, 0xc0, 0x8a, 0x22, 0x68, 0xb6, 0x11, 0xcf, 0xc2, 0x24, 0x2d, 0x6b,
+ 0xd6, 0x22, 0x69, 0x9d, 0xad, 0xba, 0x84, 0xaf, 0x4e, 0xac, 0x0e, 0x79, 0xb3, 0x6e, 0x24, 0xf2,
+ 0xb0, 0x8f, 0x3a, 0x69, 0xc7, 0x2c, 0x1e, 0x83, 0x1d, 0xf3, 0x4f, 0x72, 0x30, 0x12, 0xca, 0xeb,
+ 0xd8, 0xad, 0x98, 0x9b, 0x71, 0x2b, 0xe6, 0x6c, 0xe6, 0xe6, 0x30, 0xc0, 0x86, 0xf9, 0x9f, 0x2a,
+ 0x10, 0x0b, 0x24, 0x27, 0x1b, 0x70, 0xce, 0x4c, 0x0d, 0x87, 0x8b, 0x8c, 0x36, 0xc1, 0xce, 0xe8,
+ 0xa5, 0x81, 0x94, 0x78, 0x17, 0x14, 0xd2, 0x83, 0xea, 0x0e, 0x75, 0x7d, 0xd3, 0xa0, 0xea, 0xfb,
+ 0x16, 0x33, 0xab, 0x64, 0xd2, 0x52, 0x1b, 0xc8, 0xf4, 0xa6, 0x64, 0x80, 0x01, 0x2b, 0xb2, 0x01,
+ 0x25, 0xda, 0x6a, 0x53, 0x75, 0xfc, 0x50, 0xc6, 0xc3, 0x3d, 0x03, 0x79, 0xb2, 0x37, 0x0f, 0x05,
+ 0x34, 0xf1, 0xa0, 0x66, 0x29, 0x4f, 0x9c, 0x6c, 0x87, 0xc3, 0x2b, 0x58, 0x81, 0x4f, 0x2f, 0x3c,
+ 0x99, 0x20, 0x48, 0xc2, 0x90, 0x0f, 0xd9, 0x0e, 0x4c, 0x82, 0xa5, 0x23, 0x1a, 0x3c, 0xee, 0x62,
+ 0x10, 0xf4, 0xa0, 0x76, 0x4b, 0xf7, 0xa9, 0xdb, 0xd1, 0xdd, 0x6d, 0xb9, 0xda, 0x18, 0xfe, 0x0b,
+ 0x5f, 0x52, 0x48, 0xe1, 0x17, 0x06, 0x49, 0x18, 0xf2, 0x21, 0x0e, 0xd4, 0x7c, 0xa9, 0x3e, 0x2b,
+ 0xbb, 0xe7, 0xf0, 0x4c, 0x95, 0x22, 0xee, 0xc9, 0x80, 0x72, 0xf5, 0x8a, 0x21, 0x0f, 0xb2, 0x13,
+ 0x3b, 0x7f, 0x59, 0x9c, 0xba, 0xdd, 0xc8, 0x60, 0x3f, 0x97, 0x50, 0xe1, 0x74, 0x93, 0x7e, 0x8e,
+ 0xb3, 0xf6, 0x4e, 0x29, 0x1c, 0x96, 0xef, 0xb7, 0x55, 0xef, 0xe9, 0xb8, 0x55, 0xef, 0x42, 0xd2,
+ 0xaa, 0x97, 0x70, 0xe8, 0x1e, 0x3e, 0x04, 0x35, 0x61, 0x4f, 0x2b, 0x1e, 0x83, 0x3d, 0xed, 0x29,
+ 0xa8, 0xef, 0xf0, 0x91, 0x40, 0x9c, 0x65, 0x54, 0xe2, 0xd3, 0x08, 0x1f, 0xd9, 0x6f, 0x86, 0xc9,
+ 0x18, 0xa5, 0x61, 0x45, 0xe4, 0x8d, 0x13, 0xc1, 0x61, 0xb0, 0xb2, 0x48, 0x33, 0x4c, 0xc6, 0x28,
+ 0x0d, 0x8f, 0x5e, 0x33, 0xed, 0x6d, 0x51, 0xa0, 0xc2, 0x0b, 0x88, 0xe8, 0x35, 0x95, 0x88, 0x61,
+ 0x3e, 0xb9, 0x04, 0xd5, 0x5e, 0x6b, 0x53, 0xd0, 0x56, 0x39, 0x2d, 0xd7, 0x30, 0xd7, 0xe7, 0x17,
+ 0xe4, 0xd9, 0x4a, 0x2a, 0x97, 0xd5, 0xa4, 0xa3, 0x77, 0x55, 0x06, 0x5f, 0x1b, 0xca, 0x9a, 0xac,
+ 0x84, 0xc9, 0x18, 0xa5, 0x21, 0x9f, 0x82, 0x31, 0x97, 0xb6, 0x7a, 0x06, 0x0d, 0x4a, 0x09, 0x73,
+ 0x1c, 0x11, 0x57, 0x6b, 0x44, 0x73, 0x30, 0x41, 0x39, 0xc0, 0x2a, 0x58, 0x1f, 0x2a, 0xd6, 0xf6,
+ 0xa7, 0x39, 0x20, 0xfd, 0xd1, 0xde, 0x64, 0x0b, 0xca, 0x36, 0xb7, 0x7e, 0x65, 0x3e, 0x3e, 0x3a,
+ 0x62, 0x44, 0x13, 0xc3, 0x92, 0x4c, 0x90, 0xf8, 0xc4, 0x86, 0x2a, 0xbd, 0xed, 0x53, 0xd7, 0x0e,
+ 0x76, 0x7f, 0x1c, 0xcd, 0x51, 0xd5, 0x62, 0x35, 0x20, 0x91, 0x31, 0xe0, 0xa1, 0xfd, 0x3c, 0x0f,
+ 0xf5, 0x08, 0xdd, 0xbd, 0x16, 0x95, 0x7c, 0x03, 0xba, 0x30, 0x3a, 0xad, 0xbb, 0x96, 0xec, 0x61,
+ 0x91, 0x0d, 0xe8, 0x32, 0x0b, 0x97, 0x31, 0x4a, 0x47, 0x66, 0x00, 0x3a, 0xba, 0xe7, 0x53, 0x97,
+ 0xcf, 0xbe, 0x89, 0x6d, 0xdf, 0x2b, 0x41, 0x0e, 0x46, 0xa8, 0xc8, 0x45, 0x79, 0xd8, 0x78, 0x31,
+ 0x7e, 0x4c, 0xdf, 0x80, 0x93, 0xc4, 0x4b, 0x47, 0x70, 0x92, 0x38, 0x69, 0xc3, 0xb8, 0xaa, 0xb5,
+ 0xca, 0x3d, 0xdc, 0x21, 0x6e, 0x62, 0xfd, 0x92, 0x80, 0xc0, 0x3e, 0x50, 0xed, 0x9d, 0x1c, 0x8c,
+ 0xc6, 0x4c, 0x1e, 0xe2, 0x80, 0x3d, 0xb5, 0x57, 0x21, 0x76, 0xc0, 0x5e, 0x64, 0x8b, 0xc1, 0x93,
+ 0x50, 0x16, 0x02, 0x4a, 0x86, 0x20, 0x0a, 0x11, 0xa2, 0xcc, 0x65, 0x63, 0x99, 0x34, 0xaa, 0x26,
+ 0xc7, 0x32, 0x69, 0x75, 0x45, 0x95, 0x2f, 0x9c, 0x13, 0xa2, 0x76, 0xfd, 0xce, 0x09, 0x91, 0x8e,
+ 0x01, 0x85, 0xf6, 0x03, 0x5e, 0x6f, 0xdf, 0xdd, 0x0d, 0xd6, 0x72, 0x6d, 0xa8, 0xc8, 0xb0, 0x33,
+ 0xd9, 0x35, 0x5e, 0xcc, 0x60, 0x87, 0xe1, 0x38, 0x32, 0xc0, 0x4a, 0x37, 0xb6, 0x6f, 0x6c, 0x6e,
+ 0xa2, 0x42, 0x27, 0x97, 0xa1, 0xe6, 0xd8, 0x0b, 0xba, 0x69, 0xf5, 0x5c, 0x35, 0xb2, 0x7f, 0x84,
+ 0x8d, 0x55, 0x37, 0x54, 0xe2, 0x9d, 0xbd, 0xc9, 0x33, 0xc1, 0x4b, 0xac, 0x92, 0x18, 0x96, 0xd4,
+ 0xfe, 0xbe, 0x00, 0x3c, 0xe4, 0x88, 0x3c, 0x07, 0xb5, 0x0e, 0x35, 0xb6, 0x74, 0xdb, 0xf4, 0xd4,
+ 0x11, 0xa1, 0x6c, 0x7d, 0x5e, 0x5b, 0x51, 0x89, 0x77, 0x98, 0x08, 0x66, 0x9b, 0xcb, 0x3c, 0xaa,
+ 0x3f, 0xa4, 0x25, 0x06, 0x94, 0xdb, 0x9e, 0xa7, 0x77, 0xcd, 0xcc, 0x1e, 0x6f, 0x71, 0x24, 0xa3,
+ 0x18, 0x06, 0xc4, 0x33, 0x4a, 0x68, 0x62, 0x40, 0xa9, 0x6b, 0xe9, 0xa6, 0x9d, 0xf9, 0x6a, 0x1c,
+ 0xf6, 0x05, 0xab, 0x0c, 0x49, 0x18, 0xa5, 0xf8, 0x23, 0x0a, 0x6c, 0xd2, 0x83, 0xba, 0x67, 0xb8,
+ 0x7a, 0xc7, 0xdb, 0xd2, 0x67, 0x9e, 0x79, 0x36, 0xb3, 0x9a, 0x17, 0xb2, 0x12, 0xb3, 0xce, 0x1c,
+ 0xce, 0xae, 0x34, 0xaf, 0xcc, 0xce, 0x3c, 0xf3, 0x2c, 0x46, 0xf9, 0x44, 0xd9, 0x3e, 0xf3, 0xd4,
+ 0x8c, 0xec, 0xb9, 0x47, 0xce, 0xf6, 0x99, 0xa7, 0x66, 0x30, 0xca, 0x47, 0xfb, 0xbb, 0x1c, 0xd4,
+ 0x02, 0x5a, 0xb2, 0x0e, 0xc0, 0xc6, 0x10, 0x79, 0x88, 0xe2, 0xa1, 0x2e, 0x34, 0xe0, 0xeb, 0xfa,
+ 0xf5, 0xa0, 0x30, 0x46, 0x80, 0x52, 0x4e, 0x99, 0xcc, 0x1f, 0xf5, 0x29, 0x93, 0xd3, 0x50, 0xdb,
+ 0xd2, 0xed, 0x96, 0xb7, 0xa5, 0x6f, 0x8b, 0xa1, 0x34, 0x72, 0xee, 0xea, 0x15, 0x95, 0x81, 0x21,
+ 0x8d, 0xf6, 0x5b, 0x65, 0x10, 0x6e, 0x6a, 0xd6, 0xd9, 0x5b, 0xa6, 0x27, 0xe2, 0xa4, 0x73, 0xbc,
+ 0x64, 0xd0, 0xd9, 0xe7, 0x65, 0x3a, 0x06, 0x14, 0xe4, 0x2c, 0x14, 0x3a, 0xa6, 0x2d, 0x7d, 0x36,
+ 0xdc, 0x64, 0xb7, 0x62, 0xda, 0xc8, 0xd2, 0x78, 0x96, 0x7e, 0x5b, 0x86, 0xb8, 0x89, 0x2c, 0xfd,
+ 0x36, 0xb2, 0x34, 0xb6, 0x88, 0xb6, 0x1c, 0x67, 0x9b, 0x75, 0x5b, 0x15, 0x09, 0x57, 0xe4, 0x53,
+ 0x39, 0x5f, 0x44, 0x2f, 0xc7, 0xb3, 0x30, 0x49, 0x4b, 0xd6, 0xe1, 0x91, 0x37, 0xa8, 0xeb, 0xc8,
+ 0x71, 0xaa, 0x69, 0x51, 0xda, 0x55, 0x30, 0x42, 0x09, 0xe2, 0x01, 0x75, 0x5f, 0x4c, 0x27, 0xc1,
+ 0x41, 0x65, 0x79, 0x68, 0xae, 0xee, 0xb6, 0xa9, 0xbf, 0xea, 0x3a, 0x06, 0xf5, 0x3c, 0xd3, 0x6e,
+ 0x2b, 0xd8, 0x72, 0x08, 0xbb, 0x96, 0x4e, 0x82, 0x83, 0xca, 0x92, 0x97, 0x61, 0x42, 0x64, 0x09,
+ 0x75, 0x61, 0x76, 0x47, 0x37, 0x2d, 0x7d, 0xc3, 0xb4, 0xd4, 0x8d, 0x72, 0xa3, 0xc2, 0x33, 0xb2,
+ 0x36, 0x80, 0x06, 0x07, 0x96, 0x26, 0x57, 0x61, 0x5c, 0xf9, 0xc5, 0x56, 0xa9, 0xdb, 0x0c, 0x42,
+ 0x17, 0x46, 0x1b, 0x17, 0xd8, 0x8a, 0x75, 0x9e, 0x76, 0x5d, 0x6a, 0x44, 0xbd, 0x81, 0x8a, 0x0a,
+ 0xfb, 0xca, 0x11, 0x84, 0x33, 0x3c, 0x3e, 0x61, 0xbd, 0x3b, 0xe7, 0x38, 0x56, 0xcb, 0xb9, 0x65,
+ 0xab, 0x6f, 0x17, 0xaa, 0x19, 0x77, 0x85, 0x35, 0x53, 0x29, 0x70, 0x40, 0x49, 0xf6, 0xe5, 0x3c,
+ 0x67, 0xde, 0xb9, 0x65, 0x27, 0x51, 0x21, 0xfc, 0xf2, 0xe6, 0x00, 0x1a, 0x1c, 0x58, 0x9a, 0x2c,
+ 0x00, 0x49, 0x7e, 0xc1, 0x7a, 0x57, 0xba, 0x57, 0xcf, 0x88, 0xf3, 0x50, 0x92, 0xb9, 0x98, 0x52,
+ 0x82, 0x2c, 0xc3, 0xe9, 0x64, 0x2a, 0x63, 0x27, 0xbd, 0xac, 0xfc, 0x24, 0x54, 0x4c, 0xc9, 0xc7,
+ 0xd4, 0x52, 0xda, 0x6f, 0xe7, 0x61, 0x34, 0xb6, 0x81, 0xfe, 0x81, 0xdb, 0xa8, 0xcc, 0x74, 0xe8,
+ 0x8e, 0xd7, 0x5e, 0x9a, 0xbf, 0x42, 0xf5, 0x16, 0x75, 0xaf, 0x51, 0x75, 0xd8, 0x01, 0x1f, 0x54,
+ 0x56, 0x62, 0x39, 0x98, 0xa0, 0x24, 0x9b, 0x50, 0x12, 0x16, 0xe1, 0xac, 0x57, 0x63, 0x28, 0x19,
+ 0x71, 0xb3, 0xb0, 0xbc, 0x4f, 0xc6, 0x71, 0x29, 0x0a, 0x78, 0xcd, 0x87, 0x91, 0x28, 0x05, 0x1b,
+ 0x48, 0x42, 0x75, 0xb3, 0x12, 0x53, 0x35, 0x97, 0xa0, 0xe0, 0xfb, 0xc3, 0x6e, 0x81, 0x16, 0x1e,
+ 0x86, 0xb5, 0x65, 0x64, 0x18, 0xda, 0x26, 0xfb, 0x77, 0x9e, 0x67, 0x3a, 0xb6, 0x3c, 0x0f, 0x7b,
+ 0x1d, 0x2a, 0xbe, 0x34, 0xb2, 0x0d, 0xb7, 0x85, 0x9b, 0xeb, 0x28, 0xca, 0xc0, 0xa6, 0xb0, 0xb4,
+ 0x3f, 0xcd, 0x43, 0x2d, 0x58, 0x10, 0x1f, 0xe0, 0x9c, 0x69, 0x07, 0x6a, 0x41, 0x7c, 0x55, 0xe6,
+ 0xdb, 0xf6, 0xc2, 0xb0, 0x1f, 0xbe, 0x86, 0x0b, 0x5e, 0x31, 0xe4, 0x11, 0x8d, 0xdd, 0x2a, 0x64,
+ 0x88, 0xdd, 0xea, 0x42, 0xc5, 0x77, 0xcd, 0x76, 0x5b, 0x6a, 0xe7, 0x59, 0x82, 0xb7, 0x02, 0x71,
+ 0xad, 0x09, 0x40, 0x29, 0x59, 0xf1, 0x82, 0x8a, 0x8d, 0xf6, 0x1a, 0x8c, 0x27, 0x29, 0xb9, 0xea,
+ 0x6a, 0x6c, 0xd1, 0x56, 0xcf, 0x52, 0x32, 0x0e, 0x55, 0x57, 0x99, 0x8e, 0x01, 0x05, 0x5b, 0xbe,
+ 0xb2, 0xdf, 0xf4, 0x86, 0x63, 0x2b, 0xf5, 0x91, 0xaf, 0x02, 0xd6, 0x64, 0x1a, 0x06, 0xb9, 0xda,
+ 0x5f, 0x15, 0xe0, 0x6c, 0x68, 0xd6, 0x58, 0xd1, 0x6d, 0xbd, 0x7d, 0x80, 0x2b, 0xd6, 0x3e, 0xd8,
+ 0x14, 0x73, 0xd8, 0xcb, 0x02, 0x0a, 0x0f, 0xc0, 0x65, 0x01, 0xff, 0x90, 0x07, 0x1e, 0x0b, 0x4a,
+ 0xbe, 0x0a, 0x23, 0x7a, 0xe4, 0x76, 0x4d, 0xf9, 0x3b, 0x2f, 0x67, 0xfe, 0x9d, 0x3c, 0xe4, 0x34,
+ 0x88, 0x6d, 0x8a, 0xa6, 0x62, 0x8c, 0x21, 0x71, 0xa0, 0xba, 0xa9, 0x5b, 0x16, 0xd3, 0x85, 0x32,
+ 0xbb, 0x69, 0x62, 0xcc, 0x79, 0x33, 0x5f, 0x90, 0xd0, 0x18, 0x30, 0x21, 0x6f, 0xe6, 0x60, 0xd4,
+ 0x8d, 0x2e, 0x93, 0xe4, 0x0f, 0xc9, 0xe2, 0xc4, 0x8f, 0xa0, 0x45, 0x23, 0xa9, 0xa2, 0x6b, 0xb1,
+ 0x38, 0x4f, 0xed, 0x2f, 0x73, 0x30, 0xda, 0xb4, 0xcc, 0x96, 0x69, 0xb7, 0x8f, 0xf1, 0xae, 0x82,
+ 0x1b, 0x50, 0xf2, 0x2c, 0xb3, 0x45, 0x87, 0x9c, 0x4d, 0xc4, 0x3c, 0xc6, 0x00, 0x50, 0xe0, 0xc4,
+ 0x2f, 0x3f, 0x28, 0x1c, 0xe0, 0xf2, 0x83, 0x5f, 0x95, 0x41, 0x46, 0x35, 0x93, 0x1e, 0xd4, 0xda,
+ 0xea, 0x4c, 0x75, 0xf9, 0x8d, 0x57, 0x32, 0x9c, 0xc7, 0x17, 0x3b, 0x9d, 0x5d, 0x8c, 0xfd, 0x41,
+ 0x22, 0x86, 0x9c, 0x08, 0x8d, 0x5f, 0xeb, 0x3a, 0x9f, 0xf1, 0x5a, 0x57, 0xc1, 0xae, 0xff, 0x62,
+ 0x57, 0x1d, 0x8a, 0x5b, 0xbe, 0xdf, 0x95, 0x8d, 0x69, 0xf8, 0xb0, 0xf5, 0xf0, 0x48, 0x18, 0xa1,
+ 0x13, 0xb1, 0x77, 0xe4, 0xd0, 0x8c, 0x85, 0xad, 0x07, 0xd7, 0x78, 0xcd, 0x65, 0x0a, 0x18, 0x88,
+ 0xb2, 0x60, 0xef, 0xc8, 0xa1, 0xc9, 0x57, 0xa0, 0xee, 0xbb, 0xba, 0xed, 0x6d, 0x3a, 0x6e, 0x87,
+ 0xba, 0x72, 0x8d, 0xba, 0x90, 0xe1, 0x66, 0xd3, 0xb5, 0x10, 0x4d, 0x78, 0x22, 0x63, 0x49, 0x18,
+ 0xe5, 0x46, 0xb6, 0xa1, 0xda, 0x6b, 0x89, 0x8a, 0x49, 0xf3, 0xd3, 0x6c, 0x96, 0xcb, 0x6a, 0x23,
+ 0xe1, 0x00, 0xea, 0x0d, 0x03, 0x06, 0xf1, 0x1b, 0xeb, 0x2a, 0x47, 0x75, 0x63, 0x5d, 0xb4, 0x35,
+ 0xa6, 0x9d, 0x57, 0x41, 0x3a, 0x52, 0xaf, 0xb5, 0xdb, 0x32, 0x9a, 0x69, 0x21, 0xb3, 0xca, 0x29,
+ 0x58, 0xd6, 0x03, 0xdd, 0xd8, 0x6e, 0xa3, 0xe2, 0xa1, 0x75, 0x40, 0x7a, 0x09, 0x88, 0x11, 0xbb,
+ 0xd7, 0x45, 0x6c, 0xa2, 0x9a, 0x3e, 0xd8, 0x78, 0x10, 0x5c, 0x30, 0x12, 0x39, 0x57, 0x3a, 0xf5,
+ 0x02, 0x17, 0xed, 0xcf, 0xf2, 0x50, 0x58, 0x5b, 0x6e, 0x8a, 0xb3, 0x22, 0xf9, 0xa5, 0x49, 0xb4,
+ 0xb9, 0x6d, 0x76, 0x6f, 0x52, 0xd7, 0xdc, 0xdc, 0x95, 0x4b, 0xef, 0xc8, 0x59, 0x91, 0x49, 0x0a,
+ 0x4c, 0x29, 0x45, 0x5e, 0x81, 0x11, 0x43, 0x9f, 0xa3, 0xae, 0x3f, 0x8c, 0x61, 0x81, 0xef, 0x16,
+ 0x9d, 0x9b, 0x0d, 0x8b, 0x63, 0x0c, 0x8c, 0xac, 0x03, 0x18, 0x21, 0x74, 0xe1, 0xd0, 0xe6, 0x90,
+ 0x08, 0x70, 0x04, 0x88, 0x20, 0xd4, 0xb6, 0x19, 0x29, 0x47, 0x2d, 0x1e, 0x06, 0x95, 0xb7, 0x9c,
+ 0x6b, 0xaa, 0x2c, 0x86, 0x30, 0x9a, 0x0d, 0xa3, 0xb1, 0xcb, 0x5e, 0xc8, 0x27, 0xa1, 0xea, 0x74,
+ 0x23, 0xc3, 0x69, 0x8d, 0xc7, 0x4d, 0x56, 0x6f, 0xc8, 0xb4, 0x3b, 0x7b, 0x93, 0xa3, 0xcb, 0x4e,
+ 0xdb, 0x34, 0x54, 0x02, 0x06, 0xe4, 0x44, 0x83, 0x32, 0xdf, 0xe2, 0xa5, 0xae, 0x7a, 0xe1, 0x73,
+ 0x07, 0xbf, 0x8d, 0xc1, 0x43, 0x99, 0xa3, 0x7d, 0xad, 0x08, 0xa1, 0x6f, 0x8d, 0x78, 0x50, 0x16,
+ 0x21, 0xec, 0x72, 0xe4, 0x3e, 0xd6, 0x68, 0x79, 0xc9, 0x8a, 0xb4, 0xa1, 0xf0, 0x9a, 0xb3, 0x91,
+ 0x79, 0xe0, 0x8e, 0xec, 0xed, 0x16, 0xb6, 0xb2, 0x48, 0x02, 0x32, 0x0e, 0xe4, 0xbf, 0xe7, 0xe0,
+ 0xa4, 0x97, 0x54, 0x7d, 0x65, 0x73, 0xc0, 0xec, 0x3a, 0x7e, 0x52, 0x99, 0x96, 0x01, 0xae, 0x83,
+ 0xb2, 0xb1, 0xbf, 0x2e, 0x4c, 0xfe, 0xc2, 0xe9, 0x25, 0x9b, 0xd3, 0x62, 0xc6, 0x0b, 0x0a, 0xe3,
+ 0xf2, 0x8f, 0xa7, 0xa1, 0x64, 0xa5, 0x7d, 0x23, 0x0f, 0xf5, 0xc8, 0x68, 0x9d, 0xf9, 0x06, 0xa1,
+ 0xdb, 0x89, 0x1b, 0x84, 0x56, 0x87, 0xf7, 0x01, 0x87, 0xb5, 0x3a, 0xee, 0x4b, 0x84, 0x7e, 0x2f,
+ 0x0f, 0x85, 0xf5, 0xf9, 0x85, 0xf8, 0xa2, 0x35, 0x77, 0x1f, 0x16, 0xad, 0x5b, 0x50, 0xd9, 0xe8,
+ 0x99, 0x96, 0x6f, 0xda, 0x99, 0x4f, 0x9f, 0x50, 0x17, 0x2e, 0x49, 0x1f, 0x83, 0x40, 0x45, 0x05,
+ 0x4f, 0xda, 0x50, 0x69, 0x8b, 0xe3, 0xff, 0x32, 0x47, 0xc6, 0xc9, 0x63, 0x04, 0x05, 0x23, 0xf9,
+ 0x82, 0x0a, 0x5d, 0xdb, 0x05, 0x79, 0x73, 0xfc, 0x7d, 0x97, 0xa6, 0xf6, 0x15, 0x08, 0xb4, 0x80,
+ 0xfb, 0xcf, 0xfc, 0x6f, 0x72, 0x10, 0x57, 0x7c, 0xee, 0x7f, 0x6b, 0xda, 0x4e, 0xb6, 0xa6, 0xf9,
+ 0xa3, 0xe8, 0x7c, 0xe9, 0x0d, 0x4a, 0xfb, 0xcd, 0x3c, 0x94, 0xef, 0xdb, 0x8e, 0x61, 0x1a, 0x0b,
+ 0xf2, 0x9b, 0xcb, 0x38, 0x30, 0x0e, 0x0c, 0xf1, 0xeb, 0x24, 0x42, 0xfc, 0xb2, 0x5e, 0x11, 0x7b,
+ 0x8f, 0x00, 0xbf, 0x3f, 0xca, 0x81, 0x1c, 0x96, 0x97, 0x6c, 0xcf, 0xd7, 0x6d, 0x83, 0x12, 0x23,
+ 0x98, 0x03, 0xb2, 0x46, 0x92, 0xc8, 0x68, 0x2b, 0x31, 0xed, 0xf3, 0x67, 0x35, 0xe6, 0x93, 0x8f,
+ 0x43, 0x75, 0xcb, 0xf1, 0x7c, 0x3e, 0xce, 0xe7, 0xe3, 0xd6, 0xa5, 0x2b, 0x32, 0x1d, 0x03, 0x8a,
+ 0xa4, 0xc7, 0xb5, 0x34, 0xd8, 0xe3, 0xaa, 0x7d, 0x37, 0x0f, 0x23, 0xef, 0x97, 0x6d, 0xcf, 0x69,
+ 0x21, 0x91, 0x85, 0x8c, 0x21, 0x91, 0xc5, 0xc3, 0x84, 0x44, 0x6a, 0x3f, 0xce, 0x01, 0xdc, 0xb7,
+ 0x3d, 0xd7, 0xad, 0x78, 0xb4, 0x62, 0xe6, 0x76, 0x95, 0x1e, 0xab, 0xf8, 0xff, 0x4b, 0xea, 0x93,
+ 0x78, 0xa4, 0xe2, 0x5b, 0x39, 0x18, 0xd3, 0x63, 0xd1, 0x7f, 0x99, 0x55, 0xcb, 0x44, 0x30, 0x61,
+ 0xb0, 0xbf, 0x34, 0x9e, 0x8e, 0x09, 0xb6, 0xe4, 0xf9, 0xf0, 0xbc, 0xdf, 0xeb, 0x61, 0xb3, 0xef,
+ 0x3b, 0xa8, 0x97, 0xab, 0x39, 0x31, 0xca, 0x7b, 0x44, 0x5b, 0x16, 0x8e, 0x24, 0xda, 0x32, 0xba,
+ 0x8f, 0xac, 0x78, 0xd7, 0x7d, 0x64, 0x3b, 0x50, 0xdb, 0x74, 0x9d, 0x0e, 0x0f, 0x68, 0x94, 0x97,
+ 0xcb, 0x5e, 0xce, 0x30, 0xa7, 0x84, 0xd7, 0xaa, 0x87, 0x36, 0x9e, 0x05, 0x85, 0x8f, 0x21, 0x2b,
+ 0x6e, 0x16, 0x77, 0x04, 0xd7, 0xf2, 0x51, 0x72, 0x0d, 0xc6, 0x92, 0x35, 0x81, 0x8e, 0x8a, 0x4d,
+ 0x3c, 0x88, 0xb1, 0x72, 0x7f, 0x82, 0x18, 0xb5, 0xef, 0x97, 0xd5, 0x00, 0xf6, 0xc0, 0x1d, 0x2d,
+ 0xf9, 0xfe, 0xdf, 0xab, 0x9b, 0xdc, 0x48, 0x5b, 0xb9, 0x8f, 0x1b, 0x69, 0xab, 0x47, 0xb3, 0x91,
+ 0xb6, 0x96, 0x6d, 0x23, 0x2d, 0x64, 0xdf, 0x48, 0x5b, 0xcf, 0xb6, 0x91, 0x76, 0x64, 0xa8, 0x8d,
+ 0xb4, 0xa3, 0x07, 0xda, 0x48, 0xbb, 0x57, 0x80, 0xc4, 0x2a, 0xf3, 0x03, 0x8f, 0xd2, 0x3f, 0x29,
+ 0x8f, 0xd2, 0xdb, 0x79, 0x08, 0x87, 0xcd, 0x43, 0x46, 0xdc, 0xbc, 0x0c, 0xd5, 0x8e, 0x7e, 0x7b,
+ 0x9e, 0x5a, 0xfa, 0x6e, 0x96, 0xfb, 0x53, 0x57, 0x24, 0x06, 0x06, 0x68, 0xc4, 0x03, 0x30, 0x83,
+ 0x33, 0xcc, 0x33, 0xdb, 0xe6, 0xc3, 0xe3, 0xd0, 0x85, 0xf5, 0x2f, 0x7c, 0xc7, 0x08, 0x1b, 0xed,
+ 0x0f, 0xf3, 0x20, 0x0f, 0xbb, 0x27, 0x14, 0x4a, 0x9b, 0xe6, 0x6d, 0xda, 0xca, 0x1c, 0x3f, 0x1b,
+ 0xb9, 0xd5, 0x5a, 0x38, 0x1f, 0x78, 0x02, 0x0a, 0x74, 0x6e, 0x55, 0x16, 0xce, 0x24, 0x29, 0xbf,
+ 0x0c, 0x56, 0xe5, 0xa8, 0x53, 0x4a, 0x5a, 0x95, 0x45, 0x12, 0x2a, 0x1e, 0xc2, 0x88, 0xcd, 0xe3,
+ 0x0a, 0x32, 0xfb, 0xce, 0x62, 0xf1, 0x09, 0xca, 0x88, 0xed, 0x89, 0x9d, 0xf4, 0x92, 0x47, 0xe3,
+ 0x4b, 0x3f, 0xfa, 0xc9, 0x85, 0x87, 0x7e, 0xfc, 0x93, 0x0b, 0x0f, 0xbd, 0xfb, 0x93, 0x0b, 0x0f,
+ 0x7d, 0x6d, 0xff, 0x42, 0xee, 0x47, 0xfb, 0x17, 0x72, 0x3f, 0xde, 0xbf, 0x90, 0x7b, 0x77, 0xff,
+ 0x42, 0xee, 0xcf, 0xf7, 0x2f, 0xe4, 0xfe, 0xcb, 0x5f, 0x5c, 0x78, 0xe8, 0x8b, 0xcf, 0x85, 0x55,
+ 0x98, 0x56, 0x55, 0x98, 0x56, 0x0c, 0xa7, 0xbb, 0xdb, 0xed, 0x69, 0x56, 0x85, 0x30, 0x45, 0x55,
+ 0xe1, 0x1f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x32, 0x6d, 0x43, 0x50, 0x97, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -6474,6 +6479,22 @@ func (m *MonoVertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ i -= len(m.UpdateHash)
+ copy(dAtA[i:], m.UpdateHash)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.UpdateHash)))
+ i--
+ dAtA[i] = 0x72
+ i -= len(m.CurrentHash)
+ copy(dAtA[i:], m.CurrentHash)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.CurrentHash)))
+ i--
+ dAtA[i] = 0x6a
+ i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReplicas))
+ i--
+ dAtA[i] = 0x60
+ i = encodeVarintGenerated(dAtA, i, uint64(m.CurrentReplicas))
+ i--
+ dAtA[i] = 0x58
i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas))
i--
dAtA[i] = 0x50
@@ -8947,6 +8968,22 @@ func (m *VertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ i -= len(m.UpdateHash)
+ copy(dAtA[i:], m.UpdateHash)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.UpdateHash)))
+ i--
+ dAtA[i] = 0x6a
+ i -= len(m.CurrentHash)
+ copy(dAtA[i:], m.CurrentHash)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.CurrentHash)))
+ i--
+ dAtA[i] = 0x62
+ i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReplicas))
+ i--
+ dAtA[i] = 0x58
+ i = encodeVarintGenerated(dAtA, i, uint64(m.CurrentReplicas))
+ i--
+ dAtA[i] = 0x50
i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas))
i--
dAtA[i] = 0x48
@@ -10390,6 +10427,12 @@ func (m *MonoVertexStatus) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.ObservedGeneration))
n += 1 + sovGenerated(uint64(m.ReadyReplicas))
+ n += 1 + sovGenerated(uint64(m.CurrentReplicas))
+ n += 1 + sovGenerated(uint64(m.UpdatedReplicas))
+ l = len(m.CurrentHash)
+ n += 1 + l + sovGenerated(uint64(l))
+ l = len(m.UpdateHash)
+ n += 1 + l + sovGenerated(uint64(l))
return n
}
@@ -11305,6 +11348,12 @@ func (m *VertexStatus) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.ObservedGeneration))
n += 1 + sovGenerated(uint64(m.ReadyReplicas))
+ n += 1 + sovGenerated(uint64(m.CurrentReplicas))
+ n += 1 + sovGenerated(uint64(m.UpdatedReplicas))
+ l = len(m.CurrentHash)
+ n += 1 + l + sovGenerated(uint64(l))
+ l = len(m.UpdateHash)
+ n += 1 + l + sovGenerated(uint64(l))
return n
}
@@ -12229,6 +12278,10 @@ func (this *MonoVertexStatus) String() string {
`LastScaledAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.LastScaledAt), "Time", "v11.Time", 1), `&`, ``, 1) + `,`,
`ObservedGeneration:` + fmt.Sprintf("%v", this.ObservedGeneration) + `,`,
`ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`,
+ `CurrentReplicas:` + fmt.Sprintf("%v", this.CurrentReplicas) + `,`,
+ `UpdatedReplicas:` + fmt.Sprintf("%v", this.UpdatedReplicas) + `,`,
+ `CurrentHash:` + fmt.Sprintf("%v", this.CurrentHash) + `,`,
+ `UpdateHash:` + fmt.Sprintf("%v", this.UpdateHash) + `,`,
`}`,
}, "")
return s
@@ -12826,6 +12879,10 @@ func (this *VertexStatus) String() string {
`LastScaledAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.LastScaledAt), "Time", "v11.Time", 1), `&`, ``, 1) + `,`,
`ObservedGeneration:` + fmt.Sprintf("%v", this.ObservedGeneration) + `,`,
`ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`,
+ `CurrentReplicas:` + fmt.Sprintf("%v", this.CurrentReplicas) + `,`,
+ `UpdatedReplicas:` + fmt.Sprintf("%v", this.UpdatedReplicas) + `,`,
+ `CurrentHash:` + fmt.Sprintf("%v", this.CurrentHash) + `,`,
+ `UpdateHash:` + fmt.Sprintf("%v", this.UpdateHash) + `,`,
`}`,
}, "")
return s
@@ -23630,6 +23687,108 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
+ case 11:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field CurrentReplicas", wireType)
+ }
+ m.CurrentReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.CurrentReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 12:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType)
+ }
+ m.UpdatedReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.UpdatedReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 13:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field CurrentHash", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ 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 ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.CurrentHash = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 14:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdateHash", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ 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 ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.UpdateHash = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -30950,6 +31109,108 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
+ case 10:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field CurrentReplicas", wireType)
+ }
+ m.CurrentReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.CurrentReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 11:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType)
+ }
+ m.UpdatedReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.UpdatedReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 12:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field CurrentHash", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ 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 ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.CurrentHash = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 13:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdateHash", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ 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 ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.UpdateHash = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 9c513d6fde..8fadbab191 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -977,6 +977,18 @@ message MonoVertexStatus {
// The number of pods targeted by this MonoVertex with a Ready Condition.
// +optional
optional uint32 readyReplicas = 10;
+
+ // The number of Pods created by the controller from the MonoVertex version indicated by currentHash.
+ optional uint32 currentReplicas = 11;
+
+ // The number of Pods created by the controller from the MonoVertex version indicated by updateHash.
+ optional uint32 updatedReplicas = 12;
+
+ // If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).
+ optional string currentHash = 13;
+
+ // If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ optional string updateHash = 14;
}
message NativeRedis {
@@ -1697,6 +1709,18 @@ message VertexStatus {
// The number of pods targeted by this Vertex with a Ready Condition.
// +optional
optional uint32 readyReplicas = 9;
+
+ // The number of Pods created by the controller from the Vertex version indicated by currentHash.
+ optional uint32 currentReplicas = 10;
+
+ // The number of Pods created by the controller from the Vertex version indicated by updateHash.
+ optional uint32 updatedReplicas = 11;
+
+ // If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).
+ optional string currentHash = 12;
+
+ // If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ optional string updateHash = 13;
}
message VertexTemplate {
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index 15544b81b6..b05fd8c5f4 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -487,6 +487,14 @@ type MonoVertexStatus struct {
// The number of pods targeted by this MonoVertex with a Ready Condition.
// +optional
ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,10,opt,name=readyReplicas"`
+ // The number of Pods created by the controller from the MonoVertex version indicated by currentHash.
+ CurrentReplicas uint32 `json:"currentReplicas,omitempty" protobuf:"varint,11,opt,name=currentReplicas"`
+ // The number of Pods created by the controller from the MonoVertex version indicated by updateHash.
+ UpdatedReplicas uint32 `json:"updatedReplicas,omitempty" protobuf:"varint,12,opt,name=updatedReplicas"`
+ // If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).
+ CurrentHash string `json:"currentHash,omitempty" protobuf:"bytes,13,opt,name=currentHash"`
+ // If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ UpdateHash string `json:"updateHash,omitempty" protobuf:"bytes,14,opt,name=updateHash"`
}
// SetObservedGeneration sets the Status ObservedGeneration
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 79aab81c33..bb128213f0 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -3423,6 +3423,34 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexStatus(ref common.ReferenceCall
Format: "int64",
},
},
+ "currentReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of Pods created by the controller from the MonoVertex version indicated by currentHash.",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ "updatedReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of Pods created by the controller from the MonoVertex version indicated by updateHash.",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ "currentHash": {
+ SchemaProps: spec.SchemaProps{
+ Description: "If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "updateHash": {
+ SchemaProps: spec.SchemaProps{
+ Description: "If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
},
},
},
@@ -5770,6 +5798,34 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexStatus(ref common.ReferenceCallback
Format: "int64",
},
},
+ "currentReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of Pods created by the controller from the Vertex version indicated by currentHash.",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ "updatedReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of Pods created by the controller from the Vertex version indicated by updateHash.",
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
+ "currentHash": {
+ SchemaProps: spec.SchemaProps{
+ Description: "If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "updateHash": {
+ SchemaProps: spec.SchemaProps{
+ Description: "If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
},
},
},
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types.go b/pkg/apis/numaflow/v1alpha1/vertex_types.go
index 2e78357bb9..0b5ec7efc9 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types.go
@@ -730,6 +730,14 @@ type VertexStatus struct {
// The number of pods targeted by this Vertex with a Ready Condition.
// +optional
ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,9,opt,name=readyReplicas"`
+ // The number of Pods created by the controller from the Vertex version indicated by currentHash.
+ CurrentReplicas uint32 `json:"currentReplicas,omitempty" protobuf:"varint,10,opt,name=currentReplicas"`
+ // The number of Pods created by the controller from the Vertex version indicated by updateHash.
+ UpdatedReplicas uint32 `json:"updatedReplicas,omitempty" protobuf:"varint,11,opt,name=updatedReplicas"`
+ // If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).
+ CurrentHash string `json:"currentHash,omitempty" protobuf:"bytes,12,opt,name=currentHash"`
+ // If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ UpdateHash string `json:"updateHash,omitempty" protobuf:"bytes,13,opt,name=updateHash"`
}
func (vs *VertexStatus) MarkPhase(phase VertexPhase, reason, message string) {
diff --git a/rust/numaflow-models/src/models/mono_vertex_status.rs b/rust/numaflow-models/src/models/mono_vertex_status.rs
index b0773192e8..bd2fdee807 100644
--- a/rust/numaflow-models/src/models/mono_vertex_status.rs
+++ b/rust/numaflow-models/src/models/mono_vertex_status.rs
@@ -21,6 +21,12 @@ pub struct MonoVertexStatus {
/// Conditions are the latest available observations of a resource's current state.
#[serde(rename = "conditions", skip_serializing_if = "Option::is_none")]
pub conditions: Option>,
+ /// If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).
+ #[serde(rename = "currentHash", skip_serializing_if = "Option::is_none")]
+ pub current_hash: Option,
+ /// The number of Pods created by the controller from the MonoVertex version indicated by currentHash.
+ #[serde(rename = "currentReplicas", skip_serializing_if = "Option::is_none")]
+ pub current_replicas: Option,
#[serde(rename = "lastScaledAt", skip_serializing_if = "Option::is_none")]
pub last_scaled_at: Option,
#[serde(rename = "lastUpdated", skip_serializing_if = "Option::is_none")]
@@ -42,12 +48,20 @@ pub struct MonoVertexStatus {
pub replicas: Option,
#[serde(rename = "selector", skip_serializing_if = "Option::is_none")]
pub selector: Option,
+ /// If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ #[serde(rename = "updateHash", skip_serializing_if = "Option::is_none")]
+ pub update_hash: Option,
+ /// The number of Pods created by the controller from the MonoVertex version indicated by updateHash.
+ #[serde(rename = "updatedReplicas", skip_serializing_if = "Option::is_none")]
+ pub updated_replicas: Option,
}
impl MonoVertexStatus {
pub fn new() -> MonoVertexStatus {
MonoVertexStatus {
conditions: None,
+ current_hash: None,
+ current_replicas: None,
last_scaled_at: None,
last_updated: None,
message: None,
@@ -57,6 +71,8 @@ impl MonoVertexStatus {
reason: None,
replicas: None,
selector: None,
+ update_hash: None,
+ updated_replicas: None,
}
}
}
diff --git a/rust/numaflow-models/src/models/vertex_status.rs b/rust/numaflow-models/src/models/vertex_status.rs
index 326a83c9b7..950ffa9ba5 100644
--- a/rust/numaflow-models/src/models/vertex_status.rs
+++ b/rust/numaflow-models/src/models/vertex_status.rs
@@ -21,6 +21,12 @@ pub struct VertexStatus {
/// Conditions are the latest available observations of a resource's current state.
#[serde(rename = "conditions", skip_serializing_if = "Option::is_none")]
pub conditions: Option>,
+ /// If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).
+ #[serde(rename = "currentHash", skip_serializing_if = "Option::is_none")]
+ pub current_hash: Option,
+ /// The number of Pods created by the controller from the Vertex version indicated by currentHash.
+ #[serde(rename = "currentReplicas", skip_serializing_if = "Option::is_none")]
+ pub current_replicas: Option,
#[serde(rename = "lastScaledAt", skip_serializing_if = "Option::is_none")]
pub last_scaled_at: Option,
#[serde(rename = "message", skip_serializing_if = "Option::is_none")]
@@ -40,12 +46,20 @@ pub struct VertexStatus {
pub replicas: Option,
#[serde(rename = "selector", skip_serializing_if = "Option::is_none")]
pub selector: Option,
+ /// If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ #[serde(rename = "updateHash", skip_serializing_if = "Option::is_none")]
+ pub update_hash: Option,
+ /// The number of Pods created by the controller from the Vertex version indicated by updateHash.
+ #[serde(rename = "updatedReplicas", skip_serializing_if = "Option::is_none")]
+ pub updated_replicas: Option,
}
impl VertexStatus {
pub fn new() -> VertexStatus {
VertexStatus {
conditions: None,
+ current_hash: None,
+ current_replicas: None,
last_scaled_at: None,
message: None,
observed_generation: None,
@@ -54,6 +68,8 @@ impl VertexStatus {
reason: None,
replicas: None,
selector: None,
+ update_hash: None,
+ updated_replicas: None,
}
}
}
From 0814d1f542d165918e36a1504091f995d35524a6 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 2 Sep 2024 21:38:34 -0700
Subject: [PATCH 042/188] docs: updated CHANGELOG.md (#2021)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
CHANGELOG.md | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b2f607fbc..bbc4a1a7b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,35 @@
# Changelog
+## v1.3.1 (2024-09-02)
+
+ * [a42d0063](https://github.com/numaproj/numaflow/commit/a42d0063caf53d6f4c01c2fb2f6f6f6f74a8f987) Update manifests to v1.3.1
+ * [6993e75f](https://github.com/numaproj/numaflow/commit/6993e75f546f2ffc6db1ecbb0fc579a5d6048754) feat: allow configurable retryStrategy (#2010)
+ * [6c973698](https://github.com/numaproj/numaflow/commit/6c973698762488915df719161ec4a70a130b4bea) chore(deps): bump webpack from 5.93.0 to 5.94.0 in /ui (#2018)
+ * [cd54e86f](https://github.com/numaproj/numaflow/commit/cd54e86f7d42641182531df3823baecece0ee57c) fix: add latency metrics for mvtx (#2013)
+ * [c6530d37](https://github.com/numaproj/numaflow/commit/c6530d37efce9a1a7ffd153cde104180b2c0b287) feat: introduce `readyReplicas` for Vertex and MonoVertex (#2014)
+ * [13c13e5f](https://github.com/numaproj/numaflow/commit/13c13e5f1a36957b11219cac49ad8e872bd290be) feat: enable resourceClaims for vertex and monovtx (#2009)
+ * [1040a022](https://github.com/numaproj/numaflow/commit/1040a0223ad54ce619e6b33eeb5b99bf341d807d) fix: log format with config load error (#2000)
+ * [8d2a4b21](https://github.com/numaproj/numaflow/commit/8d2a4b21fe18085ed12303a604019dc88fca4665) feat: more flexible scaling with `replicasPerScaleUp` and `replicasPerScaleDown` (#2003)
+ * [9e54b2cd](https://github.com/numaproj/numaflow/commit/9e54b2cdaa75f9679dac2f37a0a7df88a39b481f) chore(deps): bump micromatch from 4.0.7 to 4.0.8 in /ui (#2002)
+ * [d841421f](https://github.com/numaproj/numaflow/commit/d841421f7d09da448cae10a45fa91a3bf9013d5c) fix: e2e testing isbsvc deletion timeout issue (#1997)
+ * [991bfb70](https://github.com/numaproj/numaflow/commit/991bfb701195ed2c6bfbc01f2ce8af99bfc5d763) fix: test coverage generation for Rust code (#1993)
+ * [a39746c1](https://github.com/numaproj/numaflow/commit/a39746c118791a37725f41241da4b3a9a03fa5a5) fix: do not pass scale info to MonoVertex (#1990)
+ * [0dcd9284](https://github.com/numaproj/numaflow/commit/0dcd9284d6a46869d81281a7e267a59b51282148) fix: adding not available for negative processing rates (#1983)
+ * [c49fdb9a](https://github.com/numaproj/numaflow/commit/c49fdb9af350b37aed7ef9b5b3d491cd85fe14a0) fix: minor perf improvements of mvtx fallback sink (#1967)
+ * [24239fc1](https://github.com/numaproj/numaflow/commit/24239fc1cc5a834621904cc12186b9d4dd51f950) fix: remove coloring in logs (#1975)
+ * [26b0d1db](https://github.com/numaproj/numaflow/commit/26b0d1dbdba51da944604cbae11029727ee3b26e) doc: update roadmap (#1970)
+
+### Contributors
+
+ * Derek Wang
+ * Keran Yang
+ * Sidhant Kohli
+ * Sreekanth
+ * Vedant Gupta
+ * Vigith Maurice
+ * dependabot[bot]
+ * xdevxy
+
## v1.3.0 (2024-08-19)
* [4de121c2](https://github.com/numaproj/numaflow/commit/4de121c2c3b436ac51fba97c8ce5153afc5364c9) Update manifests to v1.3.0
From 1588175fb98214dc1530c3721ed6deee08ac92b6 Mon Sep 17 00:00:00 2001
From: lanedd <43617392+lanedd@users.noreply.github.com>
Date: Tue, 3 Sep 2024 11:28:43 -0700
Subject: [PATCH 043/188] chore: added Seekr to USERS.md (#2022)
Signed-off-by: Lane Dalan
---
USERS.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/USERS.md b/USERS.md
index ce6cdd45e5..002bcc4440 100644
--- a/USERS.md
+++ b/USERS.md
@@ -7,3 +7,4 @@ Please add your company name and initial use case (optional) below.
3. [Atlan](https://atlan.com/) - Numaflow powers real time notifications, stream processing ecosystem at Atlan.
4. [Valegachain Analytics](https://www.valegachain.com/) Numaflow is used to extract, transform, and load cryptocurrency blocks and mempool transactions in data lakes, as well as for activity alerts.
5. [Lockheed Martin](https://lockheedmartin.com/) Perform ELT processing on high and low volume data streams of sensor data as recieved from IOT type systems.
+6. [Seekr](https://www.seekr.com/) Numaflow coordinates multiple ML pipelines to rate and extract information from the pipeline input.
From 14cdff5a363c758c35f5451b380ed6269459384a Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Tue, 3 Sep 2024 14:59:28 -0400
Subject: [PATCH 044/188] chore: reduce 5 second wait time every time redis
sink check runs in e2e (#2023)
Signed-off-by: Keran Yang
---
test/fixtures/redis_check.go | 3 +++
test/reduce-two-e2e/reduce_two_test.go | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/test/fixtures/redis_check.go b/test/fixtures/redis_check.go
index b784aa2d61..3b230a454a 100644
--- a/test/fixtures/redis_check.go
+++ b/test/fixtures/redis_check.go
@@ -100,6 +100,9 @@ func runChecks(ctx context.Context, performChecks CheckFunc) bool {
ticker := time.NewTicker(retryInterval)
defer ticker.Stop()
+ if performChecks() {
+ return true
+ }
for {
select {
case <-ctx.Done():
diff --git a/test/reduce-two-e2e/reduce_two_test.go b/test/reduce-two-e2e/reduce_two_test.go
index c4749569a0..115e54e92c 100644
--- a/test/reduce-two-e2e/reduce_two_test.go
+++ b/test/reduce-two-e2e/reduce_two_test.go
@@ -83,7 +83,7 @@ func (r *ReduceSuite) testReduceStream(lang string) {
// There should be no other values.
w.Expect().RedisSinkContains(pipelineName+"-sink", "102")
w.Expect().RedisSinkNotContains(pipelineName+"-sink", "99")
- w.Expect().RedisSinkNotContains(pipelineName+"sink", "105")
+ w.Expect().RedisSinkNotContains(pipelineName+"-sink", "105")
done <- struct{}{}
}
From 8d8b9e20b37ee214b33a06fed9fc54bab3b9a94d Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Tue, 3 Sep 2024 12:07:40 -0700
Subject: [PATCH 045/188] chore: convert onFailureStrategy to enum in rust
(#2020)
Signed-off-by: Sidhant Kohli
---
rust/monovertex/src/config.rs | 222 +++++++++++++++++++++++++++++--
rust/monovertex/src/forwarder.rs | 17 +--
2 files changed, 218 insertions(+), 21 deletions(-)
diff --git a/rust/monovertex/src/config.rs b/rust/monovertex/src/config.rs
index d1450500be..81b115422f 100644
--- a/rust/monovertex/src/config.rs
+++ b/rust/monovertex/src/config.rs
@@ -19,7 +19,47 @@ const DEFAULT_BATCH_SIZE: u64 = 500;
const DEFAULT_TIMEOUT_IN_MS: u32 = 1000;
const DEFAULT_MAX_SINK_RETRY_ATTEMPTS: u16 = u16::MAX;
const DEFAULT_SINK_RETRY_INTERVAL_IN_MS: u32 = 1;
-const DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY: &str = "retry";
+const DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY: OnFailureStrategy = OnFailureStrategy::Retry;
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum OnFailureStrategy {
+ Retry,
+ Fallback,
+ Drop,
+}
+
+impl OnFailureStrategy {
+ /// Converts a string slice to an `OnFailureStrategy` enum variant.
+ /// Case insensitivity is considered to enhance usability.
+ ///
+ /// # Arguments
+ /// * `s` - A string slice representing the retry strategy.
+ ///
+ /// # Returns
+ /// An option containing the corresponding enum variant if successful,
+ /// or DefaultStrategy if the input does not match known variants.
+ fn from_str(s: &str) -> Option {
+ match s.to_lowercase().as_str() {
+ "retry" => Some(OnFailureStrategy::Retry),
+ "fallback" => Some(OnFailureStrategy::Fallback),
+ "drop" => Some(OnFailureStrategy::Drop),
+ _ => Some(DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY),
+ }
+ }
+
+ /// Converts the `OnFailureStrategy` enum variant to a String.
+ /// This facilitates situations where the enum needs to be displayed or logged as a string.
+ ///
+ /// # Returns
+ /// A string representing the `OnFailureStrategy` enum variant.
+ fn to_string(&self) -> String {
+ match *self {
+ OnFailureStrategy::Retry => "retry".to_string(),
+ OnFailureStrategy::Fallback => "fallback".to_string(),
+ OnFailureStrategy::Drop => "drop".to_string(),
+ }
+ }
+}
pub fn config() -> &'static Settings {
static CONF: OnceLock = OnceLock::new();
@@ -44,7 +84,7 @@ pub struct Settings {
pub lag_refresh_interval_in_secs: u16,
pub sink_max_retry_attempts: u16,
pub sink_retry_interval_in_ms: u32,
- pub sink_retry_on_fail_strategy: String,
+ pub sink_retry_on_fail_strategy: OnFailureStrategy,
pub sink_default_retry_strategy: RetryStrategy,
}
@@ -73,7 +113,7 @@ impl Default for Settings {
lag_refresh_interval_in_secs: DEFAULT_LAG_REFRESH_INTERVAL_IN_SECS,
sink_max_retry_attempts: DEFAULT_MAX_SINK_RETRY_ATTEMPTS,
sink_retry_interval_in_ms: DEFAULT_SINK_RETRY_INTERVAL_IN_MS,
- sink_retry_on_fail_strategy: DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY.to_string(),
+ sink_retry_on_fail_strategy: DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY,
sink_default_retry_strategy: default_retry_strategy,
}
}
@@ -161,15 +201,16 @@ impl Settings {
}
}
- // Set the retry strategy using a direct reference whenever possible
+ // Set the retry strategy from the spec or use the default
settings.sink_retry_on_fail_strategy = retry_strategy
.on_failure
.clone()
- .unwrap_or_else(|| DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY.to_string());
+ .and_then(|s| OnFailureStrategy::from_str(&s))
+ .unwrap_or(DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY);
// check if the sink retry strategy is set to fallback and there is no fallback sink configured
// then we should return an error
- if settings.sink_retry_on_fail_strategy == "fallback"
+ if settings.sink_retry_on_fail_strategy == OnFailureStrategy::Fallback
&& !settings.is_fallback_enabled
{
return Err(Error::ConfigError(
@@ -198,10 +239,12 @@ impl Settings {
#[cfg(test)]
mod tests {
- use super::*;
- use serde_json::json;
use std::env;
+ use serde_json::json;
+
+ use super::*;
+
#[test]
fn test_settings_load_combined() {
// Define all JSON test configurations in separate scopes to use them distinctively
@@ -303,7 +346,118 @@ mod tests {
// Execute and verify
let settings = Settings::load().unwrap();
- assert_eq!(settings.sink_retry_on_fail_strategy, "retry");
+ assert_eq!(
+ settings.sink_retry_on_fail_strategy,
+ DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY
+ );
+ assert_eq!(settings.sink_max_retry_attempts, 5);
+ assert_eq!(settings.sink_retry_interval_in_ms, 1000);
+ env::remove_var(ENV_MONO_VERTEX_OBJ);
+ }
+
+ {
+ // Test Non default Retry Strategy Load
+ let json_data = json!({
+ "metadata": {
+ "name": "simple-mono-vertex",
+ "namespace": "default",
+ "creationTimestamp": null
+ },
+ "spec": {
+ "replicas": 0,
+ "source": {
+ "udsource": {
+ "container": {
+ "image": "xxxxxxx",
+ "resources": {}
+ }
+ }
+ },
+ "sink": {
+ "udsink": {
+ "container": {
+ "image": "xxxxxx",
+ "resources": {}
+ }
+ },
+ "retryStrategy": {
+ "backoff": {
+ "interval": "1s",
+ "steps": 5
+ },
+ "onFailure": "drop"
+ },
+ },
+ "limits": {
+ "readBatchSize": 500,
+ "readTimeout": "1s"
+ },
+ }
+ });
+ let json_str = json_data.to_string();
+ let encoded_json = BASE64_STANDARD.encode(json_str);
+ env::set_var(ENV_MONO_VERTEX_OBJ, encoded_json);
+
+ // Execute and verify
+ let settings = Settings::load().unwrap();
+ assert_eq!(
+ settings.sink_retry_on_fail_strategy,
+ OnFailureStrategy::Drop
+ );
+ assert_eq!(settings.sink_max_retry_attempts, 5);
+ assert_eq!(settings.sink_retry_interval_in_ms, 1000);
+ env::remove_var(ENV_MONO_VERTEX_OBJ);
+ }
+
+ {
+ // Test Invalid on failure strategy to use default
+ let json_data = json!({
+ "metadata": {
+ "name": "simple-mono-vertex",
+ "namespace": "default",
+ "creationTimestamp": null
+ },
+ "spec": {
+ "replicas": 0,
+ "source": {
+ "udsource": {
+ "container": {
+ "image": "xxxxxxx",
+ "resources": {}
+ }
+ }
+ },
+ "sink": {
+ "udsink": {
+ "container": {
+ "image": "xxxxxx",
+ "resources": {}
+ }
+ },
+ "retryStrategy": {
+ "backoff": {
+ "interval": "1s",
+ "steps": 5
+ },
+ "onFailure": "xxxxx"
+ },
+ },
+ "limits": {
+ "readBatchSize": 500,
+ "readTimeout": "1s"
+ },
+ }
+ });
+ let json_str = json_data.to_string();
+ let encoded_json = BASE64_STANDARD.encode(json_str);
+ env::set_var(ENV_MONO_VERTEX_OBJ, encoded_json);
+
+ // Execute and verify
+ let settings = Settings::load().unwrap();
+ assert_eq!(
+ settings.sink_retry_on_fail_strategy,
+ DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY
+ );
assert_eq!(settings.sink_max_retry_attempts, 5);
assert_eq!(settings.sink_retry_interval_in_ms, 1000);
env::remove_var(ENV_MONO_VERTEX_OBJ);
@@ -407,4 +561,54 @@ mod tests {
// General cleanup
env::remove_var(ENV_GRPC_MAX_MESSAGE_SIZE);
}
+
+ #[test]
+ fn test_on_failure_enum_from_str_valid_inputs() {
+ assert_eq!(
+ OnFailureStrategy::from_str("retry"),
+ Some(OnFailureStrategy::Retry)
+ );
+ assert_eq!(
+ OnFailureStrategy::from_str("fallback"),
+ Some(OnFailureStrategy::Fallback)
+ );
+ assert_eq!(
+ OnFailureStrategy::from_str("drop"),
+ Some(OnFailureStrategy::Drop)
+ );
+
+ // Testing case insensitivity
+ assert_eq!(
+ OnFailureStrategy::from_str("ReTry"),
+ Some(OnFailureStrategy::Retry)
+ );
+ assert_eq!(
+ OnFailureStrategy::from_str("FALLBACK"),
+ Some(OnFailureStrategy::Fallback)
+ );
+ assert_eq!(
+ OnFailureStrategy::from_str("Drop"),
+ Some(OnFailureStrategy::Drop)
+ );
+ }
+
+ #[test]
+ fn test_on_failure_enum_from_str_invalid_input() {
+ assert_eq!(
+ OnFailureStrategy::from_str("unknown"),
+ Some(DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY)
+ ); // should return None for undefined inputs
+ }
+
+ #[test]
+ fn test_on_failure_enum_to_string() {
+ let retry = OnFailureStrategy::Retry;
+ assert_eq!(retry.to_string(), "retry");
+
+ let fallback = OnFailureStrategy::Fallback;
+ assert_eq!(fallback.to_string(), "fallback");
+
+ let drop = OnFailureStrategy::Drop;
+ assert_eq!(drop.to_string(), "drop");
+ }
}
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index 0f55268576..1ba928123c 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -1,6 +1,6 @@
use std::collections::HashMap;
-use crate::config::config;
+use crate::config::{config, OnFailureStrategy};
use crate::error::{Error, Result};
use crate::message::{Message, Offset};
use crate::metrics;
@@ -296,9 +296,9 @@ impl Forwarder {
}
// check what is the failure strategy in the config
let strategy = config().sink_retry_on_fail_strategy.clone();
- match strategy.as_str() {
+ match strategy {
// if we need to retry, return true
- "retry" => {
+ OnFailureStrategy::Retry => {
warn!(
"Using onFailure Retry, Retry attempts {} completed",
attempts
@@ -306,7 +306,7 @@ impl Forwarder {
return Ok(true);
}
// if we need to drop the messages, log and return false
- "drop" => {
+ OnFailureStrategy::Drop => {
// log that we are dropping the messages as requested
warn!(
"Dropping messages after {} attempts. Errors: {:?}",
@@ -319,7 +319,7 @@ impl Forwarder {
.inc_by(messages_to_send.len() as u64);
}
// if we need to move the messages to the fallback, return false
- "fallback" => {
+ OnFailureStrategy::Fallback => {
// log that we are moving the messages to the fallback as requested
warn!(
"Moving messages to fallback after {} attempts. Errors: {:?}",
@@ -328,13 +328,6 @@ impl Forwarder {
// move the messages to the fallback messages
fallback_msgs.append(messages_to_send);
}
- // if the strategy is invalid, return an error
- _ => {
- return Err(Error::SinkError(format!(
- "Invalid sink retry on fail strategy: {}",
- strategy
- )));
- }
}
// if we are done with the messages, break the loop
Ok(false)
From 8fc99bbbd694067287e017b311b1f7cdefe9f580 Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Wed, 4 Sep 2024 13:38:02 -0700
Subject: [PATCH 046/188] chore: add docs for retry (#2024)
Signed-off-by: Sidhant Kohli
---
docs/user-guide/sinks/retry-strategy.md | 68 +++++++++++++++++++++++++
mkdocs.yml | 1 +
2 files changed, 69 insertions(+)
create mode 100644 docs/user-guide/sinks/retry-strategy.md
diff --git a/docs/user-guide/sinks/retry-strategy.md b/docs/user-guide/sinks/retry-strategy.md
new file mode 100644
index 0000000000..a5b2a7264b
--- /dev/null
+++ b/docs/user-guide/sinks/retry-strategy.md
@@ -0,0 +1,68 @@
+# Retry Strategy
+
+### Overview
+The `RetryStrategy` is used to configure the behavior for a sink after encountering failures during a write operation.
+This structure allows the user to specify how Numaflow should respond to different fail-over scenarios for Sinks, ensuring that the writing can be resilient and handle
+unexpected issues efficiently.
+
+
+### Struct Explanation
+
+
+`retryStrategy` is optional, and can be added to the Sink spec configurations where retry logic is necessary.
+
+
+
+```yaml
+sink:
+ retryStrategy:
+ # Optional
+ backoff:
+ duration: 1s # Optional
+ steps: 3 # Optional, number of retries (including the 1st try)
+ # Optional
+ onFailure: retry|fallback|drop
+```
+Note: If no custom fields are defined for retryStrategy then the **default** values are used.
+
+- `BackOff` - Defines the timing for retries, including the interval and the maximum attempts.
+ - `duration`: the time interval to wait before retry attempts
+ - Default: _1ms_
+ - `steps`: the limit on the number of times to try the sink write operation including retries
+ - Default: _Infinite_
+- `OnFailure` - Specifies the action to be undertaken if number of retries are exhausted
+ - retry: continue with the retry logic again
+ - fallback: write the leftover messages to a [fallback](https://numaflow.numaproj.io/user-guide/sinks/fallback/) sink
+ - drop: any messages left to be processed are dropped
+ - Default: _retry_
+
+
+### Constraints
+
+1) If the `onFailure` is defined as fallback, then there should be a fallback sink specified in the spec.
+
+2) The steps defined should always be `> 0`
+
+
+## Example
+
+```yaml
+ sink:
+ retryStrategy:
+ backoff:
+ interval: "500ms"
+ steps: 10
+ onFailure: "fallback"
+ udsink:
+ container:
+ image: my-sink-image
+ fallback:
+ udsink:
+ container:
+ image: my-fallback-sink
+```
+### Explanation
+
+- Normal Operation: Data is processed by the primary sink container specified by `UDSink`.
+The system retries up to 10 times for a batch write operation to succeed with an interval of 500 milliseconds between each retry.
+- After Maximum Retries: If all retries fail, data is then routed to a fallback sink instead.
diff --git a/mkdocs.yml b/mkdocs.yml
index 7c9161d480..7e57a6af21 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -66,6 +66,7 @@ nav:
- user-guide/sinks/blackhole.md
- User-defined Sinks: "user-guide/sinks/user-defined-sinks.md"
- Fallback Sink: "user-guide/sinks/fallback.md"
+ - Retry Strategy: "user-guide/sinks/retry-strategy.md"
- User-defined Functions:
- Overview: "user-guide/user-defined-functions/user-defined-functions.md"
- Map:
From 9f13068a967db36faeb0a23e86bc7faa24c364a3 Mon Sep 17 00:00:00 2001
From: xdevxy <115589853+xdevxy@users.noreply.github.com>
Date: Thu, 5 Sep 2024 15:35:51 -0700
Subject: [PATCH 047/188] chore: use `kube_codegen.sh` instead in codegen.
(#2011)
Signed-off-by: Hao Hao
---
hack/update-codegen.sh | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh
index 3ab861b73b..1137a938cc 100755
--- a/hack/update-codegen.sh
+++ b/hack/update-codegen.sh
@@ -17,15 +17,13 @@ cd "${FAKE_REPOPATH}"
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${FAKE_REPOPATH}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
-chmod +x ${CODEGEN_PKG}/*.sh
-
subheader "running codegen"
-bash -x ${CODEGEN_PKG}/generate-groups.sh "deepcopy" \
+bash -x ${CODEGEN_PKG}/kube_codegen.sh "deepcopy" \
github.com/numaproj/numaflow/pkg/client github.com/numaproj/numaflow/pkg/apis \
"numaflow:v1alpha1" \
--go-header-file hack/boilerplate/boilerplate.go.txt
-bash -x ${CODEGEN_PKG}/generate-groups.sh "client,informer,lister" \
+bash -x ${CODEGEN_PKG}/kube_codegen.sh "client,informer,lister" \
github.com/numaproj/numaflow/pkg/client github.com/numaproj/numaflow/pkg/apis \
"numaflow:v1alpha1" \
--plural-exceptions="Vertex:Vertices,MonoVertex:MonoVertices" \
From cf90e258261b50d95db2787cfe23e9008c2ab72a Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Thu, 5 Sep 2024 21:44:14 -0700
Subject: [PATCH 048/188] fix: pause lifecyle changes and add drained status
(#2028)
Signed-off-by: Sidhant Kohli
---
api/json-schema/schema.json | 4 +
api/openapi-spec/swagger.json | 4 +
.../full/numaflow.numaproj.io_pipelines.yaml | 3 +
config/install.yaml | 3 +
config/namespace-install.yaml | 3 +
docs/APIs.md | 20 +
pkg/apis/numaflow/v1alpha1/generated.pb.go | 994 +++++++++---------
pkg/apis/numaflow/v1alpha1/generated.proto | 6 +
.../numaflow/v1alpha1/openapi_generated.go | 7 +
pkg/apis/numaflow/v1alpha1/pipeline_types.go | 15 +
.../numaflow/v1alpha1/pipeline_types_test.go | 9 +
pkg/reconciler/pipeline/controller.go | 56 +-
pkg/reconciler/pipeline/controller_test.go | 70 +-
.../src/models/pipeline_status.rs | 4 +
14 files changed, 701 insertions(+), 497 deletions(-)
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index e594c79167..d58a13beb6 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19681,6 +19681,10 @@
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
},
+ "drainedOnPause": {
+ "description": "Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false",
+ "type": "boolean"
+ },
"lastUpdated": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
},
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index 8f47047db6..b3cdb6b120 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19668,6 +19668,10 @@
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
},
+ "drainedOnPause": {
+ "description": "Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false",
+ "type": "boolean"
+ },
"lastUpdated": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
},
diff --git a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
index 4ddc954f61..9670b018e0 100644
--- a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
@@ -9821,6 +9821,9 @@ spec:
- type
type: object
type: array
+ drainedOnPause:
+ default: false
+ type: boolean
lastUpdated:
format: date-time
type: string
diff --git a/config/install.yaml b/config/install.yaml
index 849958226d..8a84ffac83 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -18074,6 +18074,9 @@ spec:
- type
type: object
type: array
+ drainedOnPause:
+ default: false
+ type: boolean
lastUpdated:
format: date-time
type: string
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 3b65e083b6..c9301892de 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -18074,6 +18074,9 @@ spec:
- type
type: object
type: array
+ drainedOnPause:
+ default: false
+ type: boolean
lastUpdated:
format: date-time
type: string
diff --git a/docs/APIs.md b/docs/APIs.md
index a311d7ee07..97ddf96672 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -7941,6 +7941,26 @@ The generation observed by the Pipeline controller.
+
+
+
+
+drainedOnPause
bool
+
+
+
+
+
+
+Field to indicate if a pipeline drain successfully occurred, or it timed
+out. Set to true when the Pipeline is in Paused state, and after it has
+successfully been drained. defaults to false
+
+
+
+
+
+
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index f9bbc352a1..abf3a4e47a 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -2762,488 +2762,490 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7695 bytes of a gzipped FileDescriptorProto
+ // 7719 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0xd7,
- 0x75, 0xa6, 0xfa, 0xbf, 0xfb, 0x34, 0xc9, 0xe1, 0xdc, 0x19, 0x8d, 0x38, 0xa3, 0xd1, 0x70, 0x5c,
- 0xb2, 0xe4, 0xf1, 0xda, 0x26, 0x57, 0x5c, 0xfd, 0xf9, 0x57, 0x62, 0x93, 0x43, 0x0e, 0x67, 0xc8,
- 0x19, 0xfa, 0x34, 0x39, 0x92, 0xad, 0xb5, 0xb5, 0xc5, 0xea, 0xcb, 0x66, 0x89, 0xd5, 0x55, 0xad,
- 0xaa, 0x6a, 0xce, 0x50, 0xde, 0x85, 0x7f, 0xb4, 0x80, 0xb4, 0x58, 0x2c, 0x76, 0xe1, 0x27, 0x03,
- 0x0b, 0xef, 0x62, 0x17, 0xbb, 0xf0, 0x83, 0xe1, 0x7d, 0x58, 0x40, 0xfb, 0x60, 0x60, 0xe3, 0x38,
- 0x08, 0x12, 0x27, 0xc8, 0x8f, 0x1f, 0x02, 0x44, 0x79, 0x21, 0x62, 0x06, 0x79, 0x48, 0x80, 0x18,
- 0x46, 0x0c, 0x24, 0xf6, 0xc0, 0x88, 0x83, 0xfb, 0x57, 0x7f, 0x5d, 0x3d, 0x43, 0x76, 0x91, 0xa3,
- 0x51, 0xa2, 0xb7, 0xaa, 0x7b, 0xcf, 0xfd, 0xce, 0xad, 0x53, 0xf7, 0xe7, 0xdc, 0x73, 0xce, 0xbd,
- 0x17, 0x16, 0xdb, 0xa6, 0xbf, 0xd5, 0xdb, 0x98, 0x32, 0x9c, 0xce, 0xb4, 0xdd, 0xeb, 0xe8, 0x5d,
- 0xd7, 0x79, 0x8d, 0x3f, 0x6c, 0x5a, 0xce, 0xad, 0xe9, 0xee, 0x76, 0x7b, 0x5a, 0xef, 0x9a, 0x5e,
- 0x98, 0xb2, 0xf3, 0x94, 0x6e, 0x75, 0xb7, 0xf4, 0xa7, 0xa6, 0xdb, 0xd4, 0xa6, 0xae, 0xee, 0xd3,
- 0xd6, 0x54, 0xd7, 0x75, 0x7c, 0x87, 0x3c, 0x17, 0x02, 0x4d, 0x29, 0xa0, 0x29, 0x55, 0x6c, 0xaa,
- 0xbb, 0xdd, 0x9e, 0x62, 0x40, 0x61, 0x8a, 0x02, 0x3a, 0xf7, 0x89, 0x48, 0x0d, 0xda, 0x4e, 0xdb,
- 0x99, 0xe6, 0x78, 0x1b, 0xbd, 0x4d, 0xfe, 0xc6, 0x5f, 0xf8, 0x93, 0xe0, 0x73, 0x4e, 0xdb, 0x7e,
- 0xde, 0x9b, 0x32, 0x1d, 0x56, 0xad, 0x69, 0xc3, 0x71, 0xe9, 0xf4, 0x4e, 0x5f, 0x5d, 0xce, 0x3d,
- 0x1d, 0xd2, 0x74, 0x74, 0x63, 0xcb, 0xb4, 0xa9, 0xbb, 0xab, 0xbe, 0x65, 0xda, 0xa5, 0x9e, 0xd3,
- 0x73, 0x0d, 0x7a, 0xa8, 0x52, 0xde, 0x74, 0x87, 0xfa, 0x7a, 0x1a, 0xaf, 0xe9, 0x41, 0xa5, 0xdc,
- 0x9e, 0xed, 0x9b, 0x9d, 0x7e, 0x36, 0xcf, 0xde, 0xab, 0x80, 0x67, 0x6c, 0xd1, 0x8e, 0x9e, 0x2c,
- 0xa7, 0xfd, 0x10, 0xe0, 0xd4, 0xec, 0x86, 0xe7, 0xbb, 0xba, 0xe1, 0xaf, 0x3a, 0xad, 0x35, 0xda,
- 0xe9, 0x5a, 0xba, 0x4f, 0xc9, 0x36, 0x54, 0x59, 0xdd, 0x5a, 0xba, 0xaf, 0x4f, 0xe4, 0x2e, 0xe6,
- 0x2e, 0xd5, 0x67, 0x66, 0xa7, 0x86, 0xfc, 0x17, 0x53, 0x2b, 0x12, 0xa8, 0x31, 0xb2, 0xbf, 0x37,
- 0x59, 0x55, 0x6f, 0x18, 0x30, 0x20, 0xdf, 0xca, 0xc1, 0x88, 0xed, 0xb4, 0x68, 0x93, 0x5a, 0xd4,
- 0xf0, 0x1d, 0x77, 0x22, 0x7f, 0xb1, 0x70, 0xa9, 0x3e, 0xf3, 0xe5, 0xa1, 0x39, 0xa6, 0x7c, 0xd1,
- 0xd4, 0xf5, 0x08, 0x83, 0xcb, 0xb6, 0xef, 0xee, 0x36, 0x4e, 0xff, 0x68, 0x6f, 0xf2, 0xa1, 0xfd,
- 0xbd, 0xc9, 0x91, 0x68, 0x16, 0xc6, 0x6a, 0x42, 0xd6, 0xa1, 0xee, 0x3b, 0x16, 0x13, 0x99, 0xe9,
- 0xd8, 0xde, 0x44, 0x81, 0x57, 0xec, 0xc2, 0x94, 0x90, 0x36, 0x63, 0x3f, 0xc5, 0x9a, 0xcb, 0xd4,
- 0xce, 0x53, 0x53, 0x6b, 0x01, 0x59, 0xe3, 0x94, 0x04, 0xae, 0x87, 0x69, 0x1e, 0x46, 0x71, 0x08,
- 0x85, 0x13, 0x1e, 0x35, 0x7a, 0xae, 0xe9, 0xef, 0xce, 0x39, 0xb6, 0x4f, 0x6f, 0xfb, 0x13, 0x45,
- 0x2e, 0xe5, 0x27, 0xd3, 0xa0, 0x57, 0x9d, 0x56, 0x33, 0x4e, 0xdd, 0x38, 0xb5, 0xbf, 0x37, 0x79,
- 0x22, 0x91, 0x88, 0x49, 0x4c, 0x62, 0xc3, 0xb8, 0xd9, 0xd1, 0xdb, 0x74, 0xb5, 0x67, 0x59, 0x4d,
- 0x6a, 0xb8, 0xd4, 0xf7, 0x26, 0x4a, 0xfc, 0x13, 0x2e, 0xa5, 0xf1, 0x59, 0x76, 0x0c, 0xdd, 0xba,
- 0xb1, 0xf1, 0x1a, 0x35, 0x7c, 0xa4, 0x9b, 0xd4, 0xa5, 0xb6, 0x41, 0x1b, 0x13, 0xf2, 0x63, 0xc6,
- 0x97, 0x12, 0x48, 0xd8, 0x87, 0x4d, 0x16, 0xe1, 0x64, 0xd7, 0x35, 0x1d, 0x5e, 0x05, 0x4b, 0xf7,
- 0xbc, 0xeb, 0x7a, 0x87, 0x4e, 0x94, 0x2f, 0xe6, 0x2e, 0xd5, 0x1a, 0x67, 0x25, 0xcc, 0xc9, 0xd5,
- 0x24, 0x01, 0xf6, 0x97, 0x21, 0x97, 0xa0, 0xaa, 0x12, 0x27, 0x2a, 0x17, 0x73, 0x97, 0x4a, 0xa2,
- 0xed, 0xa8, 0xb2, 0x18, 0xe4, 0x92, 0x05, 0xa8, 0xea, 0x9b, 0x9b, 0xa6, 0xcd, 0x28, 0xab, 0x5c,
- 0x84, 0xe7, 0xd3, 0x3e, 0x6d, 0x56, 0xd2, 0x08, 0x1c, 0xf5, 0x86, 0x41, 0x59, 0x72, 0x15, 0x88,
- 0x47, 0xdd, 0x1d, 0xd3, 0xa0, 0xb3, 0x86, 0xe1, 0xf4, 0x6c, 0x9f, 0xd7, 0xbd, 0xc6, 0xeb, 0x7e,
- 0x4e, 0xd6, 0x9d, 0x34, 0xfb, 0x28, 0x30, 0xa5, 0x14, 0x79, 0x11, 0xc6, 0x65, 0xb7, 0x0b, 0xa5,
- 0x00, 0x1c, 0xe9, 0x34, 0x13, 0x24, 0x26, 0xf2, 0xb0, 0x8f, 0x9a, 0xb4, 0xe0, 0xbc, 0xde, 0xf3,
- 0x9d, 0x0e, 0x83, 0x8c, 0x33, 0x5d, 0x73, 0xb6, 0xa9, 0x3d, 0x51, 0xbf, 0x98, 0xbb, 0x54, 0x6d,
- 0x5c, 0xdc, 0xdf, 0x9b, 0x3c, 0x3f, 0x7b, 0x17, 0x3a, 0xbc, 0x2b, 0x0a, 0xb9, 0x01, 0xb5, 0x96,
- 0xed, 0xad, 0x3a, 0x96, 0x69, 0xec, 0x4e, 0x8c, 0xf0, 0x0a, 0x3e, 0x25, 0x3f, 0xb5, 0x36, 0x7f,
- 0xbd, 0x29, 0x32, 0xee, 0xec, 0x4d, 0x9e, 0xef, 0x1f, 0x1d, 0xa7, 0x82, 0x7c, 0x0c, 0x31, 0xc8,
- 0x0a, 0x07, 0x9c, 0x73, 0xec, 0x4d, 0xb3, 0x3d, 0x31, 0xca, 0xff, 0xc6, 0xc5, 0x01, 0x0d, 0x7a,
- 0xfe, 0x7a, 0x53, 0xd0, 0x35, 0x46, 0x25, 0x3b, 0xf1, 0x8a, 0x21, 0x02, 0x69, 0xc1, 0x98, 0x1a,
- 0x57, 0xe7, 0x2c, 0xdd, 0xec, 0x78, 0x13, 0x63, 0xbc, 0xf1, 0x7e, 0x78, 0x00, 0x26, 0x46, 0x89,
- 0x1b, 0x67, 0xe4, 0xa7, 0x8c, 0xc5, 0x92, 0x3d, 0x4c, 0x60, 0x9e, 0x7b, 0x01, 0x4e, 0xf6, 0x8d,
- 0x0d, 0x64, 0x1c, 0x0a, 0xdb, 0x74, 0x97, 0x0f, 0x7d, 0x35, 0x64, 0x8f, 0xe4, 0x34, 0x94, 0x76,
- 0x74, 0xab, 0x47, 0x27, 0xf2, 0x3c, 0x4d, 0xbc, 0x7c, 0x2a, 0xff, 0x7c, 0x4e, 0xfb, 0x9f, 0x05,
- 0x18, 0x51, 0x23, 0x4e, 0xd3, 0xb4, 0xb7, 0xc9, 0x4b, 0x50, 0xb0, 0x9c, 0xb6, 0x1c, 0x37, 0x3f,
- 0x33, 0xf4, 0x28, 0xb6, 0xec, 0xb4, 0x1b, 0x95, 0xfd, 0xbd, 0xc9, 0xc2, 0xb2, 0xd3, 0x46, 0x86,
- 0x48, 0x0c, 0x28, 0x6d, 0xeb, 0x9b, 0xdb, 0x3a, 0xaf, 0x43, 0x7d, 0xa6, 0x31, 0x34, 0xf4, 0x35,
- 0x86, 0xc2, 0xea, 0xda, 0xa8, 0xed, 0xef, 0x4d, 0x96, 0xf8, 0x2b, 0x0a, 0x6c, 0xe2, 0x40, 0x6d,
- 0xc3, 0xd2, 0x8d, 0xed, 0x2d, 0xc7, 0xa2, 0x13, 0x85, 0x8c, 0x8c, 0x1a, 0x0a, 0x49, 0xfc, 0xe6,
- 0xe0, 0x15, 0x43, 0x1e, 0xc4, 0x80, 0x72, 0xaf, 0xe5, 0x99, 0xf6, 0xb6, 0x1c, 0x03, 0x5f, 0x18,
- 0x9a, 0xdb, 0xfa, 0x3c, 0xff, 0x26, 0xd8, 0xdf, 0x9b, 0x2c, 0x8b, 0x67, 0x94, 0xd0, 0xda, 0x4f,
- 0xeb, 0x30, 0xa6, 0x7e, 0xd2, 0x4d, 0xea, 0xfa, 0xf4, 0x36, 0xb9, 0x08, 0x45, 0x9b, 0x75, 0x4d,
- 0xfe, 0x93, 0x1b, 0x23, 0xb2, 0xb9, 0x14, 0x79, 0x97, 0xe4, 0x39, 0xac, 0x66, 0xa2, 0xa9, 0x48,
- 0x81, 0x0f, 0x5f, 0xb3, 0x26, 0x87, 0x11, 0x35, 0x13, 0xcf, 0x28, 0xa1, 0xc9, 0x2b, 0x50, 0xe4,
- 0x1f, 0x2f, 0x44, 0xfd, 0xd9, 0xe1, 0x59, 0xb0, 0x4f, 0xaf, 0xb2, 0x2f, 0xe0, 0x1f, 0xce, 0x41,
- 0x59, 0x53, 0xec, 0xb5, 0x36, 0xa5, 0x60, 0x3f, 0x93, 0x41, 0xb0, 0x0b, 0xa2, 0x29, 0xae, 0xcf,
- 0x2f, 0x20, 0x43, 0x24, 0xff, 0x39, 0x07, 0x27, 0x0d, 0xc7, 0xf6, 0x75, 0xa6, 0x6a, 0xa8, 0x49,
- 0x76, 0xa2, 0xc4, 0xf9, 0x5c, 0x1d, 0x9a, 0xcf, 0x5c, 0x12, 0xb1, 0xf1, 0x30, 0x9b, 0x33, 0xfa,
- 0x92, 0xb1, 0x9f, 0x37, 0xf9, 0xaf, 0x39, 0x78, 0x98, 0x8d, 0xe5, 0x7d, 0xc4, 0x7c, 0x06, 0x3a,
- 0xda, 0x5a, 0x9d, 0xdd, 0xdf, 0x9b, 0x7c, 0x78, 0x29, 0x8d, 0x19, 0xa6, 0xd7, 0x81, 0xd5, 0xee,
- 0x94, 0xde, 0xaf, 0x96, 0xf0, 0xd9, 0xad, 0x3e, 0xb3, 0x7c, 0x94, 0xaa, 0x4e, 0xe3, 0x51, 0xd9,
- 0x94, 0xd3, 0x34, 0x3b, 0x4c, 0xab, 0x05, 0xb9, 0x0c, 0x95, 0x1d, 0xc7, 0xea, 0x75, 0xa8, 0x37,
- 0x51, 0xe5, 0x43, 0xec, 0xb9, 0xb4, 0x21, 0xf6, 0x26, 0x27, 0x69, 0x9c, 0x90, 0xf0, 0x15, 0xf1,
- 0xee, 0xa1, 0x2a, 0x4b, 0x4c, 0x28, 0x5b, 0x66, 0xc7, 0xf4, 0x3d, 0x3e, 0x71, 0xd6, 0x67, 0x2e,
- 0x0f, 0xfd, 0x59, 0xa2, 0x8b, 0x2e, 0x73, 0x30, 0xd1, 0x6b, 0xc4, 0x33, 0x4a, 0x06, 0x6c, 0x28,
- 0xf4, 0x0c, 0xdd, 0x12, 0x13, 0x6b, 0x7d, 0xe6, 0x73, 0xc3, 0x77, 0x1b, 0x86, 0xd2, 0x18, 0x95,
- 0xdf, 0x54, 0xe2, 0xaf, 0x28, 0xb0, 0xc9, 0x97, 0x60, 0x2c, 0xf6, 0x37, 0xbd, 0x89, 0x3a, 0x97,
- 0xce, 0x63, 0x69, 0xd2, 0x09, 0xa8, 0xc2, 0x99, 0x27, 0xd6, 0x42, 0x3c, 0x4c, 0x80, 0x91, 0x6b,
- 0x50, 0xf5, 0xcc, 0x16, 0x35, 0x74, 0xd7, 0x9b, 0x18, 0x39, 0x08, 0xf0, 0xb8, 0x04, 0xae, 0x36,
- 0x65, 0x31, 0x0c, 0x00, 0xc8, 0x14, 0x40, 0x57, 0x77, 0x7d, 0x53, 0x28, 0xaa, 0xa3, 0x5c, 0x69,
- 0x1a, 0xdb, 0xdf, 0x9b, 0x84, 0xd5, 0x20, 0x15, 0x23, 0x14, 0x8c, 0x9e, 0x95, 0x5d, 0xb2, 0xbb,
- 0x3d, 0x5f, 0x4c, 0xac, 0x35, 0x41, 0xdf, 0x0c, 0x52, 0x31, 0x42, 0x41, 0xbe, 0x97, 0x83, 0x47,
- 0xc3, 0xd7, 0xfe, 0x4e, 0x76, 0xe2, 0xc8, 0x3b, 0xd9, 0xe4, 0xfe, 0xde, 0xe4, 0xa3, 0xcd, 0xc1,
- 0x2c, 0xf1, 0x6e, 0xf5, 0xd1, 0x5e, 0x82, 0xd1, 0xd9, 0x9e, 0xbf, 0xe5, 0xb8, 0xe6, 0x1b, 0x5c,
- 0xe9, 0x26, 0x0b, 0x50, 0xf2, 0xb9, 0xf2, 0x24, 0xe6, 0xe5, 0x27, 0xd2, 0x44, 0x2d, 0x14, 0xd9,
- 0x6b, 0x74, 0x57, 0x69, 0x03, 0x62, 0x7e, 0x14, 0xca, 0x94, 0x28, 0xae, 0xfd, 0xfb, 0x1c, 0x54,
- 0x1a, 0xba, 0xb1, 0xed, 0x6c, 0x6e, 0x92, 0x97, 0xa1, 0x6a, 0xda, 0x3e, 0x75, 0x77, 0x74, 0x4b,
- 0xc2, 0x4e, 0x45, 0x60, 0x83, 0x95, 0x58, 0xf8, 0xdd, 0x6c, 0xcd, 0xc3, 0x18, 0xcd, 0xf7, 0xe4,
- 0x5a, 0x81, 0xeb, 0xa3, 0x4b, 0x12, 0x03, 0x03, 0x34, 0x32, 0x09, 0x25, 0xcf, 0xa7, 0x5d, 0x8f,
- 0xcf, 0x3c, 0xa3, 0xa2, 0x1a, 0x4d, 0x96, 0x80, 0x22, 0x5d, 0xfb, 0x1f, 0x39, 0xa8, 0x35, 0x74,
- 0xcf, 0x34, 0xd8, 0x57, 0x92, 0x39, 0x28, 0xf6, 0x3c, 0xea, 0x1e, 0xee, 0xdb, 0xf8, 0x64, 0xb1,
- 0xee, 0x51, 0x17, 0x79, 0x61, 0x72, 0x03, 0xaa, 0x5d, 0xdd, 0xf3, 0x6e, 0x39, 0x6e, 0x4b, 0x4e,
- 0x78, 0x07, 0x04, 0x12, 0xca, 0xb9, 0x2c, 0x8a, 0x01, 0x88, 0x56, 0x87, 0x70, 0xc6, 0xd7, 0x7e,
- 0x9e, 0x83, 0x53, 0x8d, 0xde, 0xe6, 0x26, 0x75, 0xa5, 0x2e, 0x2a, 0xb5, 0x3c, 0x0a, 0x25, 0x97,
- 0xb6, 0x4c, 0x4f, 0xd6, 0x7d, 0x7e, 0xe8, 0x16, 0x84, 0x0c, 0x45, 0x2a, 0x95, 0x5c, 0x5e, 0x3c,
- 0x01, 0x05, 0x3a, 0xe9, 0x41, 0xed, 0x35, 0xea, 0x7b, 0xbe, 0x4b, 0xf5, 0x8e, 0xfc, 0xba, 0x2b,
- 0x43, 0xb3, 0xba, 0x4a, 0xfd, 0x26, 0x47, 0x8a, 0xea, 0xb0, 0x41, 0x22, 0x86, 0x9c, 0xb4, 0x1f,
- 0x96, 0x60, 0x64, 0xce, 0xe9, 0x6c, 0x98, 0x36, 0x6d, 0x5d, 0x6e, 0xb5, 0x29, 0x79, 0x15, 0x8a,
- 0xb4, 0xd5, 0xa6, 0xf2, 0x6b, 0x87, 0x9f, 0xee, 0x19, 0x58, 0xa8, 0xb4, 0xb0, 0x37, 0xe4, 0xc0,
- 0x64, 0x19, 0xc6, 0x36, 0x5d, 0xa7, 0x23, 0x46, 0xd0, 0xb5, 0xdd, 0xae, 0xd4, 0x58, 0x1b, 0x1f,
- 0x56, 0xa3, 0xd2, 0x42, 0x2c, 0xf7, 0xce, 0xde, 0x24, 0x84, 0x6f, 0x98, 0x28, 0x4b, 0x5e, 0x86,
- 0x89, 0x30, 0x25, 0x18, 0x4a, 0xe6, 0xd8, 0x22, 0x82, 0x6b, 0x2c, 0xa5, 0xc6, 0xf9, 0xfd, 0xbd,
- 0xc9, 0x89, 0x85, 0x01, 0x34, 0x38, 0xb0, 0x34, 0x79, 0x2b, 0x07, 0xe3, 0x61, 0xa6, 0x18, 0xde,
- 0xa5, 0xa2, 0x72, 0x44, 0xf3, 0x06, 0x5f, 0x6d, 0x2d, 0x24, 0x58, 0x60, 0x1f, 0x53, 0xb2, 0x00,
- 0x23, 0xbe, 0x13, 0x91, 0x57, 0x89, 0xcb, 0x4b, 0x53, 0xe6, 0x81, 0x35, 0x67, 0xa0, 0xb4, 0x62,
- 0xe5, 0x08, 0xc2, 0x19, 0xf5, 0x9e, 0x90, 0x54, 0x99, 0x4b, 0xea, 0xdc, 0xfe, 0xde, 0xe4, 0x99,
- 0xb5, 0x54, 0x0a, 0x1c, 0x50, 0x92, 0x7c, 0x3d, 0x07, 0x63, 0x2a, 0x4b, 0xca, 0xa8, 0x72, 0x94,
- 0x32, 0x22, 0xac, 0x45, 0xac, 0xc5, 0x18, 0x60, 0x82, 0xa1, 0xf6, 0x8b, 0x22, 0xd4, 0x82, 0x01,
- 0x96, 0x3c, 0x0e, 0x25, 0xbe, 0xf0, 0x97, 0x7a, 0x73, 0x30, 0x73, 0x72, 0xfb, 0x00, 0x8a, 0x3c,
- 0xf2, 0x04, 0x54, 0x0c, 0xa7, 0xd3, 0xd1, 0xed, 0x16, 0x37, 0xe6, 0xd4, 0x1a, 0x75, 0xa6, 0x30,
- 0xcc, 0x89, 0x24, 0x54, 0x79, 0xe4, 0x3c, 0x14, 0x75, 0xb7, 0x2d, 0xec, 0x2a, 0x35, 0x31, 0x1e,
- 0xcd, 0xba, 0x6d, 0x0f, 0x79, 0x2a, 0xf9, 0x24, 0x14, 0xa8, 0xbd, 0x33, 0x51, 0x1c, 0xac, 0x91,
- 0x5c, 0xb6, 0x77, 0x6e, 0xea, 0x6e, 0xa3, 0x2e, 0xeb, 0x50, 0xb8, 0x6c, 0xef, 0x20, 0x2b, 0x43,
- 0x96, 0xa1, 0x42, 0xed, 0x1d, 0xf6, 0xef, 0xa5, 0xc1, 0xe3, 0x43, 0x03, 0x8a, 0x33, 0x12, 0xa9,
- 0x9c, 0x07, 0x7a, 0x8d, 0x4c, 0x46, 0x05, 0x41, 0xbe, 0x00, 0x23, 0x42, 0xc5, 0x59, 0x61, 0xff,
- 0xc4, 0x9b, 0x28, 0x73, 0xc8, 0xc9, 0xc1, 0x3a, 0x12, 0xa7, 0x0b, 0x0d, 0x4c, 0x91, 0x44, 0x0f,
- 0x63, 0x50, 0xe4, 0x0b, 0x50, 0x53, 0xeb, 0x51, 0xf5, 0x67, 0x53, 0x6d, 0x33, 0x6a, 0x11, 0x8b,
- 0xf4, 0xf5, 0x9e, 0xe9, 0xd2, 0x0e, 0xb5, 0x7d, 0xaf, 0x71, 0x52, 0xad, 0xd6, 0x55, 0xae, 0x87,
- 0x21, 0x1a, 0xd9, 0xe8, 0x37, 0x32, 0x09, 0x0b, 0xc9, 0xe3, 0x03, 0x46, 0xf5, 0x21, 0x2c, 0x4c,
- 0x5f, 0x86, 0x13, 0x81, 0x15, 0x48, 0x1a, 0x12, 0x84, 0xcd, 0xe4, 0x69, 0x56, 0x7c, 0x29, 0x9e,
- 0x75, 0x67, 0x6f, 0xf2, 0xb1, 0x14, 0x53, 0x42, 0x48, 0x80, 0x49, 0x30, 0xed, 0x07, 0x05, 0xe8,
- 0xd7, 0xfe, 0xe3, 0x42, 0xcb, 0x1d, 0xb5, 0xd0, 0x92, 0x1f, 0x24, 0x86, 0xcf, 0xe7, 0x65, 0xb1,
- 0xec, 0x1f, 0x95, 0xf6, 0x63, 0x0a, 0x47, 0xfd, 0x63, 0x1e, 0x94, 0xbe, 0xa3, 0xbd, 0x5d, 0x84,
- 0xb1, 0x79, 0x9d, 0x76, 0x1c, 0xfb, 0x9e, 0x6b, 0xa1, 0xdc, 0x03, 0xb1, 0x16, 0xba, 0x04, 0x55,
- 0x97, 0x76, 0x2d, 0xd3, 0xd0, 0x85, 0xf2, 0x25, 0x6d, 0x8f, 0x28, 0xd3, 0x30, 0xc8, 0x1d, 0xb0,
- 0x06, 0x2e, 0x3c, 0x90, 0x6b, 0xe0, 0xe2, 0x7b, 0xbf, 0x06, 0xd6, 0xbe, 0x9e, 0x07, 0xae, 0xa8,
- 0x90, 0x8b, 0x50, 0x64, 0x93, 0x70, 0xd2, 0xf2, 0xc2, 0x1b, 0x0e, 0xcf, 0x21, 0xe7, 0x20, 0xef,
- 0x3b, 0xb2, 0xe7, 0x81, 0xcc, 0xcf, 0xaf, 0x39, 0x98, 0xf7, 0x1d, 0xf2, 0x06, 0x80, 0xe1, 0xd8,
- 0x2d, 0x53, 0x99, 0xe4, 0xb3, 0x7d, 0xd8, 0x82, 0xe3, 0xde, 0xd2, 0xdd, 0xd6, 0x5c, 0x80, 0x28,
- 0x56, 0x41, 0xe1, 0x3b, 0x46, 0xb8, 0x91, 0x17, 0xa0, 0xec, 0xd8, 0x0b, 0x3d, 0xcb, 0xe2, 0x02,
- 0xad, 0x35, 0x3e, 0xc2, 0x96, 0xa6, 0x37, 0x78, 0xca, 0x9d, 0xbd, 0xc9, 0xb3, 0x42, 0xbf, 0x65,
- 0x6f, 0x2f, 0xb9, 0xa6, 0x6f, 0xda, 0xed, 0xa6, 0xef, 0xea, 0x3e, 0x6d, 0xef, 0xa2, 0x2c, 0xa6,
- 0x7d, 0x33, 0x07, 0xf5, 0x05, 0xf3, 0x36, 0x6d, 0xbd, 0x64, 0xda, 0x2d, 0xe7, 0x16, 0x41, 0x28,
- 0x5b, 0xd4, 0x6e, 0xfb, 0x5b, 0x43, 0xae, 0x1f, 0xc4, 0xda, 0x98, 0x23, 0xa0, 0x44, 0x22, 0xd3,
- 0x50, 0x13, 0xda, 0xa7, 0x69, 0xb7, 0xb9, 0x0c, 0xab, 0xe1, 0xa0, 0xd7, 0x54, 0x19, 0x18, 0xd2,
- 0x68, 0xbb, 0x70, 0xb2, 0x4f, 0x0c, 0xa4, 0x05, 0x45, 0x5f, 0x6f, 0xab, 0xf1, 0x75, 0x61, 0x68,
- 0x01, 0xaf, 0xe9, 0xed, 0x88, 0x70, 0xf9, 0x1c, 0xbf, 0xa6, 0xb3, 0x39, 0x9e, 0xa1, 0x6b, 0xbf,
- 0xca, 0x41, 0x75, 0xa1, 0x67, 0x1b, 0x7c, 0x89, 0x76, 0x6f, 0x8b, 0x9c, 0x52, 0x18, 0xf2, 0xa9,
- 0x0a, 0x43, 0x0f, 0xca, 0xdb, 0xb7, 0x02, 0x85, 0xa2, 0x3e, 0xb3, 0x32, 0x7c, 0xab, 0x90, 0x55,
- 0x9a, 0xba, 0xc6, 0xf1, 0x84, 0xc3, 0x68, 0x4c, 0x56, 0xa8, 0x7c, 0xed, 0x25, 0xce, 0x54, 0x32,
- 0x3b, 0xf7, 0x49, 0xa8, 0x47, 0xc8, 0x0e, 0x65, 0x3b, 0xfe, 0x7f, 0x45, 0x28, 0x2f, 0x36, 0x9b,
- 0xb3, 0xab, 0x4b, 0xe4, 0x19, 0xa8, 0x4b, 0x5f, 0xc2, 0xf5, 0x50, 0x06, 0x81, 0x2b, 0xa9, 0x19,
- 0x66, 0x61, 0x94, 0x8e, 0xa9, 0x63, 0x2e, 0xd5, 0xad, 0x8e, 0xec, 0x2c, 0x81, 0x3a, 0x86, 0x2c,
- 0x11, 0x45, 0x1e, 0xd1, 0x61, 0x8c, 0xad, 0xf0, 0x98, 0x08, 0xc5, 0xea, 0x4d, 0x76, 0x9b, 0x03,
- 0xae, 0xef, 0xb8, 0x92, 0xb8, 0x1e, 0x03, 0xc0, 0x04, 0x20, 0x79, 0x1e, 0xaa, 0x7a, 0xcf, 0xdf,
- 0xe2, 0x0a, 0xb4, 0xe8, 0x1b, 0xe7, 0xb9, 0xab, 0x45, 0xa6, 0xdd, 0xd9, 0x9b, 0x1c, 0xb9, 0x86,
- 0x8d, 0x67, 0xd4, 0x3b, 0x06, 0xd4, 0xac, 0x72, 0x6a, 0xc5, 0x28, 0x2b, 0x57, 0x3a, 0x74, 0xe5,
- 0x56, 0x63, 0x00, 0x98, 0x00, 0x24, 0xaf, 0xc0, 0xc8, 0x36, 0xdd, 0xf5, 0xf5, 0x0d, 0xc9, 0xa0,
- 0x7c, 0x18, 0x06, 0xe3, 0x4c, 0x85, 0xbb, 0x16, 0x29, 0x8e, 0x31, 0x30, 0xe2, 0xc1, 0xe9, 0x6d,
- 0xea, 0x6e, 0x50, 0xd7, 0x91, 0xab, 0x4f, 0xc9, 0xa4, 0x72, 0x18, 0x26, 0x13, 0xfb, 0x7b, 0x93,
- 0xa7, 0xaf, 0xa5, 0xc0, 0x60, 0x2a, 0xb8, 0xf6, 0xcb, 0x3c, 0x9c, 0x58, 0x14, 0xce, 0x5c, 0xc7,
- 0x15, 0x93, 0x30, 0x39, 0x0b, 0x05, 0xb7, 0xdb, 0xe3, 0x2d, 0xa7, 0x20, 0xcc, 0xb5, 0xb8, 0xba,
- 0x8e, 0x2c, 0x8d, 0xbc, 0x0c, 0xd5, 0x96, 0x1c, 0x32, 0xe4, 0xe2, 0x77, 0x28, 0x43, 0x85, 0x7a,
- 0xc3, 0x00, 0x8d, 0x69, 0xfa, 0x1d, 0xaf, 0xdd, 0x34, 0xdf, 0xa0, 0x72, 0x3d, 0xc8, 0x35, 0xfd,
- 0x15, 0x91, 0x84, 0x2a, 0x8f, 0xcd, 0xaa, 0xdb, 0x74, 0x57, 0xac, 0x86, 0x8a, 0xe1, 0xac, 0x7a,
- 0x4d, 0xa6, 0x61, 0x90, 0x4b, 0x26, 0x55, 0x67, 0x61, 0xad, 0xa0, 0x28, 0x56, 0xf2, 0x37, 0x59,
- 0x82, 0xec, 0x37, 0x6c, 0xc8, 0x7c, 0xcd, 0xf4, 0x7d, 0xea, 0xca, 0xdf, 0x38, 0xd4, 0x90, 0x79,
- 0x95, 0x23, 0xa0, 0x44, 0x22, 0x1f, 0x83, 0x1a, 0x07, 0x6f, 0x58, 0xce, 0x06, 0xff, 0x71, 0x35,
- 0xb1, 0xa6, 0xbf, 0xa9, 0x12, 0x31, 0xcc, 0xd7, 0x7e, 0x9d, 0x87, 0x33, 0x8b, 0xd4, 0x17, 0x5a,
- 0xcd, 0x3c, 0xed, 0x5a, 0xce, 0x2e, 0x53, 0x2d, 0x91, 0xbe, 0x4e, 0x5e, 0x04, 0x30, 0xbd, 0x8d,
- 0xe6, 0x8e, 0xc1, 0xfb, 0x81, 0xe8, 0xc3, 0x17, 0x65, 0x97, 0x84, 0xa5, 0x66, 0x43, 0xe6, 0xdc,
- 0x89, 0xbd, 0x61, 0xa4, 0x4c, 0xb8, 0xbc, 0xca, 0xdf, 0x65, 0x79, 0xd5, 0x04, 0xe8, 0x86, 0x0a,
- 0x6a, 0x81, 0x53, 0xfe, 0x2b, 0xc5, 0xe6, 0x30, 0xba, 0x69, 0x04, 0x26, 0x8b, 0xca, 0x68, 0xc3,
- 0x78, 0x8b, 0x6e, 0xea, 0x3d, 0xcb, 0x0f, 0x94, 0x6a, 0xd9, 0x89, 0x0f, 0xae, 0x97, 0x07, 0x8e,
- 0xe6, 0xf9, 0x04, 0x12, 0xf6, 0x61, 0x6b, 0xdf, 0x2f, 0xc0, 0xb9, 0x45, 0xea, 0x07, 0x16, 0x17,
- 0x39, 0x3a, 0x36, 0xbb, 0xd4, 0x60, 0x7f, 0xe1, 0xad, 0x1c, 0x94, 0x2d, 0x7d, 0x83, 0x5a, 0x6c,
- 0xf6, 0x62, 0x5f, 0xf3, 0xea, 0xd0, 0x13, 0xc1, 0x60, 0x2e, 0x53, 0xcb, 0x9c, 0x43, 0x62, 0x6a,
- 0x10, 0x89, 0x28, 0xd9, 0xb3, 0x41, 0xdd, 0xb0, 0x7a, 0x9e, 0x4f, 0xdd, 0x55, 0xc7, 0xf5, 0xa5,
- 0x3e, 0x19, 0x0c, 0xea, 0x73, 0x61, 0x16, 0x46, 0xe9, 0xc8, 0x0c, 0x80, 0x61, 0x99, 0xd4, 0xf6,
- 0x79, 0x29, 0xd1, 0xaf, 0x88, 0xfa, 0xbf, 0x73, 0x41, 0x0e, 0x46, 0xa8, 0x18, 0xab, 0x8e, 0x63,
- 0x9b, 0xbe, 0x23, 0x58, 0x15, 0xe3, 0xac, 0x56, 0xc2, 0x2c, 0x8c, 0xd2, 0xf1, 0x62, 0xd4, 0x77,
- 0x4d, 0xc3, 0xe3, 0xc5, 0x4a, 0x89, 0x62, 0x61, 0x16, 0x46, 0xe9, 0xd8, 0x9c, 0x17, 0xf9, 0xfe,
- 0x43, 0xcd, 0x79, 0xdf, 0xad, 0xc1, 0x85, 0x98, 0x58, 0x7d, 0xdd, 0xa7, 0x9b, 0x3d, 0xab, 0x49,
- 0x7d, 0xf5, 0x03, 0x87, 0x9c, 0x0b, 0xff, 0x63, 0xf8, 0xdf, 0x45, 0x08, 0x89, 0x71, 0x34, 0xff,
- 0xbd, 0xaf, 0x82, 0x07, 0xfa, 0xf7, 0xd3, 0x50, 0xb3, 0x75, 0xdf, 0xe3, 0x1d, 0x57, 0xf6, 0xd1,
- 0x40, 0x0d, 0xbb, 0xae, 0x32, 0x30, 0xa4, 0x21, 0xab, 0x70, 0x5a, 0x8a, 0xf8, 0xf2, 0xed, 0xae,
- 0xe3, 0xfa, 0xd4, 0x15, 0x65, 0xe5, 0x74, 0x2a, 0xcb, 0x9e, 0x5e, 0x49, 0xa1, 0xc1, 0xd4, 0x92,
- 0x64, 0x05, 0x4e, 0x19, 0xc2, 0xad, 0x4e, 0x2d, 0x47, 0x6f, 0x29, 0x40, 0x61, 0xe0, 0x0a, 0x96,
- 0x46, 0x73, 0xfd, 0x24, 0x98, 0x56, 0x2e, 0xd9, 0x9a, 0xcb, 0x43, 0xb5, 0xe6, 0xca, 0x30, 0xad,
- 0xb9, 0x3a, 0x5c, 0x6b, 0xae, 0x1d, 0xac, 0x35, 0x33, 0xc9, 0xb3, 0x76, 0x44, 0x5d, 0xa6, 0x9e,
- 0x88, 0x19, 0x36, 0x12, 0xb5, 0x11, 0x48, 0xbe, 0x99, 0x42, 0x83, 0xa9, 0x25, 0xc9, 0x06, 0x9c,
- 0x13, 0xe9, 0x97, 0x6d, 0xc3, 0xdd, 0xed, 0xb2, 0x89, 0x27, 0x82, 0x5b, 0x8f, 0x59, 0x18, 0xcf,
- 0x35, 0x07, 0x52, 0xe2, 0x5d, 0x50, 0xc8, 0xa7, 0x61, 0x54, 0xfc, 0xa5, 0x15, 0xbd, 0xcb, 0x61,
- 0x45, 0x0c, 0xc7, 0xc3, 0x12, 0x76, 0x74, 0x2e, 0x9a, 0x89, 0x71, 0x5a, 0x32, 0x0b, 0x27, 0xba,
- 0x3b, 0x06, 0x7b, 0x5c, 0xda, 0xbc, 0x4e, 0x69, 0x8b, 0xb6, 0xb8, 0xd3, 0xa8, 0xd6, 0x78, 0x44,
- 0x19, 0x3a, 0x56, 0xe3, 0xd9, 0x98, 0xa4, 0x27, 0xcf, 0xc3, 0x88, 0xe7, 0xeb, 0xae, 0x2f, 0xcd,
- 0x7a, 0x13, 0x63, 0x22, 0xc6, 0x45, 0x59, 0xbd, 0x9a, 0x91, 0x3c, 0x8c, 0x51, 0xa6, 0xce, 0x17,
- 0x27, 0x8e, 0x6f, 0xbe, 0xc8, 0x32, 0x5a, 0xfd, 0x6e, 0x1e, 0x2e, 0x2e, 0x52, 0x7f, 0xc5, 0xb1,
- 0xa5, 0x51, 0x34, 0x6d, 0xda, 0x3f, 0x90, 0x4d, 0x34, 0x3e, 0x69, 0xe7, 0x8f, 0x74, 0xd2, 0x2e,
- 0x1c, 0xd1, 0xa4, 0x5d, 0x3c, 0xc6, 0x49, 0xfb, 0x37, 0xf2, 0xf0, 0x48, 0x4c, 0x92, 0xab, 0x4e,
- 0x4b, 0x0d, 0xf8, 0x1f, 0x08, 0xf0, 0x00, 0x02, 0xbc, 0x23, 0xf4, 0x4e, 0xee, 0xd6, 0x4a, 0x68,
- 0x3c, 0x6f, 0x26, 0x35, 0x9e, 0x57, 0xb2, 0xcc, 0x7c, 0x29, 0x1c, 0x0e, 0x34, 0xe3, 0x5d, 0x05,
- 0xe2, 0x4a, 0x27, 0x9c, 0x30, 0xfd, 0x44, 0x94, 0x9e, 0x20, 0x88, 0x0e, 0xfb, 0x28, 0x30, 0xa5,
- 0x14, 0x69, 0xc2, 0xc3, 0x1e, 0xb5, 0x7d, 0xd3, 0xa6, 0x56, 0x1c, 0x4e, 0x68, 0x43, 0x8f, 0x49,
- 0xb8, 0x87, 0x9b, 0x69, 0x44, 0x98, 0x5e, 0x36, 0xcb, 0x38, 0xf0, 0x07, 0xc0, 0x55, 0x4e, 0x21,
- 0x9a, 0x23, 0xd3, 0x58, 0xde, 0x4a, 0x6a, 0x2c, 0xaf, 0x66, 0xff, 0x6f, 0xc3, 0x69, 0x2b, 0x33,
- 0x00, 0xfc, 0x2f, 0x44, 0xd5, 0x95, 0x60, 0x92, 0xc6, 0x20, 0x07, 0x23, 0x54, 0x6c, 0x02, 0x52,
- 0x72, 0x8e, 0x6a, 0x2a, 0xc1, 0x04, 0xd4, 0x8c, 0x66, 0x62, 0x9c, 0x76, 0xa0, 0xb6, 0x53, 0x1a,
- 0x5a, 0xdb, 0xb9, 0x0a, 0x24, 0x66, 0x78, 0x14, 0x78, 0xe5, 0x78, 0x0c, 0xe7, 0x52, 0x1f, 0x05,
- 0xa6, 0x94, 0x1a, 0xd0, 0x94, 0x2b, 0x47, 0xdb, 0x94, 0xab, 0xc3, 0x37, 0x65, 0xf2, 0x2a, 0x9c,
- 0xe5, 0xac, 0xa4, 0x7c, 0xe2, 0xc0, 0x42, 0xef, 0xf9, 0x90, 0x04, 0x3e, 0x8b, 0x83, 0x08, 0x71,
- 0x30, 0x06, 0xfb, 0x3f, 0x86, 0x4b, 0x5b, 0x8c, 0xb9, 0x6e, 0x0d, 0xd6, 0x89, 0xe6, 0x52, 0x68,
- 0x30, 0xb5, 0x24, 0x6b, 0x62, 0x3e, 0x6b, 0x86, 0xfa, 0x86, 0x45, 0x5b, 0x32, 0x86, 0x35, 0x68,
- 0x62, 0x6b, 0xcb, 0x4d, 0x99, 0x83, 0x11, 0xaa, 0x34, 0x35, 0x65, 0xe4, 0x90, 0x6a, 0xca, 0x22,
- 0xb7, 0xd2, 0x6f, 0xc6, 0xb4, 0x21, 0xa9, 0xeb, 0x04, 0x51, 0xc9, 0x73, 0x49, 0x02, 0xec, 0x2f,
- 0xc3, 0xb5, 0x44, 0xc3, 0x35, 0xbb, 0xbe, 0x17, 0xc7, 0x1a, 0x4b, 0x68, 0x89, 0x29, 0x34, 0x98,
- 0x5a, 0x92, 0xe9, 0xe7, 0x5b, 0x54, 0xb7, 0xfc, 0xad, 0x38, 0xe0, 0x89, 0xb8, 0x7e, 0x7e, 0xa5,
- 0x9f, 0x04, 0xd3, 0xca, 0xa5, 0x4e, 0x48, 0xe3, 0x0f, 0xa6, 0x5a, 0xf5, 0x8d, 0x02, 0x9c, 0x5d,
- 0xa4, 0x7e, 0x10, 0xde, 0xf3, 0x81, 0x19, 0xe5, 0x3d, 0x30, 0xa3, 0x7c, 0xa7, 0x04, 0xa7, 0x16,
- 0xa9, 0xdf, 0xa7, 0x8d, 0xfd, 0x33, 0x15, 0xff, 0x0a, 0x9c, 0x0a, 0x23, 0xca, 0x9a, 0xbe, 0xe3,
- 0x8a, 0xb9, 0x3c, 0xb1, 0x5a, 0x6e, 0xf6, 0x93, 0x60, 0x5a, 0x39, 0xf2, 0x05, 0x78, 0x84, 0x4f,
- 0xf5, 0x76, 0x5b, 0xd8, 0x67, 0x85, 0x31, 0x21, 0xb2, 0x27, 0x62, 0x52, 0x42, 0x3e, 0xd2, 0x4c,
- 0x27, 0xc3, 0x41, 0xe5, 0xc9, 0x57, 0x61, 0xa4, 0x6b, 0x76, 0xa9, 0x65, 0xda, 0x5c, 0x3f, 0xcb,
- 0x1c, 0x12, 0xb2, 0x1a, 0x01, 0x0b, 0x17, 0x70, 0xd1, 0x54, 0x8c, 0x31, 0x4c, 0x6d, 0xa9, 0xd5,
- 0x63, 0x6c, 0xa9, 0x7f, 0x9b, 0x87, 0xca, 0xa2, 0xeb, 0xf4, 0xba, 0x8d, 0x5d, 0xd2, 0x86, 0xf2,
- 0x2d, 0xee, 0x3c, 0x93, 0xae, 0xa9, 0xe1, 0xa3, 0xb2, 0x85, 0x0f, 0x2e, 0x54, 0x89, 0xc4, 0x3b,
- 0x4a, 0x78, 0xd6, 0x88, 0xb7, 0xe9, 0x2e, 0x6d, 0x49, 0x1f, 0x5a, 0xd0, 0x88, 0xaf, 0xb1, 0x44,
- 0x14, 0x79, 0xa4, 0x03, 0x27, 0x74, 0xcb, 0x72, 0x6e, 0xd1, 0xd6, 0xb2, 0xee, 0x53, 0x9b, 0x7a,
- 0xca, 0x25, 0x79, 0x58, 0xb3, 0x34, 0xf7, 0xeb, 0xcf, 0xc6, 0xa1, 0x30, 0x89, 0x4d, 0x5e, 0x83,
- 0x8a, 0xe7, 0x3b, 0xae, 0x52, 0xb6, 0xea, 0x33, 0x73, 0xc3, 0xff, 0xf4, 0xc6, 0xe7, 0x9b, 0x02,
- 0x4a, 0xd8, 0xec, 0xe5, 0x0b, 0x2a, 0x06, 0xda, 0xb7, 0x73, 0x00, 0x57, 0xd6, 0xd6, 0x56, 0xa5,
- 0x7b, 0xa1, 0x05, 0x45, 0xbd, 0x17, 0x38, 0x2a, 0x87, 0x77, 0x08, 0xc6, 0xc2, 0x32, 0xa5, 0x0f,
- 0xaf, 0xe7, 0x6f, 0x21, 0x47, 0x27, 0x1f, 0x85, 0x8a, 0x54, 0x90, 0xa5, 0xd8, 0x83, 0xd0, 0x02,
- 0xa9, 0x44, 0xa3, 0xca, 0xd7, 0xfe, 0x6f, 0x1e, 0x60, 0xa9, 0x65, 0xd1, 0xa6, 0x0a, 0xa4, 0xaf,
- 0xf9, 0x5b, 0x2e, 0xf5, 0xb6, 0x1c, 0xab, 0x35, 0xa4, 0x37, 0x95, 0xdb, 0xfc, 0xd7, 0x14, 0x08,
- 0x86, 0x78, 0xa4, 0x05, 0x23, 0x9e, 0x4f, 0xbb, 0x2a, 0x52, 0x73, 0x48, 0x27, 0xca, 0xb8, 0xb0,
- 0x8b, 0x84, 0x38, 0x18, 0x43, 0x25, 0x3a, 0xd4, 0x4d, 0xdb, 0x10, 0x1d, 0xa4, 0xb1, 0x3b, 0x64,
- 0x43, 0x3a, 0xc1, 0x56, 0x1c, 0x4b, 0x21, 0x0c, 0x46, 0x31, 0xb5, 0x9f, 0xe5, 0xe1, 0x0c, 0xe7,
- 0xc7, 0xaa, 0x11, 0x8b, 0xc7, 0x24, 0xff, 0xa6, 0x6f, 0xd3, 0xdf, 0xbf, 0x3c, 0x18, 0x6b, 0xb1,
- 0x67, 0x6c, 0x85, 0xfa, 0x7a, 0xa8, 0xcf, 0x85, 0x69, 0x91, 0x9d, 0x7e, 0x3d, 0x28, 0x7a, 0x6c,
- 0xbc, 0x12, 0xd2, 0x6b, 0x0e, 0xdd, 0x84, 0xd2, 0x3f, 0x80, 0x8f, 0x5e, 0x81, 0xd7, 0x98, 0x8f,
- 0x5a, 0x9c, 0x1d, 0xf9, 0x77, 0x50, 0xf6, 0x7c, 0xdd, 0xef, 0xa9, 0xae, 0xb9, 0x7e, 0xd4, 0x8c,
- 0x39, 0x78, 0x38, 0x8e, 0x88, 0x77, 0x94, 0x4c, 0xb5, 0x9f, 0xe5, 0xe0, 0x5c, 0x7a, 0xc1, 0x65,
- 0xd3, 0xf3, 0xc9, 0xbf, 0xee, 0x13, 0xfb, 0x01, 0xff, 0x38, 0x2b, 0xcd, 0x85, 0x1e, 0xc4, 0x85,
- 0xab, 0x94, 0x88, 0xc8, 0x7d, 0x28, 0x99, 0x3e, 0xed, 0xa8, 0xf5, 0xe5, 0x8d, 0x23, 0xfe, 0xf4,
- 0xc8, 0xd4, 0xce, 0xb8, 0xa0, 0x60, 0xa6, 0xbd, 0x9d, 0x1f, 0xf4, 0xc9, 0x7c, 0xfa, 0xb0, 0xe2,
- 0x31, 0xbf, 0xd7, 0xb2, 0xc5, 0xfc, 0xc6, 0x2b, 0xd4, 0x1f, 0xfa, 0xfb, 0x6f, 0xfb, 0x43, 0x7f,
- 0x6f, 0x64, 0x0f, 0xfd, 0x4d, 0x88, 0x61, 0x60, 0x04, 0xf0, 0xbb, 0x05, 0x38, 0x7f, 0xb7, 0x66,
- 0xc3, 0xe6, 0x33, 0xd9, 0x3a, 0xb3, 0xce, 0x67, 0x77, 0x6f, 0x87, 0x64, 0x06, 0x4a, 0xdd, 0x2d,
- 0xdd, 0x53, 0x4a, 0x99, 0x5a, 0xb0, 0x94, 0x56, 0x59, 0xe2, 0x1d, 0x36, 0x68, 0x70, 0x65, 0x8e,
- 0xbf, 0xa2, 0x20, 0x65, 0xc3, 0x71, 0x87, 0x7a, 0x5e, 0x68, 0x13, 0x08, 0x86, 0xe3, 0x15, 0x91,
- 0x8c, 0x2a, 0x9f, 0xf8, 0x50, 0x16, 0x26, 0x66, 0x39, 0x33, 0x0d, 0x1f, 0xc8, 0x95, 0x12, 0x26,
- 0x1e, 0x7e, 0x94, 0xf4, 0x56, 0x48, 0x5e, 0x64, 0x0a, 0x8a, 0x7e, 0x18, 0xb4, 0xab, 0x96, 0xe6,
- 0xc5, 0x14, 0xfd, 0x94, 0xd3, 0xb1, 0x85, 0xbd, 0xb3, 0xc1, 0x8d, 0xea, 0x2d, 0xe9, 0x3f, 0x37,
- 0x1d, 0x9b, 0x2b, 0x64, 0x85, 0x70, 0x61, 0x7f, 0xa3, 0x8f, 0x02, 0x53, 0x4a, 0x69, 0x7f, 0x5c,
- 0x85, 0x33, 0xe9, 0xed, 0x81, 0xc9, 0x6d, 0x87, 0xba, 0x1e, 0xc3, 0xce, 0xc5, 0xe5, 0x76, 0x53,
- 0x24, 0xa3, 0xca, 0x7f, 0x5f, 0x07, 0x9c, 0x7d, 0x27, 0x07, 0x67, 0x5d, 0xe9, 0x23, 0xba, 0x1f,
- 0x41, 0x67, 0x8f, 0x09, 0x73, 0xc6, 0x00, 0x86, 0x38, 0xb8, 0x2e, 0xe4, 0x7f, 0xe5, 0x60, 0xa2,
- 0x93, 0xb0, 0x73, 0x1c, 0xe3, 0xbe, 0x35, 0x1e, 0x15, 0xbf, 0x32, 0x80, 0x1f, 0x0e, 0xac, 0x09,
- 0xf9, 0x2a, 0xd4, 0xbb, 0xac, 0x5d, 0x78, 0x3e, 0xb5, 0x0d, 0xb5, 0x75, 0x6d, 0xf8, 0x9e, 0xb4,
- 0x1a, 0x62, 0xa9, 0x50, 0x34, 0xa1, 0x1f, 0x44, 0x32, 0x30, 0xca, 0xf1, 0x01, 0xdf, 0xa8, 0x76,
- 0x09, 0xaa, 0x1e, 0xf5, 0x7d, 0xd3, 0x6e, 0x8b, 0xf5, 0x46, 0x4d, 0xf4, 0x95, 0xa6, 0x4c, 0xc3,
- 0x20, 0x97, 0x7c, 0x0c, 0x6a, 0xdc, 0xe5, 0x34, 0xeb, 0xb6, 0xbd, 0x89, 0x1a, 0x0f, 0x17, 0x1b,
- 0x15, 0x01, 0x70, 0x32, 0x11, 0xc3, 0x7c, 0xf2, 0x34, 0x8c, 0x6c, 0xf0, 0xee, 0x2b, 0xf7, 0x2e,
- 0x0b, 0x1b, 0x17, 0xd7, 0xd6, 0x1a, 0x91, 0x74, 0x8c, 0x51, 0x91, 0x19, 0x00, 0x1a, 0xf8, 0xe5,
- 0x92, 0xf6, 0xac, 0xd0, 0x63, 0x87, 0x11, 0x2a, 0xf2, 0x18, 0x14, 0x7c, 0xcb, 0xe3, 0x36, 0xac,
- 0x6a, 0xb8, 0x04, 0x5d, 0x5b, 0x6e, 0x22, 0x4b, 0xd7, 0x7e, 0x9d, 0x83, 0x13, 0x89, 0xcd, 0x25,
- 0xac, 0x48, 0xcf, 0xb5, 0xe4, 0x30, 0x12, 0x14, 0x59, 0xc7, 0x65, 0x64, 0xe9, 0xe4, 0x55, 0xa9,
- 0x96, 0xe7, 0x33, 0x1e, 0xd3, 0x70, 0x5d, 0xf7, 0x3d, 0xa6, 0x87, 0xf7, 0x69, 0xe4, 0xdc, 0xcd,
- 0x17, 0xd6, 0x47, 0xce, 0x03, 0x11, 0x37, 0x5f, 0x98, 0x87, 0x31, 0xca, 0x84, 0xc1, 0xaf, 0x78,
- 0x10, 0x83, 0x9f, 0xf6, 0xcd, 0x7c, 0x44, 0x02, 0x52, 0xb3, 0xbf, 0x87, 0x04, 0x9e, 0x64, 0x13,
- 0x68, 0x30, 0xb9, 0xd7, 0xa2, 0xf3, 0x1f, 0x9f, 0x8c, 0x65, 0x2e, 0x79, 0x49, 0xc8, 0xbe, 0x90,
- 0x71, 0x33, 0xec, 0xda, 0x72, 0x53, 0x44, 0x57, 0xa9, 0xbf, 0x16, 0xfc, 0x82, 0xe2, 0x31, 0xfd,
- 0x02, 0xed, 0xf7, 0x0b, 0x50, 0xbf, 0xea, 0x6c, 0xbc, 0x4f, 0x22, 0xa8, 0xd3, 0xa7, 0xa9, 0xfc,
- 0x7b, 0x38, 0x4d, 0xad, 0xc3, 0x23, 0xbe, 0x6f, 0x35, 0xa9, 0xe1, 0xd8, 0x2d, 0x6f, 0x76, 0xd3,
- 0xa7, 0xee, 0x82, 0x69, 0x9b, 0xde, 0x16, 0x6d, 0x49, 0x77, 0xd2, 0xa3, 0xfb, 0x7b, 0x93, 0x8f,
- 0xac, 0xad, 0x2d, 0xa7, 0x91, 0xe0, 0xa0, 0xb2, 0x7c, 0xd8, 0x10, 0x3b, 0x01, 0xf9, 0x4e, 0x19,
- 0x19, 0x73, 0x23, 0x86, 0x8d, 0x48, 0x3a, 0xc6, 0xa8, 0xb4, 0x77, 0xf2, 0x50, 0x0b, 0x36, 0xe0,
- 0x93, 0x27, 0xa0, 0xb2, 0xe1, 0x3a, 0xdb, 0xd4, 0x15, 0x9e, 0x3b, 0xb9, 0x53, 0xa6, 0x21, 0x92,
- 0x50, 0xe5, 0x91, 0xc7, 0xa1, 0xe4, 0x3b, 0x5d, 0xd3, 0x48, 0x1a, 0xd4, 0xd6, 0x58, 0x22, 0x8a,
- 0xbc, 0xe3, 0x6b, 0xe0, 0x4f, 0xc6, 0x54, 0xbb, 0xda, 0x40, 0x65, 0xec, 0x15, 0x28, 0x7a, 0xba,
- 0x67, 0xc9, 0xf9, 0x34, 0xc3, 0x5e, 0xf6, 0xd9, 0xe6, 0xb2, 0xdc, 0xcb, 0x3e, 0xdb, 0x5c, 0x46,
- 0x0e, 0xaa, 0xfd, 0x22, 0x0f, 0x75, 0x21, 0x37, 0x31, 0x2a, 0x1c, 0xa5, 0xe4, 0x5e, 0xe0, 0xa1,
- 0x14, 0x5e, 0xaf, 0x43, 0x5d, 0x6e, 0x66, 0x92, 0x83, 0x5c, 0xd4, 0x3f, 0x10, 0x66, 0x06, 0xe1,
- 0x14, 0x61, 0x92, 0x12, 0x7d, 0xf1, 0x18, 0x45, 0x5f, 0x3a, 0x90, 0xe8, 0xcb, 0xc7, 0x21, 0xfa,
- 0xb7, 0xf2, 0x50, 0x5b, 0x36, 0x37, 0xa9, 0xb1, 0x6b, 0x58, 0x7c, 0x4f, 0x60, 0x8b, 0x5a, 0xd4,
- 0xa7, 0x8b, 0xae, 0x6e, 0xd0, 0x55, 0xea, 0x9a, 0xfc, 0x80, 0x1a, 0xd6, 0x3f, 0xf8, 0x08, 0x24,
- 0xf7, 0x04, 0xce, 0x0f, 0xa0, 0xc1, 0x81, 0xa5, 0xc9, 0x12, 0x8c, 0xb4, 0xa8, 0x67, 0xba, 0xb4,
- 0xb5, 0x1a, 0x59, 0xa8, 0x3c, 0xa1, 0xa6, 0x9a, 0xf9, 0x48, 0xde, 0x9d, 0xbd, 0xc9, 0x51, 0x65,
- 0xa0, 0x14, 0x2b, 0x96, 0x58, 0x51, 0xd6, 0xe5, 0xbb, 0x7a, 0xcf, 0x4b, 0xab, 0x63, 0xa4, 0xcb,
- 0xaf, 0xa6, 0x93, 0xe0, 0xa0, 0xb2, 0x5a, 0x09, 0x0a, 0xcb, 0x4e, 0x5b, 0x7b, 0xbb, 0x00, 0xc1,
- 0x49, 0x46, 0xe4, 0x3f, 0xe4, 0xa0, 0xae, 0xdb, 0xb6, 0xe3, 0xcb, 0x53, 0x82, 0x84, 0x07, 0x1e,
- 0x33, 0x1f, 0x98, 0x34, 0x35, 0x1b, 0x82, 0x0a, 0xe7, 0x6d, 0xe0, 0x50, 0x8e, 0xe4, 0x60, 0x94,
- 0x37, 0xe9, 0x25, 0xfc, 0xc9, 0x2b, 0xd9, 0x6b, 0x71, 0x00, 0xef, 0xf1, 0xb9, 0xcf, 0xc1, 0x78,
- 0xb2, 0xb2, 0x87, 0x71, 0x07, 0x65, 0x72, 0xcc, 0xe7, 0x01, 0xc2, 0x98, 0x92, 0xfb, 0x60, 0xc4,
- 0x32, 0x63, 0x46, 0xac, 0xc5, 0xe1, 0x05, 0x1c, 0x54, 0x7a, 0xa0, 0xe1, 0xea, 0xf5, 0x84, 0xe1,
- 0x6a, 0xe9, 0x28, 0x98, 0xdd, 0xdd, 0x58, 0xf5, 0x7f, 0x72, 0x30, 0x1e, 0x12, 0xcb, 0x1d, 0xb2,
- 0xcf, 0xc1, 0xa8, 0x4b, 0xf5, 0x56, 0x43, 0xf7, 0x8d, 0x2d, 0x1e, 0xea, 0x9d, 0xe3, 0xb1, 0xd9,
- 0x27, 0xf7, 0xf7, 0x26, 0x47, 0x31, 0x9a, 0x81, 0x71, 0x3a, 0xa2, 0x43, 0x9d, 0x25, 0xac, 0x99,
- 0x1d, 0xea, 0xf4, 0xfc, 0x21, 0xad, 0xa6, 0x7c, 0xc1, 0x82, 0x21, 0x0c, 0x46, 0x31, 0xb5, 0x77,
- 0x73, 0x30, 0x16, 0xad, 0xf0, 0xb1, 0x5b, 0xd4, 0xb6, 0xe2, 0x16, 0xb5, 0xb9, 0x23, 0xf8, 0x27,
- 0x03, 0xac, 0x68, 0xbf, 0xac, 0x46, 0x3f, 0x8d, 0x5b, 0xce, 0xa2, 0xc6, 0x82, 0xdc, 0x5d, 0x8d,
- 0x05, 0xef, 0xff, 0xc3, 0x6b, 0x06, 0x69, 0xb9, 0xc5, 0x07, 0x58, 0xcb, 0x7d, 0x2f, 0x4f, 0xc0,
- 0x89, 0x9c, 0xe2, 0x52, 0xce, 0x70, 0x8a, 0x4b, 0x27, 0x38, 0xc5, 0xa5, 0x72, 0x64, 0x83, 0xce,
- 0x41, 0x4e, 0x72, 0xa9, 0xde, 0xd7, 0x93, 0x5c, 0x6a, 0xc7, 0x75, 0x92, 0x0b, 0x64, 0x3d, 0xc9,
- 0xe5, 0xcd, 0x1c, 0x8c, 0xb5, 0x62, 0x3b, 0x66, 0xb9, 0x6d, 0x21, 0xcb, 0x54, 0x13, 0xdf, 0x80,
- 0x2b, 0xb6, 0x4c, 0xc5, 0xd3, 0x30, 0xc1, 0x52, 0xfb, 0xdf, 0x95, 0xe8, 0x3c, 0x70, 0xbf, 0x4d,
- 0xd5, 0xcf, 0xc6, 0x4d, 0xd5, 0x17, 0x93, 0xa6, 0xea, 0x13, 0x91, 0x28, 0xd2, 0xa8, 0xb9, 0xfa,
- 0xe3, 0x91, 0xe1, 0xb1, 0xc0, 0x4f, 0x4e, 0x09, 0x24, 0x9d, 0x32, 0x44, 0x7e, 0x1c, 0xaa, 0x9e,
- 0x3a, 0x73, 0x52, 0x2c, 0x6c, 0xc2, 0xff, 0xa2, 0xce, 0x83, 0x0c, 0x28, 0x98, 0x26, 0xee, 0x52,
- 0xdd, 0x73, 0xec, 0xa4, 0x26, 0x8e, 0x3c, 0x15, 0x65, 0x6e, 0xd4, 0x64, 0x5e, 0xbe, 0x87, 0xc9,
- 0x5c, 0x87, 0xba, 0xa5, 0x7b, 0xfe, 0x7a, 0xb7, 0xa5, 0xfb, 0xb4, 0x25, 0xfb, 0xdb, 0xbf, 0x38,
- 0xd8, 0x5c, 0xc5, 0xe6, 0xbf, 0x50, 0x21, 0x5c, 0x0e, 0x61, 0x30, 0x8a, 0x49, 0x5a, 0x30, 0xc2,
- 0x5e, 0x79, 0x6f, 0x68, 0xcd, 0xaa, 0x23, 0x00, 0x0e, 0xc3, 0x23, 0xb0, 0xf4, 0x2c, 0x47, 0x70,
- 0x30, 0x86, 0x3a, 0xc0, 0xaa, 0x5e, 0x1b, 0xc6, 0xaa, 0x4e, 0x3e, 0x2d, 0x94, 0x8d, 0x5d, 0xf5,
- 0xc3, 0xb8, 0x35, 0x6e, 0x34, 0x8c, 0x2a, 0xc4, 0x68, 0x26, 0xc6, 0x69, 0xc9, 0x2c, 0x9c, 0x30,
- 0x7a, 0xae, 0xcb, 0xe3, 0x88, 0x64, 0xf1, 0x3a, 0x2f, 0x1e, 0xc4, 0x8b, 0xcd, 0xc5, 0xb3, 0x31,
- 0x49, 0xcf, 0x20, 0x7a, 0x52, 0x92, 0x0a, 0x62, 0x24, 0x0e, 0xb1, 0x1e, 0xcf, 0xc6, 0x24, 0x3d,
- 0xdf, 0x28, 0x21, 0x50, 0xaf, 0xe8, 0xde, 0x96, 0x0c, 0x36, 0x0b, 0x37, 0x4a, 0x84, 0x59, 0x18,
- 0xa5, 0x23, 0x33, 0x00, 0x02, 0x89, 0x97, 0x1a, 0x8b, 0xc7, 0x60, 0xae, 0x07, 0x39, 0x18, 0xa1,
- 0xd2, 0xde, 0xac, 0x41, 0xfd, 0xba, 0xee, 0x9b, 0x3b, 0x94, 0xfb, 0xbc, 0x8e, 0xc7, 0xf1, 0xf0,
- 0xdf, 0x72, 0x70, 0x26, 0x1e, 0xd8, 0x78, 0x8c, 0xde, 0x07, 0x7e, 0x4c, 0x0a, 0xa6, 0x72, 0xc3,
- 0x01, 0xb5, 0xe0, 0x7e, 0x88, 0xbe, 0x38, 0xc9, 0xe3, 0xf6, 0x43, 0x34, 0x07, 0x31, 0xc4, 0xc1,
- 0x75, 0x79, 0xbf, 0xf8, 0x21, 0x1e, 0xec, 0xd3, 0xf4, 0x12, 0x5e, 0x92, 0xca, 0x03, 0xe3, 0x25,
- 0xa9, 0x3e, 0x10, 0xaa, 0x69, 0x37, 0xe2, 0x25, 0xa9, 0x65, 0x8c, 0xd6, 0x91, 0x7b, 0x01, 0x04,
- 0xda, 0x20, 0x6f, 0x0b, 0xdf, 0xc6, 0xaf, 0xac, 0xd7, 0x4c, 0xa3, 0xdb, 0xd0, 0x3d, 0xd3, 0x90,
- 0x4a, 0x42, 0x86, 0xd3, 0x43, 0xd5, 0xf9, 0x66, 0xc2, 0xa9, 0xcf, 0x5f, 0x51, 0x60, 0x87, 0xc7,
- 0xb9, 0xe5, 0x33, 0x1d, 0xe7, 0x46, 0xe6, 0xa0, 0x68, 0x6f, 0xd3, 0xdd, 0xc3, 0x6d, 0x88, 0xe7,
- 0x2b, 0x95, 0xeb, 0xd7, 0xe8, 0x2e, 0xf2, 0xc2, 0xda, 0x3b, 0x79, 0x00, 0xf6, 0xf9, 0x07, 0xf3,
- 0x57, 0x7c, 0x14, 0x2a, 0x5e, 0x8f, 0x5b, 0x16, 0xa4, 0x7a, 0x13, 0x86, 0x38, 0x89, 0x64, 0x54,
- 0xf9, 0xe4, 0x71, 0x28, 0xbd, 0xde, 0xa3, 0x3d, 0xe5, 0x7c, 0x0f, 0x94, 0xdb, 0xcf, 0xb3, 0x44,
- 0x14, 0x79, 0xc7, 0x67, 0x7b, 0x54, 0x7e, 0x8d, 0xd2, 0x71, 0xf9, 0x35, 0x6a, 0x50, 0xb9, 0xee,
- 0xf0, 0x88, 0x49, 0xed, 0xaf, 0xf3, 0x00, 0x61, 0x44, 0x1a, 0xf9, 0x76, 0x0e, 0x1e, 0x0e, 0x3a,
- 0x9c, 0x2f, 0xd6, 0x28, 0xfc, 0xc0, 0xde, 0xcc, 0x3e, 0x8e, 0xb4, 0xce, 0xce, 0x47, 0xa0, 0xd5,
- 0x34, 0x76, 0x98, 0x5e, 0x0b, 0x82, 0x50, 0xa5, 0x9d, 0xae, 0xbf, 0x3b, 0x6f, 0xba, 0xb2, 0x05,
- 0xa6, 0x06, 0x3e, 0x5e, 0x96, 0x34, 0xa2, 0xa8, 0x5c, 0x48, 0xf3, 0x4e, 0xa4, 0x72, 0x30, 0xc0,
- 0x21, 0x5b, 0x50, 0xb5, 0x9d, 0x57, 0x3d, 0x26, 0x0e, 0xd9, 0x1c, 0x5f, 0x1c, 0x5e, 0xe4, 0x42,
- 0xac, 0xc2, 0x26, 0x2e, 0x5f, 0xb0, 0x62, 0x4b, 0x61, 0x7f, 0x2b, 0x0f, 0xa7, 0x52, 0xe4, 0x40,
- 0x5e, 0x84, 0x71, 0x19, 0xfc, 0x17, 0x9e, 0x5c, 0x9d, 0x0b, 0x4f, 0xae, 0x6e, 0x26, 0xf2, 0xb0,
- 0x8f, 0x9a, 0xbc, 0x0a, 0xa0, 0x1b, 0x06, 0xf5, 0xbc, 0x15, 0xa7, 0xa5, 0xb4, 0xf7, 0x17, 0x98,
- 0xfa, 0x32, 0x1b, 0xa4, 0xde, 0xd9, 0x9b, 0xfc, 0x44, 0x5a, 0x3c, 0x6f, 0x42, 0xce, 0x61, 0x01,
- 0x8c, 0x40, 0x92, 0x2f, 0x03, 0x88, 0x85, 0x6a, 0x70, 0xe4, 0xc0, 0x3d, 0xac, 0x3b, 0x53, 0xea,
- 0x70, 0xa7, 0xa9, 0xcf, 0xf7, 0x74, 0xdb, 0x37, 0xfd, 0x5d, 0x71, 0xc2, 0xcb, 0xcd, 0x00, 0x05,
- 0x23, 0x88, 0xda, 0xef, 0xe4, 0xa1, 0xaa, 0xec, 0xca, 0xf7, 0xc1, 0x98, 0xd8, 0x8e, 0x19, 0x13,
- 0x8f, 0x28, 0x82, 0x37, 0xcd, 0x94, 0xe8, 0x24, 0x4c, 0x89, 0x8b, 0xd9, 0x59, 0xdd, 0xdd, 0x90,
- 0xf8, 0xbd, 0x3c, 0x8c, 0x29, 0xd2, 0xac, 0x66, 0xc4, 0xcf, 0xc2, 0x09, 0xe1, 0x79, 0x5f, 0xd1,
- 0x6f, 0x8b, 0xc3, 0x6e, 0xb8, 0xc0, 0x8a, 0x22, 0x68, 0xb6, 0x11, 0xcf, 0xc2, 0x24, 0x2d, 0x6b,
- 0xd6, 0x22, 0x69, 0x9d, 0xad, 0xba, 0x84, 0xaf, 0x4e, 0xac, 0x0e, 0x79, 0xb3, 0x6e, 0x24, 0xf2,
- 0xb0, 0x8f, 0x3a, 0x69, 0xc7, 0x2c, 0x1e, 0x83, 0x1d, 0xf3, 0x4f, 0x72, 0x30, 0x12, 0xca, 0xeb,
- 0xd8, 0xad, 0x98, 0x9b, 0x71, 0x2b, 0xe6, 0x6c, 0xe6, 0xe6, 0x30, 0xc0, 0x86, 0xf9, 0x9f, 0x2a,
- 0x10, 0x0b, 0x24, 0x27, 0x1b, 0x70, 0xce, 0x4c, 0x0d, 0x87, 0x8b, 0x8c, 0x36, 0xc1, 0xce, 0xe8,
- 0xa5, 0x81, 0x94, 0x78, 0x17, 0x14, 0xd2, 0x83, 0xea, 0x0e, 0x75, 0x7d, 0xd3, 0xa0, 0xea, 0xfb,
- 0x16, 0x33, 0xab, 0x64, 0xd2, 0x52, 0x1b, 0xc8, 0xf4, 0xa6, 0x64, 0x80, 0x01, 0x2b, 0xb2, 0x01,
- 0x25, 0xda, 0x6a, 0x53, 0x75, 0xfc, 0x50, 0xc6, 0xc3, 0x3d, 0x03, 0x79, 0xb2, 0x37, 0x0f, 0x05,
- 0x34, 0xf1, 0xa0, 0x66, 0x29, 0x4f, 0x9c, 0x6c, 0x87, 0xc3, 0x2b, 0x58, 0x81, 0x4f, 0x2f, 0x3c,
- 0x99, 0x20, 0x48, 0xc2, 0x90, 0x0f, 0xd9, 0x0e, 0x4c, 0x82, 0xa5, 0x23, 0x1a, 0x3c, 0xee, 0x62,
- 0x10, 0xf4, 0xa0, 0x76, 0x4b, 0xf7, 0xa9, 0xdb, 0xd1, 0xdd, 0x6d, 0xb9, 0xda, 0x18, 0xfe, 0x0b,
- 0x5f, 0x52, 0x48, 0xe1, 0x17, 0x06, 0x49, 0x18, 0xf2, 0x21, 0x0e, 0xd4, 0x7c, 0xa9, 0x3e, 0x2b,
- 0xbb, 0xe7, 0xf0, 0x4c, 0x95, 0x22, 0xee, 0xc9, 0x80, 0x72, 0xf5, 0x8a, 0x21, 0x0f, 0xb2, 0x13,
- 0x3b, 0x7f, 0x59, 0x9c, 0xba, 0xdd, 0xc8, 0x60, 0x3f, 0x97, 0x50, 0xe1, 0x74, 0x93, 0x7e, 0x8e,
- 0xb3, 0xf6, 0x4e, 0x29, 0x1c, 0x96, 0xef, 0xb7, 0x55, 0xef, 0xe9, 0xb8, 0x55, 0xef, 0x42, 0xd2,
- 0xaa, 0x97, 0x70, 0xe8, 0x1e, 0x3e, 0x04, 0x35, 0x61, 0x4f, 0x2b, 0x1e, 0x83, 0x3d, 0xed, 0x29,
- 0xa8, 0xef, 0xf0, 0x91, 0x40, 0x9c, 0x65, 0x54, 0xe2, 0xd3, 0x08, 0x1f, 0xd9, 0x6f, 0x86, 0xc9,
- 0x18, 0xa5, 0x61, 0x45, 0xe4, 0x8d, 0x13, 0xc1, 0x61, 0xb0, 0xb2, 0x48, 0x33, 0x4c, 0xc6, 0x28,
- 0x0d, 0x8f, 0x5e, 0x33, 0xed, 0x6d, 0x51, 0xa0, 0xc2, 0x0b, 0x88, 0xe8, 0x35, 0x95, 0x88, 0x61,
- 0x3e, 0xb9, 0x04, 0xd5, 0x5e, 0x6b, 0x53, 0xd0, 0x56, 0x39, 0x2d, 0xd7, 0x30, 0xd7, 0xe7, 0x17,
- 0xe4, 0xd9, 0x4a, 0x2a, 0x97, 0xd5, 0xa4, 0xa3, 0x77, 0x55, 0x06, 0x5f, 0x1b, 0xca, 0x9a, 0xac,
- 0x84, 0xc9, 0x18, 0xa5, 0x21, 0x9f, 0x82, 0x31, 0x97, 0xb6, 0x7a, 0x06, 0x0d, 0x4a, 0x09, 0x73,
- 0x1c, 0x11, 0x57, 0x6b, 0x44, 0x73, 0x30, 0x41, 0x39, 0xc0, 0x2a, 0x58, 0x1f, 0x2a, 0xd6, 0xf6,
- 0xa7, 0x39, 0x20, 0xfd, 0xd1, 0xde, 0x64, 0x0b, 0xca, 0x36, 0xb7, 0x7e, 0x65, 0x3e, 0x3e, 0x3a,
- 0x62, 0x44, 0x13, 0xc3, 0x92, 0x4c, 0x90, 0xf8, 0xc4, 0x86, 0x2a, 0xbd, 0xed, 0x53, 0xd7, 0x0e,
- 0x76, 0x7f, 0x1c, 0xcd, 0x51, 0xd5, 0x62, 0x35, 0x20, 0x91, 0x31, 0xe0, 0xa1, 0xfd, 0x3c, 0x0f,
- 0xf5, 0x08, 0xdd, 0xbd, 0x16, 0x95, 0x7c, 0x03, 0xba, 0x30, 0x3a, 0xad, 0xbb, 0x96, 0xec, 0x61,
- 0x91, 0x0d, 0xe8, 0x32, 0x0b, 0x97, 0x31, 0x4a, 0x47, 0x66, 0x00, 0x3a, 0xba, 0xe7, 0x53, 0x97,
- 0xcf, 0xbe, 0x89, 0x6d, 0xdf, 0x2b, 0x41, 0x0e, 0x46, 0xa8, 0xc8, 0x45, 0x79, 0xd8, 0x78, 0x31,
- 0x7e, 0x4c, 0xdf, 0x80, 0x93, 0xc4, 0x4b, 0x47, 0x70, 0x92, 0x38, 0x69, 0xc3, 0xb8, 0xaa, 0xb5,
- 0xca, 0x3d, 0xdc, 0x21, 0x6e, 0x62, 0xfd, 0x92, 0x80, 0xc0, 0x3e, 0x50, 0xed, 0x9d, 0x1c, 0x8c,
- 0xc6, 0x4c, 0x1e, 0xe2, 0x80, 0x3d, 0xb5, 0x57, 0x21, 0x76, 0xc0, 0x5e, 0x64, 0x8b, 0xc1, 0x93,
- 0x50, 0x16, 0x02, 0x4a, 0x86, 0x20, 0x0a, 0x11, 0xa2, 0xcc, 0x65, 0x63, 0x99, 0x34, 0xaa, 0x26,
- 0xc7, 0x32, 0x69, 0x75, 0x45, 0x95, 0x2f, 0x9c, 0x13, 0xa2, 0x76, 0xfd, 0xce, 0x09, 0x91, 0x8e,
- 0x01, 0x85, 0xf6, 0x03, 0x5e, 0x6f, 0xdf, 0xdd, 0x0d, 0xd6, 0x72, 0x6d, 0xa8, 0xc8, 0xb0, 0x33,
- 0xd9, 0x35, 0x5e, 0xcc, 0x60, 0x87, 0xe1, 0x38, 0x32, 0xc0, 0x4a, 0x37, 0xb6, 0x6f, 0x6c, 0x6e,
- 0xa2, 0x42, 0x27, 0x97, 0xa1, 0xe6, 0xd8, 0x0b, 0xba, 0x69, 0xf5, 0x5c, 0x35, 0xb2, 0x7f, 0x84,
- 0x8d, 0x55, 0x37, 0x54, 0xe2, 0x9d, 0xbd, 0xc9, 0x33, 0xc1, 0x4b, 0xac, 0x92, 0x18, 0x96, 0xd4,
- 0xfe, 0xbe, 0x00, 0x3c, 0xe4, 0x88, 0x3c, 0x07, 0xb5, 0x0e, 0x35, 0xb6, 0x74, 0xdb, 0xf4, 0xd4,
- 0x11, 0xa1, 0x6c, 0x7d, 0x5e, 0x5b, 0x51, 0x89, 0x77, 0x98, 0x08, 0x66, 0x9b, 0xcb, 0x3c, 0xaa,
- 0x3f, 0xa4, 0x25, 0x06, 0x94, 0xdb, 0x9e, 0xa7, 0x77, 0xcd, 0xcc, 0x1e, 0x6f, 0x71, 0x24, 0xa3,
- 0x18, 0x06, 0xc4, 0x33, 0x4a, 0x68, 0x62, 0x40, 0xa9, 0x6b, 0xe9, 0xa6, 0x9d, 0xf9, 0x6a, 0x1c,
- 0xf6, 0x05, 0xab, 0x0c, 0x49, 0x18, 0xa5, 0xf8, 0x23, 0x0a, 0x6c, 0xd2, 0x83, 0xba, 0x67, 0xb8,
- 0x7a, 0xc7, 0xdb, 0xd2, 0x67, 0x9e, 0x79, 0x36, 0xb3, 0x9a, 0x17, 0xb2, 0x12, 0xb3, 0xce, 0x1c,
- 0xce, 0xae, 0x34, 0xaf, 0xcc, 0xce, 0x3c, 0xf3, 0x2c, 0x46, 0xf9, 0x44, 0xd9, 0x3e, 0xf3, 0xd4,
- 0x8c, 0xec, 0xb9, 0x47, 0xce, 0xf6, 0x99, 0xa7, 0x66, 0x30, 0xca, 0x47, 0xfb, 0xbb, 0x1c, 0xd4,
- 0x02, 0x5a, 0xb2, 0x0e, 0xc0, 0xc6, 0x10, 0x79, 0x88, 0xe2, 0xa1, 0x2e, 0x34, 0xe0, 0xeb, 0xfa,
- 0xf5, 0xa0, 0x30, 0x46, 0x80, 0x52, 0x4e, 0x99, 0xcc, 0x1f, 0xf5, 0x29, 0x93, 0xd3, 0x50, 0xdb,
- 0xd2, 0xed, 0x96, 0xb7, 0xa5, 0x6f, 0x8b, 0xa1, 0x34, 0x72, 0xee, 0xea, 0x15, 0x95, 0x81, 0x21,
- 0x8d, 0xf6, 0x5b, 0x65, 0x10, 0x6e, 0x6a, 0xd6, 0xd9, 0x5b, 0xa6, 0x27, 0xe2, 0xa4, 0x73, 0xbc,
- 0x64, 0xd0, 0xd9, 0xe7, 0x65, 0x3a, 0x06, 0x14, 0xe4, 0x2c, 0x14, 0x3a, 0xa6, 0x2d, 0x7d, 0x36,
- 0xdc, 0x64, 0xb7, 0x62, 0xda, 0xc8, 0xd2, 0x78, 0x96, 0x7e, 0x5b, 0x86, 0xb8, 0x89, 0x2c, 0xfd,
- 0x36, 0xb2, 0x34, 0xb6, 0x88, 0xb6, 0x1c, 0x67, 0x9b, 0x75, 0x5b, 0x15, 0x09, 0x57, 0xe4, 0x53,
- 0x39, 0x5f, 0x44, 0x2f, 0xc7, 0xb3, 0x30, 0x49, 0x4b, 0xd6, 0xe1, 0x91, 0x37, 0xa8, 0xeb, 0xc8,
- 0x71, 0xaa, 0x69, 0x51, 0xda, 0x55, 0x30, 0x42, 0x09, 0xe2, 0x01, 0x75, 0x5f, 0x4c, 0x27, 0xc1,
- 0x41, 0x65, 0x79, 0x68, 0xae, 0xee, 0xb6, 0xa9, 0xbf, 0xea, 0x3a, 0x06, 0xf5, 0x3c, 0xd3, 0x6e,
- 0x2b, 0xd8, 0x72, 0x08, 0xbb, 0x96, 0x4e, 0x82, 0x83, 0xca, 0x92, 0x97, 0x61, 0x42, 0x64, 0x09,
- 0x75, 0x61, 0x76, 0x47, 0x37, 0x2d, 0x7d, 0xc3, 0xb4, 0xd4, 0x8d, 0x72, 0xa3, 0xc2, 0x33, 0xb2,
- 0x36, 0x80, 0x06, 0x07, 0x96, 0x26, 0x57, 0x61, 0x5c, 0xf9, 0xc5, 0x56, 0xa9, 0xdb, 0x0c, 0x42,
- 0x17, 0x46, 0x1b, 0x17, 0xd8, 0x8a, 0x75, 0x9e, 0x76, 0x5d, 0x6a, 0x44, 0xbd, 0x81, 0x8a, 0x0a,
- 0xfb, 0xca, 0x11, 0x84, 0x33, 0x3c, 0x3e, 0x61, 0xbd, 0x3b, 0xe7, 0x38, 0x56, 0xcb, 0xb9, 0x65,
- 0xab, 0x6f, 0x17, 0xaa, 0x19, 0x77, 0x85, 0x35, 0x53, 0x29, 0x70, 0x40, 0x49, 0xf6, 0xe5, 0x3c,
- 0x67, 0xde, 0xb9, 0x65, 0x27, 0x51, 0x21, 0xfc, 0xf2, 0xe6, 0x00, 0x1a, 0x1c, 0x58, 0x9a, 0x2c,
- 0x00, 0x49, 0x7e, 0xc1, 0x7a, 0x57, 0xba, 0x57, 0xcf, 0x88, 0xf3, 0x50, 0x92, 0xb9, 0x98, 0x52,
- 0x82, 0x2c, 0xc3, 0xe9, 0x64, 0x2a, 0x63, 0x27, 0xbd, 0xac, 0xfc, 0x24, 0x54, 0x4c, 0xc9, 0xc7,
- 0xd4, 0x52, 0xda, 0x6f, 0xe7, 0x61, 0x34, 0xb6, 0x81, 0xfe, 0x81, 0xdb, 0xa8, 0xcc, 0x74, 0xe8,
- 0x8e, 0xd7, 0x5e, 0x9a, 0xbf, 0x42, 0xf5, 0x16, 0x75, 0xaf, 0x51, 0x75, 0xd8, 0x01, 0x1f, 0x54,
- 0x56, 0x62, 0x39, 0x98, 0xa0, 0x24, 0x9b, 0x50, 0x12, 0x16, 0xe1, 0xac, 0x57, 0x63, 0x28, 0x19,
- 0x71, 0xb3, 0xb0, 0xbc, 0x4f, 0xc6, 0x71, 0x29, 0x0a, 0x78, 0xcd, 0x87, 0x91, 0x28, 0x05, 0x1b,
- 0x48, 0x42, 0x75, 0xb3, 0x12, 0x53, 0x35, 0x97, 0xa0, 0xe0, 0xfb, 0xc3, 0x6e, 0x81, 0x16, 0x1e,
- 0x86, 0xb5, 0x65, 0x64, 0x18, 0xda, 0x26, 0xfb, 0x77, 0x9e, 0x67, 0x3a, 0xb6, 0x3c, 0x0f, 0x7b,
- 0x1d, 0x2a, 0xbe, 0x34, 0xb2, 0x0d, 0xb7, 0x85, 0x9b, 0xeb, 0x28, 0xca, 0xc0, 0xa6, 0xb0, 0xb4,
- 0x3f, 0xcd, 0x43, 0x2d, 0x58, 0x10, 0x1f, 0xe0, 0x9c, 0x69, 0x07, 0x6a, 0x41, 0x7c, 0x55, 0xe6,
- 0xdb, 0xf6, 0xc2, 0xb0, 0x1f, 0xbe, 0x86, 0x0b, 0x5e, 0x31, 0xe4, 0x11, 0x8d, 0xdd, 0x2a, 0x64,
- 0x88, 0xdd, 0xea, 0x42, 0xc5, 0x77, 0xcd, 0x76, 0x5b, 0x6a, 0xe7, 0x59, 0x82, 0xb7, 0x02, 0x71,
- 0xad, 0x09, 0x40, 0x29, 0x59, 0xf1, 0x82, 0x8a, 0x8d, 0xf6, 0x1a, 0x8c, 0x27, 0x29, 0xb9, 0xea,
- 0x6a, 0x6c, 0xd1, 0x56, 0xcf, 0x52, 0x32, 0x0e, 0x55, 0x57, 0x99, 0x8e, 0x01, 0x05, 0x5b, 0xbe,
- 0xb2, 0xdf, 0xf4, 0x86, 0x63, 0x2b, 0xf5, 0x91, 0xaf, 0x02, 0xd6, 0x64, 0x1a, 0x06, 0xb9, 0xda,
- 0x5f, 0x15, 0xe0, 0x6c, 0x68, 0xd6, 0x58, 0xd1, 0x6d, 0xbd, 0x7d, 0x80, 0x2b, 0xd6, 0x3e, 0xd8,
- 0x14, 0x73, 0xd8, 0xcb, 0x02, 0x0a, 0x0f, 0xc0, 0x65, 0x01, 0xff, 0x90, 0x07, 0x1e, 0x0b, 0x4a,
- 0xbe, 0x0a, 0x23, 0x7a, 0xe4, 0x76, 0x4d, 0xf9, 0x3b, 0x2f, 0x67, 0xfe, 0x9d, 0x3c, 0xe4, 0x34,
- 0x88, 0x6d, 0x8a, 0xa6, 0x62, 0x8c, 0x21, 0x71, 0xa0, 0xba, 0xa9, 0x5b, 0x16, 0xd3, 0x85, 0x32,
- 0xbb, 0x69, 0x62, 0xcc, 0x79, 0x33, 0x5f, 0x90, 0xd0, 0x18, 0x30, 0x21, 0x6f, 0xe6, 0x60, 0xd4,
- 0x8d, 0x2e, 0x93, 0xe4, 0x0f, 0xc9, 0xe2, 0xc4, 0x8f, 0xa0, 0x45, 0x23, 0xa9, 0xa2, 0x6b, 0xb1,
- 0x38, 0x4f, 0xed, 0x2f, 0x73, 0x30, 0xda, 0xb4, 0xcc, 0x96, 0x69, 0xb7, 0x8f, 0xf1, 0xae, 0x82,
- 0x1b, 0x50, 0xf2, 0x2c, 0xb3, 0x45, 0x87, 0x9c, 0x4d, 0xc4, 0x3c, 0xc6, 0x00, 0x50, 0xe0, 0xc4,
- 0x2f, 0x3f, 0x28, 0x1c, 0xe0, 0xf2, 0x83, 0x5f, 0x95, 0x41, 0x46, 0x35, 0x93, 0x1e, 0xd4, 0xda,
- 0xea, 0x4c, 0x75, 0xf9, 0x8d, 0x57, 0x32, 0x9c, 0xc7, 0x17, 0x3b, 0x9d, 0x5d, 0x8c, 0xfd, 0x41,
- 0x22, 0x86, 0x9c, 0x08, 0x8d, 0x5f, 0xeb, 0x3a, 0x9f, 0xf1, 0x5a, 0x57, 0xc1, 0xae, 0xff, 0x62,
- 0x57, 0x1d, 0x8a, 0x5b, 0xbe, 0xdf, 0x95, 0x8d, 0x69, 0xf8, 0xb0, 0xf5, 0xf0, 0x48, 0x18, 0xa1,
- 0x13, 0xb1, 0x77, 0xe4, 0xd0, 0x8c, 0x85, 0xad, 0x07, 0xd7, 0x78, 0xcd, 0x65, 0x0a, 0x18, 0x88,
- 0xb2, 0x60, 0xef, 0xc8, 0xa1, 0xc9, 0x57, 0xa0, 0xee, 0xbb, 0xba, 0xed, 0x6d, 0x3a, 0x6e, 0x87,
- 0xba, 0x72, 0x8d, 0xba, 0x90, 0xe1, 0x66, 0xd3, 0xb5, 0x10, 0x4d, 0x78, 0x22, 0x63, 0x49, 0x18,
- 0xe5, 0x46, 0xb6, 0xa1, 0xda, 0x6b, 0x89, 0x8a, 0x49, 0xf3, 0xd3, 0x6c, 0x96, 0xcb, 0x6a, 0x23,
- 0xe1, 0x00, 0xea, 0x0d, 0x03, 0x06, 0xf1, 0x1b, 0xeb, 0x2a, 0x47, 0x75, 0x63, 0x5d, 0xb4, 0x35,
- 0xa6, 0x9d, 0x57, 0x41, 0x3a, 0x52, 0xaf, 0xb5, 0xdb, 0x32, 0x9a, 0x69, 0x21, 0xb3, 0xca, 0x29,
- 0x58, 0xd6, 0x03, 0xdd, 0xd8, 0x6e, 0xa3, 0xe2, 0xa1, 0x75, 0x40, 0x7a, 0x09, 0x88, 0x11, 0xbb,
- 0xd7, 0x45, 0x6c, 0xa2, 0x9a, 0x3e, 0xd8, 0x78, 0x10, 0x5c, 0x30, 0x12, 0x39, 0x57, 0x3a, 0xf5,
- 0x02, 0x17, 0xed, 0xcf, 0xf2, 0x50, 0x58, 0x5b, 0x6e, 0x8a, 0xb3, 0x22, 0xf9, 0xa5, 0x49, 0xb4,
- 0xb9, 0x6d, 0x76, 0x6f, 0x52, 0xd7, 0xdc, 0xdc, 0x95, 0x4b, 0xef, 0xc8, 0x59, 0x91, 0x49, 0x0a,
- 0x4c, 0x29, 0x45, 0x5e, 0x81, 0x11, 0x43, 0x9f, 0xa3, 0xae, 0x3f, 0x8c, 0x61, 0x81, 0xef, 0x16,
- 0x9d, 0x9b, 0x0d, 0x8b, 0x63, 0x0c, 0x8c, 0xac, 0x03, 0x18, 0x21, 0x74, 0xe1, 0xd0, 0xe6, 0x90,
- 0x08, 0x70, 0x04, 0x88, 0x20, 0xd4, 0xb6, 0x19, 0x29, 0x47, 0x2d, 0x1e, 0x06, 0x95, 0xb7, 0x9c,
- 0x6b, 0xaa, 0x2c, 0x86, 0x30, 0x9a, 0x0d, 0xa3, 0xb1, 0xcb, 0x5e, 0xc8, 0x27, 0xa1, 0xea, 0x74,
- 0x23, 0xc3, 0x69, 0x8d, 0xc7, 0x4d, 0x56, 0x6f, 0xc8, 0xb4, 0x3b, 0x7b, 0x93, 0xa3, 0xcb, 0x4e,
- 0xdb, 0x34, 0x54, 0x02, 0x06, 0xe4, 0x44, 0x83, 0x32, 0xdf, 0xe2, 0xa5, 0xae, 0x7a, 0xe1, 0x73,
- 0x07, 0xbf, 0x8d, 0xc1, 0x43, 0x99, 0xa3, 0x7d, 0xad, 0x08, 0xa1, 0x6f, 0x8d, 0x78, 0x50, 0x16,
- 0x21, 0xec, 0x72, 0xe4, 0x3e, 0xd6, 0x68, 0x79, 0xc9, 0x8a, 0xb4, 0xa1, 0xf0, 0x9a, 0xb3, 0x91,
- 0x79, 0xe0, 0x8e, 0xec, 0xed, 0x16, 0xb6, 0xb2, 0x48, 0x02, 0x32, 0x0e, 0xe4, 0xbf, 0xe7, 0xe0,
- 0xa4, 0x97, 0x54, 0x7d, 0x65, 0x73, 0xc0, 0xec, 0x3a, 0x7e, 0x52, 0x99, 0x96, 0x01, 0xae, 0x83,
- 0xb2, 0xb1, 0xbf, 0x2e, 0x4c, 0xfe, 0xc2, 0xe9, 0x25, 0x9b, 0xd3, 0x62, 0xc6, 0x0b, 0x0a, 0xe3,
- 0xf2, 0x8f, 0xa7, 0xa1, 0x64, 0xa5, 0x7d, 0x23, 0x0f, 0xf5, 0xc8, 0x68, 0x9d, 0xf9, 0x06, 0xa1,
- 0xdb, 0x89, 0x1b, 0x84, 0x56, 0x87, 0xf7, 0x01, 0x87, 0xb5, 0x3a, 0xee, 0x4b, 0x84, 0x7e, 0x2f,
- 0x0f, 0x85, 0xf5, 0xf9, 0x85, 0xf8, 0xa2, 0x35, 0x77, 0x1f, 0x16, 0xad, 0x5b, 0x50, 0xd9, 0xe8,
- 0x99, 0x96, 0x6f, 0xda, 0x99, 0x4f, 0x9f, 0x50, 0x17, 0x2e, 0x49, 0x1f, 0x83, 0x40, 0x45, 0x05,
- 0x4f, 0xda, 0x50, 0x69, 0x8b, 0xe3, 0xff, 0x32, 0x47, 0xc6, 0xc9, 0x63, 0x04, 0x05, 0x23, 0xf9,
- 0x82, 0x0a, 0x5d, 0xdb, 0x05, 0x79, 0x73, 0xfc, 0x7d, 0x97, 0xa6, 0xf6, 0x15, 0x08, 0xb4, 0x80,
- 0xfb, 0xcf, 0xfc, 0x6f, 0x72, 0x10, 0x57, 0x7c, 0xee, 0x7f, 0x6b, 0xda, 0x4e, 0xb6, 0xa6, 0xf9,
- 0xa3, 0xe8, 0x7c, 0xe9, 0x0d, 0x4a, 0xfb, 0xcd, 0x3c, 0x94, 0xef, 0xdb, 0x8e, 0x61, 0x1a, 0x0b,
- 0xf2, 0x9b, 0xcb, 0x38, 0x30, 0x0e, 0x0c, 0xf1, 0xeb, 0x24, 0x42, 0xfc, 0xb2, 0x5e, 0x11, 0x7b,
- 0x8f, 0x00, 0xbf, 0x3f, 0xca, 0x81, 0x1c, 0x96, 0x97, 0x6c, 0xcf, 0xd7, 0x6d, 0x83, 0x12, 0x23,
- 0x98, 0x03, 0xb2, 0x46, 0x92, 0xc8, 0x68, 0x2b, 0x31, 0xed, 0xf3, 0x67, 0x35, 0xe6, 0x93, 0x8f,
- 0x43, 0x75, 0xcb, 0xf1, 0x7c, 0x3e, 0xce, 0xe7, 0xe3, 0xd6, 0xa5, 0x2b, 0x32, 0x1d, 0x03, 0x8a,
- 0xa4, 0xc7, 0xb5, 0x34, 0xd8, 0xe3, 0xaa, 0x7d, 0x37, 0x0f, 0x23, 0xef, 0x97, 0x6d, 0xcf, 0x69,
- 0x21, 0x91, 0x85, 0x8c, 0x21, 0x91, 0xc5, 0xc3, 0x84, 0x44, 0x6a, 0x3f, 0xce, 0x01, 0xdc, 0xb7,
- 0x3d, 0xd7, 0xad, 0x78, 0xb4, 0x62, 0xe6, 0x76, 0x95, 0x1e, 0xab, 0xf8, 0xff, 0x4b, 0xea, 0x93,
- 0x78, 0xa4, 0xe2, 0x5b, 0x39, 0x18, 0xd3, 0x63, 0xd1, 0x7f, 0x99, 0x55, 0xcb, 0x44, 0x30, 0x61,
- 0xb0, 0xbf, 0x34, 0x9e, 0x8e, 0x09, 0xb6, 0xe4, 0xf9, 0xf0, 0xbc, 0xdf, 0xeb, 0x61, 0xb3, 0xef,
- 0x3b, 0xa8, 0x97, 0xab, 0x39, 0x31, 0xca, 0x7b, 0x44, 0x5b, 0x16, 0x8e, 0x24, 0xda, 0x32, 0xba,
- 0x8f, 0xac, 0x78, 0xd7, 0x7d, 0x64, 0x3b, 0x50, 0xdb, 0x74, 0x9d, 0x0e, 0x0f, 0x68, 0x94, 0x97,
- 0xcb, 0x5e, 0xce, 0x30, 0xa7, 0x84, 0xd7, 0xaa, 0x87, 0x36, 0x9e, 0x05, 0x85, 0x8f, 0x21, 0x2b,
- 0x6e, 0x16, 0x77, 0x04, 0xd7, 0xf2, 0x51, 0x72, 0x0d, 0xc6, 0x92, 0x35, 0x81, 0x8e, 0x8a, 0x4d,
- 0x3c, 0x88, 0xb1, 0x72, 0x7f, 0x82, 0x18, 0xb5, 0xef, 0x97, 0xd5, 0x00, 0xf6, 0xc0, 0x1d, 0x2d,
- 0xf9, 0xfe, 0xdf, 0xab, 0x9b, 0xdc, 0x48, 0x5b, 0xb9, 0x8f, 0x1b, 0x69, 0xab, 0x47, 0xb3, 0x91,
- 0xb6, 0x96, 0x6d, 0x23, 0x2d, 0x64, 0xdf, 0x48, 0x5b, 0xcf, 0xb6, 0x91, 0x76, 0x64, 0xa8, 0x8d,
- 0xb4, 0xa3, 0x07, 0xda, 0x48, 0xbb, 0x57, 0x80, 0xc4, 0x2a, 0xf3, 0x03, 0x8f, 0xd2, 0x3f, 0x29,
- 0x8f, 0xd2, 0xdb, 0x79, 0x08, 0x87, 0xcd, 0x43, 0x46, 0xdc, 0xbc, 0x0c, 0xd5, 0x8e, 0x7e, 0x7b,
- 0x9e, 0x5a, 0xfa, 0x6e, 0x96, 0xfb, 0x53, 0x57, 0x24, 0x06, 0x06, 0x68, 0xc4, 0x03, 0x30, 0x83,
- 0x33, 0xcc, 0x33, 0xdb, 0xe6, 0xc3, 0xe3, 0xd0, 0x85, 0xf5, 0x2f, 0x7c, 0xc7, 0x08, 0x1b, 0xed,
- 0x0f, 0xf3, 0x20, 0x0f, 0xbb, 0x27, 0x14, 0x4a, 0x9b, 0xe6, 0x6d, 0xda, 0xca, 0x1c, 0x3f, 0x1b,
- 0xb9, 0xd5, 0x5a, 0x38, 0x1f, 0x78, 0x02, 0x0a, 0x74, 0x6e, 0x55, 0x16, 0xce, 0x24, 0x29, 0xbf,
- 0x0c, 0x56, 0xe5, 0xa8, 0x53, 0x4a, 0x5a, 0x95, 0x45, 0x12, 0x2a, 0x1e, 0xc2, 0x88, 0xcd, 0xe3,
- 0x0a, 0x32, 0xfb, 0xce, 0x62, 0xf1, 0x09, 0xca, 0x88, 0xed, 0x89, 0x9d, 0xf4, 0x92, 0x47, 0xe3,
- 0x4b, 0x3f, 0xfa, 0xc9, 0x85, 0x87, 0x7e, 0xfc, 0x93, 0x0b, 0x0f, 0xbd, 0xfb, 0x93, 0x0b, 0x0f,
- 0x7d, 0x6d, 0xff, 0x42, 0xee, 0x47, 0xfb, 0x17, 0x72, 0x3f, 0xde, 0xbf, 0x90, 0x7b, 0x77, 0xff,
- 0x42, 0xee, 0xcf, 0xf7, 0x2f, 0xe4, 0xfe, 0xcb, 0x5f, 0x5c, 0x78, 0xe8, 0x8b, 0xcf, 0x85, 0x55,
- 0x98, 0x56, 0x55, 0x98, 0x56, 0x0c, 0xa7, 0xbb, 0xdb, 0xed, 0x69, 0x56, 0x85, 0x30, 0x45, 0x55,
- 0xe1, 0x1f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x32, 0x6d, 0x43, 0x50, 0x97, 0x00, 0x00,
+ 0x95, 0x9e, 0xfa, 0xbf, 0xfb, 0x34, 0xc9, 0xe1, 0xdc, 0x19, 0x8d, 0x38, 0xa3, 0xd1, 0x70, 0xb6,
+ 0xb4, 0xd2, 0xce, 0x66, 0xbd, 0x64, 0xc4, 0xe8, 0xcf, 0xfb, 0x63, 0x89, 0x4d, 0x0e, 0x39, 0x9c,
+ 0x21, 0x67, 0xe8, 0xd3, 0xe4, 0x48, 0x5e, 0x65, 0xad, 0x14, 0xab, 0x2e, 0x9b, 0x25, 0x56, 0x57,
+ 0xb5, 0xaa, 0xaa, 0x39, 0x43, 0x6d, 0x02, 0xef, 0xae, 0x02, 0x48, 0x41, 0x10, 0x24, 0xd8, 0x27,
+ 0x03, 0x81, 0x13, 0x24, 0x48, 0xe0, 0x07, 0xc3, 0x79, 0x08, 0xe0, 0x3c, 0x18, 0x48, 0x1c, 0x07,
+ 0x41, 0xe2, 0x04, 0xf9, 0xf1, 0x43, 0x80, 0x28, 0x2f, 0x44, 0xcc, 0x20, 0x0f, 0x09, 0x10, 0xc3,
+ 0x88, 0x91, 0xc4, 0x1e, 0x18, 0x71, 0x70, 0xff, 0xea, 0xaf, 0xab, 0x67, 0xc8, 0x2e, 0x72, 0x34,
+ 0xca, 0xea, 0xad, 0xea, 0x9e, 0x73, 0xbf, 0x73, 0xeb, 0xd6, 0xfd, 0x39, 0xf7, 0x9c, 0x73, 0xef,
+ 0x85, 0xe5, 0x8e, 0x15, 0xec, 0xf4, 0xb7, 0x66, 0x0c, 0xb7, 0x3b, 0xeb, 0xf4, 0xbb, 0x7a, 0xcf,
+ 0x73, 0xdf, 0xe3, 0x0f, 0xdb, 0xb6, 0x7b, 0x6f, 0xb6, 0xb7, 0xdb, 0x99, 0xd5, 0x7b, 0x96, 0x1f,
+ 0xa5, 0xec, 0xbd, 0xa4, 0xdb, 0xbd, 0x1d, 0xfd, 0xa5, 0xd9, 0x0e, 0x75, 0xa8, 0xa7, 0x07, 0xd4,
+ 0x9c, 0xe9, 0x79, 0x6e, 0xe0, 0x92, 0xd7, 0x22, 0xa0, 0x19, 0x05, 0x34, 0xa3, 0xb2, 0xcd, 0xf4,
+ 0x76, 0x3b, 0x33, 0x0c, 0x28, 0x4a, 0x51, 0x40, 0x97, 0x7e, 0x33, 0x56, 0x82, 0x8e, 0xdb, 0x71,
+ 0x67, 0x39, 0xde, 0x56, 0x7f, 0x9b, 0xbf, 0xf1, 0x17, 0xfe, 0x24, 0xe4, 0x5c, 0xd2, 0x76, 0x5f,
+ 0xf7, 0x67, 0x2c, 0x97, 0x15, 0x6b, 0xd6, 0x70, 0x3d, 0x3a, 0xbb, 0x37, 0x50, 0x96, 0x4b, 0x2f,
+ 0x47, 0x3c, 0x5d, 0xdd, 0xd8, 0xb1, 0x1c, 0xea, 0xed, 0xab, 0x6f, 0x99, 0xf5, 0xa8, 0xef, 0xf6,
+ 0x3d, 0x83, 0x1e, 0x2b, 0x97, 0x3f, 0xdb, 0xa5, 0x81, 0x9e, 0x25, 0x6b, 0x76, 0x58, 0x2e, 0xaf,
+ 0xef, 0x04, 0x56, 0x77, 0x50, 0xcc, 0xab, 0x8f, 0xca, 0xe0, 0x1b, 0x3b, 0xb4, 0xab, 0xa7, 0xf3,
+ 0x69, 0xdf, 0x07, 0x38, 0x37, 0xbf, 0xe5, 0x07, 0x9e, 0x6e, 0x04, 0xeb, 0xae, 0xb9, 0x41, 0xbb,
+ 0x3d, 0x5b, 0x0f, 0x28, 0xd9, 0x85, 0x3a, 0x2b, 0x9b, 0xa9, 0x07, 0xfa, 0x54, 0xe1, 0x6a, 0xe1,
+ 0x5a, 0x73, 0x6e, 0x7e, 0x66, 0xc4, 0x7f, 0x31, 0xb3, 0x26, 0x81, 0x5a, 0x63, 0x87, 0x07, 0xd3,
+ 0x75, 0xf5, 0x86, 0xa1, 0x00, 0xf2, 0xf5, 0x02, 0x8c, 0x39, 0xae, 0x49, 0xdb, 0xd4, 0xa6, 0x46,
+ 0xe0, 0x7a, 0x53, 0xc5, 0xab, 0xa5, 0x6b, 0xcd, 0xb9, 0xaf, 0x8e, 0x2c, 0x31, 0xe3, 0x8b, 0x66,
+ 0x6e, 0xc7, 0x04, 0x5c, 0x77, 0x02, 0x6f, 0xbf, 0x75, 0xfe, 0x07, 0x07, 0xd3, 0x4f, 0x1d, 0x1e,
+ 0x4c, 0x8f, 0xc5, 0x49, 0x98, 0x28, 0x09, 0xd9, 0x84, 0x66, 0xe0, 0xda, 0xac, 0xca, 0x2c, 0xd7,
+ 0xf1, 0xa7, 0x4a, 0xbc, 0x60, 0x57, 0x66, 0x44, 0x6d, 0x33, 0xf1, 0x33, 0xac, 0xb9, 0xcc, 0xec,
+ 0xbd, 0x34, 0xb3, 0x11, 0xb2, 0xb5, 0xce, 0x49, 0xe0, 0x66, 0x94, 0xe6, 0x63, 0x1c, 0x87, 0x50,
+ 0x38, 0xe3, 0x53, 0xa3, 0xef, 0x59, 0xc1, 0xfe, 0x82, 0xeb, 0x04, 0xf4, 0x7e, 0x30, 0x55, 0xe6,
+ 0xb5, 0xfc, 0x62, 0x16, 0xf4, 0xba, 0x6b, 0xb6, 0x93, 0xdc, 0xad, 0x73, 0x87, 0x07, 0xd3, 0x67,
+ 0x52, 0x89, 0x98, 0xc6, 0x24, 0x0e, 0x4c, 0x5a, 0x5d, 0xbd, 0x43, 0xd7, 0xfb, 0xb6, 0xdd, 0xa6,
+ 0x86, 0x47, 0x03, 0x7f, 0xaa, 0xc2, 0x3f, 0xe1, 0x5a, 0x96, 0x9c, 0x55, 0xd7, 0xd0, 0xed, 0x3b,
+ 0x5b, 0xef, 0x51, 0x23, 0x40, 0xba, 0x4d, 0x3d, 0xea, 0x18, 0xb4, 0x35, 0x25, 0x3f, 0x66, 0x72,
+ 0x25, 0x85, 0x84, 0x03, 0xd8, 0x64, 0x19, 0xce, 0xf6, 0x3c, 0xcb, 0xe5, 0x45, 0xb0, 0x75, 0xdf,
+ 0xbf, 0xad, 0x77, 0xe9, 0x54, 0xf5, 0x6a, 0xe1, 0x5a, 0xa3, 0x75, 0x51, 0xc2, 0x9c, 0x5d, 0x4f,
+ 0x33, 0xe0, 0x60, 0x1e, 0x72, 0x0d, 0xea, 0x2a, 0x71, 0xaa, 0x76, 0xb5, 0x70, 0xad, 0x22, 0xda,
+ 0x8e, 0xca, 0x8b, 0x21, 0x95, 0x2c, 0x41, 0x5d, 0xdf, 0xde, 0xb6, 0x1c, 0xc6, 0x59, 0xe7, 0x55,
+ 0x78, 0x39, 0xeb, 0xd3, 0xe6, 0x25, 0x8f, 0xc0, 0x51, 0x6f, 0x18, 0xe6, 0x25, 0x37, 0x81, 0xf8,
+ 0xd4, 0xdb, 0xb3, 0x0c, 0x3a, 0x6f, 0x18, 0x6e, 0xdf, 0x09, 0x78, 0xd9, 0x1b, 0xbc, 0xec, 0x97,
+ 0x64, 0xd9, 0x49, 0x7b, 0x80, 0x03, 0x33, 0x72, 0x91, 0x37, 0x61, 0x52, 0x76, 0xbb, 0xa8, 0x16,
+ 0x80, 0x23, 0x9d, 0x67, 0x15, 0x89, 0x29, 0x1a, 0x0e, 0x70, 0x13, 0x13, 0x2e, 0xeb, 0xfd, 0xc0,
+ 0xed, 0x32, 0xc8, 0xa4, 0xd0, 0x0d, 0x77, 0x97, 0x3a, 0x53, 0xcd, 0xab, 0x85, 0x6b, 0xf5, 0xd6,
+ 0xd5, 0xc3, 0x83, 0xe9, 0xcb, 0xf3, 0x0f, 0xe1, 0xc3, 0x87, 0xa2, 0x90, 0x3b, 0xd0, 0x30, 0x1d,
+ 0x7f, 0xdd, 0xb5, 0x2d, 0x63, 0x7f, 0x6a, 0x8c, 0x17, 0xf0, 0x25, 0xf9, 0xa9, 0x8d, 0xc5, 0xdb,
+ 0x6d, 0x41, 0x78, 0x70, 0x30, 0x7d, 0x79, 0x70, 0x74, 0x9c, 0x09, 0xe9, 0x18, 0x61, 0x90, 0x35,
+ 0x0e, 0xb8, 0xe0, 0x3a, 0xdb, 0x56, 0x67, 0x6a, 0x9c, 0xff, 0x8d, 0xab, 0x43, 0x1a, 0xf4, 0xe2,
+ 0xed, 0xb6, 0xe0, 0x6b, 0x8d, 0x4b, 0x71, 0xe2, 0x15, 0x23, 0x04, 0x62, 0xc2, 0x84, 0x1a, 0x57,
+ 0x17, 0x6c, 0xdd, 0xea, 0xfa, 0x53, 0x13, 0xbc, 0xf1, 0xfe, 0xea, 0x10, 0x4c, 0x8c, 0x33, 0xb7,
+ 0x2e, 0xc8, 0x4f, 0x99, 0x48, 0x24, 0xfb, 0x98, 0xc2, 0xbc, 0xf4, 0x06, 0x9c, 0x1d, 0x18, 0x1b,
+ 0xc8, 0x24, 0x94, 0x76, 0xe9, 0x3e, 0x1f, 0xfa, 0x1a, 0xc8, 0x1e, 0xc9, 0x79, 0xa8, 0xec, 0xe9,
+ 0x76, 0x9f, 0x4e, 0x15, 0x79, 0x9a, 0x78, 0xf9, 0xad, 0xe2, 0xeb, 0x05, 0xed, 0xef, 0x96, 0x60,
+ 0x4c, 0x8d, 0x38, 0x6d, 0xcb, 0xd9, 0x25, 0x6f, 0x41, 0xc9, 0x76, 0x3b, 0x72, 0xdc, 0xfc, 0x9d,
+ 0x91, 0x47, 0xb1, 0x55, 0xb7, 0xd3, 0xaa, 0x1d, 0x1e, 0x4c, 0x97, 0x56, 0xdd, 0x0e, 0x32, 0x44,
+ 0x62, 0x40, 0x65, 0x57, 0xdf, 0xde, 0xd5, 0x79, 0x19, 0x9a, 0x73, 0xad, 0x91, 0xa1, 0x6f, 0x31,
+ 0x14, 0x56, 0xd6, 0x56, 0xe3, 0xf0, 0x60, 0xba, 0xc2, 0x5f, 0x51, 0x60, 0x13, 0x17, 0x1a, 0x5b,
+ 0xb6, 0x6e, 0xec, 0xee, 0xb8, 0x36, 0x9d, 0x2a, 0xe5, 0x14, 0xd4, 0x52, 0x48, 0xe2, 0x37, 0x87,
+ 0xaf, 0x18, 0xc9, 0x20, 0x06, 0x54, 0xfb, 0xa6, 0x6f, 0x39, 0xbb, 0x72, 0x0c, 0x7c, 0x63, 0x64,
+ 0x69, 0x9b, 0x8b, 0xfc, 0x9b, 0xe0, 0xf0, 0x60, 0xba, 0x2a, 0x9e, 0x51, 0x42, 0x6b, 0x3f, 0x6e,
+ 0xc2, 0x84, 0xfa, 0x49, 0x77, 0xa9, 0x17, 0xd0, 0xfb, 0xe4, 0x2a, 0x94, 0x1d, 0xd6, 0x35, 0xf9,
+ 0x4f, 0x6e, 0x8d, 0xc9, 0xe6, 0x52, 0xe6, 0x5d, 0x92, 0x53, 0x58, 0xc9, 0x44, 0x53, 0x91, 0x15,
+ 0x3e, 0x7a, 0xc9, 0xda, 0x1c, 0x46, 0x94, 0x4c, 0x3c, 0xa3, 0x84, 0x26, 0xef, 0x40, 0x99, 0x7f,
+ 0xbc, 0xa8, 0xea, 0xdf, 0x1d, 0x5d, 0x04, 0xfb, 0xf4, 0x3a, 0xfb, 0x02, 0xfe, 0xe1, 0x1c, 0x94,
+ 0x35, 0xc5, 0xbe, 0xb9, 0x2d, 0x2b, 0xf6, 0x77, 0x72, 0x54, 0xec, 0x92, 0x68, 0x8a, 0x9b, 0x8b,
+ 0x4b, 0xc8, 0x10, 0xc9, 0x5f, 0x2f, 0xc0, 0x59, 0xc3, 0x75, 0x02, 0x9d, 0xa9, 0x1a, 0x6a, 0x92,
+ 0x9d, 0xaa, 0x70, 0x39, 0x37, 0x47, 0x96, 0xb3, 0x90, 0x46, 0x6c, 0x3d, 0xcd, 0xe6, 0x8c, 0x81,
+ 0x64, 0x1c, 0x94, 0x4d, 0xfe, 0x66, 0x01, 0x9e, 0x66, 0x63, 0xf9, 0x00, 0x33, 0x9f, 0x81, 0x4e,
+ 0xb6, 0x54, 0x17, 0x0f, 0x0f, 0xa6, 0x9f, 0x5e, 0xc9, 0x12, 0x86, 0xd9, 0x65, 0x60, 0xa5, 0x3b,
+ 0xa7, 0x0f, 0xaa, 0x25, 0x7c, 0x76, 0x6b, 0xce, 0xad, 0x9e, 0xa4, 0xaa, 0xd3, 0x7a, 0x56, 0x36,
+ 0xe5, 0x2c, 0xcd, 0x0e, 0xb3, 0x4a, 0x41, 0xae, 0x43, 0x6d, 0xcf, 0xb5, 0xfb, 0x5d, 0xea, 0x4f,
+ 0xd5, 0xf9, 0x10, 0x7b, 0x29, 0x6b, 0x88, 0xbd, 0xcb, 0x59, 0x5a, 0x67, 0x24, 0x7c, 0x4d, 0xbc,
+ 0xfb, 0xa8, 0xf2, 0x12, 0x0b, 0xaa, 0xb6, 0xd5, 0xb5, 0x02, 0x9f, 0x4f, 0x9c, 0xcd, 0xb9, 0xeb,
+ 0x23, 0x7f, 0x96, 0xe8, 0xa2, 0xab, 0x1c, 0x4c, 0xf4, 0x1a, 0xf1, 0x8c, 0x52, 0x00, 0x1b, 0x0a,
+ 0x7d, 0x43, 0xb7, 0xc5, 0xc4, 0xda, 0x9c, 0xfb, 0xd2, 0xe8, 0xdd, 0x86, 0xa1, 0xb4, 0xc6, 0xe5,
+ 0x37, 0x55, 0xf8, 0x2b, 0x0a, 0x6c, 0xf2, 0xfb, 0x30, 0x91, 0xf8, 0x9b, 0xfe, 0x54, 0x93, 0xd7,
+ 0xce, 0x73, 0x59, 0xb5, 0x13, 0x72, 0x45, 0x33, 0x4f, 0xa2, 0x85, 0xf8, 0x98, 0x02, 0x23, 0xb7,
+ 0xa0, 0xee, 0x5b, 0x26, 0x35, 0x74, 0xcf, 0x9f, 0x1a, 0x3b, 0x0a, 0xf0, 0xa4, 0x04, 0xae, 0xb7,
+ 0x65, 0x36, 0x0c, 0x01, 0xc8, 0x0c, 0x40, 0x4f, 0xf7, 0x02, 0x4b, 0x28, 0xaa, 0xe3, 0x5c, 0x69,
+ 0x9a, 0x38, 0x3c, 0x98, 0x86, 0xf5, 0x30, 0x15, 0x63, 0x1c, 0x8c, 0x9f, 0xe5, 0x5d, 0x71, 0x7a,
+ 0xfd, 0x40, 0x4c, 0xac, 0x0d, 0xc1, 0xdf, 0x0e, 0x53, 0x31, 0xc6, 0x41, 0xbe, 0x5d, 0x80, 0x67,
+ 0xa3, 0xd7, 0xc1, 0x4e, 0x76, 0xe6, 0xc4, 0x3b, 0xd9, 0xf4, 0xe1, 0xc1, 0xf4, 0xb3, 0xed, 0xe1,
+ 0x22, 0xf1, 0x61, 0xe5, 0xd1, 0xde, 0x82, 0xf1, 0xf9, 0x7e, 0xb0, 0xe3, 0x7a, 0xd6, 0x07, 0x5c,
+ 0xe9, 0x26, 0x4b, 0x50, 0x09, 0xb8, 0xf2, 0x24, 0xe6, 0xe5, 0x17, 0xb2, 0xaa, 0x5a, 0x28, 0xb2,
+ 0xb7, 0xe8, 0xbe, 0xd2, 0x06, 0xc4, 0xfc, 0x28, 0x94, 0x29, 0x91, 0x5d, 0xfb, 0xcb, 0x05, 0xa8,
+ 0xb5, 0x74, 0x63, 0xd7, 0xdd, 0xde, 0x26, 0x6f, 0x43, 0xdd, 0x72, 0x02, 0xea, 0xed, 0xe9, 0xb6,
+ 0x84, 0x9d, 0x89, 0xc1, 0x86, 0x2b, 0xb1, 0xe8, 0xbb, 0xd9, 0x9a, 0x87, 0x09, 0x5a, 0xec, 0xcb,
+ 0xb5, 0x02, 0xd7, 0x47, 0x57, 0x24, 0x06, 0x86, 0x68, 0x64, 0x1a, 0x2a, 0x7e, 0x40, 0x7b, 0x3e,
+ 0x9f, 0x79, 0xc6, 0x45, 0x31, 0xda, 0x2c, 0x01, 0x45, 0xba, 0xf6, 0x77, 0x0a, 0xd0, 0x68, 0xe9,
+ 0xbe, 0x65, 0xb0, 0xaf, 0x24, 0x0b, 0x50, 0xee, 0xfb, 0xd4, 0x3b, 0xde, 0xb7, 0xf1, 0xc9, 0x62,
+ 0xd3, 0xa7, 0x1e, 0xf2, 0xcc, 0xe4, 0x0e, 0xd4, 0x7b, 0xba, 0xef, 0xdf, 0x73, 0x3d, 0x53, 0x4e,
+ 0x78, 0x47, 0x04, 0x12, 0xca, 0xb9, 0xcc, 0x8a, 0x21, 0x88, 0xd6, 0x84, 0x68, 0xc6, 0xd7, 0x7e,
+ 0x5a, 0x80, 0x73, 0xad, 0xfe, 0xf6, 0x36, 0xf5, 0xa4, 0x2e, 0x2a, 0xb5, 0x3c, 0x0a, 0x15, 0x8f,
+ 0x9a, 0x96, 0x2f, 0xcb, 0xbe, 0x38, 0x72, 0x0b, 0x42, 0x86, 0x22, 0x95, 0x4a, 0x5e, 0x5f, 0x3c,
+ 0x01, 0x05, 0x3a, 0xe9, 0x43, 0xe3, 0x3d, 0x1a, 0xf8, 0x81, 0x47, 0xf5, 0xae, 0xfc, 0xba, 0x1b,
+ 0x23, 0x8b, 0xba, 0x49, 0x83, 0x36, 0x47, 0x8a, 0xeb, 0xb0, 0x61, 0x22, 0x46, 0x92, 0xb4, 0xef,
+ 0x57, 0x60, 0x6c, 0xc1, 0xed, 0x6e, 0x59, 0x0e, 0x35, 0xaf, 0x9b, 0x1d, 0x4a, 0xde, 0x85, 0x32,
+ 0x35, 0x3b, 0x54, 0x7e, 0xed, 0xe8, 0xd3, 0x3d, 0x03, 0x8b, 0x94, 0x16, 0xf6, 0x86, 0x1c, 0x98,
+ 0xac, 0xc2, 0xc4, 0xb6, 0xe7, 0x76, 0xc5, 0x08, 0xba, 0xb1, 0xdf, 0x93, 0x1a, 0x6b, 0xeb, 0x57,
+ 0xd5, 0xa8, 0xb4, 0x94, 0xa0, 0x3e, 0x38, 0x98, 0x86, 0xe8, 0x0d, 0x53, 0x79, 0xc9, 0xdb, 0x30,
+ 0x15, 0xa5, 0x84, 0x43, 0xc9, 0x02, 0x5b, 0x44, 0x70, 0x8d, 0xa5, 0xd2, 0xba, 0x7c, 0x78, 0x30,
+ 0x3d, 0xb5, 0x34, 0x84, 0x07, 0x87, 0xe6, 0x26, 0x1f, 0x15, 0x60, 0x32, 0x22, 0x8a, 0xe1, 0x5d,
+ 0x2a, 0x2a, 0x27, 0x34, 0x6f, 0xf0, 0xd5, 0xd6, 0x52, 0x4a, 0x04, 0x0e, 0x08, 0x25, 0x4b, 0x30,
+ 0x16, 0xb8, 0xb1, 0xfa, 0xaa, 0xf0, 0xfa, 0xd2, 0x94, 0x79, 0x60, 0xc3, 0x1d, 0x5a, 0x5b, 0x89,
+ 0x7c, 0x04, 0xe1, 0x82, 0x7a, 0x4f, 0xd5, 0x54, 0x95, 0xd7, 0xd4, 0xa5, 0xc3, 0x83, 0xe9, 0x0b,
+ 0x1b, 0x99, 0x1c, 0x38, 0x24, 0x27, 0xf9, 0xa3, 0x02, 0x4c, 0x28, 0x92, 0xac, 0xa3, 0xda, 0x49,
+ 0xd6, 0x11, 0x61, 0x2d, 0x62, 0x23, 0x21, 0x00, 0x53, 0x02, 0xb5, 0x9f, 0x95, 0xa1, 0x11, 0x0e,
+ 0xb0, 0xe4, 0x79, 0xa8, 0xf0, 0x85, 0xbf, 0xd4, 0x9b, 0xc3, 0x99, 0x93, 0xdb, 0x07, 0x50, 0xd0,
+ 0xc8, 0x0b, 0x50, 0x33, 0xdc, 0x6e, 0x57, 0x77, 0x4c, 0x6e, 0xcc, 0x69, 0xb4, 0x9a, 0x4c, 0x61,
+ 0x58, 0x10, 0x49, 0xa8, 0x68, 0xe4, 0x32, 0x94, 0x75, 0xaf, 0x23, 0xec, 0x2a, 0x0d, 0x31, 0x1e,
+ 0xcd, 0x7b, 0x1d, 0x1f, 0x79, 0x2a, 0xf9, 0x22, 0x94, 0xa8, 0xb3, 0x37, 0x55, 0x1e, 0xae, 0x91,
+ 0x5c, 0x77, 0xf6, 0xee, 0xea, 0x5e, 0xab, 0x29, 0xcb, 0x50, 0xba, 0xee, 0xec, 0x21, 0xcb, 0x43,
+ 0x56, 0xa1, 0x46, 0x9d, 0x3d, 0xf6, 0xef, 0xa5, 0xc1, 0xe3, 0x57, 0x86, 0x64, 0x67, 0x2c, 0x52,
+ 0x39, 0x0f, 0xf5, 0x1a, 0x99, 0x8c, 0x0a, 0x82, 0x7c, 0x05, 0xc6, 0x84, 0x8a, 0xb3, 0xc6, 0xfe,
+ 0x89, 0x3f, 0x55, 0xe5, 0x90, 0xd3, 0xc3, 0x75, 0x24, 0xce, 0x17, 0x19, 0x98, 0x62, 0x89, 0x3e,
+ 0x26, 0xa0, 0xc8, 0x57, 0xa0, 0xa1, 0xd6, 0xa3, 0xea, 0xcf, 0x66, 0xda, 0x66, 0xd4, 0x22, 0x16,
+ 0xe9, 0xfb, 0x7d, 0xcb, 0xa3, 0x5d, 0xea, 0x04, 0x7e, 0xeb, 0xac, 0x5a, 0xad, 0x2b, 0xaa, 0x8f,
+ 0x11, 0x1a, 0xd9, 0x1a, 0x34, 0x32, 0x09, 0x0b, 0xc9, 0xf3, 0x43, 0x46, 0xf5, 0x11, 0x2c, 0x4c,
+ 0x5f, 0x85, 0x33, 0xa1, 0x15, 0x48, 0x1a, 0x12, 0x84, 0xcd, 0xe4, 0x65, 0x96, 0x7d, 0x25, 0x49,
+ 0x7a, 0x70, 0x30, 0xfd, 0x5c, 0x86, 0x29, 0x21, 0x62, 0xc0, 0x34, 0x98, 0xf6, 0xbd, 0x12, 0x0c,
+ 0x6a, 0xff, 0xc9, 0x4a, 0x2b, 0x9c, 0x74, 0xa5, 0xa5, 0x3f, 0x48, 0x0c, 0x9f, 0xaf, 0xcb, 0x6c,
+ 0xf9, 0x3f, 0x2a, 0xeb, 0xc7, 0x94, 0x4e, 0xfa, 0xc7, 0x3c, 0x29, 0x7d, 0x47, 0xfb, 0xb8, 0x0c,
+ 0x13, 0x8b, 0x3a, 0xed, 0xba, 0xce, 0x23, 0xd7, 0x42, 0x85, 0x27, 0x62, 0x2d, 0x74, 0x0d, 0xea,
+ 0x1e, 0xed, 0xd9, 0x96, 0xa1, 0x0b, 0xe5, 0x4b, 0xda, 0x1e, 0x51, 0xa6, 0x61, 0x48, 0x1d, 0xb2,
+ 0x06, 0x2e, 0x3d, 0x91, 0x6b, 0xe0, 0xf2, 0xa7, 0xbf, 0x06, 0xd6, 0xfe, 0xa8, 0x08, 0x5c, 0x51,
+ 0x21, 0x57, 0xa1, 0xcc, 0x26, 0xe1, 0xb4, 0xe5, 0x85, 0x37, 0x1c, 0x4e, 0x21, 0x97, 0xa0, 0x18,
+ 0xb8, 0xb2, 0xe7, 0x81, 0xa4, 0x17, 0x37, 0x5c, 0x2c, 0x06, 0x2e, 0xf9, 0x00, 0xc0, 0x70, 0x1d,
+ 0xd3, 0x52, 0x26, 0xf9, 0x7c, 0x1f, 0xb6, 0xe4, 0x7a, 0xf7, 0x74, 0xcf, 0x5c, 0x08, 0x11, 0xc5,
+ 0x2a, 0x28, 0x7a, 0xc7, 0x98, 0x34, 0xf2, 0x06, 0x54, 0x5d, 0x67, 0xa9, 0x6f, 0xdb, 0xbc, 0x42,
+ 0x1b, 0xad, 0x5f, 0x63, 0x4b, 0xd3, 0x3b, 0x3c, 0xe5, 0xc1, 0xc1, 0xf4, 0x45, 0xa1, 0xdf, 0xb2,
+ 0xb7, 0xb7, 0x3c, 0x2b, 0xb0, 0x9c, 0x4e, 0x3b, 0xf0, 0xf4, 0x80, 0x76, 0xf6, 0x51, 0x66, 0xd3,
+ 0xfe, 0xa4, 0x00, 0xcd, 0x25, 0xeb, 0x3e, 0x35, 0xdf, 0xb2, 0x1c, 0xd3, 0xbd, 0x47, 0x10, 0xaa,
+ 0x36, 0x75, 0x3a, 0xc1, 0xce, 0x88, 0xeb, 0x07, 0xb1, 0x36, 0xe6, 0x08, 0x28, 0x91, 0xc8, 0x2c,
+ 0x34, 0x84, 0xf6, 0x69, 0x39, 0x1d, 0x5e, 0x87, 0xf5, 0x68, 0xd0, 0x6b, 0x2b, 0x02, 0x46, 0x3c,
+ 0xda, 0x3e, 0x9c, 0x1d, 0xa8, 0x06, 0x62, 0x42, 0x39, 0xd0, 0x3b, 0x6a, 0x7c, 0x5d, 0x1a, 0xb9,
+ 0x82, 0x37, 0xf4, 0x4e, 0xac, 0x72, 0xf9, 0x1c, 0xbf, 0xa1, 0xb3, 0x39, 0x9e, 0xa1, 0x6b, 0xbf,
+ 0x28, 0x40, 0x7d, 0xa9, 0xef, 0x18, 0x7c, 0x89, 0xf6, 0x68, 0x8b, 0x9c, 0x52, 0x18, 0x8a, 0x99,
+ 0x0a, 0x43, 0x1f, 0xaa, 0xbb, 0xf7, 0x42, 0x85, 0xa2, 0x39, 0xb7, 0x36, 0x7a, 0xab, 0x90, 0x45,
+ 0x9a, 0xb9, 0xc5, 0xf1, 0x84, 0xc3, 0x68, 0x42, 0x16, 0xa8, 0x7a, 0xeb, 0x2d, 0x2e, 0x54, 0x0a,
+ 0xbb, 0xf4, 0x45, 0x68, 0xc6, 0xd8, 0x8e, 0x65, 0x3b, 0xfe, 0x47, 0x65, 0xa8, 0x2e, 0xb7, 0xdb,
+ 0xf3, 0xeb, 0x2b, 0xe4, 0x15, 0x68, 0x4a, 0x5f, 0xc2, 0xed, 0xa8, 0x0e, 0x42, 0x57, 0x52, 0x3b,
+ 0x22, 0x61, 0x9c, 0x8f, 0xa9, 0x63, 0x1e, 0xd5, 0xed, 0xae, 0xec, 0x2c, 0xa1, 0x3a, 0x86, 0x2c,
+ 0x11, 0x05, 0x8d, 0xe8, 0x30, 0xc1, 0x56, 0x78, 0xac, 0x0a, 0xc5, 0xea, 0x4d, 0x76, 0x9b, 0x23,
+ 0xae, 0xef, 0xb8, 0x92, 0xb8, 0x99, 0x00, 0xc0, 0x14, 0x20, 0x79, 0x1d, 0xea, 0x7a, 0x3f, 0xd8,
+ 0xe1, 0x0a, 0xb4, 0xe8, 0x1b, 0x97, 0xb9, 0xab, 0x45, 0xa6, 0x3d, 0x38, 0x98, 0x1e, 0xbb, 0x85,
+ 0xad, 0x57, 0xd4, 0x3b, 0x86, 0xdc, 0xac, 0x70, 0x6a, 0xc5, 0x28, 0x0b, 0x57, 0x39, 0x76, 0xe1,
+ 0xd6, 0x13, 0x00, 0x98, 0x02, 0x24, 0xef, 0xc0, 0xd8, 0x2e, 0xdd, 0x0f, 0xf4, 0x2d, 0x29, 0xa0,
+ 0x7a, 0x1c, 0x01, 0x93, 0x4c, 0x85, 0xbb, 0x15, 0xcb, 0x8e, 0x09, 0x30, 0xe2, 0xc3, 0xf9, 0x5d,
+ 0xea, 0x6d, 0x51, 0xcf, 0x95, 0xab, 0x4f, 0x29, 0xa4, 0x76, 0x1c, 0x21, 0x53, 0x87, 0x07, 0xd3,
+ 0xe7, 0x6f, 0x65, 0xc0, 0x60, 0x26, 0xb8, 0xf6, 0xf3, 0x22, 0x9c, 0x59, 0x16, 0xce, 0x5c, 0xd7,
+ 0x13, 0x93, 0x30, 0xb9, 0x08, 0x25, 0xaf, 0xd7, 0xe7, 0x2d, 0xa7, 0x24, 0xcc, 0xb5, 0xb8, 0xbe,
+ 0x89, 0x2c, 0x8d, 0xbc, 0x0d, 0x75, 0x53, 0x0e, 0x19, 0x72, 0xf1, 0x3b, 0x92, 0xa1, 0x42, 0xbd,
+ 0x61, 0x88, 0xc6, 0x34, 0xfd, 0xae, 0xdf, 0x69, 0x5b, 0x1f, 0x50, 0xb9, 0x1e, 0xe4, 0x9a, 0xfe,
+ 0x9a, 0x48, 0x42, 0x45, 0x63, 0xb3, 0xea, 0x2e, 0xdd, 0x17, 0xab, 0xa1, 0x72, 0x34, 0xab, 0xde,
+ 0x92, 0x69, 0x18, 0x52, 0xc9, 0xb4, 0xea, 0x2c, 0xac, 0x15, 0x94, 0xc5, 0x4a, 0xfe, 0x2e, 0x4b,
+ 0x90, 0xfd, 0x86, 0x0d, 0x99, 0xef, 0x59, 0x41, 0x40, 0x3d, 0xf9, 0x1b, 0x47, 0x1a, 0x32, 0x6f,
+ 0x72, 0x04, 0x94, 0x48, 0xe4, 0x37, 0xa0, 0xc1, 0xc1, 0x5b, 0xb6, 0xbb, 0xc5, 0x7f, 0x5c, 0x43,
+ 0xac, 0xe9, 0xef, 0xaa, 0x44, 0x8c, 0xe8, 0xda, 0x2f, 0x8b, 0x70, 0x61, 0x99, 0x06, 0x42, 0xab,
+ 0x59, 0xa4, 0x3d, 0xdb, 0xdd, 0x67, 0xaa, 0x25, 0xd2, 0xf7, 0xc9, 0x9b, 0x00, 0x96, 0xbf, 0xd5,
+ 0xde, 0x33, 0x78, 0x3f, 0x10, 0x7d, 0xf8, 0xaa, 0xec, 0x92, 0xb0, 0xd2, 0x6e, 0x49, 0xca, 0x83,
+ 0xc4, 0x1b, 0xc6, 0xf2, 0x44, 0xcb, 0xab, 0xe2, 0x43, 0x96, 0x57, 0x6d, 0x80, 0x5e, 0xa4, 0xa0,
+ 0x96, 0x38, 0xe7, 0x9f, 0x53, 0x62, 0x8e, 0xa3, 0x9b, 0xc6, 0x60, 0xf2, 0xa8, 0x8c, 0x0e, 0x4c,
+ 0x9a, 0x74, 0x5b, 0xef, 0xdb, 0x41, 0xa8, 0x54, 0xcb, 0x4e, 0x7c, 0x74, 0xbd, 0x3c, 0x74, 0x34,
+ 0x2f, 0xa6, 0x90, 0x70, 0x00, 0x5b, 0xfb, 0x6e, 0x09, 0x2e, 0x2d, 0xd3, 0x20, 0xb4, 0xb8, 0xc8,
+ 0xd1, 0xb1, 0xdd, 0xa3, 0x06, 0xfb, 0x0b, 0x1f, 0x15, 0xa0, 0x6a, 0xeb, 0x5b, 0xd4, 0x66, 0xb3,
+ 0x17, 0xfb, 0x9a, 0x77, 0x47, 0x9e, 0x08, 0x86, 0x4b, 0x99, 0x59, 0xe5, 0x12, 0x52, 0x53, 0x83,
+ 0x48, 0x44, 0x29, 0x9e, 0x0d, 0xea, 0x86, 0xdd, 0xf7, 0x03, 0xea, 0xad, 0xbb, 0x5e, 0x20, 0xf5,
+ 0xc9, 0x70, 0x50, 0x5f, 0x88, 0x48, 0x18, 0xe7, 0x23, 0x73, 0x00, 0x86, 0x6d, 0x51, 0x27, 0xe0,
+ 0xb9, 0x44, 0xbf, 0x22, 0xea, 0xff, 0x2e, 0x84, 0x14, 0x8c, 0x71, 0x31, 0x51, 0x5d, 0xd7, 0xb1,
+ 0x02, 0x57, 0x88, 0x2a, 0x27, 0x45, 0xad, 0x45, 0x24, 0x8c, 0xf3, 0xf1, 0x6c, 0x34, 0xf0, 0x2c,
+ 0xc3, 0xe7, 0xd9, 0x2a, 0xa9, 0x6c, 0x11, 0x09, 0xe3, 0x7c, 0x6c, 0xce, 0x8b, 0x7d, 0xff, 0xb1,
+ 0xe6, 0xbc, 0x6f, 0x35, 0xe0, 0x4a, 0xa2, 0x5a, 0x03, 0x3d, 0xa0, 0xdb, 0x7d, 0xbb, 0x4d, 0x03,
+ 0xf5, 0x03, 0x47, 0x9c, 0x0b, 0xff, 0x6a, 0xf4, 0xdf, 0x45, 0x08, 0x89, 0x71, 0x32, 0xff, 0x7d,
+ 0xa0, 0x80, 0x47, 0xfa, 0xf7, 0xb3, 0xd0, 0x70, 0xf4, 0xc0, 0xe7, 0x1d, 0x57, 0xf6, 0xd1, 0x50,
+ 0x0d, 0xbb, 0xad, 0x08, 0x18, 0xf1, 0x90, 0x75, 0x38, 0x2f, 0xab, 0xf8, 0xfa, 0xfd, 0x9e, 0xeb,
+ 0x05, 0xd4, 0x13, 0x79, 0xe5, 0x74, 0x2a, 0xf3, 0x9e, 0x5f, 0xcb, 0xe0, 0xc1, 0xcc, 0x9c, 0x64,
+ 0x0d, 0xce, 0x19, 0xc2, 0xad, 0x4e, 0x6d, 0x57, 0x37, 0x15, 0xa0, 0x30, 0x70, 0x85, 0x4b, 0xa3,
+ 0x85, 0x41, 0x16, 0xcc, 0xca, 0x97, 0x6e, 0xcd, 0xd5, 0x91, 0x5a, 0x73, 0x6d, 0x94, 0xd6, 0x5c,
+ 0x1f, 0xad, 0x35, 0x37, 0x8e, 0xd6, 0x9a, 0x59, 0xcd, 0xb3, 0x76, 0x44, 0x3d, 0xa6, 0x9e, 0x88,
+ 0x19, 0x36, 0x16, 0xb5, 0x11, 0xd6, 0x7c, 0x3b, 0x83, 0x07, 0x33, 0x73, 0x92, 0x2d, 0xb8, 0x24,
+ 0xd2, 0xaf, 0x3b, 0x86, 0xb7, 0xdf, 0x63, 0x13, 0x4f, 0x0c, 0xb7, 0x99, 0xb0, 0x30, 0x5e, 0x6a,
+ 0x0f, 0xe5, 0xc4, 0x87, 0xa0, 0x90, 0xdf, 0x86, 0x71, 0xf1, 0x97, 0xd6, 0xf4, 0x1e, 0x87, 0x15,
+ 0x31, 0x1c, 0x4f, 0x4b, 0xd8, 0xf1, 0x85, 0x38, 0x11, 0x93, 0xbc, 0x64, 0x1e, 0xce, 0xf4, 0xf6,
+ 0x0c, 0xf6, 0xb8, 0xb2, 0x7d, 0x9b, 0x52, 0x93, 0x9a, 0xdc, 0x69, 0xd4, 0x68, 0x3d, 0xa3, 0x0c,
+ 0x1d, 0xeb, 0x49, 0x32, 0xa6, 0xf9, 0xc9, 0xeb, 0x30, 0xe6, 0x07, 0xba, 0x17, 0x48, 0xb3, 0xde,
+ 0xd4, 0x84, 0x88, 0x71, 0x51, 0x56, 0xaf, 0x76, 0x8c, 0x86, 0x09, 0xce, 0xcc, 0xf9, 0xe2, 0xcc,
+ 0xe9, 0xcd, 0x17, 0x79, 0x46, 0xab, 0x7f, 0x59, 0x84, 0xab, 0xcb, 0x34, 0x58, 0x73, 0x1d, 0x69,
+ 0x14, 0xcd, 0x9a, 0xf6, 0x8f, 0x64, 0x13, 0x4d, 0x4e, 0xda, 0xc5, 0x13, 0x9d, 0xb4, 0x4b, 0x27,
+ 0x34, 0x69, 0x97, 0x4f, 0x71, 0xd2, 0xfe, 0x27, 0x45, 0x78, 0x26, 0x51, 0x93, 0xeb, 0xae, 0xa9,
+ 0x06, 0xfc, 0xcf, 0x2b, 0xf0, 0x08, 0x15, 0xf8, 0x40, 0xe8, 0x9d, 0xdc, 0xad, 0x95, 0xd2, 0x78,
+ 0x3e, 0x4c, 0x6b, 0x3c, 0xef, 0xe4, 0x99, 0xf9, 0x32, 0x24, 0x1c, 0x69, 0xc6, 0xbb, 0x09, 0xc4,
+ 0x93, 0x4e, 0x38, 0x61, 0xfa, 0x89, 0x29, 0x3d, 0x61, 0x10, 0x1d, 0x0e, 0x70, 0x60, 0x46, 0x2e,
+ 0xd2, 0x86, 0xa7, 0x7d, 0xea, 0x04, 0x96, 0x43, 0xed, 0x24, 0x9c, 0xd0, 0x86, 0x9e, 0x93, 0x70,
+ 0x4f, 0xb7, 0xb3, 0x98, 0x30, 0x3b, 0x6f, 0x9e, 0x71, 0xe0, 0xdf, 0x00, 0x57, 0x39, 0x45, 0xd5,
+ 0x9c, 0x98, 0xc6, 0xf2, 0x51, 0x5a, 0x63, 0x79, 0x37, 0xff, 0x7f, 0x1b, 0x4d, 0x5b, 0x99, 0x03,
+ 0xe0, 0x7f, 0x21, 0xae, 0xae, 0x84, 0x93, 0x34, 0x86, 0x14, 0x8c, 0x71, 0xb1, 0x09, 0x48, 0xd5,
+ 0x73, 0x5c, 0x53, 0x09, 0x27, 0xa0, 0x76, 0x9c, 0x88, 0x49, 0xde, 0xa1, 0xda, 0x4e, 0x65, 0x64,
+ 0x6d, 0xe7, 0x26, 0x90, 0x84, 0xe1, 0x51, 0xe0, 0x55, 0x93, 0x31, 0x9c, 0x2b, 0x03, 0x1c, 0x98,
+ 0x91, 0x6b, 0x48, 0x53, 0xae, 0x9d, 0x6c, 0x53, 0xae, 0x8f, 0xde, 0x94, 0xc9, 0xbb, 0x70, 0x91,
+ 0x8b, 0x92, 0xf5, 0x93, 0x04, 0x16, 0x7a, 0xcf, 0xaf, 0x48, 0xe0, 0x8b, 0x38, 0x8c, 0x11, 0x87,
+ 0x63, 0xb0, 0xff, 0x63, 0x78, 0xd4, 0x64, 0xc2, 0x75, 0x7b, 0xb8, 0x4e, 0xb4, 0x90, 0xc1, 0x83,
+ 0x99, 0x39, 0x59, 0x13, 0x0b, 0x58, 0x33, 0xd4, 0xb7, 0x6c, 0x6a, 0xca, 0x18, 0xd6, 0xb0, 0x89,
+ 0x6d, 0xac, 0xb6, 0x25, 0x05, 0x63, 0x5c, 0x59, 0x6a, 0xca, 0xd8, 0x31, 0xd5, 0x94, 0x65, 0x6e,
+ 0xa5, 0xdf, 0x4e, 0x68, 0x43, 0x52, 0xd7, 0x09, 0xa3, 0x92, 0x17, 0xd2, 0x0c, 0x38, 0x98, 0x87,
+ 0x6b, 0x89, 0x86, 0x67, 0xf5, 0x02, 0x3f, 0x89, 0x35, 0x91, 0xd2, 0x12, 0x33, 0x78, 0x30, 0x33,
+ 0x27, 0xd3, 0xcf, 0x77, 0xa8, 0x6e, 0x07, 0x3b, 0x49, 0xc0, 0x33, 0x49, 0xfd, 0xfc, 0xc6, 0x20,
+ 0x0b, 0x66, 0xe5, 0xcb, 0x9c, 0x90, 0x26, 0x9f, 0x4c, 0xb5, 0xea, 0x8f, 0x4b, 0x70, 0x71, 0x99,
+ 0x06, 0x61, 0x78, 0xcf, 0xe7, 0x66, 0x94, 0x4f, 0xc1, 0x8c, 0xf2, 0xcd, 0x0a, 0x9c, 0x5b, 0xa6,
+ 0xc1, 0x80, 0x36, 0xf6, 0xa7, 0xb4, 0xfa, 0xd7, 0xe0, 0x5c, 0x14, 0x51, 0xd6, 0x0e, 0x5c, 0x4f,
+ 0xcc, 0xe5, 0xa9, 0xd5, 0x72, 0x7b, 0x90, 0x05, 0xb3, 0xf2, 0x91, 0xaf, 0xc0, 0x33, 0x7c, 0xaa,
+ 0x77, 0x3a, 0xc2, 0x3e, 0x2b, 0x8c, 0x09, 0xb1, 0x3d, 0x11, 0xd3, 0x12, 0xf2, 0x99, 0x76, 0x36,
+ 0x1b, 0x0e, 0xcb, 0x4f, 0xbe, 0x06, 0x63, 0x3d, 0xab, 0x47, 0x6d, 0xcb, 0xe1, 0xfa, 0x59, 0xee,
+ 0x90, 0x90, 0xf5, 0x18, 0x58, 0xb4, 0x80, 0x8b, 0xa7, 0x62, 0x42, 0x60, 0x66, 0x4b, 0xad, 0x9f,
+ 0x62, 0x4b, 0xfd, 0x9f, 0x45, 0xa8, 0x2d, 0x7b, 0x6e, 0xbf, 0xd7, 0xda, 0x27, 0x1d, 0xa8, 0xde,
+ 0xe3, 0xce, 0x33, 0xe9, 0x9a, 0x1a, 0x3d, 0x2a, 0x5b, 0xf8, 0xe0, 0x22, 0x95, 0x48, 0xbc, 0xa3,
+ 0x84, 0x67, 0x8d, 0x78, 0x97, 0xee, 0x53, 0x53, 0xfa, 0xd0, 0xc2, 0x46, 0x7c, 0x8b, 0x25, 0xa2,
+ 0xa0, 0x91, 0x2e, 0x9c, 0xd1, 0x6d, 0xdb, 0xbd, 0x47, 0xcd, 0x55, 0x3d, 0xa0, 0x0e, 0xf5, 0x95,
+ 0x4b, 0xf2, 0xb8, 0x66, 0x69, 0xee, 0xd7, 0x9f, 0x4f, 0x42, 0x61, 0x1a, 0x9b, 0xbc, 0x07, 0x35,
+ 0x3f, 0x70, 0x3d, 0xa5, 0x6c, 0x35, 0xe7, 0x16, 0x46, 0xff, 0xe9, 0xad, 0x2f, 0xb7, 0x05, 0x94,
+ 0xb0, 0xd9, 0xcb, 0x17, 0x54, 0x02, 0xb4, 0x6f, 0x14, 0x00, 0x6e, 0x6c, 0x6c, 0xac, 0x4b, 0xf7,
+ 0x82, 0x09, 0x65, 0xbd, 0x1f, 0x3a, 0x2a, 0x47, 0x77, 0x08, 0x26, 0xc2, 0x32, 0xa5, 0x0f, 0xaf,
+ 0x1f, 0xec, 0x20, 0x47, 0x27, 0xbf, 0x0e, 0x35, 0xa9, 0x20, 0xcb, 0x6a, 0x0f, 0x43, 0x0b, 0xa4,
+ 0x12, 0x8d, 0x8a, 0xae, 0xfd, 0xc3, 0x22, 0xc0, 0x8a, 0x69, 0xd3, 0xb6, 0x0a, 0xa4, 0x6f, 0x04,
+ 0x3b, 0x1e, 0xf5, 0x77, 0x5c, 0xdb, 0x1c, 0xd1, 0x9b, 0xca, 0x6d, 0xfe, 0x1b, 0x0a, 0x04, 0x23,
+ 0x3c, 0x62, 0xc2, 0x98, 0x1f, 0xd0, 0x9e, 0x8a, 0xd4, 0x1c, 0xd1, 0x89, 0x32, 0x29, 0xec, 0x22,
+ 0x11, 0x0e, 0x26, 0x50, 0x89, 0x0e, 0x4d, 0xcb, 0x31, 0x44, 0x07, 0x69, 0xed, 0x8f, 0xd8, 0x90,
+ 0xce, 0xb0, 0x15, 0xc7, 0x4a, 0x04, 0x83, 0x71, 0x4c, 0xed, 0x27, 0x45, 0xb8, 0xc0, 0xe5, 0xb1,
+ 0x62, 0x24, 0xe2, 0x31, 0xc9, 0x5f, 0x18, 0xd8, 0xf4, 0xf7, 0x67, 0x8f, 0x26, 0x5a, 0xec, 0x19,
+ 0x5b, 0xa3, 0x81, 0x1e, 0xe9, 0x73, 0x51, 0x5a, 0x6c, 0xa7, 0x5f, 0x1f, 0xca, 0x3e, 0x1b, 0xaf,
+ 0x44, 0xed, 0xb5, 0x47, 0x6e, 0x42, 0xd9, 0x1f, 0xc0, 0x47, 0xaf, 0xd0, 0x6b, 0xcc, 0x47, 0x2d,
+ 0x2e, 0x8e, 0xfc, 0x25, 0xa8, 0xfa, 0x81, 0x1e, 0xf4, 0x55, 0xd7, 0xdc, 0x3c, 0x69, 0xc1, 0x1c,
+ 0x3c, 0x1a, 0x47, 0xc4, 0x3b, 0x4a, 0xa1, 0xda, 0x4f, 0x0a, 0x70, 0x29, 0x3b, 0xe3, 0xaa, 0xe5,
+ 0x07, 0xe4, 0xcf, 0x0f, 0x54, 0xfb, 0x11, 0xff, 0x38, 0xcb, 0xcd, 0x2b, 0x3d, 0x8c, 0x0b, 0x57,
+ 0x29, 0xb1, 0x2a, 0x0f, 0xa0, 0x62, 0x05, 0xb4, 0xab, 0xd6, 0x97, 0x77, 0x4e, 0xf8, 0xd3, 0x63,
+ 0x53, 0x3b, 0x93, 0x82, 0x42, 0x98, 0xf6, 0x71, 0x71, 0xd8, 0x27, 0xf3, 0xe9, 0xc3, 0x4e, 0xc6,
+ 0xfc, 0xde, 0xca, 0x17, 0xf3, 0x9b, 0x2c, 0xd0, 0x60, 0xe8, 0xef, 0x5f, 0x1c, 0x0c, 0xfd, 0xbd,
+ 0x93, 0x3f, 0xf4, 0x37, 0x55, 0x0d, 0x43, 0x23, 0x80, 0x3f, 0x29, 0xc1, 0xe5, 0x87, 0x35, 0x1b,
+ 0x36, 0x9f, 0xc9, 0xd6, 0x99, 0x77, 0x3e, 0x7b, 0x78, 0x3b, 0x24, 0x73, 0x50, 0xe9, 0xed, 0xe8,
+ 0xbe, 0x52, 0xca, 0xd4, 0x82, 0xa5, 0xb2, 0xce, 0x12, 0x1f, 0xb0, 0x41, 0x83, 0x2b, 0x73, 0xfc,
+ 0x15, 0x05, 0x2b, 0x1b, 0x8e, 0xbb, 0xd4, 0xf7, 0x23, 0x9b, 0x40, 0x38, 0x1c, 0xaf, 0x89, 0x64,
+ 0x54, 0x74, 0x12, 0x40, 0x55, 0x98, 0x98, 0xe5, 0xcc, 0x34, 0x7a, 0x20, 0x57, 0x46, 0x98, 0x78,
+ 0xf4, 0x51, 0xd2, 0x5b, 0x21, 0x65, 0x91, 0x19, 0x28, 0x07, 0x51, 0xd0, 0xae, 0x5a, 0x9a, 0x97,
+ 0x33, 0xf4, 0x53, 0xce, 0xc7, 0x16, 0xf6, 0xee, 0x16, 0x37, 0xaa, 0x9b, 0xd2, 0x7f, 0x6e, 0xb9,
+ 0x0e, 0x57, 0xc8, 0x4a, 0xd1, 0xc2, 0xfe, 0xce, 0x00, 0x07, 0x66, 0xe4, 0xd2, 0xfe, 0x7d, 0x1d,
+ 0x2e, 0x64, 0xb7, 0x07, 0x56, 0x6f, 0x7b, 0xd4, 0xf3, 0x19, 0x76, 0x21, 0x59, 0x6f, 0x77, 0x45,
+ 0x32, 0x2a, 0xfa, 0x67, 0x3a, 0xe0, 0xec, 0x9b, 0x05, 0xb8, 0xe8, 0x49, 0x1f, 0xd1, 0xe3, 0x08,
+ 0x3a, 0x7b, 0x4e, 0x98, 0x33, 0x86, 0x08, 0xc4, 0xe1, 0x65, 0x21, 0x7f, 0xaf, 0x00, 0x53, 0xdd,
+ 0x94, 0x9d, 0xe3, 0x14, 0xf7, 0xad, 0xf1, 0xa8, 0xf8, 0xb5, 0x21, 0xf2, 0x70, 0x68, 0x49, 0xc8,
+ 0xd7, 0xa0, 0xd9, 0x63, 0xed, 0xc2, 0x0f, 0xa8, 0x63, 0xa8, 0xad, 0x6b, 0xa3, 0xf7, 0xa4, 0xf5,
+ 0x08, 0x4b, 0x85, 0xa2, 0x09, 0xfd, 0x20, 0x46, 0xc0, 0xb8, 0xc4, 0x27, 0x7c, 0xa3, 0xda, 0x35,
+ 0xa8, 0xfb, 0x34, 0x08, 0x2c, 0xa7, 0x23, 0xd6, 0x1b, 0x0d, 0xd1, 0x57, 0xda, 0x32, 0x0d, 0x43,
+ 0x2a, 0xf9, 0x0d, 0x68, 0x70, 0x97, 0xd3, 0xbc, 0xd7, 0xf1, 0xa7, 0x1a, 0x3c, 0x5c, 0x6c, 0x5c,
+ 0x04, 0xc0, 0xc9, 0x44, 0x8c, 0xe8, 0xe4, 0x65, 0x18, 0xdb, 0xe2, 0xdd, 0x57, 0xee, 0x5d, 0x16,
+ 0x36, 0x2e, 0xae, 0xad, 0xb5, 0x62, 0xe9, 0x98, 0xe0, 0x22, 0x73, 0x00, 0x34, 0xf4, 0xcb, 0xa5,
+ 0xed, 0x59, 0x91, 0xc7, 0x0e, 0x63, 0x5c, 0xe4, 0x39, 0x28, 0x05, 0xb6, 0xcf, 0x6d, 0x58, 0xf5,
+ 0x68, 0x09, 0xba, 0xb1, 0xda, 0x46, 0x96, 0xae, 0xfd, 0xb2, 0x00, 0x67, 0x52, 0x9b, 0x4b, 0x58,
+ 0x96, 0xbe, 0x67, 0xcb, 0x61, 0x24, 0xcc, 0xb2, 0x89, 0xab, 0xc8, 0xd2, 0xc9, 0xbb, 0x52, 0x2d,
+ 0x2f, 0xe6, 0x3c, 0xa6, 0xe1, 0xb6, 0x1e, 0xf8, 0x4c, 0x0f, 0x1f, 0xd0, 0xc8, 0xb9, 0x9b, 0x2f,
+ 0x2a, 0x8f, 0x9c, 0x07, 0x62, 0x6e, 0xbe, 0x88, 0x86, 0x09, 0xce, 0x94, 0xc1, 0xaf, 0x7c, 0x14,
+ 0x83, 0x9f, 0xf6, 0x27, 0xc5, 0x58, 0x0d, 0x48, 0xcd, 0xfe, 0x11, 0x35, 0xf0, 0x22, 0x9b, 0x40,
+ 0xc3, 0xc9, 0xbd, 0x11, 0x9f, 0xff, 0xf8, 0x64, 0x2c, 0xa9, 0xe4, 0x2d, 0x51, 0xf7, 0xa5, 0x9c,
+ 0x9b, 0x61, 0x37, 0x56, 0xdb, 0x22, 0xba, 0x4a, 0xfd, 0xb5, 0xf0, 0x17, 0x94, 0x4f, 0xe9, 0x17,
+ 0x68, 0xff, 0xba, 0x04, 0xcd, 0x9b, 0xee, 0xd6, 0x67, 0x24, 0x82, 0x3a, 0x7b, 0x9a, 0x2a, 0x7e,
+ 0x8a, 0xd3, 0xd4, 0x26, 0x3c, 0x13, 0x04, 0x76, 0x9b, 0x1a, 0xae, 0x63, 0xfa, 0xf3, 0xdb, 0x01,
+ 0xf5, 0x96, 0x2c, 0xc7, 0xf2, 0x77, 0xa8, 0x29, 0xdd, 0x49, 0xcf, 0x1e, 0x1e, 0x4c, 0x3f, 0xb3,
+ 0xb1, 0xb1, 0x9a, 0xc5, 0x82, 0xc3, 0xf2, 0xf2, 0x61, 0x43, 0xec, 0x04, 0xe4, 0x3b, 0x65, 0x64,
+ 0xcc, 0x8d, 0x18, 0x36, 0x62, 0xe9, 0x98, 0xe0, 0xd2, 0xbe, 0x53, 0x84, 0x46, 0xb8, 0x01, 0x9f,
+ 0xbc, 0x00, 0xb5, 0x2d, 0xcf, 0xdd, 0xa5, 0x9e, 0xf0, 0xdc, 0xc9, 0x9d, 0x32, 0x2d, 0x91, 0x84,
+ 0x8a, 0x46, 0x9e, 0x87, 0x4a, 0xe0, 0xf6, 0x2c, 0x23, 0x6d, 0x50, 0xdb, 0x60, 0x89, 0x28, 0x68,
+ 0xa7, 0xd7, 0xc0, 0x5f, 0x4c, 0xa8, 0x76, 0x8d, 0xa1, 0xca, 0xd8, 0x3b, 0x50, 0xf6, 0x75, 0xdf,
+ 0x96, 0xf3, 0x69, 0x8e, 0xbd, 0xec, 0xf3, 0xed, 0x55, 0xb9, 0x97, 0x7d, 0xbe, 0xbd, 0x8a, 0x1c,
+ 0x54, 0xfb, 0x59, 0x11, 0x9a, 0xa2, 0xde, 0xc4, 0xa8, 0x70, 0x92, 0x35, 0xf7, 0x06, 0x0f, 0xa5,
+ 0xf0, 0xfb, 0x5d, 0xea, 0x71, 0x33, 0x93, 0x1c, 0xe4, 0xe2, 0xfe, 0x81, 0x88, 0x18, 0x86, 0x53,
+ 0x44, 0x49, 0xaa, 0xea, 0xcb, 0xa7, 0x58, 0xf5, 0x95, 0x23, 0x55, 0x7d, 0xf5, 0x34, 0xaa, 0xfe,
+ 0xa3, 0x22, 0x34, 0x56, 0xad, 0x6d, 0x6a, 0xec, 0x1b, 0x36, 0xdf, 0x13, 0x68, 0x52, 0x9b, 0x06,
+ 0x74, 0xd9, 0xd3, 0x0d, 0xba, 0x4e, 0x3d, 0x8b, 0x1f, 0x50, 0xc3, 0xfa, 0x07, 0x1f, 0x81, 0xe4,
+ 0x9e, 0xc0, 0xc5, 0x21, 0x3c, 0x38, 0x34, 0x37, 0x59, 0x81, 0x31, 0x93, 0xfa, 0x96, 0x47, 0xcd,
+ 0xf5, 0xd8, 0x42, 0xe5, 0x05, 0x35, 0xd5, 0x2c, 0xc6, 0x68, 0x0f, 0x0e, 0xa6, 0xc7, 0x95, 0x81,
+ 0x52, 0xac, 0x58, 0x12, 0x59, 0x59, 0x97, 0xef, 0xe9, 0x7d, 0x3f, 0xab, 0x8c, 0xb1, 0x2e, 0xbf,
+ 0x9e, 0xcd, 0x82, 0xc3, 0xf2, 0x6a, 0x15, 0x28, 0xad, 0xba, 0x1d, 0xed, 0xe3, 0x12, 0x84, 0x27,
+ 0x19, 0x91, 0xbf, 0x52, 0x80, 0xa6, 0xee, 0x38, 0x6e, 0x20, 0x4f, 0x09, 0x12, 0x1e, 0x78, 0xcc,
+ 0x7d, 0x60, 0xd2, 0xcc, 0x7c, 0x04, 0x2a, 0x9c, 0xb7, 0xa1, 0x43, 0x39, 0x46, 0xc1, 0xb8, 0x6c,
+ 0xd2, 0x4f, 0xf9, 0x93, 0xd7, 0xf2, 0x97, 0xe2, 0x08, 0xde, 0xe3, 0x4b, 0x5f, 0x82, 0xc9, 0x74,
+ 0x61, 0x8f, 0xe3, 0x0e, 0xca, 0xe5, 0x98, 0x2f, 0x02, 0x44, 0x31, 0x25, 0x8f, 0xc1, 0x88, 0x65,
+ 0x25, 0x8c, 0x58, 0xcb, 0xa3, 0x57, 0x70, 0x58, 0xe8, 0xa1, 0x86, 0xab, 0xf7, 0x53, 0x86, 0xab,
+ 0x95, 0x93, 0x10, 0xf6, 0x70, 0x63, 0xd5, 0x3f, 0x28, 0xc0, 0x64, 0xc4, 0x2c, 0x77, 0xc8, 0xbe,
+ 0x06, 0xe3, 0x1e, 0xd5, 0xcd, 0x96, 0x1e, 0x18, 0x3b, 0x3c, 0xd4, 0xbb, 0xc0, 0x63, 0xb3, 0xcf,
+ 0x1e, 0x1e, 0x4c, 0x8f, 0x63, 0x9c, 0x80, 0x49, 0x3e, 0xa2, 0x43, 0x93, 0x25, 0x6c, 0x58, 0x5d,
+ 0xea, 0xf6, 0x83, 0x11, 0xad, 0xa6, 0x7c, 0xc1, 0x82, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0x4f, 0x0a,
+ 0x30, 0x11, 0x2f, 0xf0, 0xa9, 0x5b, 0xd4, 0x76, 0x92, 0x16, 0xb5, 0x85, 0x13, 0xf8, 0x27, 0x43,
+ 0xac, 0x68, 0x3f, 0xaf, 0xc7, 0x3f, 0x8d, 0x5b, 0xce, 0xe2, 0xc6, 0x82, 0xc2, 0x43, 0x8d, 0x05,
+ 0x9f, 0xfd, 0xc3, 0x6b, 0x86, 0x69, 0xb9, 0xe5, 0x27, 0x58, 0xcb, 0xfd, 0x34, 0x4f, 0xc0, 0x89,
+ 0x9d, 0xe2, 0x52, 0xcd, 0x71, 0x8a, 0x4b, 0x37, 0x3c, 0xc5, 0xa5, 0x76, 0x62, 0x83, 0xce, 0x51,
+ 0x4e, 0x72, 0xa9, 0x3f, 0xd6, 0x93, 0x5c, 0x1a, 0xa7, 0x75, 0x92, 0x0b, 0xe4, 0x3d, 0xc9, 0xe5,
+ 0xc3, 0x02, 0x4c, 0x98, 0x89, 0x1d, 0xb3, 0xdc, 0xb6, 0x90, 0x67, 0xaa, 0x49, 0x6e, 0xc0, 0x15,
+ 0x5b, 0xa6, 0x92, 0x69, 0x98, 0x12, 0xa9, 0xfd, 0xfd, 0x5a, 0x7c, 0x1e, 0x78, 0xdc, 0xa6, 0xea,
+ 0x57, 0x93, 0xa6, 0xea, 0xab, 0x69, 0x53, 0xf5, 0x99, 0x58, 0x14, 0x69, 0xdc, 0x5c, 0xfd, 0x85,
+ 0xd8, 0xf0, 0x58, 0xe2, 0x27, 0xa7, 0x84, 0x35, 0x9d, 0x31, 0x44, 0x7e, 0x01, 0xea, 0xbe, 0x3a,
+ 0x73, 0x52, 0x2c, 0x6c, 0xa2, 0xff, 0xa2, 0xce, 0x83, 0x0c, 0x39, 0x98, 0x26, 0xee, 0x51, 0xdd,
+ 0x77, 0x9d, 0xb4, 0x26, 0x8e, 0x3c, 0x15, 0x25, 0x35, 0x6e, 0x32, 0xaf, 0x3e, 0xc2, 0x64, 0xae,
+ 0x43, 0xd3, 0xd6, 0xfd, 0x60, 0xb3, 0x67, 0xea, 0x01, 0x35, 0x65, 0x7f, 0xfb, 0x33, 0x47, 0x9b,
+ 0xab, 0xd8, 0xfc, 0x17, 0x29, 0x84, 0xab, 0x11, 0x0c, 0xc6, 0x31, 0x89, 0x09, 0x63, 0xec, 0x95,
+ 0xf7, 0x06, 0x73, 0x5e, 0x1d, 0x01, 0x70, 0x1c, 0x19, 0xa1, 0xa5, 0x67, 0x35, 0x86, 0x83, 0x09,
+ 0xd4, 0x21, 0x56, 0xf5, 0xc6, 0x28, 0x56, 0x75, 0xf2, 0xdb, 0x42, 0xd9, 0xd8, 0x57, 0x3f, 0x8c,
+ 0x5b, 0xe3, 0xc6, 0xa3, 0xa8, 0x42, 0x8c, 0x13, 0x31, 0xc9, 0x4b, 0xe6, 0xe1, 0x8c, 0xd1, 0xf7,
+ 0x3c, 0x1e, 0x47, 0x24, 0xb3, 0x37, 0x79, 0xf6, 0x30, 0x5e, 0x6c, 0x21, 0x49, 0xc6, 0x34, 0x3f,
+ 0x83, 0xe8, 0xcb, 0x9a, 0x54, 0x10, 0x63, 0x49, 0x88, 0xcd, 0x24, 0x19, 0xd3, 0xfc, 0x7c, 0xa3,
+ 0x84, 0x40, 0xbd, 0xa1, 0xfb, 0x3b, 0x32, 0xd8, 0x2c, 0xda, 0x28, 0x11, 0x91, 0x30, 0xce, 0x47,
+ 0xe6, 0x00, 0x04, 0x12, 0xcf, 0x35, 0x91, 0x8c, 0xc1, 0xdc, 0x0c, 0x29, 0x18, 0xe3, 0xd2, 0x3e,
+ 0x6c, 0x40, 0xf3, 0xb6, 0x1e, 0x58, 0x7b, 0x94, 0xfb, 0xbc, 0x4e, 0xc7, 0xf1, 0xf0, 0xb7, 0x0a,
+ 0x70, 0x21, 0x19, 0xd8, 0x78, 0x8a, 0xde, 0x07, 0x7e, 0x4c, 0x0a, 0x66, 0x4a, 0xc3, 0x21, 0xa5,
+ 0xe0, 0x7e, 0x88, 0x81, 0x38, 0xc9, 0xd3, 0xf6, 0x43, 0xb4, 0x87, 0x09, 0xc4, 0xe1, 0x65, 0xf9,
+ 0xac, 0xf8, 0x21, 0x9e, 0xec, 0xd3, 0xf4, 0x52, 0x5e, 0x92, 0xda, 0x13, 0xe3, 0x25, 0xa9, 0x3f,
+ 0x11, 0xaa, 0x69, 0x2f, 0xe6, 0x25, 0x69, 0xe4, 0x8c, 0xd6, 0x91, 0x7b, 0x01, 0x04, 0xda, 0x30,
+ 0x6f, 0x0b, 0xdf, 0xc6, 0xaf, 0xac, 0xd7, 0x4c, 0xa3, 0xdb, 0xd2, 0x7d, 0xcb, 0x90, 0x4a, 0x42,
+ 0x8e, 0xd3, 0x43, 0xd5, 0xf9, 0x66, 0xc2, 0xa9, 0xcf, 0x5f, 0x51, 0x60, 0x47, 0xc7, 0xb9, 0x15,
+ 0x73, 0x1d, 0xe7, 0x46, 0x16, 0xa0, 0xec, 0xec, 0xd2, 0xfd, 0xe3, 0x6d, 0x88, 0xe7, 0x2b, 0x95,
+ 0xdb, 0xb7, 0xe8, 0x3e, 0xf2, 0xcc, 0xda, 0x77, 0x8a, 0x00, 0xec, 0xf3, 0x8f, 0xe6, 0xaf, 0xf8,
+ 0x75, 0xa8, 0xf9, 0x7d, 0x6e, 0x59, 0x90, 0xea, 0x4d, 0x14, 0xe2, 0x24, 0x92, 0x51, 0xd1, 0xc9,
+ 0xf3, 0x50, 0x79, 0xbf, 0x4f, 0xfb, 0xca, 0xf9, 0x1e, 0x2a, 0xb7, 0x5f, 0x66, 0x89, 0x28, 0x68,
+ 0xa7, 0x67, 0x7b, 0x54, 0x7e, 0x8d, 0xca, 0x69, 0xf9, 0x35, 0x1a, 0x50, 0xbb, 0xed, 0xf2, 0x88,
+ 0x49, 0xed, 0xbf, 0x17, 0x01, 0xa2, 0x88, 0x34, 0xf2, 0x8d, 0x02, 0x3c, 0x1d, 0x76, 0xb8, 0x40,
+ 0xac, 0x51, 0xf8, 0x81, 0xbd, 0xb9, 0x7d, 0x1c, 0x59, 0x9d, 0x9d, 0x8f, 0x40, 0xeb, 0x59, 0xe2,
+ 0x30, 0xbb, 0x14, 0x04, 0xa1, 0x4e, 0xbb, 0xbd, 0x60, 0x7f, 0xd1, 0xf2, 0x64, 0x0b, 0xcc, 0x0c,
+ 0x7c, 0xbc, 0x2e, 0x79, 0x44, 0x56, 0xb9, 0x90, 0xe6, 0x9d, 0x48, 0x51, 0x30, 0xc4, 0x21, 0x3b,
+ 0x50, 0x77, 0xdc, 0x77, 0x7d, 0x56, 0x1d, 0xb2, 0x39, 0xbe, 0x39, 0x7a, 0x95, 0x8b, 0x6a, 0x15,
+ 0x36, 0x71, 0xf9, 0x82, 0x35, 0x47, 0x56, 0xf6, 0xd7, 0x8b, 0x70, 0x2e, 0xa3, 0x1e, 0xc8, 0x9b,
+ 0x30, 0x29, 0x83, 0xff, 0xa2, 0x93, 0xab, 0x0b, 0xd1, 0xc9, 0xd5, 0xed, 0x14, 0x0d, 0x07, 0xb8,
+ 0xc9, 0xbb, 0x00, 0xba, 0x61, 0x50, 0xdf, 0x5f, 0x73, 0x4d, 0xa5, 0xbd, 0xbf, 0xc1, 0xd4, 0x97,
+ 0xf9, 0x30, 0xf5, 0xc1, 0xc1, 0xf4, 0x6f, 0x66, 0xc5, 0xf3, 0xa6, 0xea, 0x39, 0xca, 0x80, 0x31,
+ 0x48, 0xf2, 0x55, 0x00, 0xb1, 0x50, 0x0d, 0x8f, 0x1c, 0x78, 0x84, 0x75, 0x67, 0x46, 0x1d, 0xee,
+ 0x34, 0xf3, 0xe5, 0xbe, 0xee, 0x04, 0x56, 0xb0, 0x2f, 0x4e, 0x78, 0xb9, 0x1b, 0xa2, 0x60, 0x0c,
+ 0x51, 0xfb, 0x17, 0x45, 0xa8, 0x2b, 0xbb, 0xf2, 0x63, 0x30, 0x26, 0x76, 0x12, 0xc6, 0xc4, 0x13,
+ 0x8a, 0xe0, 0xcd, 0x32, 0x25, 0xba, 0x29, 0x53, 0xe2, 0x72, 0x7e, 0x51, 0x0f, 0x37, 0x24, 0x7e,
+ 0xbb, 0x08, 0x13, 0x8a, 0x35, 0xaf, 0x19, 0xf1, 0x77, 0xe1, 0x8c, 0xf0, 0xbc, 0xaf, 0xe9, 0xf7,
+ 0xc5, 0x61, 0x37, 0xbc, 0xc2, 0xca, 0x22, 0x68, 0xb6, 0x95, 0x24, 0x61, 0x9a, 0x97, 0x35, 0x6b,
+ 0x91, 0xb4, 0xc9, 0x56, 0x5d, 0xc2, 0x57, 0x27, 0x56, 0x87, 0xbc, 0x59, 0xb7, 0x52, 0x34, 0x1c,
+ 0xe0, 0x4e, 0xdb, 0x31, 0xcb, 0xa7, 0x60, 0xc7, 0xfc, 0x0f, 0x05, 0x18, 0x8b, 0xea, 0xeb, 0xd4,
+ 0xad, 0x98, 0xdb, 0x49, 0x2b, 0xe6, 0x7c, 0xee, 0xe6, 0x30, 0xc4, 0x86, 0xf9, 0xd7, 0x6a, 0x90,
+ 0x08, 0x24, 0x27, 0x5b, 0x70, 0xc9, 0xca, 0x0c, 0x87, 0x8b, 0x8d, 0x36, 0xe1, 0xce, 0xe8, 0x95,
+ 0xa1, 0x9c, 0xf8, 0x10, 0x14, 0xd2, 0x87, 0xfa, 0x1e, 0xf5, 0x02, 0xcb, 0xa0, 0xea, 0xfb, 0x96,
+ 0x73, 0xab, 0x64, 0xd2, 0x52, 0x1b, 0xd6, 0xe9, 0x5d, 0x29, 0x00, 0x43, 0x51, 0x64, 0x0b, 0x2a,
+ 0xd4, 0xec, 0x50, 0x75, 0xfc, 0x50, 0xce, 0xc3, 0x3d, 0xc3, 0xfa, 0x64, 0x6f, 0x3e, 0x0a, 0x68,
+ 0xe2, 0x43, 0xc3, 0x56, 0x9e, 0x38, 0xd9, 0x0e, 0x47, 0x57, 0xb0, 0x42, 0x9f, 0x5e, 0x74, 0x32,
+ 0x41, 0x98, 0x84, 0x91, 0x1c, 0xb2, 0x1b, 0x9a, 0x04, 0x2b, 0x27, 0x34, 0x78, 0x3c, 0xc4, 0x20,
+ 0xe8, 0x43, 0xe3, 0x9e, 0x1e, 0x50, 0xaf, 0xab, 0x7b, 0xbb, 0x72, 0xb5, 0x31, 0xfa, 0x17, 0xbe,
+ 0xa5, 0x90, 0xa2, 0x2f, 0x0c, 0x93, 0x30, 0x92, 0x43, 0x5c, 0x68, 0x04, 0x52, 0x7d, 0x56, 0x76,
+ 0xcf, 0xd1, 0x85, 0x2a, 0x45, 0xdc, 0x97, 0x01, 0xe5, 0xea, 0x15, 0x23, 0x19, 0x64, 0x2f, 0x71,
+ 0xfe, 0xb2, 0x38, 0x75, 0xbb, 0x95, 0xc3, 0x7e, 0x2e, 0xa1, 0xa2, 0xe9, 0x26, 0xfb, 0x1c, 0x67,
+ 0xed, 0x7f, 0x55, 0xa2, 0x61, 0xf9, 0x71, 0x5b, 0xf5, 0x5e, 0x4e, 0x5a, 0xf5, 0xae, 0xa4, 0xad,
+ 0x7a, 0x29, 0x87, 0xee, 0xf1, 0x43, 0x50, 0x53, 0xf6, 0xb4, 0xf2, 0x29, 0xd8, 0xd3, 0x5e, 0x82,
+ 0xe6, 0x1e, 0x1f, 0x09, 0xc4, 0x59, 0x46, 0x15, 0x3e, 0x8d, 0xf0, 0x91, 0xfd, 0x6e, 0x94, 0x8c,
+ 0x71, 0x1e, 0x96, 0x45, 0xde, 0x38, 0x11, 0x1e, 0x06, 0x2b, 0xb3, 0xb4, 0xa3, 0x64, 0x8c, 0xf3,
+ 0xf0, 0xe8, 0x35, 0xcb, 0xd9, 0x15, 0x19, 0x6a, 0x3c, 0x83, 0x88, 0x5e, 0x53, 0x89, 0x18, 0xd1,
+ 0xc9, 0x35, 0xa8, 0xf7, 0xcd, 0x6d, 0xc1, 0x5b, 0xe7, 0xbc, 0x5c, 0xc3, 0xdc, 0x5c, 0x5c, 0x92,
+ 0x67, 0x2b, 0x29, 0x2a, 0x2b, 0x49, 0x57, 0xef, 0x29, 0x02, 0x5f, 0x1b, 0xca, 0x92, 0xac, 0x45,
+ 0xc9, 0x18, 0xe7, 0x21, 0xbf, 0x05, 0x13, 0x1e, 0x35, 0xfb, 0x06, 0x0d, 0x73, 0x09, 0x73, 0x1c,
+ 0x11, 0x57, 0x6b, 0xc4, 0x29, 0x98, 0xe2, 0x1c, 0x62, 0x15, 0x6c, 0x8e, 0x64, 0x15, 0xfc, 0x12,
+ 0x4c, 0x98, 0x9e, 0x6e, 0x39, 0xd4, 0xbc, 0xe3, 0x70, 0xaf, 0xbd, 0x8c, 0xa1, 0x0b, 0x4d, 0xf4,
+ 0x8b, 0x09, 0x2a, 0xa6, 0xb8, 0xb5, 0x1f, 0x17, 0x80, 0x0c, 0x46, 0x8b, 0x93, 0x1d, 0xa8, 0x3a,
+ 0xdc, 0x7a, 0x96, 0xfb, 0xf8, 0xe9, 0x98, 0x11, 0x4e, 0x0c, 0x6b, 0x32, 0x41, 0xe2, 0x13, 0x07,
+ 0xea, 0xf4, 0x7e, 0x40, 0x3d, 0x27, 0xdc, 0x3d, 0x72, 0x32, 0x47, 0x5d, 0x8b, 0xd5, 0x84, 0x44,
+ 0xc6, 0x50, 0x86, 0xf6, 0xd3, 0x22, 0x34, 0x63, 0x7c, 0x8f, 0x5a, 0x94, 0xf2, 0x0d, 0xec, 0xc2,
+ 0x68, 0xb5, 0xe9, 0xd9, 0xb2, 0x87, 0xc6, 0x36, 0xb0, 0x4b, 0x12, 0xae, 0x62, 0x9c, 0x8f, 0xcc,
+ 0x01, 0x74, 0x75, 0x3f, 0xa0, 0x1e, 0x9f, 0xbd, 0x53, 0xdb, 0xc6, 0xd7, 0x42, 0x0a, 0xc6, 0xb8,
+ 0xc8, 0x55, 0x79, 0x58, 0x79, 0x39, 0x79, 0xcc, 0xdf, 0x90, 0x93, 0xc8, 0x2b, 0x27, 0x70, 0x12,
+ 0x39, 0xe9, 0xc0, 0xa4, 0x2a, 0xb5, 0xa2, 0x1e, 0xef, 0x10, 0x38, 0xb1, 0xfe, 0x49, 0x41, 0xe0,
+ 0x00, 0xa8, 0xf6, 0x9d, 0x02, 0x8c, 0x27, 0x4c, 0x26, 0xe2, 0x80, 0x3e, 0xb5, 0xd7, 0x21, 0x71,
+ 0x40, 0x5f, 0x6c, 0x8b, 0xc2, 0x8b, 0x50, 0x15, 0x15, 0x94, 0x0e, 0x61, 0x14, 0x55, 0x88, 0x92,
+ 0xca, 0xc6, 0x42, 0x69, 0x94, 0x4d, 0x8f, 0x85, 0xd2, 0x6a, 0x8b, 0x8a, 0x2e, 0x9c, 0x1b, 0xa2,
+ 0x74, 0x83, 0xce, 0x0d, 0x91, 0x8e, 0x21, 0x87, 0xf6, 0x3d, 0x5e, 0xee, 0xc0, 0xdb, 0x0f, 0xd7,
+ 0x82, 0x1d, 0xa8, 0xc9, 0xb0, 0x35, 0xd9, 0x35, 0xde, 0xcc, 0x61, 0xc7, 0xe1, 0x38, 0x32, 0x40,
+ 0x4b, 0x37, 0x76, 0xef, 0x6c, 0x6f, 0xa3, 0x42, 0x27, 0xd7, 0xa1, 0xe1, 0x3a, 0x4b, 0xba, 0x65,
+ 0xf7, 0x3d, 0x35, 0x33, 0xfc, 0x1a, 0x1b, 0xeb, 0xee, 0xa8, 0xc4, 0x07, 0x07, 0xd3, 0x17, 0xc2,
+ 0x97, 0x44, 0x21, 0x31, 0xca, 0xa9, 0xfd, 0x9f, 0x12, 0xf0, 0x90, 0x25, 0xf2, 0x1a, 0x34, 0xba,
+ 0xd4, 0xd8, 0xd1, 0x1d, 0xcb, 0x57, 0x47, 0x8c, 0xb2, 0xf5, 0x7d, 0x63, 0x4d, 0x25, 0x3e, 0x60,
+ 0x55, 0x30, 0xdf, 0x5e, 0xe5, 0xbb, 0x02, 0x22, 0x5e, 0x62, 0x40, 0xb5, 0xe3, 0xfb, 0x7a, 0xcf,
+ 0xca, 0xed, 0x31, 0x17, 0x47, 0x3a, 0x8a, 0x61, 0x40, 0x3c, 0xa3, 0x84, 0x26, 0x06, 0x54, 0x7a,
+ 0xb6, 0x6e, 0x39, 0xb9, 0xaf, 0xd6, 0x61, 0x5f, 0xb0, 0xce, 0x90, 0x84, 0x51, 0x8b, 0x3f, 0xa2,
+ 0xc0, 0x26, 0x7d, 0x68, 0xfa, 0x86, 0xa7, 0x77, 0xfd, 0x1d, 0x7d, 0xee, 0x95, 0x57, 0x73, 0xab,
+ 0x89, 0x91, 0x28, 0x31, 0x6b, 0x2d, 0xe0, 0xfc, 0x5a, 0xfb, 0xc6, 0xfc, 0xdc, 0x2b, 0xaf, 0x62,
+ 0x5c, 0x4e, 0x5c, 0xec, 0x2b, 0x2f, 0xcd, 0xc9, 0x9e, 0x7b, 0xe2, 0x62, 0x5f, 0x79, 0x69, 0x0e,
+ 0xe3, 0x72, 0xb4, 0xff, 0x5d, 0x80, 0x46, 0xc8, 0x4b, 0x36, 0x01, 0xd8, 0x18, 0x22, 0x0f, 0x61,
+ 0x3c, 0xd6, 0x85, 0x08, 0xdc, 0x2e, 0xb0, 0x19, 0x66, 0xc6, 0x18, 0x50, 0xc6, 0x29, 0x95, 0xc5,
+ 0x93, 0x3e, 0xa5, 0x72, 0x16, 0x1a, 0x3b, 0xba, 0x63, 0xfa, 0x3b, 0xfa, 0xae, 0x18, 0x4a, 0x63,
+ 0xe7, 0xb6, 0xde, 0x50, 0x04, 0x8c, 0x78, 0xb4, 0x7f, 0x56, 0x05, 0xe1, 0xe6, 0x66, 0x9d, 0xdd,
+ 0xb4, 0x7c, 0x11, 0x67, 0x5d, 0xe0, 0x39, 0xc3, 0xce, 0xbe, 0x28, 0xd3, 0x31, 0xe4, 0x20, 0x17,
+ 0xa1, 0xd4, 0xb5, 0x1c, 0xe9, 0xf3, 0xe1, 0x26, 0xbf, 0x35, 0xcb, 0x41, 0x96, 0xc6, 0x49, 0xfa,
+ 0x7d, 0x19, 0x22, 0x27, 0x48, 0xfa, 0x7d, 0x64, 0x69, 0x6c, 0x11, 0x6e, 0xbb, 0xee, 0x2e, 0xeb,
+ 0xb6, 0x2a, 0x92, 0xae, 0xcc, 0x55, 0x01, 0xbe, 0x08, 0x5f, 0x4d, 0x92, 0x30, 0xcd, 0x4b, 0x36,
+ 0xe1, 0x99, 0x0f, 0xa8, 0xe7, 0xca, 0x71, 0xaa, 0x6d, 0x53, 0xda, 0x53, 0x30, 0x42, 0x89, 0xe2,
+ 0x01, 0x79, 0xbf, 0x97, 0xcd, 0x82, 0xc3, 0xf2, 0xf2, 0xd0, 0x5e, 0xdd, 0xeb, 0xd0, 0x60, 0xdd,
+ 0x73, 0x0d, 0xea, 0xfb, 0x96, 0xd3, 0x51, 0xb0, 0xd5, 0x08, 0x76, 0x23, 0x9b, 0x05, 0x87, 0xe5,
+ 0x25, 0x6f, 0xc3, 0x94, 0x20, 0x09, 0x75, 0x61, 0x7e, 0x4f, 0xb7, 0x6c, 0x7d, 0xcb, 0xb2, 0xd5,
+ 0x8d, 0x74, 0xe3, 0xc2, 0xb3, 0xb2, 0x31, 0x84, 0x07, 0x87, 0xe6, 0x26, 0x37, 0x61, 0x52, 0xf9,
+ 0xd5, 0xd6, 0xa9, 0xd7, 0x0e, 0x43, 0x1f, 0xc6, 0x5b, 0x57, 0xd8, 0x8a, 0x77, 0x91, 0xf6, 0x3c,
+ 0x6a, 0xc4, 0xbd, 0x89, 0x8a, 0x0b, 0x07, 0xf2, 0x11, 0x84, 0x0b, 0x3c, 0xbe, 0x61, 0xb3, 0xb7,
+ 0xe0, 0xba, 0xb6, 0xe9, 0xde, 0x73, 0xd4, 0xb7, 0x0b, 0xd5, 0x8e, 0xbb, 0xd2, 0xda, 0x99, 0x1c,
+ 0x38, 0x24, 0x27, 0xfb, 0x72, 0x4e, 0x59, 0x74, 0xef, 0x39, 0x69, 0x54, 0x88, 0xbe, 0xbc, 0x3d,
+ 0x84, 0x07, 0x87, 0xe6, 0x26, 0x4b, 0x40, 0xd2, 0x5f, 0xb0, 0xd9, 0x93, 0xee, 0xd9, 0x0b, 0xe2,
+ 0x3c, 0x95, 0x34, 0x15, 0x33, 0x72, 0x90, 0x55, 0x38, 0x9f, 0x4e, 0x65, 0xe2, 0xa4, 0x97, 0x96,
+ 0x9f, 0xa4, 0x8a, 0x19, 0x74, 0xcc, 0xcc, 0xa5, 0xfd, 0xf3, 0x22, 0x8c, 0x27, 0x36, 0xe0, 0x3f,
+ 0x71, 0x1b, 0x9d, 0x99, 0x0e, 0xde, 0xf5, 0x3b, 0x2b, 0x8b, 0x37, 0xa8, 0x6e, 0x52, 0xef, 0x16,
+ 0x55, 0x87, 0x25, 0xf0, 0x41, 0x65, 0x2d, 0x41, 0xc1, 0x14, 0x27, 0xd9, 0x86, 0x8a, 0xb0, 0x28,
+ 0xe7, 0xbd, 0x5a, 0x43, 0xd5, 0x11, 0x37, 0x2b, 0xcb, 0xfb, 0x68, 0x5c, 0x8f, 0xa2, 0x80, 0xd7,
+ 0x02, 0x18, 0x8b, 0x73, 0xb0, 0x81, 0x24, 0x52, 0x37, 0x6b, 0x09, 0x55, 0x73, 0x05, 0x4a, 0x41,
+ 0x30, 0xea, 0x16, 0x6a, 0xe1, 0xa1, 0xd8, 0x58, 0x45, 0x86, 0xa1, 0x6d, 0xb3, 0x7f, 0xe7, 0xfb,
+ 0x96, 0xeb, 0xc8, 0xf3, 0xb4, 0x37, 0xa1, 0x16, 0x48, 0x23, 0xdd, 0x68, 0x5b, 0xc0, 0xb9, 0x8e,
+ 0xa2, 0x0c, 0x74, 0x0a, 0x4b, 0xfb, 0x8f, 0x45, 0x68, 0x84, 0x0b, 0xea, 0x23, 0x9c, 0x53, 0xed,
+ 0x42, 0x23, 0x8c, 0xcf, 0xca, 0x7d, 0x5b, 0x5f, 0x14, 0x36, 0xc4, 0xd7, 0x80, 0xe1, 0x2b, 0x46,
+ 0x32, 0xe2, 0xb1, 0x5f, 0xa5, 0x1c, 0xb1, 0x5f, 0x3d, 0xa8, 0x05, 0x9e, 0xd5, 0xe9, 0x48, 0xed,
+ 0x3c, 0x4f, 0xf0, 0x57, 0x58, 0x5d, 0x1b, 0x02, 0x50, 0xd6, 0xac, 0x78, 0x41, 0x25, 0x46, 0x7b,
+ 0x0f, 0x26, 0xd3, 0x9c, 0x5c, 0x75, 0x35, 0x76, 0xa8, 0xd9, 0xb7, 0x55, 0x1d, 0x47, 0xaa, 0xab,
+ 0x4c, 0xc7, 0x90, 0x83, 0x2d, 0x7f, 0xd9, 0x6f, 0xfa, 0xc0, 0x75, 0x94, 0xfa, 0xc8, 0x57, 0x01,
+ 0x1b, 0x32, 0x0d, 0x43, 0xaa, 0xf6, 0xdf, 0x4a, 0x70, 0x31, 0x32, 0x8b, 0xac, 0xe9, 0x8e, 0xde,
+ 0x39, 0xc2, 0x15, 0x6d, 0x9f, 0x6f, 0xaa, 0x39, 0xee, 0x65, 0x03, 0xa5, 0x27, 0xe0, 0xb2, 0x81,
+ 0xff, 0x5b, 0x04, 0x1e, 0x4b, 0x4a, 0xbe, 0x06, 0x63, 0x7a, 0xec, 0x76, 0x4e, 0xf9, 0x3b, 0xaf,
+ 0xe7, 0xfe, 0x9d, 0x3c, 0x64, 0x35, 0x8c, 0x8d, 0x8a, 0xa7, 0x62, 0x42, 0x20, 0x71, 0xa1, 0xbe,
+ 0xad, 0xdb, 0x36, 0xd3, 0x85, 0x72, 0xbb, 0x79, 0x12, 0xc2, 0x79, 0x33, 0x5f, 0x92, 0xd0, 0x18,
+ 0x0a, 0x21, 0x1f, 0x16, 0x60, 0xdc, 0x8b, 0x2f, 0x93, 0xe4, 0x0f, 0xc9, 0x13, 0x04, 0x10, 0x43,
+ 0x8b, 0x47, 0x62, 0xc5, 0xd7, 0x62, 0x49, 0x99, 0xda, 0x7f, 0x2d, 0xc0, 0x78, 0xdb, 0xb6, 0x4c,
+ 0xcb, 0xe9, 0x9c, 0xe2, 0x5d, 0x07, 0x77, 0xa0, 0xe2, 0xdb, 0x96, 0x49, 0x47, 0x9c, 0x4d, 0xc4,
+ 0x3c, 0xc6, 0x00, 0x50, 0xe0, 0x24, 0x2f, 0x4f, 0x28, 0x1d, 0xe1, 0xf2, 0x84, 0x5f, 0x54, 0x41,
+ 0x46, 0x45, 0x93, 0x3e, 0x34, 0x3a, 0xea, 0x4c, 0x76, 0xf9, 0x8d, 0x37, 0x72, 0x9c, 0xe7, 0x97,
+ 0x38, 0xdd, 0x5d, 0x8c, 0xfd, 0x61, 0x22, 0x46, 0x92, 0x08, 0x4d, 0x5e, 0x0b, 0xbb, 0x98, 0xf3,
+ 0x5a, 0x58, 0x21, 0x6e, 0xf0, 0x62, 0x58, 0x1d, 0xca, 0x3b, 0x41, 0xd0, 0x93, 0x8d, 0x69, 0xf4,
+ 0xb0, 0xf7, 0xe8, 0x48, 0x19, 0xa1, 0x13, 0xb1, 0x77, 0xe4, 0xd0, 0x4c, 0x84, 0xa3, 0x87, 0xd7,
+ 0x80, 0x2d, 0xe4, 0x0a, 0x38, 0x88, 0x8b, 0x60, 0xef, 0xc8, 0xa1, 0xc9, 0x1f, 0x40, 0x33, 0xf0,
+ 0x74, 0xc7, 0xdf, 0x76, 0xbd, 0x2e, 0xf5, 0xe4, 0x1a, 0x75, 0x29, 0xc7, 0xcd, 0xa8, 0x1b, 0x11,
+ 0x9a, 0xf0, 0x64, 0x26, 0x92, 0x30, 0x2e, 0x8d, 0xec, 0x42, 0xbd, 0x6f, 0x8a, 0x82, 0x49, 0xf3,
+ 0xd3, 0x7c, 0x9e, 0xcb, 0x6e, 0x63, 0xe1, 0x04, 0xea, 0x0d, 0x43, 0x01, 0xc9, 0x1b, 0xef, 0x6a,
+ 0x27, 0x75, 0xe3, 0x5d, 0xbc, 0x35, 0x66, 0x9d, 0x77, 0x41, 0xba, 0x52, 0xaf, 0x75, 0x3a, 0x32,
+ 0x1a, 0x6a, 0x29, 0xb7, 0xca, 0x29, 0x44, 0x36, 0x43, 0xdd, 0xd8, 0xe9, 0xa0, 0x92, 0xa1, 0x75,
+ 0x41, 0x7a, 0x19, 0x88, 0x91, 0xb8, 0x17, 0x46, 0x6c, 0xc2, 0x9a, 0x3d, 0xda, 0x78, 0x10, 0x5e,
+ 0x50, 0x12, 0x3b, 0x97, 0x3a, 0xf3, 0x02, 0x18, 0xed, 0x3f, 0x15, 0xa1, 0xb4, 0xb1, 0xda, 0x16,
+ 0x67, 0x4d, 0xf2, 0x4b, 0x97, 0x68, 0x7b, 0xd7, 0xea, 0xdd, 0xa5, 0x9e, 0xb5, 0xbd, 0x2f, 0x97,
+ 0xde, 0xb1, 0xb3, 0x26, 0xd3, 0x1c, 0x98, 0x91, 0x8b, 0xbc, 0x03, 0x63, 0x86, 0xbe, 0x40, 0xbd,
+ 0x60, 0x14, 0xc3, 0x02, 0xdf, 0x6d, 0xba, 0x30, 0x1f, 0x65, 0xc7, 0x04, 0x18, 0xd9, 0x04, 0x30,
+ 0x22, 0xe8, 0xd2, 0xb1, 0xcd, 0x21, 0x31, 0xe0, 0x18, 0x10, 0x41, 0x68, 0xec, 0x32, 0x56, 0x8e,
+ 0x5a, 0x3e, 0x0e, 0x2a, 0x6f, 0x39, 0xb7, 0x54, 0x5e, 0x8c, 0x60, 0x34, 0x07, 0xc6, 0x13, 0x97,
+ 0xc5, 0x90, 0x2f, 0x42, 0xdd, 0xed, 0xc5, 0x86, 0xd3, 0x06, 0x8f, 0xbb, 0xac, 0xdf, 0x91, 0x69,
+ 0x0f, 0x0e, 0xa6, 0xc7, 0x57, 0xdd, 0x8e, 0x65, 0xa8, 0x04, 0x0c, 0xd9, 0x89, 0x06, 0x55, 0xbe,
+ 0x45, 0x4c, 0x5d, 0x15, 0xc3, 0xe7, 0x0e, 0x7e, 0x9b, 0x83, 0x8f, 0x92, 0xa2, 0xfd, 0x61, 0x19,
+ 0x22, 0xdf, 0x1c, 0xf1, 0xa1, 0x2a, 0x42, 0xe0, 0xe5, 0xc8, 0x7d, 0xaa, 0xd1, 0xf6, 0x52, 0x14,
+ 0xe9, 0x40, 0xe9, 0x3d, 0x77, 0x2b, 0xf7, 0xc0, 0x1d, 0xdb, 0x1b, 0x2e, 0x6c, 0x65, 0xb1, 0x04,
+ 0x64, 0x12, 0xc8, 0xdf, 0x2e, 0xc0, 0x59, 0x3f, 0xad, 0xfa, 0xca, 0xe6, 0x80, 0xf9, 0x75, 0xfc,
+ 0xb4, 0x32, 0x2d, 0x03, 0x64, 0x87, 0x91, 0x71, 0xb0, 0x2c, 0xac, 0xfe, 0x85, 0xd3, 0x4c, 0x36,
+ 0xa7, 0xe5, 0x9c, 0x17, 0x1c, 0x26, 0xeb, 0x3f, 0x99, 0x86, 0x52, 0x94, 0xf6, 0xc7, 0x45, 0x68,
+ 0xc6, 0x46, 0xeb, 0xdc, 0x37, 0x10, 0xdd, 0x4f, 0xdd, 0x40, 0xb4, 0x3e, 0xba, 0x0f, 0x39, 0x2a,
+ 0xd5, 0x69, 0x5f, 0x42, 0xf4, 0xaf, 0x8a, 0x50, 0xda, 0x5c, 0x5c, 0x4a, 0x2e, 0x5a, 0x0b, 0x8f,
+ 0x61, 0xd1, 0xba, 0x03, 0xb5, 0xad, 0xbe, 0x65, 0x07, 0x96, 0x93, 0xfb, 0xf4, 0x0a, 0x75, 0x61,
+ 0x93, 0xf4, 0x31, 0x08, 0x54, 0x54, 0xf0, 0xa4, 0x03, 0xb5, 0x8e, 0x38, 0x3e, 0x30, 0x77, 0x64,
+ 0x9d, 0x3c, 0x86, 0x50, 0x08, 0x92, 0x2f, 0xa8, 0xd0, 0xb5, 0x7d, 0x90, 0x37, 0xcf, 0x3f, 0xf6,
+ 0xda, 0xd4, 0xfe, 0x00, 0x42, 0x2d, 0xe0, 0xf1, 0x0b, 0xff, 0x1f, 0x05, 0x48, 0x2a, 0x3e, 0x8f,
+ 0xbf, 0x35, 0xed, 0xa6, 0x5b, 0xd3, 0xe2, 0x49, 0x74, 0xbe, 0xec, 0x06, 0xa5, 0xfd, 0xd3, 0x22,
+ 0x54, 0x1f, 0xdb, 0x8e, 0x63, 0x9a, 0x08, 0x12, 0x5c, 0xc8, 0x39, 0x30, 0x0e, 0x0d, 0x11, 0xec,
+ 0xa6, 0x42, 0x04, 0xf3, 0x5e, 0x31, 0xfb, 0x88, 0x00, 0xc1, 0x7f, 0x57, 0x00, 0x39, 0x2c, 0xaf,
+ 0x38, 0x7e, 0xa0, 0x3b, 0x06, 0x25, 0x46, 0x38, 0x07, 0xe4, 0x8d, 0x44, 0x91, 0xd1, 0x5a, 0x62,
+ 0xda, 0xe7, 0xcf, 0x6a, 0xcc, 0x27, 0x5f, 0x80, 0xfa, 0x8e, 0xeb, 0x07, 0x7c, 0x9c, 0x2f, 0x26,
+ 0xad, 0x4b, 0x37, 0x64, 0x3a, 0x86, 0x1c, 0x69, 0x8f, 0x6b, 0x65, 0xb8, 0xc7, 0x55, 0xfb, 0x56,
+ 0x11, 0xc6, 0x3e, 0x2b, 0xdb, 0xa6, 0xb3, 0x42, 0x2a, 0x4b, 0x39, 0x43, 0x2a, 0xcb, 0xc7, 0x09,
+ 0xa9, 0xd4, 0x7e, 0x58, 0x00, 0x78, 0x6c, 0x7b, 0xb6, 0xcd, 0x64, 0xb4, 0x63, 0xee, 0x76, 0x95,
+ 0x1d, 0xeb, 0xf8, 0x8f, 0x2b, 0xea, 0x93, 0x78, 0xa4, 0xe3, 0x47, 0x05, 0x98, 0xd0, 0x13, 0xd1,
+ 0x83, 0xb9, 0x55, 0xcb, 0x54, 0x30, 0x62, 0x18, 0xfc, 0x92, 0x4c, 0xc7, 0x94, 0x58, 0xf2, 0x7a,
+ 0x74, 0x5e, 0xf0, 0xed, 0xa8, 0xd9, 0x0f, 0x1c, 0xf4, 0xcb, 0xd5, 0x9c, 0x04, 0xe7, 0x23, 0xa2,
+ 0x35, 0x4b, 0x27, 0x12, 0xad, 0x19, 0xdf, 0x87, 0x56, 0x7e, 0xe8, 0x3e, 0xb4, 0x3d, 0x68, 0x6c,
+ 0x7b, 0x6e, 0x97, 0x07, 0x44, 0xca, 0xcb, 0x69, 0xaf, 0xe7, 0x98, 0x53, 0xa2, 0x6b, 0xd9, 0x23,
+ 0x1b, 0xcf, 0x92, 0xc2, 0xc7, 0x48, 0x14, 0x37, 0x8b, 0xbb, 0x42, 0x6a, 0xf5, 0x24, 0xa5, 0x86,
+ 0x63, 0xc9, 0x86, 0x40, 0x47, 0x25, 0x26, 0x19, 0x04, 0x59, 0x7b, 0x3c, 0x41, 0x90, 0xda, 0x77,
+ 0xab, 0x6a, 0x00, 0x7b, 0xe2, 0x8e, 0xa6, 0xfc, 0xec, 0xef, 0xf5, 0x4d, 0x6f, 0xc4, 0xad, 0x3d,
+ 0xc6, 0x8d, 0xb8, 0xf5, 0x93, 0xd9, 0x88, 0xdb, 0xc8, 0xb7, 0x11, 0x17, 0xf2, 0x6f, 0xc4, 0x6d,
+ 0xe6, 0xdb, 0x88, 0x3b, 0x36, 0xd2, 0x46, 0xdc, 0xf1, 0x23, 0x6d, 0xc4, 0x3d, 0x28, 0x41, 0x6a,
+ 0x95, 0xf9, 0xb9, 0x47, 0xe9, 0xff, 0x2b, 0x8f, 0xd2, 0xc7, 0x45, 0x88, 0x86, 0xcd, 0x63, 0x46,
+ 0xdc, 0xbc, 0x0d, 0xf5, 0xae, 0x7e, 0x7f, 0x91, 0xda, 0xfa, 0x7e, 0x9e, 0xfb, 0x57, 0xd7, 0x24,
+ 0x06, 0x86, 0x68, 0xc4, 0x07, 0xb0, 0xc2, 0x33, 0xd0, 0x73, 0xdb, 0xe6, 0xa3, 0xe3, 0xd4, 0x85,
+ 0xf5, 0x2f, 0x7a, 0xc7, 0x98, 0x18, 0xed, 0xdf, 0x16, 0x41, 0x1e, 0x96, 0x4f, 0x28, 0x54, 0xb6,
+ 0xad, 0xfb, 0xd4, 0xcc, 0x1d, 0x3f, 0x1b, 0xbb, 0x15, 0x5b, 0x38, 0x1f, 0x78, 0x02, 0x0a, 0x74,
+ 0x6e, 0x55, 0x16, 0xce, 0x24, 0x59, 0x7f, 0x39, 0xac, 0xca, 0x71, 0xa7, 0x94, 0xb4, 0x2a, 0x8b,
+ 0x24, 0x54, 0x32, 0x84, 0x11, 0x9b, 0xc7, 0x15, 0xe4, 0xf6, 0x9d, 0x25, 0xe2, 0x13, 0x94, 0x11,
+ 0xdb, 0x17, 0x3b, 0xf1, 0xa5, 0x8c, 0xd6, 0xef, 0xff, 0xe0, 0x47, 0x57, 0x9e, 0xfa, 0xe1, 0x8f,
+ 0xae, 0x3c, 0xf5, 0xc9, 0x8f, 0xae, 0x3c, 0xf5, 0x87, 0x87, 0x57, 0x0a, 0x3f, 0x38, 0xbc, 0x52,
+ 0xf8, 0xe1, 0xe1, 0x95, 0xc2, 0x27, 0x87, 0x57, 0x0a, 0xff, 0xf9, 0xf0, 0x4a, 0xe1, 0x6f, 0xfc,
+ 0x97, 0x2b, 0x4f, 0xfd, 0xde, 0x6b, 0x51, 0x11, 0x66, 0x55, 0x11, 0x66, 0x95, 0xc0, 0xd9, 0xde,
+ 0x6e, 0x67, 0x96, 0x15, 0x21, 0x4a, 0x51, 0x45, 0xf8, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6c,
+ 0x98, 0x3b, 0x47, 0x90, 0x97, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -7208,6 +7210,14 @@ func (m *PipelineStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ i--
+ if m.DrainedOnPause {
+ dAtA[i] = 1
+ } else {
+ dAtA[i] = 0
+ }
+ i--
+ dAtA[i] = 0x60
i = encodeVarintGenerated(dAtA, i, uint64(m.ObservedGeneration))
i--
dAtA[i] = 0x58
@@ -10699,6 +10709,7 @@ func (m *PipelineStatus) Size() (n int) {
n += 1 + sovGenerated(uint64(*m.ReduceUDFCount))
}
n += 1 + sovGenerated(uint64(m.ObservedGeneration))
+ n += 2
return n
}
@@ -12452,6 +12463,7 @@ func (this *PipelineStatus) String() string {
`MapUDFCount:` + valueToStringGenerated(this.MapUDFCount) + `,`,
`ReduceUDFCount:` + valueToStringGenerated(this.ReduceUDFCount) + `,`,
`ObservedGeneration:` + fmt.Sprintf("%v", this.ObservedGeneration) + `,`,
+ `DrainedOnPause:` + fmt.Sprintf("%v", this.DrainedOnPause) + `,`,
`}`,
}, "")
return s
@@ -25929,6 +25941,26 @@ func (m *PipelineStatus) Unmarshal(dAtA []byte) error {
break
}
}
+ case 12:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field DrainedOnPause", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.DrainedOnPause = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 8fadbab191..55940285e3 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -1218,6 +1218,12 @@ message PipelineStatus {
// The generation observed by the Pipeline controller.
// +optional
optional int64 observedGeneration = 11;
+
+ // Field to indicate if a pipeline drain successfully occurred, or it timed out.
+ // Set to true when the Pipeline is in Paused state, and after it has successfully been drained.
+ // defaults to false
+ // +kubebuilder:default=false
+ optional bool drainedOnPause = 12;
}
message RedisBufferService {
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index bb128213f0..0ed5471bc8 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -4132,6 +4132,13 @@ func schema_pkg_apis_numaflow_v1alpha1_PipelineStatus(ref common.ReferenceCallba
Format: "int64",
},
},
+ "drainedOnPause": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false",
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
},
},
},
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types.go b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
index 4604239674..c68a7d647c 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
@@ -633,6 +633,11 @@ type PipelineStatus struct {
// The generation observed by the Pipeline controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,11,opt,name=observedGeneration"`
+ // Field to indicate if a pipeline drain successfully occurred, or it timed out.
+ // Set to true when the Pipeline is in Paused state, and after it has successfully been drained.
+ // defaults to false
+ // +kubebuilder:default=false
+ DrainedOnPause bool `json:"drainedOnPause,omitempty" protobuf:"bytes,12,opt,name=drainedOnPause"`
}
// SetVertexCounts sets the counts of vertices.
@@ -764,6 +769,16 @@ func (pls *PipelineStatus) SetObservedGeneration(value int64) {
pls.ObservedGeneration = value
}
+// MarkDrainedOnPauseTrue sets the DrainedOnPause field to true
+func (pls *PipelineStatus) MarkDrainedOnPauseTrue() {
+ pls.DrainedOnPause = true
+}
+
+// MarkDrainedOnPauseFalse sets the DrainedOnPause field to false
+func (pls *PipelineStatus) MarkDrainedOnPauseFalse() {
+ pls.DrainedOnPause = false
+}
+
// IsHealthy indicates whether the pipeline is in healthy status
func (pls *PipelineStatus) IsHealthy() bool {
switch pls.Phase {
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go b/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go
index 0bd93e53fb..d7e5c334b9 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go
@@ -339,6 +339,15 @@ func Test_PipelineMarkPhases(t *testing.T) {
assert.Equal(t, PipelinePhaseRunning, s.Phase)
}
+func Test_PipelineMarkDrained(t *testing.T) {
+ s := PipelineStatus{}
+ assert.Equal(t, false, s.DrainedOnPause)
+ s.MarkDrainedOnPauseTrue()
+ assert.Equal(t, true, s.DrainedOnPause)
+ s.MarkDrainedOnPauseFalse()
+ assert.Equal(t, false, s.DrainedOnPause)
+}
+
func Test_GetDownstreamEdges(t *testing.T) {
pl := Pipeline{
ObjectMeta: metav1.ObjectMeta{
diff --git a/pkg/reconciler/pipeline/controller.go b/pkg/reconciler/pipeline/controller.go
index 0be76a8d0b..0f5354b2c8 100644
--- a/pkg/reconciler/pipeline/controller.go
+++ b/pkg/reconciler/pipeline/controller.go
@@ -144,9 +144,17 @@ func (r *pipelineReconciler) reconcile(ctx context.Context, pl *dfv1.Pipeline) (
}()
pl.Status.SetObservedGeneration(pl.Generation)
-
- if oldPhase := pl.Status.Phase; pl.Spec.Lifecycle.GetDesiredPhase() == dfv1.PipelinePhasePaused ||
- oldPhase == dfv1.PipelinePhasePaused || oldPhase == dfv1.PipelinePhasePausing {
+ // Regular pipeline change
+ // This should be happening in call cases to ensure a clean initialization regardless of the lifecycle phase
+ // Eg: even for a pipeline started with desiredPhase = Pause, we should still create the resources for the pipeline
+ result, err := r.reconcileNonLifecycleChanges(ctx, pl)
+ if err != nil {
+ r.recorder.Eventf(pl, corev1.EventTypeWarning, "ReconcilePipelineFailed", "Failed to reconcile pipeline: %v", err.Error())
+ return result, err
+ }
+ // check if any changes related to pause/resume lifecycle for the pipeline
+ if isLifecycleChange(pl) {
+ oldPhase := pl.Status.Phase
requeue, err := r.updateDesiredState(ctx, pl)
if err != nil {
logMsg := fmt.Sprintf("Updated desired pipeline phase failed: %v", zap.Error(err))
@@ -162,16 +170,24 @@ func (r *pipelineReconciler) reconcile(ctx context.Context, pl *dfv1.Pipeline) (
return ctrl.Result{RequeueAfter: dfv1.DefaultRequeueAfter}, nil
}
return ctrl.Result{}, nil
-
}
+ return result, nil
+}
- // Regular pipeline change
- result, err := r.reconcileNonLifecycleChanges(ctx, pl)
- if err != nil {
- r.recorder.Eventf(pl, corev1.EventTypeWarning, "ReconcilePipelineFailed", "Failed to reconcile pipeline: %v", err.Error())
+// isLifecycleChange determines whether there has been a change requested in the lifecycle
+// of a Pipeline object, specifically relating to the paused and pausing states.
+func isLifecycleChange(pl *dfv1.Pipeline) bool {
+ // Extract the current phase from the status of the pipeline.
+ // Check if the desired phase of the pipeline is 'Paused', or if the current phase of the
+ // pipeline is either 'Paused' or 'Pausing'. This indicates a transition into or out of
+ // a paused state which is a lifecycle phase change
+ if oldPhase := pl.Status.Phase; pl.Spec.Lifecycle.GetDesiredPhase() == dfv1.PipelinePhasePaused ||
+ oldPhase == dfv1.PipelinePhasePaused || oldPhase == dfv1.PipelinePhasePausing {
+ return true
}
- return result, err
+ // If none of the conditions are met, return false
+ return false
}
// reconcileNonLifecycleChanges do the jobs not related to pipeline lifecycle changes.
@@ -345,7 +361,12 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
}
pl.Status.MarkDeployed()
- pl.Status.SetPhase(pl.Spec.Lifecycle.GetDesiredPhase(), "")
+ // If the pipeline has a lifecycle change, then do not update the phase as
+ // this should happen only after the required configs for the lifecycle changes
+ // have been applied.
+ if !isLifecycleChange(pl) {
+ pl.Status.SetPhase(pl.Spec.Lifecycle.GetDesiredPhase(), "")
+ }
if err := r.checkChildrenResourceStatus(ctx, pl); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to check pipeline children resource status, %w", err)
}
@@ -599,7 +620,8 @@ func buildVertices(pl *dfv1.Pipeline) map[string]dfv1.Vertex {
copyVertexTemplate(pl, vCopy)
copyVertexLimits(pl, vCopy)
replicas := int32(1)
- if pl.Status.Phase == dfv1.PipelinePhasePaused {
+ // If the desired phase is pause or we are in the middle of pausing we should not start any vertex replicas
+ if isLifecycleChange(pl) {
replicas = int32(0)
} else if v.IsReduceUDF() {
partitions := pl.NumOfPartitions(v.Name)
@@ -794,7 +816,6 @@ func (r *pipelineReconciler) updateDesiredState(ctx context.Context, pl *dfv1.Pi
}
func (r *pipelineReconciler) resumePipeline(ctx context.Context, pl *dfv1.Pipeline) (bool, error) {
-
// reset pause timestamp
if pl.GetAnnotations()[dfv1.KeyPauseTimestamp] != "" {
err := r.client.Patch(ctx, pl, client.RawPatch(types.JSONPatchType, []byte(dfv1.RemovePauseTimestampPatch)))
@@ -806,17 +827,18 @@ func (r *pipelineReconciler) resumePipeline(ctx context.Context, pl *dfv1.Pipeli
}
}
}
-
_, err := r.scaleUpAllVertices(ctx, pl)
if err != nil {
return false, err
}
+ // mark the drained field as false to refresh the drained status as this will
+ // be a new lifecycle from running
+ pl.Status.MarkDrainedOnPauseFalse()
pl.Status.MarkPhaseRunning()
return false, nil
}
func (r *pipelineReconciler) pausePipeline(ctx context.Context, pl *dfv1.Pipeline) (bool, error) {
-
// check that annotations / pause timestamp annotation exist
if pl.GetAnnotations() == nil || pl.GetAnnotations()[dfv1.KeyPauseTimestamp] == "" {
pl.SetAnnotations(map[string]string{dfv1.KeyPauseTimestamp: time.Now().Format(time.RFC3339)})
@@ -855,12 +877,16 @@ func (r *pipelineReconciler) pausePipeline(ctx context.Context, pl *dfv1.Pipelin
return false, err
}
- // if drain is completed or we have exceed pause deadline, mark pl as paused and scale down
+ // if drain is completed, or we have exceeded the pause deadline, mark pl as paused and scale down
if time.Now().After(pauseTimestamp.Add(time.Duration(pl.Spec.Lifecycle.GetPauseGracePeriodSeconds())*time.Second)) || drainCompleted {
_, err := r.scaleDownAllVertices(ctx, pl)
if err != nil {
return true, err
}
+ // if the drain completed succesfully, then set the DrainedOnPause field to true
+ if drainCompleted {
+ pl.Status.MarkDrainedOnPauseTrue()
+ }
pl.Status.MarkPhasePaused()
return false, nil
}
diff --git a/pkg/reconciler/pipeline/controller_test.go b/pkg/reconciler/pipeline/controller_test.go
index 3e65502147..0cf9205f0a 100644
--- a/pkg/reconciler/pipeline/controller_test.go
+++ b/pkg/reconciler/pipeline/controller_test.go
@@ -220,7 +220,6 @@ func Test_reconcileEvents(t *testing.T) {
_, err = r.reconcile(ctx, testObj)
assert.Error(t, err)
events := getEvents(t, r)
- assert.Contains(t, events, "Normal UpdatePipelinePhase Updated pipeline phase from Paused to Running")
assert.Contains(t, events, "Warning ReconcilePipelineFailed Failed to reconcile pipeline: the length of the pipeline name plus the vertex name is over the max limit. (very-very-very-loooooooooooooooooooooooooooooooooooong-input), [must be no more than 63 characters]")
})
@@ -945,3 +944,72 @@ func Test_checkChildrenResourceStatus(t *testing.T) {
}
})
}
+
+func TestIsLifecycleChange(t *testing.T) {
+ tests := []struct {
+ name string
+ currentPhase dfv1.PipelinePhase
+ desiredPhase dfv1.PipelinePhase
+ expectedResult bool
+ }{
+ {
+ name: "Change to paused from another state",
+ currentPhase: dfv1.PipelinePhaseRunning,
+ desiredPhase: dfv1.PipelinePhasePaused,
+ expectedResult: true,
+ },
+ {
+ name: "when already in paused",
+ currentPhase: dfv1.PipelinePhasePaused,
+ desiredPhase: dfv1.PipelinePhasePaused,
+ expectedResult: true,
+ },
+ {
+ name: "Change out of paused",
+ currentPhase: dfv1.PipelinePhasePaused,
+ desiredPhase: dfv1.PipelinePhaseRunning,
+ expectedResult: true,
+ },
+ {
+ name: "Change from another state to pausing",
+ currentPhase: dfv1.PipelinePhaseRunning,
+ desiredPhase: dfv1.PipelinePhasePausing,
+ expectedResult: false,
+ },
+ {
+ name: "Change from pausing to running",
+ currentPhase: dfv1.PipelinePhasePausing,
+ desiredPhase: dfv1.PipelinePhaseRunning,
+ expectedResult: true,
+ },
+ {
+ name: "No lifecycle change",
+ currentPhase: dfv1.PipelinePhaseRunning,
+ desiredPhase: dfv1.PipelinePhaseRunning,
+ expectedResult: false,
+ },
+ {
+ name: "No lifecycle change - updated phase",
+ currentPhase: dfv1.PipelinePhaseRunning,
+ desiredPhase: dfv1.PipelinePhaseDeleting,
+ expectedResult: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ pl := &dfv1.Pipeline{
+ Spec: dfv1.PipelineSpec{
+ Lifecycle: dfv1.Lifecycle{
+ DesiredPhase: test.desiredPhase,
+ },
+ },
+ Status: dfv1.PipelineStatus{
+ Phase: test.currentPhase,
+ },
+ }
+ result := isLifecycleChange(pl)
+ assert.Equal(t, test.expectedResult, result)
+ })
+ }
+}
diff --git a/rust/numaflow-models/src/models/pipeline_status.rs b/rust/numaflow-models/src/models/pipeline_status.rs
index 27d6049658..e67205b3cd 100644
--- a/rust/numaflow-models/src/models/pipeline_status.rs
+++ b/rust/numaflow-models/src/models/pipeline_status.rs
@@ -21,6 +21,9 @@ pub struct PipelineStatus {
/// Conditions are the latest available observations of a resource's current state.
#[serde(rename = "conditions", skip_serializing_if = "Option::is_none")]
pub conditions: Option>,
+ /// Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false
+ #[serde(rename = "drainedOnPause", skip_serializing_if = "Option::is_none")]
+ pub drained_on_pause: Option,
#[serde(rename = "lastUpdated", skip_serializing_if = "Option::is_none")]
pub last_updated: Option,
#[serde(rename = "mapUDFCount", skip_serializing_if = "Option::is_none")]
@@ -48,6 +51,7 @@ impl PipelineStatus {
pub fn new() -> PipelineStatus {
PipelineStatus {
conditions: None,
+ drained_on_pause: None,
last_updated: None,
map_udf_count: None,
message: None,
From 4f6b02e7f63a6f85373f93d4f1c493b25ea748e1 Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Fri, 6 Sep 2024 14:28:55 -0700
Subject: [PATCH 049/188] chore: improve pending logs to be on single line
(#2037)
Signed-off-by: Vigith Maurice
---
rust/monovertex/src/metrics.rs | 25 ++++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index f6f5519765..375e7c071c 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -1,3 +1,4 @@
+use std::collections::BTreeMap;
use std::net::SocketAddr;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
@@ -8,6 +9,12 @@ use axum::http::{Response, StatusCode};
use axum::response::IntoResponse;
use axum::{routing::get, Router};
use axum_server::tls_rustls::RustlsConfig;
+use prometheus_client::encoding::text::encode;
+use prometheus_client::metrics::counter::Counter;
+use prometheus_client::metrics::family::Family;
+use prometheus_client::metrics::gauge::Gauge;
+use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
+use prometheus_client::registry::Registry;
use rcgen::{generate_simple_self_signed, CertifiedKey};
use tokio::net::{TcpListener, ToSocketAddrs};
use tokio::sync::Mutex;
@@ -20,12 +27,6 @@ use crate::error::Error;
use crate::sink::SinkClient;
use crate::source::SourceClient;
use crate::transformer::TransformerClient;
-use prometheus_client::encoding::text::encode;
-use prometheus_client::metrics::counter::Counter;
-use prometheus_client::metrics::family::Family;
-use prometheus_client::metrics::gauge::Gauge;
-use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
-use prometheus_client::registry::Registry;
// Define the labels for the metrics
// Note: Please keep consistent with the definitions in MonoVertex daemon
@@ -483,6 +484,11 @@ async fn expose_pending_metrics(
) {
let mut ticker = time::interval(refresh_interval);
let lookback_seconds_map = vec![("1m", 60), ("default", 120), ("5m", 300), ("15m", 900)];
+
+ // store the pending info in a sorted way for deterministic display
+ // string concat is more efficient?
+ let mut pending_info: BTreeMap<&str, i64> = BTreeMap::new();
+
loop {
ticker.tick().await;
for (label, seconds) in &lookback_seconds_map {
@@ -490,13 +496,18 @@ async fn expose_pending_metrics(
if pending != -1 {
let mut metric_labels = forward_metrics_labels().clone();
metric_labels.push((PENDING_PERIOD_LABEL.to_string(), label.to_string()));
+ pending_info.insert(label, pending);
forward_metrics()
.source_pending
.get_or_create(&metric_labels)
.set(pending);
- info!("Pending messages ({}): {}", label, pending);
}
}
+ // skip for those the pending is not implemented
+ if !pending_info.is_empty() {
+ info!("Pending messages {:?}", pending_info);
+ pending_info.clear();
+ }
}
}
From 3287887761fa5a8da12ca70c5ce53947cbe896ec Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Fri, 6 Sep 2024 18:44:47 -0700
Subject: [PATCH 050/188] feat: rolling update for MonoVertex (#2029)
---
api/json-schema/schema.json | 44 +-
api/openapi-spec/swagger.json | 44 +-
config/advanced-install/minimal-crds.yaml | 2 +-
.../numaflow.numaproj.io_monovertices.yaml | 24 +-
.../numaflow.numaproj.io_monovertices.yaml | 2 +-
config/install.yaml | 24 +-
config/namespace-install.yaml | 24 +-
docs/APIs.md | 268 ++-
.../numaflow-transformer-config.yaml | 1 +
pkg/apis/numaflow/v1alpha1/generated.pb.go | 1558 +++++++++++------
pkg/apis/numaflow/v1alpha1/generated.proto | 68 +-
.../numaflow/v1alpha1/mono_vertex_types.go | 53 +-
.../numaflow/v1alpha1/openapi_generated.go | 81 +-
pkg/apis/numaflow/v1alpha1/update_strategy.go | 80 +
.../numaflow/v1alpha1/update_strategy_test.go | 128 ++
.../v1alpha1/zz_generated.deepcopy.go | 44 +
.../server/service/health_status.go | 8 +-
pkg/reconciler/monovertex/controller.go | 265 ++-
pkg/reconciler/monovertex/controller_test.go | 299 ++++
pkg/reconciler/monovertex/scaling/scaling.go | 8 +-
pkg/reconciler/pipeline/controller.go | 2 +-
pkg/reconciler/util.go | 21 +-
pkg/reconciler/util_test.go | 21 +
rust/numaflow-models/Makefile | 2 +
rust/numaflow-models/src/models/mod.rs | 4 +
.../src/models/mono_vertex_spec.rs | 3 +
.../src/models/mono_vertex_status.rs | 19 +-
.../src/models/rolling_update_strategy.rs | 34 +
.../src/models/update_strategy.rs | 51 +
test/fixtures/util.go | 2 +-
30 files changed, 2477 insertions(+), 707 deletions(-)
create mode 100644 pkg/apis/numaflow/v1alpha1/update_strategy.go
create mode 100644 pkg/apis/numaflow/v1alpha1/update_strategy_test.go
create mode 100644 rust/numaflow-models/src/models/rolling_update_strategy.rs
create mode 100644 rust/numaflow-models/src/models/update_strategy.rs
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index d58a13beb6..27796003ba 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19270,6 +19270,10 @@
},
"type": "array"
},
+ "updateStrategy": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UpdateStrategy",
+ "description": "The strategy to use to replace existing pods with new ones."
+ },
"volumes": {
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.Volume"
@@ -19293,11 +19297,11 @@
"x-kubernetes-patch-strategy": "merge"
},
"currentHash": {
- "description": "If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).",
+ "description": "If not empty, indicates the current version of the MonoVertex used to generate Pods.",
"type": "string"
},
- "currentReplicas": {
- "description": "The number of Pods created by the controller from the MonoVertex version indicated by currentHash.",
+ "desiredReplicas": {
+ "description": "The number of desired replicas.",
"format": "int64",
"type": "integer"
},
@@ -19336,9 +19340,14 @@
"type": "string"
},
"updateHash": {
- "description": "If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "description": "If not empty, indicates the updated version of the MonoVertex used to generate Pods.",
"type": "string"
},
+ "updatedReadyReplicas": {
+ "description": "The number of ready Pods created by the controller from the MonoVertex version indicated by updateHash.",
+ "format": "int64",
+ "type": "integer"
+ },
"updatedReplicas": {
"description": "The number of Pods created by the controller from the MonoVertex version indicated by updateHash.",
"format": "int64",
@@ -19803,6 +19812,16 @@
},
"type": "object"
},
+ "io.numaproj.numaflow.v1alpha1.RollingUpdateStrategy": {
+ "description": "RollingUpdateStrategy is used to communicate parameter for RollingUpdateStrategyType.",
+ "properties": {
+ "maxUnavailable": {
+ "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString",
+ "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. Defaults to 25%. Example: when this is set to 30%, the old pods can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old pods can be scaled down further, followed by scaling up the new pods, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods."
+ }
+ },
+ "type": "object"
+ },
"io.numaproj.numaflow.v1alpha1.SASL": {
"properties": {
"gssapi": {
@@ -20318,6 +20337,23 @@
},
"type": "object"
},
+ "io.numaproj.numaflow.v1alpha1.UpdateStrategy": {
+ "description": "UpdateStrategy indicates the strategy that the controller will use to perform updates for Vertex or MonoVertex.",
+ "properties": {
+ "rollingUpdate": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.RollingUpdateStrategy",
+ "description": "RollingUpdate is used to communicate parameters when Type is RollingUpdateStrategy."
+ },
+ "type": {
+ "description": "Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate.\n\nPossible enum values:\n - `\"RollingUpdate\"`",
+ "enum": [
+ "RollingUpdate"
+ ],
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
"io.numaproj.numaflow.v1alpha1.Vertex": {
"properties": {
"apiVersion": {
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index b3cdb6b120..bb918bac66 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19266,6 +19266,10 @@
"$ref": "#/definitions/io.k8s.api.core.v1.Toleration"
}
},
+ "updateStrategy": {
+ "description": "The strategy to use to replace existing pods with new ones.",
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UpdateStrategy"
+ },
"volumes": {
"type": "array",
"items": {
@@ -19289,11 +19293,11 @@
"x-kubernetes-patch-strategy": "merge"
},
"currentHash": {
- "description": "If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).",
+ "description": "If not empty, indicates the current version of the MonoVertex used to generate Pods.",
"type": "string"
},
- "currentReplicas": {
- "description": "The number of Pods created by the controller from the MonoVertex version indicated by currentHash.",
+ "desiredReplicas": {
+ "description": "The number of desired replicas.",
"type": "integer",
"format": "int64"
},
@@ -19332,9 +19336,14 @@
"type": "string"
},
"updateHash": {
- "description": "If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "description": "If not empty, indicates the updated version of the MonoVertex used to generate Pods.",
"type": "string"
},
+ "updatedReadyReplicas": {
+ "description": "The number of ready Pods created by the controller from the MonoVertex version indicated by updateHash.",
+ "type": "integer",
+ "format": "int64"
+ },
"updatedReplicas": {
"description": "The number of Pods created by the controller from the MonoVertex version indicated by updateHash.",
"type": "integer",
@@ -19789,6 +19798,16 @@
}
}
},
+ "io.numaproj.numaflow.v1alpha1.RollingUpdateStrategy": {
+ "description": "RollingUpdateStrategy is used to communicate parameter for RollingUpdateStrategyType.",
+ "type": "object",
+ "properties": {
+ "maxUnavailable": {
+ "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. Defaults to 25%. Example: when this is set to 30%, the old pods can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old pods can be scaled down further, followed by scaling up the new pods, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods.",
+ "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString"
+ }
+ }
+ },
"io.numaproj.numaflow.v1alpha1.SASL": {
"type": "object",
"required": [
@@ -20304,6 +20323,23 @@
}
}
},
+ "io.numaproj.numaflow.v1alpha1.UpdateStrategy": {
+ "description": "UpdateStrategy indicates the strategy that the controller will use to perform updates for Vertex or MonoVertex.",
+ "type": "object",
+ "properties": {
+ "rollingUpdate": {
+ "description": "RollingUpdate is used to communicate parameters when Type is RollingUpdateStrategy.",
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.RollingUpdateStrategy"
+ },
+ "type": {
+ "description": "Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate.\n\nPossible enum values:\n - `\"RollingUpdate\"`",
+ "type": "string",
+ "enum": [
+ "RollingUpdate"
+ ]
+ }
+ }
+ },
"io.numaproj.numaflow.v1alpha1.Vertex": {
"type": "object",
"required": [
diff --git a/config/advanced-install/minimal-crds.yaml b/config/advanced-install/minimal-crds.yaml
index 3e647ee3d6..a8eac9fc22 100644
--- a/config/advanced-install/minimal-crds.yaml
+++ b/config/advanced-install/minimal-crds.yaml
@@ -69,7 +69,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index 8e503f47d6..02ae281ebd 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -21,7 +21,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
@@ -4790,6 +4790,23 @@ spec:
type: string
type: object
type: array
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -5551,7 +5568,7 @@ spec:
type: array
currentHash:
type: string
- currentReplicas:
+ desiredReplicas:
format: int32
type: integer
lastScaledAt:
@@ -5586,6 +5603,9 @@ spec:
type: string
updateHash:
type: string
+ updatedReadyReplicas:
+ format: int32
+ type: integer
updatedReplicas:
format: int32
type: integer
diff --git a/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
index 65cb6b2652..0a3af26fbb 100644
--- a/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/minimal/numaflow.numaproj.io_monovertices.yaml
@@ -17,7 +17,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
diff --git a/config/install.yaml b/config/install.yaml
index 8a84ffac83..ac272ddf19 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -2665,7 +2665,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
@@ -7434,6 +7434,23 @@ spec:
type: string
type: object
type: array
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -8195,7 +8212,7 @@ spec:
type: array
currentHash:
type: string
- currentReplicas:
+ desiredReplicas:
format: int32
type: integer
lastScaledAt:
@@ -8230,6 +8247,9 @@ spec:
type: string
updateHash:
type: string
+ updatedReadyReplicas:
+ format: int32
+ type: integer
updatedReplicas:
format: int32
type: integer
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index c9301892de..12579dc36f 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -2665,7 +2665,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
@@ -7434,6 +7434,23 @@ spec:
type: string
type: object
type: array
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -8195,7 +8212,7 @@ spec:
type: array
currentHash:
type: string
- currentReplicas:
+ desiredReplicas:
format: int32
type: integer
lastScaledAt:
@@ -8230,6 +8247,9 @@ spec:
type: string
updateHash:
type: string
+ updatedReadyReplicas:
+ format: int32
+ type: integer
updatedReplicas:
format: int32
type: integer
diff --git a/docs/APIs.md b/docs/APIs.md
index 97ddf96672..5fd26ad505 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -5791,6 +5791,27 @@ Template for the daemon service deployment.
+
+
+
+
+updateStrategy
+ UpdateStrategy
+
+
+
+
+
+(Optional)
+
+
+The strategy to use to replace existing pods with new ones.
+
+
+
+
+
+
@@ -6159,6 +6180,27 @@ Template for the daemon service deployment.
+
+
+
+
+updateStrategy
+ UpdateStrategy
+
+
+
+
+
+(Optional)
+
+
+The strategy to use to replace existing pods with new ones.
+
+
+
+
+
+
@@ -6259,6 +6301,25 @@ labels match the selector).
+desiredReplicas
uint32
+
+
+
+
+(Optional)
+
+
+The number of desired replicas.
+
+
+
+
+
+
+
+
+
+
selector
string
@@ -6376,7 +6437,7 @@ The number of pods targeted by this MonoVertex with a Ready Condition.
-currentReplicas
uint32
+updatedReplicas
uint32
@@ -6384,7 +6445,7 @@ The number of pods targeted by this MonoVertex with a Ready Condition.
The number of Pods created by the controller from the MonoVertex version
-indicated by currentHash.
+indicated by updateHash.
@@ -6395,15 +6456,15 @@ indicated by currentHash.
-updatedReplicas
uint32
+updatedReadyReplicas
uint32
-The number of Pods created by the controller from the MonoVertex version
-indicated by updateHash.
+The number of ready Pods created by the controller from the MonoVertex
+version indicated by updateHash.
@@ -6421,8 +6482,8 @@ indicated by updateHash.
-If not empty, indicates the version of the MonoVertex used to generate
-Pods in the sequence \[0,currentReplicas).
+If not empty, indicates the current version of the MonoVertex used to
+generate Pods.
@@ -6440,8 +6501,8 @@ Pods in the sequence \[0,currentReplicas).
-If not empty, indicates the version of the MonoVertx used to generate
-Pods in the sequence \[replicas-updatedReplicas,replicas)
+If not empty, indicates the updated version of the MonoVertex used to
+generate Pods.
@@ -8418,6 +8479,81 @@ action is to retry.
+
+
+RollingUpdateStrategy
+
+
+
+
+(Appears on:
+UpdateStrategy )
+
+
+
+
+
+
+RollingUpdateStrategy is used to communicate parameter for
+RollingUpdateStrategyType.
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+
+
+
+Description
+
+
+
+
+
+
+
+
+
+
+
+
+maxUnavailable
+k8s.io/apimachinery/pkg/util/intstr.IntOrString
+
+
+
+
+(Optional)
+
+
+The maximum number of pods that can be unavailable during the update.
+Value can be an absolute number (ex: 5) or a percentage of desired pods
+(ex: 10%). Absolute number is calculated from percentage by rounding
+down. Defaults to 25%. Example: when this is set to 30%, the old pods
+can be scaled down to 70% of desired pods immediately when the rolling
+update starts. Once new pods are ready, old pods can be scaled down
+further, followed by scaling up the new pods, ensuring that the total
+number of pods available at all times during the update is at least 70%
+of desired pods.
+
+
+
+
+
+
+
+
+
+
SASL
@@ -10659,6 +10795,120 @@ Description
+
+
+UpdateStrategy
+
+
+
+
+(Appears on:
+MonoVertexSpec )
+
+
+
+
+
+
+UpdateStrategy indicates the strategy that the controller will use to
+perform updates for Vertex or MonoVertex.
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+
+
+
+Description
+
+
+
+
+
+
+
+
+
+
+
+
+type
+
+UpdateStrategyType
+
+
+
+
+(Optional)
+
+
+Type indicates the type of the StatefulSetUpdateStrategy. Default is
+RollingUpdate.
+
+
+
+
+
+
+
+
+
+
+rollingUpdate
+
+RollingUpdateStrategy
+
+
+
+
+(Optional)
+
+
+RollingUpdate is used to communicate parameters when Type is
+RollingUpdateStrategy.
+
+
+
+
+
+
+
+
+
+
+
+
+UpdateStrategyType (string
alias)
+
+
+
+
+
+
+(Appears on:
+UpdateStrategy )
+
+
+
+
+
+
+UpdateStrategyType is a string enumeration type that enumerates all
+possible update strategies.
+
+
+
+
Vertex
diff --git a/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml b/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml
index 39439f10da..37b74aba6e 100644
--- a/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml
+++ b/docs/user-guide/reference/kustomize/numaflow-transformer-config.yaml
@@ -435,6 +435,7 @@ varReference:
- path: spec/source/udsource/container/command
kind: MonoVertex
- path: spec/source/udsource/container/env/value
+ kind: MonoVertex
- path: spec/sink/udsink/container/args
kind: MonoVertex
- path: spec/sink/udsink/container/command
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index abf3a4e47a..5297014695 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -35,6 +35,8 @@ import (
math_bits "math/bits"
reflect "reflect"
strings "strings"
+
+ intstr "k8s.io/apimachinery/pkg/util/intstr"
)
// Reference imports to suppress errors if they are not otherwise used.
@@ -1812,10 +1814,38 @@ func (m *RetryStrategy) XXX_DiscardUnknown() {
var xxx_messageInfo_RetryStrategy proto.InternalMessageInfo
+func (m *RollingUpdateStrategy) Reset() { *m = RollingUpdateStrategy{} }
+func (*RollingUpdateStrategy) ProtoMessage() {}
+func (*RollingUpdateStrategy) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0d1b17d3865563, []int{63}
+}
+func (m *RollingUpdateStrategy) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *RollingUpdateStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+}
+func (m *RollingUpdateStrategy) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_RollingUpdateStrategy.Merge(m, src)
+}
+func (m *RollingUpdateStrategy) XXX_Size() int {
+ return m.Size()
+}
+func (m *RollingUpdateStrategy) XXX_DiscardUnknown() {
+ xxx_messageInfo_RollingUpdateStrategy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RollingUpdateStrategy proto.InternalMessageInfo
+
func (m *SASL) Reset() { *m = SASL{} }
func (*SASL) ProtoMessage() {}
func (*SASL) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{63}
+ return fileDescriptor_9d0d1b17d3865563, []int{64}
}
func (m *SASL) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1843,7 +1873,7 @@ var xxx_messageInfo_SASL proto.InternalMessageInfo
func (m *SASLPlain) Reset() { *m = SASLPlain{} }
func (*SASLPlain) ProtoMessage() {}
func (*SASLPlain) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{64}
+ return fileDescriptor_9d0d1b17d3865563, []int{65}
}
func (m *SASLPlain) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1871,7 +1901,7 @@ var xxx_messageInfo_SASLPlain proto.InternalMessageInfo
func (m *Scale) Reset() { *m = Scale{} }
func (*Scale) ProtoMessage() {}
func (*Scale) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{65}
+ return fileDescriptor_9d0d1b17d3865563, []int{66}
}
func (m *Scale) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1899,7 +1929,7 @@ var xxx_messageInfo_Scale proto.InternalMessageInfo
func (m *ServingSource) Reset() { *m = ServingSource{} }
func (*ServingSource) ProtoMessage() {}
func (*ServingSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{66}
+ return fileDescriptor_9d0d1b17d3865563, []int{67}
}
func (m *ServingSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1927,7 +1957,7 @@ var xxx_messageInfo_ServingSource proto.InternalMessageInfo
func (m *ServingStore) Reset() { *m = ServingStore{} }
func (*ServingStore) ProtoMessage() {}
func (*ServingStore) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{67}
+ return fileDescriptor_9d0d1b17d3865563, []int{68}
}
func (m *ServingStore) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1955,7 +1985,7 @@ var xxx_messageInfo_ServingStore proto.InternalMessageInfo
func (m *SessionWindow) Reset() { *m = SessionWindow{} }
func (*SessionWindow) ProtoMessage() {}
func (*SessionWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{68}
+ return fileDescriptor_9d0d1b17d3865563, []int{69}
}
func (m *SessionWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1983,7 +2013,7 @@ var xxx_messageInfo_SessionWindow proto.InternalMessageInfo
func (m *SideInput) Reset() { *m = SideInput{} }
func (*SideInput) ProtoMessage() {}
func (*SideInput) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{69}
+ return fileDescriptor_9d0d1b17d3865563, []int{70}
}
func (m *SideInput) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2011,7 +2041,7 @@ var xxx_messageInfo_SideInput proto.InternalMessageInfo
func (m *SideInputTrigger) Reset() { *m = SideInputTrigger{} }
func (*SideInputTrigger) ProtoMessage() {}
func (*SideInputTrigger) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{70}
+ return fileDescriptor_9d0d1b17d3865563, []int{71}
}
func (m *SideInputTrigger) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2039,7 +2069,7 @@ var xxx_messageInfo_SideInputTrigger proto.InternalMessageInfo
func (m *SideInputsManagerTemplate) Reset() { *m = SideInputsManagerTemplate{} }
func (*SideInputsManagerTemplate) ProtoMessage() {}
func (*SideInputsManagerTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{71}
+ return fileDescriptor_9d0d1b17d3865563, []int{72}
}
func (m *SideInputsManagerTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2067,7 +2097,7 @@ var xxx_messageInfo_SideInputsManagerTemplate proto.InternalMessageInfo
func (m *Sink) Reset() { *m = Sink{} }
func (*Sink) ProtoMessage() {}
func (*Sink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{72}
+ return fileDescriptor_9d0d1b17d3865563, []int{73}
}
func (m *Sink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2095,7 +2125,7 @@ var xxx_messageInfo_Sink proto.InternalMessageInfo
func (m *SlidingWindow) Reset() { *m = SlidingWindow{} }
func (*SlidingWindow) ProtoMessage() {}
func (*SlidingWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{73}
+ return fileDescriptor_9d0d1b17d3865563, []int{74}
}
func (m *SlidingWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2123,7 +2153,7 @@ var xxx_messageInfo_SlidingWindow proto.InternalMessageInfo
func (m *Source) Reset() { *m = Source{} }
func (*Source) ProtoMessage() {}
func (*Source) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{74}
+ return fileDescriptor_9d0d1b17d3865563, []int{75}
}
func (m *Source) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2151,7 +2181,7 @@ var xxx_messageInfo_Source proto.InternalMessageInfo
func (m *Status) Reset() { *m = Status{} }
func (*Status) ProtoMessage() {}
func (*Status) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{75}
+ return fileDescriptor_9d0d1b17d3865563, []int{76}
}
func (m *Status) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2179,7 +2209,7 @@ var xxx_messageInfo_Status proto.InternalMessageInfo
func (m *TLS) Reset() { *m = TLS{} }
func (*TLS) ProtoMessage() {}
func (*TLS) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{76}
+ return fileDescriptor_9d0d1b17d3865563, []int{77}
}
func (m *TLS) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2207,7 +2237,7 @@ var xxx_messageInfo_TLS proto.InternalMessageInfo
func (m *TagConditions) Reset() { *m = TagConditions{} }
func (*TagConditions) ProtoMessage() {}
func (*TagConditions) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{77}
+ return fileDescriptor_9d0d1b17d3865563, []int{78}
}
func (m *TagConditions) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2235,7 +2265,7 @@ var xxx_messageInfo_TagConditions proto.InternalMessageInfo
func (m *Templates) Reset() { *m = Templates{} }
func (*Templates) ProtoMessage() {}
func (*Templates) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{78}
+ return fileDescriptor_9d0d1b17d3865563, []int{79}
}
func (m *Templates) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2263,7 +2293,7 @@ var xxx_messageInfo_Templates proto.InternalMessageInfo
func (m *Transformer) Reset() { *m = Transformer{} }
func (*Transformer) ProtoMessage() {}
func (*Transformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{79}
+ return fileDescriptor_9d0d1b17d3865563, []int{80}
}
func (m *Transformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2291,7 +2321,7 @@ var xxx_messageInfo_Transformer proto.InternalMessageInfo
func (m *UDF) Reset() { *m = UDF{} }
func (*UDF) ProtoMessage() {}
func (*UDF) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{80}
+ return fileDescriptor_9d0d1b17d3865563, []int{81}
}
func (m *UDF) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2319,7 +2349,7 @@ var xxx_messageInfo_UDF proto.InternalMessageInfo
func (m *UDSink) Reset() { *m = UDSink{} }
func (*UDSink) ProtoMessage() {}
func (*UDSink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{81}
+ return fileDescriptor_9d0d1b17d3865563, []int{82}
}
func (m *UDSink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2347,7 +2377,7 @@ var xxx_messageInfo_UDSink proto.InternalMessageInfo
func (m *UDSource) Reset() { *m = UDSource{} }
func (*UDSource) ProtoMessage() {}
func (*UDSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{82}
+ return fileDescriptor_9d0d1b17d3865563, []int{83}
}
func (m *UDSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2375,7 +2405,7 @@ var xxx_messageInfo_UDSource proto.InternalMessageInfo
func (m *UDTransformer) Reset() { *m = UDTransformer{} }
func (*UDTransformer) ProtoMessage() {}
func (*UDTransformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{83}
+ return fileDescriptor_9d0d1b17d3865563, []int{84}
}
func (m *UDTransformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2400,10 +2430,38 @@ func (m *UDTransformer) XXX_DiscardUnknown() {
var xxx_messageInfo_UDTransformer proto.InternalMessageInfo
+func (m *UpdateStrategy) Reset() { *m = UpdateStrategy{} }
+func (*UpdateStrategy) ProtoMessage() {}
+func (*UpdateStrategy) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0d1b17d3865563, []int{85}
+}
+func (m *UpdateStrategy) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *UpdateStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+}
+func (m *UpdateStrategy) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_UpdateStrategy.Merge(m, src)
+}
+func (m *UpdateStrategy) XXX_Size() int {
+ return m.Size()
+}
+func (m *UpdateStrategy) XXX_DiscardUnknown() {
+ xxx_messageInfo_UpdateStrategy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UpdateStrategy proto.InternalMessageInfo
+
func (m *Vertex) Reset() { *m = Vertex{} }
func (*Vertex) ProtoMessage() {}
func (*Vertex) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{84}
+ return fileDescriptor_9d0d1b17d3865563, []int{86}
}
func (m *Vertex) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2431,7 +2489,7 @@ var xxx_messageInfo_Vertex proto.InternalMessageInfo
func (m *VertexInstance) Reset() { *m = VertexInstance{} }
func (*VertexInstance) ProtoMessage() {}
func (*VertexInstance) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{85}
+ return fileDescriptor_9d0d1b17d3865563, []int{87}
}
func (m *VertexInstance) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2459,7 +2517,7 @@ var xxx_messageInfo_VertexInstance proto.InternalMessageInfo
func (m *VertexLimits) Reset() { *m = VertexLimits{} }
func (*VertexLimits) ProtoMessage() {}
func (*VertexLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{86}
+ return fileDescriptor_9d0d1b17d3865563, []int{88}
}
func (m *VertexLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2487,7 +2545,7 @@ var xxx_messageInfo_VertexLimits proto.InternalMessageInfo
func (m *VertexList) Reset() { *m = VertexList{} }
func (*VertexList) ProtoMessage() {}
func (*VertexList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{87}
+ return fileDescriptor_9d0d1b17d3865563, []int{89}
}
func (m *VertexList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2515,7 +2573,7 @@ var xxx_messageInfo_VertexList proto.InternalMessageInfo
func (m *VertexSpec) Reset() { *m = VertexSpec{} }
func (*VertexSpec) ProtoMessage() {}
func (*VertexSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{88}
+ return fileDescriptor_9d0d1b17d3865563, []int{90}
}
func (m *VertexSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2543,7 +2601,7 @@ var xxx_messageInfo_VertexSpec proto.InternalMessageInfo
func (m *VertexStatus) Reset() { *m = VertexStatus{} }
func (*VertexStatus) ProtoMessage() {}
func (*VertexStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{89}
+ return fileDescriptor_9d0d1b17d3865563, []int{91}
}
func (m *VertexStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2571,7 +2629,7 @@ var xxx_messageInfo_VertexStatus proto.InternalMessageInfo
func (m *VertexTemplate) Reset() { *m = VertexTemplate{} }
func (*VertexTemplate) ProtoMessage() {}
func (*VertexTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{90}
+ return fileDescriptor_9d0d1b17d3865563, []int{92}
}
func (m *VertexTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2599,7 +2657,7 @@ var xxx_messageInfo_VertexTemplate proto.InternalMessageInfo
func (m *Watermark) Reset() { *m = Watermark{} }
func (*Watermark) ProtoMessage() {}
func (*Watermark) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{91}
+ return fileDescriptor_9d0d1b17d3865563, []int{93}
}
func (m *Watermark) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2627,7 +2685,7 @@ var xxx_messageInfo_Watermark proto.InternalMessageInfo
func (m *Window) Reset() { *m = Window{} }
func (*Window) ProtoMessage() {}
func (*Window) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{92}
+ return fileDescriptor_9d0d1b17d3865563, []int{94}
}
func (m *Window) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2724,6 +2782,7 @@ func init() {
proto.RegisterType((*RedisConfig)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisConfig")
proto.RegisterType((*RedisSettings)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisSettings")
proto.RegisterType((*RetryStrategy)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RetryStrategy")
+ proto.RegisterType((*RollingUpdateStrategy)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RollingUpdateStrategy")
proto.RegisterType((*SASL)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.SASL")
proto.RegisterType((*SASLPlain)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.SASLPlain")
proto.RegisterType((*Scale)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Scale")
@@ -2746,6 +2805,7 @@ func init() {
proto.RegisterType((*UDSink)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.UDSink")
proto.RegisterType((*UDSource)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.UDSource")
proto.RegisterType((*UDTransformer)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.UDTransformer")
+ proto.RegisterType((*UpdateStrategy)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.UpdateStrategy")
proto.RegisterType((*Vertex)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Vertex")
proto.RegisterType((*VertexInstance)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.VertexInstance")
proto.RegisterType((*VertexLimits)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.VertexLimits")
@@ -2762,490 +2822,502 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7719 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0xd7,
- 0x95, 0x9e, 0xfa, 0xbf, 0xfb, 0x34, 0xc9, 0xe1, 0xdc, 0x19, 0x8d, 0x38, 0xa3, 0xd1, 0x70, 0xb6,
- 0xb4, 0xd2, 0xce, 0x66, 0xbd, 0x64, 0xc4, 0xe8, 0xcf, 0xfb, 0x63, 0x89, 0x4d, 0x0e, 0x39, 0x9c,
- 0x21, 0x67, 0xe8, 0xd3, 0xe4, 0x48, 0x5e, 0x65, 0xad, 0x14, 0xab, 0x2e, 0x9b, 0x25, 0x56, 0x57,
- 0xb5, 0xaa, 0xaa, 0x39, 0x43, 0x6d, 0x02, 0xef, 0xae, 0x02, 0x48, 0x41, 0x10, 0x24, 0xd8, 0x27,
- 0x03, 0x81, 0x13, 0x24, 0x48, 0xe0, 0x07, 0xc3, 0x79, 0x08, 0xe0, 0x3c, 0x18, 0x48, 0x1c, 0x07,
- 0x41, 0xe2, 0x04, 0xf9, 0xf1, 0x43, 0x80, 0x28, 0x2f, 0x44, 0xcc, 0x20, 0x0f, 0x09, 0x10, 0xc3,
- 0x88, 0x91, 0xc4, 0x1e, 0x18, 0x71, 0x70, 0xff, 0xea, 0xaf, 0xab, 0x67, 0xc8, 0x2e, 0x72, 0x34,
- 0xca, 0xea, 0xad, 0xea, 0x9e, 0x73, 0xbf, 0x73, 0xeb, 0xd6, 0xfd, 0x39, 0xf7, 0x9c, 0x73, 0xef,
- 0x85, 0xe5, 0x8e, 0x15, 0xec, 0xf4, 0xb7, 0x66, 0x0c, 0xb7, 0x3b, 0xeb, 0xf4, 0xbb, 0x7a, 0xcf,
- 0x73, 0xdf, 0xe3, 0x0f, 0xdb, 0xb6, 0x7b, 0x6f, 0xb6, 0xb7, 0xdb, 0x99, 0xd5, 0x7b, 0x96, 0x1f,
- 0xa5, 0xec, 0xbd, 0xa4, 0xdb, 0xbd, 0x1d, 0xfd, 0xa5, 0xd9, 0x0e, 0x75, 0xa8, 0xa7, 0x07, 0xd4,
- 0x9c, 0xe9, 0x79, 0x6e, 0xe0, 0x92, 0xd7, 0x22, 0xa0, 0x19, 0x05, 0x34, 0xa3, 0xb2, 0xcd, 0xf4,
- 0x76, 0x3b, 0x33, 0x0c, 0x28, 0x4a, 0x51, 0x40, 0x97, 0x7e, 0x33, 0x56, 0x82, 0x8e, 0xdb, 0x71,
- 0x67, 0x39, 0xde, 0x56, 0x7f, 0x9b, 0xbf, 0xf1, 0x17, 0xfe, 0x24, 0xe4, 0x5c, 0xd2, 0x76, 0x5f,
- 0xf7, 0x67, 0x2c, 0x97, 0x15, 0x6b, 0xd6, 0x70, 0x3d, 0x3a, 0xbb, 0x37, 0x50, 0x96, 0x4b, 0x2f,
- 0x47, 0x3c, 0x5d, 0xdd, 0xd8, 0xb1, 0x1c, 0xea, 0xed, 0xab, 0x6f, 0x99, 0xf5, 0xa8, 0xef, 0xf6,
- 0x3d, 0x83, 0x1e, 0x2b, 0x97, 0x3f, 0xdb, 0xa5, 0x81, 0x9e, 0x25, 0x6b, 0x76, 0x58, 0x2e, 0xaf,
- 0xef, 0x04, 0x56, 0x77, 0x50, 0xcc, 0xab, 0x8f, 0xca, 0xe0, 0x1b, 0x3b, 0xb4, 0xab, 0xa7, 0xf3,
- 0x69, 0xdf, 0x07, 0x38, 0x37, 0xbf, 0xe5, 0x07, 0x9e, 0x6e, 0x04, 0xeb, 0xae, 0xb9, 0x41, 0xbb,
- 0x3d, 0x5b, 0x0f, 0x28, 0xd9, 0x85, 0x3a, 0x2b, 0x9b, 0xa9, 0x07, 0xfa, 0x54, 0xe1, 0x6a, 0xe1,
- 0x5a, 0x73, 0x6e, 0x7e, 0x66, 0xc4, 0x7f, 0x31, 0xb3, 0x26, 0x81, 0x5a, 0x63, 0x87, 0x07, 0xd3,
- 0x75, 0xf5, 0x86, 0xa1, 0x00, 0xf2, 0xf5, 0x02, 0x8c, 0x39, 0xae, 0x49, 0xdb, 0xd4, 0xa6, 0x46,
- 0xe0, 0x7a, 0x53, 0xc5, 0xab, 0xa5, 0x6b, 0xcd, 0xb9, 0xaf, 0x8e, 0x2c, 0x31, 0xe3, 0x8b, 0x66,
- 0x6e, 0xc7, 0x04, 0x5c, 0x77, 0x02, 0x6f, 0xbf, 0x75, 0xfe, 0x07, 0x07, 0xd3, 0x4f, 0x1d, 0x1e,
- 0x4c, 0x8f, 0xc5, 0x49, 0x98, 0x28, 0x09, 0xd9, 0x84, 0x66, 0xe0, 0xda, 0xac, 0xca, 0x2c, 0xd7,
- 0xf1, 0xa7, 0x4a, 0xbc, 0x60, 0x57, 0x66, 0x44, 0x6d, 0x33, 0xf1, 0x33, 0xac, 0xb9, 0xcc, 0xec,
- 0xbd, 0x34, 0xb3, 0x11, 0xb2, 0xb5, 0xce, 0x49, 0xe0, 0x66, 0x94, 0xe6, 0x63, 0x1c, 0x87, 0x50,
- 0x38, 0xe3, 0x53, 0xa3, 0xef, 0x59, 0xc1, 0xfe, 0x82, 0xeb, 0x04, 0xf4, 0x7e, 0x30, 0x55, 0xe6,
- 0xb5, 0xfc, 0x62, 0x16, 0xf4, 0xba, 0x6b, 0xb6, 0x93, 0xdc, 0xad, 0x73, 0x87, 0x07, 0xd3, 0x67,
- 0x52, 0x89, 0x98, 0xc6, 0x24, 0x0e, 0x4c, 0x5a, 0x5d, 0xbd, 0x43, 0xd7, 0xfb, 0xb6, 0xdd, 0xa6,
- 0x86, 0x47, 0x03, 0x7f, 0xaa, 0xc2, 0x3f, 0xe1, 0x5a, 0x96, 0x9c, 0x55, 0xd7, 0xd0, 0xed, 0x3b,
- 0x5b, 0xef, 0x51, 0x23, 0x40, 0xba, 0x4d, 0x3d, 0xea, 0x18, 0xb4, 0x35, 0x25, 0x3f, 0x66, 0x72,
- 0x25, 0x85, 0x84, 0x03, 0xd8, 0x64, 0x19, 0xce, 0xf6, 0x3c, 0xcb, 0xe5, 0x45, 0xb0, 0x75, 0xdf,
- 0xbf, 0xad, 0x77, 0xe9, 0x54, 0xf5, 0x6a, 0xe1, 0x5a, 0xa3, 0x75, 0x51, 0xc2, 0x9c, 0x5d, 0x4f,
- 0x33, 0xe0, 0x60, 0x1e, 0x72, 0x0d, 0xea, 0x2a, 0x71, 0xaa, 0x76, 0xb5, 0x70, 0xad, 0x22, 0xda,
- 0x8e, 0xca, 0x8b, 0x21, 0x95, 0x2c, 0x41, 0x5d, 0xdf, 0xde, 0xb6, 0x1c, 0xc6, 0x59, 0xe7, 0x55,
- 0x78, 0x39, 0xeb, 0xd3, 0xe6, 0x25, 0x8f, 0xc0, 0x51, 0x6f, 0x18, 0xe6, 0x25, 0x37, 0x81, 0xf8,
- 0xd4, 0xdb, 0xb3, 0x0c, 0x3a, 0x6f, 0x18, 0x6e, 0xdf, 0x09, 0x78, 0xd9, 0x1b, 0xbc, 0xec, 0x97,
- 0x64, 0xd9, 0x49, 0x7b, 0x80, 0x03, 0x33, 0x72, 0x91, 0x37, 0x61, 0x52, 0x76, 0xbb, 0xa8, 0x16,
- 0x80, 0x23, 0x9d, 0x67, 0x15, 0x89, 0x29, 0x1a, 0x0e, 0x70, 0x13, 0x13, 0x2e, 0xeb, 0xfd, 0xc0,
- 0xed, 0x32, 0xc8, 0xa4, 0xd0, 0x0d, 0x77, 0x97, 0x3a, 0x53, 0xcd, 0xab, 0x85, 0x6b, 0xf5, 0xd6,
- 0xd5, 0xc3, 0x83, 0xe9, 0xcb, 0xf3, 0x0f, 0xe1, 0xc3, 0x87, 0xa2, 0x90, 0x3b, 0xd0, 0x30, 0x1d,
- 0x7f, 0xdd, 0xb5, 0x2d, 0x63, 0x7f, 0x6a, 0x8c, 0x17, 0xf0, 0x25, 0xf9, 0xa9, 0x8d, 0xc5, 0xdb,
- 0x6d, 0x41, 0x78, 0x70, 0x30, 0x7d, 0x79, 0x70, 0x74, 0x9c, 0x09, 0xe9, 0x18, 0x61, 0x90, 0x35,
- 0x0e, 0xb8, 0xe0, 0x3a, 0xdb, 0x56, 0x67, 0x6a, 0x9c, 0xff, 0x8d, 0xab, 0x43, 0x1a, 0xf4, 0xe2,
- 0xed, 0xb6, 0xe0, 0x6b, 0x8d, 0x4b, 0x71, 0xe2, 0x15, 0x23, 0x04, 0x62, 0xc2, 0x84, 0x1a, 0x57,
- 0x17, 0x6c, 0xdd, 0xea, 0xfa, 0x53, 0x13, 0xbc, 0xf1, 0xfe, 0xea, 0x10, 0x4c, 0x8c, 0x33, 0xb7,
- 0x2e, 0xc8, 0x4f, 0x99, 0x48, 0x24, 0xfb, 0x98, 0xc2, 0xbc, 0xf4, 0x06, 0x9c, 0x1d, 0x18, 0x1b,
- 0xc8, 0x24, 0x94, 0x76, 0xe9, 0x3e, 0x1f, 0xfa, 0x1a, 0xc8, 0x1e, 0xc9, 0x79, 0xa8, 0xec, 0xe9,
- 0x76, 0x9f, 0x4e, 0x15, 0x79, 0x9a, 0x78, 0xf9, 0xad, 0xe2, 0xeb, 0x05, 0xed, 0xef, 0x96, 0x60,
- 0x4c, 0x8d, 0x38, 0x6d, 0xcb, 0xd9, 0x25, 0x6f, 0x41, 0xc9, 0x76, 0x3b, 0x72, 0xdc, 0xfc, 0x9d,
- 0x91, 0x47, 0xb1, 0x55, 0xb7, 0xd3, 0xaa, 0x1d, 0x1e, 0x4c, 0x97, 0x56, 0xdd, 0x0e, 0x32, 0x44,
- 0x62, 0x40, 0x65, 0x57, 0xdf, 0xde, 0xd5, 0x79, 0x19, 0x9a, 0x73, 0xad, 0x91, 0xa1, 0x6f, 0x31,
- 0x14, 0x56, 0xd6, 0x56, 0xe3, 0xf0, 0x60, 0xba, 0xc2, 0x5f, 0x51, 0x60, 0x13, 0x17, 0x1a, 0x5b,
- 0xb6, 0x6e, 0xec, 0xee, 0xb8, 0x36, 0x9d, 0x2a, 0xe5, 0x14, 0xd4, 0x52, 0x48, 0xe2, 0x37, 0x87,
- 0xaf, 0x18, 0xc9, 0x20, 0x06, 0x54, 0xfb, 0xa6, 0x6f, 0x39, 0xbb, 0x72, 0x0c, 0x7c, 0x63, 0x64,
- 0x69, 0x9b, 0x8b, 0xfc, 0x9b, 0xe0, 0xf0, 0x60, 0xba, 0x2a, 0x9e, 0x51, 0x42, 0x6b, 0x3f, 0x6e,
- 0xc2, 0x84, 0xfa, 0x49, 0x77, 0xa9, 0x17, 0xd0, 0xfb, 0xe4, 0x2a, 0x94, 0x1d, 0xd6, 0x35, 0xf9,
- 0x4f, 0x6e, 0x8d, 0xc9, 0xe6, 0x52, 0xe6, 0x5d, 0x92, 0x53, 0x58, 0xc9, 0x44, 0x53, 0x91, 0x15,
- 0x3e, 0x7a, 0xc9, 0xda, 0x1c, 0x46, 0x94, 0x4c, 0x3c, 0xa3, 0x84, 0x26, 0xef, 0x40, 0x99, 0x7f,
- 0xbc, 0xa8, 0xea, 0xdf, 0x1d, 0x5d, 0x04, 0xfb, 0xf4, 0x3a, 0xfb, 0x02, 0xfe, 0xe1, 0x1c, 0x94,
- 0x35, 0xc5, 0xbe, 0xb9, 0x2d, 0x2b, 0xf6, 0x77, 0x72, 0x54, 0xec, 0x92, 0x68, 0x8a, 0x9b, 0x8b,
- 0x4b, 0xc8, 0x10, 0xc9, 0x5f, 0x2f, 0xc0, 0x59, 0xc3, 0x75, 0x02, 0x9d, 0xa9, 0x1a, 0x6a, 0x92,
- 0x9d, 0xaa, 0x70, 0x39, 0x37, 0x47, 0x96, 0xb3, 0x90, 0x46, 0x6c, 0x3d, 0xcd, 0xe6, 0x8c, 0x81,
- 0x64, 0x1c, 0x94, 0x4d, 0xfe, 0x66, 0x01, 0x9e, 0x66, 0x63, 0xf9, 0x00, 0x33, 0x9f, 0x81, 0x4e,
- 0xb6, 0x54, 0x17, 0x0f, 0x0f, 0xa6, 0x9f, 0x5e, 0xc9, 0x12, 0x86, 0xd9, 0x65, 0x60, 0xa5, 0x3b,
- 0xa7, 0x0f, 0xaa, 0x25, 0x7c, 0x76, 0x6b, 0xce, 0xad, 0x9e, 0xa4, 0xaa, 0xd3, 0x7a, 0x56, 0x36,
- 0xe5, 0x2c, 0xcd, 0x0e, 0xb3, 0x4a, 0x41, 0xae, 0x43, 0x6d, 0xcf, 0xb5, 0xfb, 0x5d, 0xea, 0x4f,
- 0xd5, 0xf9, 0x10, 0x7b, 0x29, 0x6b, 0x88, 0xbd, 0xcb, 0x59, 0x5a, 0x67, 0x24, 0x7c, 0x4d, 0xbc,
- 0xfb, 0xa8, 0xf2, 0x12, 0x0b, 0xaa, 0xb6, 0xd5, 0xb5, 0x02, 0x9f, 0x4f, 0x9c, 0xcd, 0xb9, 0xeb,
- 0x23, 0x7f, 0x96, 0xe8, 0xa2, 0xab, 0x1c, 0x4c, 0xf4, 0x1a, 0xf1, 0x8c, 0x52, 0x00, 0x1b, 0x0a,
- 0x7d, 0x43, 0xb7, 0xc5, 0xc4, 0xda, 0x9c, 0xfb, 0xd2, 0xe8, 0xdd, 0x86, 0xa1, 0xb4, 0xc6, 0xe5,
- 0x37, 0x55, 0xf8, 0x2b, 0x0a, 0x6c, 0xf2, 0xfb, 0x30, 0x91, 0xf8, 0x9b, 0xfe, 0x54, 0x93, 0xd7,
- 0xce, 0x73, 0x59, 0xb5, 0x13, 0x72, 0x45, 0x33, 0x4f, 0xa2, 0x85, 0xf8, 0x98, 0x02, 0x23, 0xb7,
- 0xa0, 0xee, 0x5b, 0x26, 0x35, 0x74, 0xcf, 0x9f, 0x1a, 0x3b, 0x0a, 0xf0, 0xa4, 0x04, 0xae, 0xb7,
- 0x65, 0x36, 0x0c, 0x01, 0xc8, 0x0c, 0x40, 0x4f, 0xf7, 0x02, 0x4b, 0x28, 0xaa, 0xe3, 0x5c, 0x69,
- 0x9a, 0x38, 0x3c, 0x98, 0x86, 0xf5, 0x30, 0x15, 0x63, 0x1c, 0x8c, 0x9f, 0xe5, 0x5d, 0x71, 0x7a,
- 0xfd, 0x40, 0x4c, 0xac, 0x0d, 0xc1, 0xdf, 0x0e, 0x53, 0x31, 0xc6, 0x41, 0xbe, 0x5d, 0x80, 0x67,
- 0xa3, 0xd7, 0xc1, 0x4e, 0x76, 0xe6, 0xc4, 0x3b, 0xd9, 0xf4, 0xe1, 0xc1, 0xf4, 0xb3, 0xed, 0xe1,
- 0x22, 0xf1, 0x61, 0xe5, 0xd1, 0xde, 0x82, 0xf1, 0xf9, 0x7e, 0xb0, 0xe3, 0x7a, 0xd6, 0x07, 0x5c,
- 0xe9, 0x26, 0x4b, 0x50, 0x09, 0xb8, 0xf2, 0x24, 0xe6, 0xe5, 0x17, 0xb2, 0xaa, 0x5a, 0x28, 0xb2,
- 0xb7, 0xe8, 0xbe, 0xd2, 0x06, 0xc4, 0xfc, 0x28, 0x94, 0x29, 0x91, 0x5d, 0xfb, 0xcb, 0x05, 0xa8,
- 0xb5, 0x74, 0x63, 0xd7, 0xdd, 0xde, 0x26, 0x6f, 0x43, 0xdd, 0x72, 0x02, 0xea, 0xed, 0xe9, 0xb6,
- 0x84, 0x9d, 0x89, 0xc1, 0x86, 0x2b, 0xb1, 0xe8, 0xbb, 0xd9, 0x9a, 0x87, 0x09, 0x5a, 0xec, 0xcb,
- 0xb5, 0x02, 0xd7, 0x47, 0x57, 0x24, 0x06, 0x86, 0x68, 0x64, 0x1a, 0x2a, 0x7e, 0x40, 0x7b, 0x3e,
- 0x9f, 0x79, 0xc6, 0x45, 0x31, 0xda, 0x2c, 0x01, 0x45, 0xba, 0xf6, 0x77, 0x0a, 0xd0, 0x68, 0xe9,
- 0xbe, 0x65, 0xb0, 0xaf, 0x24, 0x0b, 0x50, 0xee, 0xfb, 0xd4, 0x3b, 0xde, 0xb7, 0xf1, 0xc9, 0x62,
- 0xd3, 0xa7, 0x1e, 0xf2, 0xcc, 0xe4, 0x0e, 0xd4, 0x7b, 0xba, 0xef, 0xdf, 0x73, 0x3d, 0x53, 0x4e,
- 0x78, 0x47, 0x04, 0x12, 0xca, 0xb9, 0xcc, 0x8a, 0x21, 0x88, 0xd6, 0x84, 0x68, 0xc6, 0xd7, 0x7e,
- 0x5a, 0x80, 0x73, 0xad, 0xfe, 0xf6, 0x36, 0xf5, 0xa4, 0x2e, 0x2a, 0xb5, 0x3c, 0x0a, 0x15, 0x8f,
- 0x9a, 0x96, 0x2f, 0xcb, 0xbe, 0x38, 0x72, 0x0b, 0x42, 0x86, 0x22, 0x95, 0x4a, 0x5e, 0x5f, 0x3c,
- 0x01, 0x05, 0x3a, 0xe9, 0x43, 0xe3, 0x3d, 0x1a, 0xf8, 0x81, 0x47, 0xf5, 0xae, 0xfc, 0xba, 0x1b,
- 0x23, 0x8b, 0xba, 0x49, 0x83, 0x36, 0x47, 0x8a, 0xeb, 0xb0, 0x61, 0x22, 0x46, 0x92, 0xb4, 0xef,
- 0x57, 0x60, 0x6c, 0xc1, 0xed, 0x6e, 0x59, 0x0e, 0x35, 0xaf, 0x9b, 0x1d, 0x4a, 0xde, 0x85, 0x32,
- 0x35, 0x3b, 0x54, 0x7e, 0xed, 0xe8, 0xd3, 0x3d, 0x03, 0x8b, 0x94, 0x16, 0xf6, 0x86, 0x1c, 0x98,
- 0xac, 0xc2, 0xc4, 0xb6, 0xe7, 0x76, 0xc5, 0x08, 0xba, 0xb1, 0xdf, 0x93, 0x1a, 0x6b, 0xeb, 0x57,
- 0xd5, 0xa8, 0xb4, 0x94, 0xa0, 0x3e, 0x38, 0x98, 0x86, 0xe8, 0x0d, 0x53, 0x79, 0xc9, 0xdb, 0x30,
- 0x15, 0xa5, 0x84, 0x43, 0xc9, 0x02, 0x5b, 0x44, 0x70, 0x8d, 0xa5, 0xd2, 0xba, 0x7c, 0x78, 0x30,
- 0x3d, 0xb5, 0x34, 0x84, 0x07, 0x87, 0xe6, 0x26, 0x1f, 0x15, 0x60, 0x32, 0x22, 0x8a, 0xe1, 0x5d,
- 0x2a, 0x2a, 0x27, 0x34, 0x6f, 0xf0, 0xd5, 0xd6, 0x52, 0x4a, 0x04, 0x0e, 0x08, 0x25, 0x4b, 0x30,
- 0x16, 0xb8, 0xb1, 0xfa, 0xaa, 0xf0, 0xfa, 0xd2, 0x94, 0x79, 0x60, 0xc3, 0x1d, 0x5a, 0x5b, 0x89,
- 0x7c, 0x04, 0xe1, 0x82, 0x7a, 0x4f, 0xd5, 0x54, 0x95, 0xd7, 0xd4, 0xa5, 0xc3, 0x83, 0xe9, 0x0b,
- 0x1b, 0x99, 0x1c, 0x38, 0x24, 0x27, 0xf9, 0xa3, 0x02, 0x4c, 0x28, 0x92, 0xac, 0xa3, 0xda, 0x49,
- 0xd6, 0x11, 0x61, 0x2d, 0x62, 0x23, 0x21, 0x00, 0x53, 0x02, 0xb5, 0x9f, 0x95, 0xa1, 0x11, 0x0e,
- 0xb0, 0xe4, 0x79, 0xa8, 0xf0, 0x85, 0xbf, 0xd4, 0x9b, 0xc3, 0x99, 0x93, 0xdb, 0x07, 0x50, 0xd0,
- 0xc8, 0x0b, 0x50, 0x33, 0xdc, 0x6e, 0x57, 0x77, 0x4c, 0x6e, 0xcc, 0x69, 0xb4, 0x9a, 0x4c, 0x61,
- 0x58, 0x10, 0x49, 0xa8, 0x68, 0xe4, 0x32, 0x94, 0x75, 0xaf, 0x23, 0xec, 0x2a, 0x0d, 0x31, 0x1e,
- 0xcd, 0x7b, 0x1d, 0x1f, 0x79, 0x2a, 0xf9, 0x22, 0x94, 0xa8, 0xb3, 0x37, 0x55, 0x1e, 0xae, 0x91,
- 0x5c, 0x77, 0xf6, 0xee, 0xea, 0x5e, 0xab, 0x29, 0xcb, 0x50, 0xba, 0xee, 0xec, 0x21, 0xcb, 0x43,
- 0x56, 0xa1, 0x46, 0x9d, 0x3d, 0xf6, 0xef, 0xa5, 0xc1, 0xe3, 0x57, 0x86, 0x64, 0x67, 0x2c, 0x52,
- 0x39, 0x0f, 0xf5, 0x1a, 0x99, 0x8c, 0x0a, 0x82, 0x7c, 0x05, 0xc6, 0x84, 0x8a, 0xb3, 0xc6, 0xfe,
- 0x89, 0x3f, 0x55, 0xe5, 0x90, 0xd3, 0xc3, 0x75, 0x24, 0xce, 0x17, 0x19, 0x98, 0x62, 0x89, 0x3e,
- 0x26, 0xa0, 0xc8, 0x57, 0xa0, 0xa1, 0xd6, 0xa3, 0xea, 0xcf, 0x66, 0xda, 0x66, 0xd4, 0x22, 0x16,
- 0xe9, 0xfb, 0x7d, 0xcb, 0xa3, 0x5d, 0xea, 0x04, 0x7e, 0xeb, 0xac, 0x5a, 0xad, 0x2b, 0xaa, 0x8f,
- 0x11, 0x1a, 0xd9, 0x1a, 0x34, 0x32, 0x09, 0x0b, 0xc9, 0xf3, 0x43, 0x46, 0xf5, 0x11, 0x2c, 0x4c,
- 0x5f, 0x85, 0x33, 0xa1, 0x15, 0x48, 0x1a, 0x12, 0x84, 0xcd, 0xe4, 0x65, 0x96, 0x7d, 0x25, 0x49,
- 0x7a, 0x70, 0x30, 0xfd, 0x5c, 0x86, 0x29, 0x21, 0x62, 0xc0, 0x34, 0x98, 0xf6, 0xbd, 0x12, 0x0c,
- 0x6a, 0xff, 0xc9, 0x4a, 0x2b, 0x9c, 0x74, 0xa5, 0xa5, 0x3f, 0x48, 0x0c, 0x9f, 0xaf, 0xcb, 0x6c,
- 0xf9, 0x3f, 0x2a, 0xeb, 0xc7, 0x94, 0x4e, 0xfa, 0xc7, 0x3c, 0x29, 0x7d, 0x47, 0xfb, 0xb8, 0x0c,
- 0x13, 0x8b, 0x3a, 0xed, 0xba, 0xce, 0x23, 0xd7, 0x42, 0x85, 0x27, 0x62, 0x2d, 0x74, 0x0d, 0xea,
- 0x1e, 0xed, 0xd9, 0x96, 0xa1, 0x0b, 0xe5, 0x4b, 0xda, 0x1e, 0x51, 0xa6, 0x61, 0x48, 0x1d, 0xb2,
- 0x06, 0x2e, 0x3d, 0x91, 0x6b, 0xe0, 0xf2, 0xa7, 0xbf, 0x06, 0xd6, 0xfe, 0xa8, 0x08, 0x5c, 0x51,
- 0x21, 0x57, 0xa1, 0xcc, 0x26, 0xe1, 0xb4, 0xe5, 0x85, 0x37, 0x1c, 0x4e, 0x21, 0x97, 0xa0, 0x18,
- 0xb8, 0xb2, 0xe7, 0x81, 0xa4, 0x17, 0x37, 0x5c, 0x2c, 0x06, 0x2e, 0xf9, 0x00, 0xc0, 0x70, 0x1d,
- 0xd3, 0x52, 0x26, 0xf9, 0x7c, 0x1f, 0xb6, 0xe4, 0x7a, 0xf7, 0x74, 0xcf, 0x5c, 0x08, 0x11, 0xc5,
- 0x2a, 0x28, 0x7a, 0xc7, 0x98, 0x34, 0xf2, 0x06, 0x54, 0x5d, 0x67, 0xa9, 0x6f, 0xdb, 0xbc, 0x42,
- 0x1b, 0xad, 0x5f, 0x63, 0x4b, 0xd3, 0x3b, 0x3c, 0xe5, 0xc1, 0xc1, 0xf4, 0x45, 0xa1, 0xdf, 0xb2,
- 0xb7, 0xb7, 0x3c, 0x2b, 0xb0, 0x9c, 0x4e, 0x3b, 0xf0, 0xf4, 0x80, 0x76, 0xf6, 0x51, 0x66, 0xd3,
- 0xfe, 0xa4, 0x00, 0xcd, 0x25, 0xeb, 0x3e, 0x35, 0xdf, 0xb2, 0x1c, 0xd3, 0xbd, 0x47, 0x10, 0xaa,
- 0x36, 0x75, 0x3a, 0xc1, 0xce, 0x88, 0xeb, 0x07, 0xb1, 0x36, 0xe6, 0x08, 0x28, 0x91, 0xc8, 0x2c,
- 0x34, 0x84, 0xf6, 0x69, 0x39, 0x1d, 0x5e, 0x87, 0xf5, 0x68, 0xd0, 0x6b, 0x2b, 0x02, 0x46, 0x3c,
- 0xda, 0x3e, 0x9c, 0x1d, 0xa8, 0x06, 0x62, 0x42, 0x39, 0xd0, 0x3b, 0x6a, 0x7c, 0x5d, 0x1a, 0xb9,
- 0x82, 0x37, 0xf4, 0x4e, 0xac, 0x72, 0xf9, 0x1c, 0xbf, 0xa1, 0xb3, 0x39, 0x9e, 0xa1, 0x6b, 0xbf,
- 0x28, 0x40, 0x7d, 0xa9, 0xef, 0x18, 0x7c, 0x89, 0xf6, 0x68, 0x8b, 0x9c, 0x52, 0x18, 0x8a, 0x99,
- 0x0a, 0x43, 0x1f, 0xaa, 0xbb, 0xf7, 0x42, 0x85, 0xa2, 0x39, 0xb7, 0x36, 0x7a, 0xab, 0x90, 0x45,
- 0x9a, 0xb9, 0xc5, 0xf1, 0x84, 0xc3, 0x68, 0x42, 0x16, 0xa8, 0x7a, 0xeb, 0x2d, 0x2e, 0x54, 0x0a,
- 0xbb, 0xf4, 0x45, 0x68, 0xc6, 0xd8, 0x8e, 0x65, 0x3b, 0xfe, 0x47, 0x65, 0xa8, 0x2e, 0xb7, 0xdb,
- 0xf3, 0xeb, 0x2b, 0xe4, 0x15, 0x68, 0x4a, 0x5f, 0xc2, 0xed, 0xa8, 0x0e, 0x42, 0x57, 0x52, 0x3b,
- 0x22, 0x61, 0x9c, 0x8f, 0xa9, 0x63, 0x1e, 0xd5, 0xed, 0xae, 0xec, 0x2c, 0xa1, 0x3a, 0x86, 0x2c,
- 0x11, 0x05, 0x8d, 0xe8, 0x30, 0xc1, 0x56, 0x78, 0xac, 0x0a, 0xc5, 0xea, 0x4d, 0x76, 0x9b, 0x23,
- 0xae, 0xef, 0xb8, 0x92, 0xb8, 0x99, 0x00, 0xc0, 0x14, 0x20, 0x79, 0x1d, 0xea, 0x7a, 0x3f, 0xd8,
- 0xe1, 0x0a, 0xb4, 0xe8, 0x1b, 0x97, 0xb9, 0xab, 0x45, 0xa6, 0x3d, 0x38, 0x98, 0x1e, 0xbb, 0x85,
- 0xad, 0x57, 0xd4, 0x3b, 0x86, 0xdc, 0xac, 0x70, 0x6a, 0xc5, 0x28, 0x0b, 0x57, 0x39, 0x76, 0xe1,
- 0xd6, 0x13, 0x00, 0x98, 0x02, 0x24, 0xef, 0xc0, 0xd8, 0x2e, 0xdd, 0x0f, 0xf4, 0x2d, 0x29, 0xa0,
- 0x7a, 0x1c, 0x01, 0x93, 0x4c, 0x85, 0xbb, 0x15, 0xcb, 0x8e, 0x09, 0x30, 0xe2, 0xc3, 0xf9, 0x5d,
- 0xea, 0x6d, 0x51, 0xcf, 0x95, 0xab, 0x4f, 0x29, 0xa4, 0x76, 0x1c, 0x21, 0x53, 0x87, 0x07, 0xd3,
- 0xe7, 0x6f, 0x65, 0xc0, 0x60, 0x26, 0xb8, 0xf6, 0xf3, 0x22, 0x9c, 0x59, 0x16, 0xce, 0x5c, 0xd7,
- 0x13, 0x93, 0x30, 0xb9, 0x08, 0x25, 0xaf, 0xd7, 0xe7, 0x2d, 0xa7, 0x24, 0xcc, 0xb5, 0xb8, 0xbe,
- 0x89, 0x2c, 0x8d, 0xbc, 0x0d, 0x75, 0x53, 0x0e, 0x19, 0x72, 0xf1, 0x3b, 0x92, 0xa1, 0x42, 0xbd,
- 0x61, 0x88, 0xc6, 0x34, 0xfd, 0xae, 0xdf, 0x69, 0x5b, 0x1f, 0x50, 0xb9, 0x1e, 0xe4, 0x9a, 0xfe,
- 0x9a, 0x48, 0x42, 0x45, 0x63, 0xb3, 0xea, 0x2e, 0xdd, 0x17, 0xab, 0xa1, 0x72, 0x34, 0xab, 0xde,
- 0x92, 0x69, 0x18, 0x52, 0xc9, 0xb4, 0xea, 0x2c, 0xac, 0x15, 0x94, 0xc5, 0x4a, 0xfe, 0x2e, 0x4b,
- 0x90, 0xfd, 0x86, 0x0d, 0x99, 0xef, 0x59, 0x41, 0x40, 0x3d, 0xf9, 0x1b, 0x47, 0x1a, 0x32, 0x6f,
- 0x72, 0x04, 0x94, 0x48, 0xe4, 0x37, 0xa0, 0xc1, 0xc1, 0x5b, 0xb6, 0xbb, 0xc5, 0x7f, 0x5c, 0x43,
- 0xac, 0xe9, 0xef, 0xaa, 0x44, 0x8c, 0xe8, 0xda, 0x2f, 0x8b, 0x70, 0x61, 0x99, 0x06, 0x42, 0xab,
- 0x59, 0xa4, 0x3d, 0xdb, 0xdd, 0x67, 0xaa, 0x25, 0xd2, 0xf7, 0xc9, 0x9b, 0x00, 0x96, 0xbf, 0xd5,
- 0xde, 0x33, 0x78, 0x3f, 0x10, 0x7d, 0xf8, 0xaa, 0xec, 0x92, 0xb0, 0xd2, 0x6e, 0x49, 0xca, 0x83,
- 0xc4, 0x1b, 0xc6, 0xf2, 0x44, 0xcb, 0xab, 0xe2, 0x43, 0x96, 0x57, 0x6d, 0x80, 0x5e, 0xa4, 0xa0,
- 0x96, 0x38, 0xe7, 0x9f, 0x53, 0x62, 0x8e, 0xa3, 0x9b, 0xc6, 0x60, 0xf2, 0xa8, 0x8c, 0x0e, 0x4c,
- 0x9a, 0x74, 0x5b, 0xef, 0xdb, 0x41, 0xa8, 0x54, 0xcb, 0x4e, 0x7c, 0x74, 0xbd, 0x3c, 0x74, 0x34,
- 0x2f, 0xa6, 0x90, 0x70, 0x00, 0x5b, 0xfb, 0x6e, 0x09, 0x2e, 0x2d, 0xd3, 0x20, 0xb4, 0xb8, 0xc8,
- 0xd1, 0xb1, 0xdd, 0xa3, 0x06, 0xfb, 0x0b, 0x1f, 0x15, 0xa0, 0x6a, 0xeb, 0x5b, 0xd4, 0x66, 0xb3,
- 0x17, 0xfb, 0x9a, 0x77, 0x47, 0x9e, 0x08, 0x86, 0x4b, 0x99, 0x59, 0xe5, 0x12, 0x52, 0x53, 0x83,
- 0x48, 0x44, 0x29, 0x9e, 0x0d, 0xea, 0x86, 0xdd, 0xf7, 0x03, 0xea, 0xad, 0xbb, 0x5e, 0x20, 0xf5,
- 0xc9, 0x70, 0x50, 0x5f, 0x88, 0x48, 0x18, 0xe7, 0x23, 0x73, 0x00, 0x86, 0x6d, 0x51, 0x27, 0xe0,
- 0xb9, 0x44, 0xbf, 0x22, 0xea, 0xff, 0x2e, 0x84, 0x14, 0x8c, 0x71, 0x31, 0x51, 0x5d, 0xd7, 0xb1,
- 0x02, 0x57, 0x88, 0x2a, 0x27, 0x45, 0xad, 0x45, 0x24, 0x8c, 0xf3, 0xf1, 0x6c, 0x34, 0xf0, 0x2c,
- 0xc3, 0xe7, 0xd9, 0x2a, 0xa9, 0x6c, 0x11, 0x09, 0xe3, 0x7c, 0x6c, 0xce, 0x8b, 0x7d, 0xff, 0xb1,
- 0xe6, 0xbc, 0x6f, 0x35, 0xe0, 0x4a, 0xa2, 0x5a, 0x03, 0x3d, 0xa0, 0xdb, 0x7d, 0xbb, 0x4d, 0x03,
- 0xf5, 0x03, 0x47, 0x9c, 0x0b, 0xff, 0x6a, 0xf4, 0xdf, 0x45, 0x08, 0x89, 0x71, 0x32, 0xff, 0x7d,
- 0xa0, 0x80, 0x47, 0xfa, 0xf7, 0xb3, 0xd0, 0x70, 0xf4, 0xc0, 0xe7, 0x1d, 0x57, 0xf6, 0xd1, 0x50,
- 0x0d, 0xbb, 0xad, 0x08, 0x18, 0xf1, 0x90, 0x75, 0x38, 0x2f, 0xab, 0xf8, 0xfa, 0xfd, 0x9e, 0xeb,
- 0x05, 0xd4, 0x13, 0x79, 0xe5, 0x74, 0x2a, 0xf3, 0x9e, 0x5f, 0xcb, 0xe0, 0xc1, 0xcc, 0x9c, 0x64,
- 0x0d, 0xce, 0x19, 0xc2, 0xad, 0x4e, 0x6d, 0x57, 0x37, 0x15, 0xa0, 0x30, 0x70, 0x85, 0x4b, 0xa3,
- 0x85, 0x41, 0x16, 0xcc, 0xca, 0x97, 0x6e, 0xcd, 0xd5, 0x91, 0x5a, 0x73, 0x6d, 0x94, 0xd6, 0x5c,
- 0x1f, 0xad, 0x35, 0x37, 0x8e, 0xd6, 0x9a, 0x59, 0xcd, 0xb3, 0x76, 0x44, 0x3d, 0xa6, 0x9e, 0x88,
- 0x19, 0x36, 0x16, 0xb5, 0x11, 0xd6, 0x7c, 0x3b, 0x83, 0x07, 0x33, 0x73, 0x92, 0x2d, 0xb8, 0x24,
- 0xd2, 0xaf, 0x3b, 0x86, 0xb7, 0xdf, 0x63, 0x13, 0x4f, 0x0c, 0xb7, 0x99, 0xb0, 0x30, 0x5e, 0x6a,
- 0x0f, 0xe5, 0xc4, 0x87, 0xa0, 0x90, 0xdf, 0x86, 0x71, 0xf1, 0x97, 0xd6, 0xf4, 0x1e, 0x87, 0x15,
- 0x31, 0x1c, 0x4f, 0x4b, 0xd8, 0xf1, 0x85, 0x38, 0x11, 0x93, 0xbc, 0x64, 0x1e, 0xce, 0xf4, 0xf6,
- 0x0c, 0xf6, 0xb8, 0xb2, 0x7d, 0x9b, 0x52, 0x93, 0x9a, 0xdc, 0x69, 0xd4, 0x68, 0x3d, 0xa3, 0x0c,
- 0x1d, 0xeb, 0x49, 0x32, 0xa6, 0xf9, 0xc9, 0xeb, 0x30, 0xe6, 0x07, 0xba, 0x17, 0x48, 0xb3, 0xde,
- 0xd4, 0x84, 0x88, 0x71, 0x51, 0x56, 0xaf, 0x76, 0x8c, 0x86, 0x09, 0xce, 0xcc, 0xf9, 0xe2, 0xcc,
- 0xe9, 0xcd, 0x17, 0x79, 0x46, 0xab, 0x7f, 0x59, 0x84, 0xab, 0xcb, 0x34, 0x58, 0x73, 0x1d, 0x69,
- 0x14, 0xcd, 0x9a, 0xf6, 0x8f, 0x64, 0x13, 0x4d, 0x4e, 0xda, 0xc5, 0x13, 0x9d, 0xb4, 0x4b, 0x27,
- 0x34, 0x69, 0x97, 0x4f, 0x71, 0xd2, 0xfe, 0x27, 0x45, 0x78, 0x26, 0x51, 0x93, 0xeb, 0xae, 0xa9,
- 0x06, 0xfc, 0xcf, 0x2b, 0xf0, 0x08, 0x15, 0xf8, 0x40, 0xe8, 0x9d, 0xdc, 0xad, 0x95, 0xd2, 0x78,
- 0x3e, 0x4c, 0x6b, 0x3c, 0xef, 0xe4, 0x99, 0xf9, 0x32, 0x24, 0x1c, 0x69, 0xc6, 0xbb, 0x09, 0xc4,
- 0x93, 0x4e, 0x38, 0x61, 0xfa, 0x89, 0x29, 0x3d, 0x61, 0x10, 0x1d, 0x0e, 0x70, 0x60, 0x46, 0x2e,
- 0xd2, 0x86, 0xa7, 0x7d, 0xea, 0x04, 0x96, 0x43, 0xed, 0x24, 0x9c, 0xd0, 0x86, 0x9e, 0x93, 0x70,
- 0x4f, 0xb7, 0xb3, 0x98, 0x30, 0x3b, 0x6f, 0x9e, 0x71, 0xe0, 0xdf, 0x00, 0x57, 0x39, 0x45, 0xd5,
- 0x9c, 0x98, 0xc6, 0xf2, 0x51, 0x5a, 0x63, 0x79, 0x37, 0xff, 0x7f, 0x1b, 0x4d, 0x5b, 0x99, 0x03,
- 0xe0, 0x7f, 0x21, 0xae, 0xae, 0x84, 0x93, 0x34, 0x86, 0x14, 0x8c, 0x71, 0xb1, 0x09, 0x48, 0xd5,
- 0x73, 0x5c, 0x53, 0x09, 0x27, 0xa0, 0x76, 0x9c, 0x88, 0x49, 0xde, 0xa1, 0xda, 0x4e, 0x65, 0x64,
- 0x6d, 0xe7, 0x26, 0x90, 0x84, 0xe1, 0x51, 0xe0, 0x55, 0x93, 0x31, 0x9c, 0x2b, 0x03, 0x1c, 0x98,
- 0x91, 0x6b, 0x48, 0x53, 0xae, 0x9d, 0x6c, 0x53, 0xae, 0x8f, 0xde, 0x94, 0xc9, 0xbb, 0x70, 0x91,
- 0x8b, 0x92, 0xf5, 0x93, 0x04, 0x16, 0x7a, 0xcf, 0xaf, 0x48, 0xe0, 0x8b, 0x38, 0x8c, 0x11, 0x87,
- 0x63, 0xb0, 0xff, 0x63, 0x78, 0xd4, 0x64, 0xc2, 0x75, 0x7b, 0xb8, 0x4e, 0xb4, 0x90, 0xc1, 0x83,
- 0x99, 0x39, 0x59, 0x13, 0x0b, 0x58, 0x33, 0xd4, 0xb7, 0x6c, 0x6a, 0xca, 0x18, 0xd6, 0xb0, 0x89,
- 0x6d, 0xac, 0xb6, 0x25, 0x05, 0x63, 0x5c, 0x59, 0x6a, 0xca, 0xd8, 0x31, 0xd5, 0x94, 0x65, 0x6e,
- 0xa5, 0xdf, 0x4e, 0x68, 0x43, 0x52, 0xd7, 0x09, 0xa3, 0x92, 0x17, 0xd2, 0x0c, 0x38, 0x98, 0x87,
- 0x6b, 0x89, 0x86, 0x67, 0xf5, 0x02, 0x3f, 0x89, 0x35, 0x91, 0xd2, 0x12, 0x33, 0x78, 0x30, 0x33,
- 0x27, 0xd3, 0xcf, 0x77, 0xa8, 0x6e, 0x07, 0x3b, 0x49, 0xc0, 0x33, 0x49, 0xfd, 0xfc, 0xc6, 0x20,
- 0x0b, 0x66, 0xe5, 0xcb, 0x9c, 0x90, 0x26, 0x9f, 0x4c, 0xb5, 0xea, 0x8f, 0x4b, 0x70, 0x71, 0x99,
- 0x06, 0x61, 0x78, 0xcf, 0xe7, 0x66, 0x94, 0x4f, 0xc1, 0x8c, 0xf2, 0xcd, 0x0a, 0x9c, 0x5b, 0xa6,
- 0xc1, 0x80, 0x36, 0xf6, 0xa7, 0xb4, 0xfa, 0xd7, 0xe0, 0x5c, 0x14, 0x51, 0xd6, 0x0e, 0x5c, 0x4f,
- 0xcc, 0xe5, 0xa9, 0xd5, 0x72, 0x7b, 0x90, 0x05, 0xb3, 0xf2, 0x91, 0xaf, 0xc0, 0x33, 0x7c, 0xaa,
- 0x77, 0x3a, 0xc2, 0x3e, 0x2b, 0x8c, 0x09, 0xb1, 0x3d, 0x11, 0xd3, 0x12, 0xf2, 0x99, 0x76, 0x36,
- 0x1b, 0x0e, 0xcb, 0x4f, 0xbe, 0x06, 0x63, 0x3d, 0xab, 0x47, 0x6d, 0xcb, 0xe1, 0xfa, 0x59, 0xee,
- 0x90, 0x90, 0xf5, 0x18, 0x58, 0xb4, 0x80, 0x8b, 0xa7, 0x62, 0x42, 0x60, 0x66, 0x4b, 0xad, 0x9f,
- 0x62, 0x4b, 0xfd, 0x9f, 0x45, 0xa8, 0x2d, 0x7b, 0x6e, 0xbf, 0xd7, 0xda, 0x27, 0x1d, 0xa8, 0xde,
- 0xe3, 0xce, 0x33, 0xe9, 0x9a, 0x1a, 0x3d, 0x2a, 0x5b, 0xf8, 0xe0, 0x22, 0x95, 0x48, 0xbc, 0xa3,
- 0x84, 0x67, 0x8d, 0x78, 0x97, 0xee, 0x53, 0x53, 0xfa, 0xd0, 0xc2, 0x46, 0x7c, 0x8b, 0x25, 0xa2,
- 0xa0, 0x91, 0x2e, 0x9c, 0xd1, 0x6d, 0xdb, 0xbd, 0x47, 0xcd, 0x55, 0x3d, 0xa0, 0x0e, 0xf5, 0x95,
- 0x4b, 0xf2, 0xb8, 0x66, 0x69, 0xee, 0xd7, 0x9f, 0x4f, 0x42, 0x61, 0x1a, 0x9b, 0xbc, 0x07, 0x35,
- 0x3f, 0x70, 0x3d, 0xa5, 0x6c, 0x35, 0xe7, 0x16, 0x46, 0xff, 0xe9, 0xad, 0x2f, 0xb7, 0x05, 0x94,
- 0xb0, 0xd9, 0xcb, 0x17, 0x54, 0x02, 0xb4, 0x6f, 0x14, 0x00, 0x6e, 0x6c, 0x6c, 0xac, 0x4b, 0xf7,
- 0x82, 0x09, 0x65, 0xbd, 0x1f, 0x3a, 0x2a, 0x47, 0x77, 0x08, 0x26, 0xc2, 0x32, 0xa5, 0x0f, 0xaf,
- 0x1f, 0xec, 0x20, 0x47, 0x27, 0xbf, 0x0e, 0x35, 0xa9, 0x20, 0xcb, 0x6a, 0x0f, 0x43, 0x0b, 0xa4,
- 0x12, 0x8d, 0x8a, 0xae, 0xfd, 0xc3, 0x22, 0xc0, 0x8a, 0x69, 0xd3, 0xb6, 0x0a, 0xa4, 0x6f, 0x04,
- 0x3b, 0x1e, 0xf5, 0x77, 0x5c, 0xdb, 0x1c, 0xd1, 0x9b, 0xca, 0x6d, 0xfe, 0x1b, 0x0a, 0x04, 0x23,
- 0x3c, 0x62, 0xc2, 0x98, 0x1f, 0xd0, 0x9e, 0x8a, 0xd4, 0x1c, 0xd1, 0x89, 0x32, 0x29, 0xec, 0x22,
- 0x11, 0x0e, 0x26, 0x50, 0x89, 0x0e, 0x4d, 0xcb, 0x31, 0x44, 0x07, 0x69, 0xed, 0x8f, 0xd8, 0x90,
- 0xce, 0xb0, 0x15, 0xc7, 0x4a, 0x04, 0x83, 0x71, 0x4c, 0xed, 0x27, 0x45, 0xb8, 0xc0, 0xe5, 0xb1,
- 0x62, 0x24, 0xe2, 0x31, 0xc9, 0x5f, 0x18, 0xd8, 0xf4, 0xf7, 0x67, 0x8f, 0x26, 0x5a, 0xec, 0x19,
- 0x5b, 0xa3, 0x81, 0x1e, 0xe9, 0x73, 0x51, 0x5a, 0x6c, 0xa7, 0x5f, 0x1f, 0xca, 0x3e, 0x1b, 0xaf,
- 0x44, 0xed, 0xb5, 0x47, 0x6e, 0x42, 0xd9, 0x1f, 0xc0, 0x47, 0xaf, 0xd0, 0x6b, 0xcc, 0x47, 0x2d,
- 0x2e, 0x8e, 0xfc, 0x25, 0xa8, 0xfa, 0x81, 0x1e, 0xf4, 0x55, 0xd7, 0xdc, 0x3c, 0x69, 0xc1, 0x1c,
- 0x3c, 0x1a, 0x47, 0xc4, 0x3b, 0x4a, 0xa1, 0xda, 0x4f, 0x0a, 0x70, 0x29, 0x3b, 0xe3, 0xaa, 0xe5,
- 0x07, 0xe4, 0xcf, 0x0f, 0x54, 0xfb, 0x11, 0xff, 0x38, 0xcb, 0xcd, 0x2b, 0x3d, 0x8c, 0x0b, 0x57,
- 0x29, 0xb1, 0x2a, 0x0f, 0xa0, 0x62, 0x05, 0xb4, 0xab, 0xd6, 0x97, 0x77, 0x4e, 0xf8, 0xd3, 0x63,
- 0x53, 0x3b, 0x93, 0x82, 0x42, 0x98, 0xf6, 0x71, 0x71, 0xd8, 0x27, 0xf3, 0xe9, 0xc3, 0x4e, 0xc6,
- 0xfc, 0xde, 0xca, 0x17, 0xf3, 0x9b, 0x2c, 0xd0, 0x60, 0xe8, 0xef, 0x5f, 0x1c, 0x0c, 0xfd, 0xbd,
- 0x93, 0x3f, 0xf4, 0x37, 0x55, 0x0d, 0x43, 0x23, 0x80, 0x3f, 0x29, 0xc1, 0xe5, 0x87, 0x35, 0x1b,
- 0x36, 0x9f, 0xc9, 0xd6, 0x99, 0x77, 0x3e, 0x7b, 0x78, 0x3b, 0x24, 0x73, 0x50, 0xe9, 0xed, 0xe8,
- 0xbe, 0x52, 0xca, 0xd4, 0x82, 0xa5, 0xb2, 0xce, 0x12, 0x1f, 0xb0, 0x41, 0x83, 0x2b, 0x73, 0xfc,
- 0x15, 0x05, 0x2b, 0x1b, 0x8e, 0xbb, 0xd4, 0xf7, 0x23, 0x9b, 0x40, 0x38, 0x1c, 0xaf, 0x89, 0x64,
- 0x54, 0x74, 0x12, 0x40, 0x55, 0x98, 0x98, 0xe5, 0xcc, 0x34, 0x7a, 0x20, 0x57, 0x46, 0x98, 0x78,
- 0xf4, 0x51, 0xd2, 0x5b, 0x21, 0x65, 0x91, 0x19, 0x28, 0x07, 0x51, 0xd0, 0xae, 0x5a, 0x9a, 0x97,
- 0x33, 0xf4, 0x53, 0xce, 0xc7, 0x16, 0xf6, 0xee, 0x16, 0x37, 0xaa, 0x9b, 0xd2, 0x7f, 0x6e, 0xb9,
- 0x0e, 0x57, 0xc8, 0x4a, 0xd1, 0xc2, 0xfe, 0xce, 0x00, 0x07, 0x66, 0xe4, 0xd2, 0xfe, 0x7d, 0x1d,
- 0x2e, 0x64, 0xb7, 0x07, 0x56, 0x6f, 0x7b, 0xd4, 0xf3, 0x19, 0x76, 0x21, 0x59, 0x6f, 0x77, 0x45,
- 0x32, 0x2a, 0xfa, 0x67, 0x3a, 0xe0, 0xec, 0x9b, 0x05, 0xb8, 0xe8, 0x49, 0x1f, 0xd1, 0xe3, 0x08,
- 0x3a, 0x7b, 0x4e, 0x98, 0x33, 0x86, 0x08, 0xc4, 0xe1, 0x65, 0x21, 0x7f, 0xaf, 0x00, 0x53, 0xdd,
- 0x94, 0x9d, 0xe3, 0x14, 0xf7, 0xad, 0xf1, 0xa8, 0xf8, 0xb5, 0x21, 0xf2, 0x70, 0x68, 0x49, 0xc8,
- 0xd7, 0xa0, 0xd9, 0x63, 0xed, 0xc2, 0x0f, 0xa8, 0x63, 0xa8, 0xad, 0x6b, 0xa3, 0xf7, 0xa4, 0xf5,
- 0x08, 0x4b, 0x85, 0xa2, 0x09, 0xfd, 0x20, 0x46, 0xc0, 0xb8, 0xc4, 0x27, 0x7c, 0xa3, 0xda, 0x35,
- 0xa8, 0xfb, 0x34, 0x08, 0x2c, 0xa7, 0x23, 0xd6, 0x1b, 0x0d, 0xd1, 0x57, 0xda, 0x32, 0x0d, 0x43,
- 0x2a, 0xf9, 0x0d, 0x68, 0x70, 0x97, 0xd3, 0xbc, 0xd7, 0xf1, 0xa7, 0x1a, 0x3c, 0x5c, 0x6c, 0x5c,
- 0x04, 0xc0, 0xc9, 0x44, 0x8c, 0xe8, 0xe4, 0x65, 0x18, 0xdb, 0xe2, 0xdd, 0x57, 0xee, 0x5d, 0x16,
- 0x36, 0x2e, 0xae, 0xad, 0xb5, 0x62, 0xe9, 0x98, 0xe0, 0x22, 0x73, 0x00, 0x34, 0xf4, 0xcb, 0xa5,
- 0xed, 0x59, 0x91, 0xc7, 0x0e, 0x63, 0x5c, 0xe4, 0x39, 0x28, 0x05, 0xb6, 0xcf, 0x6d, 0x58, 0xf5,
- 0x68, 0x09, 0xba, 0xb1, 0xda, 0x46, 0x96, 0xae, 0xfd, 0xb2, 0x00, 0x67, 0x52, 0x9b, 0x4b, 0x58,
- 0x96, 0xbe, 0x67, 0xcb, 0x61, 0x24, 0xcc, 0xb2, 0x89, 0xab, 0xc8, 0xd2, 0xc9, 0xbb, 0x52, 0x2d,
- 0x2f, 0xe6, 0x3c, 0xa6, 0xe1, 0xb6, 0x1e, 0xf8, 0x4c, 0x0f, 0x1f, 0xd0, 0xc8, 0xb9, 0x9b, 0x2f,
- 0x2a, 0x8f, 0x9c, 0x07, 0x62, 0x6e, 0xbe, 0x88, 0x86, 0x09, 0xce, 0x94, 0xc1, 0xaf, 0x7c, 0x14,
- 0x83, 0x9f, 0xf6, 0x27, 0xc5, 0x58, 0x0d, 0x48, 0xcd, 0xfe, 0x11, 0x35, 0xf0, 0x22, 0x9b, 0x40,
- 0xc3, 0xc9, 0xbd, 0x11, 0x9f, 0xff, 0xf8, 0x64, 0x2c, 0xa9, 0xe4, 0x2d, 0x51, 0xf7, 0xa5, 0x9c,
- 0x9b, 0x61, 0x37, 0x56, 0xdb, 0x22, 0xba, 0x4a, 0xfd, 0xb5, 0xf0, 0x17, 0x94, 0x4f, 0xe9, 0x17,
- 0x68, 0xff, 0xba, 0x04, 0xcd, 0x9b, 0xee, 0xd6, 0x67, 0x24, 0x82, 0x3a, 0x7b, 0x9a, 0x2a, 0x7e,
- 0x8a, 0xd3, 0xd4, 0x26, 0x3c, 0x13, 0x04, 0x76, 0x9b, 0x1a, 0xae, 0x63, 0xfa, 0xf3, 0xdb, 0x01,
- 0xf5, 0x96, 0x2c, 0xc7, 0xf2, 0x77, 0xa8, 0x29, 0xdd, 0x49, 0xcf, 0x1e, 0x1e, 0x4c, 0x3f, 0xb3,
- 0xb1, 0xb1, 0x9a, 0xc5, 0x82, 0xc3, 0xf2, 0xf2, 0x61, 0x43, 0xec, 0x04, 0xe4, 0x3b, 0x65, 0x64,
- 0xcc, 0x8d, 0x18, 0x36, 0x62, 0xe9, 0x98, 0xe0, 0xd2, 0xbe, 0x53, 0x84, 0x46, 0xb8, 0x01, 0x9f,
- 0xbc, 0x00, 0xb5, 0x2d, 0xcf, 0xdd, 0xa5, 0x9e, 0xf0, 0xdc, 0xc9, 0x9d, 0x32, 0x2d, 0x91, 0x84,
- 0x8a, 0x46, 0x9e, 0x87, 0x4a, 0xe0, 0xf6, 0x2c, 0x23, 0x6d, 0x50, 0xdb, 0x60, 0x89, 0x28, 0x68,
- 0xa7, 0xd7, 0xc0, 0x5f, 0x4c, 0xa8, 0x76, 0x8d, 0xa1, 0xca, 0xd8, 0x3b, 0x50, 0xf6, 0x75, 0xdf,
- 0x96, 0xf3, 0x69, 0x8e, 0xbd, 0xec, 0xf3, 0xed, 0x55, 0xb9, 0x97, 0x7d, 0xbe, 0xbd, 0x8a, 0x1c,
- 0x54, 0xfb, 0x59, 0x11, 0x9a, 0xa2, 0xde, 0xc4, 0xa8, 0x70, 0x92, 0x35, 0xf7, 0x06, 0x0f, 0xa5,
- 0xf0, 0xfb, 0x5d, 0xea, 0x71, 0x33, 0x93, 0x1c, 0xe4, 0xe2, 0xfe, 0x81, 0x88, 0x18, 0x86, 0x53,
- 0x44, 0x49, 0xaa, 0xea, 0xcb, 0xa7, 0x58, 0xf5, 0x95, 0x23, 0x55, 0x7d, 0xf5, 0x34, 0xaa, 0xfe,
- 0xa3, 0x22, 0x34, 0x56, 0xad, 0x6d, 0x6a, 0xec, 0x1b, 0x36, 0xdf, 0x13, 0x68, 0x52, 0x9b, 0x06,
- 0x74, 0xd9, 0xd3, 0x0d, 0xba, 0x4e, 0x3d, 0x8b, 0x1f, 0x50, 0xc3, 0xfa, 0x07, 0x1f, 0x81, 0xe4,
- 0x9e, 0xc0, 0xc5, 0x21, 0x3c, 0x38, 0x34, 0x37, 0x59, 0x81, 0x31, 0x93, 0xfa, 0x96, 0x47, 0xcd,
- 0xf5, 0xd8, 0x42, 0xe5, 0x05, 0x35, 0xd5, 0x2c, 0xc6, 0x68, 0x0f, 0x0e, 0xa6, 0xc7, 0x95, 0x81,
- 0x52, 0xac, 0x58, 0x12, 0x59, 0x59, 0x97, 0xef, 0xe9, 0x7d, 0x3f, 0xab, 0x8c, 0xb1, 0x2e, 0xbf,
- 0x9e, 0xcd, 0x82, 0xc3, 0xf2, 0x6a, 0x15, 0x28, 0xad, 0xba, 0x1d, 0xed, 0xe3, 0x12, 0x84, 0x27,
- 0x19, 0x91, 0xbf, 0x52, 0x80, 0xa6, 0xee, 0x38, 0x6e, 0x20, 0x4f, 0x09, 0x12, 0x1e, 0x78, 0xcc,
- 0x7d, 0x60, 0xd2, 0xcc, 0x7c, 0x04, 0x2a, 0x9c, 0xb7, 0xa1, 0x43, 0x39, 0x46, 0xc1, 0xb8, 0x6c,
- 0xd2, 0x4f, 0xf9, 0x93, 0xd7, 0xf2, 0x97, 0xe2, 0x08, 0xde, 0xe3, 0x4b, 0x5f, 0x82, 0xc9, 0x74,
- 0x61, 0x8f, 0xe3, 0x0e, 0xca, 0xe5, 0x98, 0x2f, 0x02, 0x44, 0x31, 0x25, 0x8f, 0xc1, 0x88, 0x65,
- 0x25, 0x8c, 0x58, 0xcb, 0xa3, 0x57, 0x70, 0x58, 0xe8, 0xa1, 0x86, 0xab, 0xf7, 0x53, 0x86, 0xab,
- 0x95, 0x93, 0x10, 0xf6, 0x70, 0x63, 0xd5, 0x3f, 0x28, 0xc0, 0x64, 0xc4, 0x2c, 0x77, 0xc8, 0xbe,
- 0x06, 0xe3, 0x1e, 0xd5, 0xcd, 0x96, 0x1e, 0x18, 0x3b, 0x3c, 0xd4, 0xbb, 0xc0, 0x63, 0xb3, 0xcf,
- 0x1e, 0x1e, 0x4c, 0x8f, 0x63, 0x9c, 0x80, 0x49, 0x3e, 0xa2, 0x43, 0x93, 0x25, 0x6c, 0x58, 0x5d,
- 0xea, 0xf6, 0x83, 0x11, 0xad, 0xa6, 0x7c, 0xc1, 0x82, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0x4f, 0x0a,
- 0x30, 0x11, 0x2f, 0xf0, 0xa9, 0x5b, 0xd4, 0x76, 0x92, 0x16, 0xb5, 0x85, 0x13, 0xf8, 0x27, 0x43,
- 0xac, 0x68, 0x3f, 0xaf, 0xc7, 0x3f, 0x8d, 0x5b, 0xce, 0xe2, 0xc6, 0x82, 0xc2, 0x43, 0x8d, 0x05,
- 0x9f, 0xfd, 0xc3, 0x6b, 0x86, 0x69, 0xb9, 0xe5, 0x27, 0x58, 0xcb, 0xfd, 0x34, 0x4f, 0xc0, 0x89,
- 0x9d, 0xe2, 0x52, 0xcd, 0x71, 0x8a, 0x4b, 0x37, 0x3c, 0xc5, 0xa5, 0x76, 0x62, 0x83, 0xce, 0x51,
- 0x4e, 0x72, 0xa9, 0x3f, 0xd6, 0x93, 0x5c, 0x1a, 0xa7, 0x75, 0x92, 0x0b, 0xe4, 0x3d, 0xc9, 0xe5,
- 0xc3, 0x02, 0x4c, 0x98, 0x89, 0x1d, 0xb3, 0xdc, 0xb6, 0x90, 0x67, 0xaa, 0x49, 0x6e, 0xc0, 0x15,
- 0x5b, 0xa6, 0x92, 0x69, 0x98, 0x12, 0xa9, 0xfd, 0xfd, 0x5a, 0x7c, 0x1e, 0x78, 0xdc, 0xa6, 0xea,
- 0x57, 0x93, 0xa6, 0xea, 0xab, 0x69, 0x53, 0xf5, 0x99, 0x58, 0x14, 0x69, 0xdc, 0x5c, 0xfd, 0x85,
- 0xd8, 0xf0, 0x58, 0xe2, 0x27, 0xa7, 0x84, 0x35, 0x9d, 0x31, 0x44, 0x7e, 0x01, 0xea, 0xbe, 0x3a,
- 0x73, 0x52, 0x2c, 0x6c, 0xa2, 0xff, 0xa2, 0xce, 0x83, 0x0c, 0x39, 0x98, 0x26, 0xee, 0x51, 0xdd,
- 0x77, 0x9d, 0xb4, 0x26, 0x8e, 0x3c, 0x15, 0x25, 0x35, 0x6e, 0x32, 0xaf, 0x3e, 0xc2, 0x64, 0xae,
- 0x43, 0xd3, 0xd6, 0xfd, 0x60, 0xb3, 0x67, 0xea, 0x01, 0x35, 0x65, 0x7f, 0xfb, 0x33, 0x47, 0x9b,
- 0xab, 0xd8, 0xfc, 0x17, 0x29, 0x84, 0xab, 0x11, 0x0c, 0xc6, 0x31, 0x89, 0x09, 0x63, 0xec, 0x95,
- 0xf7, 0x06, 0x73, 0x5e, 0x1d, 0x01, 0x70, 0x1c, 0x19, 0xa1, 0xa5, 0x67, 0x35, 0x86, 0x83, 0x09,
- 0xd4, 0x21, 0x56, 0xf5, 0xc6, 0x28, 0x56, 0x75, 0xf2, 0xdb, 0x42, 0xd9, 0xd8, 0x57, 0x3f, 0x8c,
- 0x5b, 0xe3, 0xc6, 0xa3, 0xa8, 0x42, 0x8c, 0x13, 0x31, 0xc9, 0x4b, 0xe6, 0xe1, 0x8c, 0xd1, 0xf7,
- 0x3c, 0x1e, 0x47, 0x24, 0xb3, 0x37, 0x79, 0xf6, 0x30, 0x5e, 0x6c, 0x21, 0x49, 0xc6, 0x34, 0x3f,
- 0x83, 0xe8, 0xcb, 0x9a, 0x54, 0x10, 0x63, 0x49, 0x88, 0xcd, 0x24, 0x19, 0xd3, 0xfc, 0x7c, 0xa3,
- 0x84, 0x40, 0xbd, 0xa1, 0xfb, 0x3b, 0x32, 0xd8, 0x2c, 0xda, 0x28, 0x11, 0x91, 0x30, 0xce, 0x47,
- 0xe6, 0x00, 0x04, 0x12, 0xcf, 0x35, 0x91, 0x8c, 0xc1, 0xdc, 0x0c, 0x29, 0x18, 0xe3, 0xd2, 0x3e,
- 0x6c, 0x40, 0xf3, 0xb6, 0x1e, 0x58, 0x7b, 0x94, 0xfb, 0xbc, 0x4e, 0xc7, 0xf1, 0xf0, 0xb7, 0x0a,
- 0x70, 0x21, 0x19, 0xd8, 0x78, 0x8a, 0xde, 0x07, 0x7e, 0x4c, 0x0a, 0x66, 0x4a, 0xc3, 0x21, 0xa5,
- 0xe0, 0x7e, 0x88, 0x81, 0x38, 0xc9, 0xd3, 0xf6, 0x43, 0xb4, 0x87, 0x09, 0xc4, 0xe1, 0x65, 0xf9,
- 0xac, 0xf8, 0x21, 0x9e, 0xec, 0xd3, 0xf4, 0x52, 0x5e, 0x92, 0xda, 0x13, 0xe3, 0x25, 0xa9, 0x3f,
- 0x11, 0xaa, 0x69, 0x2f, 0xe6, 0x25, 0x69, 0xe4, 0x8c, 0xd6, 0x91, 0x7b, 0x01, 0x04, 0xda, 0x30,
- 0x6f, 0x0b, 0xdf, 0xc6, 0xaf, 0xac, 0xd7, 0x4c, 0xa3, 0xdb, 0xd2, 0x7d, 0xcb, 0x90, 0x4a, 0x42,
- 0x8e, 0xd3, 0x43, 0xd5, 0xf9, 0x66, 0xc2, 0xa9, 0xcf, 0x5f, 0x51, 0x60, 0x47, 0xc7, 0xb9, 0x15,
- 0x73, 0x1d, 0xe7, 0x46, 0x16, 0xa0, 0xec, 0xec, 0xd2, 0xfd, 0xe3, 0x6d, 0x88, 0xe7, 0x2b, 0x95,
- 0xdb, 0xb7, 0xe8, 0x3e, 0xf2, 0xcc, 0xda, 0x77, 0x8a, 0x00, 0xec, 0xf3, 0x8f, 0xe6, 0xaf, 0xf8,
- 0x75, 0xa8, 0xf9, 0x7d, 0x6e, 0x59, 0x90, 0xea, 0x4d, 0x14, 0xe2, 0x24, 0x92, 0x51, 0xd1, 0xc9,
- 0xf3, 0x50, 0x79, 0xbf, 0x4f, 0xfb, 0xca, 0xf9, 0x1e, 0x2a, 0xb7, 0x5f, 0x66, 0x89, 0x28, 0x68,
- 0xa7, 0x67, 0x7b, 0x54, 0x7e, 0x8d, 0xca, 0x69, 0xf9, 0x35, 0x1a, 0x50, 0xbb, 0xed, 0xf2, 0x88,
- 0x49, 0xed, 0xbf, 0x17, 0x01, 0xa2, 0x88, 0x34, 0xf2, 0x8d, 0x02, 0x3c, 0x1d, 0x76, 0xb8, 0x40,
- 0xac, 0x51, 0xf8, 0x81, 0xbd, 0xb9, 0x7d, 0x1c, 0x59, 0x9d, 0x9d, 0x8f, 0x40, 0xeb, 0x59, 0xe2,
- 0x30, 0xbb, 0x14, 0x04, 0xa1, 0x4e, 0xbb, 0xbd, 0x60, 0x7f, 0xd1, 0xf2, 0x64, 0x0b, 0xcc, 0x0c,
- 0x7c, 0xbc, 0x2e, 0x79, 0x44, 0x56, 0xb9, 0x90, 0xe6, 0x9d, 0x48, 0x51, 0x30, 0xc4, 0x21, 0x3b,
- 0x50, 0x77, 0xdc, 0x77, 0x7d, 0x56, 0x1d, 0xb2, 0x39, 0xbe, 0x39, 0x7a, 0x95, 0x8b, 0x6a, 0x15,
- 0x36, 0x71, 0xf9, 0x82, 0x35, 0x47, 0x56, 0xf6, 0xd7, 0x8b, 0x70, 0x2e, 0xa3, 0x1e, 0xc8, 0x9b,
- 0x30, 0x29, 0x83, 0xff, 0xa2, 0x93, 0xab, 0x0b, 0xd1, 0xc9, 0xd5, 0xed, 0x14, 0x0d, 0x07, 0xb8,
- 0xc9, 0xbb, 0x00, 0xba, 0x61, 0x50, 0xdf, 0x5f, 0x73, 0x4d, 0xa5, 0xbd, 0xbf, 0xc1, 0xd4, 0x97,
- 0xf9, 0x30, 0xf5, 0xc1, 0xc1, 0xf4, 0x6f, 0x66, 0xc5, 0xf3, 0xa6, 0xea, 0x39, 0xca, 0x80, 0x31,
- 0x48, 0xf2, 0x55, 0x00, 0xb1, 0x50, 0x0d, 0x8f, 0x1c, 0x78, 0x84, 0x75, 0x67, 0x46, 0x1d, 0xee,
- 0x34, 0xf3, 0xe5, 0xbe, 0xee, 0x04, 0x56, 0xb0, 0x2f, 0x4e, 0x78, 0xb9, 0x1b, 0xa2, 0x60, 0x0c,
- 0x51, 0xfb, 0x17, 0x45, 0xa8, 0x2b, 0xbb, 0xf2, 0x63, 0x30, 0x26, 0x76, 0x12, 0xc6, 0xc4, 0x13,
- 0x8a, 0xe0, 0xcd, 0x32, 0x25, 0xba, 0x29, 0x53, 0xe2, 0x72, 0x7e, 0x51, 0x0f, 0x37, 0x24, 0x7e,
- 0xbb, 0x08, 0x13, 0x8a, 0x35, 0xaf, 0x19, 0xf1, 0x77, 0xe1, 0x8c, 0xf0, 0xbc, 0xaf, 0xe9, 0xf7,
- 0xc5, 0x61, 0x37, 0xbc, 0xc2, 0xca, 0x22, 0x68, 0xb6, 0x95, 0x24, 0x61, 0x9a, 0x97, 0x35, 0x6b,
- 0x91, 0xb4, 0xc9, 0x56, 0x5d, 0xc2, 0x57, 0x27, 0x56, 0x87, 0xbc, 0x59, 0xb7, 0x52, 0x34, 0x1c,
- 0xe0, 0x4e, 0xdb, 0x31, 0xcb, 0xa7, 0x60, 0xc7, 0xfc, 0x0f, 0x05, 0x18, 0x8b, 0xea, 0xeb, 0xd4,
- 0xad, 0x98, 0xdb, 0x49, 0x2b, 0xe6, 0x7c, 0xee, 0xe6, 0x30, 0xc4, 0x86, 0xf9, 0xd7, 0x6a, 0x90,
- 0x08, 0x24, 0x27, 0x5b, 0x70, 0xc9, 0xca, 0x0c, 0x87, 0x8b, 0x8d, 0x36, 0xe1, 0xce, 0xe8, 0x95,
- 0xa1, 0x9c, 0xf8, 0x10, 0x14, 0xd2, 0x87, 0xfa, 0x1e, 0xf5, 0x02, 0xcb, 0xa0, 0xea, 0xfb, 0x96,
- 0x73, 0xab, 0x64, 0xd2, 0x52, 0x1b, 0xd6, 0xe9, 0x5d, 0x29, 0x00, 0x43, 0x51, 0x64, 0x0b, 0x2a,
- 0xd4, 0xec, 0x50, 0x75, 0xfc, 0x50, 0xce, 0xc3, 0x3d, 0xc3, 0xfa, 0x64, 0x6f, 0x3e, 0x0a, 0x68,
- 0xe2, 0x43, 0xc3, 0x56, 0x9e, 0x38, 0xd9, 0x0e, 0x47, 0x57, 0xb0, 0x42, 0x9f, 0x5e, 0x74, 0x32,
- 0x41, 0x98, 0x84, 0x91, 0x1c, 0xb2, 0x1b, 0x9a, 0x04, 0x2b, 0x27, 0x34, 0x78, 0x3c, 0xc4, 0x20,
- 0xe8, 0x43, 0xe3, 0x9e, 0x1e, 0x50, 0xaf, 0xab, 0x7b, 0xbb, 0x72, 0xb5, 0x31, 0xfa, 0x17, 0xbe,
- 0xa5, 0x90, 0xa2, 0x2f, 0x0c, 0x93, 0x30, 0x92, 0x43, 0x5c, 0x68, 0x04, 0x52, 0x7d, 0x56, 0x76,
- 0xcf, 0xd1, 0x85, 0x2a, 0x45, 0xdc, 0x97, 0x01, 0xe5, 0xea, 0x15, 0x23, 0x19, 0x64, 0x2f, 0x71,
- 0xfe, 0xb2, 0x38, 0x75, 0xbb, 0x95, 0xc3, 0x7e, 0x2e, 0xa1, 0xa2, 0xe9, 0x26, 0xfb, 0x1c, 0x67,
- 0xed, 0x7f, 0x55, 0xa2, 0x61, 0xf9, 0x71, 0x5b, 0xf5, 0x5e, 0x4e, 0x5a, 0xf5, 0xae, 0xa4, 0xad,
- 0x7a, 0x29, 0x87, 0xee, 0xf1, 0x43, 0x50, 0x53, 0xf6, 0xb4, 0xf2, 0x29, 0xd8, 0xd3, 0x5e, 0x82,
- 0xe6, 0x1e, 0x1f, 0x09, 0xc4, 0x59, 0x46, 0x15, 0x3e, 0x8d, 0xf0, 0x91, 0xfd, 0x6e, 0x94, 0x8c,
- 0x71, 0x1e, 0x96, 0x45, 0xde, 0x38, 0x11, 0x1e, 0x06, 0x2b, 0xb3, 0xb4, 0xa3, 0x64, 0x8c, 0xf3,
- 0xf0, 0xe8, 0x35, 0xcb, 0xd9, 0x15, 0x19, 0x6a, 0x3c, 0x83, 0x88, 0x5e, 0x53, 0x89, 0x18, 0xd1,
- 0xc9, 0x35, 0xa8, 0xf7, 0xcd, 0x6d, 0xc1, 0x5b, 0xe7, 0xbc, 0x5c, 0xc3, 0xdc, 0x5c, 0x5c, 0x92,
- 0x67, 0x2b, 0x29, 0x2a, 0x2b, 0x49, 0x57, 0xef, 0x29, 0x02, 0x5f, 0x1b, 0xca, 0x92, 0xac, 0x45,
- 0xc9, 0x18, 0xe7, 0x21, 0xbf, 0x05, 0x13, 0x1e, 0x35, 0xfb, 0x06, 0x0d, 0x73, 0x09, 0x73, 0x1c,
- 0x11, 0x57, 0x6b, 0xc4, 0x29, 0x98, 0xe2, 0x1c, 0x62, 0x15, 0x6c, 0x8e, 0x64, 0x15, 0xfc, 0x12,
- 0x4c, 0x98, 0x9e, 0x6e, 0x39, 0xd4, 0xbc, 0xe3, 0x70, 0xaf, 0xbd, 0x8c, 0xa1, 0x0b, 0x4d, 0xf4,
- 0x8b, 0x09, 0x2a, 0xa6, 0xb8, 0xb5, 0x1f, 0x17, 0x80, 0x0c, 0x46, 0x8b, 0x93, 0x1d, 0xa8, 0x3a,
- 0xdc, 0x7a, 0x96, 0xfb, 0xf8, 0xe9, 0x98, 0x11, 0x4e, 0x0c, 0x6b, 0x32, 0x41, 0xe2, 0x13, 0x07,
- 0xea, 0xf4, 0x7e, 0x40, 0x3d, 0x27, 0xdc, 0x3d, 0x72, 0x32, 0x47, 0x5d, 0x8b, 0xd5, 0x84, 0x44,
- 0xc6, 0x50, 0x86, 0xf6, 0xd3, 0x22, 0x34, 0x63, 0x7c, 0x8f, 0x5a, 0x94, 0xf2, 0x0d, 0xec, 0xc2,
- 0x68, 0xb5, 0xe9, 0xd9, 0xb2, 0x87, 0xc6, 0x36, 0xb0, 0x4b, 0x12, 0xae, 0x62, 0x9c, 0x8f, 0xcc,
- 0x01, 0x74, 0x75, 0x3f, 0xa0, 0x1e, 0x9f, 0xbd, 0x53, 0xdb, 0xc6, 0xd7, 0x42, 0x0a, 0xc6, 0xb8,
- 0xc8, 0x55, 0x79, 0x58, 0x79, 0x39, 0x79, 0xcc, 0xdf, 0x90, 0x93, 0xc8, 0x2b, 0x27, 0x70, 0x12,
- 0x39, 0xe9, 0xc0, 0xa4, 0x2a, 0xb5, 0xa2, 0x1e, 0xef, 0x10, 0x38, 0xb1, 0xfe, 0x49, 0x41, 0xe0,
- 0x00, 0xa8, 0xf6, 0x9d, 0x02, 0x8c, 0x27, 0x4c, 0x26, 0xe2, 0x80, 0x3e, 0xb5, 0xd7, 0x21, 0x71,
- 0x40, 0x5f, 0x6c, 0x8b, 0xc2, 0x8b, 0x50, 0x15, 0x15, 0x94, 0x0e, 0x61, 0x14, 0x55, 0x88, 0x92,
- 0xca, 0xc6, 0x42, 0x69, 0x94, 0x4d, 0x8f, 0x85, 0xd2, 0x6a, 0x8b, 0x8a, 0x2e, 0x9c, 0x1b, 0xa2,
- 0x74, 0x83, 0xce, 0x0d, 0x91, 0x8e, 0x21, 0x87, 0xf6, 0x3d, 0x5e, 0xee, 0xc0, 0xdb, 0x0f, 0xd7,
- 0x82, 0x1d, 0xa8, 0xc9, 0xb0, 0x35, 0xd9, 0x35, 0xde, 0xcc, 0x61, 0xc7, 0xe1, 0x38, 0x32, 0x40,
- 0x4b, 0x37, 0x76, 0xef, 0x6c, 0x6f, 0xa3, 0x42, 0x27, 0xd7, 0xa1, 0xe1, 0x3a, 0x4b, 0xba, 0x65,
- 0xf7, 0x3d, 0x35, 0x33, 0xfc, 0x1a, 0x1b, 0xeb, 0xee, 0xa8, 0xc4, 0x07, 0x07, 0xd3, 0x17, 0xc2,
- 0x97, 0x44, 0x21, 0x31, 0xca, 0xa9, 0xfd, 0x9f, 0x12, 0xf0, 0x90, 0x25, 0xf2, 0x1a, 0x34, 0xba,
- 0xd4, 0xd8, 0xd1, 0x1d, 0xcb, 0x57, 0x47, 0x8c, 0xb2, 0xf5, 0x7d, 0x63, 0x4d, 0x25, 0x3e, 0x60,
- 0x55, 0x30, 0xdf, 0x5e, 0xe5, 0xbb, 0x02, 0x22, 0x5e, 0x62, 0x40, 0xb5, 0xe3, 0xfb, 0x7a, 0xcf,
- 0xca, 0xed, 0x31, 0x17, 0x47, 0x3a, 0x8a, 0x61, 0x40, 0x3c, 0xa3, 0x84, 0x26, 0x06, 0x54, 0x7a,
- 0xb6, 0x6e, 0x39, 0xb9, 0xaf, 0xd6, 0x61, 0x5f, 0xb0, 0xce, 0x90, 0x84, 0x51, 0x8b, 0x3f, 0xa2,
- 0xc0, 0x26, 0x7d, 0x68, 0xfa, 0x86, 0xa7, 0x77, 0xfd, 0x1d, 0x7d, 0xee, 0x95, 0x57, 0x73, 0xab,
- 0x89, 0x91, 0x28, 0x31, 0x6b, 0x2d, 0xe0, 0xfc, 0x5a, 0xfb, 0xc6, 0xfc, 0xdc, 0x2b, 0xaf, 0x62,
- 0x5c, 0x4e, 0x5c, 0xec, 0x2b, 0x2f, 0xcd, 0xc9, 0x9e, 0x7b, 0xe2, 0x62, 0x5f, 0x79, 0x69, 0x0e,
- 0xe3, 0x72, 0xb4, 0xff, 0x5d, 0x80, 0x46, 0xc8, 0x4b, 0x36, 0x01, 0xd8, 0x18, 0x22, 0x0f, 0x61,
- 0x3c, 0xd6, 0x85, 0x08, 0xdc, 0x2e, 0xb0, 0x19, 0x66, 0xc6, 0x18, 0x50, 0xc6, 0x29, 0x95, 0xc5,
- 0x93, 0x3e, 0xa5, 0x72, 0x16, 0x1a, 0x3b, 0xba, 0x63, 0xfa, 0x3b, 0xfa, 0xae, 0x18, 0x4a, 0x63,
- 0xe7, 0xb6, 0xde, 0x50, 0x04, 0x8c, 0x78, 0xb4, 0x7f, 0x56, 0x05, 0xe1, 0xe6, 0x66, 0x9d, 0xdd,
- 0xb4, 0x7c, 0x11, 0x67, 0x5d, 0xe0, 0x39, 0xc3, 0xce, 0xbe, 0x28, 0xd3, 0x31, 0xe4, 0x20, 0x17,
- 0xa1, 0xd4, 0xb5, 0x1c, 0xe9, 0xf3, 0xe1, 0x26, 0xbf, 0x35, 0xcb, 0x41, 0x96, 0xc6, 0x49, 0xfa,
- 0x7d, 0x19, 0x22, 0x27, 0x48, 0xfa, 0x7d, 0x64, 0x69, 0x6c, 0x11, 0x6e, 0xbb, 0xee, 0x2e, 0xeb,
- 0xb6, 0x2a, 0x92, 0xae, 0xcc, 0x55, 0x01, 0xbe, 0x08, 0x5f, 0x4d, 0x92, 0x30, 0xcd, 0x4b, 0x36,
- 0xe1, 0x99, 0x0f, 0xa8, 0xe7, 0xca, 0x71, 0xaa, 0x6d, 0x53, 0xda, 0x53, 0x30, 0x42, 0x89, 0xe2,
- 0x01, 0x79, 0xbf, 0x97, 0xcd, 0x82, 0xc3, 0xf2, 0xf2, 0xd0, 0x5e, 0xdd, 0xeb, 0xd0, 0x60, 0xdd,
- 0x73, 0x0d, 0xea, 0xfb, 0x96, 0xd3, 0x51, 0xb0, 0xd5, 0x08, 0x76, 0x23, 0x9b, 0x05, 0x87, 0xe5,
- 0x25, 0x6f, 0xc3, 0x94, 0x20, 0x09, 0x75, 0x61, 0x7e, 0x4f, 0xb7, 0x6c, 0x7d, 0xcb, 0xb2, 0xd5,
- 0x8d, 0x74, 0xe3, 0xc2, 0xb3, 0xb2, 0x31, 0x84, 0x07, 0x87, 0xe6, 0x26, 0x37, 0x61, 0x52, 0xf9,
- 0xd5, 0xd6, 0xa9, 0xd7, 0x0e, 0x43, 0x1f, 0xc6, 0x5b, 0x57, 0xd8, 0x8a, 0x77, 0x91, 0xf6, 0x3c,
- 0x6a, 0xc4, 0xbd, 0x89, 0x8a, 0x0b, 0x07, 0xf2, 0x11, 0x84, 0x0b, 0x3c, 0xbe, 0x61, 0xb3, 0xb7,
- 0xe0, 0xba, 0xb6, 0xe9, 0xde, 0x73, 0xd4, 0xb7, 0x0b, 0xd5, 0x8e, 0xbb, 0xd2, 0xda, 0x99, 0x1c,
- 0x38, 0x24, 0x27, 0xfb, 0x72, 0x4e, 0x59, 0x74, 0xef, 0x39, 0x69, 0x54, 0x88, 0xbe, 0xbc, 0x3d,
- 0x84, 0x07, 0x87, 0xe6, 0x26, 0x4b, 0x40, 0xd2, 0x5f, 0xb0, 0xd9, 0x93, 0xee, 0xd9, 0x0b, 0xe2,
- 0x3c, 0x95, 0x34, 0x15, 0x33, 0x72, 0x90, 0x55, 0x38, 0x9f, 0x4e, 0x65, 0xe2, 0xa4, 0x97, 0x96,
- 0x9f, 0xa4, 0x8a, 0x19, 0x74, 0xcc, 0xcc, 0xa5, 0xfd, 0xf3, 0x22, 0x8c, 0x27, 0x36, 0xe0, 0x3f,
- 0x71, 0x1b, 0x9d, 0x99, 0x0e, 0xde, 0xf5, 0x3b, 0x2b, 0x8b, 0x37, 0xa8, 0x6e, 0x52, 0xef, 0x16,
- 0x55, 0x87, 0x25, 0xf0, 0x41, 0x65, 0x2d, 0x41, 0xc1, 0x14, 0x27, 0xd9, 0x86, 0x8a, 0xb0, 0x28,
- 0xe7, 0xbd, 0x5a, 0x43, 0xd5, 0x11, 0x37, 0x2b, 0xcb, 0xfb, 0x68, 0x5c, 0x8f, 0xa2, 0x80, 0xd7,
- 0x02, 0x18, 0x8b, 0x73, 0xb0, 0x81, 0x24, 0x52, 0x37, 0x6b, 0x09, 0x55, 0x73, 0x05, 0x4a, 0x41,
- 0x30, 0xea, 0x16, 0x6a, 0xe1, 0xa1, 0xd8, 0x58, 0x45, 0x86, 0xa1, 0x6d, 0xb3, 0x7f, 0xe7, 0xfb,
- 0x96, 0xeb, 0xc8, 0xf3, 0xb4, 0x37, 0xa1, 0x16, 0x48, 0x23, 0xdd, 0x68, 0x5b, 0xc0, 0xb9, 0x8e,
- 0xa2, 0x0c, 0x74, 0x0a, 0x4b, 0xfb, 0x8f, 0x45, 0x68, 0x84, 0x0b, 0xea, 0x23, 0x9c, 0x53, 0xed,
- 0x42, 0x23, 0x8c, 0xcf, 0xca, 0x7d, 0x5b, 0x5f, 0x14, 0x36, 0xc4, 0xd7, 0x80, 0xe1, 0x2b, 0x46,
- 0x32, 0xe2, 0xb1, 0x5f, 0xa5, 0x1c, 0xb1, 0x5f, 0x3d, 0xa8, 0x05, 0x9e, 0xd5, 0xe9, 0x48, 0xed,
- 0x3c, 0x4f, 0xf0, 0x57, 0x58, 0x5d, 0x1b, 0x02, 0x50, 0xd6, 0xac, 0x78, 0x41, 0x25, 0x46, 0x7b,
- 0x0f, 0x26, 0xd3, 0x9c, 0x5c, 0x75, 0x35, 0x76, 0xa8, 0xd9, 0xb7, 0x55, 0x1d, 0x47, 0xaa, 0xab,
- 0x4c, 0xc7, 0x90, 0x83, 0x2d, 0x7f, 0xd9, 0x6f, 0xfa, 0xc0, 0x75, 0x94, 0xfa, 0xc8, 0x57, 0x01,
- 0x1b, 0x32, 0x0d, 0x43, 0xaa, 0xf6, 0xdf, 0x4a, 0x70, 0x31, 0x32, 0x8b, 0xac, 0xe9, 0x8e, 0xde,
- 0x39, 0xc2, 0x15, 0x6d, 0x9f, 0x6f, 0xaa, 0x39, 0xee, 0x65, 0x03, 0xa5, 0x27, 0xe0, 0xb2, 0x81,
- 0xff, 0x5b, 0x04, 0x1e, 0x4b, 0x4a, 0xbe, 0x06, 0x63, 0x7a, 0xec, 0x76, 0x4e, 0xf9, 0x3b, 0xaf,
- 0xe7, 0xfe, 0x9d, 0x3c, 0x64, 0x35, 0x8c, 0x8d, 0x8a, 0xa7, 0x62, 0x42, 0x20, 0x71, 0xa1, 0xbe,
- 0xad, 0xdb, 0x36, 0xd3, 0x85, 0x72, 0xbb, 0x79, 0x12, 0xc2, 0x79, 0x33, 0x5f, 0x92, 0xd0, 0x18,
- 0x0a, 0x21, 0x1f, 0x16, 0x60, 0xdc, 0x8b, 0x2f, 0x93, 0xe4, 0x0f, 0xc9, 0x13, 0x04, 0x10, 0x43,
- 0x8b, 0x47, 0x62, 0xc5, 0xd7, 0x62, 0x49, 0x99, 0xda, 0x7f, 0x2d, 0xc0, 0x78, 0xdb, 0xb6, 0x4c,
- 0xcb, 0xe9, 0x9c, 0xe2, 0x5d, 0x07, 0x77, 0xa0, 0xe2, 0xdb, 0x96, 0x49, 0x47, 0x9c, 0x4d, 0xc4,
- 0x3c, 0xc6, 0x00, 0x50, 0xe0, 0x24, 0x2f, 0x4f, 0x28, 0x1d, 0xe1, 0xf2, 0x84, 0x5f, 0x54, 0x41,
- 0x46, 0x45, 0x93, 0x3e, 0x34, 0x3a, 0xea, 0x4c, 0x76, 0xf9, 0x8d, 0x37, 0x72, 0x9c, 0xe7, 0x97,
- 0x38, 0xdd, 0x5d, 0x8c, 0xfd, 0x61, 0x22, 0x46, 0x92, 0x08, 0x4d, 0x5e, 0x0b, 0xbb, 0x98, 0xf3,
- 0x5a, 0x58, 0x21, 0x6e, 0xf0, 0x62, 0x58, 0x1d, 0xca, 0x3b, 0x41, 0xd0, 0x93, 0x8d, 0x69, 0xf4,
- 0xb0, 0xf7, 0xe8, 0x48, 0x19, 0xa1, 0x13, 0xb1, 0x77, 0xe4, 0xd0, 0x4c, 0x84, 0xa3, 0x87, 0xd7,
- 0x80, 0x2d, 0xe4, 0x0a, 0x38, 0x88, 0x8b, 0x60, 0xef, 0xc8, 0xa1, 0xc9, 0x1f, 0x40, 0x33, 0xf0,
- 0x74, 0xc7, 0xdf, 0x76, 0xbd, 0x2e, 0xf5, 0xe4, 0x1a, 0x75, 0x29, 0xc7, 0xcd, 0xa8, 0x1b, 0x11,
- 0x9a, 0xf0, 0x64, 0x26, 0x92, 0x30, 0x2e, 0x8d, 0xec, 0x42, 0xbd, 0x6f, 0x8a, 0x82, 0x49, 0xf3,
- 0xd3, 0x7c, 0x9e, 0xcb, 0x6e, 0x63, 0xe1, 0x04, 0xea, 0x0d, 0x43, 0x01, 0xc9, 0x1b, 0xef, 0x6a,
- 0x27, 0x75, 0xe3, 0x5d, 0xbc, 0x35, 0x66, 0x9d, 0x77, 0x41, 0xba, 0x52, 0xaf, 0x75, 0x3a, 0x32,
- 0x1a, 0x6a, 0x29, 0xb7, 0xca, 0x29, 0x44, 0x36, 0x43, 0xdd, 0xd8, 0xe9, 0xa0, 0x92, 0xa1, 0x75,
- 0x41, 0x7a, 0x19, 0x88, 0x91, 0xb8, 0x17, 0x46, 0x6c, 0xc2, 0x9a, 0x3d, 0xda, 0x78, 0x10, 0x5e,
- 0x50, 0x12, 0x3b, 0x97, 0x3a, 0xf3, 0x02, 0x18, 0xed, 0x3f, 0x15, 0xa1, 0xb4, 0xb1, 0xda, 0x16,
- 0x67, 0x4d, 0xf2, 0x4b, 0x97, 0x68, 0x7b, 0xd7, 0xea, 0xdd, 0xa5, 0x9e, 0xb5, 0xbd, 0x2f, 0x97,
- 0xde, 0xb1, 0xb3, 0x26, 0xd3, 0x1c, 0x98, 0x91, 0x8b, 0xbc, 0x03, 0x63, 0x86, 0xbe, 0x40, 0xbd,
- 0x60, 0x14, 0xc3, 0x02, 0xdf, 0x6d, 0xba, 0x30, 0x1f, 0x65, 0xc7, 0x04, 0x18, 0xd9, 0x04, 0x30,
- 0x22, 0xe8, 0xd2, 0xb1, 0xcd, 0x21, 0x31, 0xe0, 0x18, 0x10, 0x41, 0x68, 0xec, 0x32, 0x56, 0x8e,
- 0x5a, 0x3e, 0x0e, 0x2a, 0x6f, 0x39, 0xb7, 0x54, 0x5e, 0x8c, 0x60, 0x34, 0x07, 0xc6, 0x13, 0x97,
- 0xc5, 0x90, 0x2f, 0x42, 0xdd, 0xed, 0xc5, 0x86, 0xd3, 0x06, 0x8f, 0xbb, 0xac, 0xdf, 0x91, 0x69,
- 0x0f, 0x0e, 0xa6, 0xc7, 0x57, 0xdd, 0x8e, 0x65, 0xa8, 0x04, 0x0c, 0xd9, 0x89, 0x06, 0x55, 0xbe,
- 0x45, 0x4c, 0x5d, 0x15, 0xc3, 0xe7, 0x0e, 0x7e, 0x9b, 0x83, 0x8f, 0x92, 0xa2, 0xfd, 0x61, 0x19,
- 0x22, 0xdf, 0x1c, 0xf1, 0xa1, 0x2a, 0x42, 0xe0, 0xe5, 0xc8, 0x7d, 0xaa, 0xd1, 0xf6, 0x52, 0x14,
- 0xe9, 0x40, 0xe9, 0x3d, 0x77, 0x2b, 0xf7, 0xc0, 0x1d, 0xdb, 0x1b, 0x2e, 0x6c, 0x65, 0xb1, 0x04,
- 0x64, 0x12, 0xc8, 0xdf, 0x2e, 0xc0, 0x59, 0x3f, 0xad, 0xfa, 0xca, 0xe6, 0x80, 0xf9, 0x75, 0xfc,
- 0xb4, 0x32, 0x2d, 0x03, 0x64, 0x87, 0x91, 0x71, 0xb0, 0x2c, 0xac, 0xfe, 0x85, 0xd3, 0x4c, 0x36,
- 0xa7, 0xe5, 0x9c, 0x17, 0x1c, 0x26, 0xeb, 0x3f, 0x99, 0x86, 0x52, 0x94, 0xf6, 0xc7, 0x45, 0x68,
- 0xc6, 0x46, 0xeb, 0xdc, 0x37, 0x10, 0xdd, 0x4f, 0xdd, 0x40, 0xb4, 0x3e, 0xba, 0x0f, 0x39, 0x2a,
- 0xd5, 0x69, 0x5f, 0x42, 0xf4, 0xaf, 0x8a, 0x50, 0xda, 0x5c, 0x5c, 0x4a, 0x2e, 0x5a, 0x0b, 0x8f,
- 0x61, 0xd1, 0xba, 0x03, 0xb5, 0xad, 0xbe, 0x65, 0x07, 0x96, 0x93, 0xfb, 0xf4, 0x0a, 0x75, 0x61,
- 0x93, 0xf4, 0x31, 0x08, 0x54, 0x54, 0xf0, 0xa4, 0x03, 0xb5, 0x8e, 0x38, 0x3e, 0x30, 0x77, 0x64,
- 0x9d, 0x3c, 0x86, 0x50, 0x08, 0x92, 0x2f, 0xa8, 0xd0, 0xb5, 0x7d, 0x90, 0x37, 0xcf, 0x3f, 0xf6,
- 0xda, 0xd4, 0xfe, 0x00, 0x42, 0x2d, 0xe0, 0xf1, 0x0b, 0xff, 0x1f, 0x05, 0x48, 0x2a, 0x3e, 0x8f,
- 0xbf, 0x35, 0xed, 0xa6, 0x5b, 0xd3, 0xe2, 0x49, 0x74, 0xbe, 0xec, 0x06, 0xa5, 0xfd, 0xd3, 0x22,
- 0x54, 0x1f, 0xdb, 0x8e, 0x63, 0x9a, 0x08, 0x12, 0x5c, 0xc8, 0x39, 0x30, 0x0e, 0x0d, 0x11, 0xec,
- 0xa6, 0x42, 0x04, 0xf3, 0x5e, 0x31, 0xfb, 0x88, 0x00, 0xc1, 0x7f, 0x57, 0x00, 0x39, 0x2c, 0xaf,
- 0x38, 0x7e, 0xa0, 0x3b, 0x06, 0x25, 0x46, 0x38, 0x07, 0xe4, 0x8d, 0x44, 0x91, 0xd1, 0x5a, 0x62,
- 0xda, 0xe7, 0xcf, 0x6a, 0xcc, 0x27, 0x5f, 0x80, 0xfa, 0x8e, 0xeb, 0x07, 0x7c, 0x9c, 0x2f, 0x26,
- 0xad, 0x4b, 0x37, 0x64, 0x3a, 0x86, 0x1c, 0x69, 0x8f, 0x6b, 0x65, 0xb8, 0xc7, 0x55, 0xfb, 0x56,
- 0x11, 0xc6, 0x3e, 0x2b, 0xdb, 0xa6, 0xb3, 0x42, 0x2a, 0x4b, 0x39, 0x43, 0x2a, 0xcb, 0xc7, 0x09,
- 0xa9, 0xd4, 0x7e, 0x58, 0x00, 0x78, 0x6c, 0x7b, 0xb6, 0xcd, 0x64, 0xb4, 0x63, 0xee, 0x76, 0x95,
- 0x1d, 0xeb, 0xf8, 0x8f, 0x2b, 0xea, 0x93, 0x78, 0xa4, 0xe3, 0x47, 0x05, 0x98, 0xd0, 0x13, 0xd1,
- 0x83, 0xb9, 0x55, 0xcb, 0x54, 0x30, 0x62, 0x18, 0xfc, 0x92, 0x4c, 0xc7, 0x94, 0x58, 0xf2, 0x7a,
- 0x74, 0x5e, 0xf0, 0xed, 0xa8, 0xd9, 0x0f, 0x1c, 0xf4, 0xcb, 0xd5, 0x9c, 0x04, 0xe7, 0x23, 0xa2,
- 0x35, 0x4b, 0x27, 0x12, 0xad, 0x19, 0xdf, 0x87, 0x56, 0x7e, 0xe8, 0x3e, 0xb4, 0x3d, 0x68, 0x6c,
- 0x7b, 0x6e, 0x97, 0x07, 0x44, 0xca, 0xcb, 0x69, 0xaf, 0xe7, 0x98, 0x53, 0xa2, 0x6b, 0xd9, 0x23,
- 0x1b, 0xcf, 0x92, 0xc2, 0xc7, 0x48, 0x14, 0x37, 0x8b, 0xbb, 0x42, 0x6a, 0xf5, 0x24, 0xa5, 0x86,
- 0x63, 0xc9, 0x86, 0x40, 0x47, 0x25, 0x26, 0x19, 0x04, 0x59, 0x7b, 0x3c, 0x41, 0x90, 0xda, 0x77,
- 0xab, 0x6a, 0x00, 0x7b, 0xe2, 0x8e, 0xa6, 0xfc, 0xec, 0xef, 0xf5, 0x4d, 0x6f, 0xc4, 0xad, 0x3d,
- 0xc6, 0x8d, 0xb8, 0xf5, 0x93, 0xd9, 0x88, 0xdb, 0xc8, 0xb7, 0x11, 0x17, 0xf2, 0x6f, 0xc4, 0x6d,
- 0xe6, 0xdb, 0x88, 0x3b, 0x36, 0xd2, 0x46, 0xdc, 0xf1, 0x23, 0x6d, 0xc4, 0x3d, 0x28, 0x41, 0x6a,
- 0x95, 0xf9, 0xb9, 0x47, 0xe9, 0xff, 0x2b, 0x8f, 0xd2, 0xc7, 0x45, 0x88, 0x86, 0xcd, 0x63, 0x46,
- 0xdc, 0xbc, 0x0d, 0xf5, 0xae, 0x7e, 0x7f, 0x91, 0xda, 0xfa, 0x7e, 0x9e, 0xfb, 0x57, 0xd7, 0x24,
- 0x06, 0x86, 0x68, 0xc4, 0x07, 0xb0, 0xc2, 0x33, 0xd0, 0x73, 0xdb, 0xe6, 0xa3, 0xe3, 0xd4, 0x85,
- 0xf5, 0x2f, 0x7a, 0xc7, 0x98, 0x18, 0xed, 0xdf, 0x16, 0x41, 0x1e, 0x96, 0x4f, 0x28, 0x54, 0xb6,
- 0xad, 0xfb, 0xd4, 0xcc, 0x1d, 0x3f, 0x1b, 0xbb, 0x15, 0x5b, 0x38, 0x1f, 0x78, 0x02, 0x0a, 0x74,
- 0x6e, 0x55, 0x16, 0xce, 0x24, 0x59, 0x7f, 0x39, 0xac, 0xca, 0x71, 0xa7, 0x94, 0xb4, 0x2a, 0x8b,
- 0x24, 0x54, 0x32, 0x84, 0x11, 0x9b, 0xc7, 0x15, 0xe4, 0xf6, 0x9d, 0x25, 0xe2, 0x13, 0x94, 0x11,
- 0xdb, 0x17, 0x3b, 0xf1, 0xa5, 0x8c, 0xd6, 0xef, 0xff, 0xe0, 0x47, 0x57, 0x9e, 0xfa, 0xe1, 0x8f,
- 0xae, 0x3c, 0xf5, 0xc9, 0x8f, 0xae, 0x3c, 0xf5, 0x87, 0x87, 0x57, 0x0a, 0x3f, 0x38, 0xbc, 0x52,
- 0xf8, 0xe1, 0xe1, 0x95, 0xc2, 0x27, 0x87, 0x57, 0x0a, 0xff, 0xf9, 0xf0, 0x4a, 0xe1, 0x6f, 0xfc,
- 0x97, 0x2b, 0x4f, 0xfd, 0xde, 0x6b, 0x51, 0x11, 0x66, 0x55, 0x11, 0x66, 0x95, 0xc0, 0xd9, 0xde,
- 0x6e, 0x67, 0x96, 0x15, 0x21, 0x4a, 0x51, 0x45, 0xf8, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6c,
- 0x98, 0x3b, 0x47, 0x90, 0x97, 0x00, 0x00,
+ // 7907 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x25, 0x47,
+ 0x76, 0x9e, 0xee, 0xff, 0xbd, 0xe7, 0xf2, 0x6f, 0x6a, 0x7e, 0xc4, 0x19, 0x8d, 0x86, 0xe3, 0x96,
+ 0x25, 0x8f, 0x63, 0x9b, 0x8c, 0x68, 0xfd, 0xad, 0xed, 0x5d, 0x89, 0x97, 0x1c, 0x72, 0x38, 0x43,
+ 0xce, 0x70, 0xcf, 0x25, 0x47, 0x5a, 0x2b, 0x5e, 0xa5, 0xd9, 0x5d, 0xbc, 0x6c, 0xb1, 0x6f, 0xf7,
+ 0x55, 0x77, 0x5f, 0xce, 0x50, 0x4e, 0xb0, 0xb6, 0x95, 0x40, 0x0a, 0x82, 0x20, 0x81, 0x9f, 0x0c,
+ 0x04, 0x4e, 0x90, 0x20, 0x80, 0x1f, 0x0c, 0xe7, 0x21, 0xc0, 0xe6, 0xc1, 0x40, 0xe2, 0x38, 0x08,
+ 0x92, 0x4d, 0x90, 0x9f, 0x45, 0x10, 0x20, 0xca, 0x0b, 0x91, 0x65, 0x90, 0x87, 0x04, 0x88, 0x61,
+ 0xc4, 0x48, 0xec, 0x0c, 0x16, 0xd9, 0xa0, 0xfe, 0xfa, 0xef, 0xf6, 0x9d, 0x21, 0x6f, 0x93, 0xa3,
+ 0x51, 0xac, 0xb7, 0xee, 0x3a, 0xa7, 0xbe, 0x53, 0x55, 0x5d, 0x5d, 0x75, 0xea, 0x9c, 0x53, 0x55,
+ 0xb0, 0xd2, 0xb1, 0x82, 0xdd, 0xfe, 0xf6, 0xac, 0xe1, 0x76, 0xe7, 0x9c, 0x7e, 0x57, 0xef, 0x79,
+ 0xee, 0x87, 0xfc, 0x61, 0xc7, 0x76, 0x1f, 0xcc, 0xf5, 0xf6, 0x3a, 0x73, 0x7a, 0xcf, 0xf2, 0xa3,
+ 0x94, 0xfd, 0x57, 0x75, 0xbb, 0xb7, 0xab, 0xbf, 0x3a, 0xd7, 0xa1, 0x0e, 0xf5, 0xf4, 0x80, 0x9a,
+ 0xb3, 0x3d, 0xcf, 0x0d, 0x5c, 0xf2, 0x66, 0x04, 0x34, 0xab, 0x80, 0x66, 0x55, 0xb6, 0xd9, 0xde,
+ 0x5e, 0x67, 0x96, 0x01, 0x45, 0x29, 0x0a, 0xe8, 0xca, 0xcf, 0xc4, 0x4a, 0xd0, 0x71, 0x3b, 0xee,
+ 0x1c, 0xc7, 0xdb, 0xee, 0xef, 0xf0, 0x37, 0xfe, 0xc2, 0x9f, 0x84, 0x9c, 0x2b, 0xda, 0xde, 0x5b,
+ 0xfe, 0xac, 0xe5, 0xb2, 0x62, 0xcd, 0x19, 0xae, 0x47, 0xe7, 0xf6, 0x07, 0xca, 0x72, 0xe5, 0xb5,
+ 0x88, 0xa7, 0xab, 0x1b, 0xbb, 0x96, 0x43, 0xbd, 0x03, 0x55, 0x97, 0x39, 0x8f, 0xfa, 0x6e, 0xdf,
+ 0x33, 0xe8, 0x89, 0x72, 0xf9, 0x73, 0x5d, 0x1a, 0xe8, 0x59, 0xb2, 0xe6, 0x86, 0xe5, 0xf2, 0xfa,
+ 0x4e, 0x60, 0x75, 0x07, 0xc5, 0xbc, 0xf1, 0xa4, 0x0c, 0xbe, 0xb1, 0x4b, 0xbb, 0xfa, 0x40, 0xbe,
+ 0x9f, 0x1d, 0x96, 0xaf, 0x1f, 0x58, 0xf6, 0x9c, 0xe5, 0x04, 0x7e, 0xe0, 0xa5, 0x33, 0x69, 0xbf,
+ 0x0f, 0x70, 0x7e, 0x61, 0xdb, 0x0f, 0x3c, 0xdd, 0x08, 0x36, 0x5c, 0x73, 0x93, 0x76, 0x7b, 0xb6,
+ 0x1e, 0x50, 0xb2, 0x07, 0x75, 0x56, 0x21, 0x53, 0x0f, 0xf4, 0xe9, 0xc2, 0xf5, 0xc2, 0x8d, 0xe6,
+ 0xfc, 0xc2, 0xec, 0x88, 0x1f, 0x70, 0x76, 0x5d, 0x02, 0xb5, 0xc6, 0x8e, 0x0e, 0x67, 0xea, 0xea,
+ 0x0d, 0x43, 0x01, 0xe4, 0x37, 0x0a, 0x30, 0xe6, 0xb8, 0x26, 0x6d, 0x53, 0x9b, 0x1a, 0x81, 0xeb,
+ 0x4d, 0x17, 0xaf, 0x97, 0x6e, 0x34, 0xe7, 0xbf, 0x3d, 0xb2, 0xc4, 0x8c, 0x1a, 0xcd, 0xde, 0x8d,
+ 0x09, 0xb8, 0xe9, 0x04, 0xde, 0x41, 0xeb, 0xc2, 0xf7, 0x0e, 0x67, 0x9e, 0x3b, 0x3a, 0x9c, 0x19,
+ 0x8b, 0x93, 0x30, 0x51, 0x12, 0xb2, 0x05, 0xcd, 0xc0, 0xb5, 0x59, 0x93, 0x59, 0xae, 0xe3, 0x4f,
+ 0x97, 0x78, 0xc1, 0xae, 0xcd, 0x8a, 0xa6, 0x66, 0xe2, 0x67, 0x59, 0x1f, 0x9b, 0xdd, 0x7f, 0x75,
+ 0x76, 0x33, 0x64, 0x6b, 0x9d, 0x97, 0xc0, 0xcd, 0x28, 0xcd, 0xc7, 0x38, 0x0e, 0xa1, 0x30, 0xe9,
+ 0x53, 0xa3, 0xef, 0x59, 0xc1, 0xc1, 0xa2, 0xeb, 0x04, 0xf4, 0x61, 0x30, 0x5d, 0xe6, 0xad, 0xfc,
+ 0x4a, 0x16, 0xf4, 0x86, 0x6b, 0xb6, 0x93, 0xdc, 0xad, 0xf3, 0x47, 0x87, 0x33, 0x93, 0xa9, 0x44,
+ 0x4c, 0x63, 0x12, 0x07, 0xa6, 0xac, 0xae, 0xde, 0xa1, 0x1b, 0x7d, 0xdb, 0x6e, 0x53, 0xc3, 0xa3,
+ 0x81, 0x3f, 0x5d, 0xe1, 0x55, 0xb8, 0x91, 0x25, 0x67, 0xcd, 0x35, 0x74, 0xfb, 0xde, 0xf6, 0x87,
+ 0xd4, 0x08, 0x90, 0xee, 0x50, 0x8f, 0x3a, 0x06, 0x6d, 0x4d, 0xcb, 0xca, 0x4c, 0xad, 0xa6, 0x90,
+ 0x70, 0x00, 0x9b, 0xac, 0xc0, 0xb9, 0x9e, 0x67, 0xb9, 0xbc, 0x08, 0xb6, 0xee, 0xfb, 0x77, 0xf5,
+ 0x2e, 0x9d, 0xae, 0x5e, 0x2f, 0xdc, 0x68, 0xb4, 0x2e, 0x4b, 0x98, 0x73, 0x1b, 0x69, 0x06, 0x1c,
+ 0xcc, 0x43, 0x6e, 0x40, 0x5d, 0x25, 0x4e, 0xd7, 0xae, 0x17, 0x6e, 0x54, 0x44, 0xdf, 0x51, 0x79,
+ 0x31, 0xa4, 0x92, 0x65, 0xa8, 0xeb, 0x3b, 0x3b, 0x96, 0xc3, 0x38, 0xeb, 0xbc, 0x09, 0xaf, 0x66,
+ 0x55, 0x6d, 0x41, 0xf2, 0x08, 0x1c, 0xf5, 0x86, 0x61, 0x5e, 0x72, 0x1b, 0x88, 0x4f, 0xbd, 0x7d,
+ 0xcb, 0xa0, 0x0b, 0x86, 0xe1, 0xf6, 0x9d, 0x80, 0x97, 0xbd, 0xc1, 0xcb, 0x7e, 0x45, 0x96, 0x9d,
+ 0xb4, 0x07, 0x38, 0x30, 0x23, 0x17, 0x79, 0x07, 0xa6, 0xe4, 0xbf, 0x1a, 0xb5, 0x02, 0x70, 0xa4,
+ 0x0b, 0xac, 0x21, 0x31, 0x45, 0xc3, 0x01, 0x6e, 0x62, 0xc2, 0x55, 0xbd, 0x1f, 0xb8, 0x5d, 0x06,
+ 0x99, 0x14, 0xba, 0xe9, 0xee, 0x51, 0x67, 0xba, 0x79, 0xbd, 0x70, 0xa3, 0xde, 0xba, 0x7e, 0x74,
+ 0x38, 0x73, 0x75, 0xe1, 0x31, 0x7c, 0xf8, 0x58, 0x14, 0x72, 0x0f, 0x1a, 0xa6, 0xe3, 0x6f, 0xb8,
+ 0xb6, 0x65, 0x1c, 0x4c, 0x8f, 0xf1, 0x02, 0xbe, 0x2a, 0xab, 0xda, 0x58, 0xba, 0xdb, 0x16, 0x84,
+ 0x47, 0x87, 0x33, 0x57, 0x07, 0x87, 0xd4, 0xd9, 0x90, 0x8e, 0x11, 0x06, 0x59, 0xe7, 0x80, 0x8b,
+ 0xae, 0xb3, 0x63, 0x75, 0xa6, 0xc7, 0xf9, 0xd7, 0xb8, 0x3e, 0xa4, 0x43, 0x2f, 0xdd, 0x6d, 0x0b,
+ 0xbe, 0xd6, 0xb8, 0x14, 0x27, 0x5e, 0x31, 0x42, 0x20, 0x26, 0x4c, 0xa8, 0xc1, 0x78, 0xd1, 0xd6,
+ 0xad, 0xae, 0x3f, 0x3d, 0xc1, 0x3b, 0xef, 0x8f, 0x0f, 0xc1, 0xc4, 0x38, 0x73, 0xeb, 0x92, 0xac,
+ 0xca, 0x44, 0x22, 0xd9, 0xc7, 0x14, 0xe6, 0x95, 0xb7, 0xe1, 0xdc, 0xc0, 0xd8, 0x40, 0xa6, 0xa0,
+ 0xb4, 0x47, 0x0f, 0xf8, 0xd0, 0xd7, 0x40, 0xf6, 0x48, 0x2e, 0x40, 0x65, 0x5f, 0xb7, 0xfb, 0x74,
+ 0xba, 0xc8, 0xd3, 0xc4, 0xcb, 0xcf, 0x15, 0xdf, 0x2a, 0x68, 0x7f, 0xb7, 0x04, 0x63, 0x6a, 0xc4,
+ 0x69, 0x5b, 0xce, 0x1e, 0x79, 0x17, 0x4a, 0xb6, 0xdb, 0x91, 0xe3, 0xe6, 0x2f, 0x8c, 0x3c, 0x8a,
+ 0xad, 0xb9, 0x9d, 0x56, 0xed, 0xe8, 0x70, 0xa6, 0xb4, 0xe6, 0x76, 0x90, 0x21, 0x12, 0x03, 0x2a,
+ 0x7b, 0xfa, 0xce, 0x9e, 0xce, 0xcb, 0xd0, 0x9c, 0x6f, 0x8d, 0x0c, 0x7d, 0x87, 0xa1, 0xb0, 0xb2,
+ 0xb6, 0x1a, 0x47, 0x87, 0x33, 0x15, 0xfe, 0x8a, 0x02, 0x9b, 0xb8, 0xd0, 0xd8, 0xb6, 0x75, 0x63,
+ 0x6f, 0xd7, 0xb5, 0xe9, 0x74, 0x29, 0xa7, 0xa0, 0x96, 0x42, 0x12, 0x9f, 0x39, 0x7c, 0xc5, 0x48,
+ 0x06, 0x31, 0xa0, 0xda, 0x37, 0x7d, 0xcb, 0xd9, 0x93, 0x63, 0xe0, 0xdb, 0x23, 0x4b, 0xdb, 0x5a,
+ 0xe2, 0x75, 0x82, 0xa3, 0xc3, 0x99, 0xaa, 0x78, 0x46, 0x09, 0xad, 0xfd, 0x41, 0x13, 0x26, 0xd4,
+ 0x47, 0xba, 0x4f, 0xbd, 0x80, 0x3e, 0x24, 0xd7, 0xa1, 0xec, 0xb0, 0x5f, 0x93, 0x7f, 0xe4, 0xd6,
+ 0x98, 0xec, 0x2e, 0x65, 0xfe, 0x4b, 0x72, 0x0a, 0x2b, 0x99, 0xe8, 0x2a, 0xb2, 0xc1, 0x47, 0x2f,
+ 0x59, 0x9b, 0xc3, 0x88, 0x92, 0x89, 0x67, 0x94, 0xd0, 0xe4, 0x7d, 0x28, 0xf3, 0xca, 0x8b, 0xa6,
+ 0xfe, 0xfa, 0xe8, 0x22, 0x58, 0xd5, 0xeb, 0xac, 0x06, 0xbc, 0xe2, 0x1c, 0x94, 0x75, 0xc5, 0xbe,
+ 0xb9, 0x23, 0x1b, 0xf6, 0x17, 0x72, 0x34, 0xec, 0xb2, 0xe8, 0x8a, 0x5b, 0x4b, 0xcb, 0xc8, 0x10,
+ 0xc9, 0x5f, 0x2f, 0xc0, 0x39, 0xc3, 0x75, 0x02, 0x9d, 0xe9, 0x19, 0x6a, 0x92, 0x9d, 0xae, 0x70,
+ 0x39, 0xb7, 0x47, 0x96, 0xb3, 0x98, 0x46, 0x6c, 0x5d, 0x64, 0x73, 0xc6, 0x40, 0x32, 0x0e, 0xca,
+ 0x26, 0x7f, 0xb3, 0x00, 0x17, 0xd9, 0x58, 0x3e, 0xc0, 0xcc, 0x67, 0xa0, 0xd3, 0x2d, 0xd5, 0xe5,
+ 0xa3, 0xc3, 0x99, 0x8b, 0xab, 0x59, 0xc2, 0x30, 0xbb, 0x0c, 0xac, 0x74, 0xe7, 0xf5, 0x41, 0xb5,
+ 0x84, 0xcf, 0x6e, 0xcd, 0xf9, 0xb5, 0xd3, 0x54, 0x75, 0x5a, 0x2f, 0xc8, 0xae, 0x9c, 0xa5, 0xd9,
+ 0x61, 0x56, 0x29, 0xc8, 0x4d, 0xa8, 0xed, 0xbb, 0x76, 0xbf, 0x4b, 0xfd, 0xe9, 0x3a, 0x1f, 0x62,
+ 0xaf, 0x64, 0x0d, 0xb1, 0xf7, 0x39, 0x4b, 0x6b, 0x52, 0xc2, 0xd7, 0xc4, 0xbb, 0x8f, 0x2a, 0x2f,
+ 0xb1, 0xa0, 0x6a, 0x5b, 0x5d, 0x2b, 0xf0, 0xf9, 0xc4, 0xd9, 0x9c, 0xbf, 0x39, 0x72, 0xb5, 0xc4,
+ 0x2f, 0xba, 0xc6, 0xc1, 0xc4, 0x5f, 0x23, 0x9e, 0x51, 0x0a, 0x60, 0x43, 0xa1, 0x6f, 0xe8, 0xb6,
+ 0x98, 0x58, 0x9b, 0xf3, 0xdf, 0x18, 0xfd, 0xb7, 0x61, 0x28, 0xad, 0x71, 0x59, 0xa7, 0x0a, 0x7f,
+ 0x45, 0x81, 0x4d, 0x7e, 0x09, 0x26, 0x12, 0x5f, 0xd3, 0x9f, 0x6e, 0xf2, 0xd6, 0x79, 0x31, 0xab,
+ 0x75, 0x42, 0xae, 0x68, 0xe6, 0x49, 0xf4, 0x10, 0x1f, 0x53, 0x60, 0xe4, 0x0e, 0xd4, 0x7d, 0xcb,
+ 0xa4, 0x86, 0xee, 0xf9, 0xd3, 0x63, 0xc7, 0x01, 0x9e, 0x92, 0xc0, 0xf5, 0xb6, 0xcc, 0x86, 0x21,
+ 0x00, 0x99, 0x05, 0xe8, 0xe9, 0x5e, 0x60, 0x09, 0x45, 0x75, 0x9c, 0x2b, 0x4d, 0x13, 0x47, 0x87,
+ 0x33, 0xb0, 0x11, 0xa6, 0x62, 0x8c, 0x83, 0xf1, 0xb3, 0xbc, 0xab, 0x4e, 0xaf, 0x1f, 0x88, 0x89,
+ 0xb5, 0x21, 0xf8, 0xdb, 0x61, 0x2a, 0xc6, 0x38, 0xc8, 0xef, 0x14, 0xe0, 0x85, 0xe8, 0x75, 0xf0,
+ 0x27, 0x9b, 0x3c, 0xf5, 0x9f, 0x6c, 0xe6, 0xe8, 0x70, 0xe6, 0x85, 0xf6, 0x70, 0x91, 0xf8, 0xb8,
+ 0xf2, 0x68, 0xef, 0xc2, 0xf8, 0x42, 0x3f, 0xd8, 0x75, 0x3d, 0xeb, 0x63, 0xae, 0x74, 0x93, 0x65,
+ 0xa8, 0x04, 0x5c, 0x79, 0x12, 0xf3, 0xf2, 0xcb, 0x59, 0x4d, 0x2d, 0x14, 0xd9, 0x3b, 0xf4, 0x40,
+ 0x69, 0x03, 0x62, 0x7e, 0x14, 0xca, 0x94, 0xc8, 0xae, 0xfd, 0xa5, 0x02, 0xd4, 0x5a, 0xba, 0xb1,
+ 0xe7, 0xee, 0xec, 0x90, 0xf7, 0xa0, 0x6e, 0x39, 0x01, 0xf5, 0xf6, 0x75, 0x5b, 0xc2, 0xce, 0xc6,
+ 0x60, 0xc3, 0x65, 0x58, 0x54, 0x6f, 0xb6, 0xe6, 0x61, 0x82, 0x96, 0xfa, 0x72, 0xad, 0xc0, 0xf5,
+ 0xd1, 0x55, 0x89, 0x81, 0x21, 0x1a, 0x99, 0x81, 0x8a, 0x1f, 0xd0, 0x9e, 0xcf, 0x67, 0x9e, 0x71,
+ 0x51, 0x8c, 0x36, 0x4b, 0x40, 0x91, 0xae, 0xfd, 0x9d, 0x02, 0x34, 0x5a, 0xba, 0x6f, 0x19, 0xac,
+ 0x96, 0x64, 0x11, 0xca, 0x7d, 0x9f, 0x7a, 0x27, 0xab, 0x1b, 0x9f, 0x2c, 0xb6, 0x7c, 0xea, 0x21,
+ 0xcf, 0x4c, 0xee, 0x41, 0xbd, 0xa7, 0xfb, 0xfe, 0x03, 0xd7, 0x33, 0xe5, 0x84, 0x77, 0x4c, 0x20,
+ 0xa1, 0x9c, 0xcb, 0xac, 0x18, 0x82, 0x68, 0x4d, 0x88, 0x66, 0x7c, 0xed, 0x8f, 0x0a, 0x70, 0xbe,
+ 0xd5, 0xdf, 0xd9, 0xa1, 0x9e, 0xd4, 0x45, 0xa5, 0x96, 0x47, 0xa1, 0xe2, 0x51, 0xd3, 0xf2, 0x65,
+ 0xd9, 0x97, 0x46, 0xee, 0x41, 0xc8, 0x50, 0xa4, 0x52, 0xc9, 0xdb, 0x8b, 0x27, 0xa0, 0x40, 0x27,
+ 0x7d, 0x68, 0x7c, 0x48, 0xd9, 0x1a, 0x98, 0xea, 0x5d, 0x59, 0xbb, 0x5b, 0x23, 0x8b, 0xba, 0x4d,
+ 0x83, 0x36, 0x47, 0x8a, 0xeb, 0xb0, 0x61, 0x22, 0x46, 0x92, 0xb4, 0xdf, 0xaf, 0xc0, 0xd8, 0xa2,
+ 0xdb, 0xdd, 0xb6, 0x1c, 0x6a, 0xde, 0x34, 0x3b, 0x94, 0x7c, 0x00, 0x65, 0x6a, 0x76, 0xa8, 0xac,
+ 0xed, 0xe8, 0xd3, 0x3d, 0x03, 0x8b, 0x94, 0x16, 0xf6, 0x86, 0x1c, 0x98, 0xac, 0xc1, 0xc4, 0x8e,
+ 0xe7, 0x76, 0xc5, 0x08, 0xba, 0x79, 0xd0, 0x93, 0x1a, 0x6b, 0xeb, 0xc7, 0xd5, 0xa8, 0xb4, 0x9c,
+ 0xa0, 0x3e, 0x3a, 0x9c, 0x81, 0xe8, 0x0d, 0x53, 0x79, 0xc9, 0x7b, 0x30, 0x1d, 0xa5, 0x84, 0x43,
+ 0xc9, 0x22, 0x5b, 0x44, 0x70, 0x8d, 0xa5, 0xd2, 0xba, 0x7a, 0x74, 0x38, 0x33, 0xbd, 0x3c, 0x84,
+ 0x07, 0x87, 0xe6, 0x26, 0x9f, 0x16, 0x60, 0x2a, 0x22, 0x8a, 0xe1, 0x5d, 0x2a, 0x2a, 0xa7, 0x34,
+ 0x6f, 0xf0, 0xd5, 0xd6, 0x72, 0x4a, 0x04, 0x0e, 0x08, 0x25, 0xcb, 0x30, 0x16, 0xb8, 0xb1, 0xf6,
+ 0xaa, 0xf0, 0xf6, 0xd2, 0x94, 0x79, 0x60, 0xd3, 0x1d, 0xda, 0x5a, 0x89, 0x7c, 0x04, 0xe1, 0x92,
+ 0x7a, 0x4f, 0xb5, 0x54, 0x95, 0xb7, 0xd4, 0x95, 0xa3, 0xc3, 0x99, 0x4b, 0x9b, 0x99, 0x1c, 0x38,
+ 0x24, 0x27, 0xf9, 0xd5, 0x02, 0x4c, 0x28, 0x92, 0x6c, 0xa3, 0xda, 0x69, 0xb6, 0x11, 0x61, 0x3d,
+ 0x62, 0x33, 0x21, 0x00, 0x53, 0x02, 0xb5, 0x3f, 0x29, 0x43, 0x23, 0x1c, 0x60, 0xc9, 0x4b, 0x50,
+ 0xe1, 0x0b, 0x7f, 0xa9, 0x37, 0x87, 0x33, 0x27, 0xb7, 0x0f, 0xa0, 0xa0, 0x91, 0x97, 0xa1, 0x66,
+ 0xb8, 0xdd, 0xae, 0xee, 0x98, 0xdc, 0x98, 0xd3, 0x68, 0x35, 0x99, 0xc2, 0xb0, 0x28, 0x92, 0x50,
+ 0xd1, 0xc8, 0x55, 0x28, 0xeb, 0x5e, 0x47, 0xd8, 0x55, 0x1a, 0x62, 0x3c, 0x5a, 0xf0, 0x3a, 0x3e,
+ 0xf2, 0x54, 0xf2, 0x35, 0x28, 0x51, 0x67, 0x7f, 0xba, 0x3c, 0x5c, 0x23, 0xb9, 0xe9, 0xec, 0xdf,
+ 0xd7, 0xbd, 0x56, 0x53, 0x96, 0xa1, 0x74, 0xd3, 0xd9, 0x47, 0x96, 0x87, 0xac, 0x41, 0x8d, 0x3a,
+ 0xfb, 0xec, 0xdb, 0x4b, 0x83, 0xc7, 0x8f, 0x0d, 0xc9, 0xce, 0x58, 0xa4, 0x72, 0x1e, 0xea, 0x35,
+ 0x32, 0x19, 0x15, 0x04, 0xf9, 0x16, 0x8c, 0x09, 0x15, 0x67, 0x9d, 0x7d, 0x13, 0x7f, 0xba, 0xca,
+ 0x21, 0x67, 0x86, 0xeb, 0x48, 0x9c, 0x2f, 0x32, 0x30, 0xc5, 0x12, 0x7d, 0x4c, 0x40, 0x91, 0x6f,
+ 0x41, 0x43, 0xad, 0x47, 0xd5, 0x97, 0xcd, 0xb4, 0xcd, 0xa8, 0x45, 0x2c, 0xd2, 0x8f, 0xfa, 0x96,
+ 0x47, 0xbb, 0xd4, 0x09, 0xfc, 0xd6, 0x39, 0xb5, 0x5a, 0x57, 0x54, 0x1f, 0x23, 0x34, 0xb2, 0x3d,
+ 0x68, 0x64, 0x12, 0x16, 0x92, 0x97, 0x86, 0x8c, 0xea, 0x23, 0x58, 0x98, 0xbe, 0x0d, 0x93, 0xa1,
+ 0x15, 0x48, 0x1a, 0x12, 0x84, 0xcd, 0xe4, 0x35, 0x96, 0x7d, 0x35, 0x49, 0x7a, 0x74, 0x38, 0xf3,
+ 0x62, 0x86, 0x29, 0x21, 0x62, 0xc0, 0x34, 0x98, 0xf6, 0x7b, 0x25, 0x18, 0xd4, 0xfe, 0x93, 0x8d,
+ 0x56, 0x38, 0xed, 0x46, 0x4b, 0x57, 0x48, 0x0c, 0x9f, 0x6f, 0xc9, 0x6c, 0xf9, 0x2b, 0x95, 0xf5,
+ 0x61, 0x4a, 0xa7, 0xfd, 0x61, 0x9e, 0x95, 0x7f, 0x47, 0xfb, 0xac, 0x0c, 0x13, 0x4b, 0x3a, 0xed,
+ 0xba, 0xce, 0x13, 0xd7, 0x42, 0x85, 0x67, 0x62, 0x2d, 0x74, 0x03, 0xea, 0x1e, 0xed, 0xd9, 0x96,
+ 0xa1, 0x0b, 0xe5, 0x4b, 0xda, 0x1e, 0x51, 0xa6, 0x61, 0x48, 0x1d, 0xb2, 0x06, 0x2e, 0x3d, 0x93,
+ 0x6b, 0xe0, 0xf2, 0x17, 0xbf, 0x06, 0xd6, 0x7e, 0xb5, 0x08, 0x5c, 0x51, 0x21, 0xd7, 0xa1, 0xcc,
+ 0x26, 0xe1, 0xb4, 0xe5, 0x85, 0x77, 0x1c, 0x4e, 0x21, 0x57, 0xa0, 0x18, 0xb8, 0xf2, 0xcf, 0x03,
+ 0x49, 0x2f, 0x6e, 0xba, 0x58, 0x0c, 0x5c, 0xf2, 0x31, 0x80, 0xe1, 0x3a, 0xa6, 0xa5, 0x4c, 0xf2,
+ 0xf9, 0x2a, 0xb6, 0xec, 0x7a, 0x0f, 0x74, 0xcf, 0x5c, 0x0c, 0x11, 0xc5, 0x2a, 0x28, 0x7a, 0xc7,
+ 0x98, 0x34, 0xf2, 0x36, 0x54, 0x5d, 0x67, 0xb9, 0x6f, 0xdb, 0xbc, 0x41, 0x1b, 0xad, 0x9f, 0x60,
+ 0x4b, 0xd3, 0x7b, 0x3c, 0xe5, 0xd1, 0xe1, 0xcc, 0x65, 0xa1, 0xdf, 0xb2, 0xb7, 0x77, 0x3d, 0x2b,
+ 0xb0, 0x9c, 0x4e, 0x3b, 0xf0, 0xf4, 0x80, 0x76, 0x0e, 0x50, 0x66, 0xd3, 0x7e, 0xbd, 0x00, 0xcd,
+ 0x65, 0xeb, 0x21, 0x35, 0xdf, 0xb5, 0x1c, 0xd3, 0x7d, 0x40, 0x10, 0xaa, 0x36, 0x75, 0x3a, 0xc1,
+ 0xee, 0x88, 0xeb, 0x07, 0xb1, 0x36, 0xe6, 0x08, 0x28, 0x91, 0xc8, 0x1c, 0x34, 0x84, 0xf6, 0x69,
+ 0x39, 0x1d, 0xde, 0x86, 0xf5, 0x68, 0xd0, 0x6b, 0x2b, 0x02, 0x46, 0x3c, 0xda, 0x01, 0x9c, 0x1b,
+ 0x68, 0x06, 0x62, 0x42, 0x39, 0xd0, 0x3b, 0x6a, 0x7c, 0x5d, 0x1e, 0xb9, 0x81, 0x37, 0xf5, 0x4e,
+ 0xac, 0x71, 0xf9, 0x1c, 0xbf, 0xa9, 0xb3, 0x39, 0x9e, 0xa1, 0x6b, 0x3f, 0x2c, 0x40, 0x7d, 0xb9,
+ 0xef, 0x18, 0x7c, 0x89, 0xf6, 0x64, 0x8b, 0x9c, 0x52, 0x18, 0x8a, 0x99, 0x0a, 0x43, 0x1f, 0xaa,
+ 0x7b, 0x0f, 0x42, 0x85, 0xa2, 0x39, 0xbf, 0x3e, 0x7a, 0xaf, 0x90, 0x45, 0x9a, 0xbd, 0xc3, 0xf1,
+ 0x84, 0xc3, 0x68, 0x42, 0x16, 0xa8, 0x7a, 0xe7, 0x5d, 0x2e, 0x54, 0x0a, 0xbb, 0xf2, 0x35, 0x68,
+ 0xc6, 0xd8, 0x4e, 0x64, 0x3b, 0xfe, 0x87, 0x65, 0xa8, 0xae, 0xb4, 0xdb, 0x0b, 0x1b, 0xab, 0xe4,
+ 0x75, 0x68, 0x4a, 0x5f, 0xc2, 0xdd, 0xa8, 0x0d, 0x42, 0x57, 0x52, 0x3b, 0x22, 0x61, 0x9c, 0x8f,
+ 0xa9, 0x63, 0x1e, 0xd5, 0xed, 0xae, 0xfc, 0x59, 0x42, 0x75, 0x0c, 0x59, 0x22, 0x0a, 0x1a, 0xd1,
+ 0x61, 0x82, 0xad, 0xf0, 0x58, 0x13, 0x8a, 0xd5, 0x9b, 0xfc, 0x6d, 0x8e, 0xb9, 0xbe, 0xe3, 0x4a,
+ 0xe2, 0x56, 0x02, 0x00, 0x53, 0x80, 0xe4, 0x2d, 0xa8, 0xeb, 0xfd, 0x60, 0x97, 0x2b, 0xd0, 0xe2,
+ 0xdf, 0xb8, 0xca, 0x5d, 0x2d, 0x32, 0xed, 0xd1, 0xe1, 0xcc, 0xd8, 0x1d, 0x6c, 0xbd, 0xae, 0xde,
+ 0x31, 0xe4, 0x66, 0x85, 0x53, 0x2b, 0x46, 0x59, 0xb8, 0xca, 0x89, 0x0b, 0xb7, 0x91, 0x00, 0xc0,
+ 0x14, 0x20, 0x79, 0x1f, 0xc6, 0xf6, 0xe8, 0x41, 0xa0, 0x6f, 0x4b, 0x01, 0xd5, 0x93, 0x08, 0x98,
+ 0x62, 0x2a, 0xdc, 0x9d, 0x58, 0x76, 0x4c, 0x80, 0x11, 0x1f, 0x2e, 0xec, 0x51, 0x6f, 0x9b, 0x7a,
+ 0xae, 0x5c, 0x7d, 0x4a, 0x21, 0xb5, 0x93, 0x08, 0x99, 0x3e, 0x3a, 0x9c, 0xb9, 0x70, 0x27, 0x03,
+ 0x06, 0x33, 0xc1, 0xb5, 0xff, 0x53, 0x84, 0xc9, 0x15, 0xe1, 0xcc, 0x75, 0x3d, 0x31, 0x09, 0x93,
+ 0xcb, 0x50, 0xf2, 0x7a, 0x7d, 0xde, 0x73, 0x4a, 0xc2, 0x5c, 0x8b, 0x1b, 0x5b, 0xc8, 0xd2, 0xc8,
+ 0x7b, 0x50, 0x37, 0xe5, 0x90, 0x21, 0x17, 0xbf, 0x23, 0x19, 0x2a, 0xd4, 0x1b, 0x86, 0x68, 0x4c,
+ 0xd3, 0xef, 0xfa, 0x9d, 0xb6, 0xf5, 0x31, 0x95, 0xeb, 0x41, 0xae, 0xe9, 0xaf, 0x8b, 0x24, 0x54,
+ 0x34, 0x36, 0xab, 0xee, 0xd1, 0x03, 0xb1, 0x1a, 0x2a, 0x47, 0xb3, 0xea, 0x1d, 0x99, 0x86, 0x21,
+ 0x95, 0xcc, 0xa8, 0x9f, 0x85, 0xf5, 0x82, 0xb2, 0x58, 0xc9, 0xdf, 0x67, 0x09, 0xf2, 0xbf, 0x61,
+ 0x43, 0xe6, 0x87, 0x56, 0x10, 0x50, 0x4f, 0x7e, 0xc6, 0x91, 0x86, 0xcc, 0xdb, 0x1c, 0x01, 0x25,
+ 0x12, 0xf9, 0x29, 0x68, 0x70, 0xf0, 0x96, 0xed, 0x6e, 0xf3, 0x0f, 0xd7, 0x10, 0x6b, 0xfa, 0xfb,
+ 0x2a, 0x11, 0x23, 0xba, 0xf6, 0xa3, 0x22, 0x5c, 0x5a, 0xa1, 0x81, 0xd0, 0x6a, 0x96, 0x68, 0xcf,
+ 0x76, 0x0f, 0x98, 0x6a, 0x89, 0xf4, 0x23, 0xf2, 0x0e, 0x80, 0xe5, 0x6f, 0xb7, 0xf7, 0x0d, 0xfe,
+ 0x1f, 0x88, 0x7f, 0xf8, 0xba, 0xfc, 0x25, 0x61, 0xb5, 0xdd, 0x92, 0x94, 0x47, 0x89, 0x37, 0x8c,
+ 0xe5, 0x89, 0x96, 0x57, 0xc5, 0xc7, 0x2c, 0xaf, 0xda, 0x00, 0xbd, 0x48, 0x41, 0x2d, 0x71, 0xce,
+ 0x9f, 0x55, 0x62, 0x4e, 0xa2, 0x9b, 0xc6, 0x60, 0xf2, 0xa8, 0x8c, 0x0e, 0x4c, 0x99, 0x74, 0x47,
+ 0xef, 0xdb, 0x41, 0xa8, 0x54, 0xcb, 0x9f, 0xf8, 0xf8, 0x7a, 0x79, 0xe8, 0x68, 0x5e, 0x4a, 0x21,
+ 0xe1, 0x00, 0xb6, 0xf6, 0xbb, 0x25, 0xb8, 0xb2, 0x42, 0x83, 0xd0, 0xe2, 0x22, 0x47, 0xc7, 0x76,
+ 0x8f, 0x1a, 0xec, 0x2b, 0x7c, 0x5a, 0x80, 0xaa, 0xad, 0x6f, 0x53, 0x9b, 0xcd, 0x5e, 0xac, 0x36,
+ 0x1f, 0x8c, 0x3c, 0x11, 0x0c, 0x97, 0x32, 0xbb, 0xc6, 0x25, 0xa4, 0xa6, 0x06, 0x91, 0x88, 0x52,
+ 0x3c, 0x1b, 0xd4, 0x0d, 0xbb, 0xef, 0x07, 0xd4, 0xdb, 0x70, 0xbd, 0x40, 0xea, 0x93, 0xe1, 0xa0,
+ 0xbe, 0x18, 0x91, 0x30, 0xce, 0x47, 0xe6, 0x01, 0x0c, 0xdb, 0xa2, 0x4e, 0xc0, 0x73, 0x89, 0xff,
+ 0x8a, 0xa8, 0xef, 0xbb, 0x18, 0x52, 0x30, 0xc6, 0xc5, 0x44, 0x75, 0x5d, 0xc7, 0x0a, 0x5c, 0x21,
+ 0xaa, 0x9c, 0x14, 0xb5, 0x1e, 0x91, 0x30, 0xce, 0xc7, 0xb3, 0xd1, 0xc0, 0xb3, 0x0c, 0x9f, 0x67,
+ 0xab, 0xa4, 0xb2, 0x45, 0x24, 0x8c, 0xf3, 0xb1, 0x39, 0x2f, 0x56, 0xff, 0x13, 0xcd, 0x79, 0xbf,
+ 0xdd, 0x80, 0x6b, 0x89, 0x66, 0x0d, 0xf4, 0x80, 0xee, 0xf4, 0xed, 0x36, 0x0d, 0xd4, 0x07, 0x1c,
+ 0x71, 0x2e, 0xfc, 0xab, 0xd1, 0x77, 0x17, 0x21, 0x24, 0xc6, 0xe9, 0x7c, 0xf7, 0x81, 0x02, 0x1e,
+ 0xeb, 0xdb, 0xcf, 0x41, 0xc3, 0xd1, 0x03, 0x9f, 0xff, 0xb8, 0xf2, 0x1f, 0x0d, 0xd5, 0xb0, 0xbb,
+ 0x8a, 0x80, 0x11, 0x0f, 0xd9, 0x80, 0x0b, 0xb2, 0x89, 0x6f, 0x3e, 0xec, 0xb9, 0x5e, 0x40, 0x3d,
+ 0x91, 0x57, 0x4e, 0xa7, 0x32, 0xef, 0x85, 0xf5, 0x0c, 0x1e, 0xcc, 0xcc, 0x49, 0xd6, 0xe1, 0xbc,
+ 0x21, 0xdc, 0xea, 0xd4, 0x76, 0x75, 0x53, 0x01, 0x0a, 0x03, 0x57, 0xb8, 0x34, 0x5a, 0x1c, 0x64,
+ 0xc1, 0xac, 0x7c, 0xe9, 0xde, 0x5c, 0x1d, 0xa9, 0x37, 0xd7, 0x46, 0xe9, 0xcd, 0xf5, 0xd1, 0x7a,
+ 0x73, 0xe3, 0x78, 0xbd, 0x99, 0xb5, 0x3c, 0xeb, 0x47, 0xd4, 0x63, 0xea, 0x89, 0x98, 0x61, 0x63,
+ 0x51, 0x1b, 0x61, 0xcb, 0xb7, 0x33, 0x78, 0x30, 0x33, 0x27, 0xd9, 0x86, 0x2b, 0x22, 0xfd, 0xa6,
+ 0x63, 0x78, 0x07, 0x3d, 0x36, 0xf1, 0xc4, 0x70, 0x9b, 0x09, 0x0b, 0xe3, 0x95, 0xf6, 0x50, 0x4e,
+ 0x7c, 0x0c, 0x0a, 0xf9, 0x79, 0x18, 0x17, 0x5f, 0x69, 0x5d, 0xef, 0x71, 0x58, 0x11, 0xc3, 0x71,
+ 0x51, 0xc2, 0x8e, 0x2f, 0xc6, 0x89, 0x98, 0xe4, 0x25, 0x0b, 0x30, 0xd9, 0xdb, 0x37, 0xd8, 0xe3,
+ 0xea, 0xce, 0x5d, 0x4a, 0x4d, 0x6a, 0x72, 0xa7, 0x51, 0xa3, 0xf5, 0xbc, 0x32, 0x74, 0x6c, 0x24,
+ 0xc9, 0x98, 0xe6, 0x27, 0x6f, 0xc1, 0x98, 0x1f, 0xe8, 0x5e, 0x20, 0xcd, 0x7a, 0xd3, 0x13, 0x22,
+ 0xc6, 0x45, 0x59, 0xbd, 0xda, 0x31, 0x1a, 0x26, 0x38, 0x33, 0xe7, 0x8b, 0xc9, 0xb3, 0x9b, 0x2f,
+ 0xf2, 0x8c, 0x56, 0xff, 0xa2, 0x08, 0xd7, 0x57, 0x68, 0xb0, 0xee, 0x3a, 0xd2, 0x28, 0x9a, 0x35,
+ 0xed, 0x1f, 0xcb, 0x26, 0x9a, 0x9c, 0xb4, 0x8b, 0xa7, 0x3a, 0x69, 0x97, 0x4e, 0x69, 0xd2, 0x2e,
+ 0x9f, 0xe1, 0xa4, 0xfd, 0x8f, 0x8b, 0xf0, 0x7c, 0xa2, 0x25, 0x37, 0x5c, 0x53, 0x0d, 0xf8, 0x5f,
+ 0x35, 0xe0, 0x31, 0x1a, 0xf0, 0x91, 0xd0, 0x3b, 0xb9, 0x5b, 0x2b, 0xa5, 0xf1, 0x7c, 0x92, 0xd6,
+ 0x78, 0xde, 0xcf, 0x33, 0xf3, 0x65, 0x48, 0x38, 0xd6, 0x8c, 0x77, 0x1b, 0x88, 0x27, 0x9d, 0x70,
+ 0xc2, 0xf4, 0x13, 0x53, 0x7a, 0xc2, 0x20, 0x3a, 0x1c, 0xe0, 0xc0, 0x8c, 0x5c, 0xa4, 0x0d, 0x17,
+ 0x7d, 0xea, 0x04, 0x96, 0x43, 0xed, 0x24, 0x9c, 0xd0, 0x86, 0x5e, 0x94, 0x70, 0x17, 0xdb, 0x59,
+ 0x4c, 0x98, 0x9d, 0x37, 0xcf, 0x38, 0xf0, 0xaf, 0x81, 0xab, 0x9c, 0xa2, 0x69, 0x4e, 0x4d, 0x63,
+ 0xf9, 0x34, 0xad, 0xb1, 0x7c, 0x90, 0xff, 0xbb, 0x8d, 0xa6, 0xad, 0xcc, 0x03, 0xf0, 0xaf, 0x10,
+ 0x57, 0x57, 0xc2, 0x49, 0x1a, 0x43, 0x0a, 0xc6, 0xb8, 0xd8, 0x04, 0xa4, 0xda, 0x39, 0xae, 0xa9,
+ 0x84, 0x13, 0x50, 0x3b, 0x4e, 0xc4, 0x24, 0xef, 0x50, 0x6d, 0xa7, 0x32, 0xb2, 0xb6, 0x73, 0x1b,
+ 0x48, 0xc2, 0xf0, 0x28, 0xf0, 0xaa, 0xc9, 0x18, 0xce, 0xd5, 0x01, 0x0e, 0xcc, 0xc8, 0x35, 0xa4,
+ 0x2b, 0xd7, 0x4e, 0xb7, 0x2b, 0xd7, 0x47, 0xef, 0xca, 0xe4, 0x03, 0xb8, 0xcc, 0x45, 0xc9, 0xf6,
+ 0x49, 0x02, 0x0b, 0xbd, 0xe7, 0xc7, 0x24, 0xf0, 0x65, 0x1c, 0xc6, 0x88, 0xc3, 0x31, 0xd8, 0xf7,
+ 0x31, 0x3c, 0x6a, 0x32, 0xe1, 0xba, 0x3d, 0x5c, 0x27, 0x5a, 0xcc, 0xe0, 0xc1, 0xcc, 0x9c, 0xac,
+ 0x8b, 0x05, 0xac, 0x1b, 0xea, 0xdb, 0x36, 0x35, 0x65, 0x0c, 0x6b, 0xd8, 0xc5, 0x36, 0xd7, 0xda,
+ 0x92, 0x82, 0x31, 0xae, 0x2c, 0x35, 0x65, 0xec, 0x84, 0x6a, 0xca, 0x0a, 0xb7, 0xd2, 0xef, 0x24,
+ 0xb4, 0x21, 0xa9, 0xeb, 0x84, 0x51, 0xc9, 0x8b, 0x69, 0x06, 0x1c, 0xcc, 0xc3, 0xb5, 0x44, 0xc3,
+ 0xb3, 0x7a, 0x81, 0x9f, 0xc4, 0x9a, 0x48, 0x69, 0x89, 0x19, 0x3c, 0x98, 0x99, 0x93, 0xe9, 0xe7,
+ 0xbb, 0x54, 0xb7, 0x83, 0xdd, 0x24, 0xe0, 0x64, 0x52, 0x3f, 0xbf, 0x35, 0xc8, 0x82, 0x59, 0xf9,
+ 0x32, 0x27, 0xa4, 0xa9, 0x67, 0x53, 0xad, 0xfa, 0xb5, 0x12, 0x5c, 0x5e, 0xa1, 0x41, 0x18, 0xde,
+ 0xf3, 0x95, 0x19, 0xe5, 0x0b, 0x30, 0xa3, 0xfc, 0x56, 0x05, 0xce, 0xaf, 0xd0, 0x60, 0x40, 0x1b,
+ 0xfb, 0x53, 0xda, 0xfc, 0xeb, 0x70, 0x3e, 0x8a, 0x28, 0x6b, 0x07, 0xae, 0x27, 0xe6, 0xf2, 0xd4,
+ 0x6a, 0xb9, 0x3d, 0xc8, 0x82, 0x59, 0xf9, 0xc8, 0xb7, 0xe0, 0x79, 0x3e, 0xd5, 0x3b, 0x1d, 0x61,
+ 0x9f, 0x15, 0xc6, 0x84, 0xd8, 0x9e, 0x88, 0x19, 0x09, 0xf9, 0x7c, 0x3b, 0x9b, 0x0d, 0x87, 0xe5,
+ 0x27, 0xdf, 0x81, 0xb1, 0x9e, 0xd5, 0xa3, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0x77, 0x48, 0xc8, 0x46,
+ 0x0c, 0x2c, 0x5a, 0xc0, 0xc5, 0x53, 0x31, 0x21, 0x30, 0xb3, 0xa7, 0xd6, 0xcf, 0xb0, 0xa7, 0xfe,
+ 0xcf, 0x22, 0xd4, 0x56, 0x3c, 0xb7, 0xdf, 0x6b, 0x1d, 0x90, 0x0e, 0x54, 0x1f, 0x70, 0xe7, 0x99,
+ 0x74, 0x4d, 0x8d, 0x1e, 0x95, 0x2d, 0x7c, 0x70, 0x91, 0x4a, 0x24, 0xde, 0x51, 0xc2, 0xb3, 0x4e,
+ 0xbc, 0x47, 0x0f, 0xa8, 0x29, 0x7d, 0x68, 0x61, 0x27, 0xbe, 0xc3, 0x12, 0x51, 0xd0, 0x48, 0x17,
+ 0x26, 0x75, 0xdb, 0x76, 0x1f, 0x50, 0x73, 0x4d, 0x0f, 0xa8, 0x43, 0x7d, 0xe5, 0x92, 0x3c, 0xa9,
+ 0x59, 0x9a, 0xfb, 0xf5, 0x17, 0x92, 0x50, 0x98, 0xc6, 0x26, 0x1f, 0x42, 0xcd, 0x0f, 0x5c, 0x4f,
+ 0x29, 0x5b, 0xcd, 0xf9, 0xc5, 0xd1, 0x3f, 0x7a, 0xeb, 0x9b, 0x6d, 0x01, 0x25, 0x6c, 0xf6, 0xf2,
+ 0x05, 0x95, 0x00, 0xed, 0x37, 0x0b, 0x00, 0xb7, 0x36, 0x37, 0x37, 0xa4, 0x7b, 0xc1, 0x84, 0xb2,
+ 0xde, 0x0f, 0x1d, 0x95, 0xa3, 0x3b, 0x04, 0x13, 0x61, 0x99, 0xd2, 0x87, 0xd7, 0x0f, 0x76, 0x91,
+ 0xa3, 0x93, 0x9f, 0x84, 0x9a, 0x54, 0x90, 0x65, 0xb3, 0x87, 0xa1, 0x05, 0x52, 0x89, 0x46, 0x45,
+ 0xd7, 0xfe, 0x41, 0x11, 0x60, 0xd5, 0xb4, 0x69, 0x5b, 0x05, 0xd2, 0x37, 0x82, 0x5d, 0x8f, 0xfa,
+ 0xbb, 0xae, 0x6d, 0x8e, 0xe8, 0x4d, 0xe5, 0x36, 0xff, 0x4d, 0x05, 0x82, 0x11, 0x1e, 0x31, 0x61,
+ 0xcc, 0x0f, 0x68, 0x4f, 0x45, 0x6a, 0x8e, 0xe8, 0x44, 0x99, 0x12, 0x76, 0x91, 0x08, 0x07, 0x13,
+ 0xa8, 0x44, 0x87, 0xa6, 0xe5, 0x18, 0xe2, 0x07, 0x69, 0x1d, 0x8c, 0xd8, 0x91, 0x26, 0xd9, 0x8a,
+ 0x63, 0x35, 0x82, 0xc1, 0x38, 0xa6, 0xf6, 0x87, 0x45, 0xb8, 0xc4, 0xe5, 0xb1, 0x62, 0x24, 0xe2,
+ 0x31, 0xc9, 0x9f, 0x1f, 0xd8, 0xf4, 0xf7, 0x67, 0x8f, 0x27, 0x5a, 0xec, 0x19, 0x5b, 0xa7, 0x81,
+ 0x1e, 0xe9, 0x73, 0x51, 0x5a, 0x6c, 0xa7, 0x5f, 0x1f, 0xca, 0x3e, 0x1b, 0xaf, 0x44, 0xeb, 0xb5,
+ 0x47, 0xee, 0x42, 0xd9, 0x15, 0xe0, 0xa3, 0x57, 0xe8, 0x35, 0xe6, 0xa3, 0x16, 0x17, 0x47, 0xfe,
+ 0x22, 0x54, 0xfd, 0x40, 0x0f, 0xfa, 0xea, 0xd7, 0xdc, 0x3a, 0x6d, 0xc1, 0x1c, 0x3c, 0x1a, 0x47,
+ 0xc4, 0x3b, 0x4a, 0xa1, 0xda, 0x1f, 0x16, 0xe0, 0x4a, 0x76, 0xc6, 0x35, 0xcb, 0x0f, 0xc8, 0x9f,
+ 0x1b, 0x68, 0xf6, 0x63, 0x7e, 0x71, 0x96, 0x9b, 0x37, 0x7a, 0x18, 0x17, 0xae, 0x52, 0x62, 0x4d,
+ 0x1e, 0x40, 0xc5, 0x0a, 0x68, 0x57, 0xad, 0x2f, 0xef, 0x9d, 0x72, 0xd5, 0x63, 0x53, 0x3b, 0x93,
+ 0x82, 0x42, 0x98, 0xf6, 0x59, 0x71, 0x58, 0x95, 0xf9, 0xf4, 0x61, 0x27, 0x63, 0x7e, 0xef, 0xe4,
+ 0x8b, 0xf9, 0x4d, 0x16, 0x68, 0x30, 0xf4, 0xf7, 0x2f, 0x0c, 0x86, 0xfe, 0xde, 0xcb, 0x1f, 0xfa,
+ 0x9b, 0x6a, 0x86, 0xa1, 0x11, 0xc0, 0x9f, 0x97, 0xe0, 0xea, 0xe3, 0xba, 0x0d, 0x9b, 0xcf, 0x64,
+ 0xef, 0xcc, 0x3b, 0x9f, 0x3d, 0xbe, 0x1f, 0x92, 0x79, 0xa8, 0xf4, 0x76, 0x75, 0x5f, 0x29, 0x65,
+ 0x6a, 0xc1, 0x52, 0xd9, 0x60, 0x89, 0x8f, 0xd8, 0xa0, 0xc1, 0x95, 0x39, 0xfe, 0x8a, 0x82, 0x95,
+ 0x0d, 0xc7, 0x5d, 0xea, 0xfb, 0x91, 0x4d, 0x20, 0x1c, 0x8e, 0xd7, 0x45, 0x32, 0x2a, 0x3a, 0x09,
+ 0xa0, 0x2a, 0x4c, 0xcc, 0x72, 0x66, 0x1a, 0x3d, 0x90, 0x2b, 0x23, 0x4c, 0x3c, 0xaa, 0x94, 0xf4,
+ 0x56, 0x48, 0x59, 0x64, 0x16, 0xca, 0x41, 0x14, 0xb4, 0xab, 0x96, 0xe6, 0xe5, 0x0c, 0xfd, 0x94,
+ 0xf3, 0xb1, 0x85, 0xbd, 0xbb, 0xcd, 0x8d, 0xea, 0xa6, 0xf4, 0x9f, 0x5b, 0xae, 0xc3, 0x15, 0xb2,
+ 0x52, 0xb4, 0xb0, 0xbf, 0x37, 0xc0, 0x81, 0x19, 0xb9, 0xb4, 0x7f, 0x57, 0x87, 0x4b, 0xd9, 0xfd,
+ 0x81, 0xb5, 0xdb, 0x3e, 0xf5, 0x7c, 0x86, 0x5d, 0x48, 0xb6, 0xdb, 0x7d, 0x91, 0x8c, 0x8a, 0xfe,
+ 0xa5, 0x0e, 0x38, 0xfb, 0xad, 0x02, 0x5c, 0xf6, 0xa4, 0x8f, 0xe8, 0x69, 0x04, 0x9d, 0xbd, 0x28,
+ 0xcc, 0x19, 0x43, 0x04, 0xe2, 0xf0, 0xb2, 0x90, 0xbf, 0x57, 0x80, 0xe9, 0x6e, 0xca, 0xce, 0x71,
+ 0x86, 0xfb, 0xd6, 0x78, 0x54, 0xfc, 0xfa, 0x10, 0x79, 0x38, 0xb4, 0x24, 0xe4, 0x3b, 0xd0, 0xec,
+ 0xb1, 0x7e, 0xe1, 0x07, 0xd4, 0x31, 0xd4, 0xd6, 0xb5, 0xd1, 0xff, 0xa4, 0x8d, 0x08, 0x4b, 0x85,
+ 0xa2, 0x09, 0xfd, 0x20, 0x46, 0xc0, 0xb8, 0xc4, 0x67, 0x7c, 0xa3, 0xda, 0x0d, 0xa8, 0xfb, 0x34,
+ 0x08, 0x2c, 0xa7, 0x23, 0xd6, 0x1b, 0x0d, 0xf1, 0xaf, 0xb4, 0x65, 0x1a, 0x86, 0x54, 0xf2, 0x53,
+ 0xd0, 0xe0, 0x2e, 0xa7, 0x05, 0xaf, 0xe3, 0x4f, 0x37, 0x78, 0xb8, 0xd8, 0xb8, 0x08, 0x80, 0x93,
+ 0x89, 0x18, 0xd1, 0xc9, 0x6b, 0x30, 0xb6, 0xcd, 0x7f, 0x5f, 0xb9, 0x77, 0x59, 0xd8, 0xb8, 0xb8,
+ 0xb6, 0xd6, 0x8a, 0xa5, 0x63, 0x82, 0x8b, 0xcc, 0x03, 0xd0, 0xd0, 0x2f, 0x97, 0xb6, 0x67, 0x45,
+ 0x1e, 0x3b, 0x8c, 0x71, 0x91, 0x17, 0xa1, 0x14, 0xd8, 0x3e, 0xb7, 0x61, 0xd5, 0xa3, 0x25, 0xe8,
+ 0xe6, 0x5a, 0x1b, 0x59, 0xba, 0xf6, 0xa3, 0x02, 0x4c, 0xa6, 0x36, 0x97, 0xb0, 0x2c, 0x7d, 0xcf,
+ 0x96, 0xc3, 0x48, 0x98, 0x65, 0x0b, 0xd7, 0x90, 0xa5, 0x93, 0x0f, 0xa4, 0x5a, 0x5e, 0xcc, 0x79,
+ 0x4c, 0xc3, 0x5d, 0x3d, 0xf0, 0x99, 0x1e, 0x3e, 0xa0, 0x91, 0x73, 0x37, 0x5f, 0x54, 0x1e, 0x39,
+ 0x0f, 0xc4, 0xdc, 0x7c, 0x11, 0x0d, 0x13, 0x9c, 0x29, 0x83, 0x5f, 0xf9, 0x38, 0x06, 0x3f, 0xed,
+ 0xd7, 0x8b, 0xb1, 0x16, 0x90, 0x9a, 0xfd, 0x13, 0x5a, 0xe0, 0x15, 0x36, 0x81, 0x86, 0x93, 0x7b,
+ 0x23, 0x3e, 0xff, 0xf1, 0xc9, 0x58, 0x52, 0xc9, 0xbb, 0xa2, 0xed, 0x4b, 0x39, 0x37, 0xc3, 0x6e,
+ 0xae, 0xb5, 0x45, 0x74, 0x95, 0xfa, 0x6a, 0xe1, 0x27, 0x28, 0x9f, 0xd1, 0x27, 0xd0, 0xfe, 0x55,
+ 0x09, 0x9a, 0xb7, 0xdd, 0xed, 0x2f, 0x49, 0x04, 0x75, 0xf6, 0x34, 0x55, 0xfc, 0x02, 0xa7, 0xa9,
+ 0x2d, 0x78, 0x3e, 0x08, 0xec, 0x36, 0x35, 0x5c, 0xc7, 0xf4, 0x17, 0x76, 0x02, 0xea, 0x2d, 0x5b,
+ 0x8e, 0xe5, 0xef, 0x52, 0x53, 0xba, 0x93, 0x5e, 0x38, 0x3a, 0x9c, 0x79, 0x7e, 0x73, 0x73, 0x2d,
+ 0x8b, 0x05, 0x87, 0xe5, 0xe5, 0xc3, 0x86, 0xd8, 0x09, 0xc8, 0x77, 0xca, 0xc8, 0x98, 0x1b, 0x31,
+ 0x6c, 0xc4, 0xd2, 0x31, 0xc1, 0xa5, 0x7d, 0xb7, 0x08, 0x8d, 0x70, 0x03, 0x3e, 0x79, 0x19, 0x6a,
+ 0xdb, 0x9e, 0xbb, 0x47, 0x3d, 0xe1, 0xb9, 0x93, 0x3b, 0x65, 0x5a, 0x22, 0x09, 0x15, 0x8d, 0xbc,
+ 0x04, 0x95, 0xc0, 0xed, 0x59, 0x46, 0xda, 0xa0, 0xb6, 0xc9, 0x12, 0x51, 0xd0, 0xce, 0xae, 0x83,
+ 0xbf, 0x92, 0x50, 0xed, 0x1a, 0x43, 0x95, 0xb1, 0xf7, 0xa1, 0xec, 0xeb, 0xbe, 0x2d, 0xe7, 0xd3,
+ 0x1c, 0x7b, 0xd9, 0x17, 0xda, 0x6b, 0x72, 0x2f, 0xfb, 0x42, 0x7b, 0x0d, 0x39, 0xa8, 0xf6, 0x27,
+ 0x45, 0x68, 0x8a, 0x76, 0x13, 0xa3, 0xc2, 0x69, 0xb6, 0xdc, 0xdb, 0x3c, 0x94, 0xc2, 0xef, 0x77,
+ 0xa9, 0xc7, 0xcd, 0x4c, 0x72, 0x90, 0x8b, 0xfb, 0x07, 0x22, 0x62, 0x18, 0x4e, 0x11, 0x25, 0xa9,
+ 0xa6, 0x2f, 0x9f, 0x61, 0xd3, 0x57, 0x8e, 0xd5, 0xf4, 0xd5, 0xb3, 0x68, 0xfa, 0x4f, 0x8b, 0xd0,
+ 0x58, 0xb3, 0x76, 0xa8, 0x71, 0x60, 0xd8, 0x7c, 0x4f, 0xa0, 0x49, 0x6d, 0x1a, 0xd0, 0x15, 0x4f,
+ 0x37, 0xe8, 0x06, 0xf5, 0x2c, 0x7e, 0x40, 0x0d, 0xfb, 0x3f, 0xf8, 0x08, 0x24, 0xf7, 0x04, 0x2e,
+ 0x0d, 0xe1, 0xc1, 0xa1, 0xb9, 0xc9, 0x2a, 0x8c, 0x99, 0xd4, 0xb7, 0x3c, 0x6a, 0x6e, 0xc4, 0x16,
+ 0x2a, 0x2f, 0xab, 0xa9, 0x66, 0x29, 0x46, 0x7b, 0x74, 0x38, 0x33, 0xae, 0x0c, 0x94, 0x62, 0xc5,
+ 0x92, 0xc8, 0xca, 0x7e, 0xf9, 0x9e, 0xde, 0xf7, 0xb3, 0xca, 0x18, 0xfb, 0xe5, 0x37, 0xb2, 0x59,
+ 0x70, 0x58, 0x5e, 0xad, 0x02, 0xa5, 0x35, 0xb7, 0xa3, 0x7d, 0x56, 0x82, 0xf0, 0x24, 0x23, 0xf2,
+ 0x57, 0x0a, 0xd0, 0xd4, 0x1d, 0xc7, 0x0d, 0xe4, 0x29, 0x41, 0xc2, 0x03, 0x8f, 0xb9, 0x0f, 0x4c,
+ 0x9a, 0x5d, 0x88, 0x40, 0x85, 0xf3, 0x36, 0x74, 0x28, 0xc7, 0x28, 0x18, 0x97, 0x4d, 0xfa, 0x29,
+ 0x7f, 0xf2, 0x7a, 0xfe, 0x52, 0x1c, 0xc3, 0x7b, 0x7c, 0xe5, 0x1b, 0x30, 0x95, 0x2e, 0xec, 0x49,
+ 0xdc, 0x41, 0xb9, 0x1c, 0xf3, 0x45, 0x80, 0x28, 0xa6, 0xe4, 0x29, 0x18, 0xb1, 0xac, 0x84, 0x11,
+ 0x6b, 0x65, 0xf4, 0x06, 0x0e, 0x0b, 0x3d, 0xd4, 0x70, 0xf5, 0x51, 0xca, 0x70, 0xb5, 0x7a, 0x1a,
+ 0xc2, 0x1e, 0x6f, 0xac, 0xfa, 0xfb, 0x05, 0x98, 0x8a, 0x98, 0xe5, 0x0e, 0xd9, 0x37, 0x61, 0xdc,
+ 0xa3, 0xba, 0xd9, 0xd2, 0x03, 0x63, 0x97, 0x87, 0x7a, 0x17, 0x78, 0x6c, 0xf6, 0xb9, 0xa3, 0xc3,
+ 0x99, 0x71, 0x8c, 0x13, 0x30, 0xc9, 0x47, 0x74, 0x68, 0xb2, 0x84, 0x4d, 0xab, 0x4b, 0xdd, 0x7e,
+ 0x30, 0xa2, 0xd5, 0x94, 0x2f, 0x58, 0x30, 0x82, 0xc1, 0x38, 0xa6, 0xf6, 0x79, 0x01, 0x26, 0xe2,
+ 0x05, 0x3e, 0x73, 0x8b, 0xda, 0x6e, 0xd2, 0xa2, 0xb6, 0x78, 0x0a, 0xdf, 0x64, 0x88, 0x15, 0xed,
+ 0x13, 0x88, 0x57, 0x8d, 0x5b, 0xce, 0xe2, 0xc6, 0x82, 0xc2, 0x63, 0x8d, 0x05, 0x5f, 0xfe, 0xc3,
+ 0x6b, 0x86, 0x69, 0xb9, 0xe5, 0x67, 0x58, 0xcb, 0xfd, 0x22, 0x4f, 0xc0, 0x89, 0x9d, 0xe2, 0x52,
+ 0xcd, 0x71, 0x8a, 0x4b, 0x37, 0x3c, 0xc5, 0xa5, 0x76, 0x6a, 0x83, 0xce, 0x71, 0x4e, 0x72, 0xa9,
+ 0x3f, 0xd5, 0x93, 0x5c, 0x1a, 0x67, 0x75, 0x92, 0x0b, 0xe4, 0x3d, 0xc9, 0xe5, 0x93, 0x02, 0x4c,
+ 0x98, 0x89, 0x1d, 0xb3, 0xdc, 0xb6, 0x90, 0x67, 0xaa, 0x49, 0x6e, 0xc0, 0x15, 0x5b, 0xa6, 0x92,
+ 0x69, 0x98, 0x12, 0x49, 0x3e, 0x2d, 0xc0, 0x44, 0xbf, 0x67, 0xea, 0x41, 0x68, 0x38, 0xe2, 0x46,
+ 0x8b, 0x3c, 0xa5, 0xd8, 0x4a, 0xc0, 0x45, 0x8d, 0x9b, 0x4c, 0xc7, 0x94, 0x58, 0xed, 0x8f, 0x6b,
+ 0xf1, 0x19, 0xe9, 0x69, 0x1b, 0xcd, 0xdf, 0x48, 0x1a, 0xcd, 0xaf, 0xa7, 0x8d, 0xe6, 0x93, 0xb1,
+ 0x78, 0xd6, 0xb8, 0xe1, 0xfc, 0xa7, 0x63, 0x03, 0x75, 0x89, 0x9f, 0xe1, 0x12, 0x7e, 0xf3, 0x8c,
+ 0xc1, 0x7a, 0x01, 0x26, 0xa5, 0xf6, 0xaa, 0x88, 0x7c, 0x94, 0x1b, 0x8f, 0xc2, 0x9c, 0x96, 0x92,
+ 0x64, 0x4c, 0xf3, 0x33, 0x81, 0xbe, 0x3a, 0x40, 0x53, 0x2c, 0x15, 0xa2, 0x4e, 0xa6, 0x0e, 0xb7,
+ 0x0c, 0x39, 0xd8, 0xb2, 0xc2, 0xa3, 0xba, 0x2f, 0x4d, 0xdf, 0xb1, 0x65, 0x05, 0xf2, 0x54, 0x94,
+ 0xd4, 0xb8, 0xfd, 0xbf, 0xf6, 0x04, 0xfb, 0xbf, 0x0e, 0x4d, 0x5b, 0xf7, 0x03, 0xf1, 0x35, 0x4d,
+ 0xf9, 0x3b, 0xff, 0x99, 0xe3, 0x4d, 0xbc, 0x6c, 0x32, 0x8f, 0xb4, 0xdb, 0xb5, 0x08, 0x06, 0xe3,
+ 0x98, 0xc4, 0x84, 0x31, 0xf6, 0xca, 0x7f, 0x6d, 0x73, 0x21, 0x90, 0xc7, 0x4c, 0x9d, 0x44, 0x46,
+ 0x68, 0xb6, 0x5a, 0x8b, 0xe1, 0x60, 0x02, 0x75, 0x88, 0x8b, 0x00, 0x46, 0x71, 0x11, 0x90, 0x9f,
+ 0x17, 0x9a, 0xd3, 0x41, 0xf8, 0x59, 0x9b, 0xfc, 0xb3, 0x86, 0x21, 0x92, 0x18, 0x27, 0x62, 0x92,
+ 0x97, 0xf5, 0x8a, 0xbe, 0x6c, 0x06, 0x95, 0x7d, 0x2c, 0xd9, 0x2b, 0xb6, 0x92, 0x64, 0x4c, 0xf3,
+ 0x93, 0x0d, 0xb8, 0x10, 0x26, 0xc5, 0x8b, 0x31, 0xce, 0x71, 0xc2, 0x98, 0xb5, 0xad, 0x0c, 0x1e,
+ 0xcc, 0xcc, 0xc9, 0x37, 0x81, 0xf4, 0x3d, 0x8f, 0x3a, 0xc1, 0x2d, 0xdd, 0xdf, 0x95, 0xc1, 0x6f,
+ 0xd1, 0x26, 0x90, 0x88, 0x84, 0x71, 0x3e, 0x32, 0x0f, 0x20, 0xe0, 0x78, 0xae, 0xc9, 0x64, 0x7c,
+ 0xe9, 0x56, 0x48, 0xc1, 0x18, 0x97, 0xf6, 0x49, 0x03, 0x9a, 0x77, 0xf5, 0xc0, 0xda, 0xa7, 0xdc,
+ 0x9f, 0x77, 0x36, 0x4e, 0x95, 0xbf, 0x55, 0x80, 0x4b, 0xc9, 0xa0, 0xcd, 0x33, 0xf4, 0xac, 0xf0,
+ 0x23, 0x60, 0x30, 0x53, 0x1a, 0x0e, 0x29, 0x05, 0xf7, 0xb1, 0x0c, 0xc4, 0x80, 0x9e, 0xb5, 0x8f,
+ 0xa5, 0x3d, 0x4c, 0x20, 0x0e, 0x2f, 0xcb, 0x97, 0xc5, 0xc7, 0xf2, 0x6c, 0x9f, 0x14, 0x98, 0xf2,
+ 0x00, 0xd5, 0x9e, 0x19, 0x0f, 0x50, 0xfd, 0x99, 0x50, 0xbb, 0x7b, 0x31, 0x0f, 0x50, 0x23, 0x67,
+ 0x24, 0x92, 0xdc, 0xe7, 0x20, 0xd0, 0x86, 0x79, 0x92, 0xf8, 0x11, 0x05, 0xca, 0x32, 0xcf, 0xb4,
+ 0xd5, 0x6d, 0xdd, 0xb7, 0x0c, 0xa9, 0x76, 0xe4, 0x38, 0x19, 0x55, 0x9d, 0xdd, 0x26, 0x02, 0x16,
+ 0xf8, 0x2b, 0x0a, 0xec, 0xe8, 0xa8, 0xba, 0x62, 0xae, 0xa3, 0xea, 0xc8, 0x22, 0x94, 0x9d, 0x3d,
+ 0x7a, 0x70, 0xb2, 0xcd, 0xfe, 0x7c, 0x15, 0x76, 0xf7, 0x0e, 0x3d, 0x40, 0x9e, 0x59, 0xfb, 0x6e,
+ 0x11, 0x80, 0x55, 0xff, 0x78, 0xbe, 0x98, 0x9f, 0x84, 0x9a, 0xdf, 0xe7, 0x56, 0x13, 0xa9, 0x30,
+ 0x45, 0xe1, 0x5b, 0x22, 0x19, 0x15, 0x9d, 0xbc, 0x04, 0x95, 0x8f, 0xfa, 0xb4, 0xaf, 0x02, 0x0b,
+ 0x42, 0xc5, 0xfd, 0x9b, 0x2c, 0x11, 0x05, 0xed, 0xec, 0xec, 0xaa, 0xca, 0x67, 0x53, 0x39, 0x2b,
+ 0x9f, 0x4d, 0x03, 0x6a, 0x77, 0x5d, 0x1e, 0x0d, 0xaa, 0xfd, 0xf7, 0x22, 0x40, 0x14, 0x6d, 0x47,
+ 0x7e, 0xb3, 0x00, 0x17, 0xc3, 0x1f, 0x2e, 0x10, 0xeb, 0x2f, 0x7e, 0x18, 0x71, 0x6e, 0xff, 0x4d,
+ 0xd6, 0xcf, 0xce, 0x47, 0xa0, 0x8d, 0x2c, 0x71, 0x98, 0x5d, 0x0a, 0x82, 0x50, 0xa7, 0xdd, 0x5e,
+ 0x70, 0xb0, 0x64, 0x79, 0xb2, 0x07, 0x66, 0x06, 0x75, 0xde, 0x94, 0x3c, 0x22, 0xab, 0x34, 0x12,
+ 0xf0, 0x9f, 0x48, 0x51, 0x30, 0xc4, 0x21, 0xbb, 0x50, 0x77, 0xdc, 0x0f, 0x7c, 0xd6, 0x1c, 0xb2,
+ 0x3b, 0xbe, 0x33, 0x7a, 0x93, 0x8b, 0x66, 0x15, 0xf6, 0x7e, 0xf9, 0x82, 0x35, 0x47, 0x36, 0xf6,
+ 0x6f, 0x14, 0xe1, 0x7c, 0x46, 0x3b, 0x90, 0x77, 0x60, 0x4a, 0x06, 0x36, 0x46, 0xa7, 0x72, 0x17,
+ 0xa2, 0x53, 0xb9, 0xdb, 0x29, 0x1a, 0x0e, 0x70, 0x93, 0x0f, 0x00, 0x74, 0xc3, 0xa0, 0xbe, 0xbf,
+ 0xee, 0x9a, 0x6a, 0x3d, 0xf0, 0x36, 0x53, 0x5f, 0x16, 0xc2, 0xd4, 0x47, 0x87, 0x33, 0x3f, 0x93,
+ 0x15, 0xab, 0x9c, 0x6a, 0xe7, 0x28, 0x03, 0xc6, 0x20, 0xc9, 0xb7, 0x01, 0xc4, 0x22, 0x3c, 0x3c,
+ 0x4e, 0xe1, 0x09, 0x96, 0xab, 0x59, 0x75, 0x70, 0xd5, 0xec, 0x37, 0xfb, 0xba, 0x13, 0x58, 0xc1,
+ 0x81, 0x38, 0xbd, 0xe6, 0x7e, 0x88, 0x82, 0x31, 0x44, 0xed, 0x9f, 0x17, 0xa1, 0xae, 0x6c, 0xe6,
+ 0x4f, 0xc1, 0x50, 0xda, 0x49, 0x18, 0x4a, 0x4f, 0x29, 0x3a, 0x39, 0xcb, 0x4c, 0xea, 0xa6, 0xcc,
+ 0xa4, 0x2b, 0xf9, 0x45, 0x3d, 0xde, 0x48, 0xfa, 0x3b, 0x45, 0x98, 0x50, 0xac, 0x79, 0x4d, 0xa4,
+ 0x5f, 0x87, 0x49, 0x11, 0x55, 0xb0, 0xae, 0x3f, 0x14, 0x07, 0xf9, 0xf0, 0x06, 0x2b, 0x8b, 0x80,
+ 0xe0, 0x56, 0x92, 0x84, 0x69, 0x5e, 0xd6, 0xad, 0x45, 0xd2, 0x16, 0x5b, 0x84, 0x09, 0x3f, 0xa4,
+ 0x58, 0x6f, 0xf2, 0x6e, 0xdd, 0x4a, 0xd1, 0x70, 0x80, 0x3b, 0x6d, 0xa3, 0x2d, 0x9f, 0x81, 0x8d,
+ 0xf6, 0x3f, 0x14, 0x60, 0x2c, 0x6a, 0xaf, 0x33, 0xb7, 0xd0, 0xee, 0x24, 0x2d, 0xb4, 0x0b, 0xb9,
+ 0xbb, 0xc3, 0x10, 0xfb, 0xec, 0x5f, 0xab, 0x41, 0x22, 0x48, 0x9e, 0x6c, 0xc3, 0x15, 0x2b, 0x33,
+ 0xd4, 0x2f, 0x36, 0xda, 0x84, 0xbb, 0xbe, 0x57, 0x87, 0x72, 0xe2, 0x63, 0x50, 0x48, 0x1f, 0xea,
+ 0xfb, 0xd4, 0x0b, 0x2c, 0x83, 0xaa, 0xfa, 0xad, 0xe4, 0x56, 0xc9, 0xa4, 0x15, 0x3a, 0x6c, 0xd3,
+ 0xfb, 0x52, 0x00, 0x86, 0xa2, 0xc8, 0x36, 0x54, 0xa8, 0xd9, 0xa1, 0xea, 0x68, 0xa5, 0x9c, 0x07,
+ 0x97, 0x86, 0xed, 0xc9, 0xde, 0x7c, 0x14, 0xd0, 0xc4, 0x87, 0x86, 0xad, 0xbc, 0x8c, 0xb2, 0x1f,
+ 0x8e, 0xae, 0x60, 0x85, 0xfe, 0xca, 0xe8, 0xd4, 0x85, 0x30, 0x09, 0x23, 0x39, 0x64, 0x2f, 0x34,
+ 0x77, 0x56, 0x4e, 0x69, 0xf0, 0x78, 0x8c, 0xb1, 0xd3, 0x87, 0xc6, 0x03, 0x3d, 0xa0, 0x5e, 0x57,
+ 0xf7, 0xf6, 0xe4, 0x6a, 0x63, 0xf4, 0x1a, 0xbe, 0xab, 0x90, 0xa2, 0x1a, 0x86, 0x49, 0x18, 0xc9,
+ 0x21, 0x2e, 0x34, 0x02, 0xa9, 0x3e, 0x2b, 0x9b, 0xee, 0xe8, 0x42, 0x95, 0x22, 0xee, 0xcb, 0x60,
+ 0x79, 0xf5, 0x8a, 0x91, 0x0c, 0xb2, 0x9f, 0x38, 0x5b, 0x5a, 0x9c, 0x28, 0xde, 0xca, 0xe1, 0x1b,
+ 0x90, 0x50, 0xd1, 0x74, 0x93, 0x7d, 0x46, 0xb5, 0xf6, 0xbf, 0x2a, 0xd1, 0xb0, 0xfc, 0xb4, 0xed,
+ 0x84, 0xaf, 0x25, 0xed, 0x84, 0xd7, 0xd2, 0x76, 0xc2, 0x94, 0xb3, 0xfa, 0xe4, 0xe1, 0xb5, 0x29,
+ 0xf3, 0x5a, 0xf9, 0x0c, 0xcc, 0x6b, 0xaf, 0x42, 0x73, 0x9f, 0x8f, 0x04, 0xe2, 0x9c, 0xa6, 0x0a,
+ 0x9f, 0x46, 0xf8, 0xc8, 0x7e, 0x3f, 0x4a, 0xc6, 0x38, 0x0f, 0xcb, 0x22, 0x6f, 0xd3, 0x08, 0x0f,
+ 0xba, 0x95, 0x59, 0xda, 0x51, 0x32, 0xc6, 0x79, 0x78, 0x64, 0x9e, 0xe5, 0xec, 0x89, 0x0c, 0x35,
+ 0x9e, 0x41, 0x44, 0xe6, 0xa9, 0x44, 0x8c, 0xe8, 0xe4, 0x06, 0xd4, 0xfb, 0xe6, 0x8e, 0xe0, 0xad,
+ 0x73, 0x5e, 0xae, 0x61, 0x6e, 0x2d, 0x2d, 0xcb, 0x73, 0xa3, 0x14, 0x95, 0x95, 0xa4, 0xab, 0xf7,
+ 0x14, 0x81, 0xaf, 0x0d, 0x65, 0x49, 0xd6, 0xa3, 0x64, 0x8c, 0xf3, 0x90, 0x9f, 0x83, 0x09, 0x8f,
+ 0x9a, 0x7d, 0x83, 0x86, 0xb9, 0x80, 0xe7, 0x22, 0xe2, 0xda, 0x90, 0x38, 0x05, 0x53, 0x9c, 0x43,
+ 0x8c, 0x84, 0xcd, 0x91, 0x8c, 0x84, 0xdf, 0x80, 0x09, 0xd3, 0xd3, 0x2d, 0x87, 0x9a, 0xf7, 0x1c,
+ 0x1e, 0x91, 0x20, 0xe3, 0x03, 0x43, 0x0b, 0xf9, 0x52, 0x82, 0x8a, 0x29, 0x6e, 0xed, 0x0f, 0x0a,
+ 0x40, 0x06, 0x23, 0xe1, 0xc9, 0x2e, 0x54, 0x1d, 0x6e, 0x3d, 0xcb, 0x7d, 0xb4, 0x76, 0xcc, 0x08,
+ 0x27, 0x86, 0x35, 0x99, 0x20, 0xf1, 0x89, 0x03, 0x75, 0xfa, 0x30, 0xa0, 0x9e, 0x13, 0xee, 0x8c,
+ 0x39, 0x9d, 0x63, 0xbc, 0xc5, 0x6a, 0x42, 0x22, 0x63, 0x28, 0x43, 0xfb, 0xa3, 0x22, 0x34, 0x63,
+ 0x7c, 0x4f, 0x5a, 0x94, 0xf2, 0xcd, 0xf9, 0xc2, 0x68, 0xb5, 0xe5, 0xd9, 0xf2, 0x0f, 0x8d, 0x6d,
+ 0xce, 0x97, 0x24, 0x5c, 0xc3, 0x38, 0x1f, 0x99, 0x07, 0xe8, 0xea, 0x7e, 0x40, 0x3d, 0x3e, 0x7b,
+ 0xa7, 0xb6, 0xc4, 0xaf, 0x87, 0x14, 0x8c, 0x71, 0x91, 0xeb, 0xf2, 0x20, 0xf6, 0x72, 0xf2, 0x08,
+ 0xc3, 0x21, 0xa7, 0xac, 0x57, 0x4e, 0xe1, 0x94, 0x75, 0xd2, 0x81, 0x29, 0x55, 0x6a, 0x45, 0x3d,
+ 0xd9, 0x01, 0x77, 0x62, 0xfd, 0x93, 0x82, 0xc0, 0x01, 0x50, 0xed, 0xbb, 0x05, 0x18, 0x4f, 0x98,
+ 0x4c, 0xc4, 0xe1, 0x83, 0x6a, 0x1f, 0x47, 0xe2, 0xf0, 0xc1, 0xd8, 0xf6, 0x8b, 0x57, 0xa0, 0x2a,
+ 0x1a, 0x28, 0x1d, 0x9e, 0x29, 0x9a, 0x10, 0x25, 0x95, 0x8d, 0x85, 0xd2, 0x28, 0x9b, 0x1e, 0x0b,
+ 0xa5, 0xd5, 0x16, 0x15, 0x5d, 0xf8, 0x3a, 0x44, 0xe9, 0x64, 0x4b, 0xc7, 0x7c, 0x1d, 0x22, 0x1d,
+ 0x43, 0x0e, 0xed, 0xf7, 0x78, 0xb9, 0x03, 0xef, 0x20, 0x5c, 0x0b, 0x76, 0xa0, 0x26, 0x43, 0xf2,
+ 0xe4, 0xaf, 0xf1, 0x4e, 0x0e, 0x3b, 0x0e, 0xc7, 0x91, 0xc1, 0x67, 0xba, 0xb1, 0x77, 0x6f, 0x67,
+ 0x07, 0x15, 0x3a, 0xb9, 0x09, 0x0d, 0xd7, 0x59, 0xd6, 0x2d, 0xbb, 0xef, 0xa9, 0x99, 0xe1, 0x27,
+ 0xd8, 0x58, 0x77, 0x4f, 0x25, 0x3e, 0x3a, 0x9c, 0xb9, 0x14, 0xbe, 0x24, 0x0a, 0x89, 0x51, 0x4e,
+ 0xed, 0x2f, 0x17, 0xe0, 0x22, 0xba, 0xb6, 0x6d, 0x39, 0x9d, 0xa4, 0xb3, 0x8c, 0xd8, 0x30, 0xd1,
+ 0xd5, 0x1f, 0x6e, 0x39, 0xfa, 0xbe, 0x6e, 0xd9, 0xfa, 0xb6, 0x4d, 0x9f, 0xb8, 0x96, 0xeb, 0x07,
+ 0x96, 0x3d, 0x2b, 0xae, 0x83, 0x9b, 0x5d, 0x75, 0x82, 0x7b, 0x5e, 0x3b, 0xf0, 0x2c, 0xa7, 0x23,
+ 0x06, 0xbd, 0xf5, 0x04, 0x16, 0xa6, 0xb0, 0xb5, 0x3f, 0x2e, 0x01, 0x0f, 0x0b, 0x23, 0x6f, 0x42,
+ 0xa3, 0x4b, 0x8d, 0x5d, 0xdd, 0xb1, 0x7c, 0x75, 0x8c, 0xeb, 0x65, 0x56, 0xaf, 0x75, 0x95, 0xf8,
+ 0x88, 0x7d, 0x8a, 0x85, 0xf6, 0x1a, 0xdf, 0x79, 0x11, 0xf1, 0x12, 0x03, 0xaa, 0x1d, 0xdf, 0xd7,
+ 0x7b, 0x56, 0xee, 0xa8, 0x04, 0x71, 0x6c, 0xa6, 0x18, 0x8e, 0xc4, 0x33, 0x4a, 0x68, 0x62, 0x40,
+ 0xa5, 0x67, 0xeb, 0x96, 0x93, 0xfb, 0xfa, 0x22, 0x56, 0x83, 0x0d, 0x86, 0x24, 0x8c, 0x6b, 0xfc,
+ 0x11, 0x05, 0x36, 0xe9, 0x43, 0xd3, 0x37, 0x3c, 0xbd, 0xeb, 0xef, 0xea, 0xf3, 0xaf, 0xbf, 0x91,
+ 0x5b, 0x5d, 0x8d, 0x44, 0x89, 0xd9, 0x73, 0x11, 0x17, 0xd6, 0xdb, 0xb7, 0x16, 0xe6, 0x5f, 0x7f,
+ 0x03, 0xe3, 0x72, 0xe2, 0x62, 0x5f, 0x7f, 0x75, 0x5e, 0x8e, 0x20, 0xa7, 0x2e, 0xf6, 0xf5, 0x57,
+ 0xe7, 0x31, 0x2e, 0x47, 0xfb, 0xdf, 0x05, 0x68, 0x84, 0xbc, 0x64, 0x0b, 0x80, 0x8d, 0x65, 0xf2,
+ 0xa0, 0xcb, 0x13, 0x5d, 0x3a, 0xc1, 0xed, 0x13, 0x5b, 0x61, 0x66, 0x8c, 0x01, 0x65, 0x9c, 0x04,
+ 0x5a, 0x3c, 0xed, 0x93, 0x40, 0xe7, 0xa0, 0xb1, 0xab, 0x3b, 0xa6, 0xbf, 0xab, 0xef, 0x89, 0x21,
+ 0x3d, 0x76, 0x36, 0xee, 0x2d, 0x45, 0xc0, 0x88, 0x47, 0xfb, 0xa7, 0x55, 0x10, 0xa1, 0x04, 0x6c,
+ 0xd0, 0x31, 0x2d, 0x5f, 0xc4, 0xb2, 0x17, 0x78, 0xce, 0x70, 0xd0, 0x59, 0x92, 0xe9, 0x18, 0x72,
+ 0x90, 0xcb, 0x50, 0xea, 0x5a, 0x8e, 0xf4, 0x3d, 0x71, 0xd3, 0xe3, 0xba, 0xe5, 0x20, 0x4b, 0xe3,
+ 0x24, 0xfd, 0xa1, 0x0c, 0x43, 0x14, 0x24, 0xfd, 0x21, 0xb2, 0x34, 0xf2, 0x75, 0x98, 0xb4, 0x5d,
+ 0x77, 0x8f, 0x0d, 0x1f, 0x2a, 0x5a, 0x51, 0xf8, 0x81, 0xb9, 0x31, 0x60, 0x2d, 0x49, 0xc2, 0x34,
+ 0x2f, 0xd9, 0x82, 0xe7, 0x3f, 0xa6, 0x9e, 0x2b, 0xc7, 0xcb, 0xb6, 0x4d, 0x69, 0x4f, 0xc1, 0x08,
+ 0x65, 0x8e, 0x07, 0x3d, 0xfe, 0x62, 0x36, 0x0b, 0x0e, 0xcb, 0xcb, 0xc3, 0xa7, 0x75, 0xaf, 0x43,
+ 0x83, 0x0d, 0xcf, 0x35, 0xa8, 0xef, 0x5b, 0x4e, 0x47, 0xc1, 0x56, 0x23, 0xd8, 0xcd, 0x6c, 0x16,
+ 0x1c, 0x96, 0x97, 0xbc, 0x07, 0xd3, 0x82, 0x24, 0xd4, 0x96, 0x05, 0x31, 0xcc, 0x58, 0xb6, 0xba,
+ 0xf5, 0x6f, 0x5c, 0x78, 0x78, 0x36, 0x87, 0xf0, 0xe0, 0xd0, 0xdc, 0xe4, 0x36, 0x4c, 0x29, 0xff,
+ 0xde, 0x06, 0xf5, 0xda, 0x61, 0x78, 0xc9, 0x78, 0xeb, 0x1a, 0x5b, 0x79, 0x2f, 0xd1, 0x9e, 0x47,
+ 0x8d, 0xb8, 0x9f, 0x54, 0x71, 0xe1, 0x40, 0x3e, 0x82, 0x70, 0x89, 0xc7, 0x90, 0x6c, 0xf5, 0x16,
+ 0x5d, 0xd7, 0x36, 0xdd, 0x07, 0x8e, 0xaa, 0xbb, 0x50, 0x31, 0xb9, 0x4b, 0xaf, 0x9d, 0xc9, 0x81,
+ 0x43, 0x72, 0xb2, 0x9a, 0x73, 0xca, 0x92, 0xfb, 0xc0, 0x49, 0xa3, 0x42, 0x54, 0xf3, 0xf6, 0x10,
+ 0x1e, 0x1c, 0x9a, 0x9b, 0x2c, 0x03, 0x49, 0xd7, 0x60, 0xab, 0x27, 0x9d, 0xce, 0x97, 0xc4, 0x99,
+ 0x35, 0x69, 0x2a, 0x66, 0xe4, 0x20, 0x6b, 0x70, 0x21, 0x9d, 0xca, 0xc4, 0x49, 0xff, 0x33, 0x3f,
+ 0xad, 0x16, 0x33, 0xe8, 0x98, 0x99, 0x4b, 0xfb, 0x67, 0x45, 0x18, 0x4f, 0x1c, 0x72, 0xf0, 0xcc,
+ 0x6d, 0x26, 0x67, 0x6b, 0x81, 0xae, 0xdf, 0x59, 0x5d, 0xba, 0x45, 0x75, 0x93, 0x7a, 0x77, 0xa8,
+ 0x3a, 0x90, 0x42, 0x4c, 0x8b, 0x09, 0x0a, 0xa6, 0x38, 0xc9, 0x0e, 0x54, 0x84, 0x65, 0x3b, 0xef,
+ 0xf5, 0x25, 0xaa, 0x8d, 0xb8, 0x79, 0x5b, 0xde, 0xf9, 0xe3, 0x7a, 0x14, 0x05, 0xbc, 0x16, 0xc0,
+ 0x58, 0x9c, 0x83, 0x0d, 0x24, 0x91, 0xda, 0x5b, 0x4b, 0xa8, 0xbc, 0xab, 0x50, 0x0a, 0x82, 0x51,
+ 0xb7, 0xa9, 0x0b, 0x4f, 0xc9, 0xe6, 0x1a, 0x32, 0x0c, 0x6d, 0x87, 0x7d, 0x3b, 0xdf, 0xb7, 0x5c,
+ 0x47, 0x9e, 0x59, 0xbe, 0x05, 0xb5, 0x40, 0x1a, 0x0b, 0x47, 0xdb, 0x66, 0xcf, 0x75, 0x25, 0x65,
+ 0x28, 0x54, 0x58, 0xda, 0x7f, 0x2c, 0x42, 0x23, 0x5c, 0xd8, 0x1f, 0xe3, 0x2c, 0x70, 0x17, 0x1a,
+ 0x61, 0x0c, 0x5c, 0xee, 0x1b, 0x11, 0xa3, 0xd0, 0x2c, 0xbe, 0x16, 0x0d, 0x5f, 0x31, 0x92, 0x11,
+ 0x8f, 0xaf, 0x2b, 0xe5, 0x88, 0xaf, 0xeb, 0x41, 0x2d, 0xf0, 0xac, 0x4e, 0x47, 0xae, 0x12, 0xf2,
+ 0x04, 0xd8, 0x85, 0xcd, 0xb5, 0x29, 0x00, 0x65, 0xcb, 0x8a, 0x17, 0x54, 0x62, 0xb4, 0x0f, 0x61,
+ 0x2a, 0xcd, 0xc9, 0x55, 0x68, 0x63, 0x97, 0x9a, 0x7d, 0x5b, 0xb5, 0x71, 0xa4, 0x42, 0xcb, 0x74,
+ 0x0c, 0x39, 0xd8, 0x32, 0x9c, 0x7d, 0xa6, 0x8f, 0x5d, 0x47, 0xa9, 0xb1, 0x7c, 0x35, 0xb2, 0x29,
+ 0xd3, 0x30, 0xa4, 0x6a, 0xff, 0xad, 0x04, 0x97, 0x23, 0xf3, 0xcc, 0xba, 0xee, 0xe8, 0x9d, 0x63,
+ 0x5c, 0x83, 0xf7, 0xd5, 0xc6, 0xa5, 0x93, 0x5e, 0xe8, 0x50, 0x7a, 0x06, 0x2e, 0x74, 0xf8, 0xbf,
+ 0x45, 0xe0, 0xf1, 0xba, 0xe4, 0x3b, 0x30, 0xa6, 0xc7, 0x6e, 0x40, 0x95, 0x9f, 0xf3, 0x66, 0xee,
+ 0xcf, 0xc9, 0xc3, 0x82, 0xc3, 0x90, 0xad, 0x78, 0x2a, 0x26, 0x04, 0x12, 0x17, 0xea, 0x3b, 0xba,
+ 0x6d, 0x33, 0x5d, 0x28, 0xb7, 0xbb, 0x29, 0x21, 0x9c, 0x77, 0xf3, 0x65, 0x09, 0x8d, 0xa1, 0x10,
+ 0xf2, 0x49, 0x01, 0xc6, 0xbd, 0xf8, 0x72, 0x4d, 0x7e, 0x90, 0x3c, 0xc1, 0x08, 0x31, 0xb4, 0x78,
+ 0x80, 0x58, 0x7c, 0x4d, 0x98, 0x94, 0xa9, 0xfd, 0xd7, 0x02, 0x8c, 0xb7, 0x6d, 0xcb, 0xb4, 0x9c,
+ 0xce, 0x19, 0xde, 0x27, 0x71, 0x0f, 0x2a, 0xbe, 0x6d, 0x99, 0x74, 0xc4, 0xd9, 0x44, 0xcc, 0x63,
+ 0x0c, 0x00, 0x05, 0x4e, 0xf2, 0x82, 0x8a, 0xd2, 0x31, 0x2e, 0xa8, 0xf8, 0x61, 0x15, 0x64, 0xe4,
+ 0x39, 0xe9, 0x43, 0xa3, 0xa3, 0xce, 0xbd, 0x97, 0x75, 0xbc, 0x95, 0xe3, 0xcc, 0xc4, 0xc4, 0x09,
+ 0xfa, 0x62, 0xec, 0x0f, 0x13, 0x31, 0x92, 0x44, 0x68, 0xf2, 0xea, 0xdd, 0xa5, 0x9c, 0x57, 0xef,
+ 0x0a, 0x71, 0x83, 0x97, 0xef, 0xea, 0x50, 0xde, 0x0d, 0x82, 0x9e, 0xec, 0x4c, 0xa3, 0x6f, 0x2d,
+ 0x88, 0x8e, 0xed, 0x11, 0x3a, 0x11, 0x7b, 0x47, 0x0e, 0xcd, 0x44, 0x38, 0x7a, 0x78, 0xd5, 0xda,
+ 0x62, 0xae, 0xc0, 0x87, 0xb8, 0x08, 0xf6, 0x8e, 0x1c, 0x9a, 0xfc, 0x32, 0x34, 0x03, 0x4f, 0x77,
+ 0xfc, 0x1d, 0xd7, 0xeb, 0x52, 0x4f, 0xae, 0x51, 0x97, 0x73, 0xdc, 0x3e, 0xbb, 0x19, 0xa1, 0x09,
+ 0x8f, 0x6a, 0x22, 0x09, 0xe3, 0xd2, 0xc8, 0x1e, 0xd4, 0xfb, 0xa6, 0x28, 0x98, 0x34, 0x83, 0x2d,
+ 0xe4, 0xb9, 0x50, 0x38, 0x16, 0xd6, 0xa0, 0xde, 0x30, 0x14, 0x90, 0xbc, 0x55, 0xb0, 0x76, 0x5a,
+ 0xb7, 0x0a, 0xc6, 0x7b, 0x63, 0xd6, 0x99, 0x22, 0xa4, 0x2b, 0xf5, 0x5a, 0xa7, 0x23, 0xa3, 0xb2,
+ 0x96, 0x73, 0xab, 0x9c, 0x42, 0x64, 0x33, 0xd4, 0x8d, 0x9d, 0x0e, 0x2a, 0x19, 0x5a, 0x17, 0xa4,
+ 0xb7, 0x83, 0x18, 0x89, 0xbb, 0x77, 0xc4, 0x46, 0xb7, 0xb9, 0xe3, 0x8d, 0x07, 0xe1, 0x25, 0x30,
+ 0xb1, 0xb3, 0xbf, 0x33, 0x2f, 0xd9, 0xd1, 0xfe, 0x53, 0x11, 0x4a, 0x9b, 0x6b, 0x6d, 0x71, 0x9e,
+ 0x27, 0xbf, 0xd8, 0x8a, 0xb6, 0xf7, 0xac, 0xde, 0x7d, 0xea, 0x59, 0x3b, 0x07, 0x72, 0xe9, 0x1d,
+ 0x3b, 0xcf, 0x33, 0xcd, 0x81, 0x19, 0xb9, 0xc8, 0xfb, 0x30, 0x66, 0xe8, 0x8b, 0xd4, 0x0b, 0x46,
+ 0x31, 0x2c, 0xf0, 0x1d, 0xbd, 0x8b, 0x0b, 0x51, 0x76, 0x4c, 0x80, 0x91, 0x2d, 0x00, 0x23, 0x82,
+ 0x2e, 0x9d, 0xd8, 0x1c, 0x12, 0x03, 0x8e, 0x01, 0x11, 0x84, 0xc6, 0x1e, 0x63, 0xe5, 0xa8, 0xe5,
+ 0x93, 0xa0, 0xf2, 0x9e, 0x73, 0x47, 0xe5, 0xc5, 0x08, 0x46, 0x73, 0x60, 0x3c, 0x71, 0x21, 0x0f,
+ 0xf9, 0x1a, 0xd4, 0xdd, 0x5e, 0x6c, 0x38, 0x6d, 0xf0, 0xf8, 0xcf, 0xfa, 0x3d, 0x99, 0xf6, 0xe8,
+ 0x70, 0x66, 0x7c, 0xcd, 0xed, 0x58, 0x86, 0x4a, 0xc0, 0x90, 0x9d, 0x68, 0x50, 0xe5, 0xdb, 0xf0,
+ 0xd4, 0x75, 0x3c, 0x7c, 0xee, 0xe0, 0x37, 0x66, 0xf8, 0x28, 0x29, 0xda, 0xaf, 0x94, 0x21, 0xf2,
+ 0x11, 0x12, 0x1f, 0xaa, 0x62, 0x9b, 0x81, 0x1c, 0xb9, 0xcf, 0x74, 0x47, 0x83, 0x14, 0x45, 0x3a,
+ 0x50, 0xfa, 0xd0, 0xdd, 0xce, 0x3d, 0x70, 0xc7, 0xf6, 0xdf, 0x0b, 0x5b, 0x59, 0x2c, 0x01, 0x99,
+ 0x04, 0xf2, 0xb7, 0x0b, 0x70, 0xce, 0x4f, 0xab, 0xbe, 0xb2, 0x3b, 0x60, 0x7e, 0x1d, 0x3f, 0xad,
+ 0x4c, 0xcb, 0x40, 0xdd, 0x61, 0x64, 0x1c, 0x2c, 0x0b, 0x6b, 0x7f, 0xe1, 0xbc, 0x93, 0xdd, 0x69,
+ 0x25, 0xe7, 0x25, 0x92, 0xc9, 0xf6, 0x4f, 0xa6, 0xa1, 0x14, 0xa5, 0xfd, 0x5a, 0x11, 0x9a, 0xb1,
+ 0xd1, 0x3a, 0xf7, 0x2d, 0x4f, 0x0f, 0x53, 0xb7, 0x3c, 0x6d, 0x8c, 0xee, 0xcb, 0x8e, 0x4a, 0x75,
+ 0xd6, 0x17, 0x3d, 0xfd, 0xcb, 0x22, 0x94, 0xb6, 0x96, 0x96, 0x93, 0x8b, 0xd6, 0xc2, 0x53, 0x58,
+ 0xb4, 0xee, 0x42, 0x6d, 0xbb, 0x6f, 0xd9, 0x81, 0xe5, 0xe4, 0x3e, 0x21, 0x44, 0x5d, 0x8a, 0x25,
+ 0x7d, 0x1d, 0x02, 0x15, 0x15, 0x3c, 0xe9, 0x40, 0xad, 0x23, 0x8e, 0x68, 0xcc, 0x1d, 0xe1, 0x27,
+ 0x8f, 0x7a, 0x14, 0x82, 0xe4, 0x0b, 0x2a, 0x74, 0xed, 0x00, 0xe4, 0xed, 0xfe, 0x4f, 0xbd, 0x35,
+ 0xb5, 0x5f, 0x86, 0x50, 0x0b, 0x78, 0xfa, 0xc2, 0xff, 0x47, 0x01, 0x92, 0x8a, 0xcf, 0xd3, 0xef,
+ 0x4d, 0x7b, 0xe9, 0xde, 0xb4, 0x74, 0x1a, 0x3f, 0x5f, 0x76, 0x87, 0xd2, 0xfe, 0x7d, 0x01, 0x52,
+ 0x7b, 0xc3, 0xc8, 0x1b, 0xf2, 0xb4, 0xaf, 0x64, 0x28, 0x95, 0x3a, 0xed, 0x8b, 0x24, 0xb9, 0x63,
+ 0xa7, 0x7e, 0x7d, 0xca, 0x96, 0x6b, 0x71, 0x07, 0x9a, 0x2c, 0xfe, 0xdd, 0xd1, 0x97, 0x6b, 0x59,
+ 0xee, 0x38, 0x19, 0xee, 0x17, 0x27, 0x61, 0x52, 0xae, 0xf6, 0x4f, 0x8a, 0x50, 0x7d, 0x6a, 0x5b,
+ 0xd5, 0x69, 0x22, 0x02, 0x73, 0x31, 0xe7, 0x68, 0x3f, 0x34, 0xfe, 0xb2, 0x9b, 0x8a, 0xbf, 0xcc,
+ 0x7b, 0x37, 0xf1, 0x13, 0xa2, 0x2f, 0xff, 0x6d, 0x01, 0xe4, 0x5c, 0xb3, 0xea, 0xf8, 0x81, 0xee,
+ 0x18, 0x94, 0x18, 0xe1, 0xc4, 0x96, 0x37, 0xcc, 0x47, 0x86, 0xc2, 0x09, 0x5d, 0x86, 0x3f, 0xab,
+ 0x89, 0x8c, 0xfc, 0x34, 0xd4, 0x77, 0x5d, 0x3f, 0xe0, 0x93, 0x57, 0x31, 0x69, 0x32, 0xbb, 0x25,
+ 0xd3, 0x31, 0xe4, 0x48, 0xbb, 0xb3, 0x2b, 0xc3, 0xdd, 0xd9, 0xda, 0x6f, 0x17, 0x61, 0xec, 0xcb,
+ 0xb2, 0xdf, 0x3e, 0x2b, 0x5e, 0xb5, 0x94, 0x33, 0x5e, 0xb5, 0x7c, 0x92, 0x78, 0x55, 0xed, 0xfb,
+ 0x05, 0x80, 0xa7, 0xb6, 0xd9, 0xdf, 0x4c, 0x86, 0x92, 0xe6, 0xee, 0x57, 0xd9, 0x81, 0xa4, 0xff,
+ 0xa8, 0xa2, 0xaa, 0xc4, 0xc3, 0x48, 0x3f, 0x2d, 0xc0, 0x84, 0x9e, 0x08, 0xcd, 0xcc, 0xad, 0x2f,
+ 0xa7, 0x22, 0x3d, 0xc3, 0xc8, 0xa2, 0x64, 0x3a, 0xa6, 0xc4, 0x92, 0xb7, 0xa2, 0x83, 0xa6, 0xef,
+ 0x46, 0xdd, 0x7e, 0xe0, 0x84, 0x68, 0xae, 0xbb, 0x25, 0x38, 0x9f, 0x10, 0x0a, 0x5b, 0x3a, 0x95,
+ 0x50, 0xd8, 0xf8, 0x26, 0xbf, 0xf2, 0x63, 0x37, 0xf9, 0xed, 0x43, 0x63, 0xc7, 0x73, 0xbb, 0x3c,
+ 0xda, 0x54, 0xde, 0x6a, 0x7c, 0x33, 0xc7, 0x44, 0x19, 0xdd, 0xe7, 0x1f, 0x19, 0xae, 0x96, 0x15,
+ 0x3e, 0x46, 0xa2, 0xb8, 0xad, 0xdf, 0x15, 0x52, 0xab, 0xa7, 0x29, 0x35, 0x1c, 0x4b, 0x36, 0x05,
+ 0x3a, 0x2a, 0x31, 0xc9, 0x08, 0xd3, 0xda, 0xd3, 0x89, 0x30, 0xd5, 0x7e, 0xb7, 0xaa, 0x06, 0xb0,
+ 0x67, 0xee, 0x4c, 0xd3, 0xdc, 0x5b, 0xb3, 0xe3, 0xfb, 0xaa, 0xcb, 0x27, 0xd8, 0x57, 0x5d, 0x39,
+ 0xee, 0xbe, 0xea, 0xea, 0x13, 0x02, 0x3f, 0xd3, 0x9b, 0x9e, 0x6b, 0x4f, 0x71, 0xd3, 0x73, 0xfd,
+ 0x74, 0x36, 0x3d, 0x37, 0x4e, 0xb6, 0xe9, 0x59, 0xee, 0x1b, 0x0e, 0xb3, 0x43, 0x72, 0xd3, 0xf3,
+ 0x62, 0x92, 0x8c, 0x69, 0xfe, 0xac, 0x7d, 0xd3, 0xcd, 0x13, 0xee, 0x9b, 0x4e, 0xed, 0x72, 0x1e,
+ 0x1b, 0x69, 0x97, 0xf3, 0xf8, 0xb1, 0x76, 0x39, 0x1f, 0x96, 0x20, 0xb5, 0x74, 0xfe, 0xca, 0x4d,
+ 0xf6, 0xff, 0x95, 0x9b, 0xec, 0xb3, 0x22, 0x44, 0xc3, 0xe6, 0x09, 0xc3, 0x88, 0xde, 0x83, 0x7a,
+ 0x57, 0x7f, 0xb8, 0x44, 0x6d, 0xfd, 0x20, 0xcf, 0xc5, 0xbd, 0xeb, 0x12, 0x03, 0x43, 0x34, 0xe2,
+ 0x03, 0x58, 0xe1, 0xe1, 0xf9, 0xb9, 0x1d, 0x0e, 0xd1, 0x39, 0xfc, 0xc2, 0xa4, 0x19, 0xbd, 0x63,
+ 0x4c, 0x8c, 0xf6, 0x6f, 0x8a, 0x20, 0x6f, 0x59, 0x20, 0x14, 0x2a, 0x3b, 0xd6, 0x43, 0x6a, 0xe6,
+ 0x0e, 0x4e, 0x8e, 0x5d, 0xa7, 0x2e, 0x3c, 0x2a, 0x3c, 0x01, 0x05, 0x3a, 0x37, 0x95, 0x0b, 0x0f,
+ 0x99, 0x6c, 0xbf, 0x1c, 0xa6, 0xf2, 0xb8, 0xa7, 0x4d, 0x9a, 0xca, 0x45, 0x12, 0x2a, 0x19, 0xc2,
+ 0x32, 0xcf, 0x83, 0x25, 0x72, 0x3b, 0x04, 0x13, 0x41, 0x17, 0xca, 0x32, 0xef, 0x8b, 0x63, 0x0e,
+ 0xa4, 0x8c, 0xd6, 0x2f, 0x7d, 0xef, 0x07, 0xd7, 0x9e, 0xfb, 0xfe, 0x0f, 0xae, 0x3d, 0xf7, 0xf9,
+ 0x0f, 0xae, 0x3d, 0xf7, 0x2b, 0x47, 0xd7, 0x0a, 0xdf, 0x3b, 0xba, 0x56, 0xf8, 0xfe, 0xd1, 0xb5,
+ 0xc2, 0xe7, 0x47, 0xd7, 0x0a, 0xff, 0xf9, 0xe8, 0x5a, 0xe1, 0x6f, 0xfc, 0x97, 0x6b, 0xcf, 0xfd,
+ 0xe2, 0x9b, 0x51, 0x11, 0xe6, 0x54, 0x11, 0xe6, 0x94, 0xc0, 0xb9, 0xde, 0x5e, 0x67, 0x8e, 0x15,
+ 0x21, 0x4a, 0x51, 0x45, 0xf8, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x90, 0x35, 0xbe, 0x7d, 0xfe,
+ 0x99, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -6331,6 +6403,16 @@ func (m *MonoVertexSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ {
+ size, err := m.UpdateStrategy.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x62
if m.DaemonTemplate != nil {
{
size, err := m.DaemonTemplate.MarshalToSizedBuffer(dAtA[:i])
@@ -6485,24 +6567,24 @@ func (m *MonoVertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
copy(dAtA[i:], m.UpdateHash)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.UpdateHash)))
i--
- dAtA[i] = 0x72
+ dAtA[i] = 0x7a
i -= len(m.CurrentHash)
copy(dAtA[i:], m.CurrentHash)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.CurrentHash)))
i--
- dAtA[i] = 0x6a
+ dAtA[i] = 0x72
+ i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReadyReplicas))
+ i--
+ dAtA[i] = 0x68
i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReplicas))
i--
dAtA[i] = 0x60
- i = encodeVarintGenerated(dAtA, i, uint64(m.CurrentReplicas))
- i--
- dAtA[i] = 0x58
i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas))
i--
- dAtA[i] = 0x50
+ dAtA[i] = 0x58
i = encodeVarintGenerated(dAtA, i, uint64(m.ObservedGeneration))
i--
- dAtA[i] = 0x48
+ dAtA[i] = 0x50
{
size, err := m.LastScaledAt.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
@@ -6512,7 +6594,7 @@ func (m *MonoVertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
- dAtA[i] = 0x42
+ dAtA[i] = 0x4a
{
size, err := m.LastUpdated.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
@@ -6522,22 +6604,25 @@ func (m *MonoVertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
- dAtA[i] = 0x3a
+ dAtA[i] = 0x42
i -= len(m.Message)
copy(dAtA[i:], m.Message)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Message)))
i--
- dAtA[i] = 0x32
+ dAtA[i] = 0x3a
i -= len(m.Reason)
copy(dAtA[i:], m.Reason)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Reason)))
i--
- dAtA[i] = 0x2a
+ dAtA[i] = 0x32
i -= len(m.Selector)
copy(dAtA[i:], m.Selector)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Selector)))
i--
- dAtA[i] = 0x22
+ dAtA[i] = 0x2a
+ i = encodeVarintGenerated(dAtA, i, uint64(m.DesiredReplicas))
+ i--
+ dAtA[i] = 0x20
i = encodeVarintGenerated(dAtA, i, uint64(m.Replicas))
i--
dAtA[i] = 0x18
@@ -7483,6 +7568,41 @@ func (m *RetryStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *RollingUpdateStrategy) 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 *RollingUpdateStrategy) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *RollingUpdateStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if m.MaxUnavailable != nil {
+ {
+ size, err := m.MaxUnavailable.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
func (m *SASL) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -8679,6 +8799,46 @@ func (m *UDTransformer) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *UpdateStrategy) 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 *UpdateStrategy) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *UpdateStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if m.RollingUpdate != nil {
+ {
+ size, err := m.RollingUpdate.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x12
+ }
+ i -= len(m.Type)
+ copy(dAtA[i:], m.Type)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.Type)))
+ i--
+ dAtA[i] = 0xa
+ return len(dAtA) - i, nil
+}
+
func (m *Vertex) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -10411,6 +10571,8 @@ func (m *MonoVertexSpec) Size() (n int) {
l = m.DaemonTemplate.Size()
n += 1 + l + sovGenerated(uint64(l))
}
+ l = m.UpdateStrategy.Size()
+ n += 1 + l + sovGenerated(uint64(l))
return n
}
@@ -10425,6 +10587,7 @@ func (m *MonoVertexStatus) Size() (n int) {
l = len(m.Phase)
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.Replicas))
+ n += 1 + sovGenerated(uint64(m.DesiredReplicas))
l = len(m.Selector)
n += 1 + l + sovGenerated(uint64(l))
l = len(m.Reason)
@@ -10437,8 +10600,8 @@ func (m *MonoVertexStatus) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.ObservedGeneration))
n += 1 + sovGenerated(uint64(m.ReadyReplicas))
- n += 1 + sovGenerated(uint64(m.CurrentReplicas))
n += 1 + sovGenerated(uint64(m.UpdatedReplicas))
+ n += 1 + sovGenerated(uint64(m.UpdatedReadyReplicas))
l = len(m.CurrentHash)
n += 1 + l + sovGenerated(uint64(l))
l = len(m.UpdateHash)
@@ -10789,6 +10952,19 @@ func (m *RetryStrategy) Size() (n int) {
return n
}
+func (m *RollingUpdateStrategy) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.MaxUnavailable != nil {
+ l = m.MaxUnavailable.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ return n
+}
+
func (m *SASL) Size() (n int) {
if m == nil {
return 0
@@ -11236,6 +11412,21 @@ func (m *UDTransformer) Size() (n int) {
return n
}
+func (m *UpdateStrategy) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.Type)
+ n += 1 + l + sovGenerated(uint64(l))
+ if m.RollingUpdate != nil {
+ l = m.RollingUpdate.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ return n
+}
+
func (m *Vertex) Size() (n int) {
if m == nil {
return 0
@@ -12270,6 +12461,7 @@ func (this *MonoVertexSpec) String() string {
`InitContainers:` + repeatedStringForInitContainers + `,`,
`Sidecars:` + repeatedStringForSidecars + `,`,
`DaemonTemplate:` + strings.Replace(this.DaemonTemplate.String(), "DaemonTemplate", "DaemonTemplate", 1) + `,`,
+ `UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "UpdateStrategy", "UpdateStrategy", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
@@ -12282,6 +12474,7 @@ func (this *MonoVertexStatus) String() string {
`Status:` + strings.Replace(strings.Replace(this.Status.String(), "Status", "Status", 1), `&`, ``, 1) + `,`,
`Phase:` + fmt.Sprintf("%v", this.Phase) + `,`,
`Replicas:` + fmt.Sprintf("%v", this.Replicas) + `,`,
+ `DesiredReplicas:` + fmt.Sprintf("%v", this.DesiredReplicas) + `,`,
`Selector:` + fmt.Sprintf("%v", this.Selector) + `,`,
`Reason:` + fmt.Sprintf("%v", this.Reason) + `,`,
`Message:` + fmt.Sprintf("%v", this.Message) + `,`,
@@ -12289,8 +12482,8 @@ func (this *MonoVertexStatus) String() string {
`LastScaledAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.LastScaledAt), "Time", "v11.Time", 1), `&`, ``, 1) + `,`,
`ObservedGeneration:` + fmt.Sprintf("%v", this.ObservedGeneration) + `,`,
`ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`,
- `CurrentReplicas:` + fmt.Sprintf("%v", this.CurrentReplicas) + `,`,
`UpdatedReplicas:` + fmt.Sprintf("%v", this.UpdatedReplicas) + `,`,
+ `UpdatedReadyReplicas:` + fmt.Sprintf("%v", this.UpdatedReadyReplicas) + `,`,
`CurrentHash:` + fmt.Sprintf("%v", this.CurrentHash) + `,`,
`UpdateHash:` + fmt.Sprintf("%v", this.UpdateHash) + `,`,
`}`,
@@ -12518,6 +12711,16 @@ func (this *RetryStrategy) String() string {
}, "")
return s
}
+func (this *RollingUpdateStrategy) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&RollingUpdateStrategy{`,
+ `MaxUnavailable:` + strings.Replace(fmt.Sprintf("%v", this.MaxUnavailable), "IntOrString", "intstr.IntOrString", 1) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func (this *SASL) String() string {
if this == nil {
return "nil"
@@ -12798,6 +13001,17 @@ func (this *UDTransformer) String() string {
}, "")
return s
}
+func (this *UpdateStrategy) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&UpdateStrategy{`,
+ `Type:` + fmt.Sprintf("%v", this.Type) + `,`,
+ `RollingUpdate:` + strings.Replace(this.RollingUpdate.String(), "RollingUpdateStrategy", "RollingUpdateStrategy", 1) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func (this *Vertex) String() string {
if this == nil {
return "nil"
@@ -23365,6 +23579,39 @@ func (m *MonoVertexSpec) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 12:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdateStrategy", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.UpdateStrategy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -23500,6 +23747,25 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
}
}
case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field DesiredReplicas", wireType)
+ }
+ m.DesiredReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.DesiredReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Selector", wireType)
}
@@ -23531,7 +23797,7 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
}
m.Selector = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 5:
+ case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType)
}
@@ -23563,7 +23829,7 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
}
m.Reason = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 6:
+ case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
}
@@ -23595,7 +23861,7 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
}
m.Message = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 7:
+ case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LastUpdated", wireType)
}
@@ -23628,7 +23894,7 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
- case 8:
+ case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LastScaledAt", wireType)
}
@@ -23661,7 +23927,7 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
- case 9:
+ case 10:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ObservedGeneration", wireType)
}
@@ -23680,7 +23946,7 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
- case 10:
+ case 11:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ReadyReplicas", wireType)
}
@@ -23699,11 +23965,11 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
- case 11:
+ case 12:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field CurrentReplicas", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType)
}
- m.CurrentReplicas = 0
+ m.UpdatedReplicas = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
@@ -23713,16 +23979,16 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
- m.CurrentReplicas |= uint32(b&0x7F) << shift
+ m.UpdatedReplicas |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
- case 12:
+ case 13:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReadyReplicas", wireType)
}
- m.UpdatedReplicas = 0
+ m.UpdatedReadyReplicas = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
@@ -23732,12 +23998,12 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
- m.UpdatedReplicas |= uint32(b&0x7F) << shift
+ m.UpdatedReadyReplicas |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
- case 13:
+ case 14:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CurrentHash", wireType)
}
@@ -23769,7 +24035,7 @@ func (m *MonoVertexStatus) Unmarshal(dAtA []byte) error {
}
m.CurrentHash = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 14:
+ case 15:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field UpdateHash", wireType)
}
@@ -26651,6 +26917,92 @@ func (m *RetryStrategy) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *RollingUpdateStrategy) 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 ErrIntOverflowGenerated
+ }
+ 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: RollingUpdateStrategy: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: RollingUpdateStrategy: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field MaxUnavailable", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.MaxUnavailable == nil {
+ m.MaxUnavailable = &intstr.IntOrString{}
+ }
+ if err := m.MaxUnavailable.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *SASL) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -30044,6 +30396,124 @@ func (m *UDTransformer) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *UpdateStrategy) 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 ErrIntOverflowGenerated
+ }
+ 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: UpdateStrategy: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: UpdateStrategy: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ 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 ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Type = UpdateStrategyType(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RollingUpdate", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.RollingUpdate == nil {
+ m.RollingUpdate = &RollingUpdateStrategy{}
+ }
+ if err := m.RollingUpdate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *Vertex) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 55940285e3..1e6e6bd35a 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -26,6 +26,7 @@ import "k8s.io/apimachinery/pkg/api/resource/generated.proto";
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
+import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1";
@@ -864,7 +865,7 @@ message Metadata {
// +kubebuilder:subresource:status
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
-// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
+// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.status.desiredReplicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
@@ -942,6 +943,11 @@ message MonoVertexSpec {
// Template for the daemon service deployment.
// +optional
optional DaemonTemplate daemonTemplate = 11;
+
+ // The strategy to use to replace existing pods with new ones.
+ // +kubebuilder:default={"type": "RollingUpdate", "rollingUpdate": {"maxUnavailable": "25%"}}
+ // +optional
+ optional UpdateStrategy updateStrategy = 12;
}
message MonoVertexStatus {
@@ -954,41 +960,45 @@ message MonoVertexStatus {
// +optional
optional uint32 replicas = 3;
+ // The number of desired replicas.
// +optional
- optional string selector = 4;
+ optional uint32 desiredReplicas = 4;
// +optional
- optional string reason = 5;
+ optional string selector = 5;
// +optional
- optional string message = 6;
+ optional string reason = 6;
+
+ // +optional
+ optional string message = 7;
// +optional
- optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastUpdated = 7;
+ optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastUpdated = 8;
// Time of last scaling operation.
// +optional
- optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastScaledAt = 8;
+ optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastScaledAt = 9;
// The generation observed by the MonoVertex controller.
// +optional
- optional int64 observedGeneration = 9;
+ optional int64 observedGeneration = 10;
// The number of pods targeted by this MonoVertex with a Ready Condition.
// +optional
- optional uint32 readyReplicas = 10;
-
- // The number of Pods created by the controller from the MonoVertex version indicated by currentHash.
- optional uint32 currentReplicas = 11;
+ optional uint32 readyReplicas = 11;
// The number of Pods created by the controller from the MonoVertex version indicated by updateHash.
optional uint32 updatedReplicas = 12;
- // If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).
- optional string currentHash = 13;
+ // The number of ready Pods created by the controller from the MonoVertex version indicated by updateHash.
+ optional uint32 updatedReadyReplicas = 13;
- // If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
- optional string updateHash = 14;
+ // If not empty, indicates the current version of the MonoVertex used to generate Pods.
+ optional string currentHash = 14;
+
+ // If not empty, indicates the updated version of the MonoVertex used to generate Pods.
+ optional string updateHash = 15;
}
message NativeRedis {
@@ -1291,6 +1301,21 @@ message RetryStrategy {
optional string onFailure = 2;
}
+// RollingUpdateStrategy is used to communicate parameter for RollingUpdateStrategyType.
+message RollingUpdateStrategy {
+ // The maximum number of pods that can be unavailable during the update.
+ // Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%).
+ // Absolute number is calculated from percentage by rounding down.
+ // Defaults to 25%.
+ // Example: when this is set to 30%, the old pods can be scaled down to 70% of desired pods
+ // immediately when the rolling update starts. Once new pods are ready, old pods
+ // can be scaled down further, followed by scaling up the new pods, ensuring
+ // that the total number of pods available at all times during the update is at
+ // least 70% of desired pods.
+ // +optional
+ optional k8s.io.apimachinery.pkg.util.intstr.IntOrString maxUnavailable = 1;
+}
+
message SASL {
// SASL mechanism to use
optional string mechanism = 1;
@@ -1599,6 +1624,19 @@ message UDTransformer {
optional Transformer builtin = 2;
}
+// UpdateStrategy indicates the strategy that the
+// controller will use to perform updates for Vertex or MonoVertex.
+message UpdateStrategy {
+ // Type indicates the type of the StatefulSetUpdateStrategy.
+ // Default is RollingUpdate.
+ // +optional
+ optional string type = 1;
+
+ // RollingUpdate is used to communicate parameters when Type is RollingUpdateStrategy.
+ // +optional
+ optional RollingUpdateStrategy rollingUpdate = 2;
+}
+
// +genclient
// +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=vtx
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index b05fd8c5f4..677ec4fc5c 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -54,7 +54,7 @@ const (
// +kubebuilder:subresource:status
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
-// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
+// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.status.desiredReplicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
@@ -71,13 +71,28 @@ type MonoVertex struct {
Status MonoVertexStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
-func (mv MonoVertex) GetReplicas() int {
+func (mv MonoVertex) getReplicas() int {
if mv.Spec.Replicas == nil {
return 1
}
return int(*mv.Spec.Replicas)
}
+func (mv MonoVertex) CalculateReplicas() int {
+ desiredReplicas := mv.getReplicas()
+ // Don't allow replicas to be out of the range of min and max when auto scaling is enabled
+ if s := mv.Spec.Scale; !s.Disabled {
+ max := int(s.GetMaxReplicas())
+ min := int(s.GetMinReplicas())
+ if desiredReplicas < min {
+ desiredReplicas = min
+ } else if desiredReplicas > max {
+ desiredReplicas = max
+ }
+ }
+ return desiredReplicas
+}
+
func (mv MonoVertex) GetHeadlessServiceName() string {
return mv.Name + "-mv-headless"
}
@@ -291,6 +306,7 @@ func (mv MonoVertex) simpleCopy() MonoVertex {
if m.Spec.Limits.ReadTimeout == nil {
m.Spec.Limits.ReadTimeout = &metav1.Duration{Duration: DefaultReadTimeout}
}
+ m.Spec.UpdateStrategy = UpdateStrategy{}
// TODO: lifecycle
// mvVtxCopy.Spec.Lifecycle = Lifecycle{}
return m
@@ -408,6 +424,10 @@ type MonoVertexSpec struct {
// Template for the daemon service deployment.
// +optional
DaemonTemplate *DaemonTemplate `json:"daemonTemplate,omitempty" protobuf:"bytes,11,opt,name=daemonTemplate"`
+ // The strategy to use to replace existing pods with new ones.
+ // +kubebuilder:default={"type": "RollingUpdate", "rollingUpdate": {"maxUnavailable": "25%"}}
+ // +optional
+ UpdateStrategy UpdateStrategy `json:"updateStrategy,omitempty" protobuf:"bytes,12,opt,name=updateStrategy"`
}
func (mvspec MonoVertexSpec) DeepCopyWithoutReplicas() MonoVertexSpec {
@@ -470,31 +490,34 @@ type MonoVertexStatus struct {
// Total number of non-terminated pods targeted by this MonoVertex (their labels match the selector).
// +optional
Replicas uint32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
+ // The number of desired replicas.
+ // +optional
+ DesiredReplicas uint32 `json:"desiredReplicas" protobuf:"varint,4,opt,name=desiredReplicas"`
// +optional
- Selector string `json:"selector,omitempty" protobuf:"bytes,4,opt,name=selector"`
+ Selector string `json:"selector,omitempty" protobuf:"bytes,5,opt,name=selector"`
// +optional
- Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
+ Reason string `json:"reason,omitempty" protobuf:"bytes,6,opt,name=reason"`
// +optional
- Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
+ Message string `json:"message,omitempty" protobuf:"bytes,7,opt,name=message"`
// +optional
- LastUpdated metav1.Time `json:"lastUpdated,omitempty" protobuf:"bytes,7,opt,name=lastUpdated"`
+ LastUpdated metav1.Time `json:"lastUpdated,omitempty" protobuf:"bytes,8,opt,name=lastUpdated"`
// Time of last scaling operation.
// +optional
- LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,8,opt,name=lastScaledAt"`
+ LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,9,opt,name=lastScaledAt"`
// The generation observed by the MonoVertex controller.
// +optional
- ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,9,opt,name=observedGeneration"`
+ ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,10,opt,name=observedGeneration"`
// The number of pods targeted by this MonoVertex with a Ready Condition.
// +optional
- ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,10,opt,name=readyReplicas"`
- // The number of Pods created by the controller from the MonoVertex version indicated by currentHash.
- CurrentReplicas uint32 `json:"currentReplicas,omitempty" protobuf:"varint,11,opt,name=currentReplicas"`
+ ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,11,opt,name=readyReplicas"`
// The number of Pods created by the controller from the MonoVertex version indicated by updateHash.
UpdatedReplicas uint32 `json:"updatedReplicas,omitempty" protobuf:"varint,12,opt,name=updatedReplicas"`
- // If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).
- CurrentHash string `json:"currentHash,omitempty" protobuf:"bytes,13,opt,name=currentHash"`
- // If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
- UpdateHash string `json:"updateHash,omitempty" protobuf:"bytes,14,opt,name=updateHash"`
+ // The number of ready Pods created by the controller from the MonoVertex version indicated by updateHash.
+ UpdatedReadyReplicas uint32 `json:"updatedReadyReplicas,omitempty" protobuf:"varint,13,opt,name=updatedReadyReplicas"`
+ // If not empty, indicates the current version of the MonoVertex used to generate Pods.
+ CurrentHash string `json:"currentHash,omitempty" protobuf:"bytes,14,opt,name=currentHash"`
+ // If not empty, indicates the updated version of the MonoVertex used to generate Pods.
+ UpdateHash string `json:"updateHash,omitempty" protobuf:"bytes,15,opt,name=updateHash"`
}
// SetObservedGeneration sets the Status ObservedGeneration
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 0ed5471bc8..a339fdfb7a 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -93,6 +93,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisConfig": schema_pkg_apis_numaflow_v1alpha1_RedisConfig(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisSettings": schema_pkg_apis_numaflow_v1alpha1_RedisSettings(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RetryStrategy": schema_pkg_apis_numaflow_v1alpha1_RetryStrategy(ref),
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RollingUpdateStrategy": schema_pkg_apis_numaflow_v1alpha1_RollingUpdateStrategy(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.SASL": schema_pkg_apis_numaflow_v1alpha1_SASL(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.SASLPlain": schema_pkg_apis_numaflow_v1alpha1_SASLPlain(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale": schema_pkg_apis_numaflow_v1alpha1_Scale(ref),
@@ -114,6 +115,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDSink": schema_pkg_apis_numaflow_v1alpha1_UDSink(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDSource": schema_pkg_apis_numaflow_v1alpha1_UDSource(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDTransformer": schema_pkg_apis_numaflow_v1alpha1_UDTransformer(ref),
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy": schema_pkg_apis_numaflow_v1alpha1_UpdateStrategy(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Vertex": schema_pkg_apis_numaflow_v1alpha1_Vertex(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexInstance": schema_pkg_apis_numaflow_v1alpha1_VertexInstance(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits": schema_pkg_apis_numaflow_v1alpha1_VertexLimits(ref),
@@ -3332,11 +3334,18 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexSpec(ref common.ReferenceCallba
Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.DaemonTemplate"),
},
},
+ "updateStrategy": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The strategy to use to replace existing pods with new ones.",
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy"),
+ },
+ },
},
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.DaemonTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.DaemonTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
}
}
@@ -3380,6 +3389,14 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexStatus(ref common.ReferenceCall
Format: "int64",
},
},
+ "desiredReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of desired replicas.",
+ Default: 0,
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
"selector": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
@@ -3423,30 +3440,30 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexStatus(ref common.ReferenceCall
Format: "int64",
},
},
- "currentReplicas": {
+ "updatedReplicas": {
SchemaProps: spec.SchemaProps{
- Description: "The number of Pods created by the controller from the MonoVertex version indicated by currentHash.",
+ Description: "The number of Pods created by the controller from the MonoVertex version indicated by updateHash.",
Type: []string{"integer"},
Format: "int64",
},
},
- "updatedReplicas": {
+ "updatedReadyReplicas": {
SchemaProps: spec.SchemaProps{
- Description: "The number of Pods created by the controller from the MonoVertex version indicated by updateHash.",
+ Description: "The number of ready Pods created by the controller from the MonoVertex version indicated by updateHash.",
Type: []string{"integer"},
Format: "int64",
},
},
"currentHash": {
SchemaProps: spec.SchemaProps{
- Description: "If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).",
+ Description: "If not empty, indicates the current version of the MonoVertex used to generate Pods.",
Type: []string{"string"},
Format: "",
},
},
"updateHash": {
SchemaProps: spec.SchemaProps{
- Description: "If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ Description: "If not empty, indicates the updated version of the MonoVertex used to generate Pods.",
Type: []string{"string"},
Format: "",
},
@@ -4295,6 +4312,27 @@ func schema_pkg_apis_numaflow_v1alpha1_RetryStrategy(ref common.ReferenceCallbac
}
}
+func schema_pkg_apis_numaflow_v1alpha1_RollingUpdateStrategy(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "RollingUpdateStrategy is used to communicate parameter for RollingUpdateStrategyType.",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "maxUnavailable": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. Defaults to 25%. Example: when this is set to 30%, the old pods can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old pods can be scaled down further, followed by scaling up the new pods, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods.",
+ Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"),
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "k8s.io/apimachinery/pkg/util/intstr.IntOrString"},
+ }
+}
+
func schema_pkg_apis_numaflow_v1alpha1_SASL(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -5232,6 +5270,35 @@ func schema_pkg_apis_numaflow_v1alpha1_UDTransformer(ref common.ReferenceCallbac
}
}
+func schema_pkg_apis_numaflow_v1alpha1_UpdateStrategy(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "UpdateStrategy indicates the strategy that the controller will use to perform updates for Vertex or MonoVertex.",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "type": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate.\n\nPossible enum values:\n - `\"RollingUpdate\"`",
+ Type: []string{"string"},
+ Format: "",
+ Enum: []interface{}{"RollingUpdate"},
+ },
+ },
+ "rollingUpdate": {
+ SchemaProps: spec.SchemaProps{
+ Description: "RollingUpdate is used to communicate parameters when Type is RollingUpdateStrategy.",
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RollingUpdateStrategy"),
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RollingUpdateStrategy"},
+ }
+}
+
func schema_pkg_apis_numaflow_v1alpha1_Vertex(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
diff --git a/pkg/apis/numaflow/v1alpha1/update_strategy.go b/pkg/apis/numaflow/v1alpha1/update_strategy.go
new file mode 100644
index 0000000000..5313d06725
--- /dev/null
+++ b/pkg/apis/numaflow/v1alpha1/update_strategy.go
@@ -0,0 +1,80 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "k8s.io/apimachinery/pkg/util/intstr"
+)
+
+// UpdateStrategy indicates the strategy that the
+// controller will use to perform updates for Vertex or MonoVertex.
+type UpdateStrategy struct {
+ // Type indicates the type of the StatefulSetUpdateStrategy.
+ // Default is RollingUpdate.
+ // +optional
+ Type UpdateStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=UpdateStrategyType"`
+ // RollingUpdate is used to communicate parameters when Type is RollingUpdateStrategy.
+ // +optional
+ RollingUpdate *RollingUpdateStrategy `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"`
+}
+
+func (us UpdateStrategy) GetUpdateStrategyType() UpdateStrategyType {
+ switch us.Type {
+ case RollingUpdateStrategyType:
+ return us.Type
+ default:
+ return RollingUpdateStrategyType // We only support RollingUpdateStrategyType for now.
+ }
+}
+
+func (us UpdateStrategy) GetRollingUpdateStrategy() RollingUpdateStrategy {
+ if us.RollingUpdate == nil {
+ return RollingUpdateStrategy{}
+ }
+ return *us.RollingUpdate
+}
+
+// UpdateStrategyType is a string enumeration type that enumerates
+// all possible update strategies.
+// +enum
+type UpdateStrategyType string
+
+const (
+ RollingUpdateStrategyType UpdateStrategyType = "RollingUpdate"
+)
+
+// RollingUpdateStrategy is used to communicate parameter for RollingUpdateStrategyType.
+type RollingUpdateStrategy struct {
+ // The maximum number of pods that can be unavailable during the update.
+ // Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%).
+ // Absolute number is calculated from percentage by rounding down.
+ // Defaults to 25%.
+ // Example: when this is set to 30%, the old pods can be scaled down to 70% of desired pods
+ // immediately when the rolling update starts. Once new pods are ready, old pods
+ // can be scaled down further, followed by scaling up the new pods, ensuring
+ // that the total number of pods available at all times during the update is at
+ // least 70% of desired pods.
+ // +optional
+ MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,1,opt,name=maxUnavailable"`
+}
+
+func (rus RollingUpdateStrategy) GetMaxUnavailable() intstr.IntOrString {
+ if rus.MaxUnavailable == nil {
+ return intstr.FromString("25%") // Default value is 25%.
+ }
+ return *rus.MaxUnavailable
+}
diff --git a/pkg/apis/numaflow/v1alpha1/update_strategy_test.go b/pkg/apis/numaflow/v1alpha1/update_strategy_test.go
new file mode 100644
index 0000000000..e78cd34d95
--- /dev/null
+++ b/pkg/apis/numaflow/v1alpha1/update_strategy_test.go
@@ -0,0 +1,128 @@
+package v1alpha1
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ intstr "k8s.io/apimachinery/pkg/util/intstr"
+ "k8s.io/utils/ptr"
+)
+
+func TestUpdateStrategy_GetUpdateStrategyType(t *testing.T) {
+ tests := []struct {
+ name string
+ strategy UpdateStrategy
+ expected UpdateStrategyType
+ }{
+ {
+ name: "RollingUpdateStrategyType",
+ strategy: UpdateStrategy{Type: RollingUpdateStrategyType},
+ expected: RollingUpdateStrategyType,
+ },
+ {
+ name: "EmptyType",
+ strategy: UpdateStrategy{},
+ expected: RollingUpdateStrategyType,
+ },
+ {
+ name: "UnsupportedType",
+ strategy: UpdateStrategy{Type: "UnsupportedType"},
+ expected: RollingUpdateStrategyType,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := tt.strategy.GetUpdateStrategyType()
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
+func TestUpdateStrategy_GetRollingUpdateStrategy(t *testing.T) {
+ tests := []struct {
+ name string
+ strategy UpdateStrategy
+ expected RollingUpdateStrategy
+ }{
+ {
+ name: "NilRollingUpdate",
+ strategy: UpdateStrategy{},
+ expected: RollingUpdateStrategy{},
+ },
+ {
+ name: "NonNilRollingUpdate",
+ strategy: UpdateStrategy{
+ RollingUpdate: &RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromString("2")),
+ },
+ },
+ expected: RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromString("2")),
+ },
+ },
+ {
+ name: "EmptyRollingUpdate",
+ strategy: UpdateStrategy{
+ RollingUpdate: &RollingUpdateStrategy{},
+ },
+ expected: RollingUpdateStrategy{},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := tt.strategy.GetRollingUpdateStrategy()
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
+func TestRollingUpdateStrategy_GetMaxUnavailable(t *testing.T) {
+ tests := []struct {
+ name string
+ strategy RollingUpdateStrategy
+ expected intstr.IntOrString
+ }{
+ {
+ name: "NilMaxUnavailable",
+ strategy: RollingUpdateStrategy{},
+ expected: intstr.FromString("25%"),
+ },
+ {
+ name: "IntegerMaxUnavailable",
+ strategy: RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromInt(5)),
+ },
+ expected: intstr.FromInt(5),
+ },
+ {
+ name: "StringMaxUnavailable",
+ strategy: RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromString("50%")),
+ },
+ expected: intstr.FromString("50%"),
+ },
+ {
+ name: "ZeroIntegerMaxUnavailable",
+ strategy: RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromInt(0)),
+ },
+ expected: intstr.FromInt(0),
+ },
+ {
+ name: "ZeroPercentMaxUnavailable",
+ strategy: RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromString("0%")),
+ },
+ expected: intstr.FromString("0%"),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := tt.strategy.GetMaxUnavailable()
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index 6033c0302f..b6325ce920 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -25,6 +25,7 @@ import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
+ intstr "k8s.io/apimachinery/pkg/util/intstr"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@@ -1502,6 +1503,7 @@ func (in *MonoVertexSpec) DeepCopyInto(out *MonoVertexSpec) {
*out = new(DaemonTemplate)
(*in).DeepCopyInto(*out)
}
+ in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy)
return
}
@@ -2009,6 +2011,27 @@ func (in *RetryStrategy) DeepCopy() *RetryStrategy {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RollingUpdateStrategy) DeepCopyInto(out *RollingUpdateStrategy) {
+ *out = *in
+ if in.MaxUnavailable != nil {
+ in, out := &in.MaxUnavailable, &out.MaxUnavailable
+ *out = new(intstr.IntOrString)
+ **out = **in
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RollingUpdateStrategy.
+func (in *RollingUpdateStrategy) DeepCopy() *RollingUpdateStrategy {
+ if in == nil {
+ return nil
+ }
+ out := new(RollingUpdateStrategy)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SASL) DeepCopyInto(out *SASL) {
*out = *in
@@ -2654,6 +2677,27 @@ func (in *UDTransformer) DeepCopy() *UDTransformer {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UpdateStrategy) DeepCopyInto(out *UpdateStrategy) {
+ *out = *in
+ if in.RollingUpdate != nil {
+ in, out := &in.RollingUpdate, &out.RollingUpdate
+ *out = new(RollingUpdateStrategy)
+ (*in).DeepCopyInto(*out)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateStrategy.
+func (in *UpdateStrategy) DeepCopy() *UpdateStrategy {
+ if in == nil {
+ return nil
+ }
+ out := new(UpdateStrategy)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Vertex) DeepCopyInto(out *Vertex) {
*out = *in
diff --git a/pkg/mvtxdaemon/server/service/health_status.go b/pkg/mvtxdaemon/server/service/health_status.go
index 9942f7885c..b075555aff 100644
--- a/pkg/mvtxdaemon/server/service/health_status.go
+++ b/pkg/mvtxdaemon/server/service/health_status.go
@@ -132,20 +132,18 @@ func (hc *HealthChecker) setCurrentHealth(status *dataHealthResponse) {
// Else we consider the data criticality as healthy.
//
// TODO(MonoVertex): Add the logic to determine the warning state based on more conditions.
-func (hc *HealthChecker) getMonoVertexDataCriticality(ctx context.Context, mvtxMetrics *mvtxdaemon.MonoVertexMetrics) (*monoVtxState, error) {
+func (hc *HealthChecker) getMonoVertexDataCriticality(_ context.Context, mvtxMetrics *mvtxdaemon.MonoVertexMetrics) (*monoVtxState, error) {
// Get the desired replicas for the MonoVertex based on the metrics
desiredReplicas, err := hc.getDesiredReplica(mvtxMetrics)
if err != nil {
return nil, err
}
- // Get the current state of the MonoVertex replicas
- currentReplicas := hc.monoVertex.GetReplicas()
maxReplicas := int(hc.monoVertex.Spec.Scale.GetMaxReplicas())
// default status is healthy
status := v1alpha1.MonoVertexStatusHealthy
- // If the current replicas are equal to the max replicas, and the desired replicas are more than the max replicas,
+ // If the desired replicas are more than the max replicas,
// the data criticality is Critical.
- if currentReplicas == maxReplicas && desiredReplicas > maxReplicas {
+ if desiredReplicas > maxReplicas {
status = v1alpha1.MonoVertexStatusCritical
}
return newMonoVtxState(mvtxMetrics.MonoVertex, status), nil
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index 104b7d9728..a0de7e6d6a 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -32,6 +32,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -113,36 +114,19 @@ func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.Mon
if monoVtx.Scalable() {
mr.scaler.StartWatching(mVtxKey)
}
- // TODO: handle lifecycle changes
-
- // Regular mono vertex change
- result, err := mr.reconcileNonLifecycleChanges(ctx, monoVtx)
- if err != nil {
- mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "ReconcileMonoVertexFailed", "Failed to reconcile a mono vertex: %v", err.Error())
- }
- return result, err
-}
-func (mr *monoVertexReconciler) reconcileNonLifecycleChanges(ctx context.Context, monoVtx *dfv1.MonoVertex) (ctrl.Result, error) {
- // Create or update mono vtx services
- if err := mr.createOrUpdateMonoVtxServices(ctx, monoVtx); err != nil {
+ if err := mr.orchestrateFixedResources(ctx, monoVtx); err != nil {
+ monoVtx.Status.MarkDeployFailed("OrchestrateFixedResourcesFailed", err.Error())
return ctrl.Result{}, err
}
- // Mono vtx daemon service
- if err := mr.createOrUpdateDaemonService(ctx, monoVtx); err != nil {
- return ctrl.Result{}, err
- }
+ // TODO: handle lifecycle changes
- // Mono vtx daemon deployment
- if err := mr.createOrUpdateDaemonDeployment(ctx, monoVtx); err != nil {
+ if err := mr.orchestratePods(ctx, monoVtx); err != nil {
+ monoVtx.Status.MarkDeployFailed("OrchestratePodsFailed", err.Error())
return ctrl.Result{}, err
}
- // Create pods
- if err := mr.reconcilePods(ctx, monoVtx); err != nil {
- return ctrl.Result{}, err
- }
monoVtx.Status.MarkDeployed()
// Mark it running before checking the status of the pods
@@ -155,42 +139,197 @@ func (mr *monoVertexReconciler) reconcileNonLifecycleChanges(ctx context.Context
return ctrl.Result{}, nil
}
-func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1.MonoVertex) error {
- desiredReplicas := monoVtx.GetReplicas()
- // Don't allow replicas to be out of the range of min and max when auto scaling is enabled
- if s := monoVtx.Spec.Scale; !s.Disabled {
- max := int(s.GetMaxReplicas())
- min := int(s.GetMinReplicas())
- if desiredReplicas < min {
- desiredReplicas = min
- } else if desiredReplicas > max {
- desiredReplicas = max
- }
+// orchestrateFixedResources orchestrates fixed resources such as daemon service related objects for a mono vertex.
+func (mr *monoVertexReconciler) orchestrateFixedResources(ctx context.Context, monoVtx *dfv1.MonoVertex) error {
+ // Create or update mono vtx services
+ if err := mr.createOrUpdateMonoVtxServices(ctx, monoVtx); err != nil {
+ return fmt.Errorf("failed to orchestrate mono vtx services: %w", err)
+ }
+
+ // Mono vtx daemon service
+ if err := mr.createOrUpdateDaemonService(ctx, monoVtx); err != nil {
+ return fmt.Errorf("failed to orchestrate mono vtx daemon service: %w", err)
}
+
+ // Mono vtx daemon deployment
+ if err := mr.createOrUpdateDaemonDeployment(ctx, monoVtx); err != nil {
+ return fmt.Errorf("failed to orchestrate mono vtx daemon deployment: %w", err)
+ }
+ return nil
+}
+
+func (mr *monoVertexReconciler) orchestratePods(ctx context.Context, monoVtx *dfv1.MonoVertex) error {
+ log := logging.FromContext(ctx)
+ desiredReplicas := monoVtx.CalculateReplicas()
+ monoVtx.Status.DesiredReplicas = uint32(desiredReplicas)
+
// Set metrics
defer func() {
reconciler.MonoVertexDesiredReplicas.WithLabelValues(monoVtx.Namespace, monoVtx.Name).Set(float64(desiredReplicas))
reconciler.MonoVertexCurrentReplicas.WithLabelValues(monoVtx.Namespace, monoVtx.Name).Set(float64(monoVtx.Status.Replicas))
}()
+ podSpec, err := mr.buildPodSpec(monoVtx)
+ if err != nil {
+ return fmt.Errorf("failed to generate mono vertex pod spec: %w", err)
+ }
+
+ hash := sharedutil.MustHash(podSpec)
+ if monoVtx.Status.UpdateHash != hash { // New spec, or still processing last update, while new update is coming
+ monoVtx.Status.UpdateHash = hash
+ monoVtx.Status.UpdatedReplicas = 0
+ monoVtx.Status.UpdatedReadyReplicas = 0
+ }
+
+ // Manually or automatically scaled down
+ if currentReplicas := int(monoVtx.Status.Replicas); currentReplicas > desiredReplicas {
+ if err := mr.cleanUpPodsFromTo(ctx, monoVtx, desiredReplicas, currentReplicas); err != nil {
+ return fmt.Errorf("failed to clean up mono vertex pods [%v, %v): %w", desiredReplicas, currentReplicas, err)
+ }
+ monoVtx.Status.Replicas = uint32(desiredReplicas)
+ }
+ updatedReplicas := int(monoVtx.Status.UpdatedReplicas)
+ if updatedReplicas > desiredReplicas {
+ updatedReplicas = desiredReplicas
+ monoVtx.Status.UpdatedReplicas = uint32(updatedReplicas)
+ }
+
+ if updatedReplicas > 0 {
+ // Make sure [0 - updatedReplicas] with hash are in place
+ if err := mr.orchestratePodsFromTo(ctx, monoVtx, *podSpec, 0, updatedReplicas, hash); err != nil {
+ return fmt.Errorf("failed to orchestrate mono vertex pods [0, %v): %w", updatedReplicas, err)
+ }
+ // Wait for the updated pods to be ready before moving on
+ if monoVtx.Status.UpdatedReadyReplicas != monoVtx.Status.UpdatedReplicas {
+ updatedReadyReplicas := 0
+ existingPods, err := mr.findExistingPods(ctx, monoVtx, 0, updatedReplicas)
+ if err != nil {
+ return fmt.Errorf("failed to get pods of a mono vertex: %w", err)
+ }
+ for _, pod := range existingPods {
+ if pod.GetAnnotations()[dfv1.KeyHash] == monoVtx.Status.UpdateHash {
+ if reconciler.IsPodReady(pod) {
+ updatedReadyReplicas++
+ }
+ }
+ }
+ monoVtx.Status.UpdatedReadyReplicas = uint32(updatedReadyReplicas)
+ if updatedReadyReplicas < updatedReplicas {
+ return nil
+ }
+ }
+ }
+
+ if monoVtx.Status.UpdateHash == monoVtx.Status.CurrentHash ||
+ monoVtx.Status.CurrentHash == "" {
+ // 1. Regular scaling operation 2. First time
+ // create (desiredReplicas-updatedReplicas) pods directly
+ if desiredReplicas > updatedReplicas {
+ if err := mr.orchestratePodsFromTo(ctx, monoVtx, *podSpec, updatedReplicas, desiredReplicas, hash); err != nil {
+ return fmt.Errorf("failed to orchestrate mono vertex pods [%v, %v): %w", updatedReplicas, desiredReplicas, err)
+ }
+ }
+ monoVtx.Status.UpdatedReplicas = uint32(desiredReplicas)
+ monoVtx.Status.CurrentHash = monoVtx.Status.UpdateHash
+ } else { // Update scenario
+ if updatedReplicas >= desiredReplicas {
+ return nil
+ }
+
+ // Create more pods
+ if monoVtx.Spec.UpdateStrategy.GetUpdateStrategyType() != dfv1.RollingUpdateStrategyType {
+ // Revisit later, we only support rolling update for now
+ return nil
+ }
+
+ // Calculate the to be updated replicas based on the max unavailable configuration
+ maxUnavailConf := monoVtx.Spec.UpdateStrategy.GetRollingUpdateStrategy().GetMaxUnavailable()
+ toBeUpdated, err := intstr.GetScaledValueFromIntOrPercent(&maxUnavailConf, desiredReplicas, true)
+ if err != nil { // This should never happen since we have validated the configuration
+ return fmt.Errorf("invalid max unavailable configuration in rollingUpdate: %w", err)
+ }
+ if updatedReplicas+toBeUpdated > desiredReplicas {
+ toBeUpdated = desiredReplicas - updatedReplicas
+ }
+ log.Infof("Rolling update %d replicas, [%d, %d)\n", toBeUpdated, updatedReplicas, updatedReplicas+toBeUpdated)
+
+ // Create pods [updatedReplicas, updatedReplicas+toBeUpdated), and clean up any pods in that range that has a different hash
+ if err := mr.orchestratePodsFromTo(ctx, monoVtx, *podSpec, updatedReplicas, updatedReplicas+toBeUpdated, monoVtx.Status.UpdateHash); err != nil {
+ return fmt.Errorf("failed to orchestrate pods [%v, %v)]: %w", updatedReplicas, updatedReplicas+toBeUpdated, err)
+ }
+ monoVtx.Status.UpdatedReplicas = uint32(updatedReplicas + toBeUpdated)
+ if monoVtx.Status.UpdatedReplicas == uint32(desiredReplicas) {
+ monoVtx.Status.CurrentHash = monoVtx.Status.UpdateHash
+ }
+ }
+
+ currentReplicas := int(monoVtx.Status.Replicas)
+ if currentReplicas != desiredReplicas {
+ log.Infow("MonoVertex replicas changed", "currentReplicas", currentReplicas, "desiredReplicas", desiredReplicas)
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "ReplicasScaled", "Replicas changed from %d to %d", currentReplicas, desiredReplicas)
+ monoVtx.Status.Replicas = uint32(desiredReplicas)
+ monoVtx.Status.LastScaledAt = metav1.Time{Time: time.Now()}
+ }
+ if monoVtx.Status.Selector == "" {
+ selector, _ := labels.Parse(dfv1.KeyComponent + "=" + dfv1.ComponentMonoVertex + "," + dfv1.KeyMonoVertexName + "=" + monoVtx.Name)
+ monoVtx.Status.Selector = selector.String()
+ }
+
+ return nil
+}
+
+func (mr *monoVertexReconciler) findExistingPods(ctx context.Context, monoVtx *dfv1.MonoVertex, fromReplica, toReplica int) (map[string]corev1.Pod, error) {
+ pods := &corev1.PodList{}
+ selector, _ := labels.Parse(dfv1.KeyComponent + "=" + dfv1.ComponentMonoVertex + "," + dfv1.KeyMonoVertexName + "=" + monoVtx.Name)
+ if err := mr.client.List(ctx, pods, &client.ListOptions{Namespace: monoVtx.Namespace, LabelSelector: selector}); err != nil {
+ return nil, fmt.Errorf("failed to list mono vertex pods: %w", err)
+ }
+ result := make(map[string]corev1.Pod)
+ for _, pod := range pods.Items {
+ if !pod.DeletionTimestamp.IsZero() {
+ // Ignore pods being deleted
+ continue
+ }
+ replicaStr := pod.GetAnnotations()[dfv1.KeyReplica]
+ replica, _ := strconv.Atoi(replicaStr)
+ if replica >= fromReplica && replica < toReplica {
+ result[pod.Name] = pod
+ }
+ }
+ return result, nil
+}
+
+func (mr *monoVertexReconciler) cleanUpPodsFromTo(ctx context.Context, monoVtx *dfv1.MonoVertex, fromReplica, toReplica int) error {
log := logging.FromContext(ctx)
- existingPods, err := mr.findExistingPods(ctx, monoVtx)
+ existingPods, err := mr.findExistingPods(ctx, monoVtx, fromReplica, toReplica)
if err != nil {
- mr.markDeploymentFailedAndLogEvent(monoVtx, false, log, "FindExistingPodFailed", err.Error(), "Failed to find existing mono vertex pods", zap.Error(err))
- return err
+ return fmt.Errorf("failed to find existing pods: %w", err)
}
- for replica := 0; replica < desiredReplicas; replica++ {
- podSpec, err := mr.buildPodSpec(monoVtx)
- if err != nil {
- mr.markDeploymentFailedAndLogEvent(monoVtx, false, log, "PodSpecGenFailed", err.Error(), "Failed to generate mono vertex pod spec", zap.Error(err))
- return err
+
+ for _, pod := range existingPods {
+ if err := mr.client.Delete(ctx, &pod); err != nil {
+ return fmt.Errorf("failed to delete pod %s: %w", pod.Name, err)
}
- hash := sharedutil.MustHash(podSpec)
+ log.Infof("Deleted MonoVertx pod %s\n", pod.Name)
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "DeletePodSuccess", "Succeeded to delete a mono vertex pod %s", pod.Name)
+ }
+ return nil
+}
+
+// orchestratePodsFromTo orchestrates pods [fromReplica, toReplica], and clean up any pods in that range that has a different hash
+func (mr *monoVertexReconciler) orchestratePodsFromTo(ctx context.Context, monoVtx *dfv1.MonoVertex, podSpec corev1.PodSpec, fromReplica, toReplica int, newHash string) error {
+ log := logging.FromContext(ctx)
+ existingPods, err := mr.findExistingPods(ctx, monoVtx, fromReplica, toReplica)
+ if err != nil {
+ return fmt.Errorf("failed to find existing pods: %w", err)
+ }
+ // Create pods [fromReplica, toReplica)
+ for replica := fromReplica; replica < toReplica; replica++ {
podNamePrefix := fmt.Sprintf("%s-mv-%d-", monoVtx.Name, replica)
needToCreate := true
for existingPodName, existingPod := range existingPods {
if strings.HasPrefix(existingPodName, podNamePrefix) {
- if existingPod.GetAnnotations()[dfv1.KeyHash] == hash && existingPod.Status.Phase != corev1.PodFailed {
+ if existingPod.GetAnnotations()[dfv1.KeyHash] == newHash && existingPod.Status.Phase != corev1.PodFailed {
needToCreate = false
delete(existingPods, existingPodName)
}
@@ -213,7 +352,7 @@ func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1
podLabels[dfv1.KeyComponent] = dfv1.ComponentMonoVertex
podLabels[dfv1.KeyAppName] = monoVtx.Name
podLabels[dfv1.KeyMonoVertexName] = monoVtx.Name
- annotations[dfv1.KeyHash] = hash
+ annotations[dfv1.KeyHash] = newHash
annotations[dfv1.KeyReplica] = strconv.Itoa(replica)
// Defaults to udf
annotations[dfv1.KeyDefaultContainer] = dfv1.CtrMain
@@ -225,12 +364,11 @@ func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1
Annotations: annotations,
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(monoVtx.GetObjectMeta(), dfv1.MonoVertexGroupVersionKind)},
},
- Spec: *podSpec,
+ Spec: podSpec,
}
pod.Spec.Hostname = fmt.Sprintf("%s-mv-%d", monoVtx.Name, replica)
if err := mr.client.Create(ctx, pod); err != nil {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "CreatePodFailed", err.Error(), "Failed to created a mono vertex pod", zap.Error(err))
- return err
+ return fmt.Errorf("failed to create a mono vertex pod: %w", err)
}
log.Infow("Succeeded to create a mono vertex pod", zap.String("pod", pod.Name))
mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "CreatePodSuccess", "Succeeded to create a mono vertex pod %s", pod.Name)
@@ -238,21 +376,9 @@ func (mr *monoVertexReconciler) reconcilePods(ctx context.Context, monoVtx *dfv1
}
for _, v := range existingPods {
if err := mr.client.Delete(ctx, &v); err != nil && !apierrors.IsNotFound(err) {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "DelPodFailed", err.Error(), "Failed to delete a mono vertex pod", zap.Error(err))
- return err
+ return fmt.Errorf("failed to delete pod %s: %w", v.Name, err)
}
}
-
- currentReplicas := int(monoVtx.Status.Replicas)
- if currentReplicas != desiredReplicas || monoVtx.Status.Selector == "" {
- log.Infow("MonoVertex replicas changed", "currentReplicas", currentReplicas, "desiredReplicas", desiredReplicas)
- mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "ReplicasScaled", "Replicas changed from %d to %d", currentReplicas, desiredReplicas)
- monoVtx.Status.Replicas = uint32(desiredReplicas)
- monoVtx.Status.LastScaledAt = metav1.Time{Time: time.Now()}
- }
- selector, _ := labels.Parse(dfv1.KeyComponent + "=" + dfv1.ComponentMonoVertex + "," + dfv1.KeyMonoVertexName + "=" + monoVtx.Name)
- monoVtx.Status.Selector = selector.String()
-
return nil
}
@@ -404,23 +530,6 @@ func (mr *monoVertexReconciler) createOrUpdateDaemonDeployment(ctx context.Conte
return nil
}
-func (mr *monoVertexReconciler) findExistingPods(ctx context.Context, monoVtx *dfv1.MonoVertex) (map[string]corev1.Pod, error) {
- pods := &corev1.PodList{}
- selector, _ := labels.Parse(dfv1.KeyComponent + "=" + dfv1.ComponentMonoVertex + "," + dfv1.KeyMonoVertexName + "=" + monoVtx.Name)
- if err := mr.client.List(ctx, pods, &client.ListOptions{Namespace: monoVtx.Namespace, LabelSelector: selector}); err != nil {
- return nil, fmt.Errorf("failed to list mono vertex pods: %w", err)
- }
- result := make(map[string]corev1.Pod)
- for _, v := range pods.Items {
- if !v.DeletionTimestamp.IsZero() {
- // Ignore pods being deleted
- continue
- }
- result[v.Name] = v
- }
- return result, nil
-}
-
func (mr *monoVertexReconciler) buildPodSpec(monoVtx *dfv1.MonoVertex) (*corev1.PodSpec, error) {
podSpec, err := monoVtx.GetPodSpec(dfv1.GetMonoVertexPodSpecReq{
Image: mr.image,
@@ -482,7 +591,7 @@ func (mr *monoVertexReconciler) checkChildrenResourceStatus(ctx context.Context,
var podList corev1.PodList
if err := mr.client.List(ctx, &podList, &client.ListOptions{Namespace: monoVtx.GetNamespace(), LabelSelector: selector}); err != nil {
monoVtx.Status.MarkPodNotHealthy("ListMonoVerticesPodsFailed", err.Error())
- return fmt.Errorf("failed to get pods of a vertex: %w", err)
+ return fmt.Errorf("failed to get pods of a mono vertex: %w", err)
}
readyPods := reconciler.NumOfReadyPods(podList)
if readyPods > int(monoVtx.Status.Replicas) { // It might happen in some corner cases, such as during rollout
diff --git a/pkg/reconciler/monovertex/controller_test.go b/pkg/reconciler/monovertex/controller_test.go
index d84ddef68a..e9c4f9fdea 100644
--- a/pkg/reconciler/monovertex/controller_test.go
+++ b/pkg/reconciler/monovertex/controller_test.go
@@ -15,3 +15,302 @@ limitations under the License.
*/
package monovertex
+
+import (
+ "context"
+ "strings"
+ "testing"
+
+ "go.uber.org/zap/zaptest"
+ appv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/tools/record"
+ "k8s.io/utils/ptr"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+ dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+ "github.com/numaproj/numaflow/pkg/reconciler"
+ "github.com/numaproj/numaflow/pkg/reconciler/monovertex/scaling"
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ testNamespace = "test-ns"
+ testMonoVtxName = "tmvtx"
+ testFlowImage = "test-d-iamge"
+)
+
+var (
+ testMonoVtx = &dfv1.MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: testNamespace,
+ Name: testMonoVtxName,
+ },
+ Spec: dfv1.MonoVertexSpec{
+ Scale: dfv1.Scale{
+ Min: ptr.To[int32](2),
+ },
+ Source: &dfv1.Source{
+ UDSource: &dfv1.UDSource{
+ Container: &dfv1.Container{
+ Image: "test-source-image",
+ },
+ },
+ UDTransformer: &dfv1.UDTransformer{
+ Container: &dfv1.Container{
+ Image: "test-tf-image",
+ },
+ },
+ },
+ Sink: &dfv1.Sink{
+ AbstractSink: dfv1.AbstractSink{
+ UDSink: &dfv1.UDSink{
+ Container: &dfv1.Container{
+ Image: "test-sink",
+ },
+ },
+ },
+ Fallback: &dfv1.AbstractSink{
+ UDSink: &dfv1.UDSink{
+ Container: &dfv1.Container{
+ Image: "test-fb-sink",
+ },
+ },
+ },
+ },
+ },
+ }
+)
+
+func init() {
+ _ = dfv1.AddToScheme(scheme.Scheme)
+ _ = appv1.AddToScheme(scheme.Scheme)
+ _ = corev1.AddToScheme(scheme.Scheme)
+}
+
+func Test_NewReconciler(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := NewReconciler(cl, scheme.Scheme, reconciler.FakeGlobalConfig(t, nil), testFlowImage, scaling.NewScaler(cl), zaptest.NewLogger(t).Sugar(), record.NewFakeRecorder(64))
+ _, ok := r.(*monoVertexReconciler)
+ assert.True(t, ok)
+}
+
+func Test_BuildPodSpec(t *testing.T) {
+ fakeConfig := reconciler.FakeGlobalConfig(t, nil)
+ cl := fake.NewClientBuilder().Build()
+ r := &monoVertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: fakeConfig,
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ }
+ t.Run("test has everything", func(t *testing.T) {
+ testObj := testMonoVtx.DeepCopy()
+ spec, err := r.buildPodSpec(testObj)
+ assert.NoError(t, err)
+ assert.Equal(t, 5, len(spec.Containers))
+ assert.Equal(t, dfv1.CtrMain, spec.Containers[0].Name)
+ assert.Equal(t, dfv1.CtrUdsource, spec.Containers[1].Name)
+ assert.Equal(t, dfv1.CtrUdtransformer, spec.Containers[2].Name)
+ assert.Equal(t, dfv1.CtrUdsink, spec.Containers[3].Name)
+ assert.Equal(t, dfv1.CtrFallbackUdsink, spec.Containers[4].Name)
+ assert.Equal(t, 0, len(spec.InitContainers))
+ })
+
+ t.Run("test no transformer, no fallback sink", func(t *testing.T) {
+ testObj := testMonoVtx.DeepCopy()
+ testObj.Spec.Source.UDTransformer = nil
+ testObj.Spec.Sink.Fallback = nil
+ spec, err := r.buildPodSpec(testObj)
+ assert.NoError(t, err)
+ assert.Equal(t, 3, len(spec.Containers))
+ assert.Equal(t, dfv1.CtrMain, spec.Containers[0].Name)
+ assert.Equal(t, dfv1.CtrUdsource, spec.Containers[1].Name)
+ assert.Equal(t, dfv1.CtrUdsink, spec.Containers[2].Name)
+ assert.Equal(t, 0, len(spec.InitContainers))
+ })
+}
+
+func Test_createOrUpdateDaemonDeployment(t *testing.T) {
+ fakeConfig := reconciler.FakeGlobalConfig(t, nil)
+ cl := fake.NewClientBuilder().Build()
+ r := &monoVertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: fakeConfig,
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ }
+
+ t.Run("test everything from scratch for daemon deployment", func(t *testing.T) {
+ testObj := testMonoVtx.DeepCopy()
+ err := r.createOrUpdateDaemonDeployment(context.TODO(), testObj)
+ assert.NoError(t, err)
+ var daemonDeployment appv1.Deployment
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetDaemonDeploymentName()},
+ &daemonDeployment)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetDaemonDeploymentName(), daemonDeployment.Name)
+ assert.Equal(t, 1, len(daemonDeployment.Spec.Template.Spec.Containers))
+ assert.Equal(t, dfv1.CtrMain, daemonDeployment.Spec.Template.Spec.Containers[0].Name)
+ })
+}
+
+func Test_createOrUpdateDaemonService(t *testing.T) {
+ fakeConfig := reconciler.FakeGlobalConfig(t, nil)
+ cl := fake.NewClientBuilder().Build()
+ r := &monoVertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: fakeConfig,
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ }
+
+ t.Run("test everything from scratch for daemon service", func(t *testing.T) {
+ testObj := testMonoVtx.DeepCopy()
+ err := r.createOrUpdateDaemonService(context.TODO(), testObj)
+ assert.NoError(t, err)
+ var daemonSvc corev1.Service
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetDaemonServiceName()},
+ &daemonSvc)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetDaemonServiceName(), daemonSvc.Name)
+ assert.Equal(t, 1, len(daemonSvc.Spec.Ports))
+ assert.Equal(t, int32(dfv1.MonoVertexDaemonServicePort), daemonSvc.Spec.Ports[0].Port)
+ })
+}
+
+func Test_createOrUpdateMonoVtxServices(t *testing.T) {
+ fakeConfig := reconciler.FakeGlobalConfig(t, nil)
+ cl := fake.NewClientBuilder().Build()
+ r := &monoVertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: fakeConfig,
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ }
+
+ t.Run("test everything from scratch for monovtx service", func(t *testing.T) {
+ testObj := testMonoVtx.DeepCopy()
+ err := r.createOrUpdateMonoVtxServices(context.TODO(), testObj)
+ assert.NoError(t, err)
+ var svc corev1.Service
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetHeadlessServiceName()},
+ &svc)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetHeadlessServiceName(), svc.Name)
+ assert.Equal(t, 1, len(svc.Spec.Ports))
+ assert.Equal(t, int32(dfv1.MonoVertexMetricsPort), svc.Spec.Ports[0].Port)
+ m, err := r.findExistingMonoVtxServices(context.TODO(), testObj)
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(m))
+ })
+}
+
+func Test_orchestratePods(t *testing.T) {
+ fakeConfig := reconciler.FakeGlobalConfig(t, nil)
+ cl := fake.NewClientBuilder().Build()
+ r := &monoVertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: fakeConfig,
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ }
+ t.Run("test orchestratePodsFromTo and cleanUpPodsFromTo", func(t *testing.T) {
+ testObj := testMonoVtx.DeepCopy()
+ hash := "test-hasssssh"
+ podSpec, err := r.buildPodSpec(testObj)
+ assert.NoError(t, err)
+ err = r.orchestratePodsFromTo(context.TODO(), testObj, *podSpec, 2, 4, hash)
+ assert.NoError(t, err)
+ foundPods, err := r.findExistingPods(context.TODO(), testObj, 2, 4)
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(foundPods))
+ for n, pod := range foundPods {
+ assert.Equal(t, hash, pod.Annotations[dfv1.KeyHash])
+ assert.True(t, strings.HasPrefix(n, testObj.Name+"-mv-2") || strings.HasPrefix(n, testObj.Name+"-mv-3"))
+ }
+ err = r.cleanUpPodsFromTo(context.TODO(), testObj, 2, 4)
+ assert.NoError(t, err)
+ foundPods, err = r.findExistingPods(context.TODO(), testObj, 2, 4)
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(foundPods))
+ })
+
+ t.Run("test orchestratePods", func(t *testing.T) {
+ testObj := testMonoVtx.DeepCopy()
+ err := r.orchestratePods(context.TODO(), testObj)
+ assert.NoError(t, err)
+ foundPods, err := r.findExistingPods(context.TODO(), testObj, 0, 4)
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(foundPods))
+ for n := range foundPods {
+ assert.True(t, strings.HasPrefix(n, testObj.Name+"-mv-0") || strings.HasPrefix(n, testObj.Name+"-mv-1"))
+ }
+ })
+}
+
+func Test_orchestrateFixedResources(t *testing.T) {
+ fakeConfig := reconciler.FakeGlobalConfig(t, nil)
+ cl := fake.NewClientBuilder().Build()
+ r := &monoVertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: fakeConfig,
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ }
+ testObj := testMonoVtx.DeepCopy()
+ err := r.orchestrateFixedResources(context.TODO(), testObj)
+ assert.NoError(t, err)
+ var svc corev1.Service
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetHeadlessServiceName()},
+ &svc)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetHeadlessServiceName(), svc.Name)
+ var daemonSvc corev1.Service
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetDaemonServiceName()},
+ &daemonSvc)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetDaemonServiceName(), daemonSvc.Name)
+ var daemonDeployment appv1.Deployment
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetDaemonDeploymentName()},
+ &daemonDeployment)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetDaemonDeploymentName(), daemonDeployment.Name)
+}
+
+func Test_reconcile(t *testing.T) {
+ fakeConfig := reconciler.FakeGlobalConfig(t, nil)
+ cl := fake.NewClientBuilder().Build()
+ r := &monoVertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: fakeConfig,
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ scaler: scaling.NewScaler(cl),
+ }
+ testObj := testMonoVtx.DeepCopy()
+ _, err := r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ var daemonDeployment appv1.Deployment
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetDaemonDeploymentName()},
+ &daemonDeployment)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetDaemonDeploymentName(), daemonDeployment.Name)
+}
diff --git a/pkg/reconciler/monovertex/scaling/scaling.go b/pkg/reconciler/monovertex/scaling/scaling.go
index fa085fc2b4..2ae99fcf1b 100644
--- a/pkg/reconciler/monovertex/scaling/scaling.go
+++ b/pkg/reconciler/monovertex/scaling/scaling.go
@@ -157,6 +157,10 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
s.StopWatching(key) // Remove it in case it's watched.
return nil
}
+ if monoVtx.Status.UpdateHash != monoVtx.Status.CurrentHash && monoVtx.Status.UpdateHash != "" {
+ log.Info("MonoVertex is updating, skip scaling.")
+ return nil
+ }
secondsSinceLastScale := time.Since(monoVtx.Status.LastScaledAt.Time).Seconds()
scaleDownCooldown := float64(monoVtx.Spec.Scale.GetScaleDownCooldownSeconds())
scaleUpCooldown := float64(monoVtx.Spec.Scale.GetScaleUpCooldownSeconds())
@@ -174,7 +178,7 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
// log.Info("MonoVertex is pausing, skip scaling.")
// return nil
// }
- if int(monoVtx.Status.Replicas) != monoVtx.GetReplicas() {
+ if int(monoVtx.Status.Replicas) != monoVtx.CalculateReplicas() {
log.Infof("MonoVertex %s might be under processing, replicas mismatch, skip scaling.", monoVtx.Name)
return nil
}
@@ -235,7 +239,7 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
desired = min
log.Infof("Calculated desired replica number %d of MonoVertex %q is smaller than min, using min %d.", monoVtxName, desired, min)
}
- current := int32(monoVtx.GetReplicas())
+ current := int32(monoVtx.Status.Replicas)
if current > max || current < min { // Someone might have manually scaled up/down the MonoVertex
return s.patchMonoVertexReplicas(ctx, monoVtx, desired)
}
diff --git a/pkg/reconciler/pipeline/controller.go b/pkg/reconciler/pipeline/controller.go
index 0f5354b2c8..96d9fa2266 100644
--- a/pkg/reconciler/pipeline/controller.go
+++ b/pkg/reconciler/pipeline/controller.go
@@ -145,7 +145,7 @@ func (r *pipelineReconciler) reconcile(ctx context.Context, pl *dfv1.Pipeline) (
pl.Status.SetObservedGeneration(pl.Generation)
// Regular pipeline change
- // This should be happening in call cases to ensure a clean initialization regardless of the lifecycle phase
+ // This should be happening in all cases to ensure a clean initialization regardless of the lifecycle phase
// Eg: even for a pipeline started with desiredPhase = Pause, we should still create the resources for the pipeline
result, err := r.reconcileNonLifecycleChanges(ctx, pl)
if err != nil {
diff --git a/pkg/reconciler/util.go b/pkg/reconciler/util.go
index 6d27a749bb..8edad6b1a8 100644
--- a/pkg/reconciler/util.go
+++ b/pkg/reconciler/util.go
@@ -63,20 +63,25 @@ func isPodHealthy(pod *corev1.Pod) (healthy bool, reason string) {
func NumOfReadyPods(pods corev1.PodList) int {
result := 0
for _, pod := range pods.Items {
- ready := true
- for _, s := range pod.Status.ContainerStatuses {
- if !s.Ready {
- ready = false
- break
- }
- }
- if ready {
+ if IsPodReady(pod) {
result++
}
}
return result
}
+func IsPodReady(pod corev1.Pod) bool {
+ if pod.Status.Phase != corev1.PodRunning {
+ return false
+ }
+ for _, c := range pod.Status.ContainerStatuses {
+ if !c.Ready {
+ return false
+ }
+ }
+ return true
+}
+
// CheckVertexStatus will calculate the status of the vertices and return the status and reason
func CheckVertexStatus(vertices *dfv1.VertexList) (healthy bool, reason string, message string) {
for _, vertex := range vertices.Items {
diff --git a/pkg/reconciler/util_test.go b/pkg/reconciler/util_test.go
index 51348f5a32..72a09da1bd 100644
--- a/pkg/reconciler/util_test.go
+++ b/pkg/reconciler/util_test.go
@@ -260,6 +260,7 @@ func TestNumOfReadyPods(t *testing.T) {
Items: []corev1.Pod{
{
Status: corev1.PodStatus{
+ Phase: corev1.PodRunning,
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: true,
@@ -272,6 +273,7 @@ func TestNumOfReadyPods(t *testing.T) {
},
{
Status: corev1.PodStatus{
+ Phase: corev1.PodRunning,
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: false,
@@ -284,6 +286,7 @@ func TestNumOfReadyPods(t *testing.T) {
},
{
Status: corev1.PodStatus{
+ Phase: corev1.PodRunning,
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: true,
@@ -296,6 +299,7 @@ func TestNumOfReadyPods(t *testing.T) {
},
{
Status: corev1.PodStatus{
+ Phase: corev1.PodRunning,
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: true,
@@ -311,6 +315,7 @@ func TestNumOfReadyPods(t *testing.T) {
},
{
Status: corev1.PodStatus{
+ Phase: corev1.PodRunning,
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: false,
@@ -324,6 +329,22 @@ func TestNumOfReadyPods(t *testing.T) {
},
},
},
+ {
+ Status: corev1.PodStatus{
+ Phase: corev1.PodFailed,
+ ContainerStatuses: []corev1.ContainerStatus{
+ {
+ Ready: true,
+ },
+ {
+ Ready: true,
+ },
+ {
+ Ready: true,
+ },
+ },
+ },
+ },
},
}
assert.Equal(t, 2, NumOfReadyPods(pods))
diff --git a/rust/numaflow-models/Makefile b/rust/numaflow-models/Makefile
index 3e38249cae..069179f746 100644
--- a/rust/numaflow-models/Makefile
+++ b/rust/numaflow-models/Makefile
@@ -19,6 +19,7 @@ generate:
sed 's/io.k8s.api.core.v1./CoreV1/' | \
sed 's/io.k8s.apimachinery.pkg.apis.meta.v1./MetaV1/' | \
sed 's/io.k8s.apimachinery.pkg.api.resource.Quantity/ResourceQuantity/' | \
+ sed 's/io.k8s.apimachinery.pkg.util.intstr.IntOrString/IntOrString/' | \
sed 's/io.numaproj.numaflow.v1alpha1.//' \
> ./dist/swagger.json
$(DOCKER) openapitools/openapi-generator-cli:$(GENERATOR_VERSION) \
@@ -58,6 +59,7 @@ generate:
--type-mappings MetaV1Time="k8s_openapi::apimachinery::pkg::apis::meta::v1::Time" \
--type-mappings MetaV1ObjectMeta="k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta" \
--type-mappings ResourceQuantity="k8s_openapi::apimachinery::pkg::api::resource::Quantity" \
+ --type-mappings IntOrString="k8s_openapi::apimachinery::pkg::util::intstr::IntOrString" \
--generate-alias-as-model
cargo add kube
diff --git a/rust/numaflow-models/src/models/mod.rs b/rust/numaflow-models/src/models/mod.rs
index bbb11ca261..29846575d2 100644
--- a/rust/numaflow-models/src/models/mod.rs
+++ b/rust/numaflow-models/src/models/mod.rs
@@ -128,6 +128,8 @@ pub mod redis_settings;
pub use self::redis_settings::RedisSettings;
pub mod retry_strategy;
pub use self::retry_strategy::RetryStrategy;
+pub mod rolling_update_strategy;
+pub use self::rolling_update_strategy::RollingUpdateStrategy;
pub mod sasl;
pub use self::sasl::Sasl;
pub mod sasl_plain;
@@ -170,6 +172,8 @@ pub mod ud_transformer;
pub use self::ud_transformer::UdTransformer;
pub mod udf;
pub use self::udf::Udf;
+pub mod update_strategy;
+pub use self::update_strategy::UpdateStrategy;
pub mod vertex;
pub use self::vertex::Vertex;
pub mod vertex_instance;
diff --git a/rust/numaflow-models/src/models/mono_vertex_spec.rs b/rust/numaflow-models/src/models/mono_vertex_spec.rs
index 8eadbbdaab..6d4068bee7 100644
--- a/rust/numaflow-models/src/models/mono_vertex_spec.rs
+++ b/rust/numaflow-models/src/models/mono_vertex_spec.rs
@@ -79,6 +79,8 @@ pub struct MonoVertexSpec {
/// If specified, the pod's tolerations.
#[serde(rename = "tolerations", skip_serializing_if = "Option::is_none")]
pub tolerations: Option>,
+ #[serde(rename = "updateStrategy", skip_serializing_if = "Option::is_none")]
+ pub update_strategy: Option>,
#[serde(rename = "volumes", skip_serializing_if = "Option::is_none")]
pub volumes: Option>,
}
@@ -109,6 +111,7 @@ impl MonoVertexSpec {
sink: None,
source: None,
tolerations: None,
+ update_strategy: None,
volumes: None,
}
}
diff --git a/rust/numaflow-models/src/models/mono_vertex_status.rs b/rust/numaflow-models/src/models/mono_vertex_status.rs
index bd2fdee807..16e2de5479 100644
--- a/rust/numaflow-models/src/models/mono_vertex_status.rs
+++ b/rust/numaflow-models/src/models/mono_vertex_status.rs
@@ -21,12 +21,12 @@ pub struct MonoVertexStatus {
/// Conditions are the latest available observations of a resource's current state.
#[serde(rename = "conditions", skip_serializing_if = "Option::is_none")]
pub conditions: Option>,
- /// If not empty, indicates the version of the MonoVertex used to generate Pods in the sequence [0,currentReplicas).
+ /// If not empty, indicates the current version of the MonoVertex used to generate Pods.
#[serde(rename = "currentHash", skip_serializing_if = "Option::is_none")]
pub current_hash: Option,
- /// The number of Pods created by the controller from the MonoVertex version indicated by currentHash.
- #[serde(rename = "currentReplicas", skip_serializing_if = "Option::is_none")]
- pub current_replicas: Option,
+ /// The number of desired replicas.
+ #[serde(rename = "desiredReplicas", skip_serializing_if = "Option::is_none")]
+ pub desired_replicas: Option,
#[serde(rename = "lastScaledAt", skip_serializing_if = "Option::is_none")]
pub last_scaled_at: Option,
#[serde(rename = "lastUpdated", skip_serializing_if = "Option::is_none")]
@@ -48,9 +48,15 @@ pub struct MonoVertexStatus {
pub replicas: Option,
#[serde(rename = "selector", skip_serializing_if = "Option::is_none")]
pub selector: Option,
- /// If not empty, indicates the version of the MonoVertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ /// If not empty, indicates the updated version of the MonoVertex used to generate Pods.
#[serde(rename = "updateHash", skip_serializing_if = "Option::is_none")]
pub update_hash: Option,
+ /// The number of ready Pods created by the controller from the MonoVertex version indicated by updateHash.
+ #[serde(
+ rename = "updatedReadyReplicas",
+ skip_serializing_if = "Option::is_none"
+ )]
+ pub updated_ready_replicas: Option,
/// The number of Pods created by the controller from the MonoVertex version indicated by updateHash.
#[serde(rename = "updatedReplicas", skip_serializing_if = "Option::is_none")]
pub updated_replicas: Option,
@@ -61,7 +67,7 @@ impl MonoVertexStatus {
MonoVertexStatus {
conditions: None,
current_hash: None,
- current_replicas: None,
+ desired_replicas: None,
last_scaled_at: None,
last_updated: None,
message: None,
@@ -72,6 +78,7 @@ impl MonoVertexStatus {
replicas: None,
selector: None,
update_hash: None,
+ updated_ready_replicas: None,
updated_replicas: None,
}
}
diff --git a/rust/numaflow-models/src/models/rolling_update_strategy.rs b/rust/numaflow-models/src/models/rolling_update_strategy.rs
new file mode 100644
index 0000000000..2712fea2b4
--- /dev/null
+++ b/rust/numaflow-models/src/models/rolling_update_strategy.rs
@@ -0,0 +1,34 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by Openapi Generator. DO NOT EDIT.
+
+/// RollingUpdateStrategy : RollingUpdateStrategy is used to communicate parameter for RollingUpdateStrategyType.
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct RollingUpdateStrategy {
+ #[serde(rename = "maxUnavailable", skip_serializing_if = "Option::is_none")]
+ pub max_unavailable: Option,
+}
+
+impl RollingUpdateStrategy {
+ /// RollingUpdateStrategy is used to communicate parameter for RollingUpdateStrategyType.
+ pub fn new() -> RollingUpdateStrategy {
+ RollingUpdateStrategy {
+ max_unavailable: None,
+ }
+ }
+}
diff --git a/rust/numaflow-models/src/models/update_strategy.rs b/rust/numaflow-models/src/models/update_strategy.rs
new file mode 100644
index 0000000000..96f1256351
--- /dev/null
+++ b/rust/numaflow-models/src/models/update_strategy.rs
@@ -0,0 +1,51 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by Openapi Generator. DO NOT EDIT.
+
+/// UpdateStrategy : UpdateStrategy indicates the strategy that the controller will use to perform updates for Vertex or MonoVertex.
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct UpdateStrategy {
+ #[serde(rename = "rollingUpdate", skip_serializing_if = "Option::is_none")]
+ pub rolling_update: Option>,
+ /// Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate. Possible enum values: - `\"RollingUpdate\"`
+ #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
+ pub r#type: Option,
+}
+
+impl UpdateStrategy {
+ /// UpdateStrategy indicates the strategy that the controller will use to perform updates for Vertex or MonoVertex.
+ pub fn new() -> UpdateStrategy {
+ UpdateStrategy {
+ rolling_update: None,
+ r#type: None,
+ }
+ }
+}
+
+/// Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate. Possible enum values: - `\"RollingUpdate\"`
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
+pub enum Type {
+ #[serde(rename = "RollingUpdate")]
+ RollingUpdate,
+}
+
+impl Default for Type {
+ fn default() -> Type {
+ Self::RollingUpdate
+ }
+}
diff --git a/test/fixtures/util.go b/test/fixtures/util.go
index 2c8c8a0ae1..c88d28d1af 100644
--- a/test/fixtures/util.go
+++ b/test/fixtures/util.go
@@ -278,7 +278,7 @@ func WaitForMonoVertexPodRunning(kubeClient kubernetes.Interface, monoVertexClie
if err != nil {
return fmt.Errorf("error getting monovertex pod list: %w", err)
}
- ok := len(podList.Items) > 0 && len(podList.Items) == monoVertex.GetReplicas() // pod number should equal to desired replicas
+ ok := len(podList.Items) > 0 && len(podList.Items) == monoVertex.CalculateReplicas() // pod number should equal to desired replicas
for _, p := range podList.Items {
ok = ok && p.Status.Phase == corev1.PodRunning
}
From 2189ea9ec23368a9fdebaa72667b85c0fe9c4fc2 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Sat, 7 Sep 2024 10:56:28 -0700
Subject: [PATCH 051/188] chore: minor pipeline controller refactor (#2039)
---
api/json-schema/schema.json | 2 +-
api/openapi-spec/swagger.json | 2 +-
.../full/numaflow.numaproj.io_pipelines.yaml | 1 -
config/install.yaml | 1 -
config/namespace-install.yaml | 1 -
docs/APIs.md | 7 +-
examples/1-simple-pipeline.yaml | 2 +-
pkg/apis/numaflow/v1alpha1/generated.proto | 7 +-
.../numaflow/v1alpha1/openapi_generated.go | 2 +-
pkg/apis/numaflow/v1alpha1/pipeline_types.go | 7 +-
pkg/reconciler/monovertex/controller.go | 5 +-
pkg/reconciler/monovertex/scaling/scaling.go | 4 +-
pkg/reconciler/pipeline/controller.go | 66 ++++-----
pkg/reconciler/pipeline/controller_test.go | 132 ++++++++----------
pkg/reconciler/vertex/scaling/scaling.go | 10 +-
.../src/models/pipeline_status.rs | 2 +-
16 files changed, 117 insertions(+), 134 deletions(-)
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index 27796003ba..44e18ef85a 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19691,7 +19691,7 @@
"x-kubernetes-patch-strategy": "merge"
},
"drainedOnPause": {
- "description": "Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false",
+ "description": "Field to indicate if a pipeline drain successfully occurred, only meaningful when the pipeline is paused. True means it has been successfully drained.",
"type": "boolean"
},
"lastUpdated": {
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index bb918bac66..7067416291 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19678,7 +19678,7 @@
"x-kubernetes-patch-strategy": "merge"
},
"drainedOnPause": {
- "description": "Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false",
+ "description": "Field to indicate if a pipeline drain successfully occurred, only meaningful when the pipeline is paused. True means it has been successfully drained.",
"type": "boolean"
},
"lastUpdated": {
diff --git a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
index 9670b018e0..6fc509dc96 100644
--- a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
@@ -9822,7 +9822,6 @@ spec:
type: object
type: array
drainedOnPause:
- default: false
type: boolean
lastUpdated:
format: date-time
diff --git a/config/install.yaml b/config/install.yaml
index ac272ddf19..d19f2fa2f2 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -18095,7 +18095,6 @@ spec:
type: object
type: array
drainedOnPause:
- default: false
type: boolean
lastUpdated:
format: date-time
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 12579dc36f..5919b0fcf8 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -18095,7 +18095,6 @@ spec:
type: object
type: array
drainedOnPause:
- default: false
type: boolean
lastUpdated:
format: date-time
diff --git a/docs/APIs.md b/docs/APIs.md
index 5fd26ad505..bd71cf0592 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -8011,11 +8011,12 @@ The generation observed by the Pipeline controller.
+(Optional)
-Field to indicate if a pipeline drain successfully occurred, or it timed
-out. Set to true when the Pipeline is in Paused state, and after it has
-successfully been drained. defaults to false
+Field to indicate if a pipeline drain successfully occurred, only
+meaningful when the pipeline is paused. True means it has been
+successfully drained.
diff --git a/examples/1-simple-pipeline.yaml b/examples/1-simple-pipeline.yaml
index 42e9d9e095..e790fa3150 100644
--- a/examples/1-simple-pipeline.yaml
+++ b/examples/1-simple-pipeline.yaml
@@ -27,4 +27,4 @@ spec:
- from: in
to: cat
- from: cat
- to: out
\ No newline at end of file
+ to: out
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 1e6e6bd35a..78e10457a0 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -1229,10 +1229,9 @@ message PipelineStatus {
// +optional
optional int64 observedGeneration = 11;
- // Field to indicate if a pipeline drain successfully occurred, or it timed out.
- // Set to true when the Pipeline is in Paused state, and after it has successfully been drained.
- // defaults to false
- // +kubebuilder:default=false
+ // Field to indicate if a pipeline drain successfully occurred, only meaningful when the pipeline is paused.
+ // True means it has been successfully drained.
+ // +optional
optional bool drainedOnPause = 12;
}
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index a339fdfb7a..765dd96681 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -4151,7 +4151,7 @@ func schema_pkg_apis_numaflow_v1alpha1_PipelineStatus(ref common.ReferenceCallba
},
"drainedOnPause": {
SchemaProps: spec.SchemaProps{
- Description: "Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false",
+ Description: "Field to indicate if a pipeline drain successfully occurred, only meaningful when the pipeline is paused. True means it has been successfully drained.",
Type: []string{"boolean"},
Format: "",
},
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types.go b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
index c68a7d647c..ff8dfaf5e5 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
@@ -633,10 +633,9 @@ type PipelineStatus struct {
// The generation observed by the Pipeline controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,11,opt,name=observedGeneration"`
- // Field to indicate if a pipeline drain successfully occurred, or it timed out.
- // Set to true when the Pipeline is in Paused state, and after it has successfully been drained.
- // defaults to false
- // +kubebuilder:default=false
+ // Field to indicate if a pipeline drain successfully occurred, only meaningful when the pipeline is paused.
+ // True means it has been successfully drained.
+ // +optional
DrainedOnPause bool `json:"drainedOnPause,omitempty" protobuf:"bytes,12,opt,name=drainedOnPause"`
}
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index a0de7e6d6a..a7b7a90c40 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -110,6 +110,7 @@ func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.Mon
}
}()
+ monoVtx.Status.InitializeConditions()
monoVtx.Status.SetObservedGeneration(monoVtx.Generation)
if monoVtx.Scalable() {
mr.scaler.StartWatching(mVtxKey)
@@ -251,7 +252,7 @@ func (mr *monoVertexReconciler) orchestratePods(ctx context.Context, monoVtx *df
if updatedReplicas+toBeUpdated > desiredReplicas {
toBeUpdated = desiredReplicas - updatedReplicas
}
- log.Infof("Rolling update %d replicas, [%d, %d)\n", toBeUpdated, updatedReplicas, updatedReplicas+toBeUpdated)
+ log.Infof("Rolling update %d replicas, [%d, %d)", toBeUpdated, updatedReplicas, updatedReplicas+toBeUpdated)
// Create pods [updatedReplicas, updatedReplicas+toBeUpdated), and clean up any pods in that range that has a different hash
if err := mr.orchestratePodsFromTo(ctx, monoVtx, *podSpec, updatedReplicas, updatedReplicas+toBeUpdated, monoVtx.Status.UpdateHash); err != nil {
@@ -310,7 +311,7 @@ func (mr *monoVertexReconciler) cleanUpPodsFromTo(ctx context.Context, monoVtx *
if err := mr.client.Delete(ctx, &pod); err != nil {
return fmt.Errorf("failed to delete pod %s: %w", pod.Name, err)
}
- log.Infof("Deleted MonoVertx pod %s\n", pod.Name)
+ log.Infof("Deleted MonoVertx pod %q", pod.Name)
mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "DeletePodSuccess", "Succeeded to delete a mono vertex pod %s", pod.Name)
}
return nil
diff --git a/pkg/reconciler/monovertex/scaling/scaling.go b/pkg/reconciler/monovertex/scaling/scaling.go
index 2ae99fcf1b..d523800cdd 100644
--- a/pkg/reconciler/monovertex/scaling/scaling.go
+++ b/pkg/reconciler/monovertex/scaling/scaling.go
@@ -233,11 +233,11 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
min := monoVtx.Spec.Scale.GetMinReplicas()
if desired > max {
desired = max
- log.Infof("Calculated desired replica number %d of MonoVertex %q is greater than max, using max %d.", monoVtxName, desired, max)
+ log.Infof("Calculated desired replica number %d of MonoVertex %q is greater than max, using max %d.", desired, monoVtxName, max)
}
if desired < min {
desired = min
- log.Infof("Calculated desired replica number %d of MonoVertex %q is smaller than min, using min %d.", monoVtxName, desired, min)
+ log.Infof("Calculated desired replica number %d of MonoVertex %q is smaller than min, using min %d.", desired, monoVtxName, min)
}
current := int32(monoVtx.Status.Replicas)
if current > max || current < min { // Someone might have manually scaled up/down the MonoVertex
diff --git a/pkg/reconciler/pipeline/controller.go b/pkg/reconciler/pipeline/controller.go
index 96d9fa2266..955344a8c1 100644
--- a/pkg/reconciler/pipeline/controller.go
+++ b/pkg/reconciler/pipeline/controller.go
@@ -143,15 +143,25 @@ func (r *pipelineReconciler) reconcile(ctx context.Context, pl *dfv1.Pipeline) (
}
}()
+ pl.Status.InitConditions()
pl.Status.SetObservedGeneration(pl.Generation)
- // Regular pipeline change
- // This should be happening in all cases to ensure a clean initialization regardless of the lifecycle phase
- // Eg: even for a pipeline started with desiredPhase = Pause, we should still create the resources for the pipeline
- result, err := r.reconcileNonLifecycleChanges(ctx, pl)
- if err != nil {
+ // Orchestrate pipeline sub resources.
+ // This should be happening in all cases to ensure a clean initialization regardless of the lifecycle phase.
+ // Eg: even for a pipeline started with desiredPhase = Pause, we should still create the resources for the pipeline.
+ if err := r.reconcileFixedResources(ctx, pl); err != nil {
r.recorder.Eventf(pl, corev1.EventTypeWarning, "ReconcilePipelineFailed", "Failed to reconcile pipeline: %v", err.Error())
- return result, err
+ return ctrl.Result{}, err
+ }
+ // If the pipeline has a lifecycle change, then do not update the phase as
+ // this should happen only after the required configs for the lifecycle changes
+ // have been applied.
+ if !isLifecycleChange(pl) {
+ pl.Status.SetPhase(pl.Spec.Lifecycle.GetDesiredPhase(), "")
}
+ if err := r.checkChildrenResourceStatus(ctx, pl); err != nil {
+ return ctrl.Result{}, fmt.Errorf("failed to check pipeline children resource status, %w", err)
+ }
+
// check if any changes related to pause/resume lifecycle for the pipeline
if isLifecycleChange(pl) {
oldPhase := pl.Status.Phase
@@ -171,7 +181,7 @@ func (r *pipelineReconciler) reconcile(ctx context.Context, pl *dfv1.Pipeline) (
}
return ctrl.Result{}, nil
}
- return result, nil
+ return ctrl.Result{}, nil
}
// isLifecycleChange determines whether there has been a change requested in the lifecycle
@@ -190,17 +200,16 @@ func isLifecycleChange(pl *dfv1.Pipeline) bool {
return false
}
-// reconcileNonLifecycleChanges do the jobs not related to pipeline lifecycle changes.
-func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, pl *dfv1.Pipeline) (ctrl.Result, error) {
+// reconcileFixedResources do the jobs of creating fixed resources such as daemon service, vertex objects, and ISB management jobs, etc
+func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *dfv1.Pipeline) error {
log := logging.FromContext(ctx)
if !controllerutil.ContainsFinalizer(pl, finalizerName) {
controllerutil.AddFinalizer(pl, finalizerName)
}
- pl.Status.InitConditions()
if err := ValidatePipeline(pl); err != nil {
log.Errorw("Validation failed", zap.Error(err))
pl.Status.MarkNotConfigured("InvalidSpec", err.Error())
- return ctrl.Result{}, err
+ return err
}
pl.Status.SetVertexCounts(pl.Spec.Vertices)
pl.Status.MarkConfigured()
@@ -215,16 +224,16 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
if apierrors.IsNotFound(err) {
pl.Status.MarkDeployFailed("ISBSvcNotFound", "ISB Service not found.")
log.Errorw("ISB Service not found", zap.String("isbsvc", isbSvcName), zap.Error(err))
- return ctrl.Result{}, fmt.Errorf("isbsvc %s not found", isbSvcName)
+ return fmt.Errorf("isbsvc %s not found", isbSvcName)
}
pl.Status.MarkDeployFailed("GetISBSvcFailed", err.Error())
log.Errorw("Failed to get ISB Service", zap.String("isbsvc", isbSvcName), zap.Error(err))
- return ctrl.Result{}, err
+ return err
}
if !isbSvc.Status.IsHealthy() {
pl.Status.MarkDeployFailed("ISBSvcNotHealthy", "ISB Service not healthy.")
log.Errorw("ISB Service is not in healthy status", zap.String("isbsvc", isbSvcName), zap.Error(err))
- return ctrl.Result{}, fmt.Errorf("isbsvc not healthy")
+ return fmt.Errorf("isbsvc not healthy")
}
// Create or update the Side Inputs Manager deployments
@@ -232,14 +241,14 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
log.Errorw("Failed to create or update Side Inputs Manager deployments", zap.Error(err))
pl.Status.MarkDeployFailed("CreateOrUpdateSIMDeploymentsFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "CreateOrUpdateSIMDeploymentsFailed", "Failed to create or update Side Inputs Manager deployments: %w", err.Error())
- return ctrl.Result{}, err
+ return err
}
existingObjs, err := r.findExistingVertices(ctx, pl)
if err != nil {
log.Errorw("Failed to find existing vertices", zap.Error(err))
pl.Status.MarkDeployFailed("ListVerticesFailed", err.Error())
- return ctrl.Result{}, err
+ return err
}
oldBuffers := make(map[string]string)
newBuffers := make(map[string]string)
@@ -279,7 +288,7 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
} else {
pl.Status.MarkDeployFailed("CreateVertexFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "CreateVertexFailed", "Failed to create vertex: %w", err.Error())
- return ctrl.Result{}, fmt.Errorf("failed to create vertex, err: %w", err)
+ return fmt.Errorf("failed to create vertex, err: %w", err)
}
}
log.Infow("Created vertex successfully", zap.String("vertex", vertexName))
@@ -291,7 +300,7 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
if err := r.client.Update(ctx, &oldObj); err != nil {
pl.Status.MarkDeployFailed("UpdateVertexFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "UpdateVertexFailed", "Failed to update vertex: %w", err.Error())
- return ctrl.Result{}, fmt.Errorf("failed to update vertex, err: %w", err)
+ return fmt.Errorf("failed to update vertex, err: %w", err)
}
log.Infow("Updated vertex successfully", zap.String("vertex", vertexName))
r.recorder.Eventf(pl, corev1.EventTypeNormal, "UpdateVertexSuccess", "Updated vertex %s successfully", vertexName)
@@ -303,7 +312,7 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
if err := r.client.Delete(ctx, &v); err != nil {
pl.Status.MarkDeployFailed("DeleteStaleVertexFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "DeleteStaleVertexFailed", "Failed to delete vertex: %w", err.Error())
- return ctrl.Result{}, fmt.Errorf("failed to delete vertex, err: %w", err)
+ return fmt.Errorf("failed to delete vertex, err: %w", err)
}
log.Infow("Deleted stale vertex successfully", zap.String("vertex", v.Name))
r.recorder.Eventf(pl, corev1.EventTypeNormal, "DeleteStaleVertexSuccess", "Deleted stale vertex %s successfully", v.Name)
@@ -328,7 +337,7 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
batchJob := buildISBBatchJob(pl, r.image, isbSvc.Status.Config, "isbsvc-create", args, "cre")
if err := r.client.Create(ctx, batchJob); err != nil && !apierrors.IsAlreadyExists(err) {
pl.Status.MarkDeployFailed("CreateISBSvcCreatingJobFailed", err.Error())
- return ctrl.Result{}, fmt.Errorf("failed to create ISB Svc creating job, err: %w", err)
+ return fmt.Errorf("failed to create ISB Svc creating job, err: %w", err)
}
log.Infow("Created a job successfully for ISB Svc creating", zap.Any("buffers", bfs), zap.Any("buckets", bks), zap.Any("servingStreams", pl.GetServingSourceStreamNames()))
}
@@ -346,31 +355,22 @@ func (r *pipelineReconciler) reconcileNonLifecycleChanges(ctx context.Context, p
batchJob := buildISBBatchJob(pl, r.image, isbSvc.Status.Config, "isbsvc-delete", args, "del")
if err := r.client.Create(ctx, batchJob); err != nil && !apierrors.IsAlreadyExists(err) {
pl.Status.MarkDeployFailed("CreateISBSvcDeletingJobFailed", err.Error())
- return ctrl.Result{}, fmt.Errorf("failed to create ISB Svc deleting job, err: %w", err)
+ return fmt.Errorf("failed to create ISB Svc deleting job, err: %w", err)
}
log.Infow("Created ISB Svc deleting job successfully", zap.Any("buffers", bfs), zap.Any("buckets", bks))
}
// Daemon service
if err := r.createOrUpdateDaemonService(ctx, pl); err != nil {
- return ctrl.Result{}, err
+ return err
}
// Daemon deployment
if err := r.createOrUpdateDaemonDeployment(ctx, pl, isbSvc.Status.Config); err != nil {
- return ctrl.Result{}, err
+ return err
}
pl.Status.MarkDeployed()
- // If the pipeline has a lifecycle change, then do not update the phase as
- // this should happen only after the required configs for the lifecycle changes
- // have been applied.
- if !isLifecycleChange(pl) {
- pl.Status.SetPhase(pl.Spec.Lifecycle.GetDesiredPhase(), "")
- }
- if err := r.checkChildrenResourceStatus(ctx, pl); err != nil {
- return ctrl.Result{}, fmt.Errorf("failed to check pipeline children resource status, %w", err)
- }
- return ctrl.Result{}, nil
+ return nil
}
func (r *pipelineReconciler) createOrUpdateDaemonService(ctx context.Context, pl *dfv1.Pipeline) error {
diff --git a/pkg/reconciler/pipeline/controller_test.go b/pkg/reconciler/pipeline/controller_test.go
index 0cf9205f0a..2a1762aa4b 100644
--- a/pkg/reconciler/pipeline/controller_test.go
+++ b/pkg/reconciler/pipeline/controller_test.go
@@ -154,6 +154,18 @@ func init() {
_ = batchv1.AddToScheme(scheme.Scheme)
}
+func fakeReconciler(t *testing.T, cl client.WithWatch) *pipelineReconciler {
+ t.Helper()
+ return &pipelineReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ }
+}
+
func Test_NewReconciler(t *testing.T) {
cl := fake.NewClientBuilder().Build()
r := NewReconciler(cl, scheme.Scheme, reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig), testFlowImage, zaptest.NewLogger(t).Sugar(), record.NewFakeRecorder(64))
@@ -162,23 +174,17 @@ func Test_NewReconciler(t *testing.T) {
}
func Test_reconcile(t *testing.T) {
+ ctx := context.TODO()
+
t.Run("test reconcile", func(t *testing.T) {
- cl := fake.NewClientBuilder().Build()
- ctx := context.TODO()
testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
testIsbSvc.Status.MarkConfigured()
testIsbSvc.Status.MarkDeployed()
+ cl := fake.NewClientBuilder().Build()
err := cl.Create(ctx, testIsbSvc)
assert.Nil(t, err)
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
testObj := testPipeline.DeepCopy()
+ r := fakeReconciler(t, cl)
_, err = r.reconcile(ctx, testObj)
assert.NoError(t, err)
vertices := &dfv1.VertexList{}
@@ -191,27 +197,50 @@ func Test_reconcile(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 1, len(jobs.Items))
})
-}
-func Test_reconcileEvents(t *testing.T) {
+ t.Run("test reconcile deleting", func(t *testing.T) {
+ testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
+ testIsbSvc.Status.MarkConfigured()
+ testIsbSvc.Status.MarkDeployed()
+ cl := fake.NewClientBuilder().Build()
+ err := cl.Create(ctx, testIsbSvc)
+ assert.Nil(t, err)
+ testObj := testPipeline.DeepCopy()
+ testObj.DeletionTimestamp = &metav1.Time{Time: time.Now()}
+ r := fakeReconciler(t, cl)
+ _, err = r.reconcile(ctx, testObj)
+ assert.NoError(t, err)
+ })
- fakeConfig := reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig)
- t.Run("test reconcile - invalid name", func(t *testing.T) {
+ t.Run("test reconcile - no isbsvc", func(t *testing.T) {
+ testObj := testPipeline.DeepCopy()
cl := fake.NewClientBuilder().Build()
- ctx := context.TODO()
+ r := fakeReconciler(t, cl)
+ _, err := r.reconcile(ctx, testObj)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "not found")
+ })
+
+ t.Run("test reconcile - isbsvc unhealthy", func(t *testing.T) {
+ testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
+ testIsbSvc.Status.MarkConfigured()
+ cl := fake.NewClientBuilder().Build()
+ _ = cl.Create(ctx, testIsbSvc)
+ testObj := testPipeline.DeepCopy()
+ r := fakeReconciler(t, cl)
+ _, err := r.reconcile(ctx, testObj)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "not healthy")
+ })
+
+ t.Run("test reconcile - invalid name", func(t *testing.T) {
testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
testIsbSvc.Status.MarkConfigured()
testIsbSvc.Status.MarkDeployed()
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
err := cl.Create(ctx, testIsbSvc)
assert.Nil(t, err)
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
testObj := testPipeline.DeepCopy()
testObj.Status.Phase = "Paused"
_, err = r.reconcile(ctx, testObj)
@@ -224,22 +253,14 @@ func Test_reconcileEvents(t *testing.T) {
})
t.Run("test reconcile - duplicate vertex", func(t *testing.T) {
- cl := fake.NewClientBuilder().Build()
- ctx := context.TODO()
testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
testIsbSvc.Status.MarkConfigured()
testIsbSvc.Status.MarkDeployed()
+ cl := fake.NewClientBuilder().Build()
err := cl.Create(ctx, testIsbSvc)
assert.Nil(t, err)
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
testObj := testPipeline.DeepCopy()
+ r := fakeReconciler(t, cl)
_, err = r.reconcile(ctx, testObj)
assert.NoError(t, err)
testObj.Spec.Vertices = append(testObj.Spec.Vertices, dfv1.AbstractVertex{Name: "input", Source: &dfv1.Source{}})
@@ -279,14 +300,7 @@ func Test_pauseAndResumePipeline(t *testing.T) {
testIsbSvc.Status.MarkDeployed()
err := cl.Create(ctx, testIsbSvc)
assert.Nil(t, err)
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testPipeline.DeepCopy()
testObj.Spec.Vertices[0].Scale.Min = ptr.To[int32](3)
_, err = r.reconcile(ctx, testObj)
@@ -316,14 +330,7 @@ func Test_pauseAndResumePipeline(t *testing.T) {
testIsbSvc.Status.MarkDeployed()
err := cl.Create(ctx, testIsbSvc)
assert.Nil(t, err)
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testReducePipeline.DeepCopy()
_, err = r.reconcile(ctx, testObj)
assert.NoError(t, err)
@@ -566,14 +573,7 @@ func Test_cleanupBuffers(t *testing.T) {
func TestCreateOrUpdateDaemon(t *testing.T) {
cl := fake.NewClientBuilder().Build()
ctx := context.TODO()
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
t.Run("test create or update service", func(t *testing.T) {
testObj := testPipeline.DeepCopy()
@@ -601,14 +601,7 @@ func TestCreateOrUpdateDaemon(t *testing.T) {
func Test_createOrUpdateSIMDeployments(t *testing.T) {
cl := fake.NewClientBuilder().Build()
ctx := context.TODO()
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
t.Run("no side inputs", func(t *testing.T) {
err := r.createOrUpdateSIMDeployments(ctx, testPipeline, fakeIsbSvcConfig)
@@ -920,14 +913,7 @@ func Test_checkChildrenResourceStatus(t *testing.T) {
testIsbSvc.Status.MarkDeployed()
err := cl.Create(ctx, testIsbSvc)
assert.Nil(t, err)
- r := &pipelineReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testPipelineWithSideinput.DeepCopy()
_, err = r.reconcile(ctx, testObj)
assert.NoError(t, err)
diff --git a/pkg/reconciler/vertex/scaling/scaling.go b/pkg/reconciler/vertex/scaling/scaling.go
index 03f581e6d7..5ea6b7e6d5 100644
--- a/pkg/reconciler/vertex/scaling/scaling.go
+++ b/pkg/reconciler/vertex/scaling/scaling.go
@@ -302,11 +302,11 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
min := vertex.Spec.Scale.GetMinReplicas()
if desired > max {
desired = max
- log.Infof("Calculated desired replica number %d of vertex %q is greater than max, using max %d.", vertex.Name, desired, max)
+ log.Infof("Calculated desired replica number %d of vertex %q is greater than max, using max %d.", desired, vertex.Name, max)
}
if desired < min {
desired = min
- log.Infof("Calculated desired replica number %d of vertex %q is smaller than min, using min %d.", vertex.Name, desired, min)
+ log.Infof("Calculated desired replica number %d of vertex %q is smaller than min, using min %d.", desired, vertex.Name, min)
}
if current > max || current < min { // Someone might have manually scaled up/down the vertex
return s.patchVertexReplicas(ctx, vertex, desired)
@@ -328,14 +328,14 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
directPressure, downstreamPressure := s.hasBackPressure(*pl, *vertex)
if directPressure {
if current > min && current > 1 { // Scale down but not to 0
- log.Infof("Vertex %s has direct back pressure from connected vertices, decreasing one replica.", key)
+ log.Infof("Vertex %q has direct back pressure from connected vertices, decreasing one replica.", key)
return s.patchVertexReplicas(ctx, vertex, current-1)
} else {
- log.Infof("Vertex %s has direct back pressure from connected vertices, skip scaling.", key)
+ log.Infof("Vertex %q has direct back pressure from connected vertices, skip scaling.", key)
return nil
}
} else if downstreamPressure {
- log.Infof("Vertex %s has back pressure in downstream vertices, skip scaling.", key)
+ log.Infof("Vertex %q has back pressure in downstream vertices, skip scaling.", key)
return nil
}
maxAllowedUp := int32(vertex.Spec.Scale.GetReplicasPerScaleUp())
diff --git a/rust/numaflow-models/src/models/pipeline_status.rs b/rust/numaflow-models/src/models/pipeline_status.rs
index e67205b3cd..2fa64471fd 100644
--- a/rust/numaflow-models/src/models/pipeline_status.rs
+++ b/rust/numaflow-models/src/models/pipeline_status.rs
@@ -21,7 +21,7 @@ pub struct PipelineStatus {
/// Conditions are the latest available observations of a resource's current state.
#[serde(rename = "conditions", skip_serializing_if = "Option::is_none")]
pub conditions: Option>,
- /// Field to indicate if a pipeline drain successfully occurred, or it timed out. Set to true when the Pipeline is in Paused state, and after it has successfully been drained. defaults to false
+ /// Field to indicate if a pipeline drain successfully occurred, only meaningful when the pipeline is paused. True means it has been successfully drained.
#[serde(rename = "drainedOnPause", skip_serializing_if = "Option::is_none")]
pub drained_on_pause: Option,
#[serde(rename = "lastUpdated", skip_serializing_if = "Option::is_none")]
From c4b4d0068012f06980595437b3bc39c73cace8ef Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Mon, 9 Sep 2024 10:09:07 -0700
Subject: [PATCH 052/188] feat: rolling update for Pipeline Vertex (#2040)
Signed-off-by: Derek Wang
---
api/json-schema/schema.json | 21 +-
api/openapi-spec/swagger.json | 21 +-
config/advanced-install/minimal-crds.yaml | 2 +-
.../full/numaflow.numaproj.io_pipelines.yaml | 17 +
.../full/numaflow.numaproj.io_vertices.yaml | 24 +-
.../numaflow.numaproj.io_vertices.yaml | 2 +-
config/install.yaml | 41 +-
config/namespace-install.yaml | 41 +-
docs/APIs.md | 59 +-
.../configuration/sidecar-containers.md | 23 +-
.../configuration/update-strategy.md | 60 +
mkdocs.yml | 37 +-
pkg/apis/numaflow/v1alpha1/generated.pb.go | 1119 +++++++++--------
pkg/apis/numaflow/v1alpha1/generated.proto | 37 +-
.../v1alpha1/mono_vertex_types_test.go | 369 ++++++
.../numaflow/v1alpha1/openapi_generated.go | 38 +-
pkg/apis/numaflow/v1alpha1/vertex_types.go | 48 +-
.../numaflow/v1alpha1/vertex_types_test.go | 29 +-
pkg/reconciler/monovertex/controller.go | 48 +-
pkg/reconciler/monovertex/controller_test.go | 217 ++--
pkg/reconciler/pipeline/controller.go | 54 +-
pkg/reconciler/pipeline/controller_test.go | 46 +-
pkg/reconciler/pipeline/validate.go | 8 +
pkg/reconciler/pipeline/validate_test.go | 41 +
pkg/reconciler/vertex/controller.go | 419 ++++--
pkg/reconciler/vertex/controller_test.go | 354 ++++--
.../src/models/abstract_vertex.rs | 3 +
.../numaflow-models/src/models/vertex_spec.rs | 3 +
.../src/models/vertex_status.rs | 19 +-
29 files changed, 2205 insertions(+), 995 deletions(-)
create mode 100644 docs/user-guide/reference/configuration/update-strategy.md
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index 44e18ef85a..bf1f28f594 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -17785,6 +17785,10 @@
"udf": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UDF"
},
+ "updateStrategy": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UpdateStrategy",
+ "description": "The strategy to use to replace existing pods with new ones."
+ },
"volumes": {
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.Volume"
@@ -20608,6 +20612,10 @@
"udf": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UDF"
},
+ "updateStrategy": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UpdateStrategy",
+ "description": "The strategy to use to replace existing pods with new ones."
+ },
"volumes": {
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.Volume"
@@ -20639,11 +20647,11 @@
"x-kubernetes-patch-strategy": "merge"
},
"currentHash": {
- "description": "If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).",
+ "description": "If not empty, indicates the current version of the Vertex used to generate Pods.",
"type": "string"
},
- "currentReplicas": {
- "description": "The number of Pods created by the controller from the Vertex version indicated by currentHash.",
+ "desiredReplicas": {
+ "description": "The number of desired replicas.",
"format": "int64",
"type": "integer"
},
@@ -20679,9 +20687,14 @@
"type": "string"
},
"updateHash": {
- "description": "If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "description": "If not empty, indicates the updated version of the Vertex used to generate Pods.",
"type": "string"
},
+ "updatedReadyReplicas": {
+ "description": "The number of ready Pods created by the controller from the Vertex version indicated by updateHash.",
+ "format": "int64",
+ "type": "integer"
+ },
"updatedReplicas": {
"description": "The number of Pods created by the controller from the Vertex version indicated by updateHash.",
"format": "int64",
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index 7067416291..91e6e43fb6 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -17793,6 +17793,10 @@
"udf": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UDF"
},
+ "updateStrategy": {
+ "description": "The strategy to use to replace existing pods with new ones.",
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UpdateStrategy"
+ },
"volumes": {
"type": "array",
"items": {
@@ -20590,6 +20594,10 @@
"udf": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UDF"
},
+ "updateStrategy": {
+ "description": "The strategy to use to replace existing pods with new ones.",
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.UpdateStrategy"
+ },
"volumes": {
"type": "array",
"items": {
@@ -20617,11 +20625,11 @@
"x-kubernetes-patch-strategy": "merge"
},
"currentHash": {
- "description": "If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).",
+ "description": "If not empty, indicates the current version of the Vertex used to generate Pods.",
"type": "string"
},
- "currentReplicas": {
- "description": "The number of Pods created by the controller from the Vertex version indicated by currentHash.",
+ "desiredReplicas": {
+ "description": "The number of desired replicas.",
"type": "integer",
"format": "int64"
},
@@ -20657,9 +20665,14 @@
"type": "string"
},
"updateHash": {
- "description": "If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ "description": "If not empty, indicates the updated version of the Vertex used to generate Pods.",
"type": "string"
},
+ "updatedReadyReplicas": {
+ "description": "The number of ready Pods created by the controller from the Vertex version indicated by updateHash.",
+ "type": "integer",
+ "format": "int64"
+ },
"updatedReplicas": {
"description": "The number of Pods created by the controller from the Vertex version indicated by updateHash.",
"type": "integer",
diff --git a/config/advanced-install/minimal-crds.yaml b/config/advanced-install/minimal-crds.yaml
index a8eac9fc22..edf1df3528 100644
--- a/config/advanced-install/minimal-crds.yaml
+++ b/config/advanced-install/minimal-crds.yaml
@@ -206,7 +206,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
diff --git a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
index 6fc509dc96..40db1f403e 100644
--- a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
@@ -9037,6 +9037,23 @@ spec:
- window
type: object
type: object
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
diff --git a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
index e7bb52a8d8..1f02fd2b35 100644
--- a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
@@ -21,7 +21,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
@@ -4701,6 +4701,23 @@ spec:
- window
type: object
type: object
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -5486,7 +5503,7 @@ spec:
type: array
currentHash:
type: string
- currentReplicas:
+ desiredReplicas:
format: int32
type: integer
lastScaledAt:
@@ -5515,6 +5532,9 @@ spec:
type: string
updateHash:
type: string
+ updatedReadyReplicas:
+ format: int32
+ type: integer
updatedReplicas:
format: int32
type: integer
diff --git a/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml b/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml
index 68a95ee056..56799a7791 100644
--- a/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/minimal/numaflow.numaproj.io_vertices.yaml
@@ -17,7 +17,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
diff --git a/config/install.yaml b/config/install.yaml
index d19f2fa2f2..c3db75767a 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -17310,6 +17310,23 @@ spec:
- window
type: object
type: object
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -18168,7 +18185,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
@@ -22848,6 +22865,23 @@ spec:
- window
type: object
type: object
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -23633,7 +23667,7 @@ spec:
type: array
currentHash:
type: string
- currentReplicas:
+ desiredReplicas:
format: int32
type: integer
lastScaledAt:
@@ -23662,6 +23696,9 @@ spec:
type: string
updateHash:
type: string
+ updatedReadyReplicas:
+ format: int32
+ type: integer
updatedReplicas:
format: int32
type: integer
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 5919b0fcf8..5162dc6ad7 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -17310,6 +17310,23 @@ spec:
- window
type: object
type: object
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -18168,7 +18185,7 @@ spec:
- jsonPath: .status.phase
name: Phase
type: string
- - jsonPath: .spec.replicas
+ - jsonPath: .status.desiredReplicas
name: Desired
type: string
- jsonPath: .status.replicas
@@ -22848,6 +22865,23 @@ spec:
- window
type: object
type: object
+ updateStrategy:
+ default:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+ properties:
+ rollingUpdate:
+ properties:
+ maxUnavailable:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ type: object
+ type:
+ type: string
+ type: object
volumes:
items:
properties:
@@ -23633,7 +23667,7 @@ spec:
type: array
currentHash:
type: string
- currentReplicas:
+ desiredReplicas:
format: int32
type: integer
lastScaledAt:
@@ -23662,6 +23696,9 @@ spec:
type: string
updateHash:
type: string
+ updatedReadyReplicas:
+ format: int32
+ type: integer
updatedReplicas:
format: int32
type: integer
diff --git a/docs/APIs.md b/docs/APIs.md
index bd71cf0592..da6ab6eeb1 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -840,6 +840,27 @@ Container template for the side inputs watcher container.
+
+
+
+
+updateStrategy
+ UpdateStrategy
+
+
+
+
+
+(Optional)
+
+
+The strategy to use to replace existing pods with new ones.
+
+
+
+
+
+
@@ -10804,6 +10825,7 @@ UpdateStrategy
(Appears on:
+AbstractVertex ,
MonoVertexSpec )
@@ -11593,6 +11615,25 @@ labels match the selector).
+desiredReplicas
uint32
+
+
+
+
+(Optional)
+
+
+The number of desired replicas.
+
+
+
+
+
+
+
+
+
+
selector
string
@@ -11694,7 +11735,7 @@ The number of pods targeted by this Vertex with a Ready Condition.
-currentReplicas
uint32
+updatedReplicas
uint32
@@ -11702,7 +11743,7 @@ The number of pods targeted by this Vertex with a Ready Condition.
The number of Pods created by the controller from the Vertex version
-indicated by currentHash.
+indicated by updateHash.
@@ -11713,15 +11754,15 @@ indicated by currentHash.
-updatedReplicas
uint32
+updatedReadyReplicas
uint32
-The number of Pods created by the controller from the Vertex version
-indicated by updateHash.
+The number of ready Pods created by the controller from the Vertex
+version indicated by updateHash.
@@ -11739,8 +11780,8 @@ indicated by updateHash.
-If not empty, indicates the version of the Vertex used to generate Pods
-in the sequence \[0,currentReplicas).
+If not empty, indicates the current version of the Vertex used to
+generate Pods.
@@ -11758,8 +11799,8 @@ in the sequence \[0,currentReplicas).
-If not empty, indicates the version of the Vertx used to generate Pods
-in the sequence \[replicas-updatedReplicas,replicas)
+If not empty, indicates the updated version of the Vertex used to
+generate Pods.
diff --git a/docs/user-guide/reference/configuration/sidecar-containers.md b/docs/user-guide/reference/configuration/sidecar-containers.md
index 24aaadbcfe..72d07634c6 100644
--- a/docs/user-guide/reference/configuration/sidecar-containers.md
+++ b/docs/user-guide/reference/configuration/sidecar-containers.md
@@ -1,6 +1,6 @@
# Sidecar Containers
-Additional "[sidecar](https://kubernetes.io/docs/concepts/workloads/pods/#how-pods-manage-multiple-containers)" containers can be provided for `udf` and `sink` vertices. `source` vertices do not currently support sidecars.
+Additional "[sidecar](https://kubernetes.io/docs/concepts/workloads/pods/#how-pods-manage-multiple-containers)" containers can be provided for `source`, `udf` and `sink` vertices.
The following example shows how to add a sidecar container to a `udf` vertex.
@@ -15,7 +15,12 @@ spec:
sidecars:
- name: my-sidecar
image: busybox:latest
- command: ["/bin/sh", "-c", "echo \"my-sidecar is running!\" && tail -f /dev/null"]
+ command:
+ [
+ "/bin/sh",
+ "-c",
+ 'echo "my-sidecar is running!" && tail -f /dev/null',
+ ]
udf:
container:
image: my-function:latest
@@ -42,14 +47,24 @@ spec:
sidecars:
- name: my-sidecar
image: alpine:latest
- command: ["/bin/sh", "-c", "apk add socat && socat UNIX-LISTEN:/path/to/my-sidecar-mount-path/my.sock - && tail -f /dev/null"]
+ command:
+ [
+ "/bin/sh",
+ "-c",
+ "apk add socat && socat UNIX-LISTEN:/path/to/my-sidecar-mount-path/my.sock - && tail -f /dev/null",
+ ]
volumeMounts:
- mountPath: /path/to/my-sidecar-mount-path
name: my-udf-volume
udf:
container:
image: alpine:latest
- command: ["/bin/sh", "-c", "apk add socat && echo \"hello\" | socat UNIX-CONNECT:/path/to/my-udf-mount-path/my.sock,forever - && tail -f /dev/null"]
+ command:
+ [
+ "/bin/sh",
+ "-c",
+ 'apk add socat && echo "hello" | socat UNIX-CONNECT:/path/to/my-udf-mount-path/my.sock,forever - && tail -f /dev/null',
+ ]
volumeMounts:
- mountPath: /path/to/my-udf-mount-path
name: my-udf-volume
diff --git a/docs/user-guide/reference/configuration/update-strategy.md b/docs/user-guide/reference/configuration/update-strategy.md
new file mode 100644
index 0000000000..e105be5c89
--- /dev/null
+++ b/docs/user-guide/reference/configuration/update-strategy.md
@@ -0,0 +1,60 @@
+# Update Strategy
+
+When spec changes, the `RollingUpdate` update strategy is used to update pods in a `Pipeline` or `MonoVertex` by default, which means that the update is done in a rolling fashion. The default configuration is as below.
+
+```yaml
+updateStrategy:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+```
+
+- `maxUnavailable`: The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: `5`) or a percentage of total pods at the start of update (ex: `10%`). Absolute number is calculated from percentage by rounding up. Defaults to `25%`.
+
+## How It Works
+
+The `RollingUpdate` strategy in Numaflow works more like the `RollingUpdate` strategy in `StatefulSet` rather than `Deployment`. It does not create `maxUnavailable` new pods and wait for them to be ready before terminating the old pods. Instead it replaces `maxUnavailable` number of pods with the new spec, then waits for them to be ready before updating the next batch.
+
+For example, if there are 20 pods running, and `maxUnavailable` is set to the default `25%`, during the update, 5 pods will be unavailable at the same time. The update will be done in 4 batches. If your application has a long startup time, and you are sensitive to the unavailability caused tail latency, you should set `maxUnavailable` to a smaller value, and adjust the `scale.min` if it's needed.
+
+During rolling update, [autoscaling](../autoscaling.md) will not be triggered for that particular Vertex or MonoVertex.
+
+## Examples
+
+A `Pipeline` example.
+
+```yaml
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: Pipeline
+metadata:
+ name: simple-pipeline
+spec:
+ vertices:
+ - name: my-vertex
+ updateStrategy:
+ rollingUpdate:
+ maxUnavailable: 25%
+ type: RollingUpdate
+```
+
+A `MonoVertex` example.
+
+```yaml
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: MonoVertex
+metadata:
+ name: my-mvtx
+spec:
+ source:
+ udsource:
+ container:
+ image: my-image1
+ sink:
+ udsink:
+ container:
+ image: my-image2
+ updateStrategy:
+ rollingUpdate:
+ maxUnavailable: 2
+ type: RollingUpdate
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index 7e57a6af21..55dac8b1c1 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -93,17 +93,18 @@ nav:
- user-guide/reference/join-vertex.md
- user-guide/reference/multi-partition.md
- user-guide/reference/side-inputs.md
- - Configuration:
- - user-guide/reference/configuration/pod-specifications.md
- - user-guide/reference/configuration/container-resources.md
- - user-guide/reference/configuration/volumes.md
- - user-guide/reference/configuration/environment-variables.md
- - user-guide/reference/configuration/labels-and-annotations.md
- - user-guide/reference/configuration/init-containers.md
- - user-guide/reference/configuration/sidecar-containers.md
- - user-guide/reference/configuration/pipeline-customization.md
- - user-guide/reference/configuration/istio.md
- - user-guide/reference/configuration/max-message-size.md
+ - Configuration:
+ - user-guide/reference/configuration/pod-specifications.md
+ - user-guide/reference/configuration/container-resources.md
+ - user-guide/reference/configuration/volumes.md
+ - user-guide/reference/configuration/environment-variables.md
+ - user-guide/reference/configuration/labels-and-annotations.md
+ - user-guide/reference/configuration/init-containers.md
+ - user-guide/reference/configuration/sidecar-containers.md
+ - user-guide/reference/configuration/pipeline-customization.md
+ - user-guide/reference/configuration/istio.md
+ - user-guide/reference/configuration/max-message-size.md
+ - user-guide/reference/configuration/update-strategy.md
- user-guide/reference/kustomize/kustomize.md
- APIs.md
- Use Cases:
@@ -116,15 +117,15 @@ nav:
- Configuration:
- Controller Configuration: "operations/controller-configmap.md"
- UI Server:
- - Access Path: "operations/ui/ui-access-path.md"
- - Authentication:
- - Overview: "operations/ui/authn/authentication.md"
- - SSO with Dex: "operations/ui/authn/dex.md"
- - Local Users: "operations/ui/authn/local-users.md"
- - Authorization: "operations/ui/authz/rbac.md"
+ - Access Path: "operations/ui/ui-access-path.md"
+ - Authentication:
+ - Overview: "operations/ui/authn/authentication.md"
+ - SSO with Dex: "operations/ui/authn/dex.md"
+ - Local Users: "operations/ui/authn/local-users.md"
+ - Authorization: "operations/ui/authz/rbac.md"
- operations/metrics/metrics.md
- operations/grafana.md
- - Security: operations/security.md
+ - Security: operations/security.md
- Contributor Guide:
- development/development.md
- Specifications:
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index 5297014695..65ac7601e5 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -2822,502 +2822,501 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7907 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x25, 0x47,
- 0x76, 0x9e, 0xee, 0xff, 0xbd, 0xe7, 0xf2, 0x6f, 0x6a, 0x7e, 0xc4, 0x19, 0x8d, 0x86, 0xe3, 0x96,
- 0x25, 0x8f, 0x63, 0x9b, 0x8c, 0x68, 0xfd, 0xad, 0xed, 0x5d, 0x89, 0x97, 0x1c, 0x72, 0x38, 0x43,
- 0xce, 0x70, 0xcf, 0x25, 0x47, 0x5a, 0x2b, 0x5e, 0xa5, 0xd9, 0x5d, 0xbc, 0x6c, 0xb1, 0x6f, 0xf7,
- 0x55, 0x77, 0x5f, 0xce, 0x50, 0x4e, 0xb0, 0xb6, 0x95, 0x40, 0x0a, 0x82, 0x20, 0x81, 0x9f, 0x0c,
- 0x04, 0x4e, 0x90, 0x20, 0x80, 0x1f, 0x0c, 0xe7, 0x21, 0xc0, 0xe6, 0xc1, 0x40, 0xe2, 0x38, 0x08,
- 0x92, 0x4d, 0x90, 0x9f, 0x45, 0x10, 0x20, 0xca, 0x0b, 0x91, 0x65, 0x90, 0x87, 0x04, 0x88, 0x61,
- 0xc4, 0x48, 0xec, 0x0c, 0x16, 0xd9, 0xa0, 0xfe, 0xfa, 0xef, 0xf6, 0x9d, 0x21, 0x6f, 0x93, 0xa3,
- 0x51, 0xac, 0xb7, 0xee, 0x3a, 0xa7, 0xbe, 0x53, 0x55, 0x5d, 0x5d, 0x75, 0xea, 0x9c, 0x53, 0x55,
- 0xb0, 0xd2, 0xb1, 0x82, 0xdd, 0xfe, 0xf6, 0xac, 0xe1, 0x76, 0xe7, 0x9c, 0x7e, 0x57, 0xef, 0x79,
- 0xee, 0x87, 0xfc, 0x61, 0xc7, 0x76, 0x1f, 0xcc, 0xf5, 0xf6, 0x3a, 0x73, 0x7a, 0xcf, 0xf2, 0xa3,
- 0x94, 0xfd, 0x57, 0x75, 0xbb, 0xb7, 0xab, 0xbf, 0x3a, 0xd7, 0xa1, 0x0e, 0xf5, 0xf4, 0x80, 0x9a,
- 0xb3, 0x3d, 0xcf, 0x0d, 0x5c, 0xf2, 0x66, 0x04, 0x34, 0xab, 0x80, 0x66, 0x55, 0xb6, 0xd9, 0xde,
- 0x5e, 0x67, 0x96, 0x01, 0x45, 0x29, 0x0a, 0xe8, 0xca, 0xcf, 0xc4, 0x4a, 0xd0, 0x71, 0x3b, 0xee,
- 0x1c, 0xc7, 0xdb, 0xee, 0xef, 0xf0, 0x37, 0xfe, 0xc2, 0x9f, 0x84, 0x9c, 0x2b, 0xda, 0xde, 0x5b,
- 0xfe, 0xac, 0xe5, 0xb2, 0x62, 0xcd, 0x19, 0xae, 0x47, 0xe7, 0xf6, 0x07, 0xca, 0x72, 0xe5, 0xb5,
- 0x88, 0xa7, 0xab, 0x1b, 0xbb, 0x96, 0x43, 0xbd, 0x03, 0x55, 0x97, 0x39, 0x8f, 0xfa, 0x6e, 0xdf,
- 0x33, 0xe8, 0x89, 0x72, 0xf9, 0x73, 0x5d, 0x1a, 0xe8, 0x59, 0xb2, 0xe6, 0x86, 0xe5, 0xf2, 0xfa,
- 0x4e, 0x60, 0x75, 0x07, 0xc5, 0xbc, 0xf1, 0xa4, 0x0c, 0xbe, 0xb1, 0x4b, 0xbb, 0xfa, 0x40, 0xbe,
- 0x9f, 0x1d, 0x96, 0xaf, 0x1f, 0x58, 0xf6, 0x9c, 0xe5, 0x04, 0x7e, 0xe0, 0xa5, 0x33, 0x69, 0xbf,
- 0x0f, 0x70, 0x7e, 0x61, 0xdb, 0x0f, 0x3c, 0xdd, 0x08, 0x36, 0x5c, 0x73, 0x93, 0x76, 0x7b, 0xb6,
- 0x1e, 0x50, 0xb2, 0x07, 0x75, 0x56, 0x21, 0x53, 0x0f, 0xf4, 0xe9, 0xc2, 0xf5, 0xc2, 0x8d, 0xe6,
- 0xfc, 0xc2, 0xec, 0x88, 0x1f, 0x70, 0x76, 0x5d, 0x02, 0xb5, 0xc6, 0x8e, 0x0e, 0x67, 0xea, 0xea,
- 0x0d, 0x43, 0x01, 0xe4, 0x37, 0x0a, 0x30, 0xe6, 0xb8, 0x26, 0x6d, 0x53, 0x9b, 0x1a, 0x81, 0xeb,
- 0x4d, 0x17, 0xaf, 0x97, 0x6e, 0x34, 0xe7, 0xbf, 0x3d, 0xb2, 0xc4, 0x8c, 0x1a, 0xcd, 0xde, 0x8d,
- 0x09, 0xb8, 0xe9, 0x04, 0xde, 0x41, 0xeb, 0xc2, 0xf7, 0x0e, 0x67, 0x9e, 0x3b, 0x3a, 0x9c, 0x19,
- 0x8b, 0x93, 0x30, 0x51, 0x12, 0xb2, 0x05, 0xcd, 0xc0, 0xb5, 0x59, 0x93, 0x59, 0xae, 0xe3, 0x4f,
- 0x97, 0x78, 0xc1, 0xae, 0xcd, 0x8a, 0xa6, 0x66, 0xe2, 0x67, 0x59, 0x1f, 0x9b, 0xdd, 0x7f, 0x75,
- 0x76, 0x33, 0x64, 0x6b, 0x9d, 0x97, 0xc0, 0xcd, 0x28, 0xcd, 0xc7, 0x38, 0x0e, 0xa1, 0x30, 0xe9,
- 0x53, 0xa3, 0xef, 0x59, 0xc1, 0xc1, 0xa2, 0xeb, 0x04, 0xf4, 0x61, 0x30, 0x5d, 0xe6, 0xad, 0xfc,
- 0x4a, 0x16, 0xf4, 0x86, 0x6b, 0xb6, 0x93, 0xdc, 0xad, 0xf3, 0x47, 0x87, 0x33, 0x93, 0xa9, 0x44,
- 0x4c, 0x63, 0x12, 0x07, 0xa6, 0xac, 0xae, 0xde, 0xa1, 0x1b, 0x7d, 0xdb, 0x6e, 0x53, 0xc3, 0xa3,
- 0x81, 0x3f, 0x5d, 0xe1, 0x55, 0xb8, 0x91, 0x25, 0x67, 0xcd, 0x35, 0x74, 0xfb, 0xde, 0xf6, 0x87,
- 0xd4, 0x08, 0x90, 0xee, 0x50, 0x8f, 0x3a, 0x06, 0x6d, 0x4d, 0xcb, 0xca, 0x4c, 0xad, 0xa6, 0x90,
- 0x70, 0x00, 0x9b, 0xac, 0xc0, 0xb9, 0x9e, 0x67, 0xb9, 0xbc, 0x08, 0xb6, 0xee, 0xfb, 0x77, 0xf5,
- 0x2e, 0x9d, 0xae, 0x5e, 0x2f, 0xdc, 0x68, 0xb4, 0x2e, 0x4b, 0x98, 0x73, 0x1b, 0x69, 0x06, 0x1c,
- 0xcc, 0x43, 0x6e, 0x40, 0x5d, 0x25, 0x4e, 0xd7, 0xae, 0x17, 0x6e, 0x54, 0x44, 0xdf, 0x51, 0x79,
- 0x31, 0xa4, 0x92, 0x65, 0xa8, 0xeb, 0x3b, 0x3b, 0x96, 0xc3, 0x38, 0xeb, 0xbc, 0x09, 0xaf, 0x66,
- 0x55, 0x6d, 0x41, 0xf2, 0x08, 0x1c, 0xf5, 0x86, 0x61, 0x5e, 0x72, 0x1b, 0x88, 0x4f, 0xbd, 0x7d,
- 0xcb, 0xa0, 0x0b, 0x86, 0xe1, 0xf6, 0x9d, 0x80, 0x97, 0xbd, 0xc1, 0xcb, 0x7e, 0x45, 0x96, 0x9d,
- 0xb4, 0x07, 0x38, 0x30, 0x23, 0x17, 0x79, 0x07, 0xa6, 0xe4, 0xbf, 0x1a, 0xb5, 0x02, 0x70, 0xa4,
- 0x0b, 0xac, 0x21, 0x31, 0x45, 0xc3, 0x01, 0x6e, 0x62, 0xc2, 0x55, 0xbd, 0x1f, 0xb8, 0x5d, 0x06,
- 0x99, 0x14, 0xba, 0xe9, 0xee, 0x51, 0x67, 0xba, 0x79, 0xbd, 0x70, 0xa3, 0xde, 0xba, 0x7e, 0x74,
- 0x38, 0x73, 0x75, 0xe1, 0x31, 0x7c, 0xf8, 0x58, 0x14, 0x72, 0x0f, 0x1a, 0xa6, 0xe3, 0x6f, 0xb8,
- 0xb6, 0x65, 0x1c, 0x4c, 0x8f, 0xf1, 0x02, 0xbe, 0x2a, 0xab, 0xda, 0x58, 0xba, 0xdb, 0x16, 0x84,
- 0x47, 0x87, 0x33, 0x57, 0x07, 0x87, 0xd4, 0xd9, 0x90, 0x8e, 0x11, 0x06, 0x59, 0xe7, 0x80, 0x8b,
- 0xae, 0xb3, 0x63, 0x75, 0xa6, 0xc7, 0xf9, 0xd7, 0xb8, 0x3e, 0xa4, 0x43, 0x2f, 0xdd, 0x6d, 0x0b,
- 0xbe, 0xd6, 0xb8, 0x14, 0x27, 0x5e, 0x31, 0x42, 0x20, 0x26, 0x4c, 0xa8, 0xc1, 0x78, 0xd1, 0xd6,
- 0xad, 0xae, 0x3f, 0x3d, 0xc1, 0x3b, 0xef, 0x8f, 0x0f, 0xc1, 0xc4, 0x38, 0x73, 0xeb, 0x92, 0xac,
- 0xca, 0x44, 0x22, 0xd9, 0xc7, 0x14, 0xe6, 0x95, 0xb7, 0xe1, 0xdc, 0xc0, 0xd8, 0x40, 0xa6, 0xa0,
- 0xb4, 0x47, 0x0f, 0xf8, 0xd0, 0xd7, 0x40, 0xf6, 0x48, 0x2e, 0x40, 0x65, 0x5f, 0xb7, 0xfb, 0x74,
- 0xba, 0xc8, 0xd3, 0xc4, 0xcb, 0xcf, 0x15, 0xdf, 0x2a, 0x68, 0x7f, 0xb7, 0x04, 0x63, 0x6a, 0xc4,
- 0x69, 0x5b, 0xce, 0x1e, 0x79, 0x17, 0x4a, 0xb6, 0xdb, 0x91, 0xe3, 0xe6, 0x2f, 0x8c, 0x3c, 0x8a,
- 0xad, 0xb9, 0x9d, 0x56, 0xed, 0xe8, 0x70, 0xa6, 0xb4, 0xe6, 0x76, 0x90, 0x21, 0x12, 0x03, 0x2a,
- 0x7b, 0xfa, 0xce, 0x9e, 0xce, 0xcb, 0xd0, 0x9c, 0x6f, 0x8d, 0x0c, 0x7d, 0x87, 0xa1, 0xb0, 0xb2,
- 0xb6, 0x1a, 0x47, 0x87, 0x33, 0x15, 0xfe, 0x8a, 0x02, 0x9b, 0xb8, 0xd0, 0xd8, 0xb6, 0x75, 0x63,
- 0x6f, 0xd7, 0xb5, 0xe9, 0x74, 0x29, 0xa7, 0xa0, 0x96, 0x42, 0x12, 0x9f, 0x39, 0x7c, 0xc5, 0x48,
- 0x06, 0x31, 0xa0, 0xda, 0x37, 0x7d, 0xcb, 0xd9, 0x93, 0x63, 0xe0, 0xdb, 0x23, 0x4b, 0xdb, 0x5a,
- 0xe2, 0x75, 0x82, 0xa3, 0xc3, 0x99, 0xaa, 0x78, 0x46, 0x09, 0xad, 0xfd, 0x41, 0x13, 0x26, 0xd4,
- 0x47, 0xba, 0x4f, 0xbd, 0x80, 0x3e, 0x24, 0xd7, 0xa1, 0xec, 0xb0, 0x5f, 0x93, 0x7f, 0xe4, 0xd6,
- 0x98, 0xec, 0x2e, 0x65, 0xfe, 0x4b, 0x72, 0x0a, 0x2b, 0x99, 0xe8, 0x2a, 0xb2, 0xc1, 0x47, 0x2f,
- 0x59, 0x9b, 0xc3, 0x88, 0x92, 0x89, 0x67, 0x94, 0xd0, 0xe4, 0x7d, 0x28, 0xf3, 0xca, 0x8b, 0xa6,
- 0xfe, 0xfa, 0xe8, 0x22, 0x58, 0xd5, 0xeb, 0xac, 0x06, 0xbc, 0xe2, 0x1c, 0x94, 0x75, 0xc5, 0xbe,
- 0xb9, 0x23, 0x1b, 0xf6, 0x17, 0x72, 0x34, 0xec, 0xb2, 0xe8, 0x8a, 0x5b, 0x4b, 0xcb, 0xc8, 0x10,
- 0xc9, 0x5f, 0x2f, 0xc0, 0x39, 0xc3, 0x75, 0x02, 0x9d, 0xe9, 0x19, 0x6a, 0x92, 0x9d, 0xae, 0x70,
- 0x39, 0xb7, 0x47, 0x96, 0xb3, 0x98, 0x46, 0x6c, 0x5d, 0x64, 0x73, 0xc6, 0x40, 0x32, 0x0e, 0xca,
- 0x26, 0x7f, 0xb3, 0x00, 0x17, 0xd9, 0x58, 0x3e, 0xc0, 0xcc, 0x67, 0xa0, 0xd3, 0x2d, 0xd5, 0xe5,
- 0xa3, 0xc3, 0x99, 0x8b, 0xab, 0x59, 0xc2, 0x30, 0xbb, 0x0c, 0xac, 0x74, 0xe7, 0xf5, 0x41, 0xb5,
- 0x84, 0xcf, 0x6e, 0xcd, 0xf9, 0xb5, 0xd3, 0x54, 0x75, 0x5a, 0x2f, 0xc8, 0xae, 0x9c, 0xa5, 0xd9,
- 0x61, 0x56, 0x29, 0xc8, 0x4d, 0xa8, 0xed, 0xbb, 0x76, 0xbf, 0x4b, 0xfd, 0xe9, 0x3a, 0x1f, 0x62,
- 0xaf, 0x64, 0x0d, 0xb1, 0xf7, 0x39, 0x4b, 0x6b, 0x52, 0xc2, 0xd7, 0xc4, 0xbb, 0x8f, 0x2a, 0x2f,
- 0xb1, 0xa0, 0x6a, 0x5b, 0x5d, 0x2b, 0xf0, 0xf9, 0xc4, 0xd9, 0x9c, 0xbf, 0x39, 0x72, 0xb5, 0xc4,
- 0x2f, 0xba, 0xc6, 0xc1, 0xc4, 0x5f, 0x23, 0x9e, 0x51, 0x0a, 0x60, 0x43, 0xa1, 0x6f, 0xe8, 0xb6,
- 0x98, 0x58, 0x9b, 0xf3, 0xdf, 0x18, 0xfd, 0xb7, 0x61, 0x28, 0xad, 0x71, 0x59, 0xa7, 0x0a, 0x7f,
- 0x45, 0x81, 0x4d, 0x7e, 0x09, 0x26, 0x12, 0x5f, 0xd3, 0x9f, 0x6e, 0xf2, 0xd6, 0x79, 0x31, 0xab,
- 0x75, 0x42, 0xae, 0x68, 0xe6, 0x49, 0xf4, 0x10, 0x1f, 0x53, 0x60, 0xe4, 0x0e, 0xd4, 0x7d, 0xcb,
- 0xa4, 0x86, 0xee, 0xf9, 0xd3, 0x63, 0xc7, 0x01, 0x9e, 0x92, 0xc0, 0xf5, 0xb6, 0xcc, 0x86, 0x21,
- 0x00, 0x99, 0x05, 0xe8, 0xe9, 0x5e, 0x60, 0x09, 0x45, 0x75, 0x9c, 0x2b, 0x4d, 0x13, 0x47, 0x87,
- 0x33, 0xb0, 0x11, 0xa6, 0x62, 0x8c, 0x83, 0xf1, 0xb3, 0xbc, 0xab, 0x4e, 0xaf, 0x1f, 0x88, 0x89,
- 0xb5, 0x21, 0xf8, 0xdb, 0x61, 0x2a, 0xc6, 0x38, 0xc8, 0xef, 0x14, 0xe0, 0x85, 0xe8, 0x75, 0xf0,
- 0x27, 0x9b, 0x3c, 0xf5, 0x9f, 0x6c, 0xe6, 0xe8, 0x70, 0xe6, 0x85, 0xf6, 0x70, 0x91, 0xf8, 0xb8,
- 0xf2, 0x68, 0xef, 0xc2, 0xf8, 0x42, 0x3f, 0xd8, 0x75, 0x3d, 0xeb, 0x63, 0xae, 0x74, 0x93, 0x65,
- 0xa8, 0x04, 0x5c, 0x79, 0x12, 0xf3, 0xf2, 0xcb, 0x59, 0x4d, 0x2d, 0x14, 0xd9, 0x3b, 0xf4, 0x40,
- 0x69, 0x03, 0x62, 0x7e, 0x14, 0xca, 0x94, 0xc8, 0xae, 0xfd, 0xa5, 0x02, 0xd4, 0x5a, 0xba, 0xb1,
- 0xe7, 0xee, 0xec, 0x90, 0xf7, 0xa0, 0x6e, 0x39, 0x01, 0xf5, 0xf6, 0x75, 0x5b, 0xc2, 0xce, 0xc6,
- 0x60, 0xc3, 0x65, 0x58, 0x54, 0x6f, 0xb6, 0xe6, 0x61, 0x82, 0x96, 0xfa, 0x72, 0xad, 0xc0, 0xf5,
- 0xd1, 0x55, 0x89, 0x81, 0x21, 0x1a, 0x99, 0x81, 0x8a, 0x1f, 0xd0, 0x9e, 0xcf, 0x67, 0x9e, 0x71,
- 0x51, 0x8c, 0x36, 0x4b, 0x40, 0x91, 0xae, 0xfd, 0x9d, 0x02, 0x34, 0x5a, 0xba, 0x6f, 0x19, 0xac,
- 0x96, 0x64, 0x11, 0xca, 0x7d, 0x9f, 0x7a, 0x27, 0xab, 0x1b, 0x9f, 0x2c, 0xb6, 0x7c, 0xea, 0x21,
- 0xcf, 0x4c, 0xee, 0x41, 0xbd, 0xa7, 0xfb, 0xfe, 0x03, 0xd7, 0x33, 0xe5, 0x84, 0x77, 0x4c, 0x20,
- 0xa1, 0x9c, 0xcb, 0xac, 0x18, 0x82, 0x68, 0x4d, 0x88, 0x66, 0x7c, 0xed, 0x8f, 0x0a, 0x70, 0xbe,
- 0xd5, 0xdf, 0xd9, 0xa1, 0x9e, 0xd4, 0x45, 0xa5, 0x96, 0x47, 0xa1, 0xe2, 0x51, 0xd3, 0xf2, 0x65,
- 0xd9, 0x97, 0x46, 0xee, 0x41, 0xc8, 0x50, 0xa4, 0x52, 0xc9, 0xdb, 0x8b, 0x27, 0xa0, 0x40, 0x27,
- 0x7d, 0x68, 0x7c, 0x48, 0xd9, 0x1a, 0x98, 0xea, 0x5d, 0x59, 0xbb, 0x5b, 0x23, 0x8b, 0xba, 0x4d,
- 0x83, 0x36, 0x47, 0x8a, 0xeb, 0xb0, 0x61, 0x22, 0x46, 0x92, 0xb4, 0xdf, 0xaf, 0xc0, 0xd8, 0xa2,
- 0xdb, 0xdd, 0xb6, 0x1c, 0x6a, 0xde, 0x34, 0x3b, 0x94, 0x7c, 0x00, 0x65, 0x6a, 0x76, 0xa8, 0xac,
- 0xed, 0xe8, 0xd3, 0x3d, 0x03, 0x8b, 0x94, 0x16, 0xf6, 0x86, 0x1c, 0x98, 0xac, 0xc1, 0xc4, 0x8e,
- 0xe7, 0x76, 0xc5, 0x08, 0xba, 0x79, 0xd0, 0x93, 0x1a, 0x6b, 0xeb, 0xc7, 0xd5, 0xa8, 0xb4, 0x9c,
- 0xa0, 0x3e, 0x3a, 0x9c, 0x81, 0xe8, 0x0d, 0x53, 0x79, 0xc9, 0x7b, 0x30, 0x1d, 0xa5, 0x84, 0x43,
- 0xc9, 0x22, 0x5b, 0x44, 0x70, 0x8d, 0xa5, 0xd2, 0xba, 0x7a, 0x74, 0x38, 0x33, 0xbd, 0x3c, 0x84,
- 0x07, 0x87, 0xe6, 0x26, 0x9f, 0x16, 0x60, 0x2a, 0x22, 0x8a, 0xe1, 0x5d, 0x2a, 0x2a, 0xa7, 0x34,
- 0x6f, 0xf0, 0xd5, 0xd6, 0x72, 0x4a, 0x04, 0x0e, 0x08, 0x25, 0xcb, 0x30, 0x16, 0xb8, 0xb1, 0xf6,
- 0xaa, 0xf0, 0xf6, 0xd2, 0x94, 0x79, 0x60, 0xd3, 0x1d, 0xda, 0x5a, 0x89, 0x7c, 0x04, 0xe1, 0x92,
- 0x7a, 0x4f, 0xb5, 0x54, 0x95, 0xb7, 0xd4, 0x95, 0xa3, 0xc3, 0x99, 0x4b, 0x9b, 0x99, 0x1c, 0x38,
- 0x24, 0x27, 0xf9, 0xd5, 0x02, 0x4c, 0x28, 0x92, 0x6c, 0xa3, 0xda, 0x69, 0xb6, 0x11, 0x61, 0x3d,
- 0x62, 0x33, 0x21, 0x00, 0x53, 0x02, 0xb5, 0x3f, 0x29, 0x43, 0x23, 0x1c, 0x60, 0xc9, 0x4b, 0x50,
- 0xe1, 0x0b, 0x7f, 0xa9, 0x37, 0x87, 0x33, 0x27, 0xb7, 0x0f, 0xa0, 0xa0, 0x91, 0x97, 0xa1, 0x66,
- 0xb8, 0xdd, 0xae, 0xee, 0x98, 0xdc, 0x98, 0xd3, 0x68, 0x35, 0x99, 0xc2, 0xb0, 0x28, 0x92, 0x50,
- 0xd1, 0xc8, 0x55, 0x28, 0xeb, 0x5e, 0x47, 0xd8, 0x55, 0x1a, 0x62, 0x3c, 0x5a, 0xf0, 0x3a, 0x3e,
- 0xf2, 0x54, 0xf2, 0x35, 0x28, 0x51, 0x67, 0x7f, 0xba, 0x3c, 0x5c, 0x23, 0xb9, 0xe9, 0xec, 0xdf,
- 0xd7, 0xbd, 0x56, 0x53, 0x96, 0xa1, 0x74, 0xd3, 0xd9, 0x47, 0x96, 0x87, 0xac, 0x41, 0x8d, 0x3a,
- 0xfb, 0xec, 0xdb, 0x4b, 0x83, 0xc7, 0x8f, 0x0d, 0xc9, 0xce, 0x58, 0xa4, 0x72, 0x1e, 0xea, 0x35,
- 0x32, 0x19, 0x15, 0x04, 0xf9, 0x16, 0x8c, 0x09, 0x15, 0x67, 0x9d, 0x7d, 0x13, 0x7f, 0xba, 0xca,
- 0x21, 0x67, 0x86, 0xeb, 0x48, 0x9c, 0x2f, 0x32, 0x30, 0xc5, 0x12, 0x7d, 0x4c, 0x40, 0x91, 0x6f,
- 0x41, 0x43, 0xad, 0x47, 0xd5, 0x97, 0xcd, 0xb4, 0xcd, 0xa8, 0x45, 0x2c, 0xd2, 0x8f, 0xfa, 0x96,
- 0x47, 0xbb, 0xd4, 0x09, 0xfc, 0xd6, 0x39, 0xb5, 0x5a, 0x57, 0x54, 0x1f, 0x23, 0x34, 0xb2, 0x3d,
- 0x68, 0x64, 0x12, 0x16, 0x92, 0x97, 0x86, 0x8c, 0xea, 0x23, 0x58, 0x98, 0xbe, 0x0d, 0x93, 0xa1,
- 0x15, 0x48, 0x1a, 0x12, 0x84, 0xcd, 0xe4, 0x35, 0x96, 0x7d, 0x35, 0x49, 0x7a, 0x74, 0x38, 0xf3,
- 0x62, 0x86, 0x29, 0x21, 0x62, 0xc0, 0x34, 0x98, 0xf6, 0x7b, 0x25, 0x18, 0xd4, 0xfe, 0x93, 0x8d,
- 0x56, 0x38, 0xed, 0x46, 0x4b, 0x57, 0x48, 0x0c, 0x9f, 0x6f, 0xc9, 0x6c, 0xf9, 0x2b, 0x95, 0xf5,
- 0x61, 0x4a, 0xa7, 0xfd, 0x61, 0x9e, 0x95, 0x7f, 0x47, 0xfb, 0xac, 0x0c, 0x13, 0x4b, 0x3a, 0xed,
- 0xba, 0xce, 0x13, 0xd7, 0x42, 0x85, 0x67, 0x62, 0x2d, 0x74, 0x03, 0xea, 0x1e, 0xed, 0xd9, 0x96,
- 0xa1, 0x0b, 0xe5, 0x4b, 0xda, 0x1e, 0x51, 0xa6, 0x61, 0x48, 0x1d, 0xb2, 0x06, 0x2e, 0x3d, 0x93,
- 0x6b, 0xe0, 0xf2, 0x17, 0xbf, 0x06, 0xd6, 0x7e, 0xb5, 0x08, 0x5c, 0x51, 0x21, 0xd7, 0xa1, 0xcc,
- 0x26, 0xe1, 0xb4, 0xe5, 0x85, 0x77, 0x1c, 0x4e, 0x21, 0x57, 0xa0, 0x18, 0xb8, 0xf2, 0xcf, 0x03,
- 0x49, 0x2f, 0x6e, 0xba, 0x58, 0x0c, 0x5c, 0xf2, 0x31, 0x80, 0xe1, 0x3a, 0xa6, 0xa5, 0x4c, 0xf2,
- 0xf9, 0x2a, 0xb6, 0xec, 0x7a, 0x0f, 0x74, 0xcf, 0x5c, 0x0c, 0x11, 0xc5, 0x2a, 0x28, 0x7a, 0xc7,
- 0x98, 0x34, 0xf2, 0x36, 0x54, 0x5d, 0x67, 0xb9, 0x6f, 0xdb, 0xbc, 0x41, 0x1b, 0xad, 0x9f, 0x60,
- 0x4b, 0xd3, 0x7b, 0x3c, 0xe5, 0xd1, 0xe1, 0xcc, 0x65, 0xa1, 0xdf, 0xb2, 0xb7, 0x77, 0x3d, 0x2b,
- 0xb0, 0x9c, 0x4e, 0x3b, 0xf0, 0xf4, 0x80, 0x76, 0x0e, 0x50, 0x66, 0xd3, 0x7e, 0xbd, 0x00, 0xcd,
- 0x65, 0xeb, 0x21, 0x35, 0xdf, 0xb5, 0x1c, 0xd3, 0x7d, 0x40, 0x10, 0xaa, 0x36, 0x75, 0x3a, 0xc1,
- 0xee, 0x88, 0xeb, 0x07, 0xb1, 0x36, 0xe6, 0x08, 0x28, 0x91, 0xc8, 0x1c, 0x34, 0x84, 0xf6, 0x69,
- 0x39, 0x1d, 0xde, 0x86, 0xf5, 0x68, 0xd0, 0x6b, 0x2b, 0x02, 0x46, 0x3c, 0xda, 0x01, 0x9c, 0x1b,
- 0x68, 0x06, 0x62, 0x42, 0x39, 0xd0, 0x3b, 0x6a, 0x7c, 0x5d, 0x1e, 0xb9, 0x81, 0x37, 0xf5, 0x4e,
- 0xac, 0x71, 0xf9, 0x1c, 0xbf, 0xa9, 0xb3, 0x39, 0x9e, 0xa1, 0x6b, 0x3f, 0x2c, 0x40, 0x7d, 0xb9,
- 0xef, 0x18, 0x7c, 0x89, 0xf6, 0x64, 0x8b, 0x9c, 0x52, 0x18, 0x8a, 0x99, 0x0a, 0x43, 0x1f, 0xaa,
- 0x7b, 0x0f, 0x42, 0x85, 0xa2, 0x39, 0xbf, 0x3e, 0x7a, 0xaf, 0x90, 0x45, 0x9a, 0xbd, 0xc3, 0xf1,
- 0x84, 0xc3, 0x68, 0x42, 0x16, 0xa8, 0x7a, 0xe7, 0x5d, 0x2e, 0x54, 0x0a, 0xbb, 0xf2, 0x35, 0x68,
- 0xc6, 0xd8, 0x4e, 0x64, 0x3b, 0xfe, 0x87, 0x65, 0xa8, 0xae, 0xb4, 0xdb, 0x0b, 0x1b, 0xab, 0xe4,
- 0x75, 0x68, 0x4a, 0x5f, 0xc2, 0xdd, 0xa8, 0x0d, 0x42, 0x57, 0x52, 0x3b, 0x22, 0x61, 0x9c, 0x8f,
- 0xa9, 0x63, 0x1e, 0xd5, 0xed, 0xae, 0xfc, 0x59, 0x42, 0x75, 0x0c, 0x59, 0x22, 0x0a, 0x1a, 0xd1,
- 0x61, 0x82, 0xad, 0xf0, 0x58, 0x13, 0x8a, 0xd5, 0x9b, 0xfc, 0x6d, 0x8e, 0xb9, 0xbe, 0xe3, 0x4a,
- 0xe2, 0x56, 0x02, 0x00, 0x53, 0x80, 0xe4, 0x2d, 0xa8, 0xeb, 0xfd, 0x60, 0x97, 0x2b, 0xd0, 0xe2,
- 0xdf, 0xb8, 0xca, 0x5d, 0x2d, 0x32, 0xed, 0xd1, 0xe1, 0xcc, 0xd8, 0x1d, 0x6c, 0xbd, 0xae, 0xde,
- 0x31, 0xe4, 0x66, 0x85, 0x53, 0x2b, 0x46, 0x59, 0xb8, 0xca, 0x89, 0x0b, 0xb7, 0x91, 0x00, 0xc0,
- 0x14, 0x20, 0x79, 0x1f, 0xc6, 0xf6, 0xe8, 0x41, 0xa0, 0x6f, 0x4b, 0x01, 0xd5, 0x93, 0x08, 0x98,
- 0x62, 0x2a, 0xdc, 0x9d, 0x58, 0x76, 0x4c, 0x80, 0x11, 0x1f, 0x2e, 0xec, 0x51, 0x6f, 0x9b, 0x7a,
- 0xae, 0x5c, 0x7d, 0x4a, 0x21, 0xb5, 0x93, 0x08, 0x99, 0x3e, 0x3a, 0x9c, 0xb9, 0x70, 0x27, 0x03,
- 0x06, 0x33, 0xc1, 0xb5, 0xff, 0x53, 0x84, 0xc9, 0x15, 0xe1, 0xcc, 0x75, 0x3d, 0x31, 0x09, 0x93,
- 0xcb, 0x50, 0xf2, 0x7a, 0x7d, 0xde, 0x73, 0x4a, 0xc2, 0x5c, 0x8b, 0x1b, 0x5b, 0xc8, 0xd2, 0xc8,
- 0x7b, 0x50, 0x37, 0xe5, 0x90, 0x21, 0x17, 0xbf, 0x23, 0x19, 0x2a, 0xd4, 0x1b, 0x86, 0x68, 0x4c,
- 0xd3, 0xef, 0xfa, 0x9d, 0xb6, 0xf5, 0x31, 0x95, 0xeb, 0x41, 0xae, 0xe9, 0xaf, 0x8b, 0x24, 0x54,
- 0x34, 0x36, 0xab, 0xee, 0xd1, 0x03, 0xb1, 0x1a, 0x2a, 0x47, 0xb3, 0xea, 0x1d, 0x99, 0x86, 0x21,
- 0x95, 0xcc, 0xa8, 0x9f, 0x85, 0xf5, 0x82, 0xb2, 0x58, 0xc9, 0xdf, 0x67, 0x09, 0xf2, 0xbf, 0x61,
- 0x43, 0xe6, 0x87, 0x56, 0x10, 0x50, 0x4f, 0x7e, 0xc6, 0x91, 0x86, 0xcc, 0xdb, 0x1c, 0x01, 0x25,
- 0x12, 0xf9, 0x29, 0x68, 0x70, 0xf0, 0x96, 0xed, 0x6e, 0xf3, 0x0f, 0xd7, 0x10, 0x6b, 0xfa, 0xfb,
- 0x2a, 0x11, 0x23, 0xba, 0xf6, 0xa3, 0x22, 0x5c, 0x5a, 0xa1, 0x81, 0xd0, 0x6a, 0x96, 0x68, 0xcf,
- 0x76, 0x0f, 0x98, 0x6a, 0x89, 0xf4, 0x23, 0xf2, 0x0e, 0x80, 0xe5, 0x6f, 0xb7, 0xf7, 0x0d, 0xfe,
- 0x1f, 0x88, 0x7f, 0xf8, 0xba, 0xfc, 0x25, 0x61, 0xb5, 0xdd, 0x92, 0x94, 0x47, 0x89, 0x37, 0x8c,
- 0xe5, 0x89, 0x96, 0x57, 0xc5, 0xc7, 0x2c, 0xaf, 0xda, 0x00, 0xbd, 0x48, 0x41, 0x2d, 0x71, 0xce,
- 0x9f, 0x55, 0x62, 0x4e, 0xa2, 0x9b, 0xc6, 0x60, 0xf2, 0xa8, 0x8c, 0x0e, 0x4c, 0x99, 0x74, 0x47,
- 0xef, 0xdb, 0x41, 0xa8, 0x54, 0xcb, 0x9f, 0xf8, 0xf8, 0x7a, 0x79, 0xe8, 0x68, 0x5e, 0x4a, 0x21,
- 0xe1, 0x00, 0xb6, 0xf6, 0xbb, 0x25, 0xb8, 0xb2, 0x42, 0x83, 0xd0, 0xe2, 0x22, 0x47, 0xc7, 0x76,
- 0x8f, 0x1a, 0xec, 0x2b, 0x7c, 0x5a, 0x80, 0xaa, 0xad, 0x6f, 0x53, 0x9b, 0xcd, 0x5e, 0xac, 0x36,
- 0x1f, 0x8c, 0x3c, 0x11, 0x0c, 0x97, 0x32, 0xbb, 0xc6, 0x25, 0xa4, 0xa6, 0x06, 0x91, 0x88, 0x52,
- 0x3c, 0x1b, 0xd4, 0x0d, 0xbb, 0xef, 0x07, 0xd4, 0xdb, 0x70, 0xbd, 0x40, 0xea, 0x93, 0xe1, 0xa0,
- 0xbe, 0x18, 0x91, 0x30, 0xce, 0x47, 0xe6, 0x01, 0x0c, 0xdb, 0xa2, 0x4e, 0xc0, 0x73, 0x89, 0xff,
- 0x8a, 0xa8, 0xef, 0xbb, 0x18, 0x52, 0x30, 0xc6, 0xc5, 0x44, 0x75, 0x5d, 0xc7, 0x0a, 0x5c, 0x21,
- 0xaa, 0x9c, 0x14, 0xb5, 0x1e, 0x91, 0x30, 0xce, 0xc7, 0xb3, 0xd1, 0xc0, 0xb3, 0x0c, 0x9f, 0x67,
- 0xab, 0xa4, 0xb2, 0x45, 0x24, 0x8c, 0xf3, 0xb1, 0x39, 0x2f, 0x56, 0xff, 0x13, 0xcd, 0x79, 0xbf,
- 0xdd, 0x80, 0x6b, 0x89, 0x66, 0x0d, 0xf4, 0x80, 0xee, 0xf4, 0xed, 0x36, 0x0d, 0xd4, 0x07, 0x1c,
- 0x71, 0x2e, 0xfc, 0xab, 0xd1, 0x77, 0x17, 0x21, 0x24, 0xc6, 0xe9, 0x7c, 0xf7, 0x81, 0x02, 0x1e,
- 0xeb, 0xdb, 0xcf, 0x41, 0xc3, 0xd1, 0x03, 0x9f, 0xff, 0xb8, 0xf2, 0x1f, 0x0d, 0xd5, 0xb0, 0xbb,
- 0x8a, 0x80, 0x11, 0x0f, 0xd9, 0x80, 0x0b, 0xb2, 0x89, 0x6f, 0x3e, 0xec, 0xb9, 0x5e, 0x40, 0x3d,
- 0x91, 0x57, 0x4e, 0xa7, 0x32, 0xef, 0x85, 0xf5, 0x0c, 0x1e, 0xcc, 0xcc, 0x49, 0xd6, 0xe1, 0xbc,
- 0x21, 0xdc, 0xea, 0xd4, 0x76, 0x75, 0x53, 0x01, 0x0a, 0x03, 0x57, 0xb8, 0x34, 0x5a, 0x1c, 0x64,
- 0xc1, 0xac, 0x7c, 0xe9, 0xde, 0x5c, 0x1d, 0xa9, 0x37, 0xd7, 0x46, 0xe9, 0xcd, 0xf5, 0xd1, 0x7a,
- 0x73, 0xe3, 0x78, 0xbd, 0x99, 0xb5, 0x3c, 0xeb, 0x47, 0xd4, 0x63, 0xea, 0x89, 0x98, 0x61, 0x63,
- 0x51, 0x1b, 0x61, 0xcb, 0xb7, 0x33, 0x78, 0x30, 0x33, 0x27, 0xd9, 0x86, 0x2b, 0x22, 0xfd, 0xa6,
- 0x63, 0x78, 0x07, 0x3d, 0x36, 0xf1, 0xc4, 0x70, 0x9b, 0x09, 0x0b, 0xe3, 0x95, 0xf6, 0x50, 0x4e,
- 0x7c, 0x0c, 0x0a, 0xf9, 0x79, 0x18, 0x17, 0x5f, 0x69, 0x5d, 0xef, 0x71, 0x58, 0x11, 0xc3, 0x71,
- 0x51, 0xc2, 0x8e, 0x2f, 0xc6, 0x89, 0x98, 0xe4, 0x25, 0x0b, 0x30, 0xd9, 0xdb, 0x37, 0xd8, 0xe3,
- 0xea, 0xce, 0x5d, 0x4a, 0x4d, 0x6a, 0x72, 0xa7, 0x51, 0xa3, 0xf5, 0xbc, 0x32, 0x74, 0x6c, 0x24,
- 0xc9, 0x98, 0xe6, 0x27, 0x6f, 0xc1, 0x98, 0x1f, 0xe8, 0x5e, 0x20, 0xcd, 0x7a, 0xd3, 0x13, 0x22,
- 0xc6, 0x45, 0x59, 0xbd, 0xda, 0x31, 0x1a, 0x26, 0x38, 0x33, 0xe7, 0x8b, 0xc9, 0xb3, 0x9b, 0x2f,
- 0xf2, 0x8c, 0x56, 0xff, 0xa2, 0x08, 0xd7, 0x57, 0x68, 0xb0, 0xee, 0x3a, 0xd2, 0x28, 0x9a, 0x35,
- 0xed, 0x1f, 0xcb, 0x26, 0x9a, 0x9c, 0xb4, 0x8b, 0xa7, 0x3a, 0x69, 0x97, 0x4e, 0x69, 0xd2, 0x2e,
- 0x9f, 0xe1, 0xa4, 0xfd, 0x8f, 0x8b, 0xf0, 0x7c, 0xa2, 0x25, 0x37, 0x5c, 0x53, 0x0d, 0xf8, 0x5f,
- 0x35, 0xe0, 0x31, 0x1a, 0xf0, 0x91, 0xd0, 0x3b, 0xb9, 0x5b, 0x2b, 0xa5, 0xf1, 0x7c, 0x92, 0xd6,
- 0x78, 0xde, 0xcf, 0x33, 0xf3, 0x65, 0x48, 0x38, 0xd6, 0x8c, 0x77, 0x1b, 0x88, 0x27, 0x9d, 0x70,
- 0xc2, 0xf4, 0x13, 0x53, 0x7a, 0xc2, 0x20, 0x3a, 0x1c, 0xe0, 0xc0, 0x8c, 0x5c, 0xa4, 0x0d, 0x17,
- 0x7d, 0xea, 0x04, 0x96, 0x43, 0xed, 0x24, 0x9c, 0xd0, 0x86, 0x5e, 0x94, 0x70, 0x17, 0xdb, 0x59,
- 0x4c, 0x98, 0x9d, 0x37, 0xcf, 0x38, 0xf0, 0xaf, 0x81, 0xab, 0x9c, 0xa2, 0x69, 0x4e, 0x4d, 0x63,
- 0xf9, 0x34, 0xad, 0xb1, 0x7c, 0x90, 0xff, 0xbb, 0x8d, 0xa6, 0xad, 0xcc, 0x03, 0xf0, 0xaf, 0x10,
- 0x57, 0x57, 0xc2, 0x49, 0x1a, 0x43, 0x0a, 0xc6, 0xb8, 0xd8, 0x04, 0xa4, 0xda, 0x39, 0xae, 0xa9,
- 0x84, 0x13, 0x50, 0x3b, 0x4e, 0xc4, 0x24, 0xef, 0x50, 0x6d, 0xa7, 0x32, 0xb2, 0xb6, 0x73, 0x1b,
- 0x48, 0xc2, 0xf0, 0x28, 0xf0, 0xaa, 0xc9, 0x18, 0xce, 0xd5, 0x01, 0x0e, 0xcc, 0xc8, 0x35, 0xa4,
- 0x2b, 0xd7, 0x4e, 0xb7, 0x2b, 0xd7, 0x47, 0xef, 0xca, 0xe4, 0x03, 0xb8, 0xcc, 0x45, 0xc9, 0xf6,
- 0x49, 0x02, 0x0b, 0xbd, 0xe7, 0xc7, 0x24, 0xf0, 0x65, 0x1c, 0xc6, 0x88, 0xc3, 0x31, 0xd8, 0xf7,
- 0x31, 0x3c, 0x6a, 0x32, 0xe1, 0xba, 0x3d, 0x5c, 0x27, 0x5a, 0xcc, 0xe0, 0xc1, 0xcc, 0x9c, 0xac,
- 0x8b, 0x05, 0xac, 0x1b, 0xea, 0xdb, 0x36, 0x35, 0x65, 0x0c, 0x6b, 0xd8, 0xc5, 0x36, 0xd7, 0xda,
- 0x92, 0x82, 0x31, 0xae, 0x2c, 0x35, 0x65, 0xec, 0x84, 0x6a, 0xca, 0x0a, 0xb7, 0xd2, 0xef, 0x24,
- 0xb4, 0x21, 0xa9, 0xeb, 0x84, 0x51, 0xc9, 0x8b, 0x69, 0x06, 0x1c, 0xcc, 0xc3, 0xb5, 0x44, 0xc3,
- 0xb3, 0x7a, 0x81, 0x9f, 0xc4, 0x9a, 0x48, 0x69, 0x89, 0x19, 0x3c, 0x98, 0x99, 0x93, 0xe9, 0xe7,
- 0xbb, 0x54, 0xb7, 0x83, 0xdd, 0x24, 0xe0, 0x64, 0x52, 0x3f, 0xbf, 0x35, 0xc8, 0x82, 0x59, 0xf9,
- 0x32, 0x27, 0xa4, 0xa9, 0x67, 0x53, 0xad, 0xfa, 0xb5, 0x12, 0x5c, 0x5e, 0xa1, 0x41, 0x18, 0xde,
- 0xf3, 0x95, 0x19, 0xe5, 0x0b, 0x30, 0xa3, 0xfc, 0x56, 0x05, 0xce, 0xaf, 0xd0, 0x60, 0x40, 0x1b,
- 0xfb, 0x53, 0xda, 0xfc, 0xeb, 0x70, 0x3e, 0x8a, 0x28, 0x6b, 0x07, 0xae, 0x27, 0xe6, 0xf2, 0xd4,
- 0x6a, 0xb9, 0x3d, 0xc8, 0x82, 0x59, 0xf9, 0xc8, 0xb7, 0xe0, 0x79, 0x3e, 0xd5, 0x3b, 0x1d, 0x61,
- 0x9f, 0x15, 0xc6, 0x84, 0xd8, 0x9e, 0x88, 0x19, 0x09, 0xf9, 0x7c, 0x3b, 0x9b, 0x0d, 0x87, 0xe5,
- 0x27, 0xdf, 0x81, 0xb1, 0x9e, 0xd5, 0xa3, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0x77, 0x48, 0xc8, 0x46,
- 0x0c, 0x2c, 0x5a, 0xc0, 0xc5, 0x53, 0x31, 0x21, 0x30, 0xb3, 0xa7, 0xd6, 0xcf, 0xb0, 0xa7, 0xfe,
- 0xcf, 0x22, 0xd4, 0x56, 0x3c, 0xb7, 0xdf, 0x6b, 0x1d, 0x90, 0x0e, 0x54, 0x1f, 0x70, 0xe7, 0x99,
- 0x74, 0x4d, 0x8d, 0x1e, 0x95, 0x2d, 0x7c, 0x70, 0x91, 0x4a, 0x24, 0xde, 0x51, 0xc2, 0xb3, 0x4e,
- 0xbc, 0x47, 0x0f, 0xa8, 0x29, 0x7d, 0x68, 0x61, 0x27, 0xbe, 0xc3, 0x12, 0x51, 0xd0, 0x48, 0x17,
- 0x26, 0x75, 0xdb, 0x76, 0x1f, 0x50, 0x73, 0x4d, 0x0f, 0xa8, 0x43, 0x7d, 0xe5, 0x92, 0x3c, 0xa9,
- 0x59, 0x9a, 0xfb, 0xf5, 0x17, 0x92, 0x50, 0x98, 0xc6, 0x26, 0x1f, 0x42, 0xcd, 0x0f, 0x5c, 0x4f,
- 0x29, 0x5b, 0xcd, 0xf9, 0xc5, 0xd1, 0x3f, 0x7a, 0xeb, 0x9b, 0x6d, 0x01, 0x25, 0x6c, 0xf6, 0xf2,
- 0x05, 0x95, 0x00, 0xed, 0x37, 0x0b, 0x00, 0xb7, 0x36, 0x37, 0x37, 0xa4, 0x7b, 0xc1, 0x84, 0xb2,
- 0xde, 0x0f, 0x1d, 0x95, 0xa3, 0x3b, 0x04, 0x13, 0x61, 0x99, 0xd2, 0x87, 0xd7, 0x0f, 0x76, 0x91,
- 0xa3, 0x93, 0x9f, 0x84, 0x9a, 0x54, 0x90, 0x65, 0xb3, 0x87, 0xa1, 0x05, 0x52, 0x89, 0x46, 0x45,
- 0xd7, 0xfe, 0x41, 0x11, 0x60, 0xd5, 0xb4, 0x69, 0x5b, 0x05, 0xd2, 0x37, 0x82, 0x5d, 0x8f, 0xfa,
- 0xbb, 0xae, 0x6d, 0x8e, 0xe8, 0x4d, 0xe5, 0x36, 0xff, 0x4d, 0x05, 0x82, 0x11, 0x1e, 0x31, 0x61,
- 0xcc, 0x0f, 0x68, 0x4f, 0x45, 0x6a, 0x8e, 0xe8, 0x44, 0x99, 0x12, 0x76, 0x91, 0x08, 0x07, 0x13,
- 0xa8, 0x44, 0x87, 0xa6, 0xe5, 0x18, 0xe2, 0x07, 0x69, 0x1d, 0x8c, 0xd8, 0x91, 0x26, 0xd9, 0x8a,
- 0x63, 0x35, 0x82, 0xc1, 0x38, 0xa6, 0xf6, 0x87, 0x45, 0xb8, 0xc4, 0xe5, 0xb1, 0x62, 0x24, 0xe2,
- 0x31, 0xc9, 0x9f, 0x1f, 0xd8, 0xf4, 0xf7, 0x67, 0x8f, 0x27, 0x5a, 0xec, 0x19, 0x5b, 0xa7, 0x81,
- 0x1e, 0xe9, 0x73, 0x51, 0x5a, 0x6c, 0xa7, 0x5f, 0x1f, 0xca, 0x3e, 0x1b, 0xaf, 0x44, 0xeb, 0xb5,
- 0x47, 0xee, 0x42, 0xd9, 0x15, 0xe0, 0xa3, 0x57, 0xe8, 0x35, 0xe6, 0xa3, 0x16, 0x17, 0x47, 0xfe,
- 0x22, 0x54, 0xfd, 0x40, 0x0f, 0xfa, 0xea, 0xd7, 0xdc, 0x3a, 0x6d, 0xc1, 0x1c, 0x3c, 0x1a, 0x47,
- 0xc4, 0x3b, 0x4a, 0xa1, 0xda, 0x1f, 0x16, 0xe0, 0x4a, 0x76, 0xc6, 0x35, 0xcb, 0x0f, 0xc8, 0x9f,
- 0x1b, 0x68, 0xf6, 0x63, 0x7e, 0x71, 0x96, 0x9b, 0x37, 0x7a, 0x18, 0x17, 0xae, 0x52, 0x62, 0x4d,
- 0x1e, 0x40, 0xc5, 0x0a, 0x68, 0x57, 0xad, 0x2f, 0xef, 0x9d, 0x72, 0xd5, 0x63, 0x53, 0x3b, 0x93,
- 0x82, 0x42, 0x98, 0xf6, 0x59, 0x71, 0x58, 0x95, 0xf9, 0xf4, 0x61, 0x27, 0x63, 0x7e, 0xef, 0xe4,
- 0x8b, 0xf9, 0x4d, 0x16, 0x68, 0x30, 0xf4, 0xf7, 0x2f, 0x0c, 0x86, 0xfe, 0xde, 0xcb, 0x1f, 0xfa,
- 0x9b, 0x6a, 0x86, 0xa1, 0x11, 0xc0, 0x9f, 0x97, 0xe0, 0xea, 0xe3, 0xba, 0x0d, 0x9b, 0xcf, 0x64,
- 0xef, 0xcc, 0x3b, 0x9f, 0x3d, 0xbe, 0x1f, 0x92, 0x79, 0xa8, 0xf4, 0x76, 0x75, 0x5f, 0x29, 0x65,
- 0x6a, 0xc1, 0x52, 0xd9, 0x60, 0x89, 0x8f, 0xd8, 0xa0, 0xc1, 0x95, 0x39, 0xfe, 0x8a, 0x82, 0x95,
- 0x0d, 0xc7, 0x5d, 0xea, 0xfb, 0x91, 0x4d, 0x20, 0x1c, 0x8e, 0xd7, 0x45, 0x32, 0x2a, 0x3a, 0x09,
- 0xa0, 0x2a, 0x4c, 0xcc, 0x72, 0x66, 0x1a, 0x3d, 0x90, 0x2b, 0x23, 0x4c, 0x3c, 0xaa, 0x94, 0xf4,
- 0x56, 0x48, 0x59, 0x64, 0x16, 0xca, 0x41, 0x14, 0xb4, 0xab, 0x96, 0xe6, 0xe5, 0x0c, 0xfd, 0x94,
- 0xf3, 0xb1, 0x85, 0xbd, 0xbb, 0xcd, 0x8d, 0xea, 0xa6, 0xf4, 0x9f, 0x5b, 0xae, 0xc3, 0x15, 0xb2,
- 0x52, 0xb4, 0xb0, 0xbf, 0x37, 0xc0, 0x81, 0x19, 0xb9, 0xb4, 0x7f, 0x57, 0x87, 0x4b, 0xd9, 0xfd,
- 0x81, 0xb5, 0xdb, 0x3e, 0xf5, 0x7c, 0x86, 0x5d, 0x48, 0xb6, 0xdb, 0x7d, 0x91, 0x8c, 0x8a, 0xfe,
- 0xa5, 0x0e, 0x38, 0xfb, 0xad, 0x02, 0x5c, 0xf6, 0xa4, 0x8f, 0xe8, 0x69, 0x04, 0x9d, 0xbd, 0x28,
- 0xcc, 0x19, 0x43, 0x04, 0xe2, 0xf0, 0xb2, 0x90, 0xbf, 0x57, 0x80, 0xe9, 0x6e, 0xca, 0xce, 0x71,
- 0x86, 0xfb, 0xd6, 0x78, 0x54, 0xfc, 0xfa, 0x10, 0x79, 0x38, 0xb4, 0x24, 0xe4, 0x3b, 0xd0, 0xec,
- 0xb1, 0x7e, 0xe1, 0x07, 0xd4, 0x31, 0xd4, 0xd6, 0xb5, 0xd1, 0xff, 0xa4, 0x8d, 0x08, 0x4b, 0x85,
- 0xa2, 0x09, 0xfd, 0x20, 0x46, 0xc0, 0xb8, 0xc4, 0x67, 0x7c, 0xa3, 0xda, 0x0d, 0xa8, 0xfb, 0x34,
- 0x08, 0x2c, 0xa7, 0x23, 0xd6, 0x1b, 0x0d, 0xf1, 0xaf, 0xb4, 0x65, 0x1a, 0x86, 0x54, 0xf2, 0x53,
- 0xd0, 0xe0, 0x2e, 0xa7, 0x05, 0xaf, 0xe3, 0x4f, 0x37, 0x78, 0xb8, 0xd8, 0xb8, 0x08, 0x80, 0x93,
- 0x89, 0x18, 0xd1, 0xc9, 0x6b, 0x30, 0xb6, 0xcd, 0x7f, 0x5f, 0xb9, 0x77, 0x59, 0xd8, 0xb8, 0xb8,
- 0xb6, 0xd6, 0x8a, 0xa5, 0x63, 0x82, 0x8b, 0xcc, 0x03, 0xd0, 0xd0, 0x2f, 0x97, 0xb6, 0x67, 0x45,
- 0x1e, 0x3b, 0x8c, 0x71, 0x91, 0x17, 0xa1, 0x14, 0xd8, 0x3e, 0xb7, 0x61, 0xd5, 0xa3, 0x25, 0xe8,
- 0xe6, 0x5a, 0x1b, 0x59, 0xba, 0xf6, 0xa3, 0x02, 0x4c, 0xa6, 0x36, 0x97, 0xb0, 0x2c, 0x7d, 0xcf,
- 0x96, 0xc3, 0x48, 0x98, 0x65, 0x0b, 0xd7, 0x90, 0xa5, 0x93, 0x0f, 0xa4, 0x5a, 0x5e, 0xcc, 0x79,
- 0x4c, 0xc3, 0x5d, 0x3d, 0xf0, 0x99, 0x1e, 0x3e, 0xa0, 0x91, 0x73, 0x37, 0x5f, 0x54, 0x1e, 0x39,
- 0x0f, 0xc4, 0xdc, 0x7c, 0x11, 0x0d, 0x13, 0x9c, 0x29, 0x83, 0x5f, 0xf9, 0x38, 0x06, 0x3f, 0xed,
- 0xd7, 0x8b, 0xb1, 0x16, 0x90, 0x9a, 0xfd, 0x13, 0x5a, 0xe0, 0x15, 0x36, 0x81, 0x86, 0x93, 0x7b,
- 0x23, 0x3e, 0xff, 0xf1, 0xc9, 0x58, 0x52, 0xc9, 0xbb, 0xa2, 0xed, 0x4b, 0x39, 0x37, 0xc3, 0x6e,
- 0xae, 0xb5, 0x45, 0x74, 0x95, 0xfa, 0x6a, 0xe1, 0x27, 0x28, 0x9f, 0xd1, 0x27, 0xd0, 0xfe, 0x55,
- 0x09, 0x9a, 0xb7, 0xdd, 0xed, 0x2f, 0x49, 0x04, 0x75, 0xf6, 0x34, 0x55, 0xfc, 0x02, 0xa7, 0xa9,
- 0x2d, 0x78, 0x3e, 0x08, 0xec, 0x36, 0x35, 0x5c, 0xc7, 0xf4, 0x17, 0x76, 0x02, 0xea, 0x2d, 0x5b,
- 0x8e, 0xe5, 0xef, 0x52, 0x53, 0xba, 0x93, 0x5e, 0x38, 0x3a, 0x9c, 0x79, 0x7e, 0x73, 0x73, 0x2d,
- 0x8b, 0x05, 0x87, 0xe5, 0xe5, 0xc3, 0x86, 0xd8, 0x09, 0xc8, 0x77, 0xca, 0xc8, 0x98, 0x1b, 0x31,
- 0x6c, 0xc4, 0xd2, 0x31, 0xc1, 0xa5, 0x7d, 0xb7, 0x08, 0x8d, 0x70, 0x03, 0x3e, 0x79, 0x19, 0x6a,
- 0xdb, 0x9e, 0xbb, 0x47, 0x3d, 0xe1, 0xb9, 0x93, 0x3b, 0x65, 0x5a, 0x22, 0x09, 0x15, 0x8d, 0xbc,
- 0x04, 0x95, 0xc0, 0xed, 0x59, 0x46, 0xda, 0xa0, 0xb6, 0xc9, 0x12, 0x51, 0xd0, 0xce, 0xae, 0x83,
- 0xbf, 0x92, 0x50, 0xed, 0x1a, 0x43, 0x95, 0xb1, 0xf7, 0xa1, 0xec, 0xeb, 0xbe, 0x2d, 0xe7, 0xd3,
- 0x1c, 0x7b, 0xd9, 0x17, 0xda, 0x6b, 0x72, 0x2f, 0xfb, 0x42, 0x7b, 0x0d, 0x39, 0xa8, 0xf6, 0x27,
- 0x45, 0x68, 0x8a, 0x76, 0x13, 0xa3, 0xc2, 0x69, 0xb6, 0xdc, 0xdb, 0x3c, 0x94, 0xc2, 0xef, 0x77,
- 0xa9, 0xc7, 0xcd, 0x4c, 0x72, 0x90, 0x8b, 0xfb, 0x07, 0x22, 0x62, 0x18, 0x4e, 0x11, 0x25, 0xa9,
- 0xa6, 0x2f, 0x9f, 0x61, 0xd3, 0x57, 0x8e, 0xd5, 0xf4, 0xd5, 0xb3, 0x68, 0xfa, 0x4f, 0x8b, 0xd0,
- 0x58, 0xb3, 0x76, 0xa8, 0x71, 0x60, 0xd8, 0x7c, 0x4f, 0xa0, 0x49, 0x6d, 0x1a, 0xd0, 0x15, 0x4f,
- 0x37, 0xe8, 0x06, 0xf5, 0x2c, 0x7e, 0x40, 0x0d, 0xfb, 0x3f, 0xf8, 0x08, 0x24, 0xf7, 0x04, 0x2e,
- 0x0d, 0xe1, 0xc1, 0xa1, 0xb9, 0xc9, 0x2a, 0x8c, 0x99, 0xd4, 0xb7, 0x3c, 0x6a, 0x6e, 0xc4, 0x16,
- 0x2a, 0x2f, 0xab, 0xa9, 0x66, 0x29, 0x46, 0x7b, 0x74, 0x38, 0x33, 0xae, 0x0c, 0x94, 0x62, 0xc5,
- 0x92, 0xc8, 0xca, 0x7e, 0xf9, 0x9e, 0xde, 0xf7, 0xb3, 0xca, 0x18, 0xfb, 0xe5, 0x37, 0xb2, 0x59,
- 0x70, 0x58, 0x5e, 0xad, 0x02, 0xa5, 0x35, 0xb7, 0xa3, 0x7d, 0x56, 0x82, 0xf0, 0x24, 0x23, 0xf2,
- 0x57, 0x0a, 0xd0, 0xd4, 0x1d, 0xc7, 0x0d, 0xe4, 0x29, 0x41, 0xc2, 0x03, 0x8f, 0xb9, 0x0f, 0x4c,
- 0x9a, 0x5d, 0x88, 0x40, 0x85, 0xf3, 0x36, 0x74, 0x28, 0xc7, 0x28, 0x18, 0x97, 0x4d, 0xfa, 0x29,
- 0x7f, 0xf2, 0x7a, 0xfe, 0x52, 0x1c, 0xc3, 0x7b, 0x7c, 0xe5, 0x1b, 0x30, 0x95, 0x2e, 0xec, 0x49,
- 0xdc, 0x41, 0xb9, 0x1c, 0xf3, 0x45, 0x80, 0x28, 0xa6, 0xe4, 0x29, 0x18, 0xb1, 0xac, 0x84, 0x11,
- 0x6b, 0x65, 0xf4, 0x06, 0x0e, 0x0b, 0x3d, 0xd4, 0x70, 0xf5, 0x51, 0xca, 0x70, 0xb5, 0x7a, 0x1a,
- 0xc2, 0x1e, 0x6f, 0xac, 0xfa, 0xfb, 0x05, 0x98, 0x8a, 0x98, 0xe5, 0x0e, 0xd9, 0x37, 0x61, 0xdc,
- 0xa3, 0xba, 0xd9, 0xd2, 0x03, 0x63, 0x97, 0x87, 0x7a, 0x17, 0x78, 0x6c, 0xf6, 0xb9, 0xa3, 0xc3,
- 0x99, 0x71, 0x8c, 0x13, 0x30, 0xc9, 0x47, 0x74, 0x68, 0xb2, 0x84, 0x4d, 0xab, 0x4b, 0xdd, 0x7e,
- 0x30, 0xa2, 0xd5, 0x94, 0x2f, 0x58, 0x30, 0x82, 0xc1, 0x38, 0xa6, 0xf6, 0x79, 0x01, 0x26, 0xe2,
- 0x05, 0x3e, 0x73, 0x8b, 0xda, 0x6e, 0xd2, 0xa2, 0xb6, 0x78, 0x0a, 0xdf, 0x64, 0x88, 0x15, 0xed,
- 0x13, 0x88, 0x57, 0x8d, 0x5b, 0xce, 0xe2, 0xc6, 0x82, 0xc2, 0x63, 0x8d, 0x05, 0x5f, 0xfe, 0xc3,
- 0x6b, 0x86, 0x69, 0xb9, 0xe5, 0x67, 0x58, 0xcb, 0xfd, 0x22, 0x4f, 0xc0, 0x89, 0x9d, 0xe2, 0x52,
- 0xcd, 0x71, 0x8a, 0x4b, 0x37, 0x3c, 0xc5, 0xa5, 0x76, 0x6a, 0x83, 0xce, 0x71, 0x4e, 0x72, 0xa9,
- 0x3f, 0xd5, 0x93, 0x5c, 0x1a, 0x67, 0x75, 0x92, 0x0b, 0xe4, 0x3d, 0xc9, 0xe5, 0x93, 0x02, 0x4c,
- 0x98, 0x89, 0x1d, 0xb3, 0xdc, 0xb6, 0x90, 0x67, 0xaa, 0x49, 0x6e, 0xc0, 0x15, 0x5b, 0xa6, 0x92,
- 0x69, 0x98, 0x12, 0x49, 0x3e, 0x2d, 0xc0, 0x44, 0xbf, 0x67, 0xea, 0x41, 0x68, 0x38, 0xe2, 0x46,
- 0x8b, 0x3c, 0xa5, 0xd8, 0x4a, 0xc0, 0x45, 0x8d, 0x9b, 0x4c, 0xc7, 0x94, 0x58, 0xed, 0x8f, 0x6b,
- 0xf1, 0x19, 0xe9, 0x69, 0x1b, 0xcd, 0xdf, 0x48, 0x1a, 0xcd, 0xaf, 0xa7, 0x8d, 0xe6, 0x93, 0xb1,
- 0x78, 0xd6, 0xb8, 0xe1, 0xfc, 0xa7, 0x63, 0x03, 0x75, 0x89, 0x9f, 0xe1, 0x12, 0x7e, 0xf3, 0x8c,
- 0xc1, 0x7a, 0x01, 0x26, 0xa5, 0xf6, 0xaa, 0x88, 0x7c, 0x94, 0x1b, 0x8f, 0xc2, 0x9c, 0x96, 0x92,
- 0x64, 0x4c, 0xf3, 0x33, 0x81, 0xbe, 0x3a, 0x40, 0x53, 0x2c, 0x15, 0xa2, 0x4e, 0xa6, 0x0e, 0xb7,
- 0x0c, 0x39, 0xd8, 0xb2, 0xc2, 0xa3, 0xba, 0x2f, 0x4d, 0xdf, 0xb1, 0x65, 0x05, 0xf2, 0x54, 0x94,
- 0xd4, 0xb8, 0xfd, 0xbf, 0xf6, 0x04, 0xfb, 0xbf, 0x0e, 0x4d, 0x5b, 0xf7, 0x03, 0xf1, 0x35, 0x4d,
- 0xf9, 0x3b, 0xff, 0x99, 0xe3, 0x4d, 0xbc, 0x6c, 0x32, 0x8f, 0xb4, 0xdb, 0xb5, 0x08, 0x06, 0xe3,
- 0x98, 0xc4, 0x84, 0x31, 0xf6, 0xca, 0x7f, 0x6d, 0x73, 0x21, 0x90, 0xc7, 0x4c, 0x9d, 0x44, 0x46,
- 0x68, 0xb6, 0x5a, 0x8b, 0xe1, 0x60, 0x02, 0x75, 0x88, 0x8b, 0x00, 0x46, 0x71, 0x11, 0x90, 0x9f,
- 0x17, 0x9a, 0xd3, 0x41, 0xf8, 0x59, 0x9b, 0xfc, 0xb3, 0x86, 0x21, 0x92, 0x18, 0x27, 0x62, 0x92,
- 0x97, 0xf5, 0x8a, 0xbe, 0x6c, 0x06, 0x95, 0x7d, 0x2c, 0xd9, 0x2b, 0xb6, 0x92, 0x64, 0x4c, 0xf3,
- 0x93, 0x0d, 0xb8, 0x10, 0x26, 0xc5, 0x8b, 0x31, 0xce, 0x71, 0xc2, 0x98, 0xb5, 0xad, 0x0c, 0x1e,
- 0xcc, 0xcc, 0xc9, 0x37, 0x81, 0xf4, 0x3d, 0x8f, 0x3a, 0xc1, 0x2d, 0xdd, 0xdf, 0x95, 0xc1, 0x6f,
- 0xd1, 0x26, 0x90, 0x88, 0x84, 0x71, 0x3e, 0x32, 0x0f, 0x20, 0xe0, 0x78, 0xae, 0xc9, 0x64, 0x7c,
- 0xe9, 0x56, 0x48, 0xc1, 0x18, 0x97, 0xf6, 0x49, 0x03, 0x9a, 0x77, 0xf5, 0xc0, 0xda, 0xa7, 0xdc,
- 0x9f, 0x77, 0x36, 0x4e, 0x95, 0xbf, 0x55, 0x80, 0x4b, 0xc9, 0xa0, 0xcd, 0x33, 0xf4, 0xac, 0xf0,
- 0x23, 0x60, 0x30, 0x53, 0x1a, 0x0e, 0x29, 0x05, 0xf7, 0xb1, 0x0c, 0xc4, 0x80, 0x9e, 0xb5, 0x8f,
- 0xa5, 0x3d, 0x4c, 0x20, 0x0e, 0x2f, 0xcb, 0x97, 0xc5, 0xc7, 0xf2, 0x6c, 0x9f, 0x14, 0x98, 0xf2,
- 0x00, 0xd5, 0x9e, 0x19, 0x0f, 0x50, 0xfd, 0x99, 0x50, 0xbb, 0x7b, 0x31, 0x0f, 0x50, 0x23, 0x67,
- 0x24, 0x92, 0xdc, 0xe7, 0x20, 0xd0, 0x86, 0x79, 0x92, 0xf8, 0x11, 0x05, 0xca, 0x32, 0xcf, 0xb4,
- 0xd5, 0x6d, 0xdd, 0xb7, 0x0c, 0xa9, 0x76, 0xe4, 0x38, 0x19, 0x55, 0x9d, 0xdd, 0x26, 0x02, 0x16,
- 0xf8, 0x2b, 0x0a, 0xec, 0xe8, 0xa8, 0xba, 0x62, 0xae, 0xa3, 0xea, 0xc8, 0x22, 0x94, 0x9d, 0x3d,
- 0x7a, 0x70, 0xb2, 0xcd, 0xfe, 0x7c, 0x15, 0x76, 0xf7, 0x0e, 0x3d, 0x40, 0x9e, 0x59, 0xfb, 0x6e,
- 0x11, 0x80, 0x55, 0xff, 0x78, 0xbe, 0x98, 0x9f, 0x84, 0x9a, 0xdf, 0xe7, 0x56, 0x13, 0xa9, 0x30,
- 0x45, 0xe1, 0x5b, 0x22, 0x19, 0x15, 0x9d, 0xbc, 0x04, 0x95, 0x8f, 0xfa, 0xb4, 0xaf, 0x02, 0x0b,
- 0x42, 0xc5, 0xfd, 0x9b, 0x2c, 0x11, 0x05, 0xed, 0xec, 0xec, 0xaa, 0xca, 0x67, 0x53, 0x39, 0x2b,
- 0x9f, 0x4d, 0x03, 0x6a, 0x77, 0x5d, 0x1e, 0x0d, 0xaa, 0xfd, 0xf7, 0x22, 0x40, 0x14, 0x6d, 0x47,
- 0x7e, 0xb3, 0x00, 0x17, 0xc3, 0x1f, 0x2e, 0x10, 0xeb, 0x2f, 0x7e, 0x18, 0x71, 0x6e, 0xff, 0x4d,
- 0xd6, 0xcf, 0xce, 0x47, 0xa0, 0x8d, 0x2c, 0x71, 0x98, 0x5d, 0x0a, 0x82, 0x50, 0xa7, 0xdd, 0x5e,
- 0x70, 0xb0, 0x64, 0x79, 0xb2, 0x07, 0x66, 0x06, 0x75, 0xde, 0x94, 0x3c, 0x22, 0xab, 0x34, 0x12,
- 0xf0, 0x9f, 0x48, 0x51, 0x30, 0xc4, 0x21, 0xbb, 0x50, 0x77, 0xdc, 0x0f, 0x7c, 0xd6, 0x1c, 0xb2,
- 0x3b, 0xbe, 0x33, 0x7a, 0x93, 0x8b, 0x66, 0x15, 0xf6, 0x7e, 0xf9, 0x82, 0x35, 0x47, 0x36, 0xf6,
- 0x6f, 0x14, 0xe1, 0x7c, 0x46, 0x3b, 0x90, 0x77, 0x60, 0x4a, 0x06, 0x36, 0x46, 0xa7, 0x72, 0x17,
- 0xa2, 0x53, 0xb9, 0xdb, 0x29, 0x1a, 0x0e, 0x70, 0x93, 0x0f, 0x00, 0x74, 0xc3, 0xa0, 0xbe, 0xbf,
- 0xee, 0x9a, 0x6a, 0x3d, 0xf0, 0x36, 0x53, 0x5f, 0x16, 0xc2, 0xd4, 0x47, 0x87, 0x33, 0x3f, 0x93,
- 0x15, 0xab, 0x9c, 0x6a, 0xe7, 0x28, 0x03, 0xc6, 0x20, 0xc9, 0xb7, 0x01, 0xc4, 0x22, 0x3c, 0x3c,
- 0x4e, 0xe1, 0x09, 0x96, 0xab, 0x59, 0x75, 0x70, 0xd5, 0xec, 0x37, 0xfb, 0xba, 0x13, 0x58, 0xc1,
- 0x81, 0x38, 0xbd, 0xe6, 0x7e, 0x88, 0x82, 0x31, 0x44, 0xed, 0x9f, 0x17, 0xa1, 0xae, 0x6c, 0xe6,
- 0x4f, 0xc1, 0x50, 0xda, 0x49, 0x18, 0x4a, 0x4f, 0x29, 0x3a, 0x39, 0xcb, 0x4c, 0xea, 0xa6, 0xcc,
- 0xa4, 0x2b, 0xf9, 0x45, 0x3d, 0xde, 0x48, 0xfa, 0x3b, 0x45, 0x98, 0x50, 0xac, 0x79, 0x4d, 0xa4,
- 0x5f, 0x87, 0x49, 0x11, 0x55, 0xb0, 0xae, 0x3f, 0x14, 0x07, 0xf9, 0xf0, 0x06, 0x2b, 0x8b, 0x80,
- 0xe0, 0x56, 0x92, 0x84, 0x69, 0x5e, 0xd6, 0xad, 0x45, 0xd2, 0x16, 0x5b, 0x84, 0x09, 0x3f, 0xa4,
- 0x58, 0x6f, 0xf2, 0x6e, 0xdd, 0x4a, 0xd1, 0x70, 0x80, 0x3b, 0x6d, 0xa3, 0x2d, 0x9f, 0x81, 0x8d,
- 0xf6, 0x3f, 0x14, 0x60, 0x2c, 0x6a, 0xaf, 0x33, 0xb7, 0xd0, 0xee, 0x24, 0x2d, 0xb4, 0x0b, 0xb9,
- 0xbb, 0xc3, 0x10, 0xfb, 0xec, 0x5f, 0xab, 0x41, 0x22, 0x48, 0x9e, 0x6c, 0xc3, 0x15, 0x2b, 0x33,
- 0xd4, 0x2f, 0x36, 0xda, 0x84, 0xbb, 0xbe, 0x57, 0x87, 0x72, 0xe2, 0x63, 0x50, 0x48, 0x1f, 0xea,
- 0xfb, 0xd4, 0x0b, 0x2c, 0x83, 0xaa, 0xfa, 0xad, 0xe4, 0x56, 0xc9, 0xa4, 0x15, 0x3a, 0x6c, 0xd3,
- 0xfb, 0x52, 0x00, 0x86, 0xa2, 0xc8, 0x36, 0x54, 0xa8, 0xd9, 0xa1, 0xea, 0x68, 0xa5, 0x9c, 0x07,
- 0x97, 0x86, 0xed, 0xc9, 0xde, 0x7c, 0x14, 0xd0, 0xc4, 0x87, 0x86, 0xad, 0xbc, 0x8c, 0xb2, 0x1f,
- 0x8e, 0xae, 0x60, 0x85, 0xfe, 0xca, 0xe8, 0xd4, 0x85, 0x30, 0x09, 0x23, 0x39, 0x64, 0x2f, 0x34,
- 0x77, 0x56, 0x4e, 0x69, 0xf0, 0x78, 0x8c, 0xb1, 0xd3, 0x87, 0xc6, 0x03, 0x3d, 0xa0, 0x5e, 0x57,
- 0xf7, 0xf6, 0xe4, 0x6a, 0x63, 0xf4, 0x1a, 0xbe, 0xab, 0x90, 0xa2, 0x1a, 0x86, 0x49, 0x18, 0xc9,
- 0x21, 0x2e, 0x34, 0x02, 0xa9, 0x3e, 0x2b, 0x9b, 0xee, 0xe8, 0x42, 0x95, 0x22, 0xee, 0xcb, 0x60,
- 0x79, 0xf5, 0x8a, 0x91, 0x0c, 0xb2, 0x9f, 0x38, 0x5b, 0x5a, 0x9c, 0x28, 0xde, 0xca, 0xe1, 0x1b,
- 0x90, 0x50, 0xd1, 0x74, 0x93, 0x7d, 0x46, 0xb5, 0xf6, 0xbf, 0x2a, 0xd1, 0xb0, 0xfc, 0xb4, 0xed,
- 0x84, 0xaf, 0x25, 0xed, 0x84, 0xd7, 0xd2, 0x76, 0xc2, 0x94, 0xb3, 0xfa, 0xe4, 0xe1, 0xb5, 0x29,
- 0xf3, 0x5a, 0xf9, 0x0c, 0xcc, 0x6b, 0xaf, 0x42, 0x73, 0x9f, 0x8f, 0x04, 0xe2, 0x9c, 0xa6, 0x0a,
- 0x9f, 0x46, 0xf8, 0xc8, 0x7e, 0x3f, 0x4a, 0xc6, 0x38, 0x0f, 0xcb, 0x22, 0x6f, 0xd3, 0x08, 0x0f,
- 0xba, 0x95, 0x59, 0xda, 0x51, 0x32, 0xc6, 0x79, 0x78, 0x64, 0x9e, 0xe5, 0xec, 0x89, 0x0c, 0x35,
- 0x9e, 0x41, 0x44, 0xe6, 0xa9, 0x44, 0x8c, 0xe8, 0xe4, 0x06, 0xd4, 0xfb, 0xe6, 0x8e, 0xe0, 0xad,
- 0x73, 0x5e, 0xae, 0x61, 0x6e, 0x2d, 0x2d, 0xcb, 0x73, 0xa3, 0x14, 0x95, 0x95, 0xa4, 0xab, 0xf7,
- 0x14, 0x81, 0xaf, 0x0d, 0x65, 0x49, 0xd6, 0xa3, 0x64, 0x8c, 0xf3, 0x90, 0x9f, 0x83, 0x09, 0x8f,
- 0x9a, 0x7d, 0x83, 0x86, 0xb9, 0x80, 0xe7, 0x22, 0xe2, 0xda, 0x90, 0x38, 0x05, 0x53, 0x9c, 0x43,
- 0x8c, 0x84, 0xcd, 0x91, 0x8c, 0x84, 0xdf, 0x80, 0x09, 0xd3, 0xd3, 0x2d, 0x87, 0x9a, 0xf7, 0x1c,
- 0x1e, 0x91, 0x20, 0xe3, 0x03, 0x43, 0x0b, 0xf9, 0x52, 0x82, 0x8a, 0x29, 0x6e, 0xed, 0x0f, 0x0a,
- 0x40, 0x06, 0x23, 0xe1, 0xc9, 0x2e, 0x54, 0x1d, 0x6e, 0x3d, 0xcb, 0x7d, 0xb4, 0x76, 0xcc, 0x08,
- 0x27, 0x86, 0x35, 0x99, 0x20, 0xf1, 0x89, 0x03, 0x75, 0xfa, 0x30, 0xa0, 0x9e, 0x13, 0xee, 0x8c,
- 0x39, 0x9d, 0x63, 0xbc, 0xc5, 0x6a, 0x42, 0x22, 0x63, 0x28, 0x43, 0xfb, 0xa3, 0x22, 0x34, 0x63,
- 0x7c, 0x4f, 0x5a, 0x94, 0xf2, 0xcd, 0xf9, 0xc2, 0x68, 0xb5, 0xe5, 0xd9, 0xf2, 0x0f, 0x8d, 0x6d,
- 0xce, 0x97, 0x24, 0x5c, 0xc3, 0x38, 0x1f, 0x99, 0x07, 0xe8, 0xea, 0x7e, 0x40, 0x3d, 0x3e, 0x7b,
- 0xa7, 0xb6, 0xc4, 0xaf, 0x87, 0x14, 0x8c, 0x71, 0x91, 0xeb, 0xf2, 0x20, 0xf6, 0x72, 0xf2, 0x08,
- 0xc3, 0x21, 0xa7, 0xac, 0x57, 0x4e, 0xe1, 0x94, 0x75, 0xd2, 0x81, 0x29, 0x55, 0x6a, 0x45, 0x3d,
- 0xd9, 0x01, 0x77, 0x62, 0xfd, 0x93, 0x82, 0xc0, 0x01, 0x50, 0xed, 0xbb, 0x05, 0x18, 0x4f, 0x98,
- 0x4c, 0xc4, 0xe1, 0x83, 0x6a, 0x1f, 0x47, 0xe2, 0xf0, 0xc1, 0xd8, 0xf6, 0x8b, 0x57, 0xa0, 0x2a,
- 0x1a, 0x28, 0x1d, 0x9e, 0x29, 0x9a, 0x10, 0x25, 0x95, 0x8d, 0x85, 0xd2, 0x28, 0x9b, 0x1e, 0x0b,
- 0xa5, 0xd5, 0x16, 0x15, 0x5d, 0xf8, 0x3a, 0x44, 0xe9, 0x64, 0x4b, 0xc7, 0x7c, 0x1d, 0x22, 0x1d,
- 0x43, 0x0e, 0xed, 0xf7, 0x78, 0xb9, 0x03, 0xef, 0x20, 0x5c, 0x0b, 0x76, 0xa0, 0x26, 0x43, 0xf2,
- 0xe4, 0xaf, 0xf1, 0x4e, 0x0e, 0x3b, 0x0e, 0xc7, 0x91, 0xc1, 0x67, 0xba, 0xb1, 0x77, 0x6f, 0x67,
- 0x07, 0x15, 0x3a, 0xb9, 0x09, 0x0d, 0xd7, 0x59, 0xd6, 0x2d, 0xbb, 0xef, 0xa9, 0x99, 0xe1, 0x27,
- 0xd8, 0x58, 0x77, 0x4f, 0x25, 0x3e, 0x3a, 0x9c, 0xb9, 0x14, 0xbe, 0x24, 0x0a, 0x89, 0x51, 0x4e,
- 0xed, 0x2f, 0x17, 0xe0, 0x22, 0xba, 0xb6, 0x6d, 0x39, 0x9d, 0xa4, 0xb3, 0x8c, 0xd8, 0x30, 0xd1,
- 0xd5, 0x1f, 0x6e, 0x39, 0xfa, 0xbe, 0x6e, 0xd9, 0xfa, 0xb6, 0x4d, 0x9f, 0xb8, 0x96, 0xeb, 0x07,
- 0x96, 0x3d, 0x2b, 0xae, 0x83, 0x9b, 0x5d, 0x75, 0x82, 0x7b, 0x5e, 0x3b, 0xf0, 0x2c, 0xa7, 0x23,
- 0x06, 0xbd, 0xf5, 0x04, 0x16, 0xa6, 0xb0, 0xb5, 0x3f, 0x2e, 0x01, 0x0f, 0x0b, 0x23, 0x6f, 0x42,
- 0xa3, 0x4b, 0x8d, 0x5d, 0xdd, 0xb1, 0x7c, 0x75, 0x8c, 0xeb, 0x65, 0x56, 0xaf, 0x75, 0x95, 0xf8,
- 0x88, 0x7d, 0x8a, 0x85, 0xf6, 0x1a, 0xdf, 0x79, 0x11, 0xf1, 0x12, 0x03, 0xaa, 0x1d, 0xdf, 0xd7,
- 0x7b, 0x56, 0xee, 0xa8, 0x04, 0x71, 0x6c, 0xa6, 0x18, 0x8e, 0xc4, 0x33, 0x4a, 0x68, 0x62, 0x40,
- 0xa5, 0x67, 0xeb, 0x96, 0x93, 0xfb, 0xfa, 0x22, 0x56, 0x83, 0x0d, 0x86, 0x24, 0x8c, 0x6b, 0xfc,
- 0x11, 0x05, 0x36, 0xe9, 0x43, 0xd3, 0x37, 0x3c, 0xbd, 0xeb, 0xef, 0xea, 0xf3, 0xaf, 0xbf, 0x91,
- 0x5b, 0x5d, 0x8d, 0x44, 0x89, 0xd9, 0x73, 0x11, 0x17, 0xd6, 0xdb, 0xb7, 0x16, 0xe6, 0x5f, 0x7f,
- 0x03, 0xe3, 0x72, 0xe2, 0x62, 0x5f, 0x7f, 0x75, 0x5e, 0x8e, 0x20, 0xa7, 0x2e, 0xf6, 0xf5, 0x57,
- 0xe7, 0x31, 0x2e, 0x47, 0xfb, 0xdf, 0x05, 0x68, 0x84, 0xbc, 0x64, 0x0b, 0x80, 0x8d, 0x65, 0xf2,
- 0xa0, 0xcb, 0x13, 0x5d, 0x3a, 0xc1, 0xed, 0x13, 0x5b, 0x61, 0x66, 0x8c, 0x01, 0x65, 0x9c, 0x04,
- 0x5a, 0x3c, 0xed, 0x93, 0x40, 0xe7, 0xa0, 0xb1, 0xab, 0x3b, 0xa6, 0xbf, 0xab, 0xef, 0x89, 0x21,
- 0x3d, 0x76, 0x36, 0xee, 0x2d, 0x45, 0xc0, 0x88, 0x47, 0xfb, 0xa7, 0x55, 0x10, 0xa1, 0x04, 0x6c,
- 0xd0, 0x31, 0x2d, 0x5f, 0xc4, 0xb2, 0x17, 0x78, 0xce, 0x70, 0xd0, 0x59, 0x92, 0xe9, 0x18, 0x72,
- 0x90, 0xcb, 0x50, 0xea, 0x5a, 0x8e, 0xf4, 0x3d, 0x71, 0xd3, 0xe3, 0xba, 0xe5, 0x20, 0x4b, 0xe3,
- 0x24, 0xfd, 0xa1, 0x0c, 0x43, 0x14, 0x24, 0xfd, 0x21, 0xb2, 0x34, 0xf2, 0x75, 0x98, 0xb4, 0x5d,
- 0x77, 0x8f, 0x0d, 0x1f, 0x2a, 0x5a, 0x51, 0xf8, 0x81, 0xb9, 0x31, 0x60, 0x2d, 0x49, 0xc2, 0x34,
- 0x2f, 0xd9, 0x82, 0xe7, 0x3f, 0xa6, 0x9e, 0x2b, 0xc7, 0xcb, 0xb6, 0x4d, 0x69, 0x4f, 0xc1, 0x08,
- 0x65, 0x8e, 0x07, 0x3d, 0xfe, 0x62, 0x36, 0x0b, 0x0e, 0xcb, 0xcb, 0xc3, 0xa7, 0x75, 0xaf, 0x43,
- 0x83, 0x0d, 0xcf, 0x35, 0xa8, 0xef, 0x5b, 0x4e, 0x47, 0xc1, 0x56, 0x23, 0xd8, 0xcd, 0x6c, 0x16,
- 0x1c, 0x96, 0x97, 0xbc, 0x07, 0xd3, 0x82, 0x24, 0xd4, 0x96, 0x05, 0x31, 0xcc, 0x58, 0xb6, 0xba,
- 0xf5, 0x6f, 0x5c, 0x78, 0x78, 0x36, 0x87, 0xf0, 0xe0, 0xd0, 0xdc, 0xe4, 0x36, 0x4c, 0x29, 0xff,
- 0xde, 0x06, 0xf5, 0xda, 0x61, 0x78, 0xc9, 0x78, 0xeb, 0x1a, 0x5b, 0x79, 0x2f, 0xd1, 0x9e, 0x47,
- 0x8d, 0xb8, 0x9f, 0x54, 0x71, 0xe1, 0x40, 0x3e, 0x82, 0x70, 0x89, 0xc7, 0x90, 0x6c, 0xf5, 0x16,
- 0x5d, 0xd7, 0x36, 0xdd, 0x07, 0x8e, 0xaa, 0xbb, 0x50, 0x31, 0xb9, 0x4b, 0xaf, 0x9d, 0xc9, 0x81,
- 0x43, 0x72, 0xb2, 0x9a, 0x73, 0xca, 0x92, 0xfb, 0xc0, 0x49, 0xa3, 0x42, 0x54, 0xf3, 0xf6, 0x10,
- 0x1e, 0x1c, 0x9a, 0x9b, 0x2c, 0x03, 0x49, 0xd7, 0x60, 0xab, 0x27, 0x9d, 0xce, 0x97, 0xc4, 0x99,
- 0x35, 0x69, 0x2a, 0x66, 0xe4, 0x20, 0x6b, 0x70, 0x21, 0x9d, 0xca, 0xc4, 0x49, 0xff, 0x33, 0x3f,
- 0xad, 0x16, 0x33, 0xe8, 0x98, 0x99, 0x4b, 0xfb, 0x67, 0x45, 0x18, 0x4f, 0x1c, 0x72, 0xf0, 0xcc,
- 0x6d, 0x26, 0x67, 0x6b, 0x81, 0xae, 0xdf, 0x59, 0x5d, 0xba, 0x45, 0x75, 0x93, 0x7a, 0x77, 0xa8,
- 0x3a, 0x90, 0x42, 0x4c, 0x8b, 0x09, 0x0a, 0xa6, 0x38, 0xc9, 0x0e, 0x54, 0x84, 0x65, 0x3b, 0xef,
- 0xf5, 0x25, 0xaa, 0x8d, 0xb8, 0x79, 0x5b, 0xde, 0xf9, 0xe3, 0x7a, 0x14, 0x05, 0xbc, 0x16, 0xc0,
- 0x58, 0x9c, 0x83, 0x0d, 0x24, 0x91, 0xda, 0x5b, 0x4b, 0xa8, 0xbc, 0xab, 0x50, 0x0a, 0x82, 0x51,
- 0xb7, 0xa9, 0x0b, 0x4f, 0xc9, 0xe6, 0x1a, 0x32, 0x0c, 0x6d, 0x87, 0x7d, 0x3b, 0xdf, 0xb7, 0x5c,
- 0x47, 0x9e, 0x59, 0xbe, 0x05, 0xb5, 0x40, 0x1a, 0x0b, 0x47, 0xdb, 0x66, 0xcf, 0x75, 0x25, 0x65,
- 0x28, 0x54, 0x58, 0xda, 0x7f, 0x2c, 0x42, 0x23, 0x5c, 0xd8, 0x1f, 0xe3, 0x2c, 0x70, 0x17, 0x1a,
- 0x61, 0x0c, 0x5c, 0xee, 0x1b, 0x11, 0xa3, 0xd0, 0x2c, 0xbe, 0x16, 0x0d, 0x5f, 0x31, 0x92, 0x11,
- 0x8f, 0xaf, 0x2b, 0xe5, 0x88, 0xaf, 0xeb, 0x41, 0x2d, 0xf0, 0xac, 0x4e, 0x47, 0xae, 0x12, 0xf2,
- 0x04, 0xd8, 0x85, 0xcd, 0xb5, 0x29, 0x00, 0x65, 0xcb, 0x8a, 0x17, 0x54, 0x62, 0xb4, 0x0f, 0x61,
- 0x2a, 0xcd, 0xc9, 0x55, 0x68, 0x63, 0x97, 0x9a, 0x7d, 0x5b, 0xb5, 0x71, 0xa4, 0x42, 0xcb, 0x74,
- 0x0c, 0x39, 0xd8, 0x32, 0x9c, 0x7d, 0xa6, 0x8f, 0x5d, 0x47, 0xa9, 0xb1, 0x7c, 0x35, 0xb2, 0x29,
- 0xd3, 0x30, 0xa4, 0x6a, 0xff, 0xad, 0x04, 0x97, 0x23, 0xf3, 0xcc, 0xba, 0xee, 0xe8, 0x9d, 0x63,
- 0x5c, 0x83, 0xf7, 0xd5, 0xc6, 0xa5, 0x93, 0x5e, 0xe8, 0x50, 0x7a, 0x06, 0x2e, 0x74, 0xf8, 0xbf,
- 0x45, 0xe0, 0xf1, 0xba, 0xe4, 0x3b, 0x30, 0xa6, 0xc7, 0x6e, 0x40, 0x95, 0x9f, 0xf3, 0x66, 0xee,
- 0xcf, 0xc9, 0xc3, 0x82, 0xc3, 0x90, 0xad, 0x78, 0x2a, 0x26, 0x04, 0x12, 0x17, 0xea, 0x3b, 0xba,
- 0x6d, 0x33, 0x5d, 0x28, 0xb7, 0xbb, 0x29, 0x21, 0x9c, 0x77, 0xf3, 0x65, 0x09, 0x8d, 0xa1, 0x10,
- 0xf2, 0x49, 0x01, 0xc6, 0xbd, 0xf8, 0x72, 0x4d, 0x7e, 0x90, 0x3c, 0xc1, 0x08, 0x31, 0xb4, 0x78,
- 0x80, 0x58, 0x7c, 0x4d, 0x98, 0x94, 0xa9, 0xfd, 0xd7, 0x02, 0x8c, 0xb7, 0x6d, 0xcb, 0xb4, 0x9c,
- 0xce, 0x19, 0xde, 0x27, 0x71, 0x0f, 0x2a, 0xbe, 0x6d, 0x99, 0x74, 0xc4, 0xd9, 0x44, 0xcc, 0x63,
- 0x0c, 0x00, 0x05, 0x4e, 0xf2, 0x82, 0x8a, 0xd2, 0x31, 0x2e, 0xa8, 0xf8, 0x61, 0x15, 0x64, 0xe4,
- 0x39, 0xe9, 0x43, 0xa3, 0xa3, 0xce, 0xbd, 0x97, 0x75, 0xbc, 0x95, 0xe3, 0xcc, 0xc4, 0xc4, 0x09,
- 0xfa, 0x62, 0xec, 0x0f, 0x13, 0x31, 0x92, 0x44, 0x68, 0xf2, 0xea, 0xdd, 0xa5, 0x9c, 0x57, 0xef,
- 0x0a, 0x71, 0x83, 0x97, 0xef, 0xea, 0x50, 0xde, 0x0d, 0x82, 0x9e, 0xec, 0x4c, 0xa3, 0x6f, 0x2d,
- 0x88, 0x8e, 0xed, 0x11, 0x3a, 0x11, 0x7b, 0x47, 0x0e, 0xcd, 0x44, 0x38, 0x7a, 0x78, 0xd5, 0xda,
- 0x62, 0xae, 0xc0, 0x87, 0xb8, 0x08, 0xf6, 0x8e, 0x1c, 0x9a, 0xfc, 0x32, 0x34, 0x03, 0x4f, 0x77,
- 0xfc, 0x1d, 0xd7, 0xeb, 0x52, 0x4f, 0xae, 0x51, 0x97, 0x73, 0xdc, 0x3e, 0xbb, 0x19, 0xa1, 0x09,
- 0x8f, 0x6a, 0x22, 0x09, 0xe3, 0xd2, 0xc8, 0x1e, 0xd4, 0xfb, 0xa6, 0x28, 0x98, 0x34, 0x83, 0x2d,
- 0xe4, 0xb9, 0x50, 0x38, 0x16, 0xd6, 0xa0, 0xde, 0x30, 0x14, 0x90, 0xbc, 0x55, 0xb0, 0x76, 0x5a,
- 0xb7, 0x0a, 0xc6, 0x7b, 0x63, 0xd6, 0x99, 0x22, 0xa4, 0x2b, 0xf5, 0x5a, 0xa7, 0x23, 0xa3, 0xb2,
- 0x96, 0x73, 0xab, 0x9c, 0x42, 0x64, 0x33, 0xd4, 0x8d, 0x9d, 0x0e, 0x2a, 0x19, 0x5a, 0x17, 0xa4,
- 0xb7, 0x83, 0x18, 0x89, 0xbb, 0x77, 0xc4, 0x46, 0xb7, 0xb9, 0xe3, 0x8d, 0x07, 0xe1, 0x25, 0x30,
- 0xb1, 0xb3, 0xbf, 0x33, 0x2f, 0xd9, 0xd1, 0xfe, 0x53, 0x11, 0x4a, 0x9b, 0x6b, 0x6d, 0x71, 0x9e,
- 0x27, 0xbf, 0xd8, 0x8a, 0xb6, 0xf7, 0xac, 0xde, 0x7d, 0xea, 0x59, 0x3b, 0x07, 0x72, 0xe9, 0x1d,
- 0x3b, 0xcf, 0x33, 0xcd, 0x81, 0x19, 0xb9, 0xc8, 0xfb, 0x30, 0x66, 0xe8, 0x8b, 0xd4, 0x0b, 0x46,
- 0x31, 0x2c, 0xf0, 0x1d, 0xbd, 0x8b, 0x0b, 0x51, 0x76, 0x4c, 0x80, 0x91, 0x2d, 0x00, 0x23, 0x82,
- 0x2e, 0x9d, 0xd8, 0x1c, 0x12, 0x03, 0x8e, 0x01, 0x11, 0x84, 0xc6, 0x1e, 0x63, 0xe5, 0xa8, 0xe5,
- 0x93, 0xa0, 0xf2, 0x9e, 0x73, 0x47, 0xe5, 0xc5, 0x08, 0x46, 0x73, 0x60, 0x3c, 0x71, 0x21, 0x0f,
- 0xf9, 0x1a, 0xd4, 0xdd, 0x5e, 0x6c, 0x38, 0x6d, 0xf0, 0xf8, 0xcf, 0xfa, 0x3d, 0x99, 0xf6, 0xe8,
- 0x70, 0x66, 0x7c, 0xcd, 0xed, 0x58, 0x86, 0x4a, 0xc0, 0x90, 0x9d, 0x68, 0x50, 0xe5, 0xdb, 0xf0,
- 0xd4, 0x75, 0x3c, 0x7c, 0xee, 0xe0, 0x37, 0x66, 0xf8, 0x28, 0x29, 0xda, 0xaf, 0x94, 0x21, 0xf2,
- 0x11, 0x12, 0x1f, 0xaa, 0x62, 0x9b, 0x81, 0x1c, 0xb9, 0xcf, 0x74, 0x47, 0x83, 0x14, 0x45, 0x3a,
- 0x50, 0xfa, 0xd0, 0xdd, 0xce, 0x3d, 0x70, 0xc7, 0xf6, 0xdf, 0x0b, 0x5b, 0x59, 0x2c, 0x01, 0x99,
- 0x04, 0xf2, 0xb7, 0x0b, 0x70, 0xce, 0x4f, 0xab, 0xbe, 0xb2, 0x3b, 0x60, 0x7e, 0x1d, 0x3f, 0xad,
- 0x4c, 0xcb, 0x40, 0xdd, 0x61, 0x64, 0x1c, 0x2c, 0x0b, 0x6b, 0x7f, 0xe1, 0xbc, 0x93, 0xdd, 0x69,
- 0x25, 0xe7, 0x25, 0x92, 0xc9, 0xf6, 0x4f, 0xa6, 0xa1, 0x14, 0xa5, 0xfd, 0x5a, 0x11, 0x9a, 0xb1,
- 0xd1, 0x3a, 0xf7, 0x2d, 0x4f, 0x0f, 0x53, 0xb7, 0x3c, 0x6d, 0x8c, 0xee, 0xcb, 0x8e, 0x4a, 0x75,
- 0xd6, 0x17, 0x3d, 0xfd, 0xcb, 0x22, 0x94, 0xb6, 0x96, 0x96, 0x93, 0x8b, 0xd6, 0xc2, 0x53, 0x58,
- 0xb4, 0xee, 0x42, 0x6d, 0xbb, 0x6f, 0xd9, 0x81, 0xe5, 0xe4, 0x3e, 0x21, 0x44, 0x5d, 0x8a, 0x25,
- 0x7d, 0x1d, 0x02, 0x15, 0x15, 0x3c, 0xe9, 0x40, 0xad, 0x23, 0x8e, 0x68, 0xcc, 0x1d, 0xe1, 0x27,
- 0x8f, 0x7a, 0x14, 0x82, 0xe4, 0x0b, 0x2a, 0x74, 0xed, 0x00, 0xe4, 0xed, 0xfe, 0x4f, 0xbd, 0x35,
- 0xb5, 0x5f, 0x86, 0x50, 0x0b, 0x78, 0xfa, 0xc2, 0xff, 0x47, 0x01, 0x92, 0x8a, 0xcf, 0xd3, 0xef,
- 0x4d, 0x7b, 0xe9, 0xde, 0xb4, 0x74, 0x1a, 0x3f, 0x5f, 0x76, 0x87, 0xd2, 0xfe, 0x7d, 0x01, 0x52,
- 0x7b, 0xc3, 0xc8, 0x1b, 0xf2, 0xb4, 0xaf, 0x64, 0x28, 0x95, 0x3a, 0xed, 0x8b, 0x24, 0xb9, 0x63,
- 0xa7, 0x7e, 0x7d, 0xca, 0x96, 0x6b, 0x71, 0x07, 0x9a, 0x2c, 0xfe, 0xdd, 0xd1, 0x97, 0x6b, 0x59,
- 0xee, 0x38, 0x19, 0xee, 0x17, 0x27, 0x61, 0x52, 0xae, 0xf6, 0x4f, 0x8a, 0x50, 0x7d, 0x6a, 0x5b,
- 0xd5, 0x69, 0x22, 0x02, 0x73, 0x31, 0xe7, 0x68, 0x3f, 0x34, 0xfe, 0xb2, 0x9b, 0x8a, 0xbf, 0xcc,
- 0x7b, 0x37, 0xf1, 0x13, 0xa2, 0x2f, 0xff, 0x6d, 0x01, 0xe4, 0x5c, 0xb3, 0xea, 0xf8, 0x81, 0xee,
- 0x18, 0x94, 0x18, 0xe1, 0xc4, 0x96, 0x37, 0xcc, 0x47, 0x86, 0xc2, 0x09, 0x5d, 0x86, 0x3f, 0xab,
- 0x89, 0x8c, 0xfc, 0x34, 0xd4, 0x77, 0x5d, 0x3f, 0xe0, 0x93, 0x57, 0x31, 0x69, 0x32, 0xbb, 0x25,
- 0xd3, 0x31, 0xe4, 0x48, 0xbb, 0xb3, 0x2b, 0xc3, 0xdd, 0xd9, 0xda, 0x6f, 0x17, 0x61, 0xec, 0xcb,
- 0xb2, 0xdf, 0x3e, 0x2b, 0x5e, 0xb5, 0x94, 0x33, 0x5e, 0xb5, 0x7c, 0x92, 0x78, 0x55, 0xed, 0xfb,
- 0x05, 0x80, 0xa7, 0xb6, 0xd9, 0xdf, 0x4c, 0x86, 0x92, 0xe6, 0xee, 0x57, 0xd9, 0x81, 0xa4, 0xff,
- 0xa8, 0xa2, 0xaa, 0xc4, 0xc3, 0x48, 0x3f, 0x2d, 0xc0, 0x84, 0x9e, 0x08, 0xcd, 0xcc, 0xad, 0x2f,
- 0xa7, 0x22, 0x3d, 0xc3, 0xc8, 0xa2, 0x64, 0x3a, 0xa6, 0xc4, 0x92, 0xb7, 0xa2, 0x83, 0xa6, 0xef,
- 0x46, 0xdd, 0x7e, 0xe0, 0x84, 0x68, 0xae, 0xbb, 0x25, 0x38, 0x9f, 0x10, 0x0a, 0x5b, 0x3a, 0x95,
- 0x50, 0xd8, 0xf8, 0x26, 0xbf, 0xf2, 0x63, 0x37, 0xf9, 0xed, 0x43, 0x63, 0xc7, 0x73, 0xbb, 0x3c,
- 0xda, 0x54, 0xde, 0x6a, 0x7c, 0x33, 0xc7, 0x44, 0x19, 0xdd, 0xe7, 0x1f, 0x19, 0xae, 0x96, 0x15,
- 0x3e, 0x46, 0xa2, 0xb8, 0xad, 0xdf, 0x15, 0x52, 0xab, 0xa7, 0x29, 0x35, 0x1c, 0x4b, 0x36, 0x05,
- 0x3a, 0x2a, 0x31, 0xc9, 0x08, 0xd3, 0xda, 0xd3, 0x89, 0x30, 0xd5, 0x7e, 0xb7, 0xaa, 0x06, 0xb0,
- 0x67, 0xee, 0x4c, 0xd3, 0xdc, 0x5b, 0xb3, 0xe3, 0xfb, 0xaa, 0xcb, 0x27, 0xd8, 0x57, 0x5d, 0x39,
- 0xee, 0xbe, 0xea, 0xea, 0x13, 0x02, 0x3f, 0xd3, 0x9b, 0x9e, 0x6b, 0x4f, 0x71, 0xd3, 0x73, 0xfd,
- 0x74, 0x36, 0x3d, 0x37, 0x4e, 0xb6, 0xe9, 0x59, 0xee, 0x1b, 0x0e, 0xb3, 0x43, 0x72, 0xd3, 0xf3,
- 0x62, 0x92, 0x8c, 0x69, 0xfe, 0xac, 0x7d, 0xd3, 0xcd, 0x13, 0xee, 0x9b, 0x4e, 0xed, 0x72, 0x1e,
- 0x1b, 0x69, 0x97, 0xf3, 0xf8, 0xb1, 0x76, 0x39, 0x1f, 0x96, 0x20, 0xb5, 0x74, 0xfe, 0xca, 0x4d,
- 0xf6, 0xff, 0x95, 0x9b, 0xec, 0xb3, 0x22, 0x44, 0xc3, 0xe6, 0x09, 0xc3, 0x88, 0xde, 0x83, 0x7a,
- 0x57, 0x7f, 0xb8, 0x44, 0x6d, 0xfd, 0x20, 0xcf, 0xc5, 0xbd, 0xeb, 0x12, 0x03, 0x43, 0x34, 0xe2,
- 0x03, 0x58, 0xe1, 0xe1, 0xf9, 0xb9, 0x1d, 0x0e, 0xd1, 0x39, 0xfc, 0xc2, 0xa4, 0x19, 0xbd, 0x63,
- 0x4c, 0x8c, 0xf6, 0x6f, 0x8a, 0x20, 0x6f, 0x59, 0x20, 0x14, 0x2a, 0x3b, 0xd6, 0x43, 0x6a, 0xe6,
- 0x0e, 0x4e, 0x8e, 0x5d, 0xa7, 0x2e, 0x3c, 0x2a, 0x3c, 0x01, 0x05, 0x3a, 0x37, 0x95, 0x0b, 0x0f,
- 0x99, 0x6c, 0xbf, 0x1c, 0xa6, 0xf2, 0xb8, 0xa7, 0x4d, 0x9a, 0xca, 0x45, 0x12, 0x2a, 0x19, 0xc2,
- 0x32, 0xcf, 0x83, 0x25, 0x72, 0x3b, 0x04, 0x13, 0x41, 0x17, 0xca, 0x32, 0xef, 0x8b, 0x63, 0x0e,
- 0xa4, 0x8c, 0xd6, 0x2f, 0x7d, 0xef, 0x07, 0xd7, 0x9e, 0xfb, 0xfe, 0x0f, 0xae, 0x3d, 0xf7, 0xf9,
- 0x0f, 0xae, 0x3d, 0xf7, 0x2b, 0x47, 0xd7, 0x0a, 0xdf, 0x3b, 0xba, 0x56, 0xf8, 0xfe, 0xd1, 0xb5,
- 0xc2, 0xe7, 0x47, 0xd7, 0x0a, 0xff, 0xf9, 0xe8, 0x5a, 0xe1, 0x6f, 0xfc, 0x97, 0x6b, 0xcf, 0xfd,
- 0xe2, 0x9b, 0x51, 0x11, 0xe6, 0x54, 0x11, 0xe6, 0x94, 0xc0, 0xb9, 0xde, 0x5e, 0x67, 0x8e, 0x15,
- 0x21, 0x4a, 0x51, 0x45, 0xf8, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x90, 0x35, 0xbe, 0x7d, 0xfe,
- 0x99, 0x00, 0x00,
+ // 7889 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6b, 0x6c, 0x25, 0xd9,
+ 0xb5, 0xd6, 0x9c, 0x97, 0xcf, 0x39, 0xeb, 0xf8, 0xd5, 0xbb, 0x1f, 0xe3, 0xee, 0xe9, 0x69, 0xf7,
+ 0xad, 0xb9, 0x33, 0xb7, 0x2f, 0xf7, 0x5e, 0x9b, 0xf1, 0x9d, 0x57, 0xee, 0xbd, 0xc9, 0x8c, 0x8f,
+ 0xdd, 0x76, 0xbb, 0xdb, 0xee, 0x76, 0xd6, 0xb1, 0x7b, 0x26, 0x77, 0x48, 0x86, 0x72, 0xd5, 0xf6,
+ 0x71, 0x8d, 0xeb, 0x54, 0x9d, 0xa9, 0xaa, 0xe3, 0x6e, 0x4f, 0x40, 0x79, 0x0c, 0x68, 0x06, 0x01,
+ 0x02, 0xe5, 0x57, 0x24, 0x14, 0x10, 0x08, 0x29, 0x3f, 0xa2, 0xf0, 0x03, 0x29, 0xfc, 0x40, 0x82,
+ 0x10, 0x84, 0x20, 0x20, 0x1e, 0x11, 0x42, 0x62, 0xf8, 0x63, 0x11, 0x23, 0x7e, 0x80, 0x04, 0x8a,
+ 0x88, 0x20, 0xa1, 0x15, 0x11, 0xb4, 0x5f, 0xf5, 0x3a, 0x75, 0xba, 0xed, 0x53, 0x76, 0x4f, 0x0f,
+ 0xcc, 0xbf, 0xaa, 0xbd, 0xd6, 0xfe, 0xd6, 0xae, 0x5d, 0xbb, 0xf6, 0x5e, 0x7b, 0xad, 0xb5, 0x57,
+ 0xc1, 0x72, 0xdb, 0x0a, 0x76, 0x7a, 0x5b, 0x33, 0x86, 0xdb, 0x99, 0x75, 0x7a, 0x1d, 0xbd, 0xeb,
+ 0xb9, 0xef, 0xf2, 0x8b, 0x6d, 0xdb, 0xbd, 0x37, 0xdb, 0xdd, 0x6d, 0xcf, 0xea, 0x5d, 0xcb, 0x8f,
+ 0x4a, 0xf6, 0x5e, 0xd4, 0xed, 0xee, 0x8e, 0xfe, 0xe2, 0x6c, 0x9b, 0x3a, 0xd4, 0xd3, 0x03, 0x6a,
+ 0xce, 0x74, 0x3d, 0x37, 0x70, 0xc9, 0xab, 0x11, 0xd0, 0x8c, 0x02, 0x9a, 0x51, 0xd5, 0x66, 0xba,
+ 0xbb, 0xed, 0x19, 0x06, 0x14, 0x95, 0x28, 0xa0, 0x4b, 0xbf, 0x17, 0x6b, 0x41, 0xdb, 0x6d, 0xbb,
+ 0xb3, 0x1c, 0x6f, 0xab, 0xb7, 0xcd, 0xef, 0xf8, 0x0d, 0xbf, 0x12, 0x72, 0x2e, 0x69, 0xbb, 0xaf,
+ 0xf9, 0x33, 0x96, 0xcb, 0x9a, 0x35, 0x6b, 0xb8, 0x1e, 0x9d, 0xdd, 0xeb, 0x6b, 0xcb, 0xa5, 0x97,
+ 0x22, 0x9e, 0x8e, 0x6e, 0xec, 0x58, 0x0e, 0xf5, 0xf6, 0xd5, 0xb3, 0xcc, 0x7a, 0xd4, 0x77, 0x7b,
+ 0x9e, 0x41, 0x8f, 0x55, 0xcb, 0x9f, 0xed, 0xd0, 0x40, 0xcf, 0x92, 0x35, 0x3b, 0xa8, 0x96, 0xd7,
+ 0x73, 0x02, 0xab, 0xd3, 0x2f, 0xe6, 0x95, 0x47, 0x55, 0xf0, 0x8d, 0x1d, 0xda, 0xd1, 0xfb, 0xea,
+ 0xfd, 0xfe, 0xa0, 0x7a, 0xbd, 0xc0, 0xb2, 0x67, 0x2d, 0x27, 0xf0, 0x03, 0x2f, 0x5d, 0x49, 0xfb,
+ 0x11, 0xc0, 0xd9, 0xf9, 0x2d, 0x3f, 0xf0, 0x74, 0x23, 0x58, 0x77, 0xcd, 0x0d, 0xda, 0xe9, 0xda,
+ 0x7a, 0x40, 0xc9, 0x2e, 0xd4, 0xd8, 0x03, 0x99, 0x7a, 0xa0, 0x4f, 0x15, 0xae, 0x16, 0xae, 0x35,
+ 0xe6, 0xe6, 0x67, 0x86, 0x7c, 0x81, 0x33, 0x6b, 0x12, 0xa8, 0x39, 0x7a, 0x78, 0x30, 0x5d, 0x53,
+ 0x77, 0x18, 0x0a, 0x20, 0xdf, 0x2e, 0xc0, 0xa8, 0xe3, 0x9a, 0xb4, 0x45, 0x6d, 0x6a, 0x04, 0xae,
+ 0x37, 0x55, 0xbc, 0x5a, 0xba, 0xd6, 0x98, 0xfb, 0xca, 0xd0, 0x12, 0x33, 0x9e, 0x68, 0xe6, 0x76,
+ 0x4c, 0xc0, 0x75, 0x27, 0xf0, 0xf6, 0x9b, 0xe7, 0x7e, 0x7c, 0x30, 0xfd, 0xd4, 0xe1, 0xc1, 0xf4,
+ 0x68, 0x9c, 0x84, 0x89, 0x96, 0x90, 0x4d, 0x68, 0x04, 0xae, 0xcd, 0xba, 0xcc, 0x72, 0x1d, 0x7f,
+ 0xaa, 0xc4, 0x1b, 0x76, 0x65, 0x46, 0x74, 0x35, 0x13, 0x3f, 0xc3, 0xc6, 0xd8, 0xcc, 0xde, 0x8b,
+ 0x33, 0x1b, 0x21, 0x5b, 0xf3, 0xac, 0x04, 0x6e, 0x44, 0x65, 0x3e, 0xc6, 0x71, 0x08, 0x85, 0x09,
+ 0x9f, 0x1a, 0x3d, 0xcf, 0x0a, 0xf6, 0x17, 0x5c, 0x27, 0xa0, 0xf7, 0x83, 0xa9, 0x32, 0xef, 0xe5,
+ 0x17, 0xb2, 0xa0, 0xd7, 0x5d, 0xb3, 0x95, 0xe4, 0x6e, 0x9e, 0x3d, 0x3c, 0x98, 0x9e, 0x48, 0x15,
+ 0x62, 0x1a, 0x93, 0x38, 0x30, 0x69, 0x75, 0xf4, 0x36, 0x5d, 0xef, 0xd9, 0x76, 0x8b, 0x1a, 0x1e,
+ 0x0d, 0xfc, 0xa9, 0x0a, 0x7f, 0x84, 0x6b, 0x59, 0x72, 0x56, 0x5d, 0x43, 0xb7, 0xef, 0x6c, 0xbd,
+ 0x4b, 0x8d, 0x00, 0xe9, 0x36, 0xf5, 0xa8, 0x63, 0xd0, 0xe6, 0x94, 0x7c, 0x98, 0xc9, 0x95, 0x14,
+ 0x12, 0xf6, 0x61, 0x93, 0x65, 0x38, 0xd3, 0xf5, 0x2c, 0x97, 0x37, 0xc1, 0xd6, 0x7d, 0xff, 0xb6,
+ 0xde, 0xa1, 0x53, 0x23, 0x57, 0x0b, 0xd7, 0xea, 0xcd, 0x8b, 0x12, 0xe6, 0xcc, 0x7a, 0x9a, 0x01,
+ 0xfb, 0xeb, 0x90, 0x6b, 0x50, 0x53, 0x85, 0x53, 0xd5, 0xab, 0x85, 0x6b, 0x15, 0x31, 0x76, 0x54,
+ 0x5d, 0x0c, 0xa9, 0x64, 0x09, 0x6a, 0xfa, 0xf6, 0xb6, 0xe5, 0x30, 0xce, 0x1a, 0xef, 0xc2, 0xcb,
+ 0x59, 0x8f, 0x36, 0x2f, 0x79, 0x04, 0x8e, 0xba, 0xc3, 0xb0, 0x2e, 0xb9, 0x09, 0xc4, 0xa7, 0xde,
+ 0x9e, 0x65, 0xd0, 0x79, 0xc3, 0x70, 0x7b, 0x4e, 0xc0, 0xdb, 0x5e, 0xe7, 0x6d, 0xbf, 0x24, 0xdb,
+ 0x4e, 0x5a, 0x7d, 0x1c, 0x98, 0x51, 0x8b, 0xbc, 0x01, 0x93, 0xf2, 0x5b, 0x8d, 0x7a, 0x01, 0x38,
+ 0xd2, 0x39, 0xd6, 0x91, 0x98, 0xa2, 0x61, 0x1f, 0x37, 0x31, 0xe1, 0xb2, 0xde, 0x0b, 0xdc, 0x0e,
+ 0x83, 0x4c, 0x0a, 0xdd, 0x70, 0x77, 0xa9, 0x33, 0xd5, 0xb8, 0x5a, 0xb8, 0x56, 0x6b, 0x5e, 0x3d,
+ 0x3c, 0x98, 0xbe, 0x3c, 0xff, 0x10, 0x3e, 0x7c, 0x28, 0x0a, 0xb9, 0x03, 0x75, 0xd3, 0xf1, 0xd7,
+ 0x5d, 0xdb, 0x32, 0xf6, 0xa7, 0x46, 0x79, 0x03, 0x5f, 0x94, 0x8f, 0x5a, 0x5f, 0xbc, 0xdd, 0x12,
+ 0x84, 0x07, 0x07, 0xd3, 0x97, 0xfb, 0xa7, 0xd4, 0x99, 0x90, 0x8e, 0x11, 0x06, 0x59, 0xe3, 0x80,
+ 0x0b, 0xae, 0xb3, 0x6d, 0xb5, 0xa7, 0xc6, 0xf8, 0xdb, 0xb8, 0x3a, 0x60, 0x40, 0x2f, 0xde, 0x6e,
+ 0x09, 0xbe, 0xe6, 0x98, 0x14, 0x27, 0x6e, 0x31, 0x42, 0x20, 0x26, 0x8c, 0xab, 0xc9, 0x78, 0xc1,
+ 0xd6, 0xad, 0x8e, 0x3f, 0x35, 0xce, 0x07, 0xef, 0x6f, 0x0e, 0xc0, 0xc4, 0x38, 0x73, 0xf3, 0x82,
+ 0x7c, 0x94, 0xf1, 0x44, 0xb1, 0x8f, 0x29, 0xcc, 0x4b, 0xaf, 0xc3, 0x99, 0xbe, 0xb9, 0x81, 0x4c,
+ 0x42, 0x69, 0x97, 0xee, 0xf3, 0xa9, 0xaf, 0x8e, 0xec, 0x92, 0x9c, 0x83, 0xca, 0x9e, 0x6e, 0xf7,
+ 0xe8, 0x54, 0x91, 0x97, 0x89, 0x9b, 0x3f, 0x28, 0xbe, 0x56, 0xd0, 0xfe, 0x56, 0x09, 0x46, 0xd5,
+ 0x8c, 0xd3, 0xb2, 0x9c, 0x5d, 0xf2, 0x26, 0x94, 0x6c, 0xb7, 0x2d, 0xe7, 0xcd, 0x3f, 0x1a, 0x7a,
+ 0x16, 0x5b, 0x75, 0xdb, 0xcd, 0xea, 0xe1, 0xc1, 0x74, 0x69, 0xd5, 0x6d, 0x23, 0x43, 0x24, 0x06,
+ 0x54, 0x76, 0xf5, 0xed, 0x5d, 0x9d, 0xb7, 0xa1, 0x31, 0xd7, 0x1c, 0x1a, 0xfa, 0x16, 0x43, 0x61,
+ 0x6d, 0x6d, 0xd6, 0x0f, 0x0f, 0xa6, 0x2b, 0xfc, 0x16, 0x05, 0x36, 0x71, 0xa1, 0xbe, 0x65, 0xeb,
+ 0xc6, 0xee, 0x8e, 0x6b, 0xd3, 0xa9, 0x52, 0x4e, 0x41, 0x4d, 0x85, 0x24, 0x5e, 0x73, 0x78, 0x8b,
+ 0x91, 0x0c, 0x62, 0xc0, 0x48, 0xcf, 0xf4, 0x2d, 0x67, 0x57, 0xce, 0x81, 0xaf, 0x0f, 0x2d, 0x6d,
+ 0x73, 0x91, 0x3f, 0x13, 0x1c, 0x1e, 0x4c, 0x8f, 0x88, 0x6b, 0x94, 0xd0, 0xda, 0x2f, 0x47, 0x61,
+ 0x5c, 0xbd, 0xa4, 0xbb, 0xd4, 0x0b, 0xe8, 0x7d, 0x72, 0x15, 0xca, 0x0e, 0xfb, 0x34, 0xf9, 0x4b,
+ 0x6e, 0x8e, 0xca, 0xe1, 0x52, 0xe6, 0x9f, 0x24, 0xa7, 0xb0, 0x96, 0x89, 0xa1, 0x22, 0x3b, 0x7c,
+ 0xf8, 0x96, 0xb5, 0x38, 0x8c, 0x68, 0x99, 0xb8, 0x46, 0x09, 0x4d, 0xde, 0x86, 0x32, 0x7f, 0x78,
+ 0xd1, 0xd5, 0x9f, 0x1f, 0x5e, 0x04, 0x7b, 0xf4, 0x1a, 0x7b, 0x02, 0xfe, 0xe0, 0x1c, 0x94, 0x0d,
+ 0xc5, 0x9e, 0xb9, 0x2d, 0x3b, 0xf6, 0x8f, 0x72, 0x74, 0xec, 0x92, 0x18, 0x8a, 0x9b, 0x8b, 0x4b,
+ 0xc8, 0x10, 0xc9, 0x5f, 0x29, 0xc0, 0x19, 0xc3, 0x75, 0x02, 0x9d, 0xe9, 0x19, 0x6a, 0x91, 0x9d,
+ 0xaa, 0x70, 0x39, 0x37, 0x87, 0x96, 0xb3, 0x90, 0x46, 0x6c, 0x9e, 0x67, 0x6b, 0x46, 0x5f, 0x31,
+ 0xf6, 0xcb, 0x26, 0x7f, 0xad, 0x00, 0xe7, 0xd9, 0x5c, 0xde, 0xc7, 0xcc, 0x57, 0xa0, 0x93, 0x6d,
+ 0xd5, 0xc5, 0xc3, 0x83, 0xe9, 0xf3, 0x2b, 0x59, 0xc2, 0x30, 0xbb, 0x0d, 0xac, 0x75, 0x67, 0xf5,
+ 0x7e, 0xb5, 0x84, 0xaf, 0x6e, 0x8d, 0xb9, 0xd5, 0x93, 0x54, 0x75, 0x9a, 0xcf, 0xc8, 0xa1, 0x9c,
+ 0xa5, 0xd9, 0x61, 0x56, 0x2b, 0xc8, 0x75, 0xa8, 0xee, 0xb9, 0x76, 0xaf, 0x43, 0xfd, 0xa9, 0x1a,
+ 0x9f, 0x62, 0x2f, 0x65, 0x4d, 0xb1, 0x77, 0x39, 0x4b, 0x73, 0x42, 0xc2, 0x57, 0xc5, 0xbd, 0x8f,
+ 0xaa, 0x2e, 0xb1, 0x60, 0xc4, 0xb6, 0x3a, 0x56, 0xe0, 0xf3, 0x85, 0xb3, 0x31, 0x77, 0x7d, 0xe8,
+ 0xc7, 0x12, 0x9f, 0xe8, 0x2a, 0x07, 0x13, 0x5f, 0x8d, 0xb8, 0x46, 0x29, 0x80, 0x4d, 0x85, 0xbe,
+ 0xa1, 0xdb, 0x62, 0x61, 0x6d, 0xcc, 0x7d, 0x61, 0xf8, 0xcf, 0x86, 0xa1, 0x34, 0xc7, 0xe4, 0x33,
+ 0x55, 0xf8, 0x2d, 0x0a, 0x6c, 0xf2, 0x65, 0x18, 0x4f, 0xbc, 0x4d, 0x7f, 0xaa, 0xc1, 0x7b, 0xe7,
+ 0xd9, 0xac, 0xde, 0x09, 0xb9, 0xa2, 0x95, 0x27, 0x31, 0x42, 0x7c, 0x4c, 0x81, 0x91, 0x5b, 0x50,
+ 0xf3, 0x2d, 0x93, 0x1a, 0xba, 0xe7, 0x4f, 0x8d, 0x1e, 0x05, 0x78, 0x52, 0x02, 0xd7, 0x5a, 0xb2,
+ 0x1a, 0x86, 0x00, 0x64, 0x06, 0xa0, 0xab, 0x7b, 0x81, 0x25, 0x14, 0xd5, 0x31, 0xae, 0x34, 0x8d,
+ 0x1f, 0x1e, 0x4c, 0xc3, 0x7a, 0x58, 0x8a, 0x31, 0x0e, 0xc6, 0xcf, 0xea, 0xae, 0x38, 0xdd, 0x5e,
+ 0x20, 0x16, 0xd6, 0xba, 0xe0, 0x6f, 0x85, 0xa5, 0x18, 0xe3, 0x20, 0xdf, 0x2f, 0xc0, 0x33, 0xd1,
+ 0x6d, 0xff, 0x47, 0x36, 0x71, 0xe2, 0x1f, 0xd9, 0xf4, 0xe1, 0xc1, 0xf4, 0x33, 0xad, 0xc1, 0x22,
+ 0xf1, 0x61, 0xed, 0x21, 0x1f, 0x16, 0x60, 0xbc, 0xd7, 0x35, 0xf5, 0x80, 0xb6, 0x02, 0xb6, 0xe3,
+ 0x69, 0xef, 0x4f, 0x4d, 0xf2, 0x26, 0x2e, 0x0f, 0x3f, 0x0b, 0x26, 0xe0, 0xa2, 0xd7, 0x9c, 0x2c,
+ 0xc7, 0x94, 0x58, 0xed, 0x4d, 0x18, 0x9b, 0xef, 0x05, 0x3b, 0xae, 0x67, 0xbd, 0xcf, 0xd5, 0x7f,
+ 0xb2, 0x04, 0x95, 0x80, 0xab, 0x71, 0x42, 0x43, 0x78, 0x3e, 0xeb, 0xa5, 0x0b, 0x95, 0xfa, 0x16,
+ 0xdd, 0x57, 0x7a, 0x89, 0x58, 0xa9, 0x85, 0x5a, 0x27, 0xaa, 0x6b, 0x7f, 0xae, 0x00, 0xd5, 0xa6,
+ 0x6e, 0xec, 0xba, 0xdb, 0xdb, 0xe4, 0x2d, 0xa8, 0x59, 0x4e, 0x40, 0xbd, 0x3d, 0xdd, 0x96, 0xb0,
+ 0x33, 0x31, 0xd8, 0x70, 0x43, 0x18, 0x3d, 0x1e, 0xdb, 0x7d, 0x31, 0x41, 0x8b, 0x3d, 0xb9, 0x6b,
+ 0xe1, 0x9a, 0xf1, 0x8a, 0xc4, 0xc0, 0x10, 0x8d, 0x4c, 0x43, 0xc5, 0x0f, 0x68, 0xd7, 0xe7, 0x6b,
+ 0xe0, 0x98, 0x68, 0x46, 0x8b, 0x15, 0xa0, 0x28, 0xd7, 0xfe, 0x66, 0x01, 0xea, 0x4d, 0xdd, 0xb7,
+ 0x0c, 0xf6, 0x94, 0x64, 0x01, 0xca, 0x3d, 0x9f, 0x7a, 0xc7, 0x7b, 0x36, 0xbe, 0x6c, 0x6d, 0xfa,
+ 0xd4, 0x43, 0x5e, 0x99, 0xdc, 0x81, 0x5a, 0x57, 0xf7, 0xfd, 0x7b, 0xae, 0x67, 0xca, 0xa5, 0xf7,
+ 0x88, 0x40, 0x62, 0x9b, 0x20, 0xab, 0x62, 0x08, 0xa2, 0x35, 0x20, 0xd2, 0x3d, 0xb4, 0x9f, 0x17,
+ 0xe0, 0x6c, 0xb3, 0xb7, 0xbd, 0x4d, 0x3d, 0xa9, 0x15, 0x4b, 0x7d, 0x93, 0x42, 0xc5, 0xa3, 0xa6,
+ 0xe5, 0xcb, 0xb6, 0x2f, 0x0e, 0x3d, 0x50, 0x90, 0xa1, 0x48, 0xf5, 0x96, 0xf7, 0x17, 0x2f, 0x40,
+ 0x81, 0x4e, 0x7a, 0x50, 0x7f, 0x97, 0xb2, 0xdd, 0x38, 0xd5, 0x3b, 0xf2, 0xe9, 0x6e, 0x0c, 0x2d,
+ 0xea, 0x26, 0x0d, 0x5a, 0x1c, 0x29, 0xae, 0x4d, 0x87, 0x85, 0x18, 0x49, 0xd2, 0x7e, 0x54, 0x81,
+ 0xd1, 0x05, 0xb7, 0xb3, 0x65, 0x39, 0xd4, 0xbc, 0x6e, 0xb6, 0x29, 0x79, 0x07, 0xca, 0xd4, 0x6c,
+ 0x53, 0xf9, 0xb4, 0xc3, 0x2b, 0x1e, 0x0c, 0x2c, 0x52, 0x9f, 0xd8, 0x1d, 0x72, 0x60, 0xb2, 0x0a,
+ 0xe3, 0xdb, 0x9e, 0xdb, 0x11, 0x73, 0xf9, 0xc6, 0x7e, 0x57, 0xea, 0xce, 0xcd, 0xdf, 0x54, 0x1f,
+ 0xce, 0x52, 0x82, 0xfa, 0xe0, 0x60, 0x1a, 0xa2, 0x3b, 0x4c, 0xd5, 0x25, 0x6f, 0xc1, 0x54, 0x54,
+ 0x12, 0x4e, 0x6a, 0x0b, 0x6c, 0x3b, 0xc3, 0x75, 0xa7, 0x4a, 0xf3, 0xf2, 0xe1, 0xc1, 0xf4, 0xd4,
+ 0xd2, 0x00, 0x1e, 0x1c, 0x58, 0x9b, 0x4d, 0x15, 0x93, 0x11, 0x51, 0x2c, 0x34, 0x52, 0x65, 0x3a,
+ 0xa1, 0x15, 0x8c, 0xef, 0xfb, 0x96, 0x52, 0x22, 0xb0, 0x4f, 0x28, 0x59, 0x82, 0xd1, 0xc0, 0x8d,
+ 0xf5, 0x57, 0x85, 0xf7, 0x97, 0xa6, 0x0c, 0x15, 0x1b, 0xee, 0xc0, 0xde, 0x4a, 0xd4, 0x23, 0x08,
+ 0x17, 0xd4, 0x7d, 0xaa, 0xa7, 0x46, 0x78, 0x4f, 0x5d, 0x3a, 0x3c, 0x98, 0xbe, 0xb0, 0x91, 0xc9,
+ 0x81, 0x03, 0x6a, 0x92, 0x6f, 0x14, 0x60, 0x5c, 0x91, 0x64, 0x1f, 0x55, 0x4f, 0xb2, 0x8f, 0x08,
+ 0x1b, 0x11, 0x1b, 0x09, 0x01, 0x98, 0x12, 0xa8, 0xfd, 0xb2, 0x0c, 0xf5, 0x70, 0xaa, 0x27, 0xcf,
+ 0x41, 0x85, 0x9b, 0x20, 0xa4, 0x06, 0x1f, 0xae, 0xe1, 0xdc, 0x52, 0x81, 0x82, 0x46, 0x9e, 0x87,
+ 0xaa, 0xe1, 0x76, 0x3a, 0xba, 0x63, 0x72, 0xb3, 0x52, 0xbd, 0xd9, 0x60, 0xaa, 0xcb, 0x82, 0x28,
+ 0x42, 0x45, 0x23, 0x97, 0xa1, 0xac, 0x7b, 0x6d, 0x61, 0xe1, 0xa9, 0x8b, 0xf9, 0x68, 0xde, 0x6b,
+ 0xfb, 0xc8, 0x4b, 0xc9, 0xe7, 0xa0, 0x44, 0x9d, 0xbd, 0xa9, 0xf2, 0x60, 0xdd, 0xe8, 0xba, 0xb3,
+ 0x77, 0x57, 0xf7, 0x9a, 0x0d, 0xd9, 0x86, 0xd2, 0x75, 0x67, 0x0f, 0x59, 0x1d, 0xb2, 0x0a, 0x55,
+ 0xea, 0xec, 0xb1, 0x77, 0x2f, 0x4d, 0x2f, 0xbf, 0x31, 0xa0, 0x3a, 0x63, 0x91, 0xdb, 0x84, 0x50,
+ 0xc3, 0x92, 0xc5, 0xa8, 0x20, 0xc8, 0x97, 0x60, 0x54, 0x28, 0x5b, 0x6b, 0xec, 0x9d, 0xf8, 0x53,
+ 0x23, 0x1c, 0x72, 0x7a, 0xb0, 0xb6, 0xc6, 0xf9, 0x22, 0x53, 0x57, 0xac, 0xd0, 0xc7, 0x04, 0x14,
+ 0xf9, 0x12, 0xd4, 0xd5, 0xce, 0x58, 0xbd, 0xd9, 0x4c, 0x2b, 0x91, 0xda, 0x4e, 0x23, 0x7d, 0xaf,
+ 0x67, 0x79, 0xb4, 0x43, 0x9d, 0xc0, 0x6f, 0x9e, 0x51, 0x76, 0x03, 0x45, 0xf5, 0x31, 0x42, 0x23,
+ 0x5b, 0xfd, 0xe6, 0x2e, 0x61, 0xab, 0x79, 0x6e, 0xc0, 0xac, 0x3e, 0x84, 0xad, 0xeb, 0x2b, 0x30,
+ 0x11, 0xda, 0xa3, 0xa4, 0x49, 0x43, 0x58, 0x6f, 0x5e, 0x62, 0xd5, 0x57, 0x92, 0xa4, 0x07, 0x07,
+ 0xd3, 0xcf, 0x66, 0x18, 0x35, 0x22, 0x06, 0x4c, 0x83, 0x69, 0x3f, 0x2c, 0x41, 0xff, 0x3e, 0x24,
+ 0xd9, 0x69, 0x85, 0x93, 0xee, 0xb4, 0xf4, 0x03, 0x89, 0xe9, 0xf3, 0x35, 0x59, 0x2d, 0xff, 0x43,
+ 0x65, 0xbd, 0x98, 0xd2, 0x49, 0xbf, 0x98, 0x27, 0xe5, 0xdb, 0xd1, 0x3e, 0x2a, 0xc3, 0xf8, 0xa2,
+ 0x4e, 0x3b, 0xae, 0xf3, 0xc8, 0x5d, 0x59, 0xe1, 0x89, 0xd8, 0x95, 0x5d, 0x83, 0x9a, 0x47, 0xbb,
+ 0xb6, 0x65, 0xe8, 0x42, 0xf9, 0x92, 0x56, 0x50, 0x94, 0x65, 0x18, 0x52, 0x07, 0xec, 0xc6, 0x4b,
+ 0x4f, 0xe4, 0x6e, 0xbc, 0xfc, 0xc9, 0xef, 0xc6, 0xb5, 0x6f, 0x14, 0x81, 0x2b, 0x2a, 0xe4, 0x2a,
+ 0x94, 0xd9, 0x22, 0x9c, 0xb6, 0x01, 0xf1, 0x81, 0xc3, 0x29, 0xe4, 0x12, 0x14, 0x03, 0x57, 0x7e,
+ 0x79, 0x20, 0xe9, 0xc5, 0x0d, 0x17, 0x8b, 0x81, 0x4b, 0xde, 0x07, 0x30, 0x5c, 0xc7, 0xb4, 0x94,
+ 0x73, 0x20, 0xdf, 0x83, 0x2d, 0xb9, 0xde, 0x3d, 0xdd, 0x33, 0x17, 0x42, 0x44, 0xb1, 0x1f, 0x8b,
+ 0xee, 0x31, 0x26, 0x8d, 0xbc, 0x0e, 0x23, 0xae, 0xb3, 0xd4, 0xb3, 0x6d, 0xde, 0xa1, 0xf5, 0xe6,
+ 0x6f, 0xb1, 0x4d, 0xf2, 0x1d, 0x5e, 0xf2, 0xe0, 0x60, 0xfa, 0xa2, 0xd0, 0x6f, 0xd9, 0xdd, 0x9b,
+ 0x9e, 0x15, 0x58, 0x4e, 0x3b, 0xdc, 0x9e, 0xc8, 0x6a, 0xda, 0xb7, 0x0a, 0xd0, 0x58, 0xb2, 0xee,
+ 0x53, 0xf3, 0x4d, 0xcb, 0x31, 0xdd, 0x7b, 0x04, 0x61, 0xc4, 0xa6, 0x4e, 0x3b, 0xd8, 0x19, 0x72,
+ 0xff, 0x20, 0x76, 0xe9, 0x1c, 0x01, 0x25, 0x12, 0x99, 0x85, 0xba, 0xd0, 0x3e, 0x2d, 0xa7, 0xcd,
+ 0xfb, 0xb0, 0x16, 0x4d, 0x7a, 0x2d, 0x45, 0xc0, 0x88, 0x47, 0xdb, 0x87, 0x33, 0x7d, 0xdd, 0x40,
+ 0x4c, 0x28, 0x07, 0x7a, 0x5b, 0xcd, 0xaf, 0x4b, 0x43, 0x77, 0xf0, 0x86, 0xde, 0x8e, 0x75, 0x2e,
+ 0x5f, 0xe3, 0x37, 0x74, 0xb6, 0xc6, 0x33, 0x74, 0xed, 0x57, 0x05, 0xa8, 0x2d, 0xf5, 0x1c, 0x83,
+ 0x6f, 0xd1, 0x1e, 0x6d, 0x1b, 0x54, 0x0a, 0x43, 0x31, 0x53, 0x61, 0xe8, 0xc1, 0xc8, 0xee, 0xbd,
+ 0x50, 0xa1, 0x68, 0xcc, 0xad, 0x0d, 0x3f, 0x2a, 0x64, 0x93, 0x66, 0x6e, 0x71, 0x3c, 0xe1, 0xba,
+ 0x1a, 0x97, 0x0d, 0x1a, 0xb9, 0xf5, 0x26, 0x17, 0x2a, 0x85, 0x5d, 0xfa, 0x1c, 0x34, 0x62, 0x6c,
+ 0xc7, 0xb2, 0x62, 0xff, 0xbd, 0x32, 0x8c, 0x2c, 0xb7, 0x5a, 0xf3, 0xeb, 0x2b, 0xe4, 0x65, 0x68,
+ 0x48, 0xaf, 0xc6, 0xed, 0xa8, 0x0f, 0x42, 0xa7, 0x56, 0x2b, 0x22, 0x61, 0x9c, 0x8f, 0xa9, 0x63,
+ 0x1e, 0xd5, 0xed, 0x8e, 0xfc, 0x58, 0x42, 0x75, 0x0c, 0x59, 0x21, 0x0a, 0x1a, 0xd1, 0x61, 0x9c,
+ 0xed, 0xf0, 0x58, 0x17, 0x8a, 0xdd, 0x9b, 0xfc, 0x6c, 0x8e, 0xb8, 0xbf, 0xe3, 0x4a, 0xe2, 0x66,
+ 0x02, 0x00, 0x53, 0x80, 0xe4, 0x35, 0xa8, 0xe9, 0xbd, 0x60, 0x87, 0x2b, 0xd0, 0xe2, 0xdb, 0xb8,
+ 0xcc, 0x9d, 0x3e, 0xb2, 0xec, 0xc1, 0xc1, 0xf4, 0xe8, 0x2d, 0x6c, 0xbe, 0xac, 0xee, 0x31, 0xe4,
+ 0x66, 0x8d, 0x53, 0x3b, 0x46, 0xd9, 0xb8, 0xca, 0xb1, 0x1b, 0xb7, 0x9e, 0x00, 0xc0, 0x14, 0x20,
+ 0x79, 0x1b, 0x46, 0x77, 0xe9, 0x7e, 0xa0, 0x6f, 0x49, 0x01, 0x23, 0xc7, 0x11, 0x30, 0xc9, 0x54,
+ 0xb8, 0x5b, 0xb1, 0xea, 0x98, 0x00, 0x23, 0x3e, 0x9c, 0xdb, 0xa5, 0xde, 0x16, 0xf5, 0x5c, 0xb9,
+ 0xfb, 0x94, 0x42, 0xaa, 0xc7, 0x11, 0x32, 0x75, 0x78, 0x30, 0x7d, 0xee, 0x56, 0x06, 0x0c, 0x66,
+ 0x82, 0x6b, 0xff, 0xbb, 0x08, 0x13, 0xcb, 0xc2, 0xad, 0xec, 0x7a, 0x62, 0x11, 0x26, 0x17, 0xa1,
+ 0xe4, 0x75, 0x7b, 0x7c, 0xe4, 0x94, 0x84, 0xe1, 0x18, 0xd7, 0x37, 0x91, 0x95, 0x91, 0xb7, 0xa0,
+ 0x66, 0xca, 0x29, 0x43, 0x6e, 0x7e, 0x87, 0x32, 0x54, 0xa8, 0x3b, 0x0c, 0xd1, 0x98, 0xa6, 0xdf,
+ 0xf1, 0xdb, 0x2d, 0xeb, 0x7d, 0x2a, 0xf7, 0x83, 0x5c, 0xd3, 0x5f, 0x13, 0x45, 0xa8, 0x68, 0x6c,
+ 0x55, 0xdd, 0xa5, 0xfb, 0x62, 0x37, 0x54, 0x8e, 0x56, 0xd5, 0x5b, 0xb2, 0x0c, 0x43, 0x2a, 0x99,
+ 0x56, 0x1f, 0x0b, 0x1b, 0x05, 0x65, 0xb1, 0x93, 0xbf, 0xcb, 0x0a, 0xe4, 0x77, 0xc3, 0xa6, 0xcc,
+ 0x77, 0xad, 0x20, 0xa0, 0x9e, 0x7c, 0x8d, 0x43, 0x4d, 0x99, 0x37, 0x39, 0x02, 0x4a, 0x24, 0xf2,
+ 0x3b, 0x50, 0xe7, 0xe0, 0x4d, 0xdb, 0xdd, 0xe2, 0x2f, 0xae, 0x2e, 0xf6, 0xf4, 0x77, 0x55, 0x21,
+ 0x46, 0x74, 0xed, 0xd7, 0x45, 0xb8, 0xb0, 0x4c, 0x03, 0xa1, 0xd5, 0x2c, 0xd2, 0xae, 0xed, 0xee,
+ 0x33, 0xd5, 0x12, 0xe9, 0x7b, 0xe4, 0x0d, 0x00, 0xcb, 0xdf, 0x6a, 0xed, 0x19, 0xfc, 0x3b, 0x10,
+ 0xdf, 0xf0, 0x55, 0xf9, 0x49, 0xc2, 0x4a, 0xab, 0x29, 0x29, 0x0f, 0x12, 0x77, 0x18, 0xab, 0x13,
+ 0x6d, 0xaf, 0x8a, 0x0f, 0xd9, 0x5e, 0xb5, 0x00, 0xba, 0x91, 0x82, 0x5a, 0xe2, 0x9c, 0xbf, 0xaf,
+ 0xc4, 0x1c, 0x47, 0x37, 0x8d, 0xc1, 0xe4, 0x51, 0x19, 0x1d, 0x98, 0x34, 0xe9, 0xb6, 0xde, 0xb3,
+ 0x83, 0x50, 0xa9, 0x96, 0x1f, 0xf1, 0xd1, 0xf5, 0xf2, 0xd0, 0xe5, 0xbd, 0x98, 0x42, 0xc2, 0x3e,
+ 0x6c, 0xed, 0xef, 0x97, 0xe0, 0xd2, 0x32, 0x0d, 0x42, 0x8b, 0x8b, 0x9c, 0x1d, 0x5b, 0x5d, 0x6a,
+ 0xb0, 0xb7, 0xf0, 0x61, 0x01, 0x46, 0x6c, 0x7d, 0x8b, 0xda, 0x6c, 0xf5, 0x62, 0x4f, 0xf3, 0xce,
+ 0xd0, 0x0b, 0xc1, 0x60, 0x29, 0x33, 0xab, 0x5c, 0x42, 0x6a, 0x69, 0x10, 0x85, 0x28, 0xc5, 0xb3,
+ 0x49, 0xdd, 0xb0, 0x7b, 0x7e, 0x40, 0xbd, 0x75, 0xd7, 0x0b, 0xa4, 0x3e, 0x19, 0x4e, 0xea, 0x0b,
+ 0x11, 0x09, 0xe3, 0x7c, 0x64, 0x0e, 0xc0, 0xb0, 0x2d, 0xea, 0x04, 0xbc, 0x96, 0xf8, 0xae, 0x88,
+ 0x7a, 0xbf, 0x0b, 0x21, 0x05, 0x63, 0x5c, 0x4c, 0x54, 0xc7, 0x75, 0xac, 0xc0, 0x15, 0xa2, 0xca,
+ 0x49, 0x51, 0x6b, 0x11, 0x09, 0xe3, 0x7c, 0xbc, 0x1a, 0x0d, 0x3c, 0xcb, 0xf0, 0x79, 0xb5, 0x4a,
+ 0xaa, 0x5a, 0x44, 0xc2, 0x38, 0x1f, 0x5b, 0xf3, 0x62, 0xcf, 0x7f, 0xac, 0x35, 0xef, 0x7b, 0x75,
+ 0xb8, 0x92, 0xe8, 0xd6, 0x40, 0x0f, 0xe8, 0x76, 0xcf, 0x6e, 0xd1, 0x40, 0xbd, 0xc0, 0x21, 0xd7,
+ 0xc2, 0xbf, 0x18, 0xbd, 0x77, 0x11, 0xcc, 0x62, 0x9c, 0xcc, 0x7b, 0xef, 0x6b, 0xe0, 0x91, 0xde,
+ 0xfd, 0x2c, 0xd4, 0x1d, 0x3d, 0xf0, 0xf9, 0x87, 0x2b, 0xbf, 0xd1, 0x50, 0x0d, 0xbb, 0xad, 0x08,
+ 0x18, 0xf1, 0x90, 0x75, 0x38, 0x27, 0xbb, 0xf8, 0xfa, 0xfd, 0xae, 0xeb, 0x05, 0xd4, 0x13, 0x75,
+ 0xe5, 0x72, 0x2a, 0xeb, 0x9e, 0x5b, 0xcb, 0xe0, 0xc1, 0xcc, 0x9a, 0x64, 0x0d, 0xce, 0x1a, 0xc2,
+ 0xc1, 0x4f, 0x6d, 0x57, 0x37, 0x15, 0xa0, 0x30, 0x70, 0x85, 0x5b, 0xa3, 0x85, 0x7e, 0x16, 0xcc,
+ 0xaa, 0x97, 0x1e, 0xcd, 0x23, 0x43, 0x8d, 0xe6, 0xea, 0x30, 0xa3, 0xb9, 0x36, 0xdc, 0x68, 0xae,
+ 0x1f, 0x6d, 0x34, 0xb3, 0x9e, 0x67, 0xe3, 0x88, 0x7a, 0x4c, 0x3d, 0x11, 0x2b, 0x6c, 0x2c, 0x7e,
+ 0x24, 0xec, 0xf9, 0x56, 0x06, 0x0f, 0x66, 0xd6, 0x24, 0x5b, 0x70, 0x49, 0x94, 0x5f, 0x77, 0x0c,
+ 0x6f, 0xbf, 0xcb, 0x16, 0x9e, 0x18, 0x6e, 0x23, 0x61, 0x61, 0xbc, 0xd4, 0x1a, 0xc8, 0x89, 0x0f,
+ 0x41, 0x21, 0x7f, 0x08, 0x63, 0xe2, 0x2d, 0xad, 0xe9, 0x5d, 0x0e, 0x2b, 0xa2, 0x49, 0xce, 0x4b,
+ 0xd8, 0xb1, 0x85, 0x38, 0x11, 0x93, 0xbc, 0x64, 0x1e, 0x26, 0xba, 0x7b, 0x06, 0xbb, 0x5c, 0xd9,
+ 0xbe, 0x4d, 0xa9, 0x49, 0x4d, 0xee, 0xbe, 0xaa, 0x37, 0x9f, 0x56, 0x86, 0x8e, 0xf5, 0x24, 0x19,
+ 0xd3, 0xfc, 0xe4, 0x35, 0x18, 0xf5, 0x03, 0xdd, 0x0b, 0xa4, 0x59, 0x6f, 0x6a, 0x5c, 0x44, 0xdb,
+ 0x28, 0xab, 0x57, 0x2b, 0x46, 0xc3, 0x04, 0x67, 0xe6, 0x7a, 0x31, 0x71, 0x7a, 0xeb, 0x45, 0x9e,
+ 0xd9, 0xea, 0x9f, 0x15, 0xe1, 0xea, 0x32, 0x0d, 0xd6, 0x5c, 0x47, 0x1a, 0x45, 0xb3, 0x96, 0xfd,
+ 0x23, 0xd9, 0x44, 0x93, 0x8b, 0x76, 0xf1, 0x44, 0x17, 0xed, 0xd2, 0x09, 0x2d, 0xda, 0xe5, 0x53,
+ 0x5c, 0xb4, 0xff, 0x61, 0x11, 0x9e, 0x4e, 0xf4, 0xe4, 0xba, 0x6b, 0xaa, 0x09, 0xff, 0xb3, 0x0e,
+ 0x3c, 0x42, 0x07, 0x3e, 0x10, 0x7a, 0x27, 0x77, 0x6b, 0xa5, 0x34, 0x9e, 0x0f, 0xd2, 0x1a, 0xcf,
+ 0xdb, 0x79, 0x56, 0xbe, 0x0c, 0x09, 0x47, 0x5a, 0xf1, 0x6e, 0x02, 0xf1, 0xa4, 0x13, 0x4e, 0x98,
+ 0x7e, 0x62, 0x4a, 0x4f, 0x18, 0xce, 0x87, 0x7d, 0x1c, 0x98, 0x51, 0x8b, 0xb4, 0xe0, 0xbc, 0x4f,
+ 0x9d, 0xc0, 0x72, 0xa8, 0x9d, 0x84, 0x13, 0xda, 0xd0, 0xb3, 0x12, 0xee, 0x7c, 0x2b, 0x8b, 0x09,
+ 0xb3, 0xeb, 0xe6, 0x99, 0x07, 0xfe, 0x25, 0x70, 0x95, 0x53, 0x74, 0xcd, 0x89, 0x69, 0x2c, 0x1f,
+ 0xa6, 0x35, 0x96, 0x77, 0xf2, 0xbf, 0xb7, 0xe1, 0xb4, 0x95, 0x39, 0x00, 0xfe, 0x16, 0xe2, 0xea,
+ 0x4a, 0xb8, 0x48, 0x63, 0x48, 0xc1, 0x18, 0x17, 0x5b, 0x80, 0x54, 0x3f, 0xc7, 0x35, 0x95, 0x70,
+ 0x01, 0x6a, 0xc5, 0x89, 0x98, 0xe4, 0x1d, 0xa8, 0xed, 0x54, 0x86, 0xd6, 0x76, 0x6e, 0x02, 0x49,
+ 0x18, 0x1e, 0x05, 0xde, 0x48, 0x32, 0x9a, 0x74, 0xa5, 0x8f, 0x03, 0x33, 0x6a, 0x0d, 0x18, 0xca,
+ 0xd5, 0x93, 0x1d, 0xca, 0xb5, 0xe1, 0x87, 0x32, 0x79, 0x07, 0x2e, 0x72, 0x51, 0xb2, 0x7f, 0x92,
+ 0xc0, 0x42, 0xef, 0xf9, 0x0d, 0x09, 0x7c, 0x11, 0x07, 0x31, 0xe2, 0x60, 0x0c, 0xf6, 0x7e, 0x0c,
+ 0x8f, 0x9a, 0x4c, 0xb8, 0x6e, 0x0f, 0xd6, 0x89, 0x16, 0x32, 0x78, 0x30, 0xb3, 0x26, 0x1b, 0x62,
+ 0x01, 0x1b, 0x86, 0xfa, 0x96, 0x4d, 0x4d, 0x19, 0x4d, 0x1b, 0x0e, 0xb1, 0x8d, 0xd5, 0x96, 0xa4,
+ 0x60, 0x8c, 0x2b, 0x4b, 0x4d, 0x19, 0x3d, 0xa6, 0x9a, 0xb2, 0xcc, 0xad, 0xf4, 0xdb, 0x09, 0x6d,
+ 0x48, 0xea, 0x3a, 0x61, 0x7c, 0xf4, 0x42, 0x9a, 0x01, 0xfb, 0xeb, 0x70, 0x2d, 0xd1, 0xf0, 0xac,
+ 0x6e, 0xe0, 0x27, 0xb1, 0xc6, 0x53, 0x5a, 0x62, 0x06, 0x0f, 0x66, 0xd6, 0x64, 0xfa, 0xf9, 0x0e,
+ 0xd5, 0xed, 0x60, 0x27, 0x09, 0x38, 0x91, 0xd4, 0xcf, 0x6f, 0xf4, 0xb3, 0x60, 0x56, 0xbd, 0xcc,
+ 0x05, 0x69, 0xf2, 0xc9, 0x54, 0xab, 0xbe, 0x59, 0x82, 0x8b, 0xcb, 0x34, 0x08, 0x03, 0x8d, 0x3e,
+ 0x33, 0xa3, 0x7c, 0x02, 0x66, 0x94, 0xef, 0x56, 0xe0, 0xec, 0x32, 0x0d, 0xfa, 0xb4, 0xb1, 0xff,
+ 0x4f, 0xbb, 0x7f, 0x0d, 0xce, 0x46, 0xb1, 0x6d, 0xad, 0xc0, 0xf5, 0xc4, 0x5a, 0x9e, 0xda, 0x2d,
+ 0xb7, 0xfa, 0x59, 0x30, 0xab, 0x1e, 0xf9, 0x12, 0x3c, 0xcd, 0x97, 0x7a, 0xa7, 0x2d, 0xec, 0xb3,
+ 0xc2, 0x98, 0x10, 0x3b, 0x9d, 0x31, 0x2d, 0x21, 0x9f, 0x6e, 0x65, 0xb3, 0xe1, 0xa0, 0xfa, 0xe4,
+ 0x6b, 0x30, 0xda, 0xb5, 0xba, 0xd4, 0xb6, 0x1c, 0xae, 0x9f, 0xe5, 0x0e, 0x09, 0x59, 0x8f, 0x81,
+ 0x45, 0x1b, 0xb8, 0x78, 0x29, 0x26, 0x04, 0x66, 0x8e, 0xd4, 0xda, 0x29, 0x8e, 0xd4, 0xff, 0x51,
+ 0x84, 0xea, 0xb2, 0xe7, 0xf6, 0xba, 0xcd, 0x7d, 0xd2, 0x86, 0x91, 0x7b, 0xdc, 0x79, 0x26, 0x5d,
+ 0x53, 0xc3, 0xc7, 0x87, 0x0b, 0x1f, 0x5c, 0xa4, 0x12, 0x89, 0x7b, 0x94, 0xf0, 0x6c, 0x10, 0xef,
+ 0xd2, 0x7d, 0x6a, 0x4a, 0x1f, 0x5a, 0x38, 0x88, 0x6f, 0xb1, 0x42, 0x14, 0x34, 0xd2, 0x81, 0x09,
+ 0xdd, 0xb6, 0xdd, 0x7b, 0xd4, 0x5c, 0xd5, 0x03, 0xea, 0x50, 0x5f, 0xb9, 0x24, 0x8f, 0x6b, 0x96,
+ 0xe6, 0x7e, 0xfd, 0xf9, 0x24, 0x14, 0xa6, 0xb1, 0xc9, 0xbb, 0x50, 0xf5, 0x03, 0xd7, 0x53, 0xca,
+ 0x56, 0x63, 0x6e, 0x61, 0xf8, 0x97, 0xde, 0xfc, 0x62, 0x4b, 0x40, 0x09, 0x9b, 0xbd, 0xbc, 0x41,
+ 0x25, 0x40, 0xfb, 0x4e, 0x01, 0xe0, 0xc6, 0xc6, 0xc6, 0xba, 0x74, 0x2f, 0x98, 0x50, 0xd6, 0x7b,
+ 0xa1, 0xa3, 0x72, 0x78, 0x87, 0x60, 0x22, 0x2c, 0x53, 0xfa, 0xf0, 0x7a, 0xc1, 0x0e, 0x72, 0x74,
+ 0xf2, 0xdb, 0x50, 0x95, 0x0a, 0xb2, 0xec, 0xf6, 0x30, 0xb4, 0x40, 0x2a, 0xd1, 0xa8, 0xe8, 0xda,
+ 0xdf, 0x2d, 0x02, 0xac, 0x98, 0x36, 0x6d, 0xa9, 0x90, 0xfe, 0x7a, 0xb0, 0xe3, 0x51, 0x7f, 0xc7,
+ 0xb5, 0xcd, 0x21, 0xbd, 0xa9, 0xdc, 0xe6, 0xbf, 0xa1, 0x40, 0x30, 0xc2, 0x23, 0x26, 0x8c, 0xfa,
+ 0x01, 0xed, 0xaa, 0x48, 0xcd, 0x21, 0x9d, 0x28, 0x93, 0xc2, 0x2e, 0x12, 0xe1, 0x60, 0x02, 0x95,
+ 0xe8, 0xd0, 0xb0, 0x1c, 0x43, 0x7c, 0x20, 0xcd, 0xfd, 0x21, 0x07, 0xd2, 0x04, 0xdb, 0x71, 0xac,
+ 0x44, 0x30, 0x18, 0xc7, 0xd4, 0x7e, 0x56, 0x84, 0x0b, 0x5c, 0x1e, 0x6b, 0x46, 0x22, 0x1e, 0x93,
+ 0xfc, 0xe9, 0xbe, 0xe3, 0x87, 0x7f, 0xf2, 0x68, 0xa2, 0xc5, 0xe9, 0xb5, 0x35, 0x1a, 0xe8, 0x91,
+ 0x3e, 0x17, 0x95, 0xc5, 0xce, 0x1c, 0xf6, 0xa0, 0xec, 0xb3, 0xf9, 0x4a, 0xf4, 0x5e, 0x6b, 0xe8,
+ 0x21, 0x94, 0xfd, 0x00, 0x7c, 0xf6, 0x0a, 0xbd, 0xc6, 0x7c, 0xd6, 0xe2, 0xe2, 0xc8, 0x9f, 0x85,
+ 0x11, 0x3f, 0xd0, 0x83, 0x9e, 0xfa, 0x34, 0x37, 0x4f, 0x5a, 0x30, 0x07, 0x8f, 0xe6, 0x11, 0x71,
+ 0x8f, 0x52, 0xa8, 0xf6, 0xb3, 0x02, 0x5c, 0xca, 0xae, 0xb8, 0x6a, 0xf9, 0x01, 0xf9, 0x53, 0x7d,
+ 0xdd, 0x7e, 0xc4, 0x37, 0xce, 0x6a, 0xf3, 0x4e, 0x0f, 0x23, 0xd4, 0x55, 0x49, 0xac, 0xcb, 0x03,
+ 0xa8, 0x58, 0x01, 0xed, 0xa8, 0xfd, 0xe5, 0x9d, 0x13, 0x7e, 0xf4, 0xd8, 0xd2, 0xce, 0xa4, 0xa0,
+ 0x10, 0xa6, 0x7d, 0x54, 0x1c, 0xf4, 0xc8, 0x7c, 0xf9, 0xb0, 0x93, 0x31, 0xbf, 0xb7, 0xf2, 0xc5,
+ 0xfc, 0x26, 0x1b, 0xd4, 0x1f, 0xfa, 0xfb, 0x67, 0xfa, 0x43, 0x7f, 0xef, 0xe4, 0x0f, 0xfd, 0x4d,
+ 0x75, 0xc3, 0xc0, 0x08, 0xe0, 0x8f, 0x4b, 0x70, 0xf9, 0x61, 0xc3, 0x86, 0xad, 0x67, 0x72, 0x74,
+ 0xe6, 0x5d, 0xcf, 0x1e, 0x3e, 0x0e, 0xc9, 0x1c, 0x54, 0xba, 0x3b, 0xba, 0xaf, 0x94, 0x32, 0xb5,
+ 0x61, 0xa9, 0xac, 0xb3, 0xc2, 0x07, 0x6c, 0xd2, 0xe0, 0xca, 0x1c, 0xbf, 0x45, 0xc1, 0xca, 0xa6,
+ 0xe3, 0x0e, 0xf5, 0xfd, 0xc8, 0x26, 0x10, 0x4e, 0xc7, 0x6b, 0xa2, 0x18, 0x15, 0x9d, 0x04, 0x30,
+ 0x22, 0x4c, 0xcc, 0x72, 0x65, 0x1a, 0x3e, 0x90, 0x2b, 0x23, 0x4c, 0x3c, 0x7a, 0x28, 0xe9, 0xad,
+ 0x90, 0xb2, 0xc8, 0x0c, 0x94, 0x83, 0x28, 0x68, 0x57, 0x6d, 0xcd, 0xcb, 0x19, 0xfa, 0x29, 0xe7,
+ 0x63, 0x1b, 0x7b, 0x77, 0x8b, 0x1b, 0xd5, 0x4d, 0xe9, 0x3f, 0xb7, 0x5c, 0x87, 0x2b, 0x64, 0xa5,
+ 0x68, 0x63, 0x7f, 0xa7, 0x8f, 0x03, 0x33, 0x6a, 0x69, 0xff, 0xa6, 0x06, 0x17, 0xb2, 0xc7, 0x03,
+ 0xeb, 0xb7, 0x3d, 0xea, 0xf9, 0x0c, 0xbb, 0x90, 0xec, 0xb7, 0xbb, 0xa2, 0x18, 0x15, 0xfd, 0x53,
+ 0x1d, 0x70, 0xf6, 0xdd, 0x02, 0x5c, 0xf4, 0xa4, 0x8f, 0xe8, 0x71, 0x04, 0x9d, 0x3d, 0x2b, 0xcc,
+ 0x19, 0x03, 0x04, 0xe2, 0xe0, 0xb6, 0x90, 0xbf, 0x5d, 0x80, 0xa9, 0x4e, 0xca, 0xce, 0x71, 0x8a,
+ 0x27, 0xe8, 0x78, 0x54, 0xfc, 0xda, 0x00, 0x79, 0x38, 0xb0, 0x25, 0xe4, 0x6b, 0xd0, 0xe8, 0xb2,
+ 0x71, 0xe1, 0x07, 0xd4, 0x31, 0xd4, 0x21, 0xba, 0xe1, 0xbf, 0xa4, 0xf5, 0x08, 0x2b, 0x3c, 0x41,
+ 0xc3, 0xf5, 0x83, 0x18, 0x01, 0xe3, 0x12, 0x9f, 0xf0, 0x23, 0x73, 0xd7, 0xa0, 0xe6, 0xd3, 0x20,
+ 0xb0, 0x9c, 0xb6, 0xd8, 0x6f, 0xd4, 0xc5, 0xb7, 0xd2, 0x92, 0x65, 0x18, 0x52, 0xc9, 0xef, 0x40,
+ 0x9d, 0xbb, 0x9c, 0xe6, 0xbd, 0xb6, 0x3f, 0x55, 0xe7, 0xe1, 0x62, 0x63, 0x22, 0x00, 0x4e, 0x16,
+ 0x62, 0x44, 0x27, 0x2f, 0xc1, 0xe8, 0x16, 0xff, 0x7c, 0xe5, 0x29, 0x6a, 0x61, 0xe3, 0xe2, 0xda,
+ 0x5a, 0x33, 0x56, 0x8e, 0x09, 0x2e, 0x32, 0x07, 0x40, 0x43, 0xbf, 0x5c, 0xda, 0x9e, 0x15, 0x79,
+ 0xec, 0x30, 0xc6, 0x45, 0x9e, 0x85, 0x52, 0x60, 0xfb, 0xdc, 0x86, 0x55, 0x8b, 0xb6, 0xa0, 0x1b,
+ 0xab, 0x2d, 0x64, 0xe5, 0xda, 0xaf, 0x0b, 0x30, 0x91, 0x3a, 0x5c, 0xc2, 0xaa, 0xf4, 0x3c, 0x5b,
+ 0x4e, 0x23, 0x61, 0x95, 0x4d, 0x5c, 0x45, 0x56, 0x4e, 0xde, 0x91, 0x6a, 0x79, 0x31, 0x67, 0xc2,
+ 0x88, 0xdb, 0x7a, 0xe0, 0x33, 0x3d, 0xbc, 0x4f, 0x23, 0xe7, 0x6e, 0xbe, 0xa8, 0x3d, 0x72, 0x1d,
+ 0x88, 0xb9, 0xf9, 0x22, 0x1a, 0x26, 0x38, 0x53, 0x06, 0xbf, 0xf2, 0x51, 0x0c, 0x7e, 0xda, 0xb7,
+ 0x8a, 0xb1, 0x1e, 0x90, 0x9a, 0xfd, 0x23, 0x7a, 0xe0, 0x05, 0xb6, 0x80, 0x86, 0x8b, 0x7b, 0x3d,
+ 0xbe, 0xfe, 0xf1, 0xc5, 0x58, 0x52, 0xc9, 0x9b, 0xa2, 0xef, 0x4b, 0x39, 0x8f, 0xe5, 0x6e, 0xac,
+ 0xb6, 0x44, 0x74, 0x95, 0x7a, 0x6b, 0xe1, 0x2b, 0x28, 0x9f, 0xd2, 0x2b, 0xd0, 0xfe, 0x45, 0x09,
+ 0x1a, 0x37, 0xdd, 0xad, 0x4f, 0x49, 0x04, 0x75, 0xf6, 0x32, 0x55, 0xfc, 0x04, 0x97, 0xa9, 0x4d,
+ 0x78, 0x3a, 0x08, 0xec, 0x16, 0x35, 0x5c, 0xc7, 0xf4, 0xe7, 0xb7, 0x03, 0xea, 0x2d, 0x59, 0x8e,
+ 0xe5, 0xef, 0x50, 0x53, 0xba, 0x93, 0x9e, 0x39, 0x3c, 0x98, 0x7e, 0x7a, 0x63, 0x63, 0x35, 0x8b,
+ 0x05, 0x07, 0xd5, 0xe5, 0xd3, 0x86, 0x38, 0x09, 0xc8, 0x4f, 0xca, 0xc8, 0x98, 0x1b, 0x31, 0x6d,
+ 0xc4, 0xca, 0x31, 0xc1, 0xa5, 0xfd, 0xa0, 0x08, 0xf5, 0x30, 0x15, 0x00, 0x79, 0x1e, 0xaa, 0x5b,
+ 0x9e, 0xbb, 0x4b, 0x3d, 0xe1, 0xb9, 0x93, 0x27, 0x65, 0x9a, 0xa2, 0x08, 0x15, 0x8d, 0x3c, 0x07,
+ 0x95, 0xc0, 0xed, 0x5a, 0x46, 0xda, 0xa0, 0xb6, 0xc1, 0x0a, 0x51, 0xd0, 0x4e, 0x6f, 0x80, 0xbf,
+ 0x90, 0x50, 0xed, 0xea, 0x03, 0x95, 0xb1, 0xb7, 0xa1, 0xec, 0xeb, 0xbe, 0x2d, 0xd7, 0xd3, 0x1c,
+ 0xa7, 0xea, 0xe7, 0x5b, 0xab, 0xf2, 0x54, 0xfd, 0x7c, 0x6b, 0x15, 0x39, 0xa8, 0xf6, 0xcb, 0x22,
+ 0x34, 0x44, 0xbf, 0x89, 0x59, 0xe1, 0x24, 0x7b, 0xee, 0x75, 0x1e, 0x4a, 0xe1, 0xf7, 0x3a, 0xd4,
+ 0xe3, 0x66, 0x26, 0x39, 0xc9, 0xc5, 0xfd, 0x03, 0x11, 0x31, 0x0c, 0xa7, 0x88, 0x8a, 0x54, 0xd7,
+ 0x97, 0x4f, 0xb1, 0xeb, 0x2b, 0x47, 0xea, 0xfa, 0x91, 0xd3, 0xe8, 0xfa, 0x0f, 0x8b, 0x50, 0x5f,
+ 0xb5, 0xb6, 0xa9, 0xb1, 0x6f, 0xd8, 0xfc, 0x4c, 0xa0, 0x49, 0x6d, 0x1a, 0xd0, 0x65, 0x4f, 0x37,
+ 0xe8, 0x3a, 0xf5, 0x2c, 0x9e, 0x2a, 0x87, 0x7d, 0x1f, 0x7c, 0x06, 0x92, 0x67, 0x02, 0x17, 0x07,
+ 0xf0, 0xe0, 0xc0, 0xda, 0x64, 0x05, 0x46, 0x4d, 0xea, 0x5b, 0x1e, 0x35, 0xd7, 0x63, 0x1b, 0x95,
+ 0xe7, 0xd5, 0x52, 0xb3, 0x18, 0xa3, 0x3d, 0x38, 0x98, 0x1e, 0x53, 0x06, 0x4a, 0xb1, 0x63, 0x49,
+ 0x54, 0x65, 0x9f, 0x7c, 0x57, 0xef, 0xf9, 0x59, 0x6d, 0x8c, 0x7d, 0xf2, 0xeb, 0xd9, 0x2c, 0x38,
+ 0xa8, 0xae, 0x56, 0x81, 0xd2, 0xaa, 0xdb, 0xd6, 0x3e, 0x2a, 0x41, 0x98, 0x53, 0x89, 0xfc, 0x85,
+ 0x02, 0x34, 0x74, 0xc7, 0x71, 0x03, 0x99, 0xaf, 0x48, 0x78, 0xe0, 0x31, 0x77, 0xea, 0xa6, 0x99,
+ 0xf9, 0x08, 0x54, 0x38, 0x6f, 0x43, 0x87, 0x72, 0x8c, 0x82, 0x71, 0xd9, 0xa4, 0x97, 0xf2, 0x27,
+ 0xaf, 0xe5, 0x6f, 0xc5, 0x11, 0xbc, 0xc7, 0x97, 0xbe, 0x00, 0x93, 0xe9, 0xc6, 0x1e, 0xc7, 0x1d,
+ 0x94, 0xcb, 0x31, 0x5f, 0x04, 0x88, 0x62, 0x4a, 0x1e, 0x83, 0x11, 0xcb, 0x4a, 0x18, 0xb1, 0x86,
+ 0x3f, 0xd8, 0x1e, 0x35, 0x7a, 0xa0, 0xe1, 0xea, 0xbd, 0x94, 0xe1, 0x6a, 0xe5, 0x24, 0x84, 0x3d,
+ 0xdc, 0x58, 0xf5, 0x77, 0x0a, 0x30, 0x19, 0x31, 0xcb, 0x13, 0xb2, 0xaf, 0xc2, 0x98, 0x47, 0x75,
+ 0xb3, 0xa9, 0x07, 0xc6, 0x0e, 0x0f, 0xf5, 0x2e, 0xf0, 0xd8, 0xec, 0x33, 0x87, 0x07, 0xd3, 0x63,
+ 0x18, 0x27, 0x60, 0x92, 0x8f, 0xe8, 0xd0, 0x60, 0x05, 0x1b, 0x56, 0x87, 0xba, 0xbd, 0x60, 0x48,
+ 0xab, 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c, 0xed, 0xe3, 0x02, 0x8c, 0xc7, 0x1b, 0x7c,
+ 0xea, 0x16, 0xb5, 0x9d, 0xa4, 0x45, 0x6d, 0xe1, 0x04, 0xde, 0xc9, 0x00, 0x2b, 0xda, 0x07, 0x10,
+ 0x7f, 0x34, 0x6e, 0x39, 0x8b, 0x1b, 0x0b, 0x0a, 0x0f, 0x35, 0x16, 0x7c, 0xfa, 0xd3, 0xe8, 0x0c,
+ 0xd2, 0x72, 0xcb, 0x4f, 0xb0, 0x96, 0xfb, 0x49, 0xe6, 0xe2, 0x89, 0xe5, 0x93, 0x19, 0xc9, 0x91,
+ 0x4f, 0xa6, 0x13, 0xe6, 0x93, 0xa9, 0x9e, 0xd8, 0xa4, 0x73, 0x94, 0x9c, 0x32, 0xb5, 0xc7, 0x9a,
+ 0x53, 0xa6, 0x7e, 0x5a, 0x39, 0x65, 0x20, 0x6f, 0x4e, 0x99, 0x0f, 0x0a, 0x30, 0x6e, 0x26, 0x4e,
+ 0xcc, 0x72, 0xdb, 0x42, 0x9e, 0xa5, 0x26, 0x79, 0x00, 0x57, 0x1c, 0x99, 0x4a, 0x96, 0x61, 0x4a,
+ 0x64, 0x56, 0x26, 0x97, 0xd1, 0x4f, 0x26, 0x93, 0xcb, 0x2f, 0xaa, 0xf1, 0x15, 0xe9, 0x71, 0x1b,
+ 0xcd, 0x5f, 0x49, 0x1a, 0xcd, 0xaf, 0xa6, 0x8d, 0xe6, 0x13, 0xb1, 0x78, 0xd6, 0xb8, 0xe1, 0xfc,
+ 0x77, 0x63, 0x13, 0x75, 0x89, 0xe7, 0x70, 0x09, 0xdf, 0x79, 0xc6, 0x64, 0x3d, 0x0f, 0x13, 0x52,
+ 0x7b, 0x55, 0x44, 0x3e, 0xcb, 0x8d, 0x45, 0x61, 0x4e, 0x8b, 0x49, 0x32, 0xa6, 0xf9, 0x99, 0x40,
+ 0x5f, 0xa5, 0xf2, 0x14, 0x5b, 0x85, 0x68, 0x90, 0xa9, 0x34, 0x9b, 0x21, 0x07, 0xdb, 0x56, 0x78,
+ 0x54, 0xf7, 0xa5, 0xe9, 0x3b, 0xb6, 0xad, 0x40, 0x5e, 0x8a, 0x92, 0x1a, 0xb7, 0xff, 0x57, 0x1f,
+ 0x61, 0xff, 0xd7, 0xa1, 0x61, 0xeb, 0x7e, 0x20, 0xde, 0xa6, 0x29, 0x3f, 0xe7, 0x3f, 0x71, 0xb4,
+ 0x85, 0x97, 0x2d, 0xe6, 0x91, 0x76, 0xbb, 0x1a, 0xc1, 0x60, 0x1c, 0x93, 0x98, 0x30, 0xca, 0x6e,
+ 0xf9, 0xa7, 0x6d, 0xce, 0x07, 0x32, 0xe1, 0xd5, 0x71, 0x64, 0x84, 0x66, 0xab, 0xd5, 0x18, 0x0e,
+ 0x26, 0x50, 0x07, 0xb8, 0x08, 0x60, 0x18, 0x17, 0x01, 0xf9, 0x43, 0xa1, 0x39, 0xed, 0x87, 0xaf,
+ 0xb5, 0xc1, 0x5f, 0x6b, 0x18, 0x22, 0x89, 0x71, 0x22, 0x26, 0x79, 0xd9, 0xa8, 0xe8, 0xc9, 0x6e,
+ 0x50, 0xd5, 0x47, 0x93, 0xa3, 0x62, 0x33, 0x49, 0xc6, 0x34, 0x3f, 0x59, 0x87, 0x73, 0x61, 0x51,
+ 0xbc, 0x19, 0x63, 0x1c, 0x27, 0x8c, 0x59, 0xdb, 0xcc, 0xe0, 0xc1, 0xcc, 0x9a, 0xfc, 0x10, 0x48,
+ 0xcf, 0xf3, 0xa8, 0x13, 0xdc, 0xd0, 0xfd, 0x1d, 0x19, 0xfc, 0x16, 0x1d, 0x02, 0x89, 0x48, 0x18,
+ 0xe7, 0x23, 0x73, 0x00, 0x02, 0x8e, 0xd7, 0x9a, 0x48, 0xc6, 0x97, 0x6e, 0x86, 0x14, 0x8c, 0x71,
+ 0x69, 0x1f, 0xd4, 0xa1, 0x71, 0x5b, 0x0f, 0xac, 0x3d, 0xca, 0xfd, 0x79, 0xa7, 0xe3, 0x54, 0xf9,
+ 0xeb, 0x05, 0xb8, 0x90, 0x0c, 0xda, 0x3c, 0x45, 0xcf, 0x0a, 0x4f, 0x01, 0x83, 0x99, 0xd2, 0x70,
+ 0x40, 0x2b, 0xb8, 0x8f, 0xa5, 0x2f, 0x06, 0xf4, 0xb4, 0x7d, 0x2c, 0xad, 0x41, 0x02, 0x71, 0x70,
+ 0x5b, 0x3e, 0x2d, 0x3e, 0x96, 0x27, 0x3b, 0x67, 0x61, 0xca, 0x03, 0x54, 0x7d, 0x62, 0x3c, 0x40,
+ 0xb5, 0x27, 0x42, 0xed, 0xee, 0xc6, 0x3c, 0x40, 0xf5, 0x9c, 0x91, 0x48, 0xf2, 0x9c, 0x83, 0x40,
+ 0x1b, 0xe4, 0x49, 0xe2, 0x29, 0x0a, 0x94, 0x65, 0x9e, 0x69, 0xab, 0x5b, 0xba, 0x6f, 0x19, 0x52,
+ 0xed, 0xc8, 0x91, 0xa3, 0x55, 0xe5, 0x6e, 0x13, 0x01, 0x0b, 0xfc, 0x16, 0x05, 0x76, 0x94, 0xaa,
+ 0xae, 0x98, 0x2b, 0x55, 0x1d, 0x59, 0x80, 0xb2, 0xb3, 0x4b, 0xf7, 0x8f, 0x77, 0xd8, 0x9f, 0xef,
+ 0xc2, 0x6e, 0xdf, 0xa2, 0xfb, 0xc8, 0x2b, 0x6b, 0x3f, 0x28, 0x02, 0xb0, 0xc7, 0x3f, 0x9a, 0x2f,
+ 0xe6, 0xb7, 0xa1, 0xea, 0xf7, 0xb8, 0xd5, 0x44, 0x2a, 0x4c, 0x51, 0xf8, 0x96, 0x28, 0x46, 0x45,
+ 0x27, 0xcf, 0x41, 0xe5, 0xbd, 0x1e, 0xed, 0xa9, 0xc0, 0x82, 0x50, 0x71, 0xff, 0x22, 0x2b, 0x44,
+ 0x41, 0x3b, 0x3d, 0xbb, 0xaa, 0xf2, 0xd9, 0x54, 0x4e, 0xcb, 0x67, 0x53, 0x87, 0xea, 0x6d, 0x97,
+ 0x47, 0x83, 0x6a, 0xff, 0xb5, 0x08, 0x10, 0x45, 0xdb, 0x91, 0xef, 0x14, 0xe0, 0x7c, 0xf8, 0xc1,
+ 0x05, 0x62, 0xff, 0xc5, 0xd3, 0x22, 0xe7, 0xf6, 0xdf, 0x64, 0x7d, 0xec, 0x7c, 0x06, 0x5a, 0xcf,
+ 0x12, 0x87, 0xd9, 0xad, 0x20, 0x08, 0x35, 0xda, 0xe9, 0x06, 0xfb, 0x8b, 0x96, 0x27, 0x47, 0x60,
+ 0x66, 0x50, 0xe7, 0x75, 0xc9, 0x23, 0xaa, 0x4a, 0x23, 0x01, 0xff, 0x88, 0x14, 0x05, 0x43, 0x1c,
+ 0xb2, 0x03, 0x35, 0xc7, 0x7d, 0xc7, 0x67, 0xdd, 0x21, 0x87, 0xe3, 0x1b, 0xc3, 0x77, 0xb9, 0xe8,
+ 0x56, 0x61, 0xef, 0x97, 0x37, 0x58, 0x75, 0x64, 0x67, 0x7f, 0xbb, 0x08, 0x67, 0x33, 0xfa, 0x81,
+ 0xbc, 0x01, 0x93, 0x32, 0xb0, 0x31, 0xca, 0x0f, 0x5e, 0x88, 0xf2, 0x83, 0xb7, 0x52, 0x34, 0xec,
+ 0xe3, 0x26, 0xef, 0x00, 0xe8, 0x86, 0x41, 0x7d, 0x7f, 0xcd, 0x35, 0xd5, 0x7e, 0xe0, 0x75, 0xa6,
+ 0xbe, 0xcc, 0x87, 0xa5, 0x0f, 0x0e, 0xa6, 0x7f, 0x2f, 0x2b, 0x56, 0x39, 0xd5, 0xcf, 0x51, 0x05,
+ 0x8c, 0x41, 0x92, 0xaf, 0x00, 0x88, 0x4d, 0x78, 0x98, 0x4e, 0xe1, 0x11, 0x96, 0xab, 0x19, 0x95,
+ 0xb8, 0x6a, 0xe6, 0x8b, 0x3d, 0xdd, 0x09, 0xac, 0x60, 0x5f, 0x64, 0xaf, 0xb9, 0x1b, 0xa2, 0x60,
+ 0x0c, 0x51, 0xfb, 0xa7, 0x45, 0xa8, 0x29, 0x9b, 0xf9, 0x63, 0x30, 0x94, 0xb6, 0x13, 0x86, 0xd2,
+ 0x13, 0x8a, 0x4e, 0xce, 0x32, 0x93, 0xba, 0x29, 0x33, 0xe9, 0x72, 0x7e, 0x51, 0x0f, 0x37, 0x92,
+ 0x7e, 0xbf, 0x08, 0xe3, 0x8a, 0x35, 0xaf, 0x89, 0xf4, 0xf3, 0x30, 0x21, 0xa2, 0x0a, 0xd6, 0xf4,
+ 0xfb, 0x22, 0x91, 0x0f, 0xef, 0xb0, 0xb2, 0x08, 0x08, 0x6e, 0x26, 0x49, 0x98, 0xe6, 0x65, 0xc3,
+ 0x5a, 0x14, 0x6d, 0xb2, 0x4d, 0x98, 0xf0, 0x43, 0x8a, 0xfd, 0x26, 0x1f, 0xd6, 0xcd, 0x14, 0x0d,
+ 0xfb, 0xb8, 0xd3, 0x36, 0xda, 0xf2, 0x29, 0xd8, 0x68, 0xff, 0x5d, 0x01, 0x46, 0xa3, 0xfe, 0x3a,
+ 0x75, 0x0b, 0xed, 0x76, 0xd2, 0x42, 0x3b, 0x9f, 0x7b, 0x38, 0x0c, 0xb0, 0xcf, 0xfe, 0xe5, 0x2a,
+ 0x24, 0x82, 0xe4, 0xc9, 0x16, 0x5c, 0xb2, 0x32, 0x43, 0xfd, 0x62, 0xb3, 0x4d, 0x78, 0xea, 0x7b,
+ 0x65, 0x20, 0x27, 0x3e, 0x04, 0x85, 0xf4, 0xa0, 0xb6, 0x47, 0xbd, 0xc0, 0x32, 0xa8, 0x7a, 0xbe,
+ 0xe5, 0xdc, 0x2a, 0x99, 0xb4, 0x42, 0x87, 0x7d, 0x7a, 0x57, 0x0a, 0xc0, 0x50, 0x14, 0xd9, 0x82,
+ 0x0a, 0x35, 0xdb, 0x54, 0xa5, 0x56, 0xca, 0x99, 0xb8, 0x34, 0xec, 0x4f, 0x76, 0xe7, 0xa3, 0x80,
+ 0x26, 0x3e, 0xd4, 0x6d, 0xe5, 0x65, 0x94, 0xe3, 0x70, 0x78, 0x05, 0x2b, 0xf4, 0x57, 0x46, 0x59,
+ 0x17, 0xc2, 0x22, 0x8c, 0xe4, 0x90, 0xdd, 0xd0, 0xdc, 0x59, 0x39, 0xa1, 0xc9, 0xe3, 0x21, 0xc6,
+ 0x4e, 0x1f, 0xea, 0xf7, 0xf4, 0x80, 0x7a, 0x1d, 0xdd, 0xdb, 0x95, 0xbb, 0x8d, 0xe1, 0x9f, 0xf0,
+ 0x4d, 0x85, 0x14, 0x3d, 0x61, 0x58, 0x84, 0x91, 0x1c, 0xe2, 0x42, 0x3d, 0x90, 0xea, 0xb3, 0xb2,
+ 0xe9, 0x0e, 0x2f, 0x54, 0x29, 0xe2, 0xbe, 0x0c, 0x96, 0x57, 0xb7, 0x18, 0xc9, 0x20, 0x7b, 0x89,
+ 0x2c, 0xd7, 0x22, 0xb7, 0x79, 0x33, 0x87, 0x6f, 0x40, 0x42, 0x45, 0xcb, 0x4d, 0x76, 0xb6, 0x6c,
+ 0xed, 0x7f, 0x56, 0xa2, 0x69, 0xf9, 0x71, 0xdb, 0x09, 0x5f, 0x4a, 0xda, 0x09, 0xaf, 0xa4, 0xed,
+ 0x84, 0x29, 0x67, 0xf5, 0xf1, 0xc3, 0x6b, 0x53, 0xe6, 0xb5, 0xf2, 0x29, 0x98, 0xd7, 0x5e, 0x84,
+ 0xc6, 0x1e, 0x9f, 0x09, 0x44, 0x9e, 0xa6, 0x0a, 0x5f, 0x46, 0xf8, 0xcc, 0x7e, 0x37, 0x2a, 0xc6,
+ 0x38, 0x0f, 0xab, 0x22, 0xff, 0xeb, 0x11, 0x26, 0xba, 0x95, 0x55, 0x5a, 0x51, 0x31, 0xc6, 0x79,
+ 0x78, 0x64, 0x9e, 0xe5, 0xec, 0x8a, 0x0a, 0x55, 0x5e, 0x41, 0x44, 0xe6, 0xa9, 0x42, 0x8c, 0xe8,
+ 0xe4, 0x1a, 0xd4, 0x7a, 0xe6, 0xb6, 0xe0, 0xad, 0x71, 0x5e, 0xae, 0x61, 0x6e, 0x2e, 0x2e, 0xc9,
+ 0xbc, 0x51, 0x8a, 0xca, 0x5a, 0xd2, 0xd1, 0xbb, 0x8a, 0xc0, 0xf7, 0x86, 0xb2, 0x25, 0x6b, 0x51,
+ 0x31, 0xc6, 0x79, 0xc8, 0x1f, 0xc0, 0xb8, 0x47, 0xcd, 0x9e, 0x41, 0xc3, 0x5a, 0xc0, 0x6b, 0x11,
+ 0xf1, 0x03, 0x93, 0x38, 0x05, 0x53, 0x9c, 0x03, 0x8c, 0x84, 0x8d, 0xa1, 0x8c, 0x84, 0x5f, 0x80,
+ 0x71, 0xd3, 0xd3, 0x2d, 0x87, 0x9a, 0x77, 0x1c, 0x1e, 0x91, 0x20, 0xe3, 0x03, 0x43, 0x0b, 0xf9,
+ 0x62, 0x82, 0x8a, 0x29, 0x6e, 0xed, 0xbf, 0x17, 0x80, 0xf4, 0x47, 0xc2, 0x93, 0x1d, 0x18, 0x71,
+ 0xb8, 0xf5, 0x2c, 0x77, 0x6a, 0xed, 0x98, 0x11, 0x4e, 0x4c, 0x6b, 0xb2, 0x40, 0xe2, 0x13, 0x07,
+ 0x6a, 0xf4, 0x7e, 0x40, 0x3d, 0x27, 0x3c, 0x19, 0x73, 0x32, 0x69, 0xbc, 0xc5, 0x6e, 0x42, 0x22,
+ 0x63, 0x28, 0x43, 0xfb, 0x79, 0x11, 0x1a, 0x31, 0xbe, 0x47, 0x6d, 0x4a, 0xf9, 0xe1, 0x7c, 0x61,
+ 0xb4, 0xda, 0xf4, 0x6c, 0xf9, 0x85, 0xc6, 0x0e, 0xe7, 0x4b, 0x12, 0xae, 0x62, 0x9c, 0x8f, 0xcc,
+ 0x01, 0x74, 0x74, 0x3f, 0xa0, 0x1e, 0x5f, 0xbd, 0x53, 0x47, 0xe2, 0xd7, 0x42, 0x0a, 0xc6, 0xb8,
+ 0xc8, 0x55, 0x99, 0x88, 0xbd, 0x9c, 0x4c, 0x61, 0x38, 0x20, 0xcb, 0x7a, 0xe5, 0x04, 0xb2, 0xac,
+ 0x93, 0x36, 0x4c, 0xaa, 0x56, 0x2b, 0xea, 0xf1, 0x12, 0xdc, 0x89, 0xfd, 0x4f, 0x0a, 0x02, 0xfb,
+ 0x40, 0xb5, 0x1f, 0x14, 0x60, 0x2c, 0x61, 0x32, 0x11, 0xc9, 0x07, 0xd5, 0x39, 0x8e, 0x44, 0xf2,
+ 0xc1, 0xd8, 0xf1, 0x8b, 0x17, 0x60, 0x44, 0x74, 0x50, 0x3a, 0x3c, 0x53, 0x74, 0x21, 0x4a, 0x2a,
+ 0x9b, 0x0b, 0xa5, 0x51, 0x36, 0x3d, 0x17, 0x4a, 0xab, 0x2d, 0x2a, 0xba, 0xf0, 0x75, 0x88, 0xd6,
+ 0xc9, 0x9e, 0x8e, 0xf9, 0x3a, 0x44, 0x39, 0x86, 0x1c, 0xda, 0x0f, 0x79, 0xbb, 0x03, 0x6f, 0x3f,
+ 0xdc, 0x0b, 0xb6, 0xa1, 0x2a, 0x43, 0xf2, 0xe4, 0xa7, 0xf1, 0x46, 0x0e, 0x3b, 0x0e, 0xc7, 0x91,
+ 0xc1, 0x67, 0xba, 0xb1, 0x7b, 0x67, 0x7b, 0x1b, 0x15, 0x3a, 0xb9, 0x0e, 0x75, 0xd7, 0x59, 0xd2,
+ 0x2d, 0xbb, 0xe7, 0xa9, 0x95, 0xe1, 0xb7, 0xd8, 0x5c, 0x77, 0x47, 0x15, 0x3e, 0x38, 0x98, 0xbe,
+ 0x10, 0xde, 0x24, 0x1a, 0x89, 0x51, 0x4d, 0xed, 0xcf, 0x17, 0xe0, 0x3c, 0xba, 0xb6, 0x6d, 0x39,
+ 0xed, 0xa4, 0xb3, 0x8c, 0xd8, 0x30, 0xde, 0xd1, 0xef, 0x6f, 0x3a, 0xfa, 0x9e, 0x6e, 0xd9, 0xfa,
+ 0x96, 0x4d, 0x1f, 0xb9, 0x97, 0xeb, 0x05, 0x96, 0x3d, 0x23, 0x7e, 0x4c, 0x37, 0xb3, 0xe2, 0x04,
+ 0x77, 0xbc, 0x56, 0xe0, 0x59, 0x4e, 0x5b, 0x4c, 0x7a, 0x6b, 0x09, 0x2c, 0x4c, 0x61, 0x6b, 0xbf,
+ 0x28, 0x01, 0x0f, 0x0b, 0x23, 0xaf, 0x42, 0xbd, 0x43, 0x8d, 0x1d, 0xdd, 0xb1, 0x7c, 0x95, 0xc6,
+ 0xf5, 0x22, 0x7b, 0xae, 0x35, 0x55, 0xf8, 0x80, 0xbd, 0x8a, 0xf9, 0xd6, 0x2a, 0x3f, 0x79, 0x11,
+ 0xf1, 0x12, 0x03, 0x46, 0xda, 0xbe, 0xaf, 0x77, 0xad, 0xdc, 0x51, 0x09, 0x22, 0x6d, 0xa6, 0x98,
+ 0x8e, 0xc4, 0x35, 0x4a, 0x68, 0x62, 0x40, 0xa5, 0x6b, 0xeb, 0x96, 0x93, 0xfb, 0x47, 0x4a, 0xec,
+ 0x09, 0xd6, 0x19, 0x92, 0x30, 0xae, 0xf1, 0x4b, 0x14, 0xd8, 0xa4, 0x07, 0x0d, 0xdf, 0xf0, 0xf4,
+ 0x8e, 0xbf, 0xa3, 0xcf, 0xbd, 0xfc, 0x4a, 0x6e, 0x75, 0x35, 0x12, 0x25, 0x56, 0xcf, 0x05, 0x9c,
+ 0x5f, 0x6b, 0xdd, 0x98, 0x9f, 0x7b, 0xf9, 0x15, 0x8c, 0xcb, 0x89, 0x8b, 0x7d, 0xf9, 0xc5, 0x39,
+ 0x39, 0x83, 0x9c, 0xb8, 0xd8, 0x97, 0x5f, 0x9c, 0xc3, 0xb8, 0x1c, 0xed, 0x7f, 0x15, 0xa0, 0x1e,
+ 0xf2, 0x92, 0x4d, 0x00, 0x36, 0x97, 0xc9, 0x44, 0x97, 0xc7, 0xfa, 0xe9, 0x04, 0xb7, 0x4f, 0x6c,
+ 0x86, 0x95, 0x31, 0x06, 0x94, 0x91, 0x09, 0xb4, 0x78, 0xd2, 0x99, 0x40, 0x67, 0xa1, 0xbe, 0xa3,
+ 0x3b, 0xa6, 0xbf, 0xa3, 0xef, 0x8a, 0x29, 0x3d, 0x96, 0x1b, 0xf7, 0x86, 0x22, 0x60, 0xc4, 0xa3,
+ 0xfd, 0xe3, 0x11, 0x10, 0xa1, 0x04, 0x6c, 0xd2, 0x31, 0x2d, 0x5f, 0xc4, 0xb2, 0x17, 0x78, 0xcd,
+ 0x70, 0xd2, 0x59, 0x94, 0xe5, 0x18, 0x72, 0x90, 0x8b, 0x50, 0xea, 0x58, 0x8e, 0xf4, 0x3d, 0x71,
+ 0xd3, 0xe3, 0x9a, 0xe5, 0x20, 0x2b, 0xe3, 0x24, 0xfd, 0xbe, 0x0c, 0x43, 0x14, 0x24, 0xfd, 0x3e,
+ 0xb2, 0x32, 0xf2, 0x79, 0x98, 0xb0, 0x5d, 0x77, 0x97, 0x4d, 0x1f, 0x2a, 0x5a, 0x51, 0xf8, 0x81,
+ 0xb9, 0x31, 0x60, 0x35, 0x49, 0xc2, 0x34, 0x2f, 0xd9, 0x84, 0xa7, 0xdf, 0xa7, 0x9e, 0x2b, 0xe7,
+ 0xcb, 0x96, 0x4d, 0x69, 0x57, 0xc1, 0x08, 0x65, 0x8e, 0x07, 0x3d, 0xfe, 0x71, 0x36, 0x0b, 0x0e,
+ 0xaa, 0xcb, 0xc3, 0xa7, 0x75, 0xaf, 0x4d, 0x83, 0x75, 0xcf, 0x35, 0xa8, 0xef, 0x5b, 0x4e, 0x5b,
+ 0xc1, 0x8e, 0x44, 0xb0, 0x1b, 0xd9, 0x2c, 0x38, 0xa8, 0x2e, 0x79, 0x0b, 0xa6, 0x04, 0x49, 0xa8,
+ 0x2d, 0xf3, 0x62, 0x9a, 0xb1, 0x6c, 0xf5, 0xff, 0xc1, 0x31, 0xe1, 0xe1, 0xd9, 0x18, 0xc0, 0x83,
+ 0x03, 0x6b, 0x93, 0x9b, 0x30, 0xa9, 0xfc, 0x7b, 0xeb, 0xd4, 0x6b, 0x85, 0xe1, 0x25, 0x63, 0xcd,
+ 0x2b, 0x6c, 0xe7, 0xbd, 0x48, 0xbb, 0x1e, 0x35, 0xe2, 0x7e, 0x52, 0xc5, 0x85, 0x7d, 0xf5, 0x08,
+ 0xc2, 0x05, 0x1e, 0x43, 0xb2, 0xd9, 0x5d, 0x70, 0x5d, 0xdb, 0x74, 0xef, 0x39, 0xea, 0xd9, 0x85,
+ 0x8a, 0xc9, 0x5d, 0x7a, 0xad, 0x4c, 0x0e, 0x1c, 0x50, 0x93, 0x3d, 0x39, 0xa7, 0x2c, 0xba, 0xf7,
+ 0x9c, 0x34, 0x2a, 0x44, 0x4f, 0xde, 0x1a, 0xc0, 0x83, 0x03, 0x6b, 0x93, 0x25, 0x20, 0xe9, 0x27,
+ 0xd8, 0xec, 0x4a, 0xa7, 0xf3, 0x05, 0x91, 0xb3, 0x26, 0x4d, 0xc5, 0x8c, 0x1a, 0x64, 0x15, 0xce,
+ 0xa5, 0x4b, 0x99, 0x38, 0xe9, 0x7f, 0xe6, 0xd9, 0x6a, 0x31, 0x83, 0x8e, 0x99, 0xb5, 0xb4, 0x7f,
+ 0x52, 0x84, 0xb1, 0x44, 0x92, 0x83, 0x27, 0xee, 0x30, 0x39, 0xdb, 0x0b, 0x74, 0xfc, 0xf6, 0xca,
+ 0xe2, 0x0d, 0xaa, 0x9b, 0xd4, 0xbb, 0x45, 0x55, 0x42, 0x0a, 0xb1, 0x2c, 0x26, 0x28, 0x98, 0xe2,
+ 0x24, 0xdb, 0x50, 0x11, 0x96, 0xed, 0xbc, 0xbf, 0x2f, 0x51, 0x7d, 0xc4, 0xcd, 0xdb, 0xf2, 0x9f,
+ 0x3f, 0xae, 0x47, 0x51, 0xc0, 0x6b, 0x01, 0x8c, 0xc6, 0x39, 0xd8, 0x44, 0x12, 0xa9, 0xbd, 0xd5,
+ 0x84, 0xca, 0xbb, 0x02, 0xa5, 0x20, 0x18, 0xf6, 0x98, 0xba, 0xf0, 0x94, 0x6c, 0xac, 0x22, 0xc3,
+ 0xd0, 0xb6, 0xd9, 0xbb, 0xf3, 0x7d, 0xcb, 0x75, 0x64, 0xce, 0xf2, 0x4d, 0xa8, 0x06, 0xd2, 0x58,
+ 0x38, 0xdc, 0x31, 0x7b, 0xae, 0x2b, 0x29, 0x43, 0xa1, 0xc2, 0xd2, 0xfe, 0x7d, 0x11, 0xea, 0xe1,
+ 0xc6, 0xfe, 0x08, 0xb9, 0xc0, 0x5d, 0xa8, 0x87, 0x31, 0x70, 0xb9, 0xff, 0xcd, 0x18, 0x85, 0x66,
+ 0xf1, 0xbd, 0x68, 0x78, 0x8b, 0x91, 0x8c, 0x78, 0x7c, 0x5d, 0x29, 0x47, 0x7c, 0x5d, 0x17, 0xaa,
+ 0x81, 0x67, 0xb5, 0xdb, 0x72, 0x97, 0x90, 0x27, 0xc0, 0x2e, 0xec, 0xae, 0x0d, 0x01, 0x28, 0x7b,
+ 0x56, 0xdc, 0xa0, 0x12, 0xa3, 0xbd, 0x0b, 0x93, 0x69, 0x4e, 0xae, 0x42, 0x1b, 0x3b, 0xd4, 0xec,
+ 0xd9, 0xaa, 0x8f, 0x23, 0x15, 0x5a, 0x96, 0x63, 0xc8, 0xc1, 0xb6, 0xe1, 0xec, 0x35, 0xbd, 0xef,
+ 0x3a, 0x4a, 0x8d, 0xe5, 0xbb, 0x91, 0x0d, 0x59, 0x86, 0x21, 0x55, 0xfb, 0x2f, 0x25, 0xb8, 0x18,
+ 0x99, 0x67, 0xd6, 0x74, 0x47, 0x6f, 0x1f, 0xe1, 0x87, 0x7c, 0x9f, 0x1d, 0x5c, 0x3a, 0xee, 0x0f,
+ 0x1d, 0x4a, 0x4f, 0xc0, 0x0f, 0x1d, 0xfe, 0x4f, 0x11, 0x78, 0xbc, 0x2e, 0xf9, 0x1a, 0x8c, 0xea,
+ 0xb1, 0x7f, 0xb1, 0xca, 0xd7, 0x79, 0x3d, 0xf7, 0xeb, 0xe4, 0x61, 0xc1, 0x61, 0xc8, 0x56, 0xbc,
+ 0x14, 0x13, 0x02, 0x89, 0x0b, 0xb5, 0x6d, 0xdd, 0xb6, 0x99, 0x2e, 0x94, 0xdb, 0xdd, 0x94, 0x10,
+ 0xce, 0x87, 0xf9, 0x92, 0x84, 0xc6, 0x50, 0x08, 0xf9, 0xa0, 0x00, 0x63, 0x5e, 0x7c, 0xbb, 0x26,
+ 0x5f, 0x48, 0x9e, 0x60, 0x84, 0x18, 0x5a, 0x3c, 0x40, 0x2c, 0xbe, 0x27, 0x4c, 0xca, 0xd4, 0xfe,
+ 0x73, 0x01, 0xc6, 0x5a, 0xb6, 0x65, 0x5a, 0x4e, 0xfb, 0x14, 0xff, 0x27, 0x71, 0x07, 0x2a, 0xbe,
+ 0x6d, 0x99, 0x74, 0xc8, 0xd5, 0x44, 0xac, 0x63, 0x0c, 0x00, 0x05, 0x4e, 0xf2, 0x07, 0x15, 0xa5,
+ 0x23, 0xfc, 0xa0, 0xe2, 0x57, 0x23, 0x20, 0x23, 0xcf, 0x49, 0x0f, 0xea, 0x6d, 0x95, 0xf7, 0x5e,
+ 0x3e, 0xe3, 0x8d, 0x1c, 0x39, 0x13, 0x13, 0x19, 0xf4, 0xc5, 0xdc, 0x1f, 0x16, 0x62, 0x24, 0x89,
+ 0xd0, 0xe4, 0x4f, 0x80, 0x17, 0x73, 0xfe, 0x04, 0x58, 0x88, 0xeb, 0xff, 0x0d, 0xb0, 0x0e, 0xe5,
+ 0x9d, 0x20, 0xe8, 0xca, 0xc1, 0x34, 0xfc, 0xd1, 0x82, 0x28, 0x6d, 0x8f, 0xd0, 0x89, 0xd8, 0x3d,
+ 0x72, 0x68, 0x26, 0xc2, 0xd1, 0xc3, 0x5f, 0xad, 0x2d, 0xe4, 0x0a, 0x7c, 0x88, 0x8b, 0x60, 0xf7,
+ 0xc8, 0xa1, 0xc9, 0x57, 0xa1, 0x11, 0x78, 0xba, 0xe3, 0x6f, 0xbb, 0x5e, 0x87, 0x7a, 0x72, 0x8f,
+ 0xba, 0x94, 0xe3, 0x3f, 0xb8, 0x1b, 0x11, 0x9a, 0xf0, 0xa8, 0x26, 0x8a, 0x30, 0x2e, 0x8d, 0xec,
+ 0x42, 0xad, 0x67, 0x8a, 0x86, 0x49, 0x33, 0xd8, 0x7c, 0x9e, 0x5f, 0x1b, 0xc7, 0xc2, 0x1a, 0xd4,
+ 0x1d, 0x86, 0x02, 0x92, 0x7f, 0x15, 0xac, 0x9e, 0xd4, 0x5f, 0x05, 0xe3, 0xa3, 0x31, 0x2b, 0xa7,
+ 0x08, 0xe9, 0x48, 0xbd, 0xd6, 0x69, 0xcb, 0xa8, 0xac, 0xa5, 0xdc, 0x2a, 0xa7, 0x10, 0xd9, 0x08,
+ 0x75, 0x63, 0xa7, 0x8d, 0x4a, 0x86, 0xd6, 0x01, 0xe9, 0xed, 0x20, 0x46, 0xe2, 0xdf, 0x3b, 0xe2,
+ 0xa0, 0xdb, 0xec, 0xd1, 0xe6, 0x83, 0xf0, 0x27, 0x30, 0xb1, 0xdc, 0xdf, 0x99, 0x3f, 0xd9, 0xd1,
+ 0xfe, 0x43, 0x11, 0x4a, 0x1b, 0xab, 0x2d, 0x91, 0xcf, 0x93, 0xff, 0xd8, 0x8a, 0xb6, 0x76, 0xad,
+ 0xee, 0x5d, 0xea, 0x59, 0xdb, 0xfb, 0x72, 0xeb, 0x1d, 0xcb, 0xe7, 0x99, 0xe6, 0xc0, 0x8c, 0x5a,
+ 0xe4, 0x6d, 0x18, 0x35, 0xf4, 0x05, 0xea, 0x05, 0xc3, 0x18, 0x16, 0xf8, 0x89, 0xde, 0x85, 0xf9,
+ 0xa8, 0x3a, 0x26, 0xc0, 0xc8, 0x26, 0x80, 0x11, 0x41, 0x97, 0x8e, 0x6d, 0x0e, 0x89, 0x01, 0xc7,
+ 0x80, 0x08, 0x42, 0x7d, 0x97, 0xb1, 0x72, 0xd4, 0xf2, 0x71, 0x50, 0xf9, 0xc8, 0xb9, 0xa5, 0xea,
+ 0x62, 0x04, 0xa3, 0x39, 0x30, 0x96, 0xf8, 0x21, 0x0f, 0xf9, 0x1c, 0xd4, 0xdc, 0x6e, 0x6c, 0x3a,
+ 0xad, 0xf3, 0xf8, 0xcf, 0xda, 0x1d, 0x59, 0xf6, 0xe0, 0x60, 0x7a, 0x6c, 0xd5, 0x6d, 0x5b, 0x86,
+ 0x2a, 0xc0, 0x90, 0x9d, 0x68, 0x30, 0xc2, 0x8f, 0xe1, 0xa9, 0xdf, 0xf1, 0xf0, 0xb5, 0x83, 0xff,
+ 0x31, 0xc3, 0x47, 0x49, 0xd1, 0xbe, 0x5e, 0x86, 0xc8, 0x47, 0x48, 0x7c, 0x18, 0x11, 0xc7, 0x0c,
+ 0xe4, 0xcc, 0x7d, 0xaa, 0x27, 0x1a, 0xa4, 0x28, 0xd2, 0x86, 0xd2, 0xbb, 0xee, 0x56, 0xee, 0x89,
+ 0x3b, 0x76, 0xfe, 0x5e, 0xd8, 0xca, 0x62, 0x05, 0xc8, 0x24, 0x90, 0xbf, 0x51, 0x80, 0x33, 0x7e,
+ 0x5a, 0xf5, 0x95, 0xc3, 0x01, 0xf3, 0xeb, 0xf8, 0x69, 0x65, 0x5a, 0x06, 0xea, 0x0e, 0x22, 0x63,
+ 0x7f, 0x5b, 0x58, 0xff, 0x0b, 0xe7, 0x9d, 0x1c, 0x4e, 0xcb, 0x39, 0x7f, 0x22, 0x99, 0xec, 0xff,
+ 0x64, 0x19, 0x4a, 0x51, 0xda, 0x37, 0x8b, 0xd0, 0x88, 0xcd, 0xd6, 0xb9, 0xff, 0xf2, 0x74, 0x3f,
+ 0xf5, 0x97, 0xa7, 0xf5, 0xe1, 0x7d, 0xd9, 0x51, 0xab, 0x4e, 0xfb, 0x47, 0x4f, 0xff, 0xbc, 0x08,
+ 0xa5, 0xcd, 0xc5, 0xa5, 0xe4, 0xa6, 0xb5, 0xf0, 0x18, 0x36, 0xad, 0x3b, 0x50, 0xdd, 0xea, 0x59,
+ 0x76, 0x60, 0x39, 0xb9, 0x33, 0x84, 0xa8, 0x9f, 0x62, 0x49, 0x5f, 0x87, 0x40, 0x45, 0x05, 0x4f,
+ 0xda, 0x50, 0x6d, 0x8b, 0x14, 0x8d, 0xb9, 0x23, 0xfc, 0x64, 0xaa, 0x47, 0x21, 0x48, 0xde, 0xa0,
+ 0x42, 0xd7, 0xf6, 0x61, 0x64, 0x73, 0x51, 0xaa, 0xfd, 0x8f, 0xb7, 0x37, 0xb5, 0xaf, 0x42, 0xa8,
+ 0x05, 0x3c, 0x7e, 0xe1, 0xff, 0xad, 0x00, 0x49, 0xc5, 0xe7, 0xf1, 0x8f, 0xa6, 0xdd, 0xf4, 0x68,
+ 0x5a, 0x3c, 0x89, 0x8f, 0x2f, 0x7b, 0x40, 0x69, 0xff, 0xb6, 0x00, 0xa9, 0xb3, 0x61, 0xe4, 0x15,
+ 0x99, 0xed, 0x2b, 0x19, 0x4a, 0xa5, 0xb2, 0x7d, 0x91, 0x24, 0x77, 0x2c, 0xeb, 0xd7, 0x87, 0x6c,
+ 0xbb, 0x16, 0x77, 0xa0, 0xc9, 0xe6, 0xdf, 0x1e, 0x7e, 0xbb, 0x96, 0xe5, 0x8e, 0x93, 0xe1, 0x7e,
+ 0x71, 0x12, 0x26, 0xe5, 0x6a, 0xff, 0xa8, 0x08, 0x23, 0x8f, 0xed, 0xa8, 0x3a, 0x4d, 0x44, 0x60,
+ 0x2e, 0xe4, 0x9c, 0xed, 0x07, 0xc6, 0x5f, 0x76, 0x52, 0xf1, 0x97, 0x79, 0xff, 0x4d, 0xfc, 0x88,
+ 0xe8, 0xcb, 0x7f, 0x5d, 0x00, 0xb9, 0xd6, 0xac, 0x38, 0x7e, 0xa0, 0x3b, 0x06, 0x25, 0x46, 0xb8,
+ 0xb0, 0xe5, 0x0d, 0xf3, 0x91, 0xa1, 0x70, 0x42, 0x97, 0xe1, 0xd7, 0x6a, 0x21, 0x23, 0xbf, 0x0b,
+ 0xb5, 0x1d, 0xd7, 0x0f, 0xf8, 0xe2, 0x55, 0x4c, 0x9a, 0xcc, 0x6e, 0xc8, 0x72, 0x0c, 0x39, 0xd2,
+ 0xee, 0xec, 0xca, 0x60, 0x77, 0xb6, 0xf6, 0xbd, 0x22, 0x8c, 0x7e, 0x5a, 0xce, 0xdb, 0x67, 0xc5,
+ 0xab, 0x96, 0x72, 0xc6, 0xab, 0x96, 0x8f, 0x13, 0xaf, 0xaa, 0xfd, 0xa4, 0x00, 0xf0, 0xd8, 0x0e,
+ 0xfb, 0x9b, 0xc9, 0x50, 0xd2, 0xdc, 0xe3, 0x2a, 0x3b, 0x90, 0xf4, 0x1f, 0x54, 0xd4, 0x23, 0xf1,
+ 0x30, 0xd2, 0x0f, 0x0b, 0x30, 0xae, 0x27, 0x42, 0x33, 0x73, 0xeb, 0xcb, 0xa9, 0x48, 0xcf, 0x30,
+ 0xb2, 0x28, 0x59, 0x8e, 0x29, 0xb1, 0xe4, 0xb5, 0x28, 0xd1, 0xf4, 0xed, 0x68, 0xd8, 0xf7, 0x65,
+ 0x88, 0xe6, 0xba, 0x5b, 0x82, 0xf3, 0x11, 0xa1, 0xb0, 0xa5, 0x13, 0x09, 0x85, 0x8d, 0x1f, 0xf2,
+ 0x2b, 0x3f, 0xf4, 0x90, 0xdf, 0x1e, 0xd4, 0xb7, 0x3d, 0xb7, 0xc3, 0xa3, 0x4d, 0xe5, 0x5f, 0x8d,
+ 0xaf, 0xe7, 0x58, 0x28, 0xa3, 0xff, 0xf9, 0x47, 0x86, 0xab, 0x25, 0x85, 0x8f, 0x91, 0x28, 0x6e,
+ 0xeb, 0x77, 0x85, 0xd4, 0x91, 0x93, 0x94, 0x1a, 0xce, 0x25, 0x1b, 0x02, 0x1d, 0x95, 0x98, 0x64,
+ 0x84, 0x69, 0xf5, 0xf1, 0x44, 0x98, 0x6a, 0x7f, 0xa9, 0xaa, 0x26, 0xb0, 0x27, 0x2e, 0xa7, 0xe9,
+ 0x67, 0x47, 0xb3, 0xdb, 0xb4, 0xef, 0xdc, 0x74, 0xed, 0x31, 0x9e, 0x9b, 0xae, 0x9f, 0xcc, 0xb9,
+ 0x69, 0xc8, 0x77, 0x6e, 0xba, 0x71, 0x42, 0xe7, 0xa6, 0x47, 0x4f, 0xea, 0xdc, 0xf4, 0xd8, 0x50,
+ 0xe7, 0xa6, 0xc7, 0x8f, 0x74, 0x6e, 0xfa, 0xa0, 0x04, 0xa9, 0xcd, 0xf8, 0x67, 0x8e, 0xb7, 0xff,
+ 0xa7, 0x1c, 0x6f, 0x1f, 0x15, 0x21, 0x9a, 0x88, 0x8f, 0x19, 0x98, 0xf4, 0x16, 0xd4, 0x3a, 0xfa,
+ 0xfd, 0x45, 0x6a, 0xeb, 0xfb, 0x79, 0x7e, 0x05, 0xbc, 0x26, 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80,
+ 0x15, 0xa6, 0xe3, 0xcf, 0xed, 0xc2, 0x88, 0x32, 0xfb, 0x0b, 0x23, 0x69, 0x74, 0x8f, 0x31, 0x31,
+ 0xda, 0xbf, 0x2a, 0x82, 0xfc, 0x6f, 0x03, 0xa1, 0x50, 0xd9, 0xb6, 0xee, 0x53, 0x33, 0x77, 0xb8,
+ 0x73, 0xec, 0x07, 0xed, 0xc2, 0x47, 0xc3, 0x0b, 0x50, 0xa0, 0x73, 0xe3, 0xbb, 0xf0, 0xb9, 0xc9,
+ 0xfe, 0xcb, 0x61, 0x7c, 0x8f, 0xfb, 0xee, 0xa4, 0xf1, 0x5d, 0x14, 0xa1, 0x92, 0x21, 0x6c, 0xfd,
+ 0x3c, 0xfc, 0x22, 0xb7, 0x8b, 0x31, 0x11, 0xc6, 0xa1, 0x6c, 0xfd, 0xbe, 0x48, 0x9c, 0x20, 0x65,
+ 0x34, 0xbf, 0xfc, 0xe3, 0x9f, 0x5e, 0x79, 0xea, 0x27, 0x3f, 0xbd, 0xf2, 0xd4, 0xc7, 0x3f, 0xbd,
+ 0xf2, 0xd4, 0xd7, 0x0f, 0xaf, 0x14, 0x7e, 0x7c, 0x78, 0xa5, 0xf0, 0x93, 0xc3, 0x2b, 0x85, 0x8f,
+ 0x0f, 0xaf, 0x14, 0xfe, 0xe3, 0xe1, 0x95, 0xc2, 0x5f, 0xfd, 0x4f, 0x57, 0x9e, 0xfa, 0xe3, 0x57,
+ 0xa3, 0x26, 0xcc, 0xaa, 0x26, 0xcc, 0x2a, 0x81, 0xb3, 0xdd, 0xdd, 0xf6, 0x2c, 0x6b, 0x42, 0x54,
+ 0xa2, 0x9a, 0xf0, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xc4, 0xe2, 0xd0, 0x0e, 0xda, 0x9a, 0x00,
+ 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -3585,6 +3584,18 @@ func (m *AbstractVertex) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ {
+ size, err := m.UpdateStrategy.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1
+ i--
+ dAtA[i] = 0x82
if m.SideInputsContainerTemplate != nil {
{
size, err := m.SideInputsContainerTemplate.MarshalToSizedBuffer(dAtA[:i])
@@ -9142,24 +9153,24 @@ func (m *VertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
copy(dAtA[i:], m.UpdateHash)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.UpdateHash)))
i--
- dAtA[i] = 0x6a
+ dAtA[i] = 0x72
i -= len(m.CurrentHash)
copy(dAtA[i:], m.CurrentHash)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.CurrentHash)))
i--
- dAtA[i] = 0x62
+ dAtA[i] = 0x6a
+ i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReadyReplicas))
+ i--
+ dAtA[i] = 0x60
i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReplicas))
i--
dAtA[i] = 0x58
- i = encodeVarintGenerated(dAtA, i, uint64(m.CurrentReplicas))
- i--
- dAtA[i] = 0x50
i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas))
i--
- dAtA[i] = 0x48
+ dAtA[i] = 0x50
i = encodeVarintGenerated(dAtA, i, uint64(m.ObservedGeneration))
i--
- dAtA[i] = 0x40
+ dAtA[i] = 0x48
{
size, err := m.LastScaledAt.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
@@ -9169,22 +9180,25 @@ func (m *VertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
- dAtA[i] = 0x3a
+ dAtA[i] = 0x42
i -= len(m.Message)
copy(dAtA[i:], m.Message)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Message)))
i--
- dAtA[i] = 0x32
+ dAtA[i] = 0x3a
i -= len(m.Reason)
copy(dAtA[i:], m.Reason)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Reason)))
i--
- dAtA[i] = 0x2a
+ dAtA[i] = 0x32
i -= len(m.Selector)
copy(dAtA[i:], m.Selector)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Selector)))
i--
- dAtA[i] = 0x22
+ dAtA[i] = 0x2a
+ i = encodeVarintGenerated(dAtA, i, uint64(m.DesiredReplicas))
+ i--
+ dAtA[i] = 0x20
i = encodeVarintGenerated(dAtA, i, uint64(m.Replicas))
i--
dAtA[i] = 0x18
@@ -9547,6 +9561,8 @@ func (m *AbstractVertex) Size() (n int) {
l = m.SideInputsContainerTemplate.Size()
n += 1 + l + sovGenerated(uint64(l))
}
+ l = m.UpdateStrategy.Size()
+ n += 2 + l + sovGenerated(uint64(l))
return n
}
@@ -11540,6 +11556,7 @@ func (m *VertexStatus) Size() (n int) {
l = len(m.Phase)
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.Replicas))
+ n += 1 + sovGenerated(uint64(m.DesiredReplicas))
l = len(m.Selector)
n += 1 + l + sovGenerated(uint64(l))
l = len(m.Reason)
@@ -11550,8 +11567,8 @@ func (m *VertexStatus) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
n += 1 + sovGenerated(uint64(m.ObservedGeneration))
n += 1 + sovGenerated(uint64(m.ReadyReplicas))
- n += 1 + sovGenerated(uint64(m.CurrentReplicas))
n += 1 + sovGenerated(uint64(m.UpdatedReplicas))
+ n += 1 + sovGenerated(uint64(m.UpdatedReadyReplicas))
l = len(m.CurrentHash)
n += 1 + l + sovGenerated(uint64(l))
l = len(m.UpdateHash)
@@ -11719,6 +11736,7 @@ func (this *AbstractVertex) String() string {
`Partitions:` + valueToStringGenerated(this.Partitions) + `,`,
`SideInputs:` + fmt.Sprintf("%v", this.SideInputs) + `,`,
`SideInputsContainerTemplate:` + strings.Replace(this.SideInputsContainerTemplate.String(), "ContainerTemplate", "ContainerTemplate", 1) + `,`,
+ `UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "UpdateStrategy", "UpdateStrategy", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
@@ -13099,14 +13117,15 @@ func (this *VertexStatus) String() string {
`Status:` + strings.Replace(strings.Replace(this.Status.String(), "Status", "Status", 1), `&`, ``, 1) + `,`,
`Phase:` + fmt.Sprintf("%v", this.Phase) + `,`,
`Replicas:` + fmt.Sprintf("%v", this.Replicas) + `,`,
+ `DesiredReplicas:` + fmt.Sprintf("%v", this.DesiredReplicas) + `,`,
`Selector:` + fmt.Sprintf("%v", this.Selector) + `,`,
`Reason:` + fmt.Sprintf("%v", this.Reason) + `,`,
`Message:` + fmt.Sprintf("%v", this.Message) + `,`,
`LastScaledAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.LastScaledAt), "Time", "v11.Time", 1), `&`, ``, 1) + `,`,
`ObservedGeneration:` + fmt.Sprintf("%v", this.ObservedGeneration) + `,`,
`ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`,
- `CurrentReplicas:` + fmt.Sprintf("%v", this.CurrentReplicas) + `,`,
`UpdatedReplicas:` + fmt.Sprintf("%v", this.UpdatedReplicas) + `,`,
+ `UpdatedReadyReplicas:` + fmt.Sprintf("%v", this.UpdatedReadyReplicas) + `,`,
`CurrentHash:` + fmt.Sprintf("%v", this.CurrentHash) + `,`,
`UpdateHash:` + fmt.Sprintf("%v", this.UpdateHash) + `,`,
`}`,
@@ -14477,6 +14496,39 @@ func (m *AbstractVertex) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 16:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdateStrategy", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.UpdateStrategy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -31445,6 +31497,25 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
}
}
case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field DesiredReplicas", wireType)
+ }
+ m.DesiredReplicas = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.DesiredReplicas |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Selector", wireType)
}
@@ -31476,7 +31547,7 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
}
m.Selector = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 5:
+ case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType)
}
@@ -31508,7 +31579,7 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
}
m.Reason = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 6:
+ case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
}
@@ -31540,7 +31611,7 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
}
m.Message = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 7:
+ case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LastScaledAt", wireType)
}
@@ -31573,7 +31644,7 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
- case 8:
+ case 9:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ObservedGeneration", wireType)
}
@@ -31592,7 +31663,7 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
- case 9:
+ case 10:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ReadyReplicas", wireType)
}
@@ -31611,11 +31682,11 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
break
}
}
- case 10:
+ case 11:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field CurrentReplicas", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType)
}
- m.CurrentReplicas = 0
+ m.UpdatedReplicas = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
@@ -31625,16 +31696,16 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
- m.CurrentReplicas |= uint32(b&0x7F) << shift
+ m.UpdatedReplicas |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
- case 11:
+ case 12:
if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReadyReplicas", wireType)
}
- m.UpdatedReplicas = 0
+ m.UpdatedReadyReplicas = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
@@ -31644,12 +31715,12 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
- m.UpdatedReplicas |= uint32(b&0x7F) << shift
+ m.UpdatedReadyReplicas |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
- case 12:
+ case 13:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CurrentHash", wireType)
}
@@ -31681,7 +31752,7 @@ func (m *VertexStatus) Unmarshal(dAtA []byte) error {
}
m.CurrentHash = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
- case 13:
+ case 14:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field UpdateHash", wireType)
}
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 78e10457a0..c927033e7e 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -201,6 +201,11 @@ message AbstractVertex {
// Container template for the side inputs watcher container.
// +optional
optional ContainerTemplate sideInputsContainerTemplate = 15;
+
+ // The strategy to use to replace existing pods with new ones.
+ // +kubebuilder:default={"type": "RollingUpdate", "rollingUpdate": {"maxUnavailable": "25%"}}
+ // +optional
+ optional UpdateStrategy updateStrategy = 16;
}
message Authorization {
@@ -1642,7 +1647,7 @@ message UpdateStrategy {
// +kubebuilder:subresource:status
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
-// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
+// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.status.desiredReplicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
@@ -1732,38 +1737,42 @@ message VertexStatus {
// +optional
optional uint32 replicas = 3;
+ // The number of desired replicas.
+ // +optional
+ optional uint32 desiredReplicas = 4;
+
// +optional
- optional string selector = 4;
+ optional string selector = 5;
// +optional
- optional string reason = 5;
+ optional string reason = 6;
// +optional
- optional string message = 6;
+ optional string message = 7;
// Time of last scaling operation.
// +optional
- optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastScaledAt = 7;
+ optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastScaledAt = 8;
// The generation observed by the Vertex controller.
// +optional
- optional int64 observedGeneration = 8;
+ optional int64 observedGeneration = 9;
// The number of pods targeted by this Vertex with a Ready Condition.
// +optional
- optional uint32 readyReplicas = 9;
-
- // The number of Pods created by the controller from the Vertex version indicated by currentHash.
- optional uint32 currentReplicas = 10;
+ optional uint32 readyReplicas = 10;
// The number of Pods created by the controller from the Vertex version indicated by updateHash.
optional uint32 updatedReplicas = 11;
- // If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).
- optional string currentHash = 12;
+ // The number of ready Pods created by the controller from the Vertex version indicated by updateHash.
+ optional uint32 updatedReadyReplicas = 12;
+
+ // If not empty, indicates the current version of the Vertex used to generate Pods.
+ optional string currentHash = 13;
- // If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
- optional string updateHash = 13;
+ // If not empty, indicates the updated version of the Vertex used to generate Pods.
+ optional string updateHash = 14;
}
message VertexTemplate {
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
index aeb809db05..665acd8b32 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
@@ -18,6 +18,7 @@ package v1alpha1
import (
"testing"
+ "time"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
@@ -88,6 +89,31 @@ func TestMonoVertex_MarkPhaseRunning(t *testing.T) {
}
}
+func TestMonoVertex_MarkDaemonUnHealthy(t *testing.T) {
+ mvs := MonoVertexStatus{}
+ mvs.MarkDaemonUnHealthy("reason", "message")
+
+ for _, condition := range mvs.Conditions {
+ if condition.Type == string(MonoVertexConditionDaemonHealthy) {
+ if condition.Status != metav1.ConditionFalse {
+ t.Errorf("MarkDaemonUnHealthy should set the DaemonHealthy condition to false, got %v", condition.Status)
+ }
+ if condition.Reason != "reason" {
+ t.Errorf("MarkDaemonUnHealthy should set the Reason to 'reason', got %s", condition.Reason)
+ }
+ if condition.Message != "message" {
+ t.Errorf("MarkDaemonUnHealthy should set the Message to 'message', got %s", condition.Message)
+ }
+ }
+ }
+}
+
+func TestMonoVertex_SetObservedGeneration(t *testing.T) {
+ mvs := MonoVertexStatus{}
+ mvs.SetObservedGeneration(1)
+ assert.Equal(t, int64(1), mvs.ObservedGeneration)
+}
+
func TestMonoVertex_IsHealthy(t *testing.T) {
mvs := MonoVertexStatus{}
@@ -171,3 +197,346 @@ func TestMonoVertexGetPodSpec(t *testing.T) {
assert.Contains(t, envNames, EnvMonoVertexObject)
})
}
+
+func TestMonoVertexLimits_GetReadBatchSize(t *testing.T) {
+ t.Run("default value", func(t *testing.T) {
+ mvl := MonoVertexLimits{}
+ assert.Equal(t, uint64(DefaultReadBatchSize), mvl.GetReadBatchSize())
+ })
+
+ t.Run("custom value", func(t *testing.T) {
+ customSize := uint64(1000)
+ mvl := MonoVertexLimits{ReadBatchSize: &customSize}
+ assert.Equal(t, customSize, mvl.GetReadBatchSize())
+ })
+
+}
+
+func TestMonoVertexLimits_GetReadTimeout(t *testing.T) {
+ t.Run("default value", func(t *testing.T) {
+ mvl := MonoVertexLimits{}
+ assert.Equal(t, DefaultReadTimeout, mvl.GetReadTimeout())
+ })
+
+ t.Run("custom value", func(t *testing.T) {
+ customTimeout := metav1.Duration{Duration: 5 * time.Second}
+ mvl := MonoVertexLimits{ReadTimeout: &customTimeout}
+ assert.Equal(t, 5*time.Second, mvl.GetReadTimeout())
+ })
+}
+
+func TestMonoVertex_GetDaemonDeploymentName(t *testing.T) {
+ mv := MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-vertex",
+ },
+ }
+ expected := "test-vertex-mv-daemon"
+ assert.Equal(t, expected, mv.GetDaemonDeploymentName())
+}
+
+func TestMonoVertex_GetDaemonServiceURL(t *testing.T) {
+ mv := MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-vertex",
+ Namespace: "test-namespace",
+ },
+ }
+ expected := "test-vertex-mv-daemon-svc.test-namespace.svc:4327"
+ assert.Equal(t, expected, mv.GetDaemonServiceURL())
+}
+
+func TestMonoVertex_Scalable(t *testing.T) {
+ t.Run("scalable when not disabled", func(t *testing.T) {
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Scale: Scale{
+ Disabled: false,
+ },
+ },
+ }
+ assert.True(t, mv.Scalable())
+ })
+
+ t.Run("not scalable when disabled", func(t *testing.T) {
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Scale: Scale{
+ Disabled: true,
+ },
+ },
+ }
+ assert.False(t, mv.Scalable())
+ })
+}
+
+func TestMonoVertex_GetReplicas(t *testing.T) {
+ t.Run("default replicas", func(t *testing.T) {
+ mv := MonoVertex{}
+ assert.Equal(t, 1, mv.getReplicas())
+ })
+
+ t.Run("custom replicas", func(t *testing.T) {
+ replicas := int32(3)
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Replicas: &replicas,
+ },
+ }
+ assert.Equal(t, 3, mv.getReplicas())
+ })
+}
+
+func TestMonoVertex_CalculateReplicas(t *testing.T) {
+ t.Run("auto scaling disabled", func(t *testing.T) {
+ replicas := int32(5)
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Replicas: &replicas,
+ Scale: Scale{
+ Disabled: true,
+ },
+ },
+ }
+ assert.Equal(t, 5, mv.CalculateReplicas())
+ })
+
+ t.Run("auto scaling enabled, within range", func(t *testing.T) {
+ replicas := int32(3)
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Replicas: &replicas,
+ Scale: Scale{
+ Disabled: false,
+ Min: ptr.To[int32](1),
+ Max: ptr.To[int32](5),
+ },
+ },
+ }
+ assert.Equal(t, 3, mv.CalculateReplicas())
+ })
+
+ t.Run("auto scaling enabled, below min", func(t *testing.T) {
+ replicas := int32(0)
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Replicas: &replicas,
+ Scale: Scale{
+ Disabled: false,
+ Min: ptr.To[int32](2),
+ Max: ptr.To[int32](5),
+ },
+ },
+ }
+ assert.Equal(t, 2, mv.CalculateReplicas())
+ })
+
+ t.Run("auto scaling enabled, above max", func(t *testing.T) {
+ replicas := int32(10)
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Replicas: &replicas,
+ Scale: Scale{
+ Disabled: false,
+ Min: ptr.To[int32](2),
+ Max: ptr.To[int32](5),
+ },
+ },
+ }
+ assert.Equal(t, 5, mv.CalculateReplicas())
+ })
+}
+
+func TestMonoVertex_GetServiceObj(t *testing.T) {
+ mv := MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-vertex",
+ Namespace: "test-namespace",
+ },
+ }
+
+ t.Run("non-headless service", func(t *testing.T) {
+ svc := mv.getServiceObj("test-service", false, 8080, "http")
+ assert.Equal(t, "test-service", svc.Name)
+ assert.Equal(t, "test-namespace", svc.Namespace)
+ assert.Equal(t, 1, len(svc.Spec.Ports))
+ assert.Equal(t, int32(8080), svc.Spec.Ports[0].Port)
+ assert.Equal(t, "http", svc.Spec.Ports[0].Name)
+ assert.NotEqual(t, "None", svc.Spec.ClusterIP)
+ })
+
+ t.Run("headless service", func(t *testing.T) {
+ svc := mv.getServiceObj("test-headless-service", true, 9090, "grpc")
+ assert.Equal(t, "test-headless-service", svc.Name)
+ assert.Equal(t, "test-namespace", svc.Namespace)
+ assert.Equal(t, 1, len(svc.Spec.Ports))
+ assert.Equal(t, int32(9090), svc.Spec.Ports[0].Port)
+ assert.Equal(t, "grpc", svc.Spec.Ports[0].Name)
+ assert.Equal(t, "None", svc.Spec.ClusterIP)
+ })
+
+ t.Run("verify labels", func(t *testing.T) {
+ svc := mv.getServiceObj("test-label-service", false, 7070, "metrics")
+ expectedLabels := map[string]string{
+ KeyPartOf: Project,
+ KeyManagedBy: ControllerMonoVertex,
+ KeyComponent: ComponentMonoVertex,
+ KeyMonoVertexName: "test-vertex",
+ }
+ assert.Equal(t, expectedLabels, svc.Labels)
+ })
+
+ t.Run("verify selector", func(t *testing.T) {
+ svc := mv.getServiceObj("test-selector-service", false, 6060, "admin")
+ expectedSelector := map[string]string{
+ KeyPartOf: Project,
+ KeyManagedBy: ControllerMonoVertex,
+ KeyComponent: ComponentMonoVertex,
+ KeyMonoVertexName: "test-vertex",
+ }
+ assert.Equal(t, expectedSelector, svc.Spec.Selector)
+ })
+}
+
+func TestMonoVertex_GetServiceObjs(t *testing.T) {
+ mv := MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-vertex",
+ Namespace: "test-namespace",
+ },
+ }
+
+ t.Run("verify service objects", func(t *testing.T) {
+ services := mv.GetServiceObjs()
+ assert.Equal(t, 1, len(services), "Expected 1 service object")
+
+ headlessService := services[0]
+ assert.Equal(t, mv.GetHeadlessServiceName(), headlessService.Name)
+ assert.Equal(t, "test-namespace", headlessService.Namespace)
+ assert.Equal(t, "None", headlessService.Spec.ClusterIP)
+ assert.Equal(t, 1, len(headlessService.Spec.Ports))
+ assert.Equal(t, int32(MonoVertexMetricsPort), headlessService.Spec.Ports[0].Port)
+ assert.Equal(t, MonoVertexMetricsPortName, headlessService.Spec.Ports[0].Name)
+ })
+}
+
+func TestMonoVertex_GetDaemonDeploymentObj(t *testing.T) {
+ mv := MonoVertex{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-vertex",
+ Namespace: "test-namespace",
+ },
+ Spec: MonoVertexSpec{},
+ }
+
+ t.Run("basic deployment object", func(t *testing.T) {
+ req := GetMonoVertexDaemonDeploymentReq{
+ Image: "test-image:latest",
+ PullPolicy: corev1.PullAlways,
+ DefaultResources: corev1.ResourceRequirements{
+ Limits: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("100m"),
+ corev1.ResourceMemory: resource.MustParse("128Mi"),
+ },
+ },
+ }
+
+ deployment, err := mv.GetDaemonDeploymentObj(req)
+ assert.NoError(t, err)
+ assert.NotNil(t, deployment)
+ assert.Equal(t, mv.GetDaemonDeploymentName(), deployment.Name)
+ assert.Equal(t, mv.Namespace, deployment.Namespace)
+ assert.Equal(t, "test-image:latest", deployment.Spec.Template.Spec.Containers[0].Image)
+ assert.Equal(t, corev1.PullAlways, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
+ assert.Equal(t, resource.MustParse("100m"), deployment.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU])
+ assert.Equal(t, resource.MustParse("128Mi"), deployment.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory])
+ })
+
+ t.Run("with custom environment variables", func(t *testing.T) {
+ req := GetMonoVertexDaemonDeploymentReq{
+ Image: "test-image:v1",
+ Env: []corev1.EnvVar{
+ {Name: "CUSTOM_ENV", Value: "custom_value"},
+ },
+ }
+
+ deployment, err := mv.GetDaemonDeploymentObj(req)
+ assert.NoError(t, err)
+ assert.NotNil(t, deployment)
+
+ envVars := deployment.Spec.Template.Spec.Containers[0].Env
+ assert.Contains(t, envVars, corev1.EnvVar{Name: "CUSTOM_ENV", Value: "custom_value"})
+ })
+
+ t.Run("with daemon template", func(t *testing.T) {
+ mv.Spec.DaemonTemplate = &DaemonTemplate{
+ Replicas: ptr.To[int32](3),
+ AbstractPodTemplate: AbstractPodTemplate{
+ NodeSelector: map[string]string{"node": "special"},
+ },
+ ContainerTemplate: &ContainerTemplate{
+ Resources: corev1.ResourceRequirements{
+ Limits: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("200m"),
+ },
+ },
+ },
+ }
+
+ req := GetMonoVertexDaemonDeploymentReq{
+ Image: "test-image:v2",
+ }
+
+ deployment, err := mv.GetDaemonDeploymentObj(req)
+ assert.NoError(t, err)
+ assert.NotNil(t, deployment)
+ assert.Equal(t, int32(3), *deployment.Spec.Replicas)
+ assert.Equal(t, "special", deployment.Spec.Template.Spec.NodeSelector["node"])
+ assert.Equal(t, resource.MustParse("200m"), deployment.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU])
+ })
+
+ t.Run("verify probes", func(t *testing.T) {
+ req := GetMonoVertexDaemonDeploymentReq{
+ Image: "test-image:v3",
+ }
+
+ deployment, err := mv.GetDaemonDeploymentObj(req)
+ assert.NoError(t, err)
+ assert.NotNil(t, deployment)
+
+ container := deployment.Spec.Template.Spec.Containers[0]
+ assert.NotNil(t, container.ReadinessProbe)
+ assert.NotNil(t, container.LivenessProbe)
+
+ assert.Equal(t, int32(MonoVertexDaemonServicePort), container.ReadinessProbe.HTTPGet.Port.IntVal)
+ assert.Equal(t, "/readyz", container.ReadinessProbe.HTTPGet.Path)
+ assert.Equal(t, corev1.URISchemeHTTPS, container.ReadinessProbe.HTTPGet.Scheme)
+
+ assert.Equal(t, int32(MonoVertexDaemonServicePort), container.LivenessProbe.HTTPGet.Port.IntVal)
+ assert.Equal(t, "/livez", container.LivenessProbe.HTTPGet.Path)
+ assert.Equal(t, corev1.URISchemeHTTPS, container.LivenessProbe.HTTPGet.Scheme)
+ })
+
+ t.Run("verify labels and owner references", func(t *testing.T) {
+ req := GetMonoVertexDaemonDeploymentReq{
+ Image: "test-image:v4",
+ }
+
+ deployment, err := mv.GetDaemonDeploymentObj(req)
+ assert.NoError(t, err)
+ assert.NotNil(t, deployment)
+
+ expectedLabels := map[string]string{
+ KeyPartOf: Project,
+ KeyManagedBy: ControllerMonoVertex,
+ KeyComponent: ComponentMonoVertexDaemon,
+ KeyAppName: mv.GetDaemonDeploymentName(),
+ KeyMonoVertexName: mv.Name,
+ }
+ assert.Equal(t, expectedLabels, deployment.Labels)
+
+ assert.Len(t, deployment.OwnerReferences, 1)
+ assert.Equal(t, mv.Name, deployment.OwnerReferences[0].Name)
+ assert.Equal(t, MonoVertexGroupVersionKind.Kind, deployment.OwnerReferences[0].Kind)
+ })
+}
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 765dd96681..50a03d54a3 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -583,12 +583,19 @@ func schema_pkg_apis_numaflow_v1alpha1_AbstractVertex(ref common.ReferenceCallba
Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate"),
},
},
+ "updateStrategy": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The strategy to use to replace existing pods with new ones.",
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy"),
+ },
+ },
},
Required: []string{"name"},
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
}
}
@@ -5731,6 +5738,13 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexSpec(ref common.ReferenceCallback)
Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate"),
},
},
+ "updateStrategy": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The strategy to use to replace existing pods with new ones.",
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy"),
+ },
+ },
"pipelineName": {
SchemaProps: spec.SchemaProps{
Default: "",
@@ -5789,7 +5803,7 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexSpec(ref common.ReferenceCallback)
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.CombinedEdge", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Watermark", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.CombinedEdge", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UDF", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.VertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Watermark", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
}
}
@@ -5834,6 +5848,14 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexStatus(ref common.ReferenceCallback
Format: "int64",
},
},
+ "desiredReplicas": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The number of desired replicas.",
+ Default: 0,
+ Type: []string{"integer"},
+ Format: "int64",
+ },
+ },
"selector": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
@@ -5872,30 +5894,30 @@ func schema_pkg_apis_numaflow_v1alpha1_VertexStatus(ref common.ReferenceCallback
Format: "int64",
},
},
- "currentReplicas": {
+ "updatedReplicas": {
SchemaProps: spec.SchemaProps{
- Description: "The number of Pods created by the controller from the Vertex version indicated by currentHash.",
+ Description: "The number of Pods created by the controller from the Vertex version indicated by updateHash.",
Type: []string{"integer"},
Format: "int64",
},
},
- "updatedReplicas": {
+ "updatedReadyReplicas": {
SchemaProps: spec.SchemaProps{
- Description: "The number of Pods created by the controller from the Vertex version indicated by updateHash.",
+ Description: "The number of ready Pods created by the controller from the Vertex version indicated by updateHash.",
Type: []string{"integer"},
Format: "int64",
},
},
"currentHash": {
SchemaProps: spec.SchemaProps{
- Description: "If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).",
+ Description: "If not empty, indicates the current version of the Vertex used to generate Pods.",
Type: []string{"string"},
Format: "",
},
},
"updateHash": {
SchemaProps: spec.SchemaProps{
- Description: "If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)",
+ Description: "If not empty, indicates the updated version of the Vertex used to generate Pods.",
Type: []string{"string"},
Format: "",
},
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types.go b/pkg/apis/numaflow/v1alpha1/vertex_types.go
index 0b5ec7efc9..965e9f4bcc 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types.go
@@ -37,6 +37,8 @@ const (
VertexPhaseRunning VertexPhase = "Running"
VertexPhaseFailed VertexPhase = "Failed"
+ // VertexConditionDeployed has the status True when the vertex related sub resources are deployed.
+ VertexConditionDeployed ConditionType = "Deployed"
// VertexConditionPodsHealthy has the status True when all the vertex pods are healthy.
VertexConditionPodsHealthy ConditionType = "PodsHealthy"
)
@@ -58,7 +60,7 @@ const NumaflowRustBinary = "/bin/numaflow-rs"
// +kubebuilder:subresource:status
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
-// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.spec.replicas`
+// +kubebuilder:printcolumn:name="Desired",type=string,JSONPath=`.status.desiredReplicas`
// +kubebuilder:printcolumn:name="Current",type=string,JSONPath=`.status.replicas`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
@@ -608,6 +610,10 @@ type AbstractVertex struct {
// Container template for the side inputs watcher container.
// +optional
SideInputsContainerTemplate *ContainerTemplate `json:"sideInputsContainerTemplate,omitempty" protobuf:"bytes,15,opt,name=sideInputsContainerTemplate"`
+ // The strategy to use to replace existing pods with new ones.
+ // +kubebuilder:default={"type": "RollingUpdate", "rollingUpdate": {"maxUnavailable": "25%"}}
+ // +optional
+ UpdateStrategy UpdateStrategy `json:"updateStrategy,omitempty" protobuf:"bytes,16,opt,name=updateStrategy"`
}
func (av AbstractVertex) GetVertexType() VertexType {
@@ -715,29 +721,32 @@ type VertexStatus struct {
// Total number of non-terminated pods targeted by this Vertex (their labels match the selector).
// +optional
Replicas uint32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
+ // The number of desired replicas.
+ // +optional
+ DesiredReplicas uint32 `json:"desiredReplicas" protobuf:"varint,4,opt,name=desiredReplicas"`
// +optional
- Selector string `json:"selector,omitempty" protobuf:"bytes,4,opt,name=selector"`
+ Selector string `json:"selector,omitempty" protobuf:"bytes,5,opt,name=selector"`
// +optional
- Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
+ Reason string `json:"reason,omitempty" protobuf:"bytes,6,opt,name=reason"`
// +optional
- Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
+ Message string `json:"message,omitempty" protobuf:"bytes,7,opt,name=message"`
// Time of last scaling operation.
// +optional
- LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,7,opt,name=lastScaledAt"`
+ LastScaledAt metav1.Time `json:"lastScaledAt,omitempty" protobuf:"bytes,8,opt,name=lastScaledAt"`
// The generation observed by the Vertex controller.
// +optional
- ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,8,opt,name=observedGeneration"`
+ ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,9,opt,name=observedGeneration"`
// The number of pods targeted by this Vertex with a Ready Condition.
// +optional
- ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,9,opt,name=readyReplicas"`
- // The number of Pods created by the controller from the Vertex version indicated by currentHash.
- CurrentReplicas uint32 `json:"currentReplicas,omitempty" protobuf:"varint,10,opt,name=currentReplicas"`
+ ReadyReplicas uint32 `json:"readyReplicas,omitempty" protobuf:"varint,10,opt,name=readyReplicas"`
// The number of Pods created by the controller from the Vertex version indicated by updateHash.
UpdatedReplicas uint32 `json:"updatedReplicas,omitempty" protobuf:"varint,11,opt,name=updatedReplicas"`
- // If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).
- CurrentHash string `json:"currentHash,omitempty" protobuf:"bytes,12,opt,name=currentHash"`
- // If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
- UpdateHash string `json:"updateHash,omitempty" protobuf:"bytes,13,opt,name=updateHash"`
+ // The number of ready Pods created by the controller from the Vertex version indicated by updateHash.
+ UpdatedReadyReplicas uint32 `json:"updatedReadyReplicas,omitempty" protobuf:"varint,12,opt,name=updatedReadyReplicas"`
+ // If not empty, indicates the current version of the Vertex used to generate Pods.
+ CurrentHash string `json:"currentHash,omitempty" protobuf:"bytes,13,opt,name=currentHash"`
+ // If not empty, indicates the updated version of the Vertex used to generate Pods.
+ UpdateHash string `json:"updateHash,omitempty" protobuf:"bytes,14,opt,name=updateHash"`
}
func (vs *VertexStatus) MarkPhase(phase VertexPhase, reason, message string) {
@@ -756,6 +765,17 @@ func (vs *VertexStatus) MarkPhaseRunning() {
vs.MarkPhase(VertexPhaseRunning, "", "")
}
+// MarkDeployed set the Vertex has it's sub resources deployed.
+func (vs *VertexStatus) MarkDeployed() {
+ vs.MarkTrue(VertexConditionDeployed)
+}
+
+// MarkDeployFailed set the Vertex deployment failed
+func (vs *VertexStatus) MarkDeployFailed(reason, message string) {
+ vs.MarkFalse(VertexConditionDeployed, reason, message)
+ vs.MarkPhaseFailed(reason, message)
+}
+
// MarkPodNotHealthy marks the pod not healthy with the given reason and message.
func (vs *VertexStatus) MarkPodNotHealthy(reason, message string) {
vs.MarkFalse(VertexConditionPodsHealthy, reason, message)
@@ -770,7 +790,7 @@ func (vs *VertexStatus) MarkPodHealthy(reason, message string) {
// InitConditions sets conditions to Unknown state.
func (vs *VertexStatus) InitConditions() {
- vs.InitializeConditions(VertexConditionPodsHealthy)
+ vs.InitializeConditions(VertexConditionDeployed, VertexConditionPodsHealthy)
}
// IsHealthy indicates whether the vertex is healthy or not
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
index 1a4534c3ec..1f5572c424 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
@@ -565,10 +565,37 @@ func Test_VertexMarkPodHealthy(t *testing.T) {
}
}
+func Test_VertexMarkDeployed(t *testing.T) {
+ s := VertexStatus{}
+ s.MarkDeployed()
+ for _, c := range s.Conditions {
+ if c.Type == string(VertexConditionDeployed) {
+ assert.Equal(t, metav1.ConditionTrue, c.Status)
+ assert.Equal(t, "Successful", c.Reason)
+ assert.Equal(t, "Successful", c.Message)
+ }
+ }
+}
+
+func Test_VertexMarkDeployFailed(t *testing.T) {
+ s := VertexStatus{}
+ s.MarkDeployFailed("reason", "message")
+ assert.Equal(t, VertexPhaseFailed, s.Phase)
+ assert.Equal(t, "reason", s.Reason)
+ assert.Equal(t, "message", s.Message)
+ for _, c := range s.Conditions {
+ if c.Type == string(VertexConditionDeployed) {
+ assert.Equal(t, metav1.ConditionFalse, c.Status)
+ assert.Equal(t, "reason", c.Reason)
+ assert.Equal(t, "message", c.Message)
+ }
+ }
+}
+
func Test_VertexInitConditions(t *testing.T) {
v := VertexStatus{}
v.InitConditions()
- assert.Equal(t, 1, len(v.Conditions))
+ assert.Equal(t, 2, len(v.Conditions))
for _, c := range v.Conditions {
assert.Equal(t, metav1.ConditionUnknown, c.Status)
}
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index a7b7a90c40..9aca247a05 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -118,6 +118,7 @@ func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.Mon
if err := mr.orchestrateFixedResources(ctx, monoVtx); err != nil {
monoVtx.Status.MarkDeployFailed("OrchestrateFixedResourcesFailed", err.Error())
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "OrchestrateFixedResourcesFailed", "OrchestrateFixedResourcesFailed: %s", err.Error())
return ctrl.Result{}, err
}
@@ -125,6 +126,7 @@ func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.Mon
if err := mr.orchestratePods(ctx, monoVtx); err != nil {
monoVtx.Status.MarkDeployFailed("OrchestratePodsFailed", err.Error())
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "OrchestratePodsFailed", "OrchestratePodsFailed: %s", err.Error())
return ctrl.Result{}, err
}
@@ -399,8 +401,8 @@ func (mr *monoVertexReconciler) createOrUpdateMonoVtxServices(ctx context.Contex
if existingSvc.GetAnnotations()[dfv1.KeyHash] != svcHash {
if err := mr.client.Delete(ctx, &existingSvc); err != nil {
if !apierrors.IsNotFound(err) {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "DelSvcFailed", err.Error(), "Failed to delete existing mono vertex service", zap.String("service", existingSvc.Name), zap.Error(err))
- return err
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "DelSvcFailed", "Failed to delete existing mono vertex service %s: %s", existingSvc.Name, err.Error())
+ return fmt.Errorf("failed to delete existing mono vertex service %s: %w", existingSvc.Name, err)
}
} else {
log.Infow("Deleted a stale mono vertex service to recreate", zap.String("service", existingSvc.Name))
@@ -417,8 +419,8 @@ func (mr *monoVertexReconciler) createOrUpdateMonoVtxServices(ctx context.Contex
if apierrors.IsAlreadyExists(err) {
continue
}
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "CreateSvcFailed", err.Error(), "Failed to create a mono vertex service", zap.String("service", s.Name), zap.Error(err))
- return err
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "CreateSvcFailed", "Failed to create a mono vertex service %s: %s", s.Name, err.Error())
+ return fmt.Errorf("failed to create a mono vertex service %s: %w", s.Name, err)
} else {
log.Infow("Succeeded to create a mono vertex service", zap.String("service", s.Name))
mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "CreateSvcSuccess", "Succeeded to create mono vertex service %s", s.Name)
@@ -428,8 +430,8 @@ func (mr *monoVertexReconciler) createOrUpdateMonoVtxServices(ctx context.Contex
for _, v := range existingSvcs { // clean up stale services
if err := mr.client.Delete(ctx, &v); err != nil {
if !apierrors.IsNotFound(err) {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "DelSvcFailed", err.Error(), "Failed to delete mono vertex service not in use", zap.String("service", v.Name), zap.Error(err))
- return err
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "DelSvcFailed", "Failed to delete mono vertex service %s: %s", v.Name, err.Error())
+ return fmt.Errorf("failed to delete mono vertex service %s: %w", v.Name, err)
}
} else {
log.Infow("Deleted a stale mono vertx service", zap.String("service", v.Name))
@@ -463,20 +465,19 @@ func (mr *monoVertexReconciler) createOrUpdateDaemonService(ctx context.Context,
if apierrors.IsNotFound(err) {
needToCreatDaemonSvc = true
} else {
- mr.markDeploymentFailedAndLogEvent(monoVtx, false, log, "FindDaemonSvcFailed", err.Error(), "Failed to find existing mono vtx daemon service", zap.String("service", svc.Name), zap.Error(err))
- return err
+ return fmt.Errorf("failed to find existing mono vertex daemon service: %w", err)
}
} else if existingSvc.GetAnnotations()[dfv1.KeyHash] != svcHash {
if err := mr.client.Delete(ctx, existingSvc); err != nil && !apierrors.IsNotFound(err) {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "DelDaemonSvcFailed", err.Error(), "Failed to delete existing mono vtx daemon service", zap.String("service", existingSvc.Name), zap.Error(err))
- return err
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "DelDaemonSvcFailed", "Failed to delete existing mono vertex daemon service %s: %s", existingSvc.Name, err.Error())
+ return fmt.Errorf("failed to delete existing mono vertex daemon service %s: %w", existingSvc.Name, err)
}
needToCreatDaemonSvc = true
}
if needToCreatDaemonSvc {
if err := mr.client.Create(ctx, svc); err != nil {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "CreateDaemonSvcFailed", err.Error(), "Failed to create mono vtx daemon service", zap.String("service", svc.Name), zap.Error(err))
- return err
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "CreateDaemonSvcFailed", "Failed to create a mono vertex daemon service %s: %s", svc.Name, err.Error())
+ return fmt.Errorf("failed to create a mono vertex daemon service %s: %w", svc.Name, err)
}
log.Infow("Succeeded to create a mono vertex daemon service", zap.String("service", svc.Name))
mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "CreateMonoVtxDaemonSvcSuccess", "Succeeded to create a mono vertex daemon service %s", svc.Name)
@@ -496,8 +497,7 @@ func (mr *monoVertexReconciler) createOrUpdateDaemonDeployment(ctx context.Conte
}
deploy, err := monoVtx.GetDaemonDeploymentObj(req)
if err != nil {
- mr.markDeploymentFailedAndLogEvent(monoVtx, false, log, "BuildDaemonDeployFailed", err.Error(), "Failed to build mono verex daemon deployment spec", zap.Error(err))
- return err
+ return fmt.Errorf("failed to build mono vertex daemon deployment spec: %w", err)
}
deployHash := sharedutil.MustHash(deploy.Spec)
deploy.Annotations = map[string]string{dfv1.KeyHash: deployHash}
@@ -505,8 +505,7 @@ func (mr *monoVertexReconciler) createOrUpdateDaemonDeployment(ctx context.Conte
needToCreate := false
if err := mr.client.Get(ctx, types.NamespacedName{Namespace: monoVtx.Namespace, Name: deploy.Name}, existingDeploy); err != nil {
if !apierrors.IsNotFound(err) {
- mr.markDeploymentFailedAndLogEvent(monoVtx, false, log, "FindDaemonDeployFailed", err.Error(), "Failed to find existing mono vertex daemon deployment", zap.String("deployment", deploy.Name), zap.Error(err))
- return err
+ return fmt.Errorf("failed to find existing mono vertex daemon deployment: %w", err)
} else {
needToCreate = true
}
@@ -514,16 +513,16 @@ func (mr *monoVertexReconciler) createOrUpdateDaemonDeployment(ctx context.Conte
if existingDeploy.GetAnnotations()[dfv1.KeyHash] != deployHash {
// Delete and recreate, to avoid updating immutable fields problem.
if err := mr.client.Delete(ctx, existingDeploy); err != nil {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "DeleteOldDaemonDeployFailed", err.Error(), "Failed to delete the outdated daemon deployment", zap.String("deployment", existingDeploy.Name), zap.Error(err))
- return err
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "DeleteOldDaemonDeployFailed", "Failed to delete the outdated daemon deployment %s: %s", existingDeploy.Name, err.Error())
+ return fmt.Errorf("failed to delete the outdated daemon deployment %s: %w", existingDeploy.Name, err)
}
needToCreate = true
}
}
if needToCreate {
if err := mr.client.Create(ctx, deploy); err != nil && !apierrors.IsAlreadyExists(err) {
- mr.markDeploymentFailedAndLogEvent(monoVtx, true, log, "CreateDaemonDeployFailed", err.Error(), "Failed to create a mono vertex daemon deployment", zap.String("deployment", deploy.Name), zap.Error(err))
- return err
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "CreateDaemonDeployFailed", "Failed to create a mono vertex daemon deployment %s: %s", deploy.Name, err.Error())
+ return fmt.Errorf("failed to create a mono vertex daemon deployment %s: %w", deploy.Name, err)
}
log.Infow("Succeeded to create/recreate a mono vertex daemon deployment", zap.String("deployment", deploy.Name))
mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "CreateDaemonDeploySuccess", "Succeeded to create/recreate a mono vertex daemon deployment %s", deploy.Name)
@@ -548,15 +547,6 @@ func (mr *monoVertexReconciler) buildPodSpec(monoVtx *dfv1.MonoVertex) (*corev1.
return podSpec, nil
}
-// Helper function for warning event types
-func (mr *monoVertexReconciler) markDeploymentFailedAndLogEvent(monoVtx *dfv1.MonoVertex, recordEvent bool, log *zap.SugaredLogger, reason, message, logMsg string, logWith ...interface{}) {
- log.Errorw(logMsg, logWith)
- monoVtx.Status.MarkDeployFailed(reason, message)
- if recordEvent {
- mr.recorder.Event(monoVtx, corev1.EventTypeWarning, reason, message)
- }
-}
-
// checkChildrenResourceStatus checks the status of the children resources of the mono vertex
func (mr *monoVertexReconciler) checkChildrenResourceStatus(ctx context.Context, monoVtx *dfv1.MonoVertex) error {
defer func() {
diff --git a/pkg/reconciler/monovertex/controller_test.go b/pkg/reconciler/monovertex/controller_test.go
index e9c4f9fdea..8e1f179db4 100644
--- a/pkg/reconciler/monovertex/controller_test.go
+++ b/pkg/reconciler/monovertex/controller_test.go
@@ -20,20 +20,25 @@ import (
"context"
"strings"
"testing"
+ "time"
"go.uber.org/zap/zaptest"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
"k8s.io/utils/ptr"
+ ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/reconciler"
"github.com/numaproj/numaflow/pkg/reconciler/monovertex/scaling"
+ sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
"github.com/stretchr/testify/assert"
)
@@ -98,17 +103,62 @@ func Test_NewReconciler(t *testing.T) {
assert.True(t, ok)
}
-func Test_BuildPodSpec(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, nil)
- cl := fake.NewClientBuilder().Build()
- r := &monoVertexReconciler{
+func fakeReconciler(t *testing.T, cl client.WithWatch) *monoVertexReconciler {
+ t.Helper()
+ return &monoVertexReconciler{
client: cl,
scheme: scheme.Scheme,
- config: fakeConfig,
+ config: reconciler.FakeGlobalConfig(t, nil),
image: testFlowImage,
logger: zaptest.NewLogger(t).Sugar(),
recorder: record.NewFakeRecorder(64),
+ scaler: scaling.NewScaler(cl),
}
+}
+
+func TestReconcile(t *testing.T) {
+ t.Run("test not found", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ req := ctrl.Request{
+ NamespacedName: types.NamespacedName{
+ Name: "not-exist",
+ Namespace: testNamespace,
+ },
+ }
+ _, err := r.Reconcile(context.TODO(), req)
+ // Return nil when not found
+ assert.NoError(t, err)
+ })
+
+ t.Run("test found", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testMonoVtx.DeepCopy()
+ err := cl.Create(context.TODO(), testObj)
+ assert.NoError(t, err)
+ o := &dfv1.MonoVertex{}
+ err = cl.Get(context.TODO(), types.NamespacedName{
+ Namespace: testObj.Namespace,
+ Name: testObj.Name,
+ }, o)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.Name, o.Name)
+ req := ctrl.Request{
+ NamespacedName: types.NamespacedName{
+ Name: testObj.Name,
+ Namespace: testObj.Namespace,
+ },
+ }
+ _, err = r.Reconcile(context.TODO(), req)
+ assert.Error(t, err)
+ assert.ErrorContains(t, err, "not found")
+ })
+}
+
+func Test_BuildPodSpec(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
t.Run("test has everything", func(t *testing.T) {
testObj := testMonoVtx.DeepCopy()
spec, err := r.buildPodSpec(testObj)
@@ -137,16 +187,8 @@ func Test_BuildPodSpec(t *testing.T) {
}
func Test_createOrUpdateDaemonDeployment(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, nil)
cl := fake.NewClientBuilder().Build()
- r := &monoVertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
t.Run("test everything from scratch for daemon deployment", func(t *testing.T) {
testObj := testMonoVtx.DeepCopy()
@@ -163,16 +205,8 @@ func Test_createOrUpdateDaemonDeployment(t *testing.T) {
}
func Test_createOrUpdateDaemonService(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, nil)
cl := fake.NewClientBuilder().Build()
- r := &monoVertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
t.Run("test everything from scratch for daemon service", func(t *testing.T) {
testObj := testMonoVtx.DeepCopy()
@@ -189,16 +223,8 @@ func Test_createOrUpdateDaemonService(t *testing.T) {
}
func Test_createOrUpdateMonoVtxServices(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, nil)
cl := fake.NewClientBuilder().Build()
- r := &monoVertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
t.Run("test everything from scratch for monovtx service", func(t *testing.T) {
testObj := testMonoVtx.DeepCopy()
@@ -218,17 +244,10 @@ func Test_createOrUpdateMonoVtxServices(t *testing.T) {
}
func Test_orchestratePods(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, nil)
- cl := fake.NewClientBuilder().Build()
- r := &monoVertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+
t.Run("test orchestratePodsFromTo and cleanUpPodsFromTo", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
testObj := testMonoVtx.DeepCopy()
hash := "test-hasssssh"
podSpec, err := r.buildPodSpec(testObj)
@@ -250,6 +269,8 @@ func Test_orchestratePods(t *testing.T) {
})
t.Run("test orchestratePods", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
testObj := testMonoVtx.DeepCopy()
err := r.orchestratePods(context.TODO(), testObj)
assert.NoError(t, err)
@@ -263,16 +284,8 @@ func Test_orchestratePods(t *testing.T) {
}
func Test_orchestrateFixedResources(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, nil)
cl := fake.NewClientBuilder().Build()
- r := &monoVertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testMonoVtx.DeepCopy()
err := r.orchestrateFixedResources(context.TODO(), testObj)
assert.NoError(t, err)
@@ -294,23 +307,87 @@ func Test_orchestrateFixedResources(t *testing.T) {
}
func Test_reconcile(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, nil)
- cl := fake.NewClientBuilder().Build()
- r := &monoVertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- scaler: scaling.NewScaler(cl),
- }
- testObj := testMonoVtx.DeepCopy()
- _, err := r.reconcile(context.TODO(), testObj)
- assert.NoError(t, err)
- var daemonDeployment appv1.Deployment
- err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetDaemonDeploymentName()},
- &daemonDeployment)
- assert.NoError(t, err)
- assert.Equal(t, testObj.GetDaemonDeploymentName(), daemonDeployment.Name)
+
+ t.Run("test deletion", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testMonoVtx.DeepCopy()
+ testObj.DeletionTimestamp = &metav1.Time{Time: time.Now()}
+ _, err := r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ })
+
+ t.Run("test okay", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testMonoVtx.DeepCopy()
+ _, err := r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ var daemonDeployment appv1.Deployment
+ err = r.client.Get(context.TODO(), client.ObjectKey{Namespace: testObj.GetNamespace(), Name: testObj.GetDaemonDeploymentName()},
+ &daemonDeployment)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.GetDaemonDeploymentName(), daemonDeployment.Name)
+ })
+
+ t.Run("test reconcile rolling update", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testMonoVtx.DeepCopy()
+ testObj.Spec.Replicas = ptr.To[int32](3)
+ _, err := r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ pods := &corev1.PodList{}
+ selector, _ := labels.Parse(dfv1.KeyComponent + "=" + dfv1.ComponentMonoVertex + "," + dfv1.KeyMonoVertexName + "=" + testObj.Name)
+ err = r.client.List(context.TODO(), pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 3, len(pods.Items))
+
+ podSpec, _ := r.buildPodSpec(testObj)
+ hash := sharedutil.MustHash(podSpec)
+ testObj.Status.Replicas = 3
+ testObj.Status.ReadyReplicas = 3
+ testObj.Status.UpdateHash = hash
+ testObj.Status.CurrentHash = hash
+
+ // Reduce desired replicas
+ testObj.Spec.Replicas = ptr.To[int32](2)
+ _, err = r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ err = r.client.List(context.TODO(), pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(pods.Items))
+ assert.Equal(t, uint32(2), testObj.Status.Replicas)
+ assert.Equal(t, uint32(2), testObj.Status.UpdatedReplicas)
+
+ // updatedReplicas > desiredReplicas
+ testObj.Status.UpdatedReplicas = 3
+ _, err = r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ assert.Equal(t, uint32(2), testObj.Status.UpdatedReplicas)
+
+ // Clean up
+ testObj.Spec.Replicas = ptr.To[int32](0)
+ testObj.Spec.Scale.Min = ptr.To[int32](0)
+ _, err = r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ err = r.client.List(context.TODO(), pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(pods.Items))
+
+ // rolling update
+ testObj.Spec.Replicas = ptr.To[int32](20)
+ testObj.Status.UpdatedReplicas = 20
+ testObj.Status.UpdatedReadyReplicas = 20
+ testObj.Status.Replicas = 20
+ testObj.Status.CurrentHash = "123456"
+ testObj.Status.UpdateHash = "123456"
+ _, err = r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ err = r.client.List(context.TODO(), pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 5, len(pods.Items))
+ assert.Equal(t, uint32(20), testObj.Status.Replicas)
+ assert.Equal(t, uint32(5), testObj.Status.UpdatedReplicas)
+ })
}
diff --git a/pkg/reconciler/pipeline/controller.go b/pkg/reconciler/pipeline/controller.go
index 955344a8c1..d8b989f2d6 100644
--- a/pkg/reconciler/pipeline/controller.go
+++ b/pkg/reconciler/pipeline/controller.go
@@ -145,13 +145,28 @@ func (r *pipelineReconciler) reconcile(ctx context.Context, pl *dfv1.Pipeline) (
pl.Status.InitConditions()
pl.Status.SetObservedGeneration(pl.Generation)
+
+ if !controllerutil.ContainsFinalizer(pl, finalizerName) {
+ controllerutil.AddFinalizer(pl, finalizerName)
+ }
+ if err := ValidatePipeline(pl); err != nil {
+ r.recorder.Eventf(pl, corev1.EventTypeWarning, "ValidatePipelineFailed", "Invalid pipeline: %s", err.Error())
+ pl.Status.MarkNotConfigured("InvalidSpec", err.Error())
+ return ctrl.Result{}, err
+ }
+ pl.Status.SetVertexCounts(pl.Spec.Vertices)
+ pl.Status.MarkConfigured()
+
// Orchestrate pipeline sub resources.
// This should be happening in all cases to ensure a clean initialization regardless of the lifecycle phase.
// Eg: even for a pipeline started with desiredPhase = Pause, we should still create the resources for the pipeline.
if err := r.reconcileFixedResources(ctx, pl); err != nil {
- r.recorder.Eventf(pl, corev1.EventTypeWarning, "ReconcilePipelineFailed", "Failed to reconcile pipeline: %v", err.Error())
+ r.recorder.Eventf(pl, corev1.EventTypeWarning, "ReconcileFixedResourcesFailed", "Failed to reconcile pipeline sub resources: %s", err.Error())
+ pl.Status.MarkDeployFailed("ReconcileFixedResourcesFailed", err.Error())
return ctrl.Result{}, err
}
+ pl.Status.MarkDeployed()
+
// If the pipeline has a lifecycle change, then do not update the phase as
// this should happen only after the required configs for the lifecycle changes
// have been applied.
@@ -203,17 +218,6 @@ func isLifecycleChange(pl *dfv1.Pipeline) bool {
// reconcileFixedResources do the jobs of creating fixed resources such as daemon service, vertex objects, and ISB management jobs, etc
func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *dfv1.Pipeline) error {
log := logging.FromContext(ctx)
- if !controllerutil.ContainsFinalizer(pl, finalizerName) {
- controllerutil.AddFinalizer(pl, finalizerName)
- }
- if err := ValidatePipeline(pl); err != nil {
- log.Errorw("Validation failed", zap.Error(err))
- pl.Status.MarkNotConfigured("InvalidSpec", err.Error())
- return err
- }
- pl.Status.SetVertexCounts(pl.Spec.Vertices)
- pl.Status.MarkConfigured()
-
isbSvc := &dfv1.InterStepBufferService{}
isbSvcName := dfv1.DefaultISBSvcName
if len(pl.Spec.InterStepBufferServiceName) > 0 {
@@ -222,16 +226,13 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
err := r.client.Get(ctx, types.NamespacedName{Namespace: pl.Namespace, Name: isbSvcName}, isbSvc)
if err != nil {
if apierrors.IsNotFound(err) {
- pl.Status.MarkDeployFailed("ISBSvcNotFound", "ISB Service not found.")
log.Errorw("ISB Service not found", zap.String("isbsvc", isbSvcName), zap.Error(err))
return fmt.Errorf("isbsvc %s not found", isbSvcName)
}
- pl.Status.MarkDeployFailed("GetISBSvcFailed", err.Error())
log.Errorw("Failed to get ISB Service", zap.String("isbsvc", isbSvcName), zap.Error(err))
return err
}
if !isbSvc.Status.IsHealthy() {
- pl.Status.MarkDeployFailed("ISBSvcNotHealthy", "ISB Service not healthy.")
log.Errorw("ISB Service is not in healthy status", zap.String("isbsvc", isbSvcName), zap.Error(err))
return fmt.Errorf("isbsvc not healthy")
}
@@ -239,16 +240,13 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
// Create or update the Side Inputs Manager deployments
if err := r.createOrUpdateSIMDeployments(ctx, pl, isbSvc.Status.Config); err != nil {
log.Errorw("Failed to create or update Side Inputs Manager deployments", zap.Error(err))
- pl.Status.MarkDeployFailed("CreateOrUpdateSIMDeploymentsFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "CreateOrUpdateSIMDeploymentsFailed", "Failed to create or update Side Inputs Manager deployments: %w", err.Error())
- return err
+ return fmt.Errorf("failed to create or update SIM deployments: %w", err)
}
existingObjs, err := r.findExistingVertices(ctx, pl)
if err != nil {
- log.Errorw("Failed to find existing vertices", zap.Error(err))
- pl.Status.MarkDeployFailed("ListVerticesFailed", err.Error())
- return err
+ return fmt.Errorf("failed to find existing vertices: %w", err)
}
oldBuffers := make(map[string]string)
newBuffers := make(map[string]string)
@@ -286,7 +284,6 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
if apierrors.IsAlreadyExists(err) { // probably somebody else already created it
continue
} else {
- pl.Status.MarkDeployFailed("CreateVertexFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "CreateVertexFailed", "Failed to create vertex: %w", err.Error())
return fmt.Errorf("failed to create vertex, err: %w", err)
}
@@ -298,7 +295,6 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
oldObj.Spec = newObj.Spec
oldObj.Annotations[dfv1.KeyHash] = newObj.GetAnnotations()[dfv1.KeyHash]
if err := r.client.Update(ctx, &oldObj); err != nil {
- pl.Status.MarkDeployFailed("UpdateVertexFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "UpdateVertexFailed", "Failed to update vertex: %w", err.Error())
return fmt.Errorf("failed to update vertex, err: %w", err)
}
@@ -310,7 +306,6 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
}
for _, v := range existingObjs {
if err := r.client.Delete(ctx, &v); err != nil {
- pl.Status.MarkDeployFailed("DeleteStaleVertexFailed", err.Error())
r.recorder.Eventf(pl, corev1.EventTypeWarning, "DeleteStaleVertexFailed", "Failed to delete vertex: %w", err.Error())
return fmt.Errorf("failed to delete vertex, err: %w", err)
}
@@ -336,10 +331,11 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
args = append(args, fmt.Sprintf("--serving-source-streams=%s", strings.Join(pl.GetServingSourceStreamNames(), ",")))
batchJob := buildISBBatchJob(pl, r.image, isbSvc.Status.Config, "isbsvc-create", args, "cre")
if err := r.client.Create(ctx, batchJob); err != nil && !apierrors.IsAlreadyExists(err) {
- pl.Status.MarkDeployFailed("CreateISBSvcCreatingJobFailed", err.Error())
- return fmt.Errorf("failed to create ISB Svc creating job, err: %w", err)
+ r.recorder.Eventf(pl, corev1.EventTypeWarning, "CreateJobForISBCeationFailed", "Failed to create a Job: %w", err.Error())
+ return fmt.Errorf("failed to create ISB creating job, err: %w", err)
}
- log.Infow("Created a job successfully for ISB Svc creating", zap.Any("buffers", bfs), zap.Any("buckets", bks), zap.Any("servingStreams", pl.GetServingSourceStreamNames()))
+ log.Infow("Created a job successfully for ISB creating", zap.Any("buffers", bfs), zap.Any("buckets", bks), zap.Any("servingStreams", pl.GetServingSourceStreamNames()))
+ r.recorder.Eventf(pl, corev1.EventTypeNormal, "CreateJobForISBCeationSuccessful", "Create ISB creation job successfully")
}
if len(oldBuffers) > 0 || len(oldBuckets) > 0 {
@@ -354,10 +350,11 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
args := []string{fmt.Sprintf("--buffers=%s", strings.Join(bfs, ",")), fmt.Sprintf("--buckets=%s", strings.Join(bks, ","))}
batchJob := buildISBBatchJob(pl, r.image, isbSvc.Status.Config, "isbsvc-delete", args, "del")
if err := r.client.Create(ctx, batchJob); err != nil && !apierrors.IsAlreadyExists(err) {
- pl.Status.MarkDeployFailed("CreateISBSvcDeletingJobFailed", err.Error())
- return fmt.Errorf("failed to create ISB Svc deleting job, err: %w", err)
+ r.recorder.Eventf(pl, corev1.EventTypeWarning, "CreateJobForISBDeletionFailed", "Failed to create a Job: %w", err.Error())
+ return fmt.Errorf("failed to create ISB deleting job, err: %w", err)
}
log.Infow("Created ISB Svc deleting job successfully", zap.Any("buffers", bfs), zap.Any("buckets", bks))
+ r.recorder.Eventf(pl, corev1.EventTypeNormal, "CreateJobForISBDeletionSuccessful", "Create ISB deletion job successfully")
}
// Daemon service
@@ -369,7 +366,6 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
return err
}
- pl.Status.MarkDeployed()
return nil
}
diff --git a/pkg/reconciler/pipeline/controller_test.go b/pkg/reconciler/pipeline/controller_test.go
index 2a1762aa4b..e130f49656 100644
--- a/pkg/reconciler/pipeline/controller_test.go
+++ b/pkg/reconciler/pipeline/controller_test.go
@@ -30,9 +30,11 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
"k8s.io/utils/ptr"
+ ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -173,6 +175,46 @@ func Test_NewReconciler(t *testing.T) {
assert.True(t, ok)
}
+func TestReconcile(t *testing.T) {
+ t.Run("test not found", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ req := ctrl.Request{
+ NamespacedName: types.NamespacedName{
+ Name: "not-exist",
+ Namespace: testNamespace,
+ },
+ }
+ _, err := r.Reconcile(context.TODO(), req)
+ // Return nil when not found
+ assert.NoError(t, err)
+ })
+
+ t.Run("test found", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testPipeline.DeepCopy()
+ err := cl.Create(context.TODO(), testObj)
+ assert.NoError(t, err)
+ o := &dfv1.Pipeline{}
+ err = cl.Get(context.TODO(), types.NamespacedName{
+ Namespace: testObj.Namespace,
+ Name: testObj.Name,
+ }, o)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.Name, o.Name)
+ req := ctrl.Request{
+ NamespacedName: types.NamespacedName{
+ Name: testObj.Name,
+ Namespace: testObj.Namespace,
+ },
+ }
+ _, err = r.Reconcile(context.TODO(), req)
+ assert.Error(t, err)
+ assert.ErrorContains(t, err, "not found")
+ })
+}
+
func Test_reconcile(t *testing.T) {
ctx := context.TODO()
@@ -249,7 +291,7 @@ func Test_reconcile(t *testing.T) {
_, err = r.reconcile(ctx, testObj)
assert.Error(t, err)
events := getEvents(t, r)
- assert.Contains(t, events, "Warning ReconcilePipelineFailed Failed to reconcile pipeline: the length of the pipeline name plus the vertex name is over the max limit. (very-very-very-loooooooooooooooooooooooooooooooooooong-input), [must be no more than 63 characters]")
+ assert.Contains(t, events, "Warning ValidatePipelineFailed Invalid pipeline: the length of the pipeline name plus the vertex name is over the max limit. (very-very-very-loooooooooooooooooooooooooooooooooooong-input), [must be no more than 63 characters]")
})
t.Run("test reconcile - duplicate vertex", func(t *testing.T) {
@@ -267,7 +309,7 @@ func Test_reconcile(t *testing.T) {
_, err = r.reconcile(ctx, testObj)
assert.Error(t, err)
events := getEvents(t, r)
- assert.Contains(t, events, "Warning ReconcilePipelineFailed Failed to reconcile pipeline: duplicate vertex name \"input\"")
+ assert.Contains(t, events, "Warning ValidatePipelineFailed Invalid pipeline: duplicate vertex name \"input\"")
})
}
diff --git a/pkg/reconciler/pipeline/validate.go b/pkg/reconciler/pipeline/validate.go
index 2a98c2e665..7304147c16 100644
--- a/pkg/reconciler/pipeline/validate.go
+++ b/pkg/reconciler/pipeline/validate.go
@@ -19,6 +19,7 @@ package pipeline
import (
"fmt"
+ "k8s.io/apimachinery/pkg/util/intstr"
k8svalidation "k8s.io/apimachinery/pkg/util/validation"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
@@ -256,6 +257,13 @@ func validateVertex(v dfv1.AbstractVertex) error {
return fmt.Errorf("vertex %q: partitions should not > 1 for source vertices", v.Name)
}
}
+ // Validate the update strategy.
+ maxUvail := v.UpdateStrategy.GetRollingUpdateStrategy().GetMaxUnavailable()
+ _, err := intstr.GetScaledValueFromIntOrPercent(&maxUvail, 1, true) // maxUnavailable should be an interger or a percentage in string
+ if err != nil {
+ return fmt.Errorf("vertex %q: invalid maxUnavailable: %v", v.Name, err)
+ }
+
for _, ic := range v.InitContainers {
if isReservedContainerName(ic.Name) {
return fmt.Errorf("vertex %q: init container name %q is reserved for containers created by numaflow", v.Name, ic.Name)
diff --git a/pkg/reconciler/pipeline/validate_test.go b/pkg/reconciler/pipeline/validate_test.go
index f116e04825..8f7a272d89 100644
--- a/pkg/reconciler/pipeline/validate_test.go
+++ b/pkg/reconciler/pipeline/validate_test.go
@@ -25,6 +25,7 @@ import (
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/intstr"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
)
@@ -642,6 +643,46 @@ func TestValidateVertex(t *testing.T) {
assert.Contains(t, err.Error(), "can not be 0")
})
+ t.Run("rollingUpdateStrategy - invalid maxUnavailable", func(t *testing.T) {
+ v := dfv1.AbstractVertex{
+ Name: "my-vertex",
+ UpdateStrategy: dfv1.UpdateStrategy{
+ RollingUpdate: &dfv1.RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromString("10")),
+ },
+ },
+ }
+ err := validateVertex(v)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "string is not a percentage")
+ })
+
+ t.Run("rollingUpdateStrategy - good percentage maxUnavailable", func(t *testing.T) {
+ v := dfv1.AbstractVertex{
+ Name: "my-vertex",
+ UpdateStrategy: dfv1.UpdateStrategy{
+ RollingUpdate: &dfv1.RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromString("10%")),
+ },
+ },
+ }
+ err := validateVertex(v)
+ assert.NoError(t, err)
+ })
+
+ t.Run("rollingUpdateStrategy - good integer maxUnavailable", func(t *testing.T) {
+ v := dfv1.AbstractVertex{
+ Name: "my-vertex",
+ UpdateStrategy: dfv1.UpdateStrategy{
+ RollingUpdate: &dfv1.RollingUpdateStrategy{
+ MaxUnavailable: ptr.To[intstr.IntOrString](intstr.FromInt(3)),
+ },
+ },
+ }
+ err := validateVertex(v)
+ assert.NoError(t, err)
+ })
+
t.Run("good init container", func(t *testing.T) {
v := dfv1.AbstractVertex{Name: "my-vertex", InitContainers: goodContainers}
err := validateVertex(v)
diff --git a/pkg/reconciler/vertex/controller.go b/pkg/reconciler/vertex/controller.go
index c1e6b8febb..8789b5d89a 100644
--- a/pkg/reconciler/vertex/controller.go
+++ b/pkg/reconciler/vertex/controller.go
@@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -95,8 +96,11 @@ func (r *vertexReconciler) reconcile(ctx context.Context, vertex *dfv1.Vertex) (
return ctrl.Result{}, nil
}
+ vertex.Status.InitConditions()
vertex.Status.SetObservedGeneration(vertex.Generation)
+ desiredReplicas := vertex.GetReplicas()
+
isbSvc := &dfv1.InterStepBufferService{}
isbSvcName := dfv1.DefaultISBSvcName
if len(vertex.Spec.InterStepBufferServiceName) > 0 {
@@ -123,132 +127,199 @@ func (r *vertexReconciler) reconcile(ctx context.Context, vertex *dfv1.Vertex) (
r.scaler.StartWatching(vertexKey)
}
+ // Create PVCs for reduce vertex
+ if vertex.IsReduceUDF() {
+ if err := r.buildReduceVertexPVCs(ctx, vertex); err != nil {
+ vertex.Status.MarkDeployFailed("BuildReduceVertexPVCsFailed", err.Error())
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "BuildReduceVertexPVCsFailed", err.Error())
+ return ctrl.Result{}, err
+ }
+ }
+
+ // Create services
+ if err := r.createOrUpdateServices(ctx, vertex); err != nil {
+ vertex.Status.MarkDeployFailed("CreateOrUpdateServicesFailed", err.Error())
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "CreateOrUpdateServicesFailed", err.Error())
+ return ctrl.Result{}, err
+ }
+
+ pipeline := &dfv1.Pipeline{}
+ if err := r.client.Get(ctx, types.NamespacedName{Namespace: vertex.Namespace, Name: vertex.Spec.PipelineName}, pipeline); err != nil {
+ log.Errorw("Failed to get pipeline object", zap.Error(err))
+ vertex.Status.MarkDeployFailed("GetPipelineFailed", err.Error())
+ return ctrl.Result{}, err
+ }
+
+ // Create pods
+ if err := r.orchestratePods(ctx, vertex, pipeline, isbSvc); err != nil {
+ vertex.Status.MarkDeployFailed("OrchestratePodsFailed", err.Error())
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "OrchestratePodsFailed", err.Error())
+ return ctrl.Result{}, err
+ }
+
+ vertex.Status.MarkDeployed()
+
+ // Mark it running before checking the status of the pods
+ vertex.Status.MarkPhaseRunning()
+
+ // Check status of the pods
+ var podList corev1.PodList
+ selector, _ := labels.Parse(dfv1.KeyPipelineName + "=" + vertex.Spec.PipelineName + "," + dfv1.KeyVertexName + "=" + vertex.Spec.Name)
+ if err := r.client.List(ctx, &podList, &client.ListOptions{Namespace: vertex.GetNamespace(), LabelSelector: selector}); err != nil {
+ vertex.Status.MarkPodNotHealthy("ListVerticesPodsFailed", err.Error())
+ return ctrl.Result{}, fmt.Errorf("failed to get pods of a vertex: %w", err)
+ }
+ readyPods := reconciler.NumOfReadyPods(podList)
+ if readyPods > desiredReplicas { // It might happen in some corner cases, such as during rollout
+ readyPods = desiredReplicas
+ }
+ vertex.Status.ReadyReplicas = uint32(readyPods)
+ if healthy, reason, msg := reconciler.CheckPodsStatus(&podList); healthy {
+ vertex.Status.MarkPodHealthy(reason, msg)
+ } else {
+ // Do not need to explicitly requeue, since the it keeps watching the status change of the pods
+ vertex.Status.MarkPodNotHealthy(reason, msg)
+ }
+
+ return ctrl.Result{}, nil
+}
+
+func (r *vertexReconciler) orchestratePods(ctx context.Context, vertex *dfv1.Vertex, pipeline *dfv1.Pipeline, isbSvc *dfv1.InterStepBufferService) error {
+ log := logging.FromContext(ctx)
desiredReplicas := vertex.GetReplicas()
+ vertex.Status.DesiredReplicas = uint32(desiredReplicas)
+
// Set metrics
defer func() {
reconciler.VertexDesiredReplicas.WithLabelValues(vertex.Namespace, vertex.Spec.PipelineName, vertex.Spec.Name).Set(float64(desiredReplicas))
reconciler.VertexCurrentReplicas.WithLabelValues(vertex.Namespace, vertex.Spec.PipelineName, vertex.Spec.Name).Set(float64(vertex.Status.Replicas))
}()
- if vertex.IsReduceUDF() {
- if x := vertex.Spec.UDF.GroupBy.Storage; x != nil && x.PersistentVolumeClaim != nil {
- for i := 0; i < desiredReplicas; i++ {
- newPvc, err := r.buildReduceVertexPVCSpec(vertex, i)
- if err != nil {
- log.Errorw("Error building a PVC spec", zap.Error(err))
- vertex.Status.MarkPhaseFailed("BuildPVCSpecFailed", err.Error())
- return ctrl.Result{}, err
- }
- hash := sharedutil.MustHash(newPvc.Spec)
- newPvc.SetAnnotations(map[string]string{dfv1.KeyHash: hash})
- existingPvc := &corev1.PersistentVolumeClaim{}
- if err := r.client.Get(ctx, types.NamespacedName{Namespace: vertex.Namespace, Name: newPvc.Name}, existingPvc); err != nil {
- if !apierrors.IsNotFound(err) {
- log.Errorw("Error finding existing PVC", zap.Error(err))
- vertex.Status.MarkPhaseFailed("FindExistingPVCFailed", err.Error())
- return ctrl.Result{}, err
- }
- if err := r.client.Create(ctx, newPvc); err != nil && !apierrors.IsAlreadyExists(err) {
- r.markPhaseFailedAndLogEvent(vertex, log, "CreatePVCFailed", err.Error(), "Error creating a PVC", zap.Error(err))
- return ctrl.Result{}, err
- }
- r.recorder.Eventf(vertex, corev1.EventTypeNormal, "CreatePVCSuccess", "Successfully created PVC %s", newPvc.Name)
- } else {
- if existingPvc.GetAnnotations()[dfv1.KeyHash] != hash {
- // TODO: deal with spec difference
- if false {
- log.Debug("TODO: check spec difference")
- }
- }
- }
- }
- }
+ // Build pod spec of the 1st replica to calculate the hash, which is used to determine whether the pod spec is changed
+ tmpSpec, err := r.buildPodSpec(vertex, pipeline, isbSvc.Status.Config, 0)
+ if err != nil {
+ return fmt.Errorf("failed to build a pod spec: %w", err)
+ }
+ hash := sharedutil.MustHash(tmpSpec)
+ if vertex.Status.UpdateHash != hash { // New spec, or still processing last update, while new update is coming
+ vertex.Status.UpdateHash = hash
+ vertex.Status.UpdatedReplicas = 0
+ vertex.Status.UpdatedReadyReplicas = 0
}
- // Create services
- // Note: We purposely put service reconciliation before pod,
- // to prevent pod reconciliation failure from blocking service creation.
- // It's ok to keep failing to scale up/down pods (e.g., due to quota),
- // but without services, certain platform functionalities will be broken.
- // E.g., the vertex processing rate calculation relies on the headless service to determine the number of active pods.
- existingSvcs, err := r.findExistingServices(ctx, vertex)
- if err != nil {
- log.Errorw("Failed to find existing services", zap.Error(err))
- vertex.Status.MarkPhaseFailed("FindExistingSvcsFailed", err.Error())
- return ctrl.Result{}, err
+ // Manually or automatically scaled down
+ if currentReplicas := int(vertex.Status.Replicas); currentReplicas > desiredReplicas {
+ if err := r.cleanUpPodsFromTo(ctx, vertex, desiredReplicas, currentReplicas); err != nil {
+ return fmt.Errorf("failed to clean up vertex pods [%v, %v): %w", desiredReplicas, currentReplicas, err)
+ }
+ vertex.Status.Replicas = uint32(desiredReplicas)
}
- for _, s := range vertex.GetServiceObjs() {
- svcHash := sharedutil.MustHash(s.Spec)
- s.Annotations = map[string]string{dfv1.KeyHash: svcHash}
- needToCreate := false
- if existingSvc, existing := existingSvcs[s.Name]; existing {
- if existingSvc.GetAnnotations()[dfv1.KeyHash] != svcHash {
- if err := r.client.Delete(ctx, &existingSvc); err != nil {
- if !apierrors.IsNotFound(err) {
- r.markPhaseFailedAndLogEvent(vertex, log, "DelSvcFailed", err.Error(), "Failed to delete existing service", zap.String("service", existingSvc.Name), zap.Error(err))
- return ctrl.Result{}, err
+ updatedReplicas := int(vertex.Status.UpdatedReplicas)
+ if updatedReplicas > desiredReplicas {
+ updatedReplicas = desiredReplicas
+ vertex.Status.UpdatedReplicas = uint32(updatedReplicas)
+ }
+
+ if updatedReplicas > 0 {
+ // Make sure [0 - updatedReplicas] with hash are in place
+ if err := r.orchestratePodsFromTo(ctx, vertex, pipeline, isbSvc, 0, updatedReplicas, hash); err != nil {
+ return fmt.Errorf("failed to orchestrate vertex pods [0, %v): %w", updatedReplicas, err)
+ }
+ // Wait for the updated pods to be ready before moving on
+ if vertex.Status.UpdatedReadyReplicas != vertex.Status.UpdatedReplicas {
+ updatedReadyReplicas := 0
+ existingPods, err := r.findExistingPods(ctx, vertex, 0, updatedReplicas)
+ if err != nil {
+ return fmt.Errorf("failed to get pods of a vertex: %w", err)
+ }
+ for _, pod := range existingPods {
+ if pod.GetAnnotations()[dfv1.KeyHash] == vertex.Status.UpdateHash {
+ if reconciler.IsPodReady(pod) {
+ updatedReadyReplicas++
}
- } else {
- log.Infow("Deleted a stale service to recreate", zap.String("service", existingSvc.Name))
- r.recorder.Eventf(vertex, corev1.EventTypeNormal, "DelSvcSuccess", "Deleted stale service %s to recreate", existingSvc.Name)
}
- needToCreate = true
}
- delete(existingSvcs, s.Name)
- } else {
- needToCreate = true
- }
- if needToCreate {
- if err := r.client.Create(ctx, s); err != nil {
- if apierrors.IsAlreadyExists(err) {
- continue
- }
- r.markPhaseFailedAndLogEvent(vertex, log, "CreateSvcFailed", err.Error(), "Failed to create a service", zap.String("service", s.Name), zap.Error(err))
- return ctrl.Result{}, err
- } else {
- log.Infow("Succeeded to create a service", zap.String("service", s.Name))
- r.recorder.Eventf(vertex, corev1.EventTypeNormal, "CreateSvcSuccess", "Succeeded to create service %s", s.Name)
+ vertex.Status.UpdatedReadyReplicas = uint32(updatedReadyReplicas)
+ if updatedReadyReplicas < updatedReplicas {
+ return nil
}
}
}
- for _, v := range existingSvcs { // clean up stale services
- if err := r.client.Delete(ctx, &v); err != nil {
- if !apierrors.IsNotFound(err) {
- r.markPhaseFailedAndLogEvent(vertex, log, "DelSvcFailed", err.Error(), "Failed to delete service not in use", zap.String("service", v.Name), zap.Error(err))
- return ctrl.Result{}, err
+
+ if vertex.Status.UpdateHash == vertex.Status.CurrentHash ||
+ vertex.Status.CurrentHash == "" {
+ // 1. Regular scaling operation 2. First time
+ // create (desiredReplicas-updatedReplicas) pods directly
+ if desiredReplicas > updatedReplicas {
+ if err := r.orchestratePodsFromTo(ctx, vertex, pipeline, isbSvc, updatedReplicas, desiredReplicas, hash); err != nil {
+ return fmt.Errorf("failed to orchestrate vertex pods [%v, %v): %w", updatedReplicas, desiredReplicas, err)
}
- } else {
- log.Infow("Deleted a stale service", zap.String("service", v.Name))
- r.recorder.Eventf(vertex, corev1.EventTypeNormal, "DelSvcSuccess", "Deleted stale service %s", v.Name)
+ }
+ vertex.Status.UpdatedReplicas = uint32(desiredReplicas)
+ vertex.Status.CurrentHash = vertex.Status.UpdateHash
+ } else { // Update scenario
+ if updatedReplicas >= desiredReplicas {
+ return nil
+ }
+
+ // Create more pods
+ if vertex.Spec.UpdateStrategy.GetUpdateStrategyType() != dfv1.RollingUpdateStrategyType {
+ // Revisit later, we only support rolling update for now
+ return nil
+ }
+
+ // Calculate the to be updated replicas based on the max unavailable configuration
+ maxUnavailConf := vertex.Spec.UpdateStrategy.GetRollingUpdateStrategy().GetMaxUnavailable()
+ toBeUpdated, err := intstr.GetScaledValueFromIntOrPercent(&maxUnavailConf, desiredReplicas, true)
+ if err != nil { // This should never happen since we have validated the configuration
+ return fmt.Errorf("invalid max unavailable configuration in rollingUpdate: %w", err)
+ }
+ if updatedReplicas+toBeUpdated > desiredReplicas {
+ toBeUpdated = desiredReplicas - updatedReplicas
+ }
+ log.Infof("Rolling update %d replicas, [%d, %d)", toBeUpdated, updatedReplicas, updatedReplicas+toBeUpdated)
+
+ // Create pods [updatedReplicas, updatedReplicas+toBeUpdated), and clean up any pods in that range that has a different hash
+ if err := r.orchestratePodsFromTo(ctx, vertex, pipeline, isbSvc, updatedReplicas, updatedReplicas+toBeUpdated, vertex.Status.UpdateHash); err != nil {
+ return fmt.Errorf("failed to orchestrate pods [%v, %v)]: %w", updatedReplicas, updatedReplicas+toBeUpdated, err)
+ }
+ vertex.Status.UpdatedReplicas = uint32(updatedReplicas + toBeUpdated)
+ if vertex.Status.UpdatedReplicas == uint32(desiredReplicas) {
+ vertex.Status.CurrentHash = vertex.Status.UpdateHash
}
}
- pipeline := &dfv1.Pipeline{}
- if err := r.client.Get(ctx, types.NamespacedName{Namespace: vertex.Namespace, Name: vertex.Spec.PipelineName}, pipeline); err != nil {
- log.Errorw("Failed to get pipeline object", zap.Error(err))
- vertex.Status.MarkPhaseFailed("GetPipelineFailed", err.Error())
- return ctrl.Result{}, err
+ currentReplicas := int(vertex.Status.Replicas)
+ if currentReplicas != desiredReplicas {
+ log.Infow("Pipeline Vertex replicas changed", "currentReplicas", currentReplicas, "desiredReplicas", desiredReplicas)
+ r.recorder.Eventf(vertex, corev1.EventTypeNormal, "ReplicasScaled", "Replicas changed from %d to %d", currentReplicas, desiredReplicas)
+ vertex.Status.Replicas = uint32(desiredReplicas)
+ vertex.Status.LastScaledAt = metav1.Time{Time: time.Now()}
}
- // Create pods
- existingPods, err := r.findExistingPods(ctx, vertex)
+ if vertex.Status.Selector == "" {
+ selector, _ := labels.Parse(dfv1.KeyPipelineName + "=" + vertex.Spec.PipelineName + "," + dfv1.KeyVertexName + "=" + vertex.Spec.Name)
+ vertex.Status.Selector = selector.String()
+ }
+
+ return nil
+}
+
+func (r *vertexReconciler) orchestratePodsFromTo(ctx context.Context, vertex *dfv1.Vertex, pipeline *dfv1.Pipeline, isbSvc *dfv1.InterStepBufferService, fromReplica, toReplica int, newHash string) error {
+ log := logging.FromContext(ctx)
+ existingPods, err := r.findExistingPods(ctx, vertex, fromReplica, toReplica)
if err != nil {
- log.Errorw("Failed to find existing pods", zap.Error(err))
- vertex.Status.MarkPhaseFailed("FindExistingPodFailed", err.Error())
- return ctrl.Result{}, err
+ return fmt.Errorf("failed to find existing pods: %w", err)
}
- for replica := 0; replica < desiredReplicas; replica++ {
+ for replica := fromReplica; replica < toReplica; replica++ {
podSpec, err := r.buildPodSpec(vertex, pipeline, isbSvc.Status.Config, replica)
if err != nil {
- log.Errorw("Failed to generate pod spec", zap.Error(err))
- vertex.Status.MarkPhaseFailed("PodSpecGenFailed", err.Error())
- return ctrl.Result{}, err
+ return fmt.Errorf("failed to generate pod spec: %w", err)
}
- hash := sharedutil.MustHash(podSpec)
podNamePrefix := fmt.Sprintf("%s-%d-", vertex.Name, replica)
needToCreate := true
for existingPodName, existingPod := range existingPods {
if strings.HasPrefix(existingPodName, podNamePrefix) {
- if existingPod.GetAnnotations()[dfv1.KeyHash] == hash && existingPod.Status.Phase != corev1.PodFailed {
+ if existingPod.GetAnnotations()[dfv1.KeyHash] == newHash && existingPod.Status.Phase != corev1.PodFailed {
needToCreate = false
delete(existingPods, existingPodName)
}
@@ -272,7 +343,7 @@ func (r *vertexReconciler) reconcile(ctx context.Context, vertex *dfv1.Vertex) (
labels[dfv1.KeyAppName] = vertex.Name
labels[dfv1.KeyPipelineName] = vertex.Spec.PipelineName
labels[dfv1.KeyVertexName] = vertex.Spec.Name
- annotations[dfv1.KeyHash] = hash
+ annotations[dfv1.KeyHash] = newHash
annotations[dfv1.KeyReplica] = strconv.Itoa(replica)
if vertex.IsMapUDF() || vertex.IsReduceUDF() {
annotations[dfv1.KeyDefaultContainer] = dfv1.CtrUdf
@@ -297,52 +368,73 @@ func (r *vertexReconciler) reconcile(ctx context.Context, vertex *dfv1.Vertex) (
}
pod.Spec.Hostname = fmt.Sprintf("%s-%d", vertex.Name, replica)
if err := r.client.Create(ctx, pod); err != nil {
- r.markPhaseFailedAndLogEvent(vertex, log, "CreatePodFailed", err.Error(), "Failed to created pod", zap.Error(err))
- return ctrl.Result{}, err
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "CreatePodFailed", "Failed to create a pod %s", pod.Name)
+ return fmt.Errorf("failed to create a vertex pod: %w", err)
}
log.Infow("Succeeded to create a pod", zap.String("pod", pod.Name))
- r.recorder.Eventf(vertex, corev1.EventTypeNormal, "CreatePodSuccess", "Succeeded to create pod %s", pod.Name)
+ r.recorder.Eventf(vertex, corev1.EventTypeNormal, "CreatePodSuccess", "Succeeded to create a pod %s", pod.Name)
}
}
for _, v := range existingPods {
if err := r.client.Delete(ctx, &v); err != nil && !apierrors.IsNotFound(err) {
- r.markPhaseFailedAndLogEvent(vertex, log, "DelPodFailed", err.Error(), "Failed to delete pod", zap.Error(err))
- return ctrl.Result{}, err
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "DelPodFailed", "Failed to delete pod %s", v.Name)
+ return fmt.Errorf("failed to delete a vertex pod %s: %w", v.Name, err)
}
}
+ return nil
+}
- currentReplicas := int(vertex.Status.Replicas)
- if currentReplicas != desiredReplicas || vertex.Status.Selector == "" {
- log.Infow("Pipeline Vertex replicas changed", "currentReplicas", currentReplicas, "desiredReplicas", desiredReplicas)
- r.recorder.Eventf(vertex, corev1.EventTypeNormal, "ReplicasScaled", "Replicas changed from %d to %d", currentReplicas, desiredReplicas)
- vertex.Status.Replicas = uint32(desiredReplicas)
- vertex.Status.LastScaledAt = metav1.Time{Time: time.Now()}
+func (r *vertexReconciler) cleanUpPodsFromTo(ctx context.Context, vertex *dfv1.Vertex, fromReplica, toReplica int) error {
+ log := logging.FromContext(ctx)
+ existingPods, err := r.findExistingPods(ctx, vertex, fromReplica, toReplica)
+ if err != nil {
+ return fmt.Errorf("failed to find existing pods: %w", err)
}
- selector, _ := labels.Parse(dfv1.KeyPipelineName + "=" + vertex.Spec.PipelineName + "," + dfv1.KeyVertexName + "=" + vertex.Spec.Name)
- vertex.Status.Selector = selector.String()
-
- // Mark it running before checking the status of the pods
- vertex.Status.MarkPhaseRunning()
- // Check status of the pods
- var podList corev1.PodList
- if err := r.client.List(ctx, &podList, &client.ListOptions{Namespace: vertex.GetNamespace(), LabelSelector: selector}); err != nil {
- vertex.Status.MarkPodNotHealthy("ListVerticesPodsFailed", err.Error())
- return ctrl.Result{}, fmt.Errorf("failed to get pods of a vertex: %w", err)
+ for _, pod := range existingPods {
+ if err := r.client.Delete(ctx, &pod); err != nil {
+ return fmt.Errorf("failed to delete pod %s: %w", pod.Name, err)
+ }
+ log.Infof("Deleted Vertx pod %q", pod.Name)
+ r.recorder.Eventf(vertex, corev1.EventTypeNormal, "DeletePodSuccess", "Succeeded to delete a vertex pod %s", pod.Name)
}
- readyPods := reconciler.NumOfReadyPods(podList)
- if readyPods > desiredReplicas { // It might happen in some corner cases, such as during rollout
- readyPods = desiredReplicas
+ return nil
+}
+
+func (r *vertexReconciler) buildReduceVertexPVCs(ctx context.Context, vertex *dfv1.Vertex) error {
+ if !vertex.IsReduceUDF() {
+ return nil
}
- vertex.Status.ReadyReplicas = uint32(readyPods)
- if healthy, reason, msg := reconciler.CheckPodsStatus(&podList); healthy {
- vertex.Status.MarkPodHealthy(reason, msg)
- } else {
- // Do not need to explicitly requeue, since the it keeps watching the status change of the pods
- vertex.Status.MarkPodNotHealthy(reason, msg)
+ if x := vertex.Spec.UDF.GroupBy.Storage; x != nil && x.PersistentVolumeClaim != nil {
+ log := logging.FromContext(ctx)
+ for i := 0; i < vertex.GetPartitionCount(); i++ {
+ newPvc, err := r.buildReduceVertexPVCSpec(vertex, i)
+ if err != nil {
+ return fmt.Errorf("failed to build a PVC spec: %w", err)
+ }
+ hash := sharedutil.MustHash(newPvc.Spec)
+ newPvc.SetAnnotations(map[string]string{dfv1.KeyHash: hash})
+ existingPvc := &corev1.PersistentVolumeClaim{}
+ if err := r.client.Get(ctx, types.NamespacedName{Namespace: vertex.Namespace, Name: newPvc.Name}, existingPvc); err != nil {
+ if !apierrors.IsNotFound(err) {
+ return fmt.Errorf("failed to find existing PVC: %w", err)
+ }
+ if err := r.client.Create(ctx, newPvc); err != nil && !apierrors.IsAlreadyExists(err) {
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "CreatePVCFailed", "Error creating a PVC: %s", err.Error())
+ return fmt.Errorf("failed to create a PVC: %w", err)
+ }
+ r.recorder.Eventf(vertex, corev1.EventTypeNormal, "CreatePVCSuccess", "Successfully created PVC %s", newPvc.Name)
+ } else {
+ if existingPvc.GetAnnotations()[dfv1.KeyHash] != hash {
+ // TODO: deal with spec difference
+ if false {
+ log.Debug("TODO: check spec difference")
+ }
+ }
+ }
+ }
}
-
- return ctrl.Result{}, nil
+ return nil
}
func (r *vertexReconciler) buildReduceVertexPVCSpec(vertex *dfv1.Vertex, replicaIndex int) (*corev1.PersistentVolumeClaim, error) {
@@ -364,6 +456,60 @@ func (r *vertexReconciler) buildReduceVertexPVCSpec(vertex *dfv1.Vertex, replica
return &newPvc, nil
}
+func (r *vertexReconciler) createOrUpdateServices(ctx context.Context, vertex *dfv1.Vertex) error {
+ log := logging.FromContext(ctx)
+ existingSvcs, err := r.findExistingServices(ctx, vertex)
+ if err != nil {
+ return fmt.Errorf("failed to find existing services: %w", err)
+ }
+ for _, s := range vertex.GetServiceObjs() {
+ svcHash := sharedutil.MustHash(s.Spec)
+ s.Annotations = map[string]string{dfv1.KeyHash: svcHash}
+ needToCreate := false
+ if existingSvc, existing := existingSvcs[s.Name]; existing {
+ if existingSvc.GetAnnotations()[dfv1.KeyHash] != svcHash {
+ if err := r.client.Delete(ctx, &existingSvc); err != nil {
+ if !apierrors.IsNotFound(err) {
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "DelSvcFailed", "Error deleting existing service: %s", err.Error())
+ return fmt.Errorf("failed to delete existing service: %w", err)
+ }
+ } else {
+ log.Infow("Deleted a stale service to recreate", zap.String("service", existingSvc.Name))
+ r.recorder.Eventf(vertex, corev1.EventTypeNormal, "DelSvcSuccess", "Deleted stale service %s to recreate", existingSvc.Name)
+ }
+ needToCreate = true
+ }
+ delete(existingSvcs, s.Name)
+ } else {
+ needToCreate = true
+ }
+ if needToCreate {
+ if err := r.client.Create(ctx, s); err != nil {
+ if apierrors.IsAlreadyExists(err) {
+ continue
+ }
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "CreateSvcFailed", "Error creating a service: %s", err.Error())
+ return fmt.Errorf("failed to create a service: %w", err)
+ } else {
+ log.Infow("Succeeded to create a service", zap.String("service", s.Name))
+ r.recorder.Eventf(vertex, corev1.EventTypeNormal, "CreateSvcSuccess", "Succeeded to create service %s", s.Name)
+ }
+ }
+ }
+ for _, v := range existingSvcs { // clean up stale services
+ if err := r.client.Delete(ctx, &v); err != nil {
+ if !apierrors.IsNotFound(err) {
+ r.recorder.Eventf(vertex, corev1.EventTypeWarning, "DelSvcFailed", "Error deleting existing service that is not in use: %s", err.Error())
+ return fmt.Errorf("failed to delete existing service that is not in use: %w", err)
+ }
+ } else {
+ log.Infow("Deleted a stale service", zap.String("service", v.Name))
+ r.recorder.Eventf(vertex, corev1.EventTypeNormal, "DelSvcSuccess", "Deleted stale service %s", v.Name)
+ }
+ }
+ return nil
+}
+
func (r *vertexReconciler) buildPodSpec(vertex *dfv1.Vertex, pl *dfv1.Pipeline, isbSvcConfig dfv1.BufferServiceConfig, replicaIndex int) (*corev1.PodSpec, error) {
isbSvcType, envs := sharedutil.GetIsbSvcEnvVars(isbSvcConfig)
podSpec, err := vertex.GetPodSpec(dfv1.GetVertexPodSpecReq{
@@ -425,7 +571,7 @@ func (r *vertexReconciler) buildPodSpec(vertex *dfv1.Vertex, pl *dfv1.Pipeline,
return podSpec, nil
}
-func (r *vertexReconciler) findExistingPods(ctx context.Context, vertex *dfv1.Vertex) (map[string]corev1.Pod, error) {
+func (r *vertexReconciler) findExistingPods(ctx context.Context, vertex *dfv1.Vertex, fromReplica, toReplica int) (map[string]corev1.Pod, error) {
pods := &corev1.PodList{}
selector, _ := labels.Parse(dfv1.KeyPipelineName + "=" + vertex.Spec.PipelineName + "," + dfv1.KeyVertexName + "=" + vertex.Spec.Name)
if err := r.client.List(ctx, pods, &client.ListOptions{Namespace: vertex.Namespace, LabelSelector: selector}); err != nil {
@@ -437,7 +583,11 @@ func (r *vertexReconciler) findExistingPods(ctx context.Context, vertex *dfv1.Ve
// Ignore pods being deleted
continue
}
- result[v.Name] = v
+ replicaStr := v.GetAnnotations()[dfv1.KeyReplica]
+ replica, _ := strconv.Atoi(replicaStr)
+ if replica >= fromReplica && replica < toReplica {
+ result[v.Name] = v
+ }
}
return result, nil
}
@@ -454,10 +604,3 @@ func (r *vertexReconciler) findExistingServices(ctx context.Context, vertex *dfv
}
return result, nil
}
-
-// Helper function for warning event types
-func (r *vertexReconciler) markPhaseFailedAndLogEvent(vertex *dfv1.Vertex, log *zap.SugaredLogger, reason, message, logMsg string, logWith ...interface{}) {
- log.Errorw(logMsg, logWith)
- vertex.Status.MarkPhaseFailed(reason, message)
- r.recorder.Event(vertex, corev1.EventTypeWarning, reason, message)
-}
diff --git a/pkg/reconciler/vertex/controller_test.go b/pkg/reconciler/vertex/controller_test.go
index dedc25898e..4a2faa56b0 100644
--- a/pkg/reconciler/vertex/controller_test.go
+++ b/pkg/reconciler/vertex/controller_test.go
@@ -20,6 +20,7 @@ import (
"context"
"strings"
"testing"
+ "time"
"github.com/stretchr/testify/assert"
"go.uber.org/zap/zaptest"
@@ -28,14 +29,18 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
+ "k8s.io/utils/ptr"
+ ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/reconciler"
"github.com/numaproj/numaflow/pkg/reconciler/vertex/scaling"
+ sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
)
const (
@@ -166,6 +171,19 @@ func init() {
_ = corev1.AddToScheme(scheme.Scheme)
}
+func fakeReconciler(t *testing.T, cl client.WithWatch) *vertexReconciler {
+ t.Helper()
+ return &vertexReconciler{
+ client: cl,
+ scheme: scheme.Scheme,
+ config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
+ image: testFlowImage,
+ logger: zaptest.NewLogger(t).Sugar(),
+ recorder: record.NewFakeRecorder(64),
+ scaler: scaling.NewScaler(cl),
+ }
+}
+
func Test_NewReconciler(t *testing.T) {
cl := fake.NewClientBuilder().Build()
r := NewReconciler(cl, scheme.Scheme, reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig), testFlowImage, scaling.NewScaler(cl), zaptest.NewLogger(t).Sugar(), record.NewFakeRecorder(64))
@@ -173,19 +191,51 @@ func Test_NewReconciler(t *testing.T) {
assert.True(t, ok)
}
+func TestReconcile(t *testing.T) {
+ t.Run("test not found", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ req := ctrl.Request{
+ NamespacedName: types.NamespacedName{
+ Name: "not-exist",
+ Namespace: testNamespace,
+ },
+ }
+ _, err := r.Reconcile(context.TODO(), req)
+ // Return nil when not found
+ assert.NoError(t, err)
+ })
+
+ t.Run("test found", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testVertex.DeepCopy()
+ err := cl.Create(context.TODO(), testObj)
+ assert.NoError(t, err)
+ o := &dfv1.Vertex{}
+ err = cl.Get(context.TODO(), types.NamespacedName{
+ Namespace: testObj.Namespace,
+ Name: testObj.Name,
+ }, o)
+ assert.NoError(t, err)
+ assert.Equal(t, testObj.Name, o.Name)
+ req := ctrl.Request{
+ NamespacedName: types.NamespacedName{
+ Name: testObj.Name,
+ Namespace: testObj.Namespace,
+ },
+ }
+ _, err = r.Reconcile(context.TODO(), req)
+ assert.Error(t, err)
+ assert.ErrorContains(t, err, "not found")
+ })
+}
+
func Test_BuildPodSpec(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig)
t.Run("test source", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testSrcVertex.DeepCopy()
spec, err := r.buildPodSpec(testObj, testPipeline, fakeIsbSvcConfig, 0)
assert.NoError(t, err)
@@ -211,14 +261,7 @@ func Test_BuildPodSpec(t *testing.T) {
t.Run("test source with transformer", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testSrcVertex.DeepCopy()
testObj.Spec.Source = &dfv1.Source{
HTTP: &dfv1.HTTPSource{},
@@ -236,14 +279,7 @@ func Test_BuildPodSpec(t *testing.T) {
t.Run("test user-defined source with transformer", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testSrcVertex.DeepCopy()
testObj.Spec.Source = &dfv1.Source{
UDSource: &dfv1.UDSource{
@@ -265,14 +301,7 @@ func Test_BuildPodSpec(t *testing.T) {
t.Run("test sink", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Name = "test-pl-output"
testObj.Spec.Name = "output"
@@ -303,14 +332,7 @@ func Test_BuildPodSpec(t *testing.T) {
t.Run("test user-defined sink", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Name = "test-pl-output"
testObj.Spec.Name = "output"
@@ -340,14 +362,7 @@ func Test_BuildPodSpec(t *testing.T) {
t.Run("test map udf", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Spec.UDF = &dfv1.UDF{
Builtin: &dfv1.Function{
@@ -378,14 +393,7 @@ func Test_BuildPodSpec(t *testing.T) {
t.Run("test reduce udf", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
volSize, _ := resource.ParseQuantity("1Gi")
testObj.Spec.UDF = &dfv1.UDF{
@@ -422,7 +430,54 @@ func Test_BuildPodSpec(t *testing.T) {
}
func Test_reconcile(t *testing.T) {
- fakeConfig := reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig)
+
+ t.Run("test deletion", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testVertex.DeepCopy()
+ testObj.DeletionTimestamp = &metav1.Time{
+ Time: time.Now(),
+ }
+ _, err := r.reconcile(context.TODO(), testObj)
+ assert.NoError(t, err)
+ })
+
+ t.Run("test no isbsvc", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testObj := testVertex.DeepCopy()
+ _, err := r.reconcile(context.TODO(), testObj)
+ assert.Error(t, err)
+ assert.ErrorContains(t, err, "not found")
+ assert.Equal(t, testObj.Status.Phase, dfv1.VertexPhaseFailed)
+ assert.Equal(t, testObj.Status.Reason, "ISBSvcNotFound")
+ assert.Contains(t, testObj.Status.Message, "not found")
+ })
+
+ t.Run("test isbsvc unhealthy", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ r := fakeReconciler(t, cl)
+ testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
+ testIsbSvc.Status.MarkConfigured()
+ err := cl.Create(context.TODO(), testIsbSvc)
+ assert.Nil(t, err)
+ testPl := testPipeline.DeepCopy()
+ err = cl.Create(context.TODO(), testPl)
+ assert.Nil(t, err)
+ testObj := testVertex.DeepCopy()
+ testObj.Spec.Source = &dfv1.Source{
+ HTTP: &dfv1.HTTPSource{
+ Service: true,
+ },
+ }
+ _, err = r.reconcile(context.TODO(), testObj)
+ assert.Error(t, err)
+ assert.ErrorContains(t, err, "not healthy")
+ assert.Equal(t, testObj.Status.Phase, dfv1.VertexPhaseFailed)
+ assert.Equal(t, testObj.Status.Reason, "ISBSvcNotHealthy")
+ assert.Contains(t, testObj.Status.Message, "not healthy")
+ })
+
t.Run("test reconcile source", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
ctx := context.TODO()
@@ -434,15 +489,7 @@ func Test_reconcile(t *testing.T) {
testPl := testPipeline.DeepCopy()
err = cl.Create(ctx, testPl)
assert.Nil(t, err)
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- scaler: scaling.NewScaler(cl),
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Spec.Source = &dfv1.Source{
HTTP: &dfv1.HTTPSource{
@@ -486,15 +533,7 @@ func Test_reconcile(t *testing.T) {
testPl := testPipeline.DeepCopy()
err = cl.Create(ctx, testPl)
assert.Nil(t, err)
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- scaler: scaling.NewScaler(cl),
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Spec.Sink = &dfv1.Sink{}
_, err = r.reconcile(ctx, testObj)
@@ -519,15 +558,7 @@ func Test_reconcile(t *testing.T) {
testPl := testPipeline.DeepCopy()
err = cl.Create(ctx, testPl)
assert.Nil(t, err)
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- scaler: scaling.NewScaler(cl),
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Spec.UDF = &dfv1.UDF{
Builtin: &dfv1.Function{
@@ -545,7 +576,7 @@ func Test_reconcile(t *testing.T) {
assert.Equal(t, 2, len(pods.Items[0].Spec.Containers))
})
- t.Run("test reconcile vertex with customization", func(t *testing.T) {
+ t.Run("test reconcile reduce udf", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
ctx := context.TODO()
testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
@@ -556,15 +587,54 @@ func Test_reconcile(t *testing.T) {
testPl := testPipeline.DeepCopy()
err = cl.Create(ctx, testPl)
assert.Nil(t, err)
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- scaler: scaling.NewScaler(cl),
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
+ r := fakeReconciler(t, cl)
+ testObj := testVertex.DeepCopy()
+ testObj.Spec.UDF = &dfv1.UDF{
+ Container: &dfv1.Container{
+ Image: "my-image",
+ },
+ GroupBy: &dfv1.GroupBy{
+ Window: dfv1.Window{
+ Fixed: &dfv1.FixedWindow{
+ Length: &metav1.Duration{
+ Duration: 10 * time.Second,
+ },
+ },
+ },
+ Storage: &dfv1.PBQStorage{
+ PersistentVolumeClaim: &dfv1.PersistenceStrategy{
+ AccessMode: ptr.To[corev1.PersistentVolumeAccessMode](corev1.ReadWriteOnce),
+ },
+ },
+ },
}
+ _, err = r.reconcile(ctx, testObj)
+ assert.NoError(t, err)
+ pods := &corev1.PodList{}
+ selector, _ := labels.Parse(dfv1.KeyPipelineName + "=" + testPipelineName + "," + dfv1.KeyVertexName + "=" + testVertexSpecName)
+ err = r.client.List(ctx, pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(pods.Items))
+ assert.True(t, strings.HasPrefix(pods.Items[0].Name, testVertexName+"-0-"))
+ assert.Equal(t, 2, len(pods.Items[0].Spec.Containers))
+ pvc := &corev1.PersistentVolumeClaim{}
+ err = r.client.Get(ctx, types.NamespacedName{Name: dfv1.GeneratePBQStoragePVCName(testPl.Name, testObj.Spec.Name, 0), Namespace: testNamespace}, pvc)
+ assert.NoError(t, err)
+ assert.Equal(t, dfv1.GeneratePBQStoragePVCName(testPl.Name, testObj.Spec.Name, 0), pvc.Name)
+ })
+
+ t.Run("test reconcile vertex with customization", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ ctx := context.TODO()
+ testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
+ testIsbSvc.Status.MarkConfigured()
+ testIsbSvc.Status.MarkDeployed()
+ err := cl.Create(ctx, testIsbSvc)
+ assert.Nil(t, err)
+ testPl := testPipeline.DeepCopy()
+ err = cl.Create(ctx, testPl)
+ assert.Nil(t, err)
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Spec.Sink = &dfv1.Sink{}
testObj.Spec.ContainerTemplate = &dfv1.ContainerTemplate{
@@ -629,15 +699,7 @@ func Test_reconcile(t *testing.T) {
testPl.Spec.Vertices[1].SideInputs = []string{"s1"}
err = cl.Create(ctx, testPl)
assert.Nil(t, err)
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: fakeConfig,
- image: testFlowImage,
- scaler: scaling.NewScaler(cl),
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Spec.UDF = &dfv1.UDF{
Builtin: &dfv1.Function{
@@ -656,10 +718,84 @@ func Test_reconcile(t *testing.T) {
assert.Equal(t, 3, len(pods.Items[0].Spec.Containers))
assert.Equal(t, 2, len(pods.Items[0].Spec.InitContainers))
})
+
+ t.Run("test reconcile rolling update", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ ctx := context.TODO()
+ testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
+ testIsbSvc.Status.MarkConfigured()
+ testIsbSvc.Status.MarkDeployed()
+ err := cl.Create(ctx, testIsbSvc)
+ assert.Nil(t, err)
+ testPl := testPipeline.DeepCopy()
+ err = cl.Create(ctx, testPl)
+ assert.Nil(t, err)
+ r := fakeReconciler(t, cl)
+ testObj := testVertex.DeepCopy()
+ testObj.Spec.UDF = &dfv1.UDF{
+ Builtin: &dfv1.Function{
+ Name: "cat",
+ },
+ }
+ testObj.Spec.Replicas = ptr.To[int32](3)
+ _, err = r.reconcile(ctx, testObj)
+ assert.NoError(t, err)
+ pods := &corev1.PodList{}
+ selector, _ := labels.Parse(dfv1.KeyPipelineName + "=" + testPipelineName + "," + dfv1.KeyVertexName + "=" + testVertexSpecName)
+ err = r.client.List(ctx, pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 3, len(pods.Items))
+
+ tmpSpec, _ := r.buildPodSpec(testObj, testPl, testIsbSvc.Status.Config, 0)
+ hash := sharedutil.MustHash(tmpSpec)
+ testObj.Status.Replicas = 3
+ testObj.Status.ReadyReplicas = 3
+ testObj.Status.UpdateHash = hash
+ testObj.Status.CurrentHash = hash
+
+ // Reduce desired replicas
+ testObj.Spec.Replicas = ptr.To[int32](2)
+ _, err = r.reconcile(ctx, testObj)
+ assert.NoError(t, err)
+ err = r.client.List(ctx, pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(pods.Items))
+ assert.Equal(t, uint32(2), testObj.Status.Replicas)
+ assert.Equal(t, uint32(2), testObj.Status.UpdatedReplicas)
+
+ // updatedReplicas > desiredReplicas
+ testObj.Status.UpdatedReplicas = 3
+ _, err = r.reconcile(ctx, testObj)
+ assert.NoError(t, err)
+ assert.Equal(t, uint32(2), testObj.Status.UpdatedReplicas)
+
+ // Clean up
+ testObj.Spec.Replicas = ptr.To[int32](0)
+ _, err = r.reconcile(ctx, testObj)
+ assert.NoError(t, err)
+ err = r.client.List(ctx, pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(pods.Items))
+
+ // rolling update
+ testObj.Spec.Replicas = ptr.To[int32](20)
+ testObj.Status.UpdatedReplicas = 20
+ testObj.Status.UpdatedReadyReplicas = 20
+ testObj.Status.Replicas = 20
+ testObj.Status.CurrentHash = "123456"
+ testObj.Status.UpdateHash = "123456"
+ _, err = r.reconcile(ctx, testObj)
+ assert.NoError(t, err)
+ err = r.client.List(ctx, pods, &client.ListOptions{Namespace: testNamespace, LabelSelector: selector})
+ assert.NoError(t, err)
+ assert.Equal(t, 5, len(pods.Items))
+ assert.Equal(t, uint32(20), testObj.Status.Replicas)
+ assert.Equal(t, uint32(5), testObj.Status.UpdatedReplicas)
+ })
}
func Test_reconcileEvents(t *testing.T) {
- t.Run("test reconcile - isbsvc doesn't exist", func(t *testing.T) {
+ t.Run("test reconcile - events", func(t *testing.T) {
cl := fake.NewClientBuilder().Build()
ctx := context.TODO()
testIsbSvc := testNativeRedisIsbSvc.DeepCopy()
@@ -670,15 +806,7 @@ func Test_reconcileEvents(t *testing.T) {
testPl := testPipeline.DeepCopy()
err = cl.Create(ctx, testPl)
assert.Nil(t, err)
- r := &vertexReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- image: testFlowImage,
- scaler: scaling.NewScaler(cl),
- logger: zaptest.NewLogger(t).Sugar(),
- recorder: record.NewFakeRecorder(64),
- }
+ r := fakeReconciler(t, cl)
testObj := testVertex.DeepCopy()
testObj.Spec.UDF = &dfv1.UDF{
Builtin: &dfv1.Function{
diff --git a/rust/numaflow-models/src/models/abstract_vertex.rs b/rust/numaflow-models/src/models/abstract_vertex.rs
index 23fb85c813..6dffd1237a 100644
--- a/rust/numaflow-models/src/models/abstract_vertex.rs
+++ b/rust/numaflow-models/src/models/abstract_vertex.rs
@@ -95,6 +95,8 @@ pub struct AbstractVertex {
pub tolerations: Option>,
#[serde(rename = "udf", skip_serializing_if = "Option::is_none")]
pub udf: Option>,
+ #[serde(rename = "updateStrategy", skip_serializing_if = "Option::is_none")]
+ pub update_strategy: Option>,
#[serde(rename = "volumes", skip_serializing_if = "Option::is_none")]
pub volumes: Option>,
}
@@ -129,6 +131,7 @@ impl AbstractVertex {
source: None,
tolerations: None,
udf: None,
+ update_strategy: None,
volumes: None,
}
}
diff --git a/rust/numaflow-models/src/models/vertex_spec.rs b/rust/numaflow-models/src/models/vertex_spec.rs
index a647ecc7ae..7583c1d6ac 100644
--- a/rust/numaflow-models/src/models/vertex_spec.rs
+++ b/rust/numaflow-models/src/models/vertex_spec.rs
@@ -108,6 +108,8 @@ pub struct VertexSpec {
pub tolerations: Option>,
#[serde(rename = "udf", skip_serializing_if = "Option::is_none")]
pub udf: Option>,
+ #[serde(rename = "updateStrategy", skip_serializing_if = "Option::is_none")]
+ pub update_strategy: Option>,
#[serde(rename = "volumes", skip_serializing_if = "Option::is_none")]
pub volumes: Option>,
#[serde(rename = "watermark", skip_serializing_if = "Option::is_none")]
@@ -149,6 +151,7 @@ impl VertexSpec {
to_edges: None,
tolerations: None,
udf: None,
+ update_strategy: None,
volumes: None,
watermark: None,
}
diff --git a/rust/numaflow-models/src/models/vertex_status.rs b/rust/numaflow-models/src/models/vertex_status.rs
index 950ffa9ba5..30cb952f6d 100644
--- a/rust/numaflow-models/src/models/vertex_status.rs
+++ b/rust/numaflow-models/src/models/vertex_status.rs
@@ -21,12 +21,12 @@ pub struct VertexStatus {
/// Conditions are the latest available observations of a resource's current state.
#[serde(rename = "conditions", skip_serializing_if = "Option::is_none")]
pub conditions: Option>,
- /// If not empty, indicates the version of the Vertex used to generate Pods in the sequence [0,currentReplicas).
+ /// If not empty, indicates the current version of the Vertex used to generate Pods.
#[serde(rename = "currentHash", skip_serializing_if = "Option::is_none")]
pub current_hash: Option,
- /// The number of Pods created by the controller from the Vertex version indicated by currentHash.
- #[serde(rename = "currentReplicas", skip_serializing_if = "Option::is_none")]
- pub current_replicas: Option,
+ /// The number of desired replicas.
+ #[serde(rename = "desiredReplicas", skip_serializing_if = "Option::is_none")]
+ pub desired_replicas: Option,
#[serde(rename = "lastScaledAt", skip_serializing_if = "Option::is_none")]
pub last_scaled_at: Option,
#[serde(rename = "message", skip_serializing_if = "Option::is_none")]
@@ -46,9 +46,15 @@ pub struct VertexStatus {
pub replicas: Option,
#[serde(rename = "selector", skip_serializing_if = "Option::is_none")]
pub selector: Option,
- /// If not empty, indicates the version of the Vertx used to generate Pods in the sequence [replicas-updatedReplicas,replicas)
+ /// If not empty, indicates the updated version of the Vertex used to generate Pods.
#[serde(rename = "updateHash", skip_serializing_if = "Option::is_none")]
pub update_hash: Option,
+ /// The number of ready Pods created by the controller from the Vertex version indicated by updateHash.
+ #[serde(
+ rename = "updatedReadyReplicas",
+ skip_serializing_if = "Option::is_none"
+ )]
+ pub updated_ready_replicas: Option,
/// The number of Pods created by the controller from the Vertex version indicated by updateHash.
#[serde(rename = "updatedReplicas", skip_serializing_if = "Option::is_none")]
pub updated_replicas: Option,
@@ -59,7 +65,7 @@ impl VertexStatus {
VertexStatus {
conditions: None,
current_hash: None,
- current_replicas: None,
+ desired_replicas: None,
last_scaled_at: None,
message: None,
observed_generation: None,
@@ -69,6 +75,7 @@ impl VertexStatus {
replicas: None,
selector: None,
update_hash: None,
+ updated_ready_replicas: None,
updated_replicas: None,
}
}
From 24c6553bbf4af02b6660520e51d1e8331b200ecb Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Mon, 9 Sep 2024 12:43:00 -0700
Subject: [PATCH 053/188] chore: pin dependency versions in rust model (#2048)
Signed-off-by: Derek Wang
---
rust/Cargo.lock | 13 ++---
rust/monovertex/Cargo.toml | 2 +-
rust/numaflow-models/Cargo.toml | 2 +-
rust/numaflow-models/Makefile | 2 -
rust/numaflow-models/templates/Cargo.mustache | 53 ++-----------------
5 files changed, 13 insertions(+), 59 deletions(-)
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 6b34934210..21d6a28a7d 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1274,9 +1274,9 @@ dependencies = [
[[package]]
name = "kube"
-version = "0.93.1"
+version = "0.94.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0365920075af1a2d23619c1ca801c492f2400157de42627f041a061716e76416"
+checksum = "65b8611df85a1a2eed6f47bd8bcca4e2b3dc14fbf83658efd01423ca9a13b72a"
dependencies = [
"k8s-openapi",
"kube-client",
@@ -1285,9 +1285,9 @@ dependencies = [
[[package]]
name = "kube-client"
-version = "0.93.1"
+version = "0.94.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d81336eb3a5b10a40c97a5a97ad66622e92bad942ce05ee789edd730aa4f8603"
+checksum = "93c5ee3e48ef9b8d8fdb40ddd935f8addc8a201397e3c7552edae7bc96bc0a78"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -1323,15 +1323,16 @@ dependencies = [
[[package]]
name = "kube-core"
-version = "0.93.1"
+version = "0.94.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cce373a74d787d439063cdefab0f3672860bd7bac01a38e39019177e764a0fe6"
+checksum = "7fe6e24d4cc7e32576f363986dc3dfc13e8e90731bd7a467b67fc6c4bfbf8e95"
dependencies = [
"chrono",
"form_urlencoded",
"http 1.1.0",
"k8s-openapi",
"serde",
+ "serde-value",
"serde_json",
"thiserror",
]
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index 90ddc5cbec..01eb5afafd 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -33,7 +33,7 @@ pep440_rs = "0.6.6"
backoff = { path = "../backoff" }
parking_lot = "0.12.3"
prometheus-client = "0.22.3"
-kube = "0.93.1"
+kube = "0.94.0"
[dev-dependencies]
tempfile = "3.11.0"
diff --git a/rust/numaflow-models/Cargo.toml b/rust/numaflow-models/Cargo.toml
index 1e133e3b08..ecc0592efe 100644
--- a/rust/numaflow-models/Cargo.toml
+++ b/rust/numaflow-models/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
k8s-openapi = { version = "0.22.0", features = ["v1_29"] }
-kube = "0.93.1"
+kube = "0.94.0"
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
diff --git a/rust/numaflow-models/Makefile b/rust/numaflow-models/Makefile
index 069179f746..42cfdda255 100644
--- a/rust/numaflow-models/Makefile
+++ b/rust/numaflow-models/Makefile
@@ -62,6 +62,4 @@ generate:
--type-mappings IntOrString="k8s_openapi::apimachinery::pkg::util::intstr::IntOrString" \
--generate-alias-as-model
- cargo add kube
- cargo add k8s-openapi --features v1_29
cargo fmt
diff --git a/rust/numaflow-models/templates/Cargo.mustache b/rust/numaflow-models/templates/Cargo.mustache
index f7d1cdeb7c..ecc0592efe 100644
--- a/rust/numaflow-models/templates/Cargo.mustache
+++ b/rust/numaflow-models/templates/Cargo.mustache
@@ -1,65 +1,20 @@
[package]
-name = "{{{packageName}}}"
-version = "{{#lambdaVersion}}{{{packageVersion}}}{{/lambdaVersion}}"
-{{#infoEmail}}
-authors = ["{{{.}}}"]
-{{/infoEmail}}
-{{^infoEmail}}
+name = "numaflow-models"
+version = "0.0.0-pre"
authors = ["The Numaproj Authors"]
-{{/infoEmail}}
-{{#appDescription}}
description = "Numaflow models"
-{{/appDescription}}
license = "Apache-2.0 license"
edition = "2021"
-{{#publishRustRegistry}}
-publish = ["{{.}}"]
-{{/publishRustRegistry}}
-{{#repositoryUrl}}
-repository = "{{.}}"
-{{/repositoryUrl}}
-{{#documentationUrl}}
-documentation = "{{.}}"
-{{/documentationUrl}}
-{{#homePageUrl}}
-homepage = "{{.}}
-{{/homePageUrl}}
[dependencies]
+k8s-openapi = { version = "0.22.0", features = ["v1_29"] }
+kube = "0.94.0"
serde = "^1.0"
serde_derive = "^1.0"
-{{#serdeWith}}
-serde_with = "^2.0"
-{{/serdeWith}}
serde_json = "^1.0"
url = "^2.2"
uuid = { version = "^1.0", features = ["serde", "v4"] }
-{{#hyper}}
-hyper = { version = "~0.14", features = ["full"] }
-hyper-tls = "~0.5"
-http = "~0.2"
-base64 = "~0.7.0"
-futures = "^0.3"
-{{/hyper}}
-{{#withAWSV4Signature}}
-aws-sigv4 = "0.3.0"
-http = "0.2.5"
-secrecy = "0.8.0"
-{{/withAWSV4Signature}}
-{{#reqwest}}
-{{^supportAsync}}
-[dependencies.reqwest]
-version = "^0.11"
-default-features = false
-features = ["json", "blocking", "multipart", "rustls-tls"]
-{{/supportAsync}}
-{{#supportAsync}}
-{{#supportMiddleware}}
-reqwest-middleware = "0.2.0"
-{{/supportMiddleware}}
[dependencies.reqwest]
version = "^0.11"
default-features = false
features = ["json", "multipart", "rustls-tls"]
-{{/supportAsync}}
-{{/reqwest}}
From ba40b1500416a258fe131273d3cfc4b46a93a88f Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Mon, 9 Sep 2024 17:21:35 -0400
Subject: [PATCH 054/188] fix: builtin transformer should keep the keys (#2047)
Signed-off-by: Keran Yang
---
.../event_time/event_time_extractor.go | 10 +++---
.../event_time/event_time_extractor_test.go | 34 ++++++++++++++-----
.../transformer/builtin/filter/filter.go | 10 +++---
.../transformer/builtin/filter/filter_test.go | 10 ++++--
.../time_extraction_filter.go | 10 +++---
.../time_extraction_filter_test.go | 21 ++++++++----
6 files changed, 61 insertions(+), 34 deletions(-)
diff --git a/pkg/sources/transformer/builtin/event_time/event_time_extractor.go b/pkg/sources/transformer/builtin/event_time/event_time_extractor.go
index ff143f2c3d..c844c518db 100644
--- a/pkg/sources/transformer/builtin/event_time/event_time_extractor.go
+++ b/pkg/sources/transformer/builtin/event_time/event_time_extractor.go
@@ -56,7 +56,7 @@ func New(args map[string]string) (sourcetransformer.SourceTransformFunc, error)
return func(ctx context.Context, keys []string, datum sourcetransformer.Datum) sourcetransformer.Messages {
log := logging.FromContext(ctx)
- resultMsg, err := e.apply(datum.Value(), datum.EventTime())
+ resultMsg, err := e.apply(datum.Value(), datum.EventTime(), keys)
if err != nil {
log.Warnf("event time extractor got an error: %v, skip updating event time...", err)
}
@@ -66,10 +66,10 @@ func New(args map[string]string) (sourcetransformer.SourceTransformFunc, error)
// apply compiles the payload to extract the new event time. If there is any error during extraction,
// we pass on the original input event time. Otherwise, we assign the new event time to the message.
-func (e eventTimeExtractor) apply(payload []byte, et time.Time) (sourcetransformer.Message, error) {
+func (e eventTimeExtractor) apply(payload []byte, et time.Time, keys []string) (sourcetransformer.Message, error) {
timeStr, err := expr.EvalStr(e.expression, payload)
if err != nil {
- return sourcetransformer.NewMessage(payload, et), err
+ return sourcetransformer.NewMessage(payload, et).WithKeys(keys), err
}
var newEventTime time.Time
@@ -80,8 +80,8 @@ func (e eventTimeExtractor) apply(payload []byte, et time.Time) (sourcetransform
newEventTime, err = dateparse.ParseStrict(timeStr)
}
if err != nil {
- return sourcetransformer.NewMessage(payload, et), err
+ return sourcetransformer.NewMessage(payload, et).WithKeys(keys), err
} else {
- return sourcetransformer.NewMessage(payload, newEventTime), nil
+ return sourcetransformer.NewMessage(payload, newEventTime).WithKeys(keys), nil
}
}
diff --git a/pkg/sources/transformer/builtin/event_time/event_time_extractor_test.go b/pkg/sources/transformer/builtin/event_time/event_time_extractor_test.go
index 22fc6b631e..d14e37beed 100644
--- a/pkg/sources/transformer/builtin/event_time/event_time_extractor_test.go
+++ b/pkg/sources/transformer/builtin/event_time/event_time_extractor_test.go
@@ -24,6 +24,8 @@ import (
"github.com/stretchr/testify/assert"
)
+var _keys = []string{"test-key"}
+
type testDatum struct {
value []byte
eventTime time.Time
@@ -74,7 +76,7 @@ func TestEventTimeExtractor(t *testing.T) {
assert.NoError(t, err)
testJsonMsg := `{"test": 21, "item": [{"id": 1, "name": "numa", "time": "2022-02-18T21:54:42.123Z"},{"id": 2, "name": "numa", "time": "2021-02-18T21:54:42.123Z"}]}`
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: time.Time{},
watermark: time.Time{},
@@ -86,6 +88,8 @@ func TestEventTimeExtractor(t *testing.T) {
assert.True(t, expected.Equal(result.Items()[0].EventTime()))
// Verify the payload remains unchanged.
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // Verify the keys remain unchanged.
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("Json expression valid, assign a new event time to the message - format specified", func(t *testing.T) {
@@ -94,7 +98,7 @@ func TestEventTimeExtractor(t *testing.T) {
assert.NoError(t, err)
testJsonMsg := `{"test": 21, "item": [{"id": 1, "name": "numa", "time": "2022-02-18T21:54:42.123Z"},{"id": 2, "name": "numa", "time": "2021-02-18T21:54:42.123Z"}]}`
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: time.Time{},
watermark: time.Time{},
@@ -106,6 +110,8 @@ func TestEventTimeExtractor(t *testing.T) {
assert.True(t, expected.Equal(result.Items()[0].EventTime()))
// Verify the payload remains unchanged.
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // Verify the keys remain unchanged.
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("Time string not matching user-provided format, pass on the message without assigning new event time", func(t *testing.T) {
@@ -114,9 +120,9 @@ func TestEventTimeExtractor(t *testing.T) {
assert.NoError(t, err)
testInputEventTime := time.Date(2022, 1, 4, 2, 3, 4, 5, time.UTC)
- // Handler receives format as time.ANSIC but in the message, we use time.RFC3339. Format is not matched.
+ // Handler receives a format as time.ANSIC but in the message, we use time.RFC3339. Format is not matched.
testJsonMsg := `{"test": 21, "item": [{"id": 1, "name": "numa", "time": "2022-02-18T21:54:42.123Z"},{"id": 2, "name": "numa", "time": "2021-02-18T21:54:42.123Z"}]}`
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: testInputEventTime,
watermark: time.Time{},
@@ -126,6 +132,8 @@ func TestEventTimeExtractor(t *testing.T) {
assert.Equal(t, testInputEventTime, result.Items()[0].EventTime())
// Verify the payload remains unchanged.
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // Verify the keys remain unchanged.
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("Cannot compile json expression, pass on the message without assigning new event time", func(t *testing.T) {
@@ -135,7 +143,7 @@ func TestEventTimeExtractor(t *testing.T) {
testInputEventTime := time.Date(2022, 1, 4, 2, 3, 4, 5, time.UTC)
testJsonMsg := `{"test": 21, "item": [{"id": 1, "name": "numa", "time": "2022-02-18T21:54:42.123Z"},{"id": 2, "name": "numa", "time": "2021-02-18T21:54:42.123Z"}]}`
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: testInputEventTime,
watermark: time.Time{},
@@ -146,6 +154,8 @@ func TestEventTimeExtractor(t *testing.T) {
assert.True(t, expected.Equal(result.Items()[0].EventTime()))
// Verify the payload remains unchanged.
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // Verify the keys remain unchanged.
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("The time string is in epoch format with a granularity of seconds, assign a new event time to the message", func(t *testing.T) {
@@ -154,9 +164,9 @@ func TestEventTimeExtractor(t *testing.T) {
assert.NoError(t, err)
testInputEventTime := time.Date(2022, 1, 4, 2, 3, 4, 5, time.UTC)
- // Handler receives format as time.ANSIC but in the message, we use time.RFC3339. Format is not matched.
+ // Handler receives a format as time.ANSIC but in the message, we use time.RFC3339. Format is not matched.
testJsonMsg := `{"test": 21, "item": [{"id": 1, "name": "numa", "time": "1673239888"},{"id": 2, "name": "numa", "time": "1673239888"}]}`
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: testInputEventTime,
watermark: time.Time{},
@@ -168,6 +178,8 @@ func TestEventTimeExtractor(t *testing.T) {
assert.True(t, expected.Equal(result.Items()[0].EventTime()))
// Verify the payload remains unchanged.
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // Verify the keys remain unchanged.
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("The time string is in epoch format with a granularity of milliseconds, assign a new event time to the message", func(t *testing.T) {
@@ -177,7 +189,7 @@ func TestEventTimeExtractor(t *testing.T) {
testInputEventTime := time.Date(2022, 1, 4, 2, 3, 4, 5, time.UTC)
testJsonMsg := `{"test": 21, "item": [{"id": 1, "name": "numa", "time": "1673239888123"},{"id": 2, "name": "numa", "time": "1673239888123"}]}`
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: testInputEventTime,
watermark: time.Time{},
@@ -189,6 +201,8 @@ func TestEventTimeExtractor(t *testing.T) {
assert.True(t, expected.Equal(result.Items()[0].EventTime()))
// Verify the payload remains unchanged.
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // Verify the keys remain unchanged.
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("The time string is ambiguous, pass on the message without assigning new event time", func(t *testing.T) {
@@ -199,7 +213,7 @@ func TestEventTimeExtractor(t *testing.T) {
testInputEventTime := time.Date(2022, 1, 4, 2, 3, 4, 5, time.UTC)
// 04/08/2014 is ambiguous because it could be mm/dd/yyyy or dd/mm/yyyy.
testJsonMsg := `{"test": 21, "item": [{"id": 1, "name": "numa", "time": "04/08/2014 22:05"},{"id": 2, "name": "numa", "time": "04/08/2014 22:05"}]}`
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: testInputEventTime,
watermark: time.Time{},
@@ -211,5 +225,7 @@ func TestEventTimeExtractor(t *testing.T) {
assert.True(t, expected.Equal(result.Items()[0].EventTime()))
// Verify the payload remains unchanged.
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // Verify the keys remain unchanged.
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
}
diff --git a/pkg/sources/transformer/builtin/filter/filter.go b/pkg/sources/transformer/builtin/filter/filter.go
index 08f47795f2..c30b99017f 100644
--- a/pkg/sources/transformer/builtin/filter/filter.go
+++ b/pkg/sources/transformer/builtin/filter/filter.go
@@ -32,17 +32,17 @@ type filter struct {
}
func New(args map[string]string) (sourcetransformer.SourceTransformFunc, error) {
- expr, existing := args["expression"]
+ exp, existing := args["expression"]
if !existing {
return nil, fmt.Errorf(`missing "expression"`)
}
f := filter{
- expression: expr,
+ expression: exp,
}
return func(ctx context.Context, keys []string, datum sourcetransformer.Datum) sourcetransformer.Messages {
log := logging.FromContext(ctx)
- resultMsg, err := f.apply(datum.EventTime(), datum.Value())
+ resultMsg, err := f.apply(datum.EventTime(), datum.Value(), keys)
if err != nil {
log.Errorf("Filter map function apply got an error: %v", err)
}
@@ -50,13 +50,13 @@ func New(args map[string]string) (sourcetransformer.SourceTransformFunc, error)
}, nil
}
-func (f filter) apply(et time.Time, msg []byte) (sourcetransformer.Message, error) {
+func (f filter) apply(et time.Time, msg []byte, keys []string) (sourcetransformer.Message, error) {
result, err := expr.EvalBool(f.expression, msg)
if err != nil {
return sourcetransformer.MessageToDrop(et), err
}
if result {
- return sourcetransformer.NewMessage(msg, et), nil
+ return sourcetransformer.NewMessage(msg, et).WithKeys(keys), nil
}
return sourcetransformer.MessageToDrop(et), nil
}
diff --git a/pkg/sources/transformer/builtin/filter/filter_test.go b/pkg/sources/transformer/builtin/filter/filter_test.go
index 8ac227bdc2..fb0d7a5768 100644
--- a/pkg/sources/transformer/builtin/filter/filter_test.go
+++ b/pkg/sources/transformer/builtin/filter/filter_test.go
@@ -22,6 +22,7 @@ import (
"testing"
"time"
+ "github.com/numaproj/numaflow-go/pkg/sourcetransformer"
"github.com/stretchr/testify/assert"
)
@@ -86,6 +87,7 @@ func TestExpression(t *testing.T) {
watermark: time.Time{},
})
assert.Equal(t, jsonMsg, string(result.Items()[0].Value()))
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("invalid expression", func(t *testing.T) {
@@ -99,7 +101,7 @@ func TestExpression(t *testing.T) {
eventTime: time.Time{},
watermark: time.Time{},
})
- assert.Equal(t, "", string(result.Items()[0].Value()))
+ assert.Equal(t, sourcetransformer.MessageToDrop(time.Time{}), result.Items()[0])
})
t.Run("Json expression invalid", func(t *testing.T) {
@@ -113,7 +115,7 @@ func TestExpression(t *testing.T) {
eventTime: time.Time{},
watermark: time.Time{},
})
- assert.Equal(t, "", string(result.Items()[0].Value()))
+ assert.Equal(t, sourcetransformer.MessageToDrop(time.Time{}), result.Items()[0])
})
t.Run("String expression invalid", func(t *testing.T) {
@@ -127,7 +129,7 @@ func TestExpression(t *testing.T) {
eventTime: time.Time{},
watermark: time.Time{},
})
- assert.Equal(t, "", string(result.Items()[0].Value()))
+ assert.Equal(t, sourcetransformer.MessageToDrop(time.Time{}), result.Items()[0])
})
t.Run("base64 expression valid", func(t *testing.T) {
@@ -142,6 +144,7 @@ func TestExpression(t *testing.T) {
watermark: time.Time{},
})
assert.Equal(t, base64Msg, string(result.Items()[0].Value()))
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
t.Run("event time unchanged", func(t *testing.T) {
@@ -157,5 +160,6 @@ func TestExpression(t *testing.T) {
watermark: time.Time{},
})
assert.Equal(t, testEventTime, result.Items()[0].EventTime())
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
}
diff --git a/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter.go b/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter.go
index 112c5de25c..bb1515cb1e 100644
--- a/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter.go
+++ b/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter.go
@@ -59,7 +59,7 @@ func New(args map[string]string) (sourcetransformer.SourceTransformFunc, error)
return func(ctx context.Context, keys []string, datum sourcetransformer.Datum) sourcetransformer.Messages {
log := logging.FromContext(ctx)
- resultMsg, err := e.apply(datum.EventTime(), datum.Value())
+ resultMsg, err := e.apply(datum.EventTime(), datum.Value(), keys)
if err != nil {
log.Errorf("Filter or event time extractor got an error: %v", err)
}
@@ -68,7 +68,7 @@ func New(args map[string]string) (sourcetransformer.SourceTransformFunc, error)
}
-func (e expressions) apply(et time.Time, payload []byte) (sourcetransformer.Message, error) {
+func (e expressions) apply(et time.Time, payload []byte, keys []string) (sourcetransformer.Message, error) {
result, err := expr.EvalBool(e.filterExpr, payload)
if err != nil {
return sourcetransformer.MessageToDrop(et), err
@@ -76,7 +76,7 @@ func (e expressions) apply(et time.Time, payload []byte) (sourcetransformer.Mess
if result {
timeStr, err := expr.EvalStr(e.eventTimeExpr, payload)
if err != nil {
- return sourcetransformer.NewMessage(payload, et), err
+ return sourcetransformer.NewMessage(payload, et).WithKeys(keys), err
}
var newEventTime time.Time
time.Local, _ = time.LoadLocation("UTC")
@@ -86,9 +86,9 @@ func (e expressions) apply(et time.Time, payload []byte) (sourcetransformer.Mess
newEventTime, err = dateparse.ParseStrict(timeStr)
}
if err != nil {
- return sourcetransformer.NewMessage(payload, et), err
+ return sourcetransformer.NewMessage(payload, et).WithKeys(keys), err
} else {
- return sourcetransformer.NewMessage(payload, newEventTime), nil
+ return sourcetransformer.NewMessage(payload, newEventTime).WithKeys(keys), nil
}
}
return sourcetransformer.MessageToDrop(et), nil
diff --git a/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter_test.go b/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter_test.go
index 02631c25cd..a178ce251b 100644
--- a/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter_test.go
+++ b/pkg/sources/transformer/builtin/time_extraction_filter/time_extraction_filter_test.go
@@ -21,9 +21,12 @@ import (
"testing"
"time"
+ "github.com/numaproj/numaflow-go/pkg/sourcetransformer"
"github.com/stretchr/testify/assert"
)
+var _keys = []string{"test-key"}
+
type testDatum struct {
value []byte
eventTime time.Time
@@ -66,7 +69,6 @@ var (
)
func TestFilterEventTime(t *testing.T) {
-
t.Run("Missing both expressions, return error", func(t *testing.T) {
_, err := New(map[string]string{})
assert.Error(t, err)
@@ -89,14 +91,16 @@ func TestFilterEventTime(t *testing.T) {
handle, err := New(map[string]string{"filterExpr": "int(json(payload).item[1].id) == 2", "eventTimeExpr": "json(payload).item[1].time", "eventTimeFormat": time.RFC3339})
assert.NoError(t, err)
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: time.Time{},
watermark: time.Time{},
})
- // check that messsage has not changed
+ // check that message has not changed
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // check that keys have not changed
+ assert.Equal(t, _keys, result.Items()[0].Keys())
// check that event time has changed
time.Local, _ = time.LoadLocation("UTC")
@@ -108,13 +112,13 @@ func TestFilterEventTime(t *testing.T) {
handle, err := New(map[string]string{"filterExpr": "int(json(payload).item[1].id) == 3", "eventTimeExpr": "json(payload).item[1].time", "eventTimeFormat": time.RFC3339})
assert.NoError(t, err)
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: time.Time{},
watermark: time.Time{},
})
- assert.Equal(t, "", string(result.Items()[0].Value()))
+ assert.Equal(t, sourcetransformer.MessageToDrop(time.Time{}), result.Items()[0])
})
t.Run("Valid JSON expression for filter, incorrect format to eventTime", func(t *testing.T) {
@@ -122,14 +126,17 @@ func TestFilterEventTime(t *testing.T) {
assert.NoError(t, err)
testInputEventTime := time.Date(2022, 1, 4, 2, 3, 4, 5, time.UTC)
- result := handle(context.Background(), []string{"test-key"}, &testDatum{
+ result := handle(context.Background(), _keys, &testDatum{
value: []byte(testJsonMsg),
eventTime: testInputEventTime,
watermark: time.Time{},
})
+ // check that message event time has not changed
assert.Equal(t, testInputEventTime, result.Items()[0].EventTime())
+ // check that message has not changed
assert.Equal(t, testJsonMsg, string(result.Items()[0].Value()))
+ // check that keys have not been added
+ assert.Equal(t, _keys, result.Items()[0].Keys())
})
-
}
From 0811eb4aff59dda8b9143a7420b2beb415143d27 Mon Sep 17 00:00:00 2001
From: Sreekanth
Date: Tue, 10 Sep 2024 20:57:25 +0530
Subject: [PATCH 055/188] fix: Fix numaflow-rs binary location in image (#2050)
Signed-off-by: Sreekanth
---
.github/workflows/nightly-build.yml | 4 ++--
.github/workflows/release.yml | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml
index 641bc2d593..51bdb6bfbc 100644
--- a/.github/workflows/nightly-build.yml
+++ b/.github/workflows/nightly-build.yml
@@ -136,13 +136,13 @@ jobs:
uses: actions/download-artifact@v3
with:
name: numaflow-rs-linux-amd64
- path: dist/numaflow-rs-linux-amd64
+ path: dist/
- name: Download Rust arm64 binaries
uses: actions/download-artifact@v3
with:
name: numaflow-rs-linux-arm64
- path: dist/numaflow-rs-linux-arm64
+ path: dist/
- name: Registry Login
uses: docker/login-action@v2
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5c17591fe8..dcab109e0e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -120,13 +120,13 @@ jobs:
uses: actions/download-artifact@v3
with:
name: numaflow-rs-linux-amd64
- path: dist/numaflow-rs-linux-amd64
+ path: dist/
- name: Download Rust arm64 binaries
uses: actions/download-artifact@v3
with:
name: numaflow-rs-linux-arm64
- path: dist/numaflow-rs-linux-arm64
+ path: dist/
- name: Registry Login
uses: docker/login-action@v2
From 49b733e68a895048a94e67fc082bded2de5872f4 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Wed, 11 Sep 2024 11:47:26 -0700
Subject: [PATCH 056/188] chore: use readyReplicas to calculate desired
replicas (#2052)
Signed-off-by: Derek Wang
---
pkg/reconciler/monovertex/scaling/scaling.go | 28 +++---
pkg/reconciler/vertex/scaling/scaling.go | 24 +++--
pkg/reconciler/vertex/scaling/scaling_test.go | 99 ++++++++++---------
3 files changed, 86 insertions(+), 65 deletions(-)
diff --git a/pkg/reconciler/monovertex/scaling/scaling.go b/pkg/reconciler/monovertex/scaling/scaling.go
index d523800cdd..481408672c 100644
--- a/pkg/reconciler/monovertex/scaling/scaling.go
+++ b/pkg/reconciler/monovertex/scaling/scaling.go
@@ -183,16 +183,6 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
return nil
}
- var err error
- daemonClient, _ := s.mvtxDaemonClientsCache.Get(monoVtx.GetDaemonServiceURL())
- if daemonClient == nil {
- daemonClient, err = mvtxdaemonclient.NewGRPCClient(monoVtx.GetDaemonServiceURL())
- if err != nil {
- return fmt.Errorf("failed to get daemon service client for MonoVertex %s, %w", monoVtx.Name, err)
- }
- s.mvtxDaemonClientsCache.Add(monoVtx.GetDaemonServiceURL(), daemonClient)
- }
-
if monoVtx.Status.Replicas == 0 { // Was scaled to 0
// Periodically wake them up from 0 replicas to 1, to peek for the incoming messages
if secondsSinceLastScale >= float64(monoVtx.Spec.Scale.GetZeroReplicaSleepSeconds()) {
@@ -204,6 +194,22 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
}
}
+ // There's no ready pods, skip scaling
+ if monoVtx.Status.ReadyReplicas == 0 {
+ log.Infof("MonoVertex has no ready replicas, skip scaling.")
+ return nil
+ }
+
+ var err error
+ daemonClient, _ := s.mvtxDaemonClientsCache.Get(monoVtx.GetDaemonServiceURL())
+ if daemonClient == nil {
+ daemonClient, err = mvtxdaemonclient.NewGRPCClient(monoVtx.GetDaemonServiceURL())
+ if err != nil {
+ return fmt.Errorf("failed to get daemon service client for MonoVertex %s, %w", monoVtx.Name, err)
+ }
+ s.mvtxDaemonClientsCache.Add(monoVtx.GetDaemonServiceURL(), daemonClient)
+ }
+
vMetrics, err := daemonClient.GetMonoVertexMetrics(ctx)
if err != nil {
return fmt.Errorf("failed to get metrics of mono vertex key %q, %w", key, err)
@@ -282,7 +288,7 @@ func (s *Scaler) desiredReplicas(_ context.Context, monoVtx *dfv1.MonoVertex, pr
var desired int32
// We calculate the time of finishing processing the pending messages,
// and then we know how many replicas are needed to get them done in target seconds.
- desired = int32(math.Round(((float64(pending) / processingRate) / float64(monoVtx.Spec.Scale.GetTargetProcessingSeconds())) * float64(monoVtx.Status.Replicas)))
+ desired = int32(math.Round(((float64(pending) / processingRate) / float64(monoVtx.Spec.Scale.GetTargetProcessingSeconds())) * float64(monoVtx.Status.ReadyReplicas)))
// we only scale down to zero when the pending and rate are both zero.
if desired == 0 {
diff --git a/pkg/reconciler/vertex/scaling/scaling.go b/pkg/reconciler/vertex/scaling/scaling.go
index 5ea6b7e6d5..eed5981e89 100644
--- a/pkg/reconciler/vertex/scaling/scaling.go
+++ b/pkg/reconciler/vertex/scaling/scaling.go
@@ -170,6 +170,14 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
s.StopWatching(key) // Remove it in case it's watched.
return nil
}
+ if vertex.Status.Phase != dfv1.VertexPhaseRunning {
+ log.Infof("Vertex not in Running phase, skip scaling.")
+ return nil
+ }
+ if vertex.Status.UpdateHash != vertex.Status.CurrentHash && vertex.Status.UpdateHash != "" {
+ log.Info("Vertex is updating, skip scaling.")
+ return nil
+ }
secondsSinceLastScale := time.Since(vertex.Status.LastScaledAt.Time).Seconds()
scaleDownCooldown := float64(vertex.Spec.Scale.GetScaleDownCooldownSeconds())
scaleUpCooldown := float64(vertex.Spec.Scale.GetScaleUpCooldownSeconds())
@@ -178,10 +186,6 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
log.Infof("Cooldown period, skip scaling.")
return nil
}
- if vertex.Status.Phase != dfv1.VertexPhaseRunning {
- log.Infof("Vertex not in Running phase, skip scaling.")
- return nil
- }
pl := &dfv1.Pipeline{}
if err := s.client.Get(ctx, client.ObjectKey{Namespace: namespace, Name: vertex.Spec.PipelineName}, pl); err != nil {
if apierrors.IsNotFound(err) {
@@ -246,6 +250,12 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
}
}
+ // Vertex pods are not ready yet.
+ if vertex.Status.ReadyReplicas == 0 {
+ log.Infof("Vertex %q has no ready replicas, skip scaling.", vertex.Name)
+ return nil
+ }
+
vMetrics, err := daemonClient.GetVertexMetrics(ctx, pl.Name, vertex.Spec.Name)
if err != nil {
return fmt.Errorf("failed to get metrics of vertex key %q, %w", key, err)
@@ -289,7 +299,7 @@ func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) err
}
var desired int32
- current := int32(vertex.GetReplicas())
+ current := int32(vertex.Status.Replicas)
// if both totalRate and totalPending are 0, we scale down to 0
// since pending contains the pending acks, we can scale down to 0.
if totalPending == 0 && totalRate == 0 {
@@ -370,7 +380,7 @@ func (s *Scaler) desiredReplicas(_ context.Context, vertex *dfv1.Vertex, partiti
if vertex.IsASource() {
// For sources, we calculate the time of finishing processing the pending messages,
// and then we know how many replicas are needed to get them done in target seconds.
- desired = int32(math.Round(((float64(pending) / rate) / float64(vertex.Spec.Scale.GetTargetProcessingSeconds())) * float64(vertex.Status.Replicas)))
+ desired = int32(math.Round(((float64(pending) / rate) / float64(vertex.Spec.Scale.GetTargetProcessingSeconds())) * float64(vertex.Status.ReadyReplicas)))
} else {
// For UDF and sinks, we calculate the available buffer length, and consider it is the contribution of current replicas,
// then we figure out how many replicas are needed to keep the available buffer length at target level.
@@ -378,7 +388,7 @@ func (s *Scaler) desiredReplicas(_ context.Context, vertex *dfv1.Vertex, partiti
// Simply return current replica number + max allowed if the pending messages are more than available buffer length
desired = int32(vertex.Status.Replicas) + int32(vertex.Spec.Scale.GetReplicasPerScaleUp())
} else {
- singleReplicaContribution := float64(partitionBufferLengths[i]-pending) / float64(vertex.Status.Replicas)
+ singleReplicaContribution := float64(partitionBufferLengths[i]-pending) / float64(vertex.Status.ReadyReplicas)
desired = int32(math.Round(float64(partitionAvailableBufferLengths[i]) / singleReplicaContribution))
}
}
diff --git a/pkg/reconciler/vertex/scaling/scaling_test.go b/pkg/reconciler/vertex/scaling/scaling_test.go
index 9a2d14554b..0ea80cef17 100644
--- a/pkg/reconciler/vertex/scaling/scaling_test.go
+++ b/pkg/reconciler/vertex/scaling/scaling_test.go
@@ -27,6 +27,23 @@ import (
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
)
+var (
+ fakeVertex = &dfv1.Vertex{
+ Spec: dfv1.VertexSpec{
+ Replicas: ptr.To[int32](3),
+ AbstractVertex: dfv1.AbstractVertex{
+ Scale: dfv1.Scale{
+ TargetProcessingSeconds: ptr.To[uint32](1),
+ },
+ },
+ },
+ Status: dfv1.VertexStatus{
+ Replicas: uint32(3),
+ ReadyReplicas: uint32(2),
+ },
+ }
+)
+
func Test_BasicOperations(t *testing.T) {
cl := fake.NewClientBuilder().Build()
s := NewScaler(cl)
@@ -39,53 +56,40 @@ func Test_BasicOperations(t *testing.T) {
}
func Test_desiredReplicasSinglePartition(t *testing.T) {
- cl := fake.NewClientBuilder().Build()
- s := NewScaler(cl)
- one := uint32(1)
- src := &dfv1.Vertex{
- Spec: dfv1.VertexSpec{
- Replicas: ptr.To[int32](2),
- AbstractVertex: dfv1.AbstractVertex{
- Source: &dfv1.Source{
- Kafka: &dfv1.KafkaSource{},
- },
- Scale: dfv1.Scale{
- TargetProcessingSeconds: &one,
- },
- },
- },
- Status: dfv1.VertexStatus{
- Replicas: uint32(2),
- },
- }
- assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{0}, []int64{0}, []int64{10000}, []int64{5000}))
- assert.Equal(t, int32(8), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{10010}, []int64{30000}, []int64{20000}))
- assert.Equal(t, int32(8), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{9950}, []int64{30000}, []int64{20000}))
- assert.Equal(t, int32(7), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{8751}, []int64{30000}, []int64{20000}))
- assert.Equal(t, int32(7), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{8749}, []int64{30000}, []int64{20000}))
- assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{0}, []int64{9950}, []int64{30000}, []int64{20000}))
- assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{2}, []int64{30000}, []int64{20000}))
- assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{0}, []int64{30000}, []int64{20000}))
- udf := &dfv1.Vertex{
- Spec: dfv1.VertexSpec{
- Replicas: ptr.To[int32](2),
- AbstractVertex: dfv1.AbstractVertex{
- UDF: &dfv1.UDF{},
- },
- },
- Status: dfv1.VertexStatus{
- Replicas: uint32(2),
- },
- }
- assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, []float64{0}, []int64{0}, []int64{10000}, []int64{5000}))
- assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{5000}))
- assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{6000}))
- assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{7500}))
- assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{7900}))
- assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{10000}))
- assert.Equal(t, int32(3), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{12500}))
- assert.Equal(t, int32(3), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{12550}))
+ t.Run("test src", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ s := NewScaler(cl)
+ src := fakeVertex.DeepCopy()
+ src.Spec.Source = &dfv1.Source{
+ Kafka: &dfv1.KafkaSource{},
+ }
+ assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{0}, []int64{0}, []int64{10000}, []int64{5000}))
+ assert.Equal(t, int32(8), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{10010}, []int64{30000}, []int64{20000}))
+ assert.Equal(t, int32(8), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{9950}, []int64{30000}, []int64{20000}))
+ assert.Equal(t, int32(7), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{8751}, []int64{30000}, []int64{20000}))
+ assert.Equal(t, int32(7), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{8749}, []int64{30000}, []int64{20000}))
+ assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{0}, []int64{9950}, []int64{30000}, []int64{20000}))
+ assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{2}, []int64{30000}, []int64{20000}))
+ assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, []float64{2500}, []int64{0}, []int64{30000}, []int64{20000}))
+
+ })
+
+ t.Run("test udf", func(t *testing.T) {
+ cl := fake.NewClientBuilder().Build()
+ s := NewScaler(cl)
+ udf := fakeVertex.DeepCopy()
+ udf.Spec.UDF = &dfv1.UDF{}
+ assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, []float64{0}, []int64{0}, []int64{10000}, []int64{5000}))
+ assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{5000}))
+ assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{6000}))
+ assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{7500}))
+ assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{7900}))
+ assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{10000}))
+ assert.Equal(t, int32(3), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{12500}))
+ assert.Equal(t, int32(3), s.desiredReplicas(context.TODO(), udf, []float64{250}, []int64{10000}, []int64{20000}, []int64{12550}))
+ })
+
}
func Test_desiredReplicasMultiplePartitions(t *testing.T) {
@@ -99,7 +103,8 @@ func Test_desiredReplicasMultiplePartitions(t *testing.T) {
},
},
Status: dfv1.VertexStatus{
- Replicas: uint32(2),
+ Replicas: uint32(2),
+ ReadyReplicas: uint32(2),
},
}
From f00685a15983330980447113086f31eede200276 Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Fri, 13 Sep 2024 22:58:58 -0400
Subject: [PATCH 057/188] chore: remove server info dependencies on go sdk
(#2060)
Signed-off-by: Keran Yang
---
api/json-schema/schema.json | 2 +-
api/openapi-spec/swagger.json | 2 +-
docs/APIs.md | 8 +-
pkg/apis/numaflow/v1alpha1/generated.proto | 2 +-
.../numaflow/v1alpha1/mono_vertex_types.go | 2 +-
.../numaflow/v1alpha1/openapi_generated.go | 2 +-
pkg/sdkclient/batchmapper/client.go | 7 +-
pkg/sdkclient/grpc/grpc_utils.go | 7 +-
pkg/sdkclient/mapper/client.go | 5 +-
pkg/sdkclient/mapstreamer/client.go | 6 +-
pkg/sdkclient/reducer/client.go | 6 +-
pkg/sdkclient/serverinfo/serverinfo.go | 103 ++++--
pkg/sdkclient/serverinfo/serverinfo_test.go | 136 ++++++-
pkg/sdkclient/serverinfo/types.go | 69 ++++
pkg/sdkclient/serverinfo/versions.go | 29 --
pkg/sdkclient/sessionreducer/client.go | 5 +-
pkg/sdkclient/sideinput/client.go | 6 +-
pkg/sdkclient/sinker/client.go | 6 +-
pkg/sdkclient/source/client.go | 6 +-
pkg/sdkclient/sourcetransformer/client.go | 5 +-
pkg/sideinputs/manager/manager.go | 4 +-
pkg/sinks/sink.go | 6 +-
pkg/sources/source.go | 6 +-
pkg/udf/map_udf.go | 12 +-
pkg/udf/reduce_udf.go | 16 +-
rust/Cargo.lock | 350 +++++++++---------
rust/monovertex/src/lib.rs | 8 +-
rust/monovertex/src/server_info.rs | 34 +-
28 files changed, 531 insertions(+), 319 deletions(-)
create mode 100644 pkg/sdkclient/serverinfo/types.go
delete mode 100644 pkg/sdkclient/serverinfo/versions.go
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index bf1f28f594..fefd397d8c 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19203,7 +19203,7 @@
},
"limits": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.MonoVertexLimits",
- "description": "Limits define the limitations such as buffer read batch size for all the vertices of a pipeline, will override pipeline level settings"
+ "description": "Limits define the limitations such as read batch size for the mono vertex."
},
"metadata": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Metadata",
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index 91e6e43fb6..f688e51c8d 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19198,7 +19198,7 @@
}
},
"limits": {
- "description": "Limits define the limitations such as buffer read batch size for all the vertices of a pipeline, will override pipeline level settings",
+ "description": "Limits define the limitations such as read batch size for the mono vertex.",
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.MonoVertexLimits"
},
"metadata": {
diff --git a/docs/APIs.md b/docs/APIs.md
index da6ab6eeb1..d5deaa76b2 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -5720,8 +5720,8 @@ MonoVertexLimits
(Optional)
-Limits define the limitations such as buffer read batch size for all the
-vertices of a pipeline, will override pipeline level settings
+Limits define the limitations such as read batch size for the mono
+vertex.
@@ -6109,8 +6109,8 @@ MonoVertexLimits
(Optional)
-Limits define the limitations such as buffer read batch size for all the
-vertices of a pipeline, will override pipeline level settings
+Limits define the limitations such as read batch size for the mono
+vertex.
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index c927033e7e..70936b9d47 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -928,7 +928,7 @@ message MonoVertexSpec {
// +patchMergeKey=name
repeated k8s.io.api.core.v1.Volume volumes = 6;
- // Limits define the limitations such as buffer read batch size for all the vertices of a pipeline, will override pipeline level settings
+ // Limits define the limitations such as read batch size for the mono vertex.
// +optional
optional MonoVertexLimits limits = 7;
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index 677ec4fc5c..934d497878 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -408,7 +408,7 @@ type MonoVertexSpec struct {
// +patchStrategy=merge
// +patchMergeKey=name
Volumes []corev1.Volume `json:"volumes,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,6,rep,name=volumes"`
- // Limits define the limitations such as buffer read batch size for all the vertices of a pipeline, will override pipeline level settings
+ // Limits define the limitations such as read batch size for the mono vertex.
// +optional
Limits *MonoVertexLimits `json:"limits,omitempty" protobuf:"bytes,7,opt,name=limits"`
// Settings for autoscaling
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 50a03d54a3..5987162567 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -3296,7 +3296,7 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexSpec(ref common.ReferenceCallba
},
"limits": {
SchemaProps: spec.SchemaProps{
- Description: "Limits define the limitations such as buffer read batch size for all the vertices of a pipeline, will override pipeline level settings",
+ Description: "Limits define the limitations such as read batch size for the mono vertex.",
Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits"),
},
},
diff --git a/pkg/sdkclient/batchmapper/client.go b/pkg/sdkclient/batchmapper/client.go
index 7c6db2f608..5cc1718492 100644
--- a/pkg/sdkclient/batchmapper/client.go
+++ b/pkg/sdkclient/batchmapper/client.go
@@ -21,15 +21,14 @@ import (
"errors"
"io"
+ batchmappb "github.com/numaproj/numaflow-go/pkg/apis/proto/batchmap/v1"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
- batchmappb "github.com/numaproj/numaflow-go/pkg/apis/proto/batchmap/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
-
"github.com/numaproj/numaflow/pkg/sdkclient"
sdkerr "github.com/numaproj/numaflow/pkg/sdkclient/error"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -39,7 +38,7 @@ type client struct {
}
// New creates a new client object.
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.BatchMapAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/grpc/grpc_utils.go b/pkg/sdkclient/grpc/grpc_utils.go
index 6d3574a290..42fba83e86 100644
--- a/pkg/sdkclient/grpc/grpc_utils.go
+++ b/pkg/sdkclient/grpc/grpc_utils.go
@@ -21,22 +21,21 @@ import (
"log"
"strconv"
- "github.com/numaproj/numaflow-go/pkg/info"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
resolver "github.com/numaproj/numaflow/pkg/sdkclient/grpc_resolver"
- sdkserverinfo "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// ConnectToServer connects to the server with the given socket address based on the server info protocol.
-func ConnectToServer(udsSockAddr string, serverInfo *info.ServerInfo, maxMessageSize int) (*grpc.ClientConn, error) {
+func ConnectToServer(udsSockAddr string, serverInfo *serverinfo.ServerInfo, maxMessageSize int) (*grpc.ClientConn, error) {
var conn *grpc.ClientConn
var err error
var sockAddr string
// Check if Multiproc server mode is enabled
- if multiProcServer, ok := serverInfo.Metadata[sdkserverinfo.MultiProcMetadata]; ok {
+ if multiProcServer, ok := serverInfo.Metadata[serverinfo.MultiProcKey]; ok {
// Extract the server ports from the server info file
numServers, _ := strconv.Atoi(multiProcServer)
// In Multiprocessing server mode we have multiple servers forks
diff --git a/pkg/sdkclient/mapper/client.go b/pkg/sdkclient/mapper/client.go
index d22c852906..07ef848a09 100644
--- a/pkg/sdkclient/mapper/client.go
+++ b/pkg/sdkclient/mapper/client.go
@@ -23,10 +23,11 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
mappb "github.com/numaproj/numaflow-go/pkg/apis/proto/map/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
+
"github.com/numaproj/numaflow/pkg/sdkclient"
sdkerror "github.com/numaproj/numaflow/pkg/sdkclient/error"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -36,7 +37,7 @@ type client struct {
}
// New creates a new client object.
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.MapAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/mapstreamer/client.go b/pkg/sdkclient/mapstreamer/client.go
index 9b512d22ad..ff5d07a7a6 100644
--- a/pkg/sdkclient/mapstreamer/client.go
+++ b/pkg/sdkclient/mapstreamer/client.go
@@ -21,14 +21,14 @@ import (
"fmt"
"io"
+ mapstreampb "github.com/numaproj/numaflow-go/pkg/apis/proto/mapstream/v1"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
- mapstreampb "github.com/numaproj/numaflow-go/pkg/apis/proto/mapstream/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
"github.com/numaproj/numaflow/pkg/sdkclient"
sdkerror "github.com/numaproj/numaflow/pkg/sdkclient/error"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -38,7 +38,7 @@ type client struct {
}
// New creates a new client object.
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.MapStreamAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/reducer/client.go b/pkg/sdkclient/reducer/client.go
index 64580c831d..6825fdc4c2 100644
--- a/pkg/sdkclient/reducer/client.go
+++ b/pkg/sdkclient/reducer/client.go
@@ -21,14 +21,14 @@ import (
"errors"
"io"
+ reducepb "github.com/numaproj/numaflow-go/pkg/apis/proto/reduce/v1"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
- reducepb "github.com/numaproj/numaflow-go/pkg/apis/proto/reduce/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
"github.com/numaproj/numaflow/pkg/sdkclient"
sdkerr "github.com/numaproj/numaflow/pkg/sdkclient/error"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -38,7 +38,7 @@ type client struct {
}
// New creates a new client object.
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.ReduceAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/serverinfo/serverinfo.go b/pkg/sdkclient/serverinfo/serverinfo.go
index aa1cdde29d..932ab2ff50 100644
--- a/pkg/sdkclient/serverinfo/serverinfo.go
+++ b/pkg/sdkclient/serverinfo/serverinfo.go
@@ -18,39 +18,23 @@ package serverinfo
import (
"context"
+ "encoding/json"
"fmt"
"log"
+ "os"
"strings"
"time"
"github.com/Masterminds/semver/v3"
pep440 "github.com/aquasecurity/go-pep440-version"
- "github.com/numaproj/numaflow-go/pkg/info"
-
"github.com/numaproj/numaflow"
)
-// Metadata keys used in the server info file
-const (
- // MultiProcMetadata is the field used to indicate that MultiProc map mode is enabled
- // The value contains the number of servers spawned.
- MultiProcMetadata = "MULTIPROC"
- // MapModeMetadata field is used to indicate which map mode is enabled
- // If none is set, we consider unary map as default
- MapModeMetadata = "MAP_MODE"
-)
-
-type MapMode string
-
-const (
- UnaryMap MapMode = "unary-map"
- StreamMap MapMode = "stream-map"
- BatchMap MapMode = "batch-map"
-)
+var END = fmt.Sprintf("%U__END__", '\\')
// SDKServerInfo wait for the server to start and return the server info.
-func SDKServerInfo(inputOptions ...Option) (*info.ServerInfo, error) {
+func SDKServerInfo(inputOptions ...Option) (*ServerInfo, error) {
var opts = DefaultOptions()
for _, inputOption := range inputOptions {
@@ -68,33 +52,32 @@ func SDKServerInfo(inputOptions ...Option) (*info.ServerInfo, error) {
}
// waitForServerInfo waits until the server info is ready. It returns an error if the server info is not ready within the given timeout
-func waitForServerInfo(timeout time.Duration, filePath string) (*info.ServerInfo, error) {
+func waitForServerInfo(timeout time.Duration, filePath string) (*ServerInfo, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
- if err := info.WaitUntilReady(ctx, info.WithServerInfoFilePath(filePath)); err != nil {
+ if err := waitUntilReady(ctx, WithServerInfoFilePath(filePath)); err != nil {
return nil, fmt.Errorf("failed to wait until server info is ready: %w", err)
}
-
- serverInfo, err := info.Read(info.WithServerInfoFilePath(filePath))
+ serverInfo, err := read(WithServerInfoFilePath(filePath))
if err != nil {
return nil, fmt.Errorf("failed to read server info: %w", err)
}
-
sdkVersion := serverInfo.Version
minNumaflowVersion := serverInfo.MinimumNumaflowVersion
sdkLanguage := serverInfo.Language
numaflowVersion := numaflow.GetVersion().Version
// If MinimumNumaflowVersion is empty, skip the numaflow compatibility check as there was an
- // error writing server info on the SDK side
+ // error writing server info file on the SDK side
if minNumaflowVersion == "" {
log.Printf("warning: failed to get the minimum numaflow version, skipping numaflow version compatibility check")
// If we are testing locally or in CI, we can skip checking for numaflow compatibility issues
- // because both return us a version string that the version check libraries can't properly parse (local: "*latest*" CI: commit SHA)
+ // because both return us a version string that the version-check libraries can't properly parse,
+ // local: "*latest*", CI: commit SHA
} else if !strings.Contains(numaflowVersion, "latest") && !strings.Contains(numaflowVersion, numaflow.GetVersion().GitCommit) {
if err := checkNumaflowCompatibility(numaflowVersion, minNumaflowVersion); err != nil {
- return nil, fmt.Errorf("numaflow %s does not satisfy the minimum required by SDK %s: %w",
+ return nil, fmt.Errorf("numaflow version %s does not satisfy the minimum required by SDK version %s: %w",
numaflowVersion, sdkVersion, err)
}
}
@@ -105,14 +88,66 @@ func waitForServerInfo(timeout time.Duration, filePath string) (*info.ServerInfo
log.Printf("warning: failed to get the SDK version/language, skipping SDK version compatibility check")
} else {
if err := checkSDKCompatibility(sdkVersion, sdkLanguage, minimumSupportedSDKVersions); err != nil {
- return nil, fmt.Errorf("SDK %s does not satisfy the minimum required by numaflow %s: %w",
+ return nil, fmt.Errorf("SDK version %s does not satisfy the minimum required by numaflow version %s: %w",
sdkVersion, numaflowVersion, err)
}
}
-
return serverInfo, nil
}
+// waitUntilReady waits until the server info is ready
+func waitUntilReady(ctx context.Context, opts ...Option) error {
+ options := DefaultOptions()
+ for _, opt := range opts {
+ opt(options)
+ }
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ if fileInfo, err := os.Stat(options.serverInfoFilePath); err != nil {
+ log.Printf("Server info file %s is not ready...", options.serverInfoFilePath)
+ time.Sleep(1 * time.Second)
+ continue
+ } else {
+ if fileInfo.Size() > 0 {
+ return nil
+ }
+ }
+ }
+ }
+}
+
+// read reads the server info from a file
+func read(opts ...Option) (*ServerInfo, error) {
+ options := DefaultOptions()
+ for _, opt := range opts {
+ opt(options)
+ }
+ // It takes some time for the server to write the server info file
+ // TODO: use a better way to wait for the file to be ready
+ retry := 0
+ b, err := os.ReadFile(options.serverInfoFilePath)
+ for !strings.HasSuffix(string(b), END) && err == nil && retry < 10 {
+ time.Sleep(100 * time.Millisecond)
+ b, err = os.ReadFile(options.serverInfoFilePath)
+ retry++
+ }
+ if err != nil {
+ return nil, err
+ }
+ if !strings.HasSuffix(string(b), END) {
+ return nil, fmt.Errorf("server info file is not ready")
+ }
+ b = b[:len(b)-len([]byte(END))]
+ info := &ServerInfo{}
+ if err := json.Unmarshal(b, info); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal server info: %w", err)
+ }
+ return info, nil
+}
+
func checkConstraint(version *semver.Version, constraint string) error {
if c, err := semver.NewConstraint(constraint); err != nil {
return fmt.Errorf("error parsing constraint: %w, constraint string: %s", err, constraint)
@@ -128,26 +163,23 @@ func checkNumaflowCompatibility(numaflowVersion string, minNumaflowVersion strin
if minNumaflowVersion == "" {
return fmt.Errorf("server info does not contain minimum numaflow version. Upgrade to newer SDK version")
}
-
numaflowVersionSemVer, err := semver.NewVersion(numaflowVersion)
if err != nil {
return fmt.Errorf("error parsing numaflow version: %w", err)
}
-
numaflowConstraint := fmt.Sprintf(">= %s", minNumaflowVersion)
if err = checkConstraint(numaflowVersionSemVer, numaflowConstraint); err != nil {
return fmt.Errorf("numaflow version %s must be upgraded to at least %s, in order to work with current SDK version: %w",
numaflowVersionSemVer.String(), minNumaflowVersion, err)
}
-
return nil
}
// checkSDKCompatibility checks if the current SDK version is compatible with the numaflow version
-func checkSDKCompatibility(sdkVersion string, sdkLanguage info.Language, minSupportedSDKVersions sdkConstraints) error {
+func checkSDKCompatibility(sdkVersion string, sdkLanguage Language, minSupportedSDKVersions sdkConstraints) error {
if sdkRequiredVersion, ok := minSupportedSDKVersions[sdkLanguage]; ok {
sdkConstraint := fmt.Sprintf(">= %s", sdkRequiredVersion)
- if sdkLanguage == info.Python {
+ if sdkLanguage == Python {
// Python pre-releases/releases follow PEP440 specification which requires a different library for parsing
sdkVersionPEP440, err := pep440.Parse(sdkVersion)
if err != nil {
@@ -175,6 +207,5 @@ func checkSDKCompatibility(sdkVersion string, sdkLanguage info.Language, minSupp
}
}
}
-
return nil
}
diff --git a/pkg/sdkclient/serverinfo/serverinfo_test.go b/pkg/sdkclient/serverinfo/serverinfo_test.go
index 90a683a81f..e96919e243 100644
--- a/pkg/sdkclient/serverinfo/serverinfo_test.go
+++ b/pkg/sdkclient/serverinfo/serverinfo_test.go
@@ -17,20 +17,78 @@ limitations under the License.
package serverinfo
import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
"testing"
-
- "github.com/numaproj/numaflow-go/pkg/info"
+ "time"
"github.com/stretchr/testify/assert"
)
-var testMinimumSupportedSDKVersions = sdkConstraints{
- info.Go: "0.6.0-0",
- info.Python: "0.6.0a",
- info.Java: "0.6.0-0",
+func Test_SDKServerInfo(t *testing.T) {
+ filepath := os.TempDir() + "/server-info"
+ defer os.Remove(filepath)
+ info := &ServerInfo{
+ Protocol: TCP,
+ Language: Java,
+ MinimumNumaflowVersion: "1.3.0-rc1",
+ Version: "v0.8.0",
+ Metadata: map[string]string{"key1": "value1", "key2": "value2"},
+ }
+ err := write(info, WithServerInfoFilePath(filepath))
+ assert.NoError(t, err)
+ got, err := SDKServerInfo(WithServerInfoFilePath(filepath))
+ assert.NoError(t, err)
+ assert.Equal(t, info, got)
+}
+
+func Test_WaitUntilReady(t *testing.T) {
+ serverInfoFile, err := os.CreateTemp("/tmp", "server-info")
+ assert.NoError(t, err)
+ defer os.Remove(serverInfoFile.Name())
+ err = os.WriteFile(serverInfoFile.Name(), []byte("test"), 0644)
+ assert.NoError(t, err)
+
+ t.Run("test timeout", func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
+ defer cancel()
+ err := waitUntilReady(ctx, WithServerInfoFilePath("/tmp/not-exist"))
+ assert.True(t, errors.Is(err, context.DeadlineExceeded))
+ })
+
+ t.Run("test success", func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
+ defer cancel()
+ err = waitUntilReady(ctx, WithServerInfoFilePath(serverInfoFile.Name()))
+ assert.NoError(t, err)
+ })
+}
+
+func Test_ReadServerInfoFile(t *testing.T) {
+ filepath := os.TempDir() + "/server-info"
+ defer os.Remove(filepath)
+ info := &ServerInfo{
+ Protocol: TCP,
+ Language: Java,
+ MinimumNumaflowVersion: "1.3.0-rc1",
+ Version: "v0.8.0",
+ Metadata: map[string]string{"key1": "value1", "key2": "value2"},
+ }
+ err := write(info, WithServerInfoFilePath(filepath))
+ assert.NoError(t, err)
+ got, err := read(WithServerInfoFilePath("/tmp/not-exist"))
+ assert.Error(t, err)
+ assert.True(t, os.IsNotExist(err))
+ assert.Nil(t, got)
+ got, err = read(WithServerInfoFilePath(filepath))
+ assert.NoError(t, err)
+ assert.Equal(t, info, got)
}
-func TestCheckNumaflowCompatibility(t *testing.T) {
+func Test_CheckNumaflowCompatibility(t *testing.T) {
tests := []struct {
name string
numaflowVersion string
@@ -59,7 +117,6 @@ func TestCheckNumaflowCompatibility(t *testing.T) {
shouldErr: false,
},
}
-
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := checkNumaflowCompatibility(tt.numaflowVersion, tt.minNumaflowVersion)
@@ -73,11 +130,17 @@ func TestCheckNumaflowCompatibility(t *testing.T) {
}
}
-func TestCheckSDKCompatibility(t *testing.T) {
+func Test_CheckSDKCompatibility(t *testing.T) {
+ var testMinimumSupportedSDKVersions = sdkConstraints{
+ Go: "0.6.0-0",
+ Python: "0.6.0a",
+ Java: "0.6.0-0",
+ Rust: "0.1.0",
+ }
tests := []struct {
name string
sdkVersion string
- sdkLanguage info.Language
+ sdkLanguage Language
minimumSupportedSDKVersions sdkConstraints
shouldErr bool
errMessage string
@@ -85,7 +148,7 @@ func TestCheckSDKCompatibility(t *testing.T) {
{
name: "Test with incompatible Python version",
sdkVersion: "v0.5.3a1",
- sdkLanguage: info.Python,
+ sdkLanguage: Python,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: true,
errMessage: "SDK version 0.5.3a1 must be upgraded to at least 0.6.0a, in order to work with current numaflow version",
@@ -93,14 +156,14 @@ func TestCheckSDKCompatibility(t *testing.T) {
{
name: "Test with compatible Python version",
sdkVersion: "v0.6.0a2",
- sdkLanguage: info.Python,
+ sdkLanguage: Python,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: false,
},
{
name: "Test with incompatible Java version",
sdkVersion: "v0.4.3",
- sdkLanguage: info.Java,
+ sdkLanguage: Java,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: true,
errMessage: "SDK version 0.4.3 must be upgraded to at least 0.6.0-0, in order to work with current numaflow version",
@@ -108,12 +171,26 @@ func TestCheckSDKCompatibility(t *testing.T) {
{
name: "Test with compatible Go version",
sdkVersion: "v0.6.0-rc2",
- sdkLanguage: info.Go,
+ sdkLanguage: Go,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
+ },
+ {
+ name: "Test with incompatible Rust version",
+ sdkVersion: "v0.0.3",
+ sdkLanguage: Rust,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.0.3 must be upgraded to at least 0.1.0, in order to work with current numaflow version",
+ },
+ {
+ name: "Test with compatible Rust version",
+ sdkVersion: "v0.1.1",
+ sdkLanguage: Rust,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: false,
},
}
-
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := checkSDKCompatibility(tt.sdkVersion, tt.sdkLanguage, tt.minimumSupportedSDKVersions)
@@ -126,3 +203,32 @@ func TestCheckSDKCompatibility(t *testing.T) {
})
}
}
+
+// write is a test helper function to prepare server info file
+func write(svrInfo *ServerInfo, opts ...Option) error {
+ b, err := json.Marshal(svrInfo)
+ if err != nil {
+ return fmt.Errorf("failed to marshal server info: %w", err)
+ }
+ options := DefaultOptions()
+ for _, opt := range opts {
+ opt(options)
+ }
+ if err := os.Remove(options.serverInfoFilePath); !os.IsNotExist(err) && err != nil {
+ return fmt.Errorf("failed to remove server-info file: %w", err)
+ }
+ f, err := os.Create(options.serverInfoFilePath)
+ if err != nil {
+ return fmt.Errorf("failed to create server-info file: %w", err)
+ }
+ defer f.Close()
+ _, err = f.Write(b)
+ if err != nil {
+ return fmt.Errorf("failed to write server-info file: %w", err)
+ }
+ _, err = f.WriteString(END)
+ if err != nil {
+ return fmt.Errorf("failed to write END server-info file: %w", err)
+ }
+ return nil
+}
diff --git a/pkg/sdkclient/serverinfo/types.go b/pkg/sdkclient/serverinfo/types.go
new file mode 100644
index 0000000000..fc8fdd9b81
--- /dev/null
+++ b/pkg/sdkclient/serverinfo/types.go
@@ -0,0 +1,69 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package serverinfo
+
+type Language string
+
+const (
+ Go Language = "go"
+ Python Language = "python"
+ Java Language = "java"
+ Rust Language = "rust"
+)
+
+type sdkConstraints map[Language]string
+
+var minimumSupportedSDKVersions = sdkConstraints{
+ Go: "0.8.0",
+ Python: "0.8.0",
+ Java: "0.8.0",
+ Rust: "0.1.0",
+}
+
+type Protocol string
+
+const (
+ UDS Protocol = "uds"
+ TCP Protocol = "tcp"
+)
+
+type MapMode string
+
+const (
+ UnaryMap MapMode = "unary-map"
+ StreamMap MapMode = "stream-map"
+ BatchMap MapMode = "batch-map"
+)
+
+// Metadata keys used in the server info file
+const (
+ // MultiProcKey is the field used to indicate that MultiProc map mode is enabled
+ // The value contains the number of servers spawned.
+ MultiProcKey = "MULTIPROC"
+ // MapModeKey field is used to indicate which map mode is enabled
+ // If none is set, we consider the unary map as default
+ MapModeKey = "MAP_MODE"
+)
+
+// ServerInfo is the information about the server
+type ServerInfo struct {
+ Protocol Protocol `json:"protocol"`
+ Language Language `json:"language"`
+ MinimumNumaflowVersion string `json:"minimum_numaflow_version"`
+ Version string `json:"version"`
+ Metadata map[string]string `json:"metadata"`
+}
diff --git a/pkg/sdkclient/serverinfo/versions.go b/pkg/sdkclient/serverinfo/versions.go
deleted file mode 100644
index aded1c9bd3..0000000000
--- a/pkg/sdkclient/serverinfo/versions.go
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-Copyright 2022 The Numaproj Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package serverinfo
-
-import (
- "github.com/numaproj/numaflow-go/pkg/info"
-)
-
-type sdkConstraints map[info.Language]string
-
-var minimumSupportedSDKVersions = sdkConstraints{
- info.Go: "0.8.0",
- info.Python: "0.8.0",
- info.Java: "0.8.0",
-}
diff --git a/pkg/sdkclient/sessionreducer/client.go b/pkg/sdkclient/sessionreducer/client.go
index f2ff4d6770..a2c8b12f8e 100644
--- a/pkg/sdkclient/sessionreducer/client.go
+++ b/pkg/sdkclient/sessionreducer/client.go
@@ -25,10 +25,11 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
sessionreducepb "github.com/numaproj/numaflow-go/pkg/apis/proto/sessionreduce/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
+
"github.com/numaproj/numaflow/pkg/sdkclient"
sdkerr "github.com/numaproj/numaflow/pkg/sdkclient/error"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -38,7 +39,7 @@ type client struct {
}
// New creates a new client object.
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.SessionReduceAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/sideinput/client.go b/pkg/sdkclient/sideinput/client.go
index 8b9f9730f2..48c101f260 100644
--- a/pkg/sdkclient/sideinput/client.go
+++ b/pkg/sdkclient/sideinput/client.go
@@ -21,13 +21,13 @@ import (
"fmt"
"time"
+ sideinputpb "github.com/numaproj/numaflow-go/pkg/apis/proto/sideinput/v1"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
- sideinputpb "github.com/numaproj/numaflow-go/pkg/apis/proto/sideinput/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
"github.com/numaproj/numaflow/pkg/sdkclient"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -39,7 +39,7 @@ type client struct {
var _ Client = (*client)(nil)
// New creates a new client object.
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (*client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (*client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.SideInputAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/sinker/client.go b/pkg/sdkclient/sinker/client.go
index e8249d1859..67fe08557c 100644
--- a/pkg/sdkclient/sinker/client.go
+++ b/pkg/sdkclient/sinker/client.go
@@ -20,13 +20,13 @@ import (
"context"
"fmt"
+ sinkpb "github.com/numaproj/numaflow-go/pkg/apis/proto/sink/v1"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
- sinkpb "github.com/numaproj/numaflow-go/pkg/apis/proto/sink/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
"github.com/numaproj/numaflow/pkg/sdkclient"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -37,7 +37,7 @@ type client struct {
var _ Client = (*client)(nil)
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.SinkAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/source/client.go b/pkg/sdkclient/source/client.go
index c5275b3c77..39b96a13ed 100644
--- a/pkg/sdkclient/source/client.go
+++ b/pkg/sdkclient/source/client.go
@@ -21,13 +21,13 @@ import (
"fmt"
"io"
+ sourcepb "github.com/numaproj/numaflow-go/pkg/apis/proto/source/v1"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
- sourcepb "github.com/numaproj/numaflow-go/pkg/apis/proto/source/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
"github.com/numaproj/numaflow/pkg/sdkclient"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -38,7 +38,7 @@ type client struct {
var _ Client = (*client)(nil)
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.SourceAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sdkclient/sourcetransformer/client.go b/pkg/sdkclient/sourcetransformer/client.go
index 1893640a73..d9d47302c0 100644
--- a/pkg/sdkclient/sourcetransformer/client.go
+++ b/pkg/sdkclient/sourcetransformer/client.go
@@ -23,10 +23,11 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
transformpb "github.com/numaproj/numaflow-go/pkg/apis/proto/sourcetransform/v1"
- "github.com/numaproj/numaflow-go/pkg/info"
+
"github.com/numaproj/numaflow/pkg/sdkclient"
sdkerr "github.com/numaproj/numaflow/pkg/sdkclient/error"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
)
// client contains the grpc connection and the grpc client.
@@ -36,7 +37,7 @@ type client struct {
}
// New creates a new client object.
-func New(serverInfo *info.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.SourceTransformerAddr)
for _, inputOption := range inputOptions {
diff --git a/pkg/sideinputs/manager/manager.go b/pkg/sideinputs/manager/manager.go
index 84de31a2c9..5aea876c72 100644
--- a/pkg/sideinputs/manager/manager.go
+++ b/pkg/sideinputs/manager/manager.go
@@ -28,7 +28,7 @@ import (
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/isbsvc"
"github.com/numaproj/numaflow/pkg/sdkclient"
- sdkserverinfo "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
"github.com/numaproj/numaflow/pkg/sdkclient/sideinput"
jsclient "github.com/numaproj/numaflow/pkg/shared/clients/nats"
"github.com/numaproj/numaflow/pkg/shared/kvs"
@@ -83,7 +83,7 @@ func (sim *sideInputsManager) Start(ctx context.Context) error {
}
// Wait for server info to be ready
- serverInfo, err := sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.SideInputServerInfoFile))
+ serverInfo, err := serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.SideInputServerInfoFile))
if err != nil {
return err
}
diff --git a/pkg/sinks/sink.go b/pkg/sinks/sink.go
index f18f545127..413d232799 100644
--- a/pkg/sinks/sink.go
+++ b/pkg/sinks/sink.go
@@ -32,7 +32,7 @@ import (
"github.com/numaproj/numaflow/pkg/isbsvc"
"github.com/numaproj/numaflow/pkg/metrics"
"github.com/numaproj/numaflow/pkg/sdkclient"
- sdkserverinfo "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
sinkclient "github.com/numaproj/numaflow/pkg/sdkclient/sinker"
"github.com/numaproj/numaflow/pkg/shared/callback"
jsclient "github.com/numaproj/numaflow/pkg/shared/clients/nats"
@@ -150,7 +150,7 @@ func (u *SinkProcessor) Start(ctx context.Context) error {
maxMessageSize := sharedutil.LookupEnvIntOr(dfv1.EnvGRPCMaxMessageSize, sdkclient.DefaultGRPCMaxMessageSize)
if udSink := u.VertexInstance.Vertex.Spec.Sink.UDSink; udSink != nil {
// Wait for server info to be ready
- serverInfo, err := sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.SinkServerInfoFile))
+ serverInfo, err := serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.SinkServerInfoFile))
if err != nil {
return err
}
@@ -179,7 +179,7 @@ func (u *SinkProcessor) Start(ctx context.Context) error {
if u.VertexInstance.Vertex.HasFallbackUDSink() {
// Wait for server info to be ready
- serverInfo, err := sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.FbSinkServerInfoFile))
+ serverInfo, err := serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.FbSinkServerInfoFile))
if err != nil {
return err
}
diff --git a/pkg/sources/source.go b/pkg/sources/source.go
index 8a8e64ffd6..e206d76dbc 100644
--- a/pkg/sources/source.go
+++ b/pkg/sources/source.go
@@ -34,7 +34,7 @@ import (
"github.com/numaproj/numaflow/pkg/isbsvc"
"github.com/numaproj/numaflow/pkg/metrics"
"github.com/numaproj/numaflow/pkg/sdkclient"
- sdkserverinfo "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
sourceclient "github.com/numaproj/numaflow/pkg/sdkclient/source"
"github.com/numaproj/numaflow/pkg/sdkclient/sourcetransformer"
"github.com/numaproj/numaflow/pkg/shared/callback"
@@ -196,7 +196,7 @@ func (sp *SourceProcessor) Start(ctx context.Context) error {
var udsGRPCClient *udsource.GRPCBasedUDSource
if sp.VertexInstance.Vertex.IsUDSource() {
// Wait for server info to be ready
- serverInfo, err := sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.SourceServerInfoFile))
+ serverInfo, err := serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.SourceServerInfoFile))
if err != nil {
return err
}
@@ -235,7 +235,7 @@ func (sp *SourceProcessor) Start(ctx context.Context) error {
if sp.VertexInstance.Vertex.HasUDTransformer() {
// Wait for server info to be ready
- serverInfo, err := sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.SourceTransformerServerInfoFile))
+ serverInfo, err := serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.SourceTransformerServerInfoFile))
if err != nil {
return err
}
diff --git a/pkg/udf/map_udf.go b/pkg/udf/map_udf.go
index 44cb1b5aa7..5d926751f0 100644
--- a/pkg/udf/map_udf.go
+++ b/pkg/udf/map_udf.go
@@ -33,7 +33,7 @@ import (
"github.com/numaproj/numaflow/pkg/sdkclient/batchmapper"
"github.com/numaproj/numaflow/pkg/sdkclient/mapper"
"github.com/numaproj/numaflow/pkg/sdkclient/mapstreamer"
- sdkserverinfo "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
"github.com/numaproj/numaflow/pkg/shared/callback"
jsclient "github.com/numaproj/numaflow/pkg/shared/clients/nats"
"github.com/numaproj/numaflow/pkg/shared/logging"
@@ -138,16 +138,16 @@ func (u *MapUDFProcessor) Start(ctx context.Context) error {
maxMessageSize := sharedutil.LookupEnvIntOr(dfv1.EnvGRPCMaxMessageSize, sdkclient.DefaultGRPCMaxMessageSize)
// Wait for map server info to be ready, we use the same info file for all the map modes
- serverInfo, err := sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.MapServerInfoFile))
+ serverInfo, err := serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.MapServerInfoFile))
if err != nil {
return err
}
// Read the server info file to read which map mode is enabled
// Based on the value set, we will create the corresponding handler and clients
- mapMode, ok := serverInfo.Metadata[sdkserverinfo.MapModeMetadata]
+ mapMode, ok := serverInfo.Metadata[serverinfo.MapModeKey]
- if ok && (sdkserverinfo.MapMode(mapMode) == sdkserverinfo.StreamMap) {
+ if ok && (serverinfo.MapMode(mapMode) == serverinfo.StreamMap) {
log.Info("Map mode enabled: Stream Map")
// Map Stream mode
enableMapUdfStream = true
@@ -170,9 +170,9 @@ func (u *MapUDFProcessor) Start(ctx context.Context) error {
}()
opts = append(opts, forward.WithUDFStreamingMap(mapStreamHandler))
- } else if ok && (sdkserverinfo.MapMode(mapMode) == sdkserverinfo.BatchMap) {
+ } else if ok && (serverinfo.MapMode(mapMode) == serverinfo.BatchMap) {
log.Info("Map mode enabled: Batch Map")
- // if Batch Map mode is enabled create the client and handler for that accordingly
+ // if Batch Map mode is enabled, create the client and handler for that accordingly
enableBatchMapUdf = true
// create the client and handler for batch map interface
diff --git a/pkg/udf/reduce_udf.go b/pkg/udf/reduce_udf.go
index eec3df3ff4..a1c9500f2e 100644
--- a/pkg/udf/reduce_udf.go
+++ b/pkg/udf/reduce_udf.go
@@ -23,12 +23,8 @@ import (
"strings"
"sync"
- "github.com/numaproj/numaflow-go/pkg/info"
"go.uber.org/zap"
- alignedfs "github.com/numaproj/numaflow/pkg/reduce/pbq/wal/aligned/fs"
- noopwal "github.com/numaproj/numaflow/pkg/reduce/pbq/wal/noop"
-
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/forwarder"
"github.com/numaproj/numaflow/pkg/isb"
@@ -36,12 +32,14 @@ import (
"github.com/numaproj/numaflow/pkg/reduce"
"github.com/numaproj/numaflow/pkg/reduce/applier"
"github.com/numaproj/numaflow/pkg/reduce/pbq"
+ alignedfs "github.com/numaproj/numaflow/pkg/reduce/pbq/wal/aligned/fs"
+ noopwal "github.com/numaproj/numaflow/pkg/reduce/pbq/wal/noop"
"github.com/numaproj/numaflow/pkg/reduce/pbq/wal/unaligned"
unalignedfs "github.com/numaproj/numaflow/pkg/reduce/pbq/wal/unaligned/fs"
"github.com/numaproj/numaflow/pkg/reduce/pnf"
"github.com/numaproj/numaflow/pkg/sdkclient"
"github.com/numaproj/numaflow/pkg/sdkclient/reducer"
- sdkserverinfo "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
"github.com/numaproj/numaflow/pkg/sdkclient/sessionreducer"
jsclient "github.com/numaproj/numaflow/pkg/shared/clients/nats"
"github.com/numaproj/numaflow/pkg/shared/logging"
@@ -94,19 +92,19 @@ func (u *ReduceUDFProcessor) Start(ctx context.Context) error {
// create udf handler and wait until it is ready
if windowType.Fixed != nil || windowType.Sliding != nil {
- var serverInfo *info.ServerInfo
+ var serverInfo *serverinfo.ServerInfo
var client reducer.Client
// if streaming is enabled, use the reduceStreaming address
if (windowType.Fixed != nil && windowType.Fixed.Streaming) || (windowType.Sliding != nil && windowType.Sliding.Streaming) {
// Wait for server info to be ready
- serverInfo, err = sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.ReduceStreamServerInfoFile))
+ serverInfo, err = serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.ReduceStreamServerInfoFile))
if err != nil {
return err
}
client, err = reducer.New(serverInfo, sdkclient.WithMaxMessageSize(maxMessageSize), sdkclient.WithUdsSockAddr(sdkclient.ReduceStreamAddr))
} else {
// Wait for server info to be ready
- serverInfo, err = sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.ReduceServerInfoFile))
+ serverInfo, err = serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.ReduceServerInfoFile))
if err != nil {
return err
}
@@ -132,7 +130,7 @@ func (u *ReduceUDFProcessor) Start(ctx context.Context) error {
healthChecker = reduceHandler
} else if windowType.Session != nil {
// Wait for server info to be ready
- serverInfo, err := sdkserverinfo.SDKServerInfo(sdkserverinfo.WithServerInfoFilePath(sdkclient.SessionReduceServerInfoFile))
+ serverInfo, err := serverinfo.SDKServerInfo(serverinfo.WithServerInfoFilePath(sdkclient.SessionReduceServerInfoFile))
if err != nil {
return err
}
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 21d6a28a7d..7748607ca5 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -4,18 +4,18 @@ version = 3
[[package]]
name = "addr2line"
-version = "0.22.0"
+version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375"
dependencies = [
"gimli",
]
[[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aho-corasick"
@@ -43,9 +43,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.86"
+version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356"
[[package]]
name = "arc-swap"
@@ -70,9 +70,9 @@ dependencies = [
"rand",
"regex",
"ring",
- "rustls-native-certs",
+ "rustls-native-certs 0.7.3",
"rustls-pemfile 2.1.3",
- "rustls-webpki 0.102.6",
+ "rustls-webpki 0.102.8",
"serde",
"serde_json",
"serde_nanos",
@@ -110,9 +110,9 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.81"
+version = "0.1.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
+checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
dependencies = [
"proc-macro2",
"quote",
@@ -133,9 +133,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "aws-lc-rs"
-version = "1.8.1"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77"
+checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070"
dependencies = [
"aws-lc-sys",
"mirai-annotations",
@@ -145,9 +145,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
-version = "0.20.1"
+version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f0e249228c6ad2d240c2dc94b714d711629d52bad946075d8e9b2f5391f0703"
+checksum = "234314bd569802ec87011d653d6815c6d7b9ffb969e9fee5b8b20ef860e8dce9"
dependencies = [
"bindgen",
"cc",
@@ -240,7 +240,7 @@ dependencies = [
"hyper 1.4.1",
"hyper-util",
"pin-project-lite",
- "rustls 0.23.12",
+ "rustls 0.23.13",
"rustls-pemfile 2.1.3",
"rustls-pki-types",
"tokio",
@@ -259,17 +259,17 @@ dependencies = [
[[package]]
name = "backtrace"
-version = "0.3.73"
+version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
- "cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -360,12 +360,13 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.1.7"
+version = "1.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
+checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476"
dependencies = [
"jobserver",
"libc",
+ "shlex",
]
[[package]]
@@ -411,9 +412,9 @@ dependencies = [
[[package]]
name = "cmake"
-version = "0.1.50"
+version = "0.1.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
+checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a"
dependencies = [
"cc",
]
@@ -499,15 +500,15 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
dependencies = [
"libc",
]
@@ -667,9 +668,9 @@ dependencies = [
[[package]]
name = "fastrand"
-version = "2.1.0"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fiat-crypto"
@@ -816,9 +817,9 @@ dependencies = [
[[package]]
name = "gimli"
-version = "0.29.0"
+version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64"
[[package]]
name = "glob"
@@ -838,7 +839,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
- "indexmap 2.3.0",
+ "indexmap 2.5.0",
"slab",
"tokio",
"tokio-util",
@@ -847,9 +848,9 @@ dependencies = [
[[package]]
name = "h2"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
+checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
dependencies = [
"atomic-waker",
"bytes",
@@ -857,7 +858,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.1.0",
- "indexmap 2.3.0",
+ "indexmap 2.5.0",
"slab",
"tokio",
"tokio-util",
@@ -1034,7 +1035,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
- "h2 0.4.5",
+ "h2 0.4.6",
"http 1.1.0",
"http-body 1.0.1",
"httparse",
@@ -1057,10 +1058,10 @@ dependencies = [
"headers",
"http 1.1.0",
"hyper 1.4.1",
- "hyper-rustls 0.27.2",
+ "hyper-rustls 0.27.3",
"hyper-util",
"pin-project-lite",
- "rustls-native-certs",
+ "rustls-native-certs 0.7.3",
"tokio",
"tokio-rustls 0.26.0",
"tower-service",
@@ -1082,17 +1083,17 @@ dependencies = [
[[package]]
name = "hyper-rustls"
-version = "0.27.2"
+version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
+checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http 1.1.0",
"hyper 1.4.1",
"hyper-util",
"log",
- "rustls 0.23.12",
- "rustls-native-certs",
+ "rustls 0.23.13",
+ "rustls-native-certs 0.8.0",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.0",
@@ -1114,9 +1115,9 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.6"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
+checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba"
dependencies = [
"bytes",
"futures-channel",
@@ -1177,9 +1178,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.3.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
+checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
@@ -1187,9 +1188,9 @@ dependencies = [
[[package]]
name = "ipnet"
-version = "2.9.0"
+version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4"
[[package]]
name = "itertools"
@@ -1226,9 +1227,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.69"
+version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
dependencies = [
"wasm-bindgen",
]
@@ -1274,9 +1275,9 @@ dependencies = [
[[package]]
name = "kube"
-version = "0.94.0"
+version = "0.94.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65b8611df85a1a2eed6f47bd8bcca4e2b3dc14fbf83658efd01423ca9a13b72a"
+checksum = "52ace78a62b361077505f2950bd48aa3e46596fb15350c9c993de15ddfa3cac5"
dependencies = [
"k8s-openapi",
"kube-client",
@@ -1285,9 +1286,9 @@ dependencies = [
[[package]]
name = "kube-client"
-version = "0.94.0"
+version = "0.94.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93c5ee3e48ef9b8d8fdb40ddd935f8addc8a201397e3c7552edae7bc96bc0a78"
+checksum = "18ec0fcafd3add30b413b096a61d69b0a37f94d3f95b6f505a57ea3d27cec2a7"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -1300,14 +1301,14 @@ dependencies = [
"http-body-util",
"hyper 1.4.1",
"hyper-http-proxy",
- "hyper-rustls 0.27.2",
+ "hyper-rustls 0.27.3",
"hyper-timeout",
"hyper-util",
"jsonpath-rust",
"k8s-openapi",
"kube-core",
"pem",
- "rustls 0.23.12",
+ "rustls 0.23.13",
"rustls-pemfile 2.1.3",
"secrecy",
"serde",
@@ -1323,9 +1324,9 @@ dependencies = [
[[package]]
name = "kube-core"
-version = "0.94.0"
+version = "0.94.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe6e24d4cc7e32576f363986dc3dfc13e8e90731bd7a467b67fc6c4bfbf8e95"
+checksum = "a50c095f051dada37740d883b6d47ad0430e95082140718073b773c8a70f231c"
dependencies = [
"chrono",
"form_urlencoded",
@@ -1351,9 +1352,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
-version = "0.2.155"
+version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libloading"
@@ -1438,18 +1439,18 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
-version = "0.7.4"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
- "adler",
+ "adler2",
]
[[package]]
name = "mio"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
"hermit-abi",
"libc",
@@ -1475,7 +1476,7 @@ dependencies = [
"chrono",
"hyper-util",
"kube",
- "numaflow 0.1.0 (git+https://github.com/numaproj/numaflow-rs.git?branch=main)",
+ "numaflow 0.1.1",
"numaflow-models",
"once_cell",
"parking_lot",
@@ -1484,7 +1485,7 @@ dependencies = [
"prost",
"prost-types",
"rcgen",
- "rustls 0.23.12",
+ "rustls 0.23.13",
"semver",
"serde",
"serde_json",
@@ -1601,8 +1602,8 @@ dependencies = [
[[package]]
name = "numaflow"
-version = "0.1.0"
-source = "git+https://github.com/numaproj/numaflow-rs.git?branch=main#f265a615716ab3ec3adf85e8c24413cc076cd695"
+version = "0.1.1"
+source = "git+https://github.com/numaproj/numaflow-rs.git?branch=main#d3afabd2fff1d070bb3fd79866c0389f009556b3"
dependencies = [
"chrono",
"futures-util",
@@ -1637,9 +1638,9 @@ dependencies = [
[[package]]
name = "object"
-version = "0.36.2"
+version = "0.36.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
+checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a"
dependencies = [
"memchr",
]
@@ -1754,9 +1755,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
-version = "2.7.11"
+version = "2.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
+checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea"
dependencies = [
"memchr",
"thiserror",
@@ -1765,9 +1766,9 @@ dependencies = [
[[package]]
name = "pest_derive"
-version = "2.7.11"
+version = "2.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
+checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d"
dependencies = [
"pest",
"pest_generator",
@@ -1775,9 +1776,9 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.7.11"
+version = "2.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
+checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe"
dependencies = [
"pest",
"pest_meta",
@@ -1788,9 +1789,9 @@ dependencies = [
[[package]]
name = "pest_meta"
-version = "2.7.11"
+version = "2.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
+checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174"
dependencies = [
"once_cell",
"pest",
@@ -1804,7 +1805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
- "indexmap 2.3.0",
+ "indexmap 2.5.0",
]
[[package]]
@@ -1872,9 +1873,9 @@ dependencies = [
[[package]]
name = "prettyplease"
-version = "0.2.20"
+version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
+checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
dependencies = [
"proc-macro2",
"syn",
@@ -1914,9 +1915,9 @@ dependencies = [
[[package]]
name = "prost"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc"
+checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995"
dependencies = [
"bytes",
"prost-derive",
@@ -1924,9 +1925,9 @@ dependencies = [
[[package]]
name = "prost-build"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1"
+checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302"
dependencies = [
"bytes",
"heck 0.5.0",
@@ -1945,9 +1946,9 @@ dependencies = [
[[package]]
name = "prost-derive"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca"
+checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac"
dependencies = [
"anyhow",
"itertools 0.13.0",
@@ -1958,18 +1959,18 @@ dependencies = [
[[package]]
name = "prost-types"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2"
+checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519"
dependencies = [
"prost",
]
[[package]]
name = "quote"
-version = "1.0.36"
+version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
@@ -2044,9 +2045,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.3"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
dependencies = [
"bitflags 2.6.0",
]
@@ -2188,18 +2189,18 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
-version = "0.38.34"
+version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [
"bitflags 2.6.0",
"errno",
@@ -2222,25 +2223,38 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.23.12"
+version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
+checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring",
"rustls-pki-types",
- "rustls-webpki 0.102.6",
+ "rustls-webpki 0.102.8",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
-version = "0.7.1"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 2.1.3",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba"
+checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
dependencies = [
"openssl-probe",
"rustls-pemfile 2.1.3",
@@ -2270,9 +2284,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-webpki"
@@ -2286,9 +2300,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.102.6"
+version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
+checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"aws-lc-rs",
"ring",
@@ -2310,11 +2324,11 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "schannel"
-version = "0.1.23"
+version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2374,9 +2388,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
-version = "1.0.204"
+version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
@@ -2393,9 +2407,9 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.204"
+version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
@@ -2404,9 +2418,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.122"
+version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
+checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
@@ -2471,7 +2485,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.5.0",
"itoa",
"ryu",
"serde",
@@ -2482,7 +2496,7 @@ dependencies = [
name = "servesink"
version = "0.1.0"
dependencies = [
- "numaflow 0.1.0 (git+https://github.com/numaproj/numaflow-rs.git?branch=main)",
+ "numaflow 0.1.1",
"reqwest",
"tokio",
"tonic",
@@ -2644,9 +2658,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
-version = "2.0.72"
+version = "2.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
dependencies = [
"proc-macro2",
"quote",
@@ -2688,15 +2702,15 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.11.0"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
dependencies = [
"cfg-if",
"fastrand",
"once_cell",
"rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2786,9 +2800,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.39.3"
+version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
+checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
dependencies = [
"backtrace",
"bytes",
@@ -2840,16 +2854,16 @@ version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
- "rustls 0.23.12",
+ "rustls 0.23.13",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-stream"
-version = "0.1.15"
+version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
+checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -2858,9 +2872,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.11"
+version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
+checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
dependencies = [
"bytes",
"futures-core",
@@ -2896,7 +2910,7 @@ version = "0.22.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.5.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -2905,16 +2919,16 @@ dependencies = [
[[package]]
name = "tonic"
-version = "0.12.1"
+version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401"
+checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad"
dependencies = [
"async-stream",
"async-trait",
"axum",
"base64 0.22.1",
"bytes",
- "h2 0.4.5",
+ "h2 0.4.6",
"http 1.1.0",
"http-body 1.0.1",
"http-body-util",
@@ -2935,9 +2949,9 @@ dependencies = [
[[package]]
name = "tonic-build"
-version = "0.12.1"
+version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "568392c5a2bd0020723e3f387891176aabafe36fd9fcd074ad309dfa0c8eb964"
+checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67"
dependencies = [
"prettyplease",
"proc-macro2",
@@ -2994,9 +3008,9 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
@@ -3117,9 +3131,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-normalization"
@@ -3132,9 +3146,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
@@ -3210,19 +3224,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
+ "once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
@@ -3235,9 +3250,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.42"
+version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
dependencies = [
"cfg-if",
"js-sys",
@@ -3247,9 +3262,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3257,9 +3272,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
@@ -3270,15 +3285,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "web-sys"
-version = "0.3.69"
+version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3351,6 +3366,15 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -3535,17 +3559,3 @@ name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
diff --git a/rust/monovertex/src/lib.rs b/rust/monovertex/src/lib.rs
index c1d172adf9..af69199be2 100644
--- a/rust/monovertex/src/lib.rs
+++ b/rust/monovertex/src/lib.rs
@@ -134,7 +134,7 @@ pub async fn init(
.await
.map_err(|e| {
warn!("Error waiting for source server info file: {:?}", e);
- Error::ForwarderError("Error waiting for server info file".to_string())
+ Error::ForwarderError(format!("Error waiting for source server info file: {}", e))
})?;
let mut source_client = SourceClient::connect(source_config).await?;
@@ -142,7 +142,7 @@ pub async fn init(
.await
.map_err(|e| {
warn!("Error waiting for sink server info file: {:?}", e);
- Error::ForwarderError("Error waiting for server info file".to_string())
+ Error::ForwarderError(format!("Error waiting for sink server info file: {}", e))
})?;
let mut sink_client = SinkClient::connect(sink_config).await?;
@@ -152,7 +152,7 @@ pub async fn init(
.await
.map_err(|e| {
warn!("Error waiting for transformer server info file: {:?}", e);
- Error::ForwarderError("Error waiting for server info file".to_string())
+ Error::ForwarderError(format!("Error waiting for transformer server info file: {}", e))
})?;
Some(TransformerClient::connect(config).await?)
} else {
@@ -164,7 +164,7 @@ pub async fn init(
.await
.map_err(|e| {
warn!("Error waiting for fallback sink server info file: {:?}", e);
- Error::ForwarderError("Error waiting for server info file".to_string())
+ Error::ForwarderError(format!("Error waiting for fallback sink server info file: {}", e))
})?;
Some(SinkClient::connect(config).await?)
} else {
diff --git a/rust/monovertex/src/server_info.rs b/rust/monovertex/src/server_info.rs
index 7412b2ca9d..225218b158 100644
--- a/rust/monovertex/src/server_info.rs
+++ b/rust/monovertex/src/server_info.rs
@@ -256,11 +256,12 @@ mod version {
// MINIMUM_SUPPORTED_SDK_VERSIONS is a HashMap with SDK language as key and minimum supported version as value
static MINIMUM_SUPPORTED_SDK_VERSIONS: Lazy = Lazy::new(|| {
// TODO: populate this from a static file and make it part of the release process
+ // the value of the map matches `minimumSupportedSDKVersions` in pkg/sdkclient/serverinfo/types.go
let mut m = HashMap::new();
- m.insert("go".to_string(), "0.7.0-rc2".to_string());
- m.insert("python".to_string(), "0.7.0a1".to_string());
- m.insert("java".to_string(), "0.7.2-0".to_string());
- m.insert("rust".to_string(), "0.0.1".to_string());
+ m.insert("go".to_string(), "0.8.0".to_string());
+ m.insert("python".to_string(), "0.8.0".to_string());
+ m.insert("java".to_string(), "0.8.0".to_string());
+ m.insert("rust".to_string(), "0.1.0".to_string());
m
});
@@ -402,6 +403,7 @@ mod tests {
constraints.insert("python".to_string(), "1.2.0".to_string());
constraints.insert("java".to_string(), "2.0.0".to_string());
constraints.insert("go".to_string(), "0.10.0".to_string());
+ constraints.insert("rust".to_string(), "0.1.0".to_string());
constraints
}
@@ -477,6 +479,30 @@ mod tests {
assert!(result.is_err());
}
+ #[tokio::test]
+ async fn test_sdk_compatibility_rust_valid() {
+ let sdk_version = "v0.1.0";
+ let sdk_language = "rust";
+
+ let min_supported_sdk_versions = create_sdk_constraints();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_rust_invalid() {
+ let sdk_version = "0.0.9";
+ let sdk_language = "rust";
+
+ let min_supported_sdk_versions = create_sdk_constraints();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_err());
+ }
+
#[tokio::test]
async fn test_numaflow_compatibility_valid() {
let numaflow_version = "1.4.0";
From c6003314c8f77905fbd86ddccab12853ca6c63a1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 13 Sep 2024 22:28:28 -0700
Subject: [PATCH 058/188] chore(deps): bump express from 4.19.2 to 4.21.0 in
/ui (#2061)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
ui/yarn.lock | 93 +++++++++++++++++++++++++++-------------------------
1 file changed, 49 insertions(+), 44 deletions(-)
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 36a04c8dd9..d71fb7d1e6 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -3857,10 +3857,10 @@ bluebird@^3.7.2:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
-body-parser@1.20.2:
- version "1.20.2"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
- integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
+body-parser@1.20.3:
+ version "1.20.3"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
+ integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
@@ -3870,7 +3870,7 @@ body-parser@1.20.2:
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
- qs "6.11.0"
+ qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@@ -5176,6 +5176,11 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+encodeurl@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
+ integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
+
enhanced-resolve@^5.17.1:
version "5.17.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
@@ -5791,36 +5796,36 @@ expect@^27.5.1:
jest-message-util "^27.5.1"
express@^4.17.3:
- version "4.19.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
- integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.21.0.tgz#d57cb706d49623d4ac27833f1cbc466b668eb915"
+ integrity sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
- body-parser "1.20.2"
+ body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
- encodeurl "~1.0.2"
+ encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
- finalhandler "1.2.0"
+ finalhandler "1.3.1"
fresh "0.5.2"
http-errors "2.0.0"
- merge-descriptors "1.0.1"
+ merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
- path-to-regexp "0.1.7"
+ path-to-regexp "0.1.10"
proxy-addr "~2.0.7"
- qs "6.11.0"
+ qs "6.13.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
- send "0.18.0"
- serve-static "1.15.0"
+ send "0.19.0"
+ serve-static "1.16.2"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
@@ -5959,13 +5964,13 @@ fill-range@^7.1.1:
dependencies:
to-regex-range "^5.0.1"
-finalhandler@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
- integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
+finalhandler@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
+ integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
dependencies:
debug "2.6.9"
- encodeurl "~1.0.2"
+ encodeurl "~2.0.0"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
@@ -8054,10 +8059,10 @@ memoize-one@^4.0.0:
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906"
integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA==
-merge-descriptors@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
- integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
+merge-descriptors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
+ integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
merge-stream@^2.0.0:
version "2.0.0"
@@ -8638,10 +8643,10 @@ path-scurry@^1.11.1:
lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
-path-to-regexp@0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
- integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+path-to-regexp@0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
+ integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path-to-regexp@^1.7.0:
version "1.8.0"
@@ -9394,12 +9399,12 @@ q@^1.1.2:
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
-qs@6.11.0:
- version "6.11.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
- integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
+qs@6.13.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+ integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
- side-channel "^1.0.4"
+ side-channel "^1.0.6"
querystringify@^2.1.1:
version "2.2.0"
@@ -10112,10 +10117,10 @@ semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semve
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
-send@0.18.0:
- version "0.18.0"
- resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
- integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
+send@0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
+ integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
@@ -10158,15 +10163,15 @@ serve-index@^1.9.1:
mime-types "~2.1.17"
parseurl "~1.3.2"
-serve-static@1.15.0:
- version "1.15.0"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
- integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+serve-static@1.16.2:
+ version "1.16.2"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
+ integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
dependencies:
- encodeurl "~1.0.2"
+ encodeurl "~2.0.0"
escape-html "~1.0.3"
parseurl "~1.3.3"
- send "0.18.0"
+ send "0.19.0"
set-cookie-parser@^2.4.6:
version "2.6.0"
From 910ff9b4ec15e4a6d0bea0b790a9ec97bbe7e119 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Fri, 13 Sep 2024 22:51:04 -0700
Subject: [PATCH 059/188] chore: patch instead of update and bugfix (#2059)
Signed-off-by: Derek Wang
---
pkg/apis/numaflow/v1alpha1/const.go | 2 -
pkg/reconciler/isbsvc/controller.go | 26 +++++-------
pkg/reconciler/isbsvc/controller_test.go | 24 -----------
pkg/reconciler/monovertex/controller.go | 2 +
pkg/reconciler/monovertex/scaling/scaling.go | 10 +----
pkg/reconciler/pipeline/controller.go | 43 +++++++-------------
pkg/reconciler/pipeline/controller_test.go | 15 -------
pkg/reconciler/vertex/controller.go | 2 +
pkg/reconciler/vertex/scaling/scaling.go | 10 +----
9 files changed, 32 insertions(+), 102 deletions(-)
diff --git a/pkg/apis/numaflow/v1alpha1/const.go b/pkg/apis/numaflow/v1alpha1/const.go
index d0e9eb62f0..e36ec9bd34 100644
--- a/pkg/apis/numaflow/v1alpha1/const.go
+++ b/pkg/apis/numaflow/v1alpha1/const.go
@@ -41,8 +41,6 @@ const (
KeyPauseTimestamp = "numaflow.numaproj.io/pause-timestamp"
KeyDefaultContainer = "kubectl.kubernetes.io/default-container"
- RemovePauseTimestampPatch = `[{"op": "remove", "path": "/metadata/annotations/numaflow.numaproj.io~1pause-timestamp"}]`
-
// ID key in the header of sources like http
KeyMetaID = "X-Numaflow-Id"
KeyMetaEventTime = "X-Numaflow-Event-Time"
diff --git a/pkg/reconciler/isbsvc/controller.go b/pkg/reconciler/isbsvc/controller.go
index 1ab7d4e79b..d94e14424d 100644
--- a/pkg/reconciler/isbsvc/controller.go
+++ b/pkg/reconciler/isbsvc/controller.go
@@ -18,17 +18,20 @@ package isbsvc
import (
"context"
+ "strings"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
+ "sigs.k8s.io/yaml"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/reconciler"
@@ -58,7 +61,7 @@ func (r *interStepBufferServiceReconciler) Reconcile(ctx context.Context, req ct
isbSvc := &dfv1.InterStepBufferService{}
if err := r.client.Get(ctx, req.NamespacedName, isbSvc); err != nil {
if apierrors.IsNotFound(err) {
- return reconcile.Result{}, nil
+ return ctrl.Result{}, nil
}
r.logger.Errorw("Unable to get ISB Service", zap.Any("request", req), zap.Error(err))
return ctrl.Result{}, err
@@ -69,14 +72,15 @@ func (r *interStepBufferServiceReconciler) Reconcile(ctx context.Context, req ct
if reconcileErr != nil {
log.Errorw("Reconcile error", zap.Error(reconcileErr))
}
- if r.needsUpdate(isbSvc, isbSvcCopy) {
- // Update with a DeepCopy because .Status will be cleaned up.
- if err := r.client.Update(ctx, isbSvcCopy.DeepCopy()); err != nil {
- return reconcile.Result{}, err
+ if !equality.Semantic.DeepEqual(isbSvc.Finalizers, isbSvcCopy.Finalizers) {
+ patchYaml := "metadata:\n finalizers: [" + strings.Join(isbSvcCopy.Finalizers, ",") + "]"
+ patchJson, _ := yaml.YAMLToJSON([]byte(patchYaml))
+ if err := r.client.Patch(ctx, isbSvc, client.RawPatch(types.MergePatchType, []byte(patchJson))); err != nil {
+ return ctrl.Result{}, err
}
}
if err := r.client.Status().Update(ctx, isbSvcCopy); err != nil {
- return reconcile.Result{}, err
+ return ctrl.Result{}, err
}
return ctrl.Result{}, reconcileErr
}
@@ -122,16 +126,6 @@ func (r *interStepBufferServiceReconciler) reconcile(ctx context.Context, isbSvc
return installer.Install(ctx, isbSvc, r.client, r.kubeClient, r.config, log, r.recorder)
}
-func (r *interStepBufferServiceReconciler) needsUpdate(old, new *dfv1.InterStepBufferService) bool {
- if old == nil {
- return true
- }
- if !equality.Semantic.DeepEqual(old.Finalizers, new.Finalizers) {
- return true
- }
- return false
-}
-
func needsFinalizer(isbSvc *dfv1.InterStepBufferService) bool {
if isbSvc.Spec.Redis != nil && isbSvc.Spec.Redis.Native != nil && isbSvc.Spec.Redis.Native.Persistence != nil {
return true
diff --git a/pkg/reconciler/isbsvc/controller_test.go b/pkg/reconciler/isbsvc/controller_test.go
index 82aee9d90f..2a24ec7c69 100644
--- a/pkg/reconciler/isbsvc/controller_test.go
+++ b/pkg/reconciler/isbsvc/controller_test.go
@@ -202,42 +202,18 @@ func TestReconcileJetStream(t *testing.T) {
func TestNeedsUpdate(t *testing.T) {
t.Run("needs redis update", func(t *testing.T) {
testIsbs := nativeRedisIsbs.DeepCopy()
- cl := fake.NewClientBuilder().Build()
- r := &interStepBufferServiceReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- logger: zaptest.NewLogger(t).Sugar(),
- }
- assert.False(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
controllerutil.AddFinalizer(testIsbs, finalizerName)
assert.True(t, contains(testIsbs.Finalizers, finalizerName))
- assert.True(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
controllerutil.RemoveFinalizer(testIsbs, finalizerName)
assert.False(t, contains(testIsbs.Finalizers, finalizerName))
- assert.False(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
- testIsbs.Status.MarkConfigured()
- assert.False(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
})
t.Run("needs jetstream update", func(t *testing.T) {
testIsbs := jetStreamIsbs.DeepCopy()
- cl := fake.NewClientBuilder().Build()
- r := &interStepBufferServiceReconciler{
- client: cl,
- scheme: scheme.Scheme,
- config: reconciler.FakeGlobalConfig(t, fakeGlobalISBSvcConfig),
- logger: zaptest.NewLogger(t).Sugar(),
- }
- assert.False(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
controllerutil.AddFinalizer(testIsbs, finalizerName)
assert.True(t, contains(testIsbs.Finalizers, finalizerName))
- assert.True(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
controllerutil.RemoveFinalizer(testIsbs, finalizerName)
assert.False(t, contains(testIsbs.Finalizers, finalizerName))
- assert.False(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
- testIsbs.Status.MarkConfigured()
- assert.False(t, r.needsUpdate(nativeRedisIsbs, testIsbs))
})
}
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index 9aca247a05..3fbfb3c1ab 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -236,6 +236,8 @@ func (mr *monoVertexReconciler) orchestratePods(ctx context.Context, monoVtx *df
monoVtx.Status.CurrentHash = monoVtx.Status.UpdateHash
} else { // Update scenario
if updatedReplicas >= desiredReplicas {
+ monoVtx.Status.UpdatedReplicas = uint32(desiredReplicas)
+ monoVtx.Status.CurrentHash = monoVtx.Status.UpdateHash
return nil
}
diff --git a/pkg/reconciler/monovertex/scaling/scaling.go b/pkg/reconciler/monovertex/scaling/scaling.go
index 481408672c..0b35265190 100644
--- a/pkg/reconciler/monovertex/scaling/scaling.go
+++ b/pkg/reconciler/monovertex/scaling/scaling.go
@@ -19,7 +19,6 @@ package scaling
import (
"container/list"
"context"
- "encoding/json"
"fmt"
"math"
"strings"
@@ -30,7 +29,6 @@ import (
"go.uber.org/zap"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
- "k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
@@ -359,12 +357,8 @@ func (s *Scaler) Start(ctx context.Context) error {
func (s *Scaler) patchMonoVertexReplicas(ctx context.Context, monoVtx *dfv1.MonoVertex, desiredReplicas int32) error {
log := logging.FromContext(ctx)
origin := monoVtx.Spec.Replicas
- monoVtx.Spec.Replicas = ptr.To[int32](desiredReplicas)
- body, err := json.Marshal(monoVtx)
- if err != nil {
- return fmt.Errorf("failed to marshal MonoVertex object to json, %w", err)
- }
- if err := s.client.Patch(ctx, monoVtx, client.RawPatch(types.MergePatchType, body)); err != nil && !apierrors.IsNotFound(err) {
+ patchJson := fmt.Sprintf(`{"spec":{"replicas":%d}}`, desiredReplicas)
+ if err := s.client.Patch(ctx, monoVtx, client.RawPatch(types.MergePatchType, []byte(patchJson))); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to patch MonoVertex replicas, %w", err)
}
log.Infow("Auto scaling - mono vertex replicas changed.", zap.Int32p("from", origin), zap.Int32("to", desiredReplicas), zap.String("namespace", monoVtx.Namespace), zap.String("vertex", monoVtx.Name))
diff --git a/pkg/reconciler/pipeline/controller.go b/pkg/reconciler/pipeline/controller.go
index d8b989f2d6..b2f99e7b1d 100644
--- a/pkg/reconciler/pipeline/controller.go
+++ b/pkg/reconciler/pipeline/controller.go
@@ -18,7 +18,6 @@ package pipeline
import (
"context"
- "encoding/json"
"fmt"
"strings"
"time"
@@ -40,6 +39,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
+ "sigs.k8s.io/yaml"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
daemonclient "github.com/numaproj/numaflow/pkg/daemon/client"
@@ -51,6 +51,8 @@ import (
const (
finalizerName = dfv1.ControllerPipeline
+
+ pauseTimestampPath = `/metadata/annotations/numaflow.numaproj.io~1pause-timestamp`
)
// pipelineReconciler reconciles a pipeline object.
@@ -85,9 +87,10 @@ func (r *pipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
log.Errorw("Reconcile error", zap.Error(reconcileErr))
}
plCopy.Status.LastUpdated = metav1.Now()
- if needsUpdate(pl, plCopy) {
- // Update with a DeepCopy because .Status will be cleaned up.
- if err := r.client.Update(ctx, plCopy.DeepCopy()); err != nil {
+ if !equality.Semantic.DeepEqual(pl.Finalizers, plCopy.Finalizers) {
+ patchYaml := "metadata:\n finalizers: [" + strings.Join(plCopy.Finalizers, ",") + "]"
+ patchJson, _ := yaml.YAMLToJSON([]byte(patchYaml))
+ if err := r.client.Patch(ctx, pl, client.RawPatch(types.MergePatchType, []byte(patchJson))); err != nil {
return result, err
}
}
@@ -292,7 +295,9 @@ func (r *pipelineReconciler) reconcileFixedResources(ctx context.Context, pl *df
r.recorder.Eventf(pl, corev1.EventTypeNormal, "CreateVertexSuccess", "Created vertex %s successfully", vertexName)
} else {
if oldObj.GetAnnotations()[dfv1.KeyHash] != newObj.GetAnnotations()[dfv1.KeyHash] { // need to update
+ originalReplicas := oldObj.Spec.Replicas
oldObj.Spec = newObj.Spec
+ oldObj.Spec.Replicas = originalReplicas
oldObj.Annotations[dfv1.KeyHash] = newObj.GetAnnotations()[dfv1.KeyHash]
if err := r.client.Update(ctx, &oldObj); err != nil {
r.recorder.Eventf(pl, corev1.EventTypeWarning, "UpdateVertexFailed", "Failed to update vertex: %w", err.Error())
@@ -588,17 +593,6 @@ func (r *pipelineReconciler) cleanUpBuffers(ctx context.Context, pl *dfv1.Pipeli
return nil
}
-func needsUpdate(old, new *dfv1.Pipeline) bool {
- if old == nil {
- return true
- }
- if !equality.Semantic.DeepEqual(old.Finalizers, new.Finalizers) {
- return true
- }
-
- return false
-}
-
func buildVertices(pl *dfv1.Pipeline) map[string]dfv1.Vertex {
result := make(map[string]dfv1.Vertex)
for _, v := range pl.Spec.Vertices {
@@ -814,7 +808,7 @@ func (r *pipelineReconciler) updateDesiredState(ctx context.Context, pl *dfv1.Pi
func (r *pipelineReconciler) resumePipeline(ctx context.Context, pl *dfv1.Pipeline) (bool, error) {
// reset pause timestamp
if pl.GetAnnotations()[dfv1.KeyPauseTimestamp] != "" {
- err := r.client.Patch(ctx, pl, client.RawPatch(types.JSONPatchType, []byte(dfv1.RemovePauseTimestampPatch)))
+ err := r.client.Patch(ctx, pl, client.RawPatch(types.JSONPatchType, []byte(`[{"op": "remove", "path": "`+pauseTimestampPath+`"}]`)))
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil // skip pipeline if it can't be found
@@ -837,13 +831,8 @@ func (r *pipelineReconciler) resumePipeline(ctx context.Context, pl *dfv1.Pipeli
func (r *pipelineReconciler) pausePipeline(ctx context.Context, pl *dfv1.Pipeline) (bool, error) {
// check that annotations / pause timestamp annotation exist
if pl.GetAnnotations() == nil || pl.GetAnnotations()[dfv1.KeyPauseTimestamp] == "" {
- pl.SetAnnotations(map[string]string{dfv1.KeyPauseTimestamp: time.Now().Format(time.RFC3339)})
- body, err := json.Marshal(pl)
- if err != nil {
- return false, err
- }
- err = r.client.Patch(ctx, pl, client.RawPatch(types.MergePatchType, body))
- if err != nil && !apierrors.IsNotFound(err) {
+ patchJson := `[{"op": "add", "path": "` + pauseTimestampPath + `", "value": "` + time.Now().Format(time.RFC3339) + `"}]`
+ if err := r.client.Patch(ctx, pl, client.RawPatch(types.JSONPatchType, []byte(patchJson))); err != nil && !apierrors.IsNotFound(err) {
return true, err
}
}
@@ -924,12 +913,8 @@ func (r *pipelineReconciler) scaleVertex(ctx context.Context, pl *dfv1.Pipeline,
}
}
}
- vertex.Spec.Replicas = ptr.To[int32](scaleTo)
- body, err := json.Marshal(vertex)
- if err != nil {
- return false, err
- }
- err = r.client.Patch(ctx, &vertex, client.RawPatch(types.MergePatchType, body))
+ patchJson := fmt.Sprintf(`{"spec":{"replicas":%d}}`, scaleTo)
+ err = r.client.Patch(ctx, &vertex, client.RawPatch(types.MergePatchType, []byte(patchJson)))
if err != nil && !apierrors.IsNotFound(err) {
return false, err
}
diff --git a/pkg/reconciler/pipeline/controller_test.go b/pkg/reconciler/pipeline/controller_test.go
index e130f49656..aafff27cc3 100644
--- a/pkg/reconciler/pipeline/controller_test.go
+++ b/pkg/reconciler/pipeline/controller_test.go
@@ -37,7 +37,6 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
- "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/reconciler"
@@ -352,8 +351,6 @@ func Test_pauseAndResumePipeline(t *testing.T) {
v, err := r.findExistingVertices(ctx, testObj)
assert.NoError(t, err)
assert.Equal(t, int32(0), *v[testObj.Name+"-"+testObj.Spec.Vertices[0].Name].Spec.Replicas)
- assert.NotNil(t, testObj.Annotations[dfv1.KeyPauseTimestamp])
- testObj.Annotations[dfv1.KeyPauseTimestamp] = ""
_, err = r.resumePipeline(ctx, testObj)
assert.NoError(t, err)
v, err = r.findExistingVertices(ctx, testObj)
@@ -380,8 +377,6 @@ func Test_pauseAndResumePipeline(t *testing.T) {
assert.NoError(t, err)
_, err = r.findExistingVertices(ctx, testObj)
assert.NoError(t, err)
- assert.NotNil(t, testObj.Annotations[dfv1.KeyPauseTimestamp])
- testObj.Annotations[dfv1.KeyPauseTimestamp] = ""
_, err = r.resumePipeline(ctx, testObj)
assert.NoError(t, err)
v, err := r.findExistingVertices(ctx, testObj)
@@ -560,16 +555,6 @@ func Test_buildISBBatchJob(t *testing.T) {
})
}
-func Test_needsUpdate(t *testing.T) {
- testObj := testPipeline.DeepCopy()
- assert.True(t, needsUpdate(nil, testObj))
- assert.False(t, needsUpdate(testPipeline, testObj))
- controllerutil.AddFinalizer(testObj, finalizerName)
- assert.True(t, needsUpdate(testPipeline, testObj))
- testobj1 := testObj.DeepCopy()
- assert.False(t, needsUpdate(testObj, testobj1))
-}
-
func Test_cleanupBuffers(t *testing.T) {
cl := fake.NewClientBuilder().Build()
ctx := context.TODO()
diff --git a/pkg/reconciler/vertex/controller.go b/pkg/reconciler/vertex/controller.go
index 8789b5d89a..20945639ab 100644
--- a/pkg/reconciler/vertex/controller.go
+++ b/pkg/reconciler/vertex/controller.go
@@ -259,6 +259,8 @@ func (r *vertexReconciler) orchestratePods(ctx context.Context, vertex *dfv1.Ver
vertex.Status.CurrentHash = vertex.Status.UpdateHash
} else { // Update scenario
if updatedReplicas >= desiredReplicas {
+ vertex.Status.UpdatedReplicas = uint32(desiredReplicas)
+ vertex.Status.CurrentHash = vertex.Status.UpdateHash
return nil
}
diff --git a/pkg/reconciler/vertex/scaling/scaling.go b/pkg/reconciler/vertex/scaling/scaling.go
index eed5981e89..139e189f5f 100644
--- a/pkg/reconciler/vertex/scaling/scaling.go
+++ b/pkg/reconciler/vertex/scaling/scaling.go
@@ -19,7 +19,6 @@ package scaling
import (
"container/list"
"context"
- "encoding/json"
"fmt"
"math"
"strings"
@@ -30,7 +29,6 @@ import (
"go.uber.org/zap"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
- "k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
@@ -499,12 +497,8 @@ loop:
func (s *Scaler) patchVertexReplicas(ctx context.Context, vertex *dfv1.Vertex, desiredReplicas int32) error {
log := logging.FromContext(ctx)
origin := vertex.Spec.Replicas
- vertex.Spec.Replicas = ptr.To[int32](desiredReplicas)
- body, err := json.Marshal(vertex)
- if err != nil {
- return fmt.Errorf("failed to marshal vertex object to json, %w", err)
- }
- if err := s.client.Patch(ctx, vertex, client.RawPatch(types.MergePatchType, body)); err != nil && !apierrors.IsNotFound(err) {
+ patchJson := fmt.Sprintf(`{"spec":{"replicas":%d}}`, desiredReplicas)
+ if err := s.client.Patch(ctx, vertex, client.RawPatch(types.MergePatchType, []byte(patchJson))); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to patch vertex replicas, %w", err)
}
log.Infow("Auto scaling - vertex replicas changed.", zap.Int32p("from", origin), zap.Int32("to", desiredReplicas), zap.String("namespace", vertex.Namespace), zap.String("pipeline", vertex.Spec.PipelineName), zap.String("vertex", vertex.Spec.Name))
From 692fbeec1b94d8ff66a82b9c3fe5d8242962750b Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Mon, 16 Sep 2024 23:52:54 -0700
Subject: [PATCH 060/188] fix: skip updating phase for resource check (#2065)
Signed-off-by: Sidhant Kohli
---
pkg/reconciler/pipeline/controller.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pkg/reconciler/pipeline/controller.go b/pkg/reconciler/pipeline/controller.go
index b2f99e7b1d..5776c2873c 100644
--- a/pkg/reconciler/pipeline/controller.go
+++ b/pkg/reconciler/pipeline/controller.go
@@ -610,7 +610,7 @@ func buildVertices(pl *dfv1.Pipeline) map[string]dfv1.Vertex {
copyVertexTemplate(pl, vCopy)
copyVertexLimits(pl, vCopy)
replicas := int32(1)
- // If the desired phase is pause or we are in the middle of pausing we should not start any vertex replicas
+ // If the desired phase is paused or we are in the middle of pausing we should not start any vertex replicas
if isLifecycleChange(pl) {
replicas = int32(0)
} else if v.IsReduceUDF() {
@@ -952,7 +952,7 @@ func (r *pipelineReconciler) checkChildrenResourceStatus(ctx context.Context, pi
defer func() {
for _, c := range pipeline.Status.Conditions {
if c.Status != metav1.ConditionTrue {
- pipeline.Status.SetPhase(pipeline.Spec.Lifecycle.GetDesiredPhase(), "Degraded: "+c.Message)
+ pipeline.Status.Message = "Degraded: " + c.Message
return
}
}
From cbe9054f8507639dac3a48b7b8eeb9e236ce706e Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Tue, 17 Sep 2024 10:37:07 -0700
Subject: [PATCH 061/188] doc: example for PVC (#2067)
Signed-off-by: Vigith Maurice
---
.../reference/configuration/volumes.md | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/docs/user-guide/reference/configuration/volumes.md b/docs/user-guide/reference/configuration/volumes.md
index 3e34adb123..4ac3affbf6 100644
--- a/docs/user-guide/reference/configuration/volumes.md
+++ b/docs/user-guide/reference/configuration/volumes.md
@@ -47,3 +47,28 @@ spec:
- mountPath: /path/to/my-sink-config
name: my-udsink-config
```
+
+## PVC Example
+
+Example to show how to attach a Persistent Volume Claim (PVC) to a container.
+
+```yaml
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: Pipeline
+metadata:
+ name: my-pipeline
+spec:
+ vertices:
+ - name: my-source
+ volumes:
+ - name: mypd
+ persistentVolumeClaim:
+ claimName: myclaim
+ source:
+ udsource:
+ container:
+ image: my-source:latest
+ volumeMounts:
+ - mountPath: /path/to/my-source-config
+ name: mypd
+```
\ No newline at end of file
From 9995ff813d39489d22c94e574adae9e6a8a4ebe8 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Thu, 19 Sep 2024 00:01:26 -0700
Subject: [PATCH 062/188] feat: allow customization on readyz and livez config
(#2068)
Signed-off-by: Derek Wang
---
api/json-schema/schema.json | 43 +
api/openapi-spec/swagger.json | 43 +
...w.numaproj.io_interstepbufferservices.yaml | 252 +++
.../numaflow.numaproj.io_monovertices.yaml | 252 +++
.../full/numaflow.numaproj.io_pipelines.yaml | 576 ++++++
.../full/numaflow.numaproj.io_vertices.yaml | 288 +++
config/install.yaml | 1368 +++++++++++++++
config/namespace-install.yaml | 1368 +++++++++++++++
docs/APIs.md | 209 +++
.../configuration/liveness-and-readiness.md | 117 ++
go.mod | 20 +-
go.sum | 40 +-
mkdocs.yml | 1 +
pkg/apis/numaflow/v1alpha1/const.go | 14 +
.../numaflow/v1alpha1/container_template.go | 38 +
.../v1alpha1/container_template_test.go | 146 ++
pkg/apis/numaflow/v1alpha1/generated.pb.go | 1548 +++++++++++------
pkg/apis/numaflow/v1alpha1/generated.proto | 39 +
.../numaflow/v1alpha1/mono_vertex_types.go | 26 +-
.../v1alpha1/mono_vertex_types_test.go | 24 +
.../numaflow/v1alpha1/openapi_generated.go | 73 +-
pkg/apis/numaflow/v1alpha1/probe.go | 74 +
pkg/apis/numaflow/v1alpha1/probe_test.go | 199 +++
pkg/apis/numaflow/v1alpha1/sink.go | 14 +-
pkg/apis/numaflow/v1alpha1/sink_test.go | 22 +
pkg/apis/numaflow/v1alpha1/source.go | 30 +-
pkg/apis/numaflow/v1alpha1/source_test.go | 62 +-
pkg/apis/numaflow/v1alpha1/udf.go | 15 +-
pkg/apis/numaflow/v1alpha1/udf_test.go | 11 +
.../v1alpha1/user_defined_container.go | 4 +
pkg/apis/numaflow/v1alpha1/vertex_types.go | 27 +-
.../numaflow/v1alpha1/vertex_types_test.go | 24 +
pkg/shared/clients/nats/test/server.go | 1 +
pkg/sources/nats/nats_test.go | 4 +-
rust/numaflow-models/src/models/container.rs | 6 +
.../src/models/container_template.rs | 6 +
rust/numaflow-models/src/models/mod.rs | 2 +
rust/numaflow-models/src/models/probe.rs | 54 +
test/e2e/functional_test.go | 6 +-
test/fixtures/util.go | 14 +-
40 files changed, 6463 insertions(+), 597 deletions(-)
create mode 100644 docs/user-guide/reference/configuration/liveness-and-readiness.md
create mode 100644 pkg/apis/numaflow/v1alpha1/probe.go
create mode 100644 pkg/apis/numaflow/v1alpha1/probe_test.go
create mode 100644 rust/numaflow-models/src/models/probe.rs
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index fefd397d8c..24ca429580 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -17939,6 +17939,12 @@
"imagePullPolicy": {
"type": "string"
},
+ "livenessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
+ "readinessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
"resources": {
"$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements"
},
@@ -17972,6 +17978,12 @@
"imagePullPolicy": {
"type": "string"
},
+ "livenessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
+ "readinessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
"resources": {
"$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements"
},
@@ -19739,6 +19751,37 @@
},
"type": "object"
},
+ "io.numaproj.numaflow.v1alpha1.Probe": {
+ "description": "Probe is used to customize the configuration for Readiness and Liveness probes.",
+ "properties": {
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": "integer"
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
"io.numaproj.numaflow.v1alpha1.RedisBufferService": {
"properties": {
"external": {
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index f688e51c8d..e2e29fbabf 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -17944,6 +17944,12 @@
"imagePullPolicy": {
"type": "string"
},
+ "livenessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
+ "readinessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
"resources": {
"$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements"
},
@@ -17977,6 +17983,12 @@
"imagePullPolicy": {
"type": "string"
},
+ "livenessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
+ "readinessProbe": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.Probe"
+ },
"resources": {
"$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements"
},
@@ -19725,6 +19737,37 @@
}
}
},
+ "io.numaproj.numaflow.v1alpha1.Probe": {
+ "description": "Probe is used to customize the configuration for Readiness and Liveness probes.",
+ "type": "object",
+ "properties": {
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "type": "integer",
+ "format": "int32"
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "type": "integer",
+ "format": "int32"
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe.",
+ "type": "integer",
+ "format": "int32"
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "type": "integer",
+ "format": "int32"
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ },
"io.numaproj.numaflow.v1alpha1.RedisBufferService": {
"type": "object",
"properties": {
diff --git a/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml b/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml
index acc540cd10..0fb6ff2583 100644
--- a/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_interstepbufferservices.yaml
@@ -527,6 +527,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -747,6 +783,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -947,6 +1019,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1702,6 +1810,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1891,6 +2035,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2091,6 +2271,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2355,6 +2571,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index 02ae281ebd..4d8a23ba14 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -532,6 +532,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1108,6 +1144,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1315,6 +1387,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3290,6 +3398,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3727,6 +3871,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -4446,6 +4626,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -4657,6 +4873,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
diff --git a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
index 40db1f403e..d262a8664b 100644
--- a/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_pipelines.yaml
@@ -246,6 +246,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1581,6 +1617,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1788,6 +1860,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2491,6 +2599,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3220,6 +3364,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3427,6 +3607,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -4124,6 +4340,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -4331,6 +4583,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -5030,6 +5318,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -5237,6 +5561,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -6210,6 +6570,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -7246,6 +7642,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -7683,6 +8115,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -8402,6 +8870,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -8613,6 +9117,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -8861,6 +9401,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
diff --git a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
index 1f02fd2b35..a1756ec313 100644
--- a/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_vertices.yaml
@@ -532,6 +532,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -816,6 +852,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1797,6 +1869,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2833,6 +2941,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3270,6 +3414,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3989,6 +4169,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -4200,6 +4416,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -4525,6 +4777,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
diff --git a/config/install.yaml b/config/install.yaml
index c3db75767a..c6551b513a 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -526,6 +526,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -746,6 +782,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -946,6 +1018,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1701,6 +1809,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1890,6 +2034,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2090,6 +2270,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2354,6 +2570,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3176,6 +3428,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3752,6 +4040,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3959,6 +4283,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -5934,6 +6294,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -6371,6 +6767,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -7090,6 +7522,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -7301,6 +7769,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -8519,6 +9023,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -9854,6 +10394,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -10061,6 +10637,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -10764,6 +11376,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -11493,6 +12141,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -11700,6 +12384,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -12397,6 +13117,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -12604,6 +13360,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -13303,6 +14095,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -13510,6 +14338,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -14483,6 +15347,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -15519,6 +16419,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -15956,6 +16892,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -16675,6 +17647,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -16886,6 +17894,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -17134,6 +18178,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -18696,6 +19776,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -18980,6 +20096,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -19961,6 +21113,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -20997,6 +22185,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -21434,6 +22658,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -22153,6 +23413,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -22364,6 +23660,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -22689,6 +24021,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 5162dc6ad7..07fc13628d 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -526,6 +526,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -746,6 +782,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -946,6 +1018,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1701,6 +1809,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -1890,6 +2034,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2090,6 +2270,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -2354,6 +2570,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3176,6 +3428,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3752,6 +4040,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -3959,6 +4283,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -5934,6 +6294,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -6371,6 +6767,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -7090,6 +7522,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -7301,6 +7769,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -8519,6 +9023,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -9854,6 +10394,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -10061,6 +10637,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -10764,6 +11376,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -11493,6 +12141,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -11700,6 +12384,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -12397,6 +13117,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -12604,6 +13360,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -13303,6 +14095,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -13510,6 +14338,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -14483,6 +15347,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -15519,6 +16419,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -15956,6 +16892,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -16675,6 +17647,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -16886,6 +17894,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -17134,6 +18178,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -18696,6 +19776,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -18980,6 +20096,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -19961,6 +21113,42 @@ spec:
type: array
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -20997,6 +22185,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -21434,6 +22658,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -22153,6 +23413,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -22364,6 +23660,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
@@ -22689,6 +24021,42 @@ spec:
type: string
imagePullPolicy:
type: string
+ livenessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ readinessProbe:
+ properties:
+ failureThreshold:
+ format: int32
+ type: integer
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
resources:
properties:
claims:
diff --git a/docs/APIs.md b/docs/APIs.md
index d5deaa76b2..7b698883e7 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -1597,6 +1597,36 @@ Kubernetes core/v1.PullPolicy
+
+
+
+
+readinessProbe
+ Probe
+
+
+
+
+(Optional)
+
+
+
+
+
+
+
+
+livenessProbe
+ Probe
+
+
+
+
+(Optional)
+
+
+
+
@@ -1730,6 +1760,36 @@ Kubernetes core/v1.SecurityContext
+
+
+
+
+readinessProbe
+ Probe
+
+
+
+
+(Optional)
+
+
+
+
+
+
+
+
+livenessProbe
+ Probe
+
+
+
+
+(Optional)
+
+
+
+
@@ -8048,6 +8108,155 @@ successfully drained.
+
+
+Probe
+
+
+
+
+(Appears on:
+Container ,
+ContainerTemplate )
+
+
+
+
+
+
+Probe is used to customize the configuration for Readiness and Liveness
+probes.
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+
+
+
+Description
+
+
+
+
+
+
+
+
+
+
+
+
+initialDelaySeconds
int32
+
+
+
+
+(Optional)
+
+
+Number of seconds after the container has started before liveness probes
+are initiated. More info:
+https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+
+
+
+
+
+
+
+
+
+timeoutSeconds
int32
+
+
+
+
+(Optional)
+
+
+Number of seconds after which the probe times out. More info:
+https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+
+
+
+
+
+
+
+
+
+periodSeconds
int32
+
+
+
+
+(Optional)
+
+
+How often (in seconds) to perform the probe.
+
+
+
+
+
+
+
+
+
+
+successThreshold
int32
+
+
+
+
+(Optional)
+
+
+Minimum consecutive successes for the probe to be considered successful
+after having failed. Defaults to 1. Must be 1 for liveness and startup.
+Minimum value is 1.
+
+
+
+
+
+
+
+
+
+
+failureThreshold
int32
+
+
+
+
+(Optional)
+
+
+Minimum consecutive failures for the probe to be considered failed after
+having succeeded. Defaults to 3. Minimum value is 1.
+
+
+
+
+
+
+
+
+
+
RedisBufferService
diff --git a/docs/user-guide/reference/configuration/liveness-and-readiness.md b/docs/user-guide/reference/configuration/liveness-and-readiness.md
new file mode 100644
index 0000000000..f4a3dd5d0f
--- /dev/null
+++ b/docs/user-guide/reference/configuration/liveness-and-readiness.md
@@ -0,0 +1,117 @@
+# Liveness and Readiness
+
+`Liveness` and `Readiness` probes have been pre-configured in the pods orchestrated in Numaflow, including the containers of `Vertex` and `MonoVertex` pods. For these probes, the probe handlers are not allowed to be customized, but the other configurations are.
+
+- `initialDelaySeconds`
+- `timeoutSeconds`
+- `periodSeconds`
+- `successThreshold`
+- `failureThreshold`
+
+Here is an example for `Pipeline` customization, similar configuration can be applied to containers including `udf`, `udsource`, `transformer`, `udsink` and `fb-udsink`.
+
+```yaml
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: Pipeline
+metadata:
+ name: my-pipeline
+spec:
+ vertices:
+ - name: my-source
+ containerTemplate: # For "numa" container
+ readinessProbe:
+ initialDelaySeconds: 30
+ periodSeconds: 60
+ livenessProbe:
+ initialDelaySeconds: 60
+ periodSeconds: 120
+ volumes:
+ - name: my-udsource-config
+ configMap:
+ name: udsource-config
+ source:
+ udsource:
+ container:
+ image: my-source:latest
+ volumeMounts:
+ - mountPath: /path/to/my-source-config
+ name: my-udsource-config
+ # For User-Defined source
+ livenessProbe:
+ initialDelaySeconds: 40
+ failureThreshold: 5
+ - name: my-udf
+ containerTemplate: # For "numa" container
+ readinessProbe:
+ initialDelaySeconds: 20
+ periodSeconds: 60
+ livenessProbe:
+ initialDelaySeconds: 180
+ periodSeconds: 60
+ timeoutSeconds: 50
+ volumes:
+ - name: my-udf-config
+ configMap:
+ name: udf-config
+ udf:
+ container:
+ image: my-function:latest
+ volumeMounts:
+ - mountPath: /path/to/my-function-config
+ name: my-udf-config
+ # For "udf"
+ livenessProbe:
+ initialDelaySeconds: 40
+ failureThreshold: 5
+```
+
+The customization for `numa` container is also available with a [Vertex Template](./pipeline-customization.md#vertices) defined in `spec.templates.vertex`, which is going to be applied to all the vertices of a pipeline.
+
+A `MonoVertex` example is as below.
+
+```yaml
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: MonoVertex
+metadata:
+ name: simple-mono-vertex
+spec:
+ containerTemplate: # For "numa" container
+ readinessProbe:
+ initialDelaySeconds: 20
+ periodSeconds: 60
+ livenessProbe:
+ initialDelaySeconds: 180
+ periodSeconds: 60
+ source:
+ udsource:
+ container:
+ image: quay.io/numaio/numaflow-java/source-simple-source:stable
+ # For User-Defined source
+ livenessProbe:
+ initialDelaySeconds: 40
+ failureThreshold: 5
+ timeoutSeconds: 40
+ transformer:
+ container:
+ image: quay.io/numaio/numaflow-rs/source-transformer-now:stable
+ # For transformer
+ livenessProbe:
+ initialDelaySeconds: 40
+ failureThreshold: 5
+ sink:
+ udsink:
+ container:
+ image: quay.io/numaio/numaflow-java/simple-sink:stable
+ # For User-Defined Sink
+ livenessProbe:
+ initialDelaySeconds: 40
+ failureThreshold: 5
+ fallback:
+ udsink:
+ container:
+ image: my-sink:latest
+ # # For Fallback Sink
+ livenessProbe:
+ initialDelaySeconds: 40
+ failureThreshold: 5
+```
diff --git a/go.mod b/go.mod
index f0dd236bd7..9f54c88017 100644
--- a/go.mod
+++ b/go.mod
@@ -30,8 +30,8 @@ require (
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/imdario/mergo v0.3.16
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe
- github.com/nats-io/nats-server/v2 v2.10.17
- github.com/nats-io/nats.go v1.36.0
+ github.com/nats-io/nats-server/v2 v2.10.20
+ github.com/nats-io/nats.go v1.37.0
github.com/numaproj/numaflow-go v0.8.0
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_model v0.5.0
@@ -48,10 +48,10 @@ require (
go.uber.org/goleak v1.3.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.26.0
- golang.org/x/crypto v0.24.0
+ golang.org/x/crypto v0.26.0
golang.org/x/net v0.25.0
golang.org/x/oauth2 v0.20.0
- golang.org/x/sync v0.7.0
+ golang.org/x/sync v0.8.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
google.golang.org/grpc v1.59.0
@@ -148,7 +148,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
- github.com/minio/highwayhash v1.0.2 // indirect
+ github.com/minio/highwayhash v1.0.3 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -158,7 +158,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
- github.com/nats-io/jwt/v2 v2.5.7 // indirect
+ github.com/nats-io/jwt/v2 v2.5.8 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/oklog/ulid v1.3.1 // indirect
@@ -201,10 +201,10 @@ require (
golang.org/x/arch v0.7.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/mod v0.17.0 // indirect
- golang.org/x/sys v0.21.0 // indirect
- golang.org/x/term v0.21.0 // indirect
- golang.org/x/text v0.16.0 // indirect
- golang.org/x/time v0.5.0 // indirect
+ golang.org/x/sys v0.24.0 // indirect
+ golang.org/x/term v0.23.0 // indirect
+ golang.org/x/text v0.17.0 // indirect
+ golang.org/x/time v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
diff --git a/go.sum b/go.sum
index 1e46c54600..77784a6a8d 100644
--- a/go.sum
+++ b/go.sum
@@ -446,8 +446,8 @@ github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
-github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
-github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
+github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
+github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@@ -474,12 +474,12 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c=
-github.com/nats-io/jwt/v2 v2.5.7/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
-github.com/nats-io/nats-server/v2 v2.10.17 h1:PTVObNBD3TZSNUDgzFb1qQsQX4mOgFmOuG9vhT+KBUY=
-github.com/nats-io/nats-server/v2 v2.10.17/go.mod h1:5OUyc4zg42s/p2i92zbbqXvUNsbF0ivdTLKshVMn2YQ=
-github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
-github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
+github.com/nats-io/jwt/v2 v2.5.8 h1:uvdSzwWiEGWGXf+0Q+70qv6AQdvcvxrv9hPM0RiPamE=
+github.com/nats-io/jwt/v2 v2.5.8/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
+github.com/nats-io/nats-server/v2 v2.10.20 h1:CXDTYNHeBiAKBTAIP2gjpgbWap2GhATnTLgP8etyvEI=
+github.com/nats-io/nats-server/v2 v2.10.20/go.mod h1:hgcPnoUtMfxz1qVOvLZGurVypQ+Cg6GXVXjG53iHk+M=
+github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
+github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
@@ -687,8 +687,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
-golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
+golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -802,11 +802,10 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -863,15 +862,16 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
+golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
-golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
+golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
+golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -884,14 +884,14 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
+golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
-golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
+golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
diff --git a/mkdocs.yml b/mkdocs.yml
index 55dac8b1c1..1118c1ff43 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -101,6 +101,7 @@ nav:
- user-guide/reference/configuration/labels-and-annotations.md
- user-guide/reference/configuration/init-containers.md
- user-guide/reference/configuration/sidecar-containers.md
+ - user-guide/reference/configuration/liveness-and-readiness.md
- user-guide/reference/configuration/pipeline-customization.md
- user-guide/reference/configuration/istio.md
- user-guide/reference/configuration/max-message-size.md
diff --git a/pkg/apis/numaflow/v1alpha1/const.go b/pkg/apis/numaflow/v1alpha1/const.go
index e36ec9bd34..f65e2a5bd7 100644
--- a/pkg/apis/numaflow/v1alpha1/const.go
+++ b/pkg/apis/numaflow/v1alpha1/const.go
@@ -265,6 +265,20 @@ const (
// This strategy argues for robustness in operations, aiming
// to minimize the chances of data loss or failed deliveries in transient failure scenarios.
DefaultOnFailureRetryStrategy = OnFailureRetry
+
+ // Defeault values for readiness and liveness probes
+ NumaContainerReadyzInitialDelaySeconds = 5
+ NumaContainerReadyzPeriodSeconds = 10
+ NumaContainerReadyzTimeoutSeconds = 30
+ NumaContainerReadyzFailureThreshold = 6
+ NumaContainerLivezInitialDelaySeconds = 20
+ NumaContainerLivezPeriodSeconds = 60
+ NumaContainerLivezTimeoutSeconds = 30
+ NumaContainerLivezFailureThreshold = 5
+ UDContainerLivezInitialDelaySeconds = 30
+ UDContainerLivezPeriodSeconds = 60
+ UDContainerLivezTimeoutSeconds = 30
+ UDContainerLivezFailureThreshold = 5
)
var (
diff --git a/pkg/apis/numaflow/v1alpha1/container_template.go b/pkg/apis/numaflow/v1alpha1/container_template.go
index 071229114c..5b3ad3270c 100644
--- a/pkg/apis/numaflow/v1alpha1/container_template.go
+++ b/pkg/apis/numaflow/v1alpha1/container_template.go
@@ -33,6 +33,10 @@ type ContainerTemplate struct {
Env []corev1.EnvVar `json:"env,omitempty" protobuf:"bytes,4,rep,name=env"`
// +optional
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty" protobuf:"bytes,5,rep,name=envFrom"`
+ // +optional
+ ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,6,opt,name=readinessProbe"`
+ // +optional
+ LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,7,opt,name=livenessProbe"`
}
// ApplyToContainer updates the Container with the values from the ContainerTemplate
@@ -48,6 +52,40 @@ func (ct *ContainerTemplate) ApplyToContainer(c *corev1.Container) {
if len(ct.EnvFrom) > 0 {
c.EnvFrom = append(c.EnvFrom, ct.EnvFrom...)
}
+ if rp := ct.ReadinessProbe; rp != nil && c.ReadinessProbe != nil {
+ if rp.InitialDelaySeconds != nil {
+ c.ReadinessProbe.InitialDelaySeconds = *rp.InitialDelaySeconds
+ }
+ if rp.TimeoutSeconds != nil {
+ c.ReadinessProbe.TimeoutSeconds = *rp.TimeoutSeconds
+ }
+ if rp.PeriodSeconds != nil {
+ c.ReadinessProbe.PeriodSeconds = *rp.PeriodSeconds
+ }
+ if rp.FailureThreshold != nil {
+ c.ReadinessProbe.FailureThreshold = *rp.FailureThreshold
+ }
+ if rp.SuccessThreshold != nil {
+ c.ReadinessProbe.SuccessThreshold = *rp.SuccessThreshold
+ }
+ }
+ if lp := ct.LivenessProbe; lp != nil && c.LivenessProbe != nil {
+ if lp.InitialDelaySeconds != nil {
+ c.LivenessProbe.InitialDelaySeconds = *lp.InitialDelaySeconds
+ }
+ if lp.TimeoutSeconds != nil {
+ c.LivenessProbe.TimeoutSeconds = *lp.TimeoutSeconds
+ }
+ if lp.PeriodSeconds != nil {
+ c.LivenessProbe.PeriodSeconds = *lp.PeriodSeconds
+ }
+ if lp.FailureThreshold != nil {
+ c.LivenessProbe.FailureThreshold = *lp.FailureThreshold
+ }
+ if lp.SuccessThreshold != nil {
+ c.LivenessProbe.SuccessThreshold = *lp.SuccessThreshold
+ }
+ }
}
// ApplyToNumaflowContainers updates any numa or init containers with the values from the ContainerTemplate
diff --git a/pkg/apis/numaflow/v1alpha1/container_template_test.go b/pkg/apis/numaflow/v1alpha1/container_template_test.go
index a1e8b8f479..5b835cda72 100644
--- a/pkg/apis/numaflow/v1alpha1/container_template_test.go
+++ b/pkg/apis/numaflow/v1alpha1/container_template_test.go
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
+ "k8s.io/utils/ptr"
)
var (
@@ -81,3 +82,148 @@ func Test_ApplyToNumaflowContainers(t *testing.T) {
assert.Equal(t, testContainerTemplate.Resources, cs[0].Resources)
assert.NotEqual(t, testContainerTemplate.Resources, cs[1].Resources)
}
+
+func TestApplyProbes(t *testing.T) {
+ tests := []struct {
+ name string
+ template *ContainerTemplate
+ input *corev1.Container
+ expected *corev1.Container
+ }{
+ {
+ name: "Apply ReadinessProbe",
+ template: &ContainerTemplate{
+ ReadinessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](5),
+ TimeoutSeconds: ptr.To[int32](10),
+ PeriodSeconds: ptr.To[int32](15),
+ FailureThreshold: ptr.To[int32](3),
+ SuccessThreshold: ptr.To[int32](1),
+ },
+ },
+ input: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{},
+ },
+ expected: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{
+ InitialDelaySeconds: 5,
+ TimeoutSeconds: 10,
+ PeriodSeconds: 15,
+ FailureThreshold: 3,
+ SuccessThreshold: 1,
+ },
+ },
+ },
+ {
+ name: "Apply LivenessProbe",
+ template: &ContainerTemplate{
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](10),
+ TimeoutSeconds: ptr.To[int32](5),
+ PeriodSeconds: ptr.To[int32](20),
+ FailureThreshold: ptr.To[int32](5),
+ SuccessThreshold: ptr.To[int32](1),
+ },
+ },
+ input: &corev1.Container{
+ LivenessProbe: &corev1.Probe{},
+ },
+ expected: &corev1.Container{
+ LivenessProbe: &corev1.Probe{
+ InitialDelaySeconds: 10,
+ TimeoutSeconds: 5,
+ PeriodSeconds: 20,
+ FailureThreshold: 5,
+ SuccessThreshold: 1,
+ },
+ },
+ },
+ {
+ name: "Apply Both Probes",
+ template: &ContainerTemplate{
+ ReadinessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](5),
+ TimeoutSeconds: ptr.To[int32](10),
+ },
+ LivenessProbe: &Probe{
+ PeriodSeconds: ptr.To[int32](20),
+ FailureThreshold: ptr.To[int32](5),
+ },
+ },
+ input: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{},
+ LivenessProbe: &corev1.Probe{},
+ },
+ expected: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{
+ InitialDelaySeconds: 5,
+ TimeoutSeconds: 10,
+ },
+ LivenessProbe: &corev1.Probe{
+ PeriodSeconds: 20,
+ FailureThreshold: 5,
+ },
+ },
+ },
+ {
+ name: "No Probes in Template",
+ template: &ContainerTemplate{},
+ input: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{InitialDelaySeconds: 30},
+ LivenessProbe: &corev1.Probe{TimeoutSeconds: 15},
+ },
+ expected: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{InitialDelaySeconds: 30},
+ LivenessProbe: &corev1.Probe{TimeoutSeconds: 15},
+ },
+ },
+ {
+ name: "No Probes in Container",
+ template: &ContainerTemplate{
+ ReadinessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](5),
+ TimeoutSeconds: ptr.To[int32](10),
+ },
+ LivenessProbe: &Probe{
+ PeriodSeconds: ptr.To[int32](20),
+ FailureThreshold: ptr.To[int32](5),
+ },
+ },
+ input: &corev1.Container{},
+ expected: &corev1.Container{},
+ },
+ {
+ name: "Partial Probe Updates",
+ template: &ContainerTemplate{
+ ReadinessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](25),
+ },
+ LivenessProbe: &Probe{
+ FailureThreshold: ptr.To[int32](4),
+ },
+ },
+ input: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{TimeoutSeconds: 5},
+ LivenessProbe: &corev1.Probe{PeriodSeconds: 10},
+ },
+ expected: &corev1.Container{
+ ReadinessProbe: &corev1.Probe{
+ InitialDelaySeconds: 25,
+ TimeoutSeconds: 5,
+ },
+ LivenessProbe: &corev1.Probe{
+ PeriodSeconds: 10,
+ FailureThreshold: 4,
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tt.template.ApplyToContainer(tt.input)
+ assert.Equal(t, tt.expected.ReadinessProbe, tt.input.ReadinessProbe)
+ assert.Equal(t, tt.expected.LivenessProbe, tt.input.LivenessProbe)
+ })
+ }
+}
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index 65ac7601e5..138962c4c2 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -1702,10 +1702,38 @@ func (m *PipelineStatus) XXX_DiscardUnknown() {
var xxx_messageInfo_PipelineStatus proto.InternalMessageInfo
+func (m *Probe) Reset() { *m = Probe{} }
+func (*Probe) ProtoMessage() {}
+func (*Probe) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0d1b17d3865563, []int{59}
+}
+func (m *Probe) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *Probe) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+}
+func (m *Probe) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Probe.Merge(m, src)
+}
+func (m *Probe) XXX_Size() int {
+ return m.Size()
+}
+func (m *Probe) XXX_DiscardUnknown() {
+ xxx_messageInfo_Probe.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Probe proto.InternalMessageInfo
+
func (m *RedisBufferService) Reset() { *m = RedisBufferService{} }
func (*RedisBufferService) ProtoMessage() {}
func (*RedisBufferService) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{59}
+ return fileDescriptor_9d0d1b17d3865563, []int{60}
}
func (m *RedisBufferService) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1733,7 +1761,7 @@ var xxx_messageInfo_RedisBufferService proto.InternalMessageInfo
func (m *RedisConfig) Reset() { *m = RedisConfig{} }
func (*RedisConfig) ProtoMessage() {}
func (*RedisConfig) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{60}
+ return fileDescriptor_9d0d1b17d3865563, []int{61}
}
func (m *RedisConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1761,7 +1789,7 @@ var xxx_messageInfo_RedisConfig proto.InternalMessageInfo
func (m *RedisSettings) Reset() { *m = RedisSettings{} }
func (*RedisSettings) ProtoMessage() {}
func (*RedisSettings) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{61}
+ return fileDescriptor_9d0d1b17d3865563, []int{62}
}
func (m *RedisSettings) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1789,7 +1817,7 @@ var xxx_messageInfo_RedisSettings proto.InternalMessageInfo
func (m *RetryStrategy) Reset() { *m = RetryStrategy{} }
func (*RetryStrategy) ProtoMessage() {}
func (*RetryStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{62}
+ return fileDescriptor_9d0d1b17d3865563, []int{63}
}
func (m *RetryStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1817,7 +1845,7 @@ var xxx_messageInfo_RetryStrategy proto.InternalMessageInfo
func (m *RollingUpdateStrategy) Reset() { *m = RollingUpdateStrategy{} }
func (*RollingUpdateStrategy) ProtoMessage() {}
func (*RollingUpdateStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{63}
+ return fileDescriptor_9d0d1b17d3865563, []int{64}
}
func (m *RollingUpdateStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1845,7 +1873,7 @@ var xxx_messageInfo_RollingUpdateStrategy proto.InternalMessageInfo
func (m *SASL) Reset() { *m = SASL{} }
func (*SASL) ProtoMessage() {}
func (*SASL) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{64}
+ return fileDescriptor_9d0d1b17d3865563, []int{65}
}
func (m *SASL) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1873,7 +1901,7 @@ var xxx_messageInfo_SASL proto.InternalMessageInfo
func (m *SASLPlain) Reset() { *m = SASLPlain{} }
func (*SASLPlain) ProtoMessage() {}
func (*SASLPlain) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{65}
+ return fileDescriptor_9d0d1b17d3865563, []int{66}
}
func (m *SASLPlain) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1901,7 +1929,7 @@ var xxx_messageInfo_SASLPlain proto.InternalMessageInfo
func (m *Scale) Reset() { *m = Scale{} }
func (*Scale) ProtoMessage() {}
func (*Scale) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{66}
+ return fileDescriptor_9d0d1b17d3865563, []int{67}
}
func (m *Scale) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1929,7 +1957,7 @@ var xxx_messageInfo_Scale proto.InternalMessageInfo
func (m *ServingSource) Reset() { *m = ServingSource{} }
func (*ServingSource) ProtoMessage() {}
func (*ServingSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{67}
+ return fileDescriptor_9d0d1b17d3865563, []int{68}
}
func (m *ServingSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1957,7 +1985,7 @@ var xxx_messageInfo_ServingSource proto.InternalMessageInfo
func (m *ServingStore) Reset() { *m = ServingStore{} }
func (*ServingStore) ProtoMessage() {}
func (*ServingStore) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{68}
+ return fileDescriptor_9d0d1b17d3865563, []int{69}
}
func (m *ServingStore) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1985,7 +2013,7 @@ var xxx_messageInfo_ServingStore proto.InternalMessageInfo
func (m *SessionWindow) Reset() { *m = SessionWindow{} }
func (*SessionWindow) ProtoMessage() {}
func (*SessionWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{69}
+ return fileDescriptor_9d0d1b17d3865563, []int{70}
}
func (m *SessionWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2013,7 +2041,7 @@ var xxx_messageInfo_SessionWindow proto.InternalMessageInfo
func (m *SideInput) Reset() { *m = SideInput{} }
func (*SideInput) ProtoMessage() {}
func (*SideInput) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{70}
+ return fileDescriptor_9d0d1b17d3865563, []int{71}
}
func (m *SideInput) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2041,7 +2069,7 @@ var xxx_messageInfo_SideInput proto.InternalMessageInfo
func (m *SideInputTrigger) Reset() { *m = SideInputTrigger{} }
func (*SideInputTrigger) ProtoMessage() {}
func (*SideInputTrigger) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{71}
+ return fileDescriptor_9d0d1b17d3865563, []int{72}
}
func (m *SideInputTrigger) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2069,7 +2097,7 @@ var xxx_messageInfo_SideInputTrigger proto.InternalMessageInfo
func (m *SideInputsManagerTemplate) Reset() { *m = SideInputsManagerTemplate{} }
func (*SideInputsManagerTemplate) ProtoMessage() {}
func (*SideInputsManagerTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{72}
+ return fileDescriptor_9d0d1b17d3865563, []int{73}
}
func (m *SideInputsManagerTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2097,7 +2125,7 @@ var xxx_messageInfo_SideInputsManagerTemplate proto.InternalMessageInfo
func (m *Sink) Reset() { *m = Sink{} }
func (*Sink) ProtoMessage() {}
func (*Sink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{73}
+ return fileDescriptor_9d0d1b17d3865563, []int{74}
}
func (m *Sink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2125,7 +2153,7 @@ var xxx_messageInfo_Sink proto.InternalMessageInfo
func (m *SlidingWindow) Reset() { *m = SlidingWindow{} }
func (*SlidingWindow) ProtoMessage() {}
func (*SlidingWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{74}
+ return fileDescriptor_9d0d1b17d3865563, []int{75}
}
func (m *SlidingWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2153,7 +2181,7 @@ var xxx_messageInfo_SlidingWindow proto.InternalMessageInfo
func (m *Source) Reset() { *m = Source{} }
func (*Source) ProtoMessage() {}
func (*Source) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{75}
+ return fileDescriptor_9d0d1b17d3865563, []int{76}
}
func (m *Source) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2181,7 +2209,7 @@ var xxx_messageInfo_Source proto.InternalMessageInfo
func (m *Status) Reset() { *m = Status{} }
func (*Status) ProtoMessage() {}
func (*Status) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{76}
+ return fileDescriptor_9d0d1b17d3865563, []int{77}
}
func (m *Status) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2209,7 +2237,7 @@ var xxx_messageInfo_Status proto.InternalMessageInfo
func (m *TLS) Reset() { *m = TLS{} }
func (*TLS) ProtoMessage() {}
func (*TLS) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{77}
+ return fileDescriptor_9d0d1b17d3865563, []int{78}
}
func (m *TLS) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2237,7 +2265,7 @@ var xxx_messageInfo_TLS proto.InternalMessageInfo
func (m *TagConditions) Reset() { *m = TagConditions{} }
func (*TagConditions) ProtoMessage() {}
func (*TagConditions) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{78}
+ return fileDescriptor_9d0d1b17d3865563, []int{79}
}
func (m *TagConditions) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2265,7 +2293,7 @@ var xxx_messageInfo_TagConditions proto.InternalMessageInfo
func (m *Templates) Reset() { *m = Templates{} }
func (*Templates) ProtoMessage() {}
func (*Templates) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{79}
+ return fileDescriptor_9d0d1b17d3865563, []int{80}
}
func (m *Templates) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2293,7 +2321,7 @@ var xxx_messageInfo_Templates proto.InternalMessageInfo
func (m *Transformer) Reset() { *m = Transformer{} }
func (*Transformer) ProtoMessage() {}
func (*Transformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{80}
+ return fileDescriptor_9d0d1b17d3865563, []int{81}
}
func (m *Transformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2321,7 +2349,7 @@ var xxx_messageInfo_Transformer proto.InternalMessageInfo
func (m *UDF) Reset() { *m = UDF{} }
func (*UDF) ProtoMessage() {}
func (*UDF) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{81}
+ return fileDescriptor_9d0d1b17d3865563, []int{82}
}
func (m *UDF) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2349,7 +2377,7 @@ var xxx_messageInfo_UDF proto.InternalMessageInfo
func (m *UDSink) Reset() { *m = UDSink{} }
func (*UDSink) ProtoMessage() {}
func (*UDSink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{82}
+ return fileDescriptor_9d0d1b17d3865563, []int{83}
}
func (m *UDSink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2377,7 +2405,7 @@ var xxx_messageInfo_UDSink proto.InternalMessageInfo
func (m *UDSource) Reset() { *m = UDSource{} }
func (*UDSource) ProtoMessage() {}
func (*UDSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{83}
+ return fileDescriptor_9d0d1b17d3865563, []int{84}
}
func (m *UDSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2405,7 +2433,7 @@ var xxx_messageInfo_UDSource proto.InternalMessageInfo
func (m *UDTransformer) Reset() { *m = UDTransformer{} }
func (*UDTransformer) ProtoMessage() {}
func (*UDTransformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{84}
+ return fileDescriptor_9d0d1b17d3865563, []int{85}
}
func (m *UDTransformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2433,7 +2461,7 @@ var xxx_messageInfo_UDTransformer proto.InternalMessageInfo
func (m *UpdateStrategy) Reset() { *m = UpdateStrategy{} }
func (*UpdateStrategy) ProtoMessage() {}
func (*UpdateStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{85}
+ return fileDescriptor_9d0d1b17d3865563, []int{86}
}
func (m *UpdateStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2461,7 +2489,7 @@ var xxx_messageInfo_UpdateStrategy proto.InternalMessageInfo
func (m *Vertex) Reset() { *m = Vertex{} }
func (*Vertex) ProtoMessage() {}
func (*Vertex) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{86}
+ return fileDescriptor_9d0d1b17d3865563, []int{87}
}
func (m *Vertex) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2489,7 +2517,7 @@ var xxx_messageInfo_Vertex proto.InternalMessageInfo
func (m *VertexInstance) Reset() { *m = VertexInstance{} }
func (*VertexInstance) ProtoMessage() {}
func (*VertexInstance) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{87}
+ return fileDescriptor_9d0d1b17d3865563, []int{88}
}
func (m *VertexInstance) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2517,7 +2545,7 @@ var xxx_messageInfo_VertexInstance proto.InternalMessageInfo
func (m *VertexLimits) Reset() { *m = VertexLimits{} }
func (*VertexLimits) ProtoMessage() {}
func (*VertexLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{88}
+ return fileDescriptor_9d0d1b17d3865563, []int{89}
}
func (m *VertexLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2545,7 +2573,7 @@ var xxx_messageInfo_VertexLimits proto.InternalMessageInfo
func (m *VertexList) Reset() { *m = VertexList{} }
func (*VertexList) ProtoMessage() {}
func (*VertexList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{89}
+ return fileDescriptor_9d0d1b17d3865563, []int{90}
}
func (m *VertexList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2573,7 +2601,7 @@ var xxx_messageInfo_VertexList proto.InternalMessageInfo
func (m *VertexSpec) Reset() { *m = VertexSpec{} }
func (*VertexSpec) ProtoMessage() {}
func (*VertexSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{90}
+ return fileDescriptor_9d0d1b17d3865563, []int{91}
}
func (m *VertexSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2601,7 +2629,7 @@ var xxx_messageInfo_VertexSpec proto.InternalMessageInfo
func (m *VertexStatus) Reset() { *m = VertexStatus{} }
func (*VertexStatus) ProtoMessage() {}
func (*VertexStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{91}
+ return fileDescriptor_9d0d1b17d3865563, []int{92}
}
func (m *VertexStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2629,7 +2657,7 @@ var xxx_messageInfo_VertexStatus proto.InternalMessageInfo
func (m *VertexTemplate) Reset() { *m = VertexTemplate{} }
func (*VertexTemplate) ProtoMessage() {}
func (*VertexTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{92}
+ return fileDescriptor_9d0d1b17d3865563, []int{93}
}
func (m *VertexTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2657,7 +2685,7 @@ var xxx_messageInfo_VertexTemplate proto.InternalMessageInfo
func (m *Watermark) Reset() { *m = Watermark{} }
func (*Watermark) ProtoMessage() {}
func (*Watermark) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{93}
+ return fileDescriptor_9d0d1b17d3865563, []int{94}
}
func (m *Watermark) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2685,7 +2713,7 @@ var xxx_messageInfo_Watermark proto.InternalMessageInfo
func (m *Window) Reset() { *m = Window{} }
func (*Window) ProtoMessage() {}
func (*Window) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{94}
+ return fileDescriptor_9d0d1b17d3865563, []int{95}
}
func (m *Window) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2778,6 +2806,7 @@ func init() {
proto.RegisterType((*PipelineList)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.PipelineList")
proto.RegisterType((*PipelineSpec)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.PipelineSpec")
proto.RegisterType((*PipelineStatus)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.PipelineStatus")
+ proto.RegisterType((*Probe)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Probe")
proto.RegisterType((*RedisBufferService)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisBufferService")
proto.RegisterType((*RedisConfig)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisConfig")
proto.RegisterType((*RedisSettings)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.RedisSettings")
@@ -2822,501 +2851,510 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 7889 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6b, 0x6c, 0x25, 0xd9,
- 0xb5, 0xd6, 0x9c, 0x97, 0xcf, 0x39, 0xeb, 0xf8, 0xd5, 0xbb, 0x1f, 0xe3, 0xee, 0xe9, 0x69, 0xf7,
- 0xad, 0xb9, 0x33, 0xb7, 0x2f, 0xf7, 0x5e, 0x9b, 0xf1, 0x9d, 0x57, 0xee, 0xbd, 0xc9, 0x8c, 0x8f,
- 0xdd, 0x76, 0xbb, 0xdb, 0xee, 0x76, 0xd6, 0xb1, 0x7b, 0x26, 0x77, 0x48, 0x86, 0x72, 0xd5, 0xf6,
- 0x71, 0x8d, 0xeb, 0x54, 0x9d, 0xa9, 0xaa, 0xe3, 0x6e, 0x4f, 0x40, 0x79, 0x0c, 0x68, 0x06, 0x01,
- 0x02, 0xe5, 0x57, 0x24, 0x14, 0x10, 0x08, 0x29, 0x3f, 0xa2, 0xf0, 0x03, 0x29, 0xfc, 0x40, 0x82,
- 0x10, 0x84, 0x20, 0x20, 0x1e, 0x11, 0x42, 0x62, 0xf8, 0x63, 0x11, 0x23, 0x7e, 0x80, 0x04, 0x8a,
- 0x88, 0x20, 0xa1, 0x15, 0x11, 0xb4, 0x5f, 0xf5, 0x3a, 0x75, 0xba, 0xed, 0x53, 0x76, 0x4f, 0x0f,
- 0xcc, 0xbf, 0xaa, 0xbd, 0xd6, 0xfe, 0xd6, 0xae, 0x5d, 0xbb, 0xf6, 0x5e, 0x7b, 0xad, 0xb5, 0x57,
- 0xc1, 0x72, 0xdb, 0x0a, 0x76, 0x7a, 0x5b, 0x33, 0x86, 0xdb, 0x99, 0x75, 0x7a, 0x1d, 0xbd, 0xeb,
- 0xb9, 0xef, 0xf2, 0x8b, 0x6d, 0xdb, 0xbd, 0x37, 0xdb, 0xdd, 0x6d, 0xcf, 0xea, 0x5d, 0xcb, 0x8f,
- 0x4a, 0xf6, 0x5e, 0xd4, 0xed, 0xee, 0x8e, 0xfe, 0xe2, 0x6c, 0x9b, 0x3a, 0xd4, 0xd3, 0x03, 0x6a,
- 0xce, 0x74, 0x3d, 0x37, 0x70, 0xc9, 0xab, 0x11, 0xd0, 0x8c, 0x02, 0x9a, 0x51, 0xd5, 0x66, 0xba,
- 0xbb, 0xed, 0x19, 0x06, 0x14, 0x95, 0x28, 0xa0, 0x4b, 0xbf, 0x17, 0x6b, 0x41, 0xdb, 0x6d, 0xbb,
- 0xb3, 0x1c, 0x6f, 0xab, 0xb7, 0xcd, 0xef, 0xf8, 0x0d, 0xbf, 0x12, 0x72, 0x2e, 0x69, 0xbb, 0xaf,
- 0xf9, 0x33, 0x96, 0xcb, 0x9a, 0x35, 0x6b, 0xb8, 0x1e, 0x9d, 0xdd, 0xeb, 0x6b, 0xcb, 0xa5, 0x97,
- 0x22, 0x9e, 0x8e, 0x6e, 0xec, 0x58, 0x0e, 0xf5, 0xf6, 0xd5, 0xb3, 0xcc, 0x7a, 0xd4, 0x77, 0x7b,
- 0x9e, 0x41, 0x8f, 0x55, 0xcb, 0x9f, 0xed, 0xd0, 0x40, 0xcf, 0x92, 0x35, 0x3b, 0xa8, 0x96, 0xd7,
- 0x73, 0x02, 0xab, 0xd3, 0x2f, 0xe6, 0x95, 0x47, 0x55, 0xf0, 0x8d, 0x1d, 0xda, 0xd1, 0xfb, 0xea,
- 0xfd, 0xfe, 0xa0, 0x7a, 0xbd, 0xc0, 0xb2, 0x67, 0x2d, 0x27, 0xf0, 0x03, 0x2f, 0x5d, 0x49, 0xfb,
- 0x11, 0xc0, 0xd9, 0xf9, 0x2d, 0x3f, 0xf0, 0x74, 0x23, 0x58, 0x77, 0xcd, 0x0d, 0xda, 0xe9, 0xda,
- 0x7a, 0x40, 0xc9, 0x2e, 0xd4, 0xd8, 0x03, 0x99, 0x7a, 0xa0, 0x4f, 0x15, 0xae, 0x16, 0xae, 0x35,
- 0xe6, 0xe6, 0x67, 0x86, 0x7c, 0x81, 0x33, 0x6b, 0x12, 0xa8, 0x39, 0x7a, 0x78, 0x30, 0x5d, 0x53,
- 0x77, 0x18, 0x0a, 0x20, 0xdf, 0x2e, 0xc0, 0xa8, 0xe3, 0x9a, 0xb4, 0x45, 0x6d, 0x6a, 0x04, 0xae,
- 0x37, 0x55, 0xbc, 0x5a, 0xba, 0xd6, 0x98, 0xfb, 0xca, 0xd0, 0x12, 0x33, 0x9e, 0x68, 0xe6, 0x76,
- 0x4c, 0xc0, 0x75, 0x27, 0xf0, 0xf6, 0x9b, 0xe7, 0x7e, 0x7c, 0x30, 0xfd, 0xd4, 0xe1, 0xc1, 0xf4,
- 0x68, 0x9c, 0x84, 0x89, 0x96, 0x90, 0x4d, 0x68, 0x04, 0xae, 0xcd, 0xba, 0xcc, 0x72, 0x1d, 0x7f,
- 0xaa, 0xc4, 0x1b, 0x76, 0x65, 0x46, 0x74, 0x35, 0x13, 0x3f, 0xc3, 0xc6, 0xd8, 0xcc, 0xde, 0x8b,
- 0x33, 0x1b, 0x21, 0x5b, 0xf3, 0xac, 0x04, 0x6e, 0x44, 0x65, 0x3e, 0xc6, 0x71, 0x08, 0x85, 0x09,
- 0x9f, 0x1a, 0x3d, 0xcf, 0x0a, 0xf6, 0x17, 0x5c, 0x27, 0xa0, 0xf7, 0x83, 0xa9, 0x32, 0xef, 0xe5,
- 0x17, 0xb2, 0xa0, 0xd7, 0x5d, 0xb3, 0x95, 0xe4, 0x6e, 0x9e, 0x3d, 0x3c, 0x98, 0x9e, 0x48, 0x15,
- 0x62, 0x1a, 0x93, 0x38, 0x30, 0x69, 0x75, 0xf4, 0x36, 0x5d, 0xef, 0xd9, 0x76, 0x8b, 0x1a, 0x1e,
- 0x0d, 0xfc, 0xa9, 0x0a, 0x7f, 0x84, 0x6b, 0x59, 0x72, 0x56, 0x5d, 0x43, 0xb7, 0xef, 0x6c, 0xbd,
- 0x4b, 0x8d, 0x00, 0xe9, 0x36, 0xf5, 0xa8, 0x63, 0xd0, 0xe6, 0x94, 0x7c, 0x98, 0xc9, 0x95, 0x14,
- 0x12, 0xf6, 0x61, 0x93, 0x65, 0x38, 0xd3, 0xf5, 0x2c, 0x97, 0x37, 0xc1, 0xd6, 0x7d, 0xff, 0xb6,
- 0xde, 0xa1, 0x53, 0x23, 0x57, 0x0b, 0xd7, 0xea, 0xcd, 0x8b, 0x12, 0xe6, 0xcc, 0x7a, 0x9a, 0x01,
- 0xfb, 0xeb, 0x90, 0x6b, 0x50, 0x53, 0x85, 0x53, 0xd5, 0xab, 0x85, 0x6b, 0x15, 0x31, 0x76, 0x54,
- 0x5d, 0x0c, 0xa9, 0x64, 0x09, 0x6a, 0xfa, 0xf6, 0xb6, 0xe5, 0x30, 0xce, 0x1a, 0xef, 0xc2, 0xcb,
- 0x59, 0x8f, 0x36, 0x2f, 0x79, 0x04, 0x8e, 0xba, 0xc3, 0xb0, 0x2e, 0xb9, 0x09, 0xc4, 0xa7, 0xde,
- 0x9e, 0x65, 0xd0, 0x79, 0xc3, 0x70, 0x7b, 0x4e, 0xc0, 0xdb, 0x5e, 0xe7, 0x6d, 0xbf, 0x24, 0xdb,
- 0x4e, 0x5a, 0x7d, 0x1c, 0x98, 0x51, 0x8b, 0xbc, 0x01, 0x93, 0xf2, 0x5b, 0x8d, 0x7a, 0x01, 0x38,
- 0xd2, 0x39, 0xd6, 0x91, 0x98, 0xa2, 0x61, 0x1f, 0x37, 0x31, 0xe1, 0xb2, 0xde, 0x0b, 0xdc, 0x0e,
- 0x83, 0x4c, 0x0a, 0xdd, 0x70, 0x77, 0xa9, 0x33, 0xd5, 0xb8, 0x5a, 0xb8, 0x56, 0x6b, 0x5e, 0x3d,
- 0x3c, 0x98, 0xbe, 0x3c, 0xff, 0x10, 0x3e, 0x7c, 0x28, 0x0a, 0xb9, 0x03, 0x75, 0xd3, 0xf1, 0xd7,
- 0x5d, 0xdb, 0x32, 0xf6, 0xa7, 0x46, 0x79, 0x03, 0x5f, 0x94, 0x8f, 0x5a, 0x5f, 0xbc, 0xdd, 0x12,
- 0x84, 0x07, 0x07, 0xd3, 0x97, 0xfb, 0xa7, 0xd4, 0x99, 0x90, 0x8e, 0x11, 0x06, 0x59, 0xe3, 0x80,
- 0x0b, 0xae, 0xb3, 0x6d, 0xb5, 0xa7, 0xc6, 0xf8, 0xdb, 0xb8, 0x3a, 0x60, 0x40, 0x2f, 0xde, 0x6e,
- 0x09, 0xbe, 0xe6, 0x98, 0x14, 0x27, 0x6e, 0x31, 0x42, 0x20, 0x26, 0x8c, 0xab, 0xc9, 0x78, 0xc1,
- 0xd6, 0xad, 0x8e, 0x3f, 0x35, 0xce, 0x07, 0xef, 0x6f, 0x0e, 0xc0, 0xc4, 0x38, 0x73, 0xf3, 0x82,
- 0x7c, 0x94, 0xf1, 0x44, 0xb1, 0x8f, 0x29, 0xcc, 0x4b, 0xaf, 0xc3, 0x99, 0xbe, 0xb9, 0x81, 0x4c,
- 0x42, 0x69, 0x97, 0xee, 0xf3, 0xa9, 0xaf, 0x8e, 0xec, 0x92, 0x9c, 0x83, 0xca, 0x9e, 0x6e, 0xf7,
- 0xe8, 0x54, 0x91, 0x97, 0x89, 0x9b, 0x3f, 0x28, 0xbe, 0x56, 0xd0, 0xfe, 0x56, 0x09, 0x46, 0xd5,
- 0x8c, 0xd3, 0xb2, 0x9c, 0x5d, 0xf2, 0x26, 0x94, 0x6c, 0xb7, 0x2d, 0xe7, 0xcd, 0x3f, 0x1a, 0x7a,
- 0x16, 0x5b, 0x75, 0xdb, 0xcd, 0xea, 0xe1, 0xc1, 0x74, 0x69, 0xd5, 0x6d, 0x23, 0x43, 0x24, 0x06,
- 0x54, 0x76, 0xf5, 0xed, 0x5d, 0x9d, 0xb7, 0xa1, 0x31, 0xd7, 0x1c, 0x1a, 0xfa, 0x16, 0x43, 0x61,
- 0x6d, 0x6d, 0xd6, 0x0f, 0x0f, 0xa6, 0x2b, 0xfc, 0x16, 0x05, 0x36, 0x71, 0xa1, 0xbe, 0x65, 0xeb,
- 0xc6, 0xee, 0x8e, 0x6b, 0xd3, 0xa9, 0x52, 0x4e, 0x41, 0x4d, 0x85, 0x24, 0x5e, 0x73, 0x78, 0x8b,
- 0x91, 0x0c, 0x62, 0xc0, 0x48, 0xcf, 0xf4, 0x2d, 0x67, 0x57, 0xce, 0x81, 0xaf, 0x0f, 0x2d, 0x6d,
- 0x73, 0x91, 0x3f, 0x13, 0x1c, 0x1e, 0x4c, 0x8f, 0x88, 0x6b, 0x94, 0xd0, 0xda, 0x2f, 0x47, 0x61,
- 0x5c, 0xbd, 0xa4, 0xbb, 0xd4, 0x0b, 0xe8, 0x7d, 0x72, 0x15, 0xca, 0x0e, 0xfb, 0x34, 0xf9, 0x4b,
- 0x6e, 0x8e, 0xca, 0xe1, 0x52, 0xe6, 0x9f, 0x24, 0xa7, 0xb0, 0x96, 0x89, 0xa1, 0x22, 0x3b, 0x7c,
- 0xf8, 0x96, 0xb5, 0x38, 0x8c, 0x68, 0x99, 0xb8, 0x46, 0x09, 0x4d, 0xde, 0x86, 0x32, 0x7f, 0x78,
- 0xd1, 0xd5, 0x9f, 0x1f, 0x5e, 0x04, 0x7b, 0xf4, 0x1a, 0x7b, 0x02, 0xfe, 0xe0, 0x1c, 0x94, 0x0d,
- 0xc5, 0x9e, 0xb9, 0x2d, 0x3b, 0xf6, 0x8f, 0x72, 0x74, 0xec, 0x92, 0x18, 0x8a, 0x9b, 0x8b, 0x4b,
- 0xc8, 0x10, 0xc9, 0x5f, 0x29, 0xc0, 0x19, 0xc3, 0x75, 0x02, 0x9d, 0xe9, 0x19, 0x6a, 0x91, 0x9d,
- 0xaa, 0x70, 0x39, 0x37, 0x87, 0x96, 0xb3, 0x90, 0x46, 0x6c, 0x9e, 0x67, 0x6b, 0x46, 0x5f, 0x31,
- 0xf6, 0xcb, 0x26, 0x7f, 0xad, 0x00, 0xe7, 0xd9, 0x5c, 0xde, 0xc7, 0xcc, 0x57, 0xa0, 0x93, 0x6d,
- 0xd5, 0xc5, 0xc3, 0x83, 0xe9, 0xf3, 0x2b, 0x59, 0xc2, 0x30, 0xbb, 0x0d, 0xac, 0x75, 0x67, 0xf5,
- 0x7e, 0xb5, 0x84, 0xaf, 0x6e, 0x8d, 0xb9, 0xd5, 0x93, 0x54, 0x75, 0x9a, 0xcf, 0xc8, 0xa1, 0x9c,
- 0xa5, 0xd9, 0x61, 0x56, 0x2b, 0xc8, 0x75, 0xa8, 0xee, 0xb9, 0x76, 0xaf, 0x43, 0xfd, 0xa9, 0x1a,
- 0x9f, 0x62, 0x2f, 0x65, 0x4d, 0xb1, 0x77, 0x39, 0x4b, 0x73, 0x42, 0xc2, 0x57, 0xc5, 0xbd, 0x8f,
- 0xaa, 0x2e, 0xb1, 0x60, 0xc4, 0xb6, 0x3a, 0x56, 0xe0, 0xf3, 0x85, 0xb3, 0x31, 0x77, 0x7d, 0xe8,
- 0xc7, 0x12, 0x9f, 0xe8, 0x2a, 0x07, 0x13, 0x5f, 0x8d, 0xb8, 0x46, 0x29, 0x80, 0x4d, 0x85, 0xbe,
- 0xa1, 0xdb, 0x62, 0x61, 0x6d, 0xcc, 0x7d, 0x61, 0xf8, 0xcf, 0x86, 0xa1, 0x34, 0xc7, 0xe4, 0x33,
- 0x55, 0xf8, 0x2d, 0x0a, 0x6c, 0xf2, 0x65, 0x18, 0x4f, 0xbc, 0x4d, 0x7f, 0xaa, 0xc1, 0x7b, 0xe7,
- 0xd9, 0xac, 0xde, 0x09, 0xb9, 0xa2, 0x95, 0x27, 0x31, 0x42, 0x7c, 0x4c, 0x81, 0x91, 0x5b, 0x50,
- 0xf3, 0x2d, 0x93, 0x1a, 0xba, 0xe7, 0x4f, 0x8d, 0x1e, 0x05, 0x78, 0x52, 0x02, 0xd7, 0x5a, 0xb2,
- 0x1a, 0x86, 0x00, 0x64, 0x06, 0xa0, 0xab, 0x7b, 0x81, 0x25, 0x14, 0xd5, 0x31, 0xae, 0x34, 0x8d,
- 0x1f, 0x1e, 0x4c, 0xc3, 0x7a, 0x58, 0x8a, 0x31, 0x0e, 0xc6, 0xcf, 0xea, 0xae, 0x38, 0xdd, 0x5e,
- 0x20, 0x16, 0xd6, 0xba, 0xe0, 0x6f, 0x85, 0xa5, 0x18, 0xe3, 0x20, 0xdf, 0x2f, 0xc0, 0x33, 0xd1,
- 0x6d, 0xff, 0x47, 0x36, 0x71, 0xe2, 0x1f, 0xd9, 0xf4, 0xe1, 0xc1, 0xf4, 0x33, 0xad, 0xc1, 0x22,
- 0xf1, 0x61, 0xed, 0x21, 0x1f, 0x16, 0x60, 0xbc, 0xd7, 0x35, 0xf5, 0x80, 0xb6, 0x02, 0xb6, 0xe3,
- 0x69, 0xef, 0x4f, 0x4d, 0xf2, 0x26, 0x2e, 0x0f, 0x3f, 0x0b, 0x26, 0xe0, 0xa2, 0xd7, 0x9c, 0x2c,
- 0xc7, 0x94, 0x58, 0xed, 0x4d, 0x18, 0x9b, 0xef, 0x05, 0x3b, 0xae, 0x67, 0xbd, 0xcf, 0xd5, 0x7f,
- 0xb2, 0x04, 0x95, 0x80, 0xab, 0x71, 0x42, 0x43, 0x78, 0x3e, 0xeb, 0xa5, 0x0b, 0x95, 0xfa, 0x16,
- 0xdd, 0x57, 0x7a, 0x89, 0x58, 0xa9, 0x85, 0x5a, 0x27, 0xaa, 0x6b, 0x7f, 0xae, 0x00, 0xd5, 0xa6,
- 0x6e, 0xec, 0xba, 0xdb, 0xdb, 0xe4, 0x2d, 0xa8, 0x59, 0x4e, 0x40, 0xbd, 0x3d, 0xdd, 0x96, 0xb0,
- 0x33, 0x31, 0xd8, 0x70, 0x43, 0x18, 0x3d, 0x1e, 0xdb, 0x7d, 0x31, 0x41, 0x8b, 0x3d, 0xb9, 0x6b,
- 0xe1, 0x9a, 0xf1, 0x8a, 0xc4, 0xc0, 0x10, 0x8d, 0x4c, 0x43, 0xc5, 0x0f, 0x68, 0xd7, 0xe7, 0x6b,
- 0xe0, 0x98, 0x68, 0x46, 0x8b, 0x15, 0xa0, 0x28, 0xd7, 0xfe, 0x66, 0x01, 0xea, 0x4d, 0xdd, 0xb7,
- 0x0c, 0xf6, 0x94, 0x64, 0x01, 0xca, 0x3d, 0x9f, 0x7a, 0xc7, 0x7b, 0x36, 0xbe, 0x6c, 0x6d, 0xfa,
- 0xd4, 0x43, 0x5e, 0x99, 0xdc, 0x81, 0x5a, 0x57, 0xf7, 0xfd, 0x7b, 0xae, 0x67, 0xca, 0xa5, 0xf7,
- 0x88, 0x40, 0x62, 0x9b, 0x20, 0xab, 0x62, 0x08, 0xa2, 0x35, 0x20, 0xd2, 0x3d, 0xb4, 0x9f, 0x17,
- 0xe0, 0x6c, 0xb3, 0xb7, 0xbd, 0x4d, 0x3d, 0xa9, 0x15, 0x4b, 0x7d, 0x93, 0x42, 0xc5, 0xa3, 0xa6,
- 0xe5, 0xcb, 0xb6, 0x2f, 0x0e, 0x3d, 0x50, 0x90, 0xa1, 0x48, 0xf5, 0x96, 0xf7, 0x17, 0x2f, 0x40,
- 0x81, 0x4e, 0x7a, 0x50, 0x7f, 0x97, 0xb2, 0xdd, 0x38, 0xd5, 0x3b, 0xf2, 0xe9, 0x6e, 0x0c, 0x2d,
- 0xea, 0x26, 0x0d, 0x5a, 0x1c, 0x29, 0xae, 0x4d, 0x87, 0x85, 0x18, 0x49, 0xd2, 0x7e, 0x54, 0x81,
- 0xd1, 0x05, 0xb7, 0xb3, 0x65, 0x39, 0xd4, 0xbc, 0x6e, 0xb6, 0x29, 0x79, 0x07, 0xca, 0xd4, 0x6c,
- 0x53, 0xf9, 0xb4, 0xc3, 0x2b, 0x1e, 0x0c, 0x2c, 0x52, 0x9f, 0xd8, 0x1d, 0x72, 0x60, 0xb2, 0x0a,
- 0xe3, 0xdb, 0x9e, 0xdb, 0x11, 0x73, 0xf9, 0xc6, 0x7e, 0x57, 0xea, 0xce, 0xcd, 0xdf, 0x54, 0x1f,
- 0xce, 0x52, 0x82, 0xfa, 0xe0, 0x60, 0x1a, 0xa2, 0x3b, 0x4c, 0xd5, 0x25, 0x6f, 0xc1, 0x54, 0x54,
- 0x12, 0x4e, 0x6a, 0x0b, 0x6c, 0x3b, 0xc3, 0x75, 0xa7, 0x4a, 0xf3, 0xf2, 0xe1, 0xc1, 0xf4, 0xd4,
- 0xd2, 0x00, 0x1e, 0x1c, 0x58, 0x9b, 0x4d, 0x15, 0x93, 0x11, 0x51, 0x2c, 0x34, 0x52, 0x65, 0x3a,
- 0xa1, 0x15, 0x8c, 0xef, 0xfb, 0x96, 0x52, 0x22, 0xb0, 0x4f, 0x28, 0x59, 0x82, 0xd1, 0xc0, 0x8d,
- 0xf5, 0x57, 0x85, 0xf7, 0x97, 0xa6, 0x0c, 0x15, 0x1b, 0xee, 0xc0, 0xde, 0x4a, 0xd4, 0x23, 0x08,
- 0x17, 0xd4, 0x7d, 0xaa, 0xa7, 0x46, 0x78, 0x4f, 0x5d, 0x3a, 0x3c, 0x98, 0xbe, 0xb0, 0x91, 0xc9,
- 0x81, 0x03, 0x6a, 0x92, 0x6f, 0x14, 0x60, 0x5c, 0x91, 0x64, 0x1f, 0x55, 0x4f, 0xb2, 0x8f, 0x08,
- 0x1b, 0x11, 0x1b, 0x09, 0x01, 0x98, 0x12, 0xa8, 0xfd, 0xb2, 0x0c, 0xf5, 0x70, 0xaa, 0x27, 0xcf,
- 0x41, 0x85, 0x9b, 0x20, 0xa4, 0x06, 0x1f, 0xae, 0xe1, 0xdc, 0x52, 0x81, 0x82, 0x46, 0x9e, 0x87,
- 0xaa, 0xe1, 0x76, 0x3a, 0xba, 0x63, 0x72, 0xb3, 0x52, 0xbd, 0xd9, 0x60, 0xaa, 0xcb, 0x82, 0x28,
- 0x42, 0x45, 0x23, 0x97, 0xa1, 0xac, 0x7b, 0x6d, 0x61, 0xe1, 0xa9, 0x8b, 0xf9, 0x68, 0xde, 0x6b,
- 0xfb, 0xc8, 0x4b, 0xc9, 0xe7, 0xa0, 0x44, 0x9d, 0xbd, 0xa9, 0xf2, 0x60, 0xdd, 0xe8, 0xba, 0xb3,
- 0x77, 0x57, 0xf7, 0x9a, 0x0d, 0xd9, 0x86, 0xd2, 0x75, 0x67, 0x0f, 0x59, 0x1d, 0xb2, 0x0a, 0x55,
- 0xea, 0xec, 0xb1, 0x77, 0x2f, 0x4d, 0x2f, 0xbf, 0x31, 0xa0, 0x3a, 0x63, 0x91, 0xdb, 0x84, 0x50,
- 0xc3, 0x92, 0xc5, 0xa8, 0x20, 0xc8, 0x97, 0x60, 0x54, 0x28, 0x5b, 0x6b, 0xec, 0x9d, 0xf8, 0x53,
- 0x23, 0x1c, 0x72, 0x7a, 0xb0, 0xb6, 0xc6, 0xf9, 0x22, 0x53, 0x57, 0xac, 0xd0, 0xc7, 0x04, 0x14,
- 0xf9, 0x12, 0xd4, 0xd5, 0xce, 0x58, 0xbd, 0xd9, 0x4c, 0x2b, 0x91, 0xda, 0x4e, 0x23, 0x7d, 0xaf,
- 0x67, 0x79, 0xb4, 0x43, 0x9d, 0xc0, 0x6f, 0x9e, 0x51, 0x76, 0x03, 0x45, 0xf5, 0x31, 0x42, 0x23,
- 0x5b, 0xfd, 0xe6, 0x2e, 0x61, 0xab, 0x79, 0x6e, 0xc0, 0xac, 0x3e, 0x84, 0xad, 0xeb, 0x2b, 0x30,
- 0x11, 0xda, 0xa3, 0xa4, 0x49, 0x43, 0x58, 0x6f, 0x5e, 0x62, 0xd5, 0x57, 0x92, 0xa4, 0x07, 0x07,
- 0xd3, 0xcf, 0x66, 0x18, 0x35, 0x22, 0x06, 0x4c, 0x83, 0x69, 0x3f, 0x2c, 0x41, 0xff, 0x3e, 0x24,
- 0xd9, 0x69, 0x85, 0x93, 0xee, 0xb4, 0xf4, 0x03, 0x89, 0xe9, 0xf3, 0x35, 0x59, 0x2d, 0xff, 0x43,
- 0x65, 0xbd, 0x98, 0xd2, 0x49, 0xbf, 0x98, 0x27, 0xe5, 0xdb, 0xd1, 0x3e, 0x2a, 0xc3, 0xf8, 0xa2,
- 0x4e, 0x3b, 0xae, 0xf3, 0xc8, 0x5d, 0x59, 0xe1, 0x89, 0xd8, 0x95, 0x5d, 0x83, 0x9a, 0x47, 0xbb,
- 0xb6, 0x65, 0xe8, 0x42, 0xf9, 0x92, 0x56, 0x50, 0x94, 0x65, 0x18, 0x52, 0x07, 0xec, 0xc6, 0x4b,
- 0x4f, 0xe4, 0x6e, 0xbc, 0xfc, 0xc9, 0xef, 0xc6, 0xb5, 0x6f, 0x14, 0x81, 0x2b, 0x2a, 0xe4, 0x2a,
- 0x94, 0xd9, 0x22, 0x9c, 0xb6, 0x01, 0xf1, 0x81, 0xc3, 0x29, 0xe4, 0x12, 0x14, 0x03, 0x57, 0x7e,
- 0x79, 0x20, 0xe9, 0xc5, 0x0d, 0x17, 0x8b, 0x81, 0x4b, 0xde, 0x07, 0x30, 0x5c, 0xc7, 0xb4, 0x94,
- 0x73, 0x20, 0xdf, 0x83, 0x2d, 0xb9, 0xde, 0x3d, 0xdd, 0x33, 0x17, 0x42, 0x44, 0xb1, 0x1f, 0x8b,
- 0xee, 0x31, 0x26, 0x8d, 0xbc, 0x0e, 0x23, 0xae, 0xb3, 0xd4, 0xb3, 0x6d, 0xde, 0xa1, 0xf5, 0xe6,
- 0x6f, 0xb1, 0x4d, 0xf2, 0x1d, 0x5e, 0xf2, 0xe0, 0x60, 0xfa, 0xa2, 0xd0, 0x6f, 0xd9, 0xdd, 0x9b,
- 0x9e, 0x15, 0x58, 0x4e, 0x3b, 0xdc, 0x9e, 0xc8, 0x6a, 0xda, 0xb7, 0x0a, 0xd0, 0x58, 0xb2, 0xee,
- 0x53, 0xf3, 0x4d, 0xcb, 0x31, 0xdd, 0x7b, 0x04, 0x61, 0xc4, 0xa6, 0x4e, 0x3b, 0xd8, 0x19, 0x72,
- 0xff, 0x20, 0x76, 0xe9, 0x1c, 0x01, 0x25, 0x12, 0x99, 0x85, 0xba, 0xd0, 0x3e, 0x2d, 0xa7, 0xcd,
- 0xfb, 0xb0, 0x16, 0x4d, 0x7a, 0x2d, 0x45, 0xc0, 0x88, 0x47, 0xdb, 0x87, 0x33, 0x7d, 0xdd, 0x40,
- 0x4c, 0x28, 0x07, 0x7a, 0x5b, 0xcd, 0xaf, 0x4b, 0x43, 0x77, 0xf0, 0x86, 0xde, 0x8e, 0x75, 0x2e,
- 0x5f, 0xe3, 0x37, 0x74, 0xb6, 0xc6, 0x33, 0x74, 0xed, 0x57, 0x05, 0xa8, 0x2d, 0xf5, 0x1c, 0x83,
- 0x6f, 0xd1, 0x1e, 0x6d, 0x1b, 0x54, 0x0a, 0x43, 0x31, 0x53, 0x61, 0xe8, 0xc1, 0xc8, 0xee, 0xbd,
- 0x50, 0xa1, 0x68, 0xcc, 0xad, 0x0d, 0x3f, 0x2a, 0x64, 0x93, 0x66, 0x6e, 0x71, 0x3c, 0xe1, 0xba,
- 0x1a, 0x97, 0x0d, 0x1a, 0xb9, 0xf5, 0x26, 0x17, 0x2a, 0x85, 0x5d, 0xfa, 0x1c, 0x34, 0x62, 0x6c,
- 0xc7, 0xb2, 0x62, 0xff, 0xbd, 0x32, 0x8c, 0x2c, 0xb7, 0x5a, 0xf3, 0xeb, 0x2b, 0xe4, 0x65, 0x68,
- 0x48, 0xaf, 0xc6, 0xed, 0xa8, 0x0f, 0x42, 0xa7, 0x56, 0x2b, 0x22, 0x61, 0x9c, 0x8f, 0xa9, 0x63,
- 0x1e, 0xd5, 0xed, 0x8e, 0xfc, 0x58, 0x42, 0x75, 0x0c, 0x59, 0x21, 0x0a, 0x1a, 0xd1, 0x61, 0x9c,
- 0xed, 0xf0, 0x58, 0x17, 0x8a, 0xdd, 0x9b, 0xfc, 0x6c, 0x8e, 0xb8, 0xbf, 0xe3, 0x4a, 0xe2, 0x66,
- 0x02, 0x00, 0x53, 0x80, 0xe4, 0x35, 0xa8, 0xe9, 0xbd, 0x60, 0x87, 0x2b, 0xd0, 0xe2, 0xdb, 0xb8,
- 0xcc, 0x9d, 0x3e, 0xb2, 0xec, 0xc1, 0xc1, 0xf4, 0xe8, 0x2d, 0x6c, 0xbe, 0xac, 0xee, 0x31, 0xe4,
- 0x66, 0x8d, 0x53, 0x3b, 0x46, 0xd9, 0xb8, 0xca, 0xb1, 0x1b, 0xb7, 0x9e, 0x00, 0xc0, 0x14, 0x20,
- 0x79, 0x1b, 0x46, 0x77, 0xe9, 0x7e, 0xa0, 0x6f, 0x49, 0x01, 0x23, 0xc7, 0x11, 0x30, 0xc9, 0x54,
- 0xb8, 0x5b, 0xb1, 0xea, 0x98, 0x00, 0x23, 0x3e, 0x9c, 0xdb, 0xa5, 0xde, 0x16, 0xf5, 0x5c, 0xb9,
- 0xfb, 0x94, 0x42, 0xaa, 0xc7, 0x11, 0x32, 0x75, 0x78, 0x30, 0x7d, 0xee, 0x56, 0x06, 0x0c, 0x66,
- 0x82, 0x6b, 0xff, 0xbb, 0x08, 0x13, 0xcb, 0xc2, 0xad, 0xec, 0x7a, 0x62, 0x11, 0x26, 0x17, 0xa1,
- 0xe4, 0x75, 0x7b, 0x7c, 0xe4, 0x94, 0x84, 0xe1, 0x18, 0xd7, 0x37, 0x91, 0x95, 0x91, 0xb7, 0xa0,
- 0x66, 0xca, 0x29, 0x43, 0x6e, 0x7e, 0x87, 0x32, 0x54, 0xa8, 0x3b, 0x0c, 0xd1, 0x98, 0xa6, 0xdf,
- 0xf1, 0xdb, 0x2d, 0xeb, 0x7d, 0x2a, 0xf7, 0x83, 0x5c, 0xd3, 0x5f, 0x13, 0x45, 0xa8, 0x68, 0x6c,
- 0x55, 0xdd, 0xa5, 0xfb, 0x62, 0x37, 0x54, 0x8e, 0x56, 0xd5, 0x5b, 0xb2, 0x0c, 0x43, 0x2a, 0x99,
- 0x56, 0x1f, 0x0b, 0x1b, 0x05, 0x65, 0xb1, 0x93, 0xbf, 0xcb, 0x0a, 0xe4, 0x77, 0xc3, 0xa6, 0xcc,
- 0x77, 0xad, 0x20, 0xa0, 0x9e, 0x7c, 0x8d, 0x43, 0x4d, 0x99, 0x37, 0x39, 0x02, 0x4a, 0x24, 0xf2,
- 0x3b, 0x50, 0xe7, 0xe0, 0x4d, 0xdb, 0xdd, 0xe2, 0x2f, 0xae, 0x2e, 0xf6, 0xf4, 0x77, 0x55, 0x21,
- 0x46, 0x74, 0xed, 0xd7, 0x45, 0xb8, 0xb0, 0x4c, 0x03, 0xa1, 0xd5, 0x2c, 0xd2, 0xae, 0xed, 0xee,
- 0x33, 0xd5, 0x12, 0xe9, 0x7b, 0xe4, 0x0d, 0x00, 0xcb, 0xdf, 0x6a, 0xed, 0x19, 0xfc, 0x3b, 0x10,
- 0xdf, 0xf0, 0x55, 0xf9, 0x49, 0xc2, 0x4a, 0xab, 0x29, 0x29, 0x0f, 0x12, 0x77, 0x18, 0xab, 0x13,
- 0x6d, 0xaf, 0x8a, 0x0f, 0xd9, 0x5e, 0xb5, 0x00, 0xba, 0x91, 0x82, 0x5a, 0xe2, 0x9c, 0xbf, 0xaf,
- 0xc4, 0x1c, 0x47, 0x37, 0x8d, 0xc1, 0xe4, 0x51, 0x19, 0x1d, 0x98, 0x34, 0xe9, 0xb6, 0xde, 0xb3,
- 0x83, 0x50, 0xa9, 0x96, 0x1f, 0xf1, 0xd1, 0xf5, 0xf2, 0xd0, 0xe5, 0xbd, 0x98, 0x42, 0xc2, 0x3e,
- 0x6c, 0xed, 0xef, 0x97, 0xe0, 0xd2, 0x32, 0x0d, 0x42, 0x8b, 0x8b, 0x9c, 0x1d, 0x5b, 0x5d, 0x6a,
- 0xb0, 0xb7, 0xf0, 0x61, 0x01, 0x46, 0x6c, 0x7d, 0x8b, 0xda, 0x6c, 0xf5, 0x62, 0x4f, 0xf3, 0xce,
- 0xd0, 0x0b, 0xc1, 0x60, 0x29, 0x33, 0xab, 0x5c, 0x42, 0x6a, 0x69, 0x10, 0x85, 0x28, 0xc5, 0xb3,
- 0x49, 0xdd, 0xb0, 0x7b, 0x7e, 0x40, 0xbd, 0x75, 0xd7, 0x0b, 0xa4, 0x3e, 0x19, 0x4e, 0xea, 0x0b,
- 0x11, 0x09, 0xe3, 0x7c, 0x64, 0x0e, 0xc0, 0xb0, 0x2d, 0xea, 0x04, 0xbc, 0x96, 0xf8, 0xae, 0x88,
- 0x7a, 0xbf, 0x0b, 0x21, 0x05, 0x63, 0x5c, 0x4c, 0x54, 0xc7, 0x75, 0xac, 0xc0, 0x15, 0xa2, 0xca,
- 0x49, 0x51, 0x6b, 0x11, 0x09, 0xe3, 0x7c, 0xbc, 0x1a, 0x0d, 0x3c, 0xcb, 0xf0, 0x79, 0xb5, 0x4a,
- 0xaa, 0x5a, 0x44, 0xc2, 0x38, 0x1f, 0x5b, 0xf3, 0x62, 0xcf, 0x7f, 0xac, 0x35, 0xef, 0x7b, 0x75,
- 0xb8, 0x92, 0xe8, 0xd6, 0x40, 0x0f, 0xe8, 0x76, 0xcf, 0x6e, 0xd1, 0x40, 0xbd, 0xc0, 0x21, 0xd7,
- 0xc2, 0xbf, 0x18, 0xbd, 0x77, 0x11, 0xcc, 0x62, 0x9c, 0xcc, 0x7b, 0xef, 0x6b, 0xe0, 0x91, 0xde,
- 0xfd, 0x2c, 0xd4, 0x1d, 0x3d, 0xf0, 0xf9, 0x87, 0x2b, 0xbf, 0xd1, 0x50, 0x0d, 0xbb, 0xad, 0x08,
- 0x18, 0xf1, 0x90, 0x75, 0x38, 0x27, 0xbb, 0xf8, 0xfa, 0xfd, 0xae, 0xeb, 0x05, 0xd4, 0x13, 0x75,
- 0xe5, 0x72, 0x2a, 0xeb, 0x9e, 0x5b, 0xcb, 0xe0, 0xc1, 0xcc, 0x9a, 0x64, 0x0d, 0xce, 0x1a, 0xc2,
- 0xc1, 0x4f, 0x6d, 0x57, 0x37, 0x15, 0xa0, 0x30, 0x70, 0x85, 0x5b, 0xa3, 0x85, 0x7e, 0x16, 0xcc,
- 0xaa, 0x97, 0x1e, 0xcd, 0x23, 0x43, 0x8d, 0xe6, 0xea, 0x30, 0xa3, 0xb9, 0x36, 0xdc, 0x68, 0xae,
- 0x1f, 0x6d, 0x34, 0xb3, 0x9e, 0x67, 0xe3, 0x88, 0x7a, 0x4c, 0x3d, 0x11, 0x2b, 0x6c, 0x2c, 0x7e,
- 0x24, 0xec, 0xf9, 0x56, 0x06, 0x0f, 0x66, 0xd6, 0x24, 0x5b, 0x70, 0x49, 0x94, 0x5f, 0x77, 0x0c,
- 0x6f, 0xbf, 0xcb, 0x16, 0x9e, 0x18, 0x6e, 0x23, 0x61, 0x61, 0xbc, 0xd4, 0x1a, 0xc8, 0x89, 0x0f,
- 0x41, 0x21, 0x7f, 0x08, 0x63, 0xe2, 0x2d, 0xad, 0xe9, 0x5d, 0x0e, 0x2b, 0xa2, 0x49, 0xce, 0x4b,
- 0xd8, 0xb1, 0x85, 0x38, 0x11, 0x93, 0xbc, 0x64, 0x1e, 0x26, 0xba, 0x7b, 0x06, 0xbb, 0x5c, 0xd9,
- 0xbe, 0x4d, 0xa9, 0x49, 0x4d, 0xee, 0xbe, 0xaa, 0x37, 0x9f, 0x56, 0x86, 0x8e, 0xf5, 0x24, 0x19,
- 0xd3, 0xfc, 0xe4, 0x35, 0x18, 0xf5, 0x03, 0xdd, 0x0b, 0xa4, 0x59, 0x6f, 0x6a, 0x5c, 0x44, 0xdb,
- 0x28, 0xab, 0x57, 0x2b, 0x46, 0xc3, 0x04, 0x67, 0xe6, 0x7a, 0x31, 0x71, 0x7a, 0xeb, 0x45, 0x9e,
- 0xd9, 0xea, 0x9f, 0x15, 0xe1, 0xea, 0x32, 0x0d, 0xd6, 0x5c, 0x47, 0x1a, 0x45, 0xb3, 0x96, 0xfd,
- 0x23, 0xd9, 0x44, 0x93, 0x8b, 0x76, 0xf1, 0x44, 0x17, 0xed, 0xd2, 0x09, 0x2d, 0xda, 0xe5, 0x53,
- 0x5c, 0xb4, 0xff, 0x61, 0x11, 0x9e, 0x4e, 0xf4, 0xe4, 0xba, 0x6b, 0xaa, 0x09, 0xff, 0xb3, 0x0e,
- 0x3c, 0x42, 0x07, 0x3e, 0x10, 0x7a, 0x27, 0x77, 0x6b, 0xa5, 0x34, 0x9e, 0x0f, 0xd2, 0x1a, 0xcf,
- 0xdb, 0x79, 0x56, 0xbe, 0x0c, 0x09, 0x47, 0x5a, 0xf1, 0x6e, 0x02, 0xf1, 0xa4, 0x13, 0x4e, 0x98,
- 0x7e, 0x62, 0x4a, 0x4f, 0x18, 0xce, 0x87, 0x7d, 0x1c, 0x98, 0x51, 0x8b, 0xb4, 0xe0, 0xbc, 0x4f,
- 0x9d, 0xc0, 0x72, 0xa8, 0x9d, 0x84, 0x13, 0xda, 0xd0, 0xb3, 0x12, 0xee, 0x7c, 0x2b, 0x8b, 0x09,
- 0xb3, 0xeb, 0xe6, 0x99, 0x07, 0xfe, 0x25, 0x70, 0x95, 0x53, 0x74, 0xcd, 0x89, 0x69, 0x2c, 0x1f,
- 0xa6, 0x35, 0x96, 0x77, 0xf2, 0xbf, 0xb7, 0xe1, 0xb4, 0x95, 0x39, 0x00, 0xfe, 0x16, 0xe2, 0xea,
- 0x4a, 0xb8, 0x48, 0x63, 0x48, 0xc1, 0x18, 0x17, 0x5b, 0x80, 0x54, 0x3f, 0xc7, 0x35, 0x95, 0x70,
- 0x01, 0x6a, 0xc5, 0x89, 0x98, 0xe4, 0x1d, 0xa8, 0xed, 0x54, 0x86, 0xd6, 0x76, 0x6e, 0x02, 0x49,
- 0x18, 0x1e, 0x05, 0xde, 0x48, 0x32, 0x9a, 0x74, 0xa5, 0x8f, 0x03, 0x33, 0x6a, 0x0d, 0x18, 0xca,
- 0xd5, 0x93, 0x1d, 0xca, 0xb5, 0xe1, 0x87, 0x32, 0x79, 0x07, 0x2e, 0x72, 0x51, 0xb2, 0x7f, 0x92,
- 0xc0, 0x42, 0xef, 0xf9, 0x0d, 0x09, 0x7c, 0x11, 0x07, 0x31, 0xe2, 0x60, 0x0c, 0xf6, 0x7e, 0x0c,
- 0x8f, 0x9a, 0x4c, 0xb8, 0x6e, 0x0f, 0xd6, 0x89, 0x16, 0x32, 0x78, 0x30, 0xb3, 0x26, 0x1b, 0x62,
- 0x01, 0x1b, 0x86, 0xfa, 0x96, 0x4d, 0x4d, 0x19, 0x4d, 0x1b, 0x0e, 0xb1, 0x8d, 0xd5, 0x96, 0xa4,
- 0x60, 0x8c, 0x2b, 0x4b, 0x4d, 0x19, 0x3d, 0xa6, 0x9a, 0xb2, 0xcc, 0xad, 0xf4, 0xdb, 0x09, 0x6d,
- 0x48, 0xea, 0x3a, 0x61, 0x7c, 0xf4, 0x42, 0x9a, 0x01, 0xfb, 0xeb, 0x70, 0x2d, 0xd1, 0xf0, 0xac,
- 0x6e, 0xe0, 0x27, 0xb1, 0xc6, 0x53, 0x5a, 0x62, 0x06, 0x0f, 0x66, 0xd6, 0x64, 0xfa, 0xf9, 0x0e,
- 0xd5, 0xed, 0x60, 0x27, 0x09, 0x38, 0x91, 0xd4, 0xcf, 0x6f, 0xf4, 0xb3, 0x60, 0x56, 0xbd, 0xcc,
- 0x05, 0x69, 0xf2, 0xc9, 0x54, 0xab, 0xbe, 0x59, 0x82, 0x8b, 0xcb, 0x34, 0x08, 0x03, 0x8d, 0x3e,
- 0x33, 0xa3, 0x7c, 0x02, 0x66, 0x94, 0xef, 0x56, 0xe0, 0xec, 0x32, 0x0d, 0xfa, 0xb4, 0xb1, 0xff,
- 0x4f, 0xbb, 0x7f, 0x0d, 0xce, 0x46, 0xb1, 0x6d, 0xad, 0xc0, 0xf5, 0xc4, 0x5a, 0x9e, 0xda, 0x2d,
- 0xb7, 0xfa, 0x59, 0x30, 0xab, 0x1e, 0xf9, 0x12, 0x3c, 0xcd, 0x97, 0x7a, 0xa7, 0x2d, 0xec, 0xb3,
- 0xc2, 0x98, 0x10, 0x3b, 0x9d, 0x31, 0x2d, 0x21, 0x9f, 0x6e, 0x65, 0xb3, 0xe1, 0xa0, 0xfa, 0xe4,
- 0x6b, 0x30, 0xda, 0xb5, 0xba, 0xd4, 0xb6, 0x1c, 0xae, 0x9f, 0xe5, 0x0e, 0x09, 0x59, 0x8f, 0x81,
- 0x45, 0x1b, 0xb8, 0x78, 0x29, 0x26, 0x04, 0x66, 0x8e, 0xd4, 0xda, 0x29, 0x8e, 0xd4, 0xff, 0x51,
- 0x84, 0xea, 0xb2, 0xe7, 0xf6, 0xba, 0xcd, 0x7d, 0xd2, 0x86, 0x91, 0x7b, 0xdc, 0x79, 0x26, 0x5d,
- 0x53, 0xc3, 0xc7, 0x87, 0x0b, 0x1f, 0x5c, 0xa4, 0x12, 0x89, 0x7b, 0x94, 0xf0, 0x6c, 0x10, 0xef,
- 0xd2, 0x7d, 0x6a, 0x4a, 0x1f, 0x5a, 0x38, 0x88, 0x6f, 0xb1, 0x42, 0x14, 0x34, 0xd2, 0x81, 0x09,
- 0xdd, 0xb6, 0xdd, 0x7b, 0xd4, 0x5c, 0xd5, 0x03, 0xea, 0x50, 0x5f, 0xb9, 0x24, 0x8f, 0x6b, 0x96,
- 0xe6, 0x7e, 0xfd, 0xf9, 0x24, 0x14, 0xa6, 0xb1, 0xc9, 0xbb, 0x50, 0xf5, 0x03, 0xd7, 0x53, 0xca,
- 0x56, 0x63, 0x6e, 0x61, 0xf8, 0x97, 0xde, 0xfc, 0x62, 0x4b, 0x40, 0x09, 0x9b, 0xbd, 0xbc, 0x41,
- 0x25, 0x40, 0xfb, 0x4e, 0x01, 0xe0, 0xc6, 0xc6, 0xc6, 0xba, 0x74, 0x2f, 0x98, 0x50, 0xd6, 0x7b,
- 0xa1, 0xa3, 0x72, 0x78, 0x87, 0x60, 0x22, 0x2c, 0x53, 0xfa, 0xf0, 0x7a, 0xc1, 0x0e, 0x72, 0x74,
- 0xf2, 0xdb, 0x50, 0x95, 0x0a, 0xb2, 0xec, 0xf6, 0x30, 0xb4, 0x40, 0x2a, 0xd1, 0xa8, 0xe8, 0xda,
- 0xdf, 0x2d, 0x02, 0xac, 0x98, 0x36, 0x6d, 0xa9, 0x90, 0xfe, 0x7a, 0xb0, 0xe3, 0x51, 0x7f, 0xc7,
- 0xb5, 0xcd, 0x21, 0xbd, 0xa9, 0xdc, 0xe6, 0xbf, 0xa1, 0x40, 0x30, 0xc2, 0x23, 0x26, 0x8c, 0xfa,
- 0x01, 0xed, 0xaa, 0x48, 0xcd, 0x21, 0x9d, 0x28, 0x93, 0xc2, 0x2e, 0x12, 0xe1, 0x60, 0x02, 0x95,
- 0xe8, 0xd0, 0xb0, 0x1c, 0x43, 0x7c, 0x20, 0xcd, 0xfd, 0x21, 0x07, 0xd2, 0x04, 0xdb, 0x71, 0xac,
- 0x44, 0x30, 0x18, 0xc7, 0xd4, 0x7e, 0x56, 0x84, 0x0b, 0x5c, 0x1e, 0x6b, 0x46, 0x22, 0x1e, 0x93,
- 0xfc, 0xe9, 0xbe, 0xe3, 0x87, 0x7f, 0xf2, 0x68, 0xa2, 0xc5, 0xe9, 0xb5, 0x35, 0x1a, 0xe8, 0x91,
- 0x3e, 0x17, 0x95, 0xc5, 0xce, 0x1c, 0xf6, 0xa0, 0xec, 0xb3, 0xf9, 0x4a, 0xf4, 0x5e, 0x6b, 0xe8,
- 0x21, 0x94, 0xfd, 0x00, 0x7c, 0xf6, 0x0a, 0xbd, 0xc6, 0x7c, 0xd6, 0xe2, 0xe2, 0xc8, 0x9f, 0x85,
- 0x11, 0x3f, 0xd0, 0x83, 0x9e, 0xfa, 0x34, 0x37, 0x4f, 0x5a, 0x30, 0x07, 0x8f, 0xe6, 0x11, 0x71,
- 0x8f, 0x52, 0xa8, 0xf6, 0xb3, 0x02, 0x5c, 0xca, 0xae, 0xb8, 0x6a, 0xf9, 0x01, 0xf9, 0x53, 0x7d,
- 0xdd, 0x7e, 0xc4, 0x37, 0xce, 0x6a, 0xf3, 0x4e, 0x0f, 0x23, 0xd4, 0x55, 0x49, 0xac, 0xcb, 0x03,
- 0xa8, 0x58, 0x01, 0xed, 0xa8, 0xfd, 0xe5, 0x9d, 0x13, 0x7e, 0xf4, 0xd8, 0xd2, 0xce, 0xa4, 0xa0,
- 0x10, 0xa6, 0x7d, 0x54, 0x1c, 0xf4, 0xc8, 0x7c, 0xf9, 0xb0, 0x93, 0x31, 0xbf, 0xb7, 0xf2, 0xc5,
- 0xfc, 0x26, 0x1b, 0xd4, 0x1f, 0xfa, 0xfb, 0x67, 0xfa, 0x43, 0x7f, 0xef, 0xe4, 0x0f, 0xfd, 0x4d,
- 0x75, 0xc3, 0xc0, 0x08, 0xe0, 0x8f, 0x4b, 0x70, 0xf9, 0x61, 0xc3, 0x86, 0xad, 0x67, 0x72, 0x74,
- 0xe6, 0x5d, 0xcf, 0x1e, 0x3e, 0x0e, 0xc9, 0x1c, 0x54, 0xba, 0x3b, 0xba, 0xaf, 0x94, 0x32, 0xb5,
- 0x61, 0xa9, 0xac, 0xb3, 0xc2, 0x07, 0x6c, 0xd2, 0xe0, 0xca, 0x1c, 0xbf, 0x45, 0xc1, 0xca, 0xa6,
- 0xe3, 0x0e, 0xf5, 0xfd, 0xc8, 0x26, 0x10, 0x4e, 0xc7, 0x6b, 0xa2, 0x18, 0x15, 0x9d, 0x04, 0x30,
- 0x22, 0x4c, 0xcc, 0x72, 0x65, 0x1a, 0x3e, 0x90, 0x2b, 0x23, 0x4c, 0x3c, 0x7a, 0x28, 0xe9, 0xad,
- 0x90, 0xb2, 0xc8, 0x0c, 0x94, 0x83, 0x28, 0x68, 0x57, 0x6d, 0xcd, 0xcb, 0x19, 0xfa, 0x29, 0xe7,
- 0x63, 0x1b, 0x7b, 0x77, 0x8b, 0x1b, 0xd5, 0x4d, 0xe9, 0x3f, 0xb7, 0x5c, 0x87, 0x2b, 0x64, 0xa5,
- 0x68, 0x63, 0x7f, 0xa7, 0x8f, 0x03, 0x33, 0x6a, 0x69, 0xff, 0xa6, 0x06, 0x17, 0xb2, 0xc7, 0x03,
- 0xeb, 0xb7, 0x3d, 0xea, 0xf9, 0x0c, 0xbb, 0x90, 0xec, 0xb7, 0xbb, 0xa2, 0x18, 0x15, 0xfd, 0x53,
- 0x1d, 0x70, 0xf6, 0xdd, 0x02, 0x5c, 0xf4, 0xa4, 0x8f, 0xe8, 0x71, 0x04, 0x9d, 0x3d, 0x2b, 0xcc,
- 0x19, 0x03, 0x04, 0xe2, 0xe0, 0xb6, 0x90, 0xbf, 0x5d, 0x80, 0xa9, 0x4e, 0xca, 0xce, 0x71, 0x8a,
- 0x27, 0xe8, 0x78, 0x54, 0xfc, 0xda, 0x00, 0x79, 0x38, 0xb0, 0x25, 0xe4, 0x6b, 0xd0, 0xe8, 0xb2,
- 0x71, 0xe1, 0x07, 0xd4, 0x31, 0xd4, 0x21, 0xba, 0xe1, 0xbf, 0xa4, 0xf5, 0x08, 0x2b, 0x3c, 0x41,
- 0xc3, 0xf5, 0x83, 0x18, 0x01, 0xe3, 0x12, 0x9f, 0xf0, 0x23, 0x73, 0xd7, 0xa0, 0xe6, 0xd3, 0x20,
- 0xb0, 0x9c, 0xb6, 0xd8, 0x6f, 0xd4, 0xc5, 0xb7, 0xd2, 0x92, 0x65, 0x18, 0x52, 0xc9, 0xef, 0x40,
- 0x9d, 0xbb, 0x9c, 0xe6, 0xbd, 0xb6, 0x3f, 0x55, 0xe7, 0xe1, 0x62, 0x63, 0x22, 0x00, 0x4e, 0x16,
- 0x62, 0x44, 0x27, 0x2f, 0xc1, 0xe8, 0x16, 0xff, 0x7c, 0xe5, 0x29, 0x6a, 0x61, 0xe3, 0xe2, 0xda,
- 0x5a, 0x33, 0x56, 0x8e, 0x09, 0x2e, 0x32, 0x07, 0x40, 0x43, 0xbf, 0x5c, 0xda, 0x9e, 0x15, 0x79,
- 0xec, 0x30, 0xc6, 0x45, 0x9e, 0x85, 0x52, 0x60, 0xfb, 0xdc, 0x86, 0x55, 0x8b, 0xb6, 0xa0, 0x1b,
- 0xab, 0x2d, 0x64, 0xe5, 0xda, 0xaf, 0x0b, 0x30, 0x91, 0x3a, 0x5c, 0xc2, 0xaa, 0xf4, 0x3c, 0x5b,
- 0x4e, 0x23, 0x61, 0x95, 0x4d, 0x5c, 0x45, 0x56, 0x4e, 0xde, 0x91, 0x6a, 0x79, 0x31, 0x67, 0xc2,
- 0x88, 0xdb, 0x7a, 0xe0, 0x33, 0x3d, 0xbc, 0x4f, 0x23, 0xe7, 0x6e, 0xbe, 0xa8, 0x3d, 0x72, 0x1d,
- 0x88, 0xb9, 0xf9, 0x22, 0x1a, 0x26, 0x38, 0x53, 0x06, 0xbf, 0xf2, 0x51, 0x0c, 0x7e, 0xda, 0xb7,
- 0x8a, 0xb1, 0x1e, 0x90, 0x9a, 0xfd, 0x23, 0x7a, 0xe0, 0x05, 0xb6, 0x80, 0x86, 0x8b, 0x7b, 0x3d,
- 0xbe, 0xfe, 0xf1, 0xc5, 0x58, 0x52, 0xc9, 0x9b, 0xa2, 0xef, 0x4b, 0x39, 0x8f, 0xe5, 0x6e, 0xac,
- 0xb6, 0x44, 0x74, 0x95, 0x7a, 0x6b, 0xe1, 0x2b, 0x28, 0x9f, 0xd2, 0x2b, 0xd0, 0xfe, 0x45, 0x09,
- 0x1a, 0x37, 0xdd, 0xad, 0x4f, 0x49, 0x04, 0x75, 0xf6, 0x32, 0x55, 0xfc, 0x04, 0x97, 0xa9, 0x4d,
- 0x78, 0x3a, 0x08, 0xec, 0x16, 0x35, 0x5c, 0xc7, 0xf4, 0xe7, 0xb7, 0x03, 0xea, 0x2d, 0x59, 0x8e,
- 0xe5, 0xef, 0x50, 0x53, 0xba, 0x93, 0x9e, 0x39, 0x3c, 0x98, 0x7e, 0x7a, 0x63, 0x63, 0x35, 0x8b,
- 0x05, 0x07, 0xd5, 0xe5, 0xd3, 0x86, 0x38, 0x09, 0xc8, 0x4f, 0xca, 0xc8, 0x98, 0x1b, 0x31, 0x6d,
- 0xc4, 0xca, 0x31, 0xc1, 0xa5, 0xfd, 0xa0, 0x08, 0xf5, 0x30, 0x15, 0x00, 0x79, 0x1e, 0xaa, 0x5b,
- 0x9e, 0xbb, 0x4b, 0x3d, 0xe1, 0xb9, 0x93, 0x27, 0x65, 0x9a, 0xa2, 0x08, 0x15, 0x8d, 0x3c, 0x07,
- 0x95, 0xc0, 0xed, 0x5a, 0x46, 0xda, 0xa0, 0xb6, 0xc1, 0x0a, 0x51, 0xd0, 0x4e, 0x6f, 0x80, 0xbf,
- 0x90, 0x50, 0xed, 0xea, 0x03, 0x95, 0xb1, 0xb7, 0xa1, 0xec, 0xeb, 0xbe, 0x2d, 0xd7, 0xd3, 0x1c,
- 0xa7, 0xea, 0xe7, 0x5b, 0xab, 0xf2, 0x54, 0xfd, 0x7c, 0x6b, 0x15, 0x39, 0xa8, 0xf6, 0xcb, 0x22,
- 0x34, 0x44, 0xbf, 0x89, 0x59, 0xe1, 0x24, 0x7b, 0xee, 0x75, 0x1e, 0x4a, 0xe1, 0xf7, 0x3a, 0xd4,
- 0xe3, 0x66, 0x26, 0x39, 0xc9, 0xc5, 0xfd, 0x03, 0x11, 0x31, 0x0c, 0xa7, 0x88, 0x8a, 0x54, 0xd7,
- 0x97, 0x4f, 0xb1, 0xeb, 0x2b, 0x47, 0xea, 0xfa, 0x91, 0xd3, 0xe8, 0xfa, 0x0f, 0x8b, 0x50, 0x5f,
- 0xb5, 0xb6, 0xa9, 0xb1, 0x6f, 0xd8, 0xfc, 0x4c, 0xa0, 0x49, 0x6d, 0x1a, 0xd0, 0x65, 0x4f, 0x37,
- 0xe8, 0x3a, 0xf5, 0x2c, 0x9e, 0x2a, 0x87, 0x7d, 0x1f, 0x7c, 0x06, 0x92, 0x67, 0x02, 0x17, 0x07,
- 0xf0, 0xe0, 0xc0, 0xda, 0x64, 0x05, 0x46, 0x4d, 0xea, 0x5b, 0x1e, 0x35, 0xd7, 0x63, 0x1b, 0x95,
- 0xe7, 0xd5, 0x52, 0xb3, 0x18, 0xa3, 0x3d, 0x38, 0x98, 0x1e, 0x53, 0x06, 0x4a, 0xb1, 0x63, 0x49,
- 0x54, 0x65, 0x9f, 0x7c, 0x57, 0xef, 0xf9, 0x59, 0x6d, 0x8c, 0x7d, 0xf2, 0xeb, 0xd9, 0x2c, 0x38,
- 0xa8, 0xae, 0x56, 0x81, 0xd2, 0xaa, 0xdb, 0xd6, 0x3e, 0x2a, 0x41, 0x98, 0x53, 0x89, 0xfc, 0x85,
- 0x02, 0x34, 0x74, 0xc7, 0x71, 0x03, 0x99, 0xaf, 0x48, 0x78, 0xe0, 0x31, 0x77, 0xea, 0xa6, 0x99,
- 0xf9, 0x08, 0x54, 0x38, 0x6f, 0x43, 0x87, 0x72, 0x8c, 0x82, 0x71, 0xd9, 0xa4, 0x97, 0xf2, 0x27,
- 0xaf, 0xe5, 0x6f, 0xc5, 0x11, 0xbc, 0xc7, 0x97, 0xbe, 0x00, 0x93, 0xe9, 0xc6, 0x1e, 0xc7, 0x1d,
- 0x94, 0xcb, 0x31, 0x5f, 0x04, 0x88, 0x62, 0x4a, 0x1e, 0x83, 0x11, 0xcb, 0x4a, 0x18, 0xb1, 0x86,
- 0x3f, 0xd8, 0x1e, 0x35, 0x7a, 0xa0, 0xe1, 0xea, 0xbd, 0x94, 0xe1, 0x6a, 0xe5, 0x24, 0x84, 0x3d,
- 0xdc, 0x58, 0xf5, 0x77, 0x0a, 0x30, 0x19, 0x31, 0xcb, 0x13, 0xb2, 0xaf, 0xc2, 0x98, 0x47, 0x75,
- 0xb3, 0xa9, 0x07, 0xc6, 0x0e, 0x0f, 0xf5, 0x2e, 0xf0, 0xd8, 0xec, 0x33, 0x87, 0x07, 0xd3, 0x63,
- 0x18, 0x27, 0x60, 0x92, 0x8f, 0xe8, 0xd0, 0x60, 0x05, 0x1b, 0x56, 0x87, 0xba, 0xbd, 0x60, 0x48,
- 0xab, 0x29, 0xdf, 0xb0, 0x60, 0x04, 0x83, 0x71, 0x4c, 0xed, 0xe3, 0x02, 0x8c, 0xc7, 0x1b, 0x7c,
- 0xea, 0x16, 0xb5, 0x9d, 0xa4, 0x45, 0x6d, 0xe1, 0x04, 0xde, 0xc9, 0x00, 0x2b, 0xda, 0x07, 0x10,
- 0x7f, 0x34, 0x6e, 0x39, 0x8b, 0x1b, 0x0b, 0x0a, 0x0f, 0x35, 0x16, 0x7c, 0xfa, 0xd3, 0xe8, 0x0c,
- 0xd2, 0x72, 0xcb, 0x4f, 0xb0, 0x96, 0xfb, 0x49, 0xe6, 0xe2, 0x89, 0xe5, 0x93, 0x19, 0xc9, 0x91,
- 0x4f, 0xa6, 0x13, 0xe6, 0x93, 0xa9, 0x9e, 0xd8, 0xa4, 0x73, 0x94, 0x9c, 0x32, 0xb5, 0xc7, 0x9a,
- 0x53, 0xa6, 0x7e, 0x5a, 0x39, 0x65, 0x20, 0x6f, 0x4e, 0x99, 0x0f, 0x0a, 0x30, 0x6e, 0x26, 0x4e,
- 0xcc, 0x72, 0xdb, 0x42, 0x9e, 0xa5, 0x26, 0x79, 0x00, 0x57, 0x1c, 0x99, 0x4a, 0x96, 0x61, 0x4a,
- 0x64, 0x56, 0x26, 0x97, 0xd1, 0x4f, 0x26, 0x93, 0xcb, 0x2f, 0xaa, 0xf1, 0x15, 0xe9, 0x71, 0x1b,
- 0xcd, 0x5f, 0x49, 0x1a, 0xcd, 0xaf, 0xa6, 0x8d, 0xe6, 0x13, 0xb1, 0x78, 0xd6, 0xb8, 0xe1, 0xfc,
- 0x77, 0x63, 0x13, 0x75, 0x89, 0xe7, 0x70, 0x09, 0xdf, 0x79, 0xc6, 0x64, 0x3d, 0x0f, 0x13, 0x52,
- 0x7b, 0x55, 0x44, 0x3e, 0xcb, 0x8d, 0x45, 0x61, 0x4e, 0x8b, 0x49, 0x32, 0xa6, 0xf9, 0x99, 0x40,
- 0x5f, 0xa5, 0xf2, 0x14, 0x5b, 0x85, 0x68, 0x90, 0xa9, 0x34, 0x9b, 0x21, 0x07, 0xdb, 0x56, 0x78,
- 0x54, 0xf7, 0xa5, 0xe9, 0x3b, 0xb6, 0xad, 0x40, 0x5e, 0x8a, 0x92, 0x1a, 0xb7, 0xff, 0x57, 0x1f,
- 0x61, 0xff, 0xd7, 0xa1, 0x61, 0xeb, 0x7e, 0x20, 0xde, 0xa6, 0x29, 0x3f, 0xe7, 0x3f, 0x71, 0xb4,
- 0x85, 0x97, 0x2d, 0xe6, 0x91, 0x76, 0xbb, 0x1a, 0xc1, 0x60, 0x1c, 0x93, 0x98, 0x30, 0xca, 0x6e,
- 0xf9, 0xa7, 0x6d, 0xce, 0x07, 0x32, 0xe1, 0xd5, 0x71, 0x64, 0x84, 0x66, 0xab, 0xd5, 0x18, 0x0e,
- 0x26, 0x50, 0x07, 0xb8, 0x08, 0x60, 0x18, 0x17, 0x01, 0xf9, 0x43, 0xa1, 0x39, 0xed, 0x87, 0xaf,
- 0xb5, 0xc1, 0x5f, 0x6b, 0x18, 0x22, 0x89, 0x71, 0x22, 0x26, 0x79, 0xd9, 0xa8, 0xe8, 0xc9, 0x6e,
- 0x50, 0xd5, 0x47, 0x93, 0xa3, 0x62, 0x33, 0x49, 0xc6, 0x34, 0x3f, 0x59, 0x87, 0x73, 0x61, 0x51,
- 0xbc, 0x19, 0x63, 0x1c, 0x27, 0x8c, 0x59, 0xdb, 0xcc, 0xe0, 0xc1, 0xcc, 0x9a, 0xfc, 0x10, 0x48,
- 0xcf, 0xf3, 0xa8, 0x13, 0xdc, 0xd0, 0xfd, 0x1d, 0x19, 0xfc, 0x16, 0x1d, 0x02, 0x89, 0x48, 0x18,
- 0xe7, 0x23, 0x73, 0x00, 0x02, 0x8e, 0xd7, 0x9a, 0x48, 0xc6, 0x97, 0x6e, 0x86, 0x14, 0x8c, 0x71,
- 0x69, 0x1f, 0xd4, 0xa1, 0x71, 0x5b, 0x0f, 0xac, 0x3d, 0xca, 0xfd, 0x79, 0xa7, 0xe3, 0x54, 0xf9,
- 0xeb, 0x05, 0xb8, 0x90, 0x0c, 0xda, 0x3c, 0x45, 0xcf, 0x0a, 0x4f, 0x01, 0x83, 0x99, 0xd2, 0x70,
- 0x40, 0x2b, 0xb8, 0x8f, 0xa5, 0x2f, 0x06, 0xf4, 0xb4, 0x7d, 0x2c, 0xad, 0x41, 0x02, 0x71, 0x70,
- 0x5b, 0x3e, 0x2d, 0x3e, 0x96, 0x27, 0x3b, 0x67, 0x61, 0xca, 0x03, 0x54, 0x7d, 0x62, 0x3c, 0x40,
- 0xb5, 0x27, 0x42, 0xed, 0xee, 0xc6, 0x3c, 0x40, 0xf5, 0x9c, 0x91, 0x48, 0xf2, 0x9c, 0x83, 0x40,
- 0x1b, 0xe4, 0x49, 0xe2, 0x29, 0x0a, 0x94, 0x65, 0x9e, 0x69, 0xab, 0x5b, 0xba, 0x6f, 0x19, 0x52,
- 0xed, 0xc8, 0x91, 0xa3, 0x55, 0xe5, 0x6e, 0x13, 0x01, 0x0b, 0xfc, 0x16, 0x05, 0x76, 0x94, 0xaa,
- 0xae, 0x98, 0x2b, 0x55, 0x1d, 0x59, 0x80, 0xb2, 0xb3, 0x4b, 0xf7, 0x8f, 0x77, 0xd8, 0x9f, 0xef,
- 0xc2, 0x6e, 0xdf, 0xa2, 0xfb, 0xc8, 0x2b, 0x6b, 0x3f, 0x28, 0x02, 0xb0, 0xc7, 0x3f, 0x9a, 0x2f,
- 0xe6, 0xb7, 0xa1, 0xea, 0xf7, 0xb8, 0xd5, 0x44, 0x2a, 0x4c, 0x51, 0xf8, 0x96, 0x28, 0x46, 0x45,
- 0x27, 0xcf, 0x41, 0xe5, 0xbd, 0x1e, 0xed, 0xa9, 0xc0, 0x82, 0x50, 0x71, 0xff, 0x22, 0x2b, 0x44,
- 0x41, 0x3b, 0x3d, 0xbb, 0xaa, 0xf2, 0xd9, 0x54, 0x4e, 0xcb, 0x67, 0x53, 0x87, 0xea, 0x6d, 0x97,
- 0x47, 0x83, 0x6a, 0xff, 0xb5, 0x08, 0x10, 0x45, 0xdb, 0x91, 0xef, 0x14, 0xe0, 0x7c, 0xf8, 0xc1,
- 0x05, 0x62, 0xff, 0xc5, 0xd3, 0x22, 0xe7, 0xf6, 0xdf, 0x64, 0x7d, 0xec, 0x7c, 0x06, 0x5a, 0xcf,
- 0x12, 0x87, 0xd9, 0xad, 0x20, 0x08, 0x35, 0xda, 0xe9, 0x06, 0xfb, 0x8b, 0x96, 0x27, 0x47, 0x60,
- 0x66, 0x50, 0xe7, 0x75, 0xc9, 0x23, 0xaa, 0x4a, 0x23, 0x01, 0xff, 0x88, 0x14, 0x05, 0x43, 0x1c,
- 0xb2, 0x03, 0x35, 0xc7, 0x7d, 0xc7, 0x67, 0xdd, 0x21, 0x87, 0xe3, 0x1b, 0xc3, 0x77, 0xb9, 0xe8,
- 0x56, 0x61, 0xef, 0x97, 0x37, 0x58, 0x75, 0x64, 0x67, 0x7f, 0xbb, 0x08, 0x67, 0x33, 0xfa, 0x81,
- 0xbc, 0x01, 0x93, 0x32, 0xb0, 0x31, 0xca, 0x0f, 0x5e, 0x88, 0xf2, 0x83, 0xb7, 0x52, 0x34, 0xec,
- 0xe3, 0x26, 0xef, 0x00, 0xe8, 0x86, 0x41, 0x7d, 0x7f, 0xcd, 0x35, 0xd5, 0x7e, 0xe0, 0x75, 0xa6,
- 0xbe, 0xcc, 0x87, 0xa5, 0x0f, 0x0e, 0xa6, 0x7f, 0x2f, 0x2b, 0x56, 0x39, 0xd5, 0xcf, 0x51, 0x05,
- 0x8c, 0x41, 0x92, 0xaf, 0x00, 0x88, 0x4d, 0x78, 0x98, 0x4e, 0xe1, 0x11, 0x96, 0xab, 0x19, 0x95,
- 0xb8, 0x6a, 0xe6, 0x8b, 0x3d, 0xdd, 0x09, 0xac, 0x60, 0x5f, 0x64, 0xaf, 0xb9, 0x1b, 0xa2, 0x60,
- 0x0c, 0x51, 0xfb, 0xa7, 0x45, 0xa8, 0x29, 0x9b, 0xf9, 0x63, 0x30, 0x94, 0xb6, 0x13, 0x86, 0xd2,
- 0x13, 0x8a, 0x4e, 0xce, 0x32, 0x93, 0xba, 0x29, 0x33, 0xe9, 0x72, 0x7e, 0x51, 0x0f, 0x37, 0x92,
- 0x7e, 0xbf, 0x08, 0xe3, 0x8a, 0x35, 0xaf, 0x89, 0xf4, 0xf3, 0x30, 0x21, 0xa2, 0x0a, 0xd6, 0xf4,
- 0xfb, 0x22, 0x91, 0x0f, 0xef, 0xb0, 0xb2, 0x08, 0x08, 0x6e, 0x26, 0x49, 0x98, 0xe6, 0x65, 0xc3,
- 0x5a, 0x14, 0x6d, 0xb2, 0x4d, 0x98, 0xf0, 0x43, 0x8a, 0xfd, 0x26, 0x1f, 0xd6, 0xcd, 0x14, 0x0d,
- 0xfb, 0xb8, 0xd3, 0x36, 0xda, 0xf2, 0x29, 0xd8, 0x68, 0xff, 0x5d, 0x01, 0x46, 0xa3, 0xfe, 0x3a,
- 0x75, 0x0b, 0xed, 0x76, 0xd2, 0x42, 0x3b, 0x9f, 0x7b, 0x38, 0x0c, 0xb0, 0xcf, 0xfe, 0xe5, 0x2a,
- 0x24, 0x82, 0xe4, 0xc9, 0x16, 0x5c, 0xb2, 0x32, 0x43, 0xfd, 0x62, 0xb3, 0x4d, 0x78, 0xea, 0x7b,
- 0x65, 0x20, 0x27, 0x3e, 0x04, 0x85, 0xf4, 0xa0, 0xb6, 0x47, 0xbd, 0xc0, 0x32, 0xa8, 0x7a, 0xbe,
- 0xe5, 0xdc, 0x2a, 0x99, 0xb4, 0x42, 0x87, 0x7d, 0x7a, 0x57, 0x0a, 0xc0, 0x50, 0x14, 0xd9, 0x82,
- 0x0a, 0x35, 0xdb, 0x54, 0xa5, 0x56, 0xca, 0x99, 0xb8, 0x34, 0xec, 0x4f, 0x76, 0xe7, 0xa3, 0x80,
- 0x26, 0x3e, 0xd4, 0x6d, 0xe5, 0x65, 0x94, 0xe3, 0x70, 0x78, 0x05, 0x2b, 0xf4, 0x57, 0x46, 0x59,
- 0x17, 0xc2, 0x22, 0x8c, 0xe4, 0x90, 0xdd, 0xd0, 0xdc, 0x59, 0x39, 0xa1, 0xc9, 0xe3, 0x21, 0xc6,
- 0x4e, 0x1f, 0xea, 0xf7, 0xf4, 0x80, 0x7a, 0x1d, 0xdd, 0xdb, 0x95, 0xbb, 0x8d, 0xe1, 0x9f, 0xf0,
- 0x4d, 0x85, 0x14, 0x3d, 0x61, 0x58, 0x84, 0x91, 0x1c, 0xe2, 0x42, 0x3d, 0x90, 0xea, 0xb3, 0xb2,
- 0xe9, 0x0e, 0x2f, 0x54, 0x29, 0xe2, 0xbe, 0x0c, 0x96, 0x57, 0xb7, 0x18, 0xc9, 0x20, 0x7b, 0x89,
- 0x2c, 0xd7, 0x22, 0xb7, 0x79, 0x33, 0x87, 0x6f, 0x40, 0x42, 0x45, 0xcb, 0x4d, 0x76, 0xb6, 0x6c,
- 0xed, 0x7f, 0x56, 0xa2, 0x69, 0xf9, 0x71, 0xdb, 0x09, 0x5f, 0x4a, 0xda, 0x09, 0xaf, 0xa4, 0xed,
- 0x84, 0x29, 0x67, 0xf5, 0xf1, 0xc3, 0x6b, 0x53, 0xe6, 0xb5, 0xf2, 0x29, 0x98, 0xd7, 0x5e, 0x84,
- 0xc6, 0x1e, 0x9f, 0x09, 0x44, 0x9e, 0xa6, 0x0a, 0x5f, 0x46, 0xf8, 0xcc, 0x7e, 0x37, 0x2a, 0xc6,
- 0x38, 0x0f, 0xab, 0x22, 0xff, 0xeb, 0x11, 0x26, 0xba, 0x95, 0x55, 0x5a, 0x51, 0x31, 0xc6, 0x79,
- 0x78, 0x64, 0x9e, 0xe5, 0xec, 0x8a, 0x0a, 0x55, 0x5e, 0x41, 0x44, 0xe6, 0xa9, 0x42, 0x8c, 0xe8,
- 0xe4, 0x1a, 0xd4, 0x7a, 0xe6, 0xb6, 0xe0, 0xad, 0x71, 0x5e, 0xae, 0x61, 0x6e, 0x2e, 0x2e, 0xc9,
- 0xbc, 0x51, 0x8a, 0xca, 0x5a, 0xd2, 0xd1, 0xbb, 0x8a, 0xc0, 0xf7, 0x86, 0xb2, 0x25, 0x6b, 0x51,
- 0x31, 0xc6, 0x79, 0xc8, 0x1f, 0xc0, 0xb8, 0x47, 0xcd, 0x9e, 0x41, 0xc3, 0x5a, 0xc0, 0x6b, 0x11,
- 0xf1, 0x03, 0x93, 0x38, 0x05, 0x53, 0x9c, 0x03, 0x8c, 0x84, 0x8d, 0xa1, 0x8c, 0x84, 0x5f, 0x80,
- 0x71, 0xd3, 0xd3, 0x2d, 0x87, 0x9a, 0x77, 0x1c, 0x1e, 0x91, 0x20, 0xe3, 0x03, 0x43, 0x0b, 0xf9,
- 0x62, 0x82, 0x8a, 0x29, 0x6e, 0xed, 0xbf, 0x17, 0x80, 0xf4, 0x47, 0xc2, 0x93, 0x1d, 0x18, 0x71,
- 0xb8, 0xf5, 0x2c, 0x77, 0x6a, 0xed, 0x98, 0x11, 0x4e, 0x4c, 0x6b, 0xb2, 0x40, 0xe2, 0x13, 0x07,
- 0x6a, 0xf4, 0x7e, 0x40, 0x3d, 0x27, 0x3c, 0x19, 0x73, 0x32, 0x69, 0xbc, 0xc5, 0x6e, 0x42, 0x22,
- 0x63, 0x28, 0x43, 0xfb, 0x79, 0x11, 0x1a, 0x31, 0xbe, 0x47, 0x6d, 0x4a, 0xf9, 0xe1, 0x7c, 0x61,
- 0xb4, 0xda, 0xf4, 0x6c, 0xf9, 0x85, 0xc6, 0x0e, 0xe7, 0x4b, 0x12, 0xae, 0x62, 0x9c, 0x8f, 0xcc,
- 0x01, 0x74, 0x74, 0x3f, 0xa0, 0x1e, 0x5f, 0xbd, 0x53, 0x47, 0xe2, 0xd7, 0x42, 0x0a, 0xc6, 0xb8,
- 0xc8, 0x55, 0x99, 0x88, 0xbd, 0x9c, 0x4c, 0x61, 0x38, 0x20, 0xcb, 0x7a, 0xe5, 0x04, 0xb2, 0xac,
- 0x93, 0x36, 0x4c, 0xaa, 0x56, 0x2b, 0xea, 0xf1, 0x12, 0xdc, 0x89, 0xfd, 0x4f, 0x0a, 0x02, 0xfb,
- 0x40, 0xb5, 0x1f, 0x14, 0x60, 0x2c, 0x61, 0x32, 0x11, 0xc9, 0x07, 0xd5, 0x39, 0x8e, 0x44, 0xf2,
- 0xc1, 0xd8, 0xf1, 0x8b, 0x17, 0x60, 0x44, 0x74, 0x50, 0x3a, 0x3c, 0x53, 0x74, 0x21, 0x4a, 0x2a,
- 0x9b, 0x0b, 0xa5, 0x51, 0x36, 0x3d, 0x17, 0x4a, 0xab, 0x2d, 0x2a, 0xba, 0xf0, 0x75, 0x88, 0xd6,
- 0xc9, 0x9e, 0x8e, 0xf9, 0x3a, 0x44, 0x39, 0x86, 0x1c, 0xda, 0x0f, 0x79, 0xbb, 0x03, 0x6f, 0x3f,
- 0xdc, 0x0b, 0xb6, 0xa1, 0x2a, 0x43, 0xf2, 0xe4, 0xa7, 0xf1, 0x46, 0x0e, 0x3b, 0x0e, 0xc7, 0x91,
- 0xc1, 0x67, 0xba, 0xb1, 0x7b, 0x67, 0x7b, 0x1b, 0x15, 0x3a, 0xb9, 0x0e, 0x75, 0xd7, 0x59, 0xd2,
- 0x2d, 0xbb, 0xe7, 0xa9, 0x95, 0xe1, 0xb7, 0xd8, 0x5c, 0x77, 0x47, 0x15, 0x3e, 0x38, 0x98, 0xbe,
- 0x10, 0xde, 0x24, 0x1a, 0x89, 0x51, 0x4d, 0xed, 0xcf, 0x17, 0xe0, 0x3c, 0xba, 0xb6, 0x6d, 0x39,
- 0xed, 0xa4, 0xb3, 0x8c, 0xd8, 0x30, 0xde, 0xd1, 0xef, 0x6f, 0x3a, 0xfa, 0x9e, 0x6e, 0xd9, 0xfa,
- 0x96, 0x4d, 0x1f, 0xb9, 0x97, 0xeb, 0x05, 0x96, 0x3d, 0x23, 0x7e, 0x4c, 0x37, 0xb3, 0xe2, 0x04,
- 0x77, 0xbc, 0x56, 0xe0, 0x59, 0x4e, 0x5b, 0x4c, 0x7a, 0x6b, 0x09, 0x2c, 0x4c, 0x61, 0x6b, 0xbf,
- 0x28, 0x01, 0x0f, 0x0b, 0x23, 0xaf, 0x42, 0xbd, 0x43, 0x8d, 0x1d, 0xdd, 0xb1, 0x7c, 0x95, 0xc6,
- 0xf5, 0x22, 0x7b, 0xae, 0x35, 0x55, 0xf8, 0x80, 0xbd, 0x8a, 0xf9, 0xd6, 0x2a, 0x3f, 0x79, 0x11,
- 0xf1, 0x12, 0x03, 0x46, 0xda, 0xbe, 0xaf, 0x77, 0xad, 0xdc, 0x51, 0x09, 0x22, 0x6d, 0xa6, 0x98,
- 0x8e, 0xc4, 0x35, 0x4a, 0x68, 0x62, 0x40, 0xa5, 0x6b, 0xeb, 0x96, 0x93, 0xfb, 0x47, 0x4a, 0xec,
- 0x09, 0xd6, 0x19, 0x92, 0x30, 0xae, 0xf1, 0x4b, 0x14, 0xd8, 0xa4, 0x07, 0x0d, 0xdf, 0xf0, 0xf4,
- 0x8e, 0xbf, 0xa3, 0xcf, 0xbd, 0xfc, 0x4a, 0x6e, 0x75, 0x35, 0x12, 0x25, 0x56, 0xcf, 0x05, 0x9c,
- 0x5f, 0x6b, 0xdd, 0x98, 0x9f, 0x7b, 0xf9, 0x15, 0x8c, 0xcb, 0x89, 0x8b, 0x7d, 0xf9, 0xc5, 0x39,
- 0x39, 0x83, 0x9c, 0xb8, 0xd8, 0x97, 0x5f, 0x9c, 0xc3, 0xb8, 0x1c, 0xed, 0x7f, 0x15, 0xa0, 0x1e,
- 0xf2, 0x92, 0x4d, 0x00, 0x36, 0x97, 0xc9, 0x44, 0x97, 0xc7, 0xfa, 0xe9, 0x04, 0xb7, 0x4f, 0x6c,
- 0x86, 0x95, 0x31, 0x06, 0x94, 0x91, 0x09, 0xb4, 0x78, 0xd2, 0x99, 0x40, 0x67, 0xa1, 0xbe, 0xa3,
- 0x3b, 0xa6, 0xbf, 0xa3, 0xef, 0x8a, 0x29, 0x3d, 0x96, 0x1b, 0xf7, 0x86, 0x22, 0x60, 0xc4, 0xa3,
- 0xfd, 0xe3, 0x11, 0x10, 0xa1, 0x04, 0x6c, 0xd2, 0x31, 0x2d, 0x5f, 0xc4, 0xb2, 0x17, 0x78, 0xcd,
- 0x70, 0xd2, 0x59, 0x94, 0xe5, 0x18, 0x72, 0x90, 0x8b, 0x50, 0xea, 0x58, 0x8e, 0xf4, 0x3d, 0x71,
- 0xd3, 0xe3, 0x9a, 0xe5, 0x20, 0x2b, 0xe3, 0x24, 0xfd, 0xbe, 0x0c, 0x43, 0x14, 0x24, 0xfd, 0x3e,
- 0xb2, 0x32, 0xf2, 0x79, 0x98, 0xb0, 0x5d, 0x77, 0x97, 0x4d, 0x1f, 0x2a, 0x5a, 0x51, 0xf8, 0x81,
- 0xb9, 0x31, 0x60, 0x35, 0x49, 0xc2, 0x34, 0x2f, 0xd9, 0x84, 0xa7, 0xdf, 0xa7, 0x9e, 0x2b, 0xe7,
- 0xcb, 0x96, 0x4d, 0x69, 0x57, 0xc1, 0x08, 0x65, 0x8e, 0x07, 0x3d, 0xfe, 0x71, 0x36, 0x0b, 0x0e,
- 0xaa, 0xcb, 0xc3, 0xa7, 0x75, 0xaf, 0x4d, 0x83, 0x75, 0xcf, 0x35, 0xa8, 0xef, 0x5b, 0x4e, 0x5b,
- 0xc1, 0x8e, 0x44, 0xb0, 0x1b, 0xd9, 0x2c, 0x38, 0xa8, 0x2e, 0x79, 0x0b, 0xa6, 0x04, 0x49, 0xa8,
- 0x2d, 0xf3, 0x62, 0x9a, 0xb1, 0x6c, 0xf5, 0xff, 0xc1, 0x31, 0xe1, 0xe1, 0xd9, 0x18, 0xc0, 0x83,
- 0x03, 0x6b, 0x93, 0x9b, 0x30, 0xa9, 0xfc, 0x7b, 0xeb, 0xd4, 0x6b, 0x85, 0xe1, 0x25, 0x63, 0xcd,
- 0x2b, 0x6c, 0xe7, 0xbd, 0x48, 0xbb, 0x1e, 0x35, 0xe2, 0x7e, 0x52, 0xc5, 0x85, 0x7d, 0xf5, 0x08,
- 0xc2, 0x05, 0x1e, 0x43, 0xb2, 0xd9, 0x5d, 0x70, 0x5d, 0xdb, 0x74, 0xef, 0x39, 0xea, 0xd9, 0x85,
- 0x8a, 0xc9, 0x5d, 0x7a, 0xad, 0x4c, 0x0e, 0x1c, 0x50, 0x93, 0x3d, 0x39, 0xa7, 0x2c, 0xba, 0xf7,
- 0x9c, 0x34, 0x2a, 0x44, 0x4f, 0xde, 0x1a, 0xc0, 0x83, 0x03, 0x6b, 0x93, 0x25, 0x20, 0xe9, 0x27,
- 0xd8, 0xec, 0x4a, 0xa7, 0xf3, 0x05, 0x91, 0xb3, 0x26, 0x4d, 0xc5, 0x8c, 0x1a, 0x64, 0x15, 0xce,
- 0xa5, 0x4b, 0x99, 0x38, 0xe9, 0x7f, 0xe6, 0xd9, 0x6a, 0x31, 0x83, 0x8e, 0x99, 0xb5, 0xb4, 0x7f,
- 0x52, 0x84, 0xb1, 0x44, 0x92, 0x83, 0x27, 0xee, 0x30, 0x39, 0xdb, 0x0b, 0x74, 0xfc, 0xf6, 0xca,
- 0xe2, 0x0d, 0xaa, 0x9b, 0xd4, 0xbb, 0x45, 0x55, 0x42, 0x0a, 0xb1, 0x2c, 0x26, 0x28, 0x98, 0xe2,
- 0x24, 0xdb, 0x50, 0x11, 0x96, 0xed, 0xbc, 0xbf, 0x2f, 0x51, 0x7d, 0xc4, 0xcd, 0xdb, 0xf2, 0x9f,
- 0x3f, 0xae, 0x47, 0x51, 0xc0, 0x6b, 0x01, 0x8c, 0xc6, 0x39, 0xd8, 0x44, 0x12, 0xa9, 0xbd, 0xd5,
- 0x84, 0xca, 0xbb, 0x02, 0xa5, 0x20, 0x18, 0xf6, 0x98, 0xba, 0xf0, 0x94, 0x6c, 0xac, 0x22, 0xc3,
- 0xd0, 0xb6, 0xd9, 0xbb, 0xf3, 0x7d, 0xcb, 0x75, 0x64, 0xce, 0xf2, 0x4d, 0xa8, 0x06, 0xd2, 0x58,
- 0x38, 0xdc, 0x31, 0x7b, 0xae, 0x2b, 0x29, 0x43, 0xa1, 0xc2, 0xd2, 0xfe, 0x7d, 0x11, 0xea, 0xe1,
- 0xc6, 0xfe, 0x08, 0xb9, 0xc0, 0x5d, 0xa8, 0x87, 0x31, 0x70, 0xb9, 0xff, 0xcd, 0x18, 0x85, 0x66,
- 0xf1, 0xbd, 0x68, 0x78, 0x8b, 0x91, 0x8c, 0x78, 0x7c, 0x5d, 0x29, 0x47, 0x7c, 0x5d, 0x17, 0xaa,
- 0x81, 0x67, 0xb5, 0xdb, 0x72, 0x97, 0x90, 0x27, 0xc0, 0x2e, 0xec, 0xae, 0x0d, 0x01, 0x28, 0x7b,
- 0x56, 0xdc, 0xa0, 0x12, 0xa3, 0xbd, 0x0b, 0x93, 0x69, 0x4e, 0xae, 0x42, 0x1b, 0x3b, 0xd4, 0xec,
- 0xd9, 0xaa, 0x8f, 0x23, 0x15, 0x5a, 0x96, 0x63, 0xc8, 0xc1, 0xb6, 0xe1, 0xec, 0x35, 0xbd, 0xef,
- 0x3a, 0x4a, 0x8d, 0xe5, 0xbb, 0x91, 0x0d, 0x59, 0x86, 0x21, 0x55, 0xfb, 0x2f, 0x25, 0xb8, 0x18,
- 0x99, 0x67, 0xd6, 0x74, 0x47, 0x6f, 0x1f, 0xe1, 0x87, 0x7c, 0x9f, 0x1d, 0x5c, 0x3a, 0xee, 0x0f,
- 0x1d, 0x4a, 0x4f, 0xc0, 0x0f, 0x1d, 0xfe, 0x4f, 0x11, 0x78, 0xbc, 0x2e, 0xf9, 0x1a, 0x8c, 0xea,
- 0xb1, 0x7f, 0xb1, 0xca, 0xd7, 0x79, 0x3d, 0xf7, 0xeb, 0xe4, 0x61, 0xc1, 0x61, 0xc8, 0x56, 0xbc,
- 0x14, 0x13, 0x02, 0x89, 0x0b, 0xb5, 0x6d, 0xdd, 0xb6, 0x99, 0x2e, 0x94, 0xdb, 0xdd, 0x94, 0x10,
- 0xce, 0x87, 0xf9, 0x92, 0x84, 0xc6, 0x50, 0x08, 0xf9, 0xa0, 0x00, 0x63, 0x5e, 0x7c, 0xbb, 0x26,
- 0x5f, 0x48, 0x9e, 0x60, 0x84, 0x18, 0x5a, 0x3c, 0x40, 0x2c, 0xbe, 0x27, 0x4c, 0xca, 0xd4, 0xfe,
- 0x73, 0x01, 0xc6, 0x5a, 0xb6, 0x65, 0x5a, 0x4e, 0xfb, 0x14, 0xff, 0x27, 0x71, 0x07, 0x2a, 0xbe,
- 0x6d, 0x99, 0x74, 0xc8, 0xd5, 0x44, 0xac, 0x63, 0x0c, 0x00, 0x05, 0x4e, 0xf2, 0x07, 0x15, 0xa5,
- 0x23, 0xfc, 0xa0, 0xe2, 0x57, 0x23, 0x20, 0x23, 0xcf, 0x49, 0x0f, 0xea, 0x6d, 0x95, 0xf7, 0x5e,
- 0x3e, 0xe3, 0x8d, 0x1c, 0x39, 0x13, 0x13, 0x19, 0xf4, 0xc5, 0xdc, 0x1f, 0x16, 0x62, 0x24, 0x89,
- 0xd0, 0xe4, 0x4f, 0x80, 0x17, 0x73, 0xfe, 0x04, 0x58, 0x88, 0xeb, 0xff, 0x0d, 0xb0, 0x0e, 0xe5,
- 0x9d, 0x20, 0xe8, 0xca, 0xc1, 0x34, 0xfc, 0xd1, 0x82, 0x28, 0x6d, 0x8f, 0xd0, 0x89, 0xd8, 0x3d,
- 0x72, 0x68, 0x26, 0xc2, 0xd1, 0xc3, 0x5f, 0xad, 0x2d, 0xe4, 0x0a, 0x7c, 0x88, 0x8b, 0x60, 0xf7,
- 0xc8, 0xa1, 0xc9, 0x57, 0xa1, 0x11, 0x78, 0xba, 0xe3, 0x6f, 0xbb, 0x5e, 0x87, 0x7a, 0x72, 0x8f,
- 0xba, 0x94, 0xe3, 0x3f, 0xb8, 0x1b, 0x11, 0x9a, 0xf0, 0xa8, 0x26, 0x8a, 0x30, 0x2e, 0x8d, 0xec,
- 0x42, 0xad, 0x67, 0x8a, 0x86, 0x49, 0x33, 0xd8, 0x7c, 0x9e, 0x5f, 0x1b, 0xc7, 0xc2, 0x1a, 0xd4,
- 0x1d, 0x86, 0x02, 0x92, 0x7f, 0x15, 0xac, 0x9e, 0xd4, 0x5f, 0x05, 0xe3, 0xa3, 0x31, 0x2b, 0xa7,
- 0x08, 0xe9, 0x48, 0xbd, 0xd6, 0x69, 0xcb, 0xa8, 0xac, 0xa5, 0xdc, 0x2a, 0xa7, 0x10, 0xd9, 0x08,
- 0x75, 0x63, 0xa7, 0x8d, 0x4a, 0x86, 0xd6, 0x01, 0xe9, 0xed, 0x20, 0x46, 0xe2, 0xdf, 0x3b, 0xe2,
- 0xa0, 0xdb, 0xec, 0xd1, 0xe6, 0x83, 0xf0, 0x27, 0x30, 0xb1, 0xdc, 0xdf, 0x99, 0x3f, 0xd9, 0xd1,
- 0xfe, 0x43, 0x11, 0x4a, 0x1b, 0xab, 0x2d, 0x91, 0xcf, 0x93, 0xff, 0xd8, 0x8a, 0xb6, 0x76, 0xad,
- 0xee, 0x5d, 0xea, 0x59, 0xdb, 0xfb, 0x72, 0xeb, 0x1d, 0xcb, 0xe7, 0x99, 0xe6, 0xc0, 0x8c, 0x5a,
- 0xe4, 0x6d, 0x18, 0x35, 0xf4, 0x05, 0xea, 0x05, 0xc3, 0x18, 0x16, 0xf8, 0x89, 0xde, 0x85, 0xf9,
- 0xa8, 0x3a, 0x26, 0xc0, 0xc8, 0x26, 0x80, 0x11, 0x41, 0x97, 0x8e, 0x6d, 0x0e, 0x89, 0x01, 0xc7,
- 0x80, 0x08, 0x42, 0x7d, 0x97, 0xb1, 0x72, 0xd4, 0xf2, 0x71, 0x50, 0xf9, 0xc8, 0xb9, 0xa5, 0xea,
- 0x62, 0x04, 0xa3, 0x39, 0x30, 0x96, 0xf8, 0x21, 0x0f, 0xf9, 0x1c, 0xd4, 0xdc, 0x6e, 0x6c, 0x3a,
- 0xad, 0xf3, 0xf8, 0xcf, 0xda, 0x1d, 0x59, 0xf6, 0xe0, 0x60, 0x7a, 0x6c, 0xd5, 0x6d, 0x5b, 0x86,
- 0x2a, 0xc0, 0x90, 0x9d, 0x68, 0x30, 0xc2, 0x8f, 0xe1, 0xa9, 0xdf, 0xf1, 0xf0, 0xb5, 0x83, 0xff,
- 0x31, 0xc3, 0x47, 0x49, 0xd1, 0xbe, 0x5e, 0x86, 0xc8, 0x47, 0x48, 0x7c, 0x18, 0x11, 0xc7, 0x0c,
- 0xe4, 0xcc, 0x7d, 0xaa, 0x27, 0x1a, 0xa4, 0x28, 0xd2, 0x86, 0xd2, 0xbb, 0xee, 0x56, 0xee, 0x89,
- 0x3b, 0x76, 0xfe, 0x5e, 0xd8, 0xca, 0x62, 0x05, 0xc8, 0x24, 0x90, 0xbf, 0x51, 0x80, 0x33, 0x7e,
- 0x5a, 0xf5, 0x95, 0xc3, 0x01, 0xf3, 0xeb, 0xf8, 0x69, 0x65, 0x5a, 0x06, 0xea, 0x0e, 0x22, 0x63,
- 0x7f, 0x5b, 0x58, 0xff, 0x0b, 0xe7, 0x9d, 0x1c, 0x4e, 0xcb, 0x39, 0x7f, 0x22, 0x99, 0xec, 0xff,
- 0x64, 0x19, 0x4a, 0x51, 0xda, 0x37, 0x8b, 0xd0, 0x88, 0xcd, 0xd6, 0xb9, 0xff, 0xf2, 0x74, 0x3f,
- 0xf5, 0x97, 0xa7, 0xf5, 0xe1, 0x7d, 0xd9, 0x51, 0xab, 0x4e, 0xfb, 0x47, 0x4f, 0xff, 0xbc, 0x08,
- 0xa5, 0xcd, 0xc5, 0xa5, 0xe4, 0xa6, 0xb5, 0xf0, 0x18, 0x36, 0xad, 0x3b, 0x50, 0xdd, 0xea, 0x59,
- 0x76, 0x60, 0x39, 0xb9, 0x33, 0x84, 0xa8, 0x9f, 0x62, 0x49, 0x5f, 0x87, 0x40, 0x45, 0x05, 0x4f,
- 0xda, 0x50, 0x6d, 0x8b, 0x14, 0x8d, 0xb9, 0x23, 0xfc, 0x64, 0xaa, 0x47, 0x21, 0x48, 0xde, 0xa0,
- 0x42, 0xd7, 0xf6, 0x61, 0x64, 0x73, 0x51, 0xaa, 0xfd, 0x8f, 0xb7, 0x37, 0xb5, 0xaf, 0x42, 0xa8,
- 0x05, 0x3c, 0x7e, 0xe1, 0xff, 0xad, 0x00, 0x49, 0xc5, 0xe7, 0xf1, 0x8f, 0xa6, 0xdd, 0xf4, 0x68,
- 0x5a, 0x3c, 0x89, 0x8f, 0x2f, 0x7b, 0x40, 0x69, 0xff, 0xb6, 0x00, 0xa9, 0xb3, 0x61, 0xe4, 0x15,
- 0x99, 0xed, 0x2b, 0x19, 0x4a, 0xa5, 0xb2, 0x7d, 0x91, 0x24, 0x77, 0x2c, 0xeb, 0xd7, 0x87, 0x6c,
- 0xbb, 0x16, 0x77, 0xa0, 0xc9, 0xe6, 0xdf, 0x1e, 0x7e, 0xbb, 0x96, 0xe5, 0x8e, 0x93, 0xe1, 0x7e,
- 0x71, 0x12, 0x26, 0xe5, 0x6a, 0xff, 0xa8, 0x08, 0x23, 0x8f, 0xed, 0xa8, 0x3a, 0x4d, 0x44, 0x60,
- 0x2e, 0xe4, 0x9c, 0xed, 0x07, 0xc6, 0x5f, 0x76, 0x52, 0xf1, 0x97, 0x79, 0xff, 0x4d, 0xfc, 0x88,
- 0xe8, 0xcb, 0x7f, 0x5d, 0x00, 0xb9, 0xd6, 0xac, 0x38, 0x7e, 0xa0, 0x3b, 0x06, 0x25, 0x46, 0xb8,
- 0xb0, 0xe5, 0x0d, 0xf3, 0x91, 0xa1, 0x70, 0x42, 0x97, 0xe1, 0xd7, 0x6a, 0x21, 0x23, 0xbf, 0x0b,
- 0xb5, 0x1d, 0xd7, 0x0f, 0xf8, 0xe2, 0x55, 0x4c, 0x9a, 0xcc, 0x6e, 0xc8, 0x72, 0x0c, 0x39, 0xd2,
- 0xee, 0xec, 0xca, 0x60, 0x77, 0xb6, 0xf6, 0xbd, 0x22, 0x8c, 0x7e, 0x5a, 0xce, 0xdb, 0x67, 0xc5,
- 0xab, 0x96, 0x72, 0xc6, 0xab, 0x96, 0x8f, 0x13, 0xaf, 0xaa, 0xfd, 0xa4, 0x00, 0xf0, 0xd8, 0x0e,
- 0xfb, 0x9b, 0xc9, 0x50, 0xd2, 0xdc, 0xe3, 0x2a, 0x3b, 0x90, 0xf4, 0x1f, 0x54, 0xd4, 0x23, 0xf1,
- 0x30, 0xd2, 0x0f, 0x0b, 0x30, 0xae, 0x27, 0x42, 0x33, 0x73, 0xeb, 0xcb, 0xa9, 0x48, 0xcf, 0x30,
- 0xb2, 0x28, 0x59, 0x8e, 0x29, 0xb1, 0xe4, 0xb5, 0x28, 0xd1, 0xf4, 0xed, 0x68, 0xd8, 0xf7, 0x65,
- 0x88, 0xe6, 0xba, 0x5b, 0x82, 0xf3, 0x11, 0xa1, 0xb0, 0xa5, 0x13, 0x09, 0x85, 0x8d, 0x1f, 0xf2,
- 0x2b, 0x3f, 0xf4, 0x90, 0xdf, 0x1e, 0xd4, 0xb7, 0x3d, 0xb7, 0xc3, 0xa3, 0x4d, 0xe5, 0x5f, 0x8d,
- 0xaf, 0xe7, 0x58, 0x28, 0xa3, 0xff, 0xf9, 0x47, 0x86, 0xab, 0x25, 0x85, 0x8f, 0x91, 0x28, 0x6e,
- 0xeb, 0x77, 0x85, 0xd4, 0x91, 0x93, 0x94, 0x1a, 0xce, 0x25, 0x1b, 0x02, 0x1d, 0x95, 0x98, 0x64,
- 0x84, 0x69, 0xf5, 0xf1, 0x44, 0x98, 0x6a, 0x7f, 0xa9, 0xaa, 0x26, 0xb0, 0x27, 0x2e, 0xa7, 0xe9,
- 0x67, 0x47, 0xb3, 0xdb, 0xb4, 0xef, 0xdc, 0x74, 0xed, 0x31, 0x9e, 0x9b, 0xae, 0x9f, 0xcc, 0xb9,
- 0x69, 0xc8, 0x77, 0x6e, 0xba, 0x71, 0x42, 0xe7, 0xa6, 0x47, 0x4f, 0xea, 0xdc, 0xf4, 0xd8, 0x50,
- 0xe7, 0xa6, 0xc7, 0x8f, 0x74, 0x6e, 0xfa, 0xa0, 0x04, 0xa9, 0xcd, 0xf8, 0x67, 0x8e, 0xb7, 0xff,
- 0xa7, 0x1c, 0x6f, 0x1f, 0x15, 0x21, 0x9a, 0x88, 0x8f, 0x19, 0x98, 0xf4, 0x16, 0xd4, 0x3a, 0xfa,
- 0xfd, 0x45, 0x6a, 0xeb, 0xfb, 0x79, 0x7e, 0x05, 0xbc, 0x26, 0x31, 0x30, 0x44, 0x23, 0x3e, 0x80,
- 0x15, 0xa6, 0xe3, 0xcf, 0xed, 0xc2, 0x88, 0x32, 0xfb, 0x0b, 0x23, 0x69, 0x74, 0x8f, 0x31, 0x31,
- 0xda, 0xbf, 0x2a, 0x82, 0xfc, 0x6f, 0x03, 0xa1, 0x50, 0xd9, 0xb6, 0xee, 0x53, 0x33, 0x77, 0xb8,
- 0x73, 0xec, 0x07, 0xed, 0xc2, 0x47, 0xc3, 0x0b, 0x50, 0xa0, 0x73, 0xe3, 0xbb, 0xf0, 0xb9, 0xc9,
- 0xfe, 0xcb, 0x61, 0x7c, 0x8f, 0xfb, 0xee, 0xa4, 0xf1, 0x5d, 0x14, 0xa1, 0x92, 0x21, 0x6c, 0xfd,
- 0x3c, 0xfc, 0x22, 0xb7, 0x8b, 0x31, 0x11, 0xc6, 0xa1, 0x6c, 0xfd, 0xbe, 0x48, 0x9c, 0x20, 0x65,
- 0x34, 0xbf, 0xfc, 0xe3, 0x9f, 0x5e, 0x79, 0xea, 0x27, 0x3f, 0xbd, 0xf2, 0xd4, 0xc7, 0x3f, 0xbd,
- 0xf2, 0xd4, 0xd7, 0x0f, 0xaf, 0x14, 0x7e, 0x7c, 0x78, 0xa5, 0xf0, 0x93, 0xc3, 0x2b, 0x85, 0x8f,
- 0x0f, 0xaf, 0x14, 0xfe, 0xe3, 0xe1, 0x95, 0xc2, 0x5f, 0xfd, 0x4f, 0x57, 0x9e, 0xfa, 0xe3, 0x57,
- 0xa3, 0x26, 0xcc, 0xaa, 0x26, 0xcc, 0x2a, 0x81, 0xb3, 0xdd, 0xdd, 0xf6, 0x2c, 0x6b, 0x42, 0x54,
- 0xa2, 0x9a, 0xf0, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xc4, 0xe2, 0xd0, 0x0e, 0xda, 0x9a, 0x00,
- 0x00,
+ // 8046 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0x57,
+ 0x76, 0x9e, 0xfa, 0xbf, 0xfb, 0x34, 0xff, 0x74, 0x67, 0x34, 0xe2, 0xcc, 0x4a, 0xd3, 0xe3, 0x5a,
+ 0xef, 0x7a, 0x1c, 0xdb, 0x64, 0x44, 0xaf, 0xb4, 0x5a, 0xdb, 0xbb, 0x12, 0x9b, 0x1c, 0x72, 0x38,
+ 0x43, 0xce, 0x70, 0x4f, 0x93, 0x23, 0xad, 0x15, 0xaf, 0x52, 0xac, 0xba, 0x6c, 0x96, 0x58, 0x5d,
+ 0xd5, 0x5b, 0x55, 0xcd, 0x19, 0xca, 0x09, 0xd6, 0xb6, 0x12, 0x68, 0x83, 0x24, 0x48, 0xe0, 0x27,
+ 0x03, 0x81, 0x13, 0x24, 0x08, 0xe0, 0x07, 0xc3, 0x79, 0x08, 0xb2, 0x79, 0x08, 0x90, 0x1f, 0x07,
+ 0x41, 0xb2, 0xf9, 0x5f, 0x04, 0x01, 0xb2, 0x79, 0x21, 0xb2, 0x0c, 0xf2, 0x90, 0x00, 0x0e, 0x8c,
+ 0x18, 0x89, 0x9d, 0x81, 0x11, 0x07, 0xf7, 0xaf, 0xfe, 0xba, 0x7a, 0x86, 0xec, 0x6a, 0x8e, 0x46,
+ 0x89, 0xde, 0xba, 0xef, 0x39, 0xf7, 0x3b, 0xb7, 0x6e, 0xdd, 0xba, 0xf7, 0xdc, 0x73, 0xce, 0x3d,
+ 0x17, 0xd6, 0xbb, 0x56, 0x70, 0x30, 0xd8, 0x5b, 0x30, 0xdc, 0xde, 0xa2, 0x33, 0xe8, 0xe9, 0x7d,
+ 0xcf, 0xfd, 0x80, 0xff, 0xd8, 0xb7, 0xdd, 0x87, 0x8b, 0xfd, 0xc3, 0xee, 0xa2, 0xde, 0xb7, 0xfc,
+ 0xa8, 0xe4, 0xe8, 0x35, 0xdd, 0xee, 0x1f, 0xe8, 0xaf, 0x2d, 0x76, 0xa9, 0x43, 0x3d, 0x3d, 0xa0,
+ 0xe6, 0x42, 0xdf, 0x73, 0x03, 0x97, 0x7c, 0x39, 0x02, 0x5a, 0x50, 0x40, 0x0b, 0xaa, 0xda, 0x42,
+ 0xff, 0xb0, 0xbb, 0xc0, 0x80, 0xa2, 0x12, 0x05, 0x74, 0xed, 0xa7, 0x62, 0x2d, 0xe8, 0xba, 0x5d,
+ 0x77, 0x91, 0xe3, 0xed, 0x0d, 0xf6, 0xf9, 0x3f, 0xfe, 0x87, 0xff, 0x12, 0x72, 0xae, 0x69, 0x87,
+ 0x6f, 0xfa, 0x0b, 0x96, 0xcb, 0x9a, 0xb5, 0x68, 0xb8, 0x1e, 0x5d, 0x3c, 0x1a, 0x6a, 0xcb, 0xb5,
+ 0x2f, 0x45, 0x3c, 0x3d, 0xdd, 0x38, 0xb0, 0x1c, 0xea, 0x1d, 0xab, 0x67, 0x59, 0xf4, 0xa8, 0xef,
+ 0x0e, 0x3c, 0x83, 0x9e, 0xab, 0x96, 0xbf, 0xd8, 0xa3, 0x81, 0x9e, 0x25, 0x6b, 0x71, 0x54, 0x2d,
+ 0x6f, 0xe0, 0x04, 0x56, 0x6f, 0x58, 0xcc, 0x1b, 0x4f, 0xab, 0xe0, 0x1b, 0x07, 0xb4, 0xa7, 0x0f,
+ 0xd5, 0xfb, 0xe9, 0x51, 0xf5, 0x06, 0x81, 0x65, 0x2f, 0x5a, 0x4e, 0xe0, 0x07, 0x5e, 0xba, 0x92,
+ 0xf6, 0xdb, 0x00, 0x97, 0x96, 0xf7, 0xfc, 0xc0, 0xd3, 0x8d, 0x60, 0xdb, 0x35, 0x77, 0x68, 0xaf,
+ 0x6f, 0xeb, 0x01, 0x25, 0x87, 0x50, 0x67, 0x0f, 0x64, 0xea, 0x81, 0x3e, 0x5f, 0xb8, 0x51, 0xb8,
+ 0xd9, 0x5c, 0x5a, 0x5e, 0x18, 0xf3, 0x05, 0x2e, 0x6c, 0x49, 0xa0, 0xf6, 0xd4, 0xe9, 0x49, 0xab,
+ 0xae, 0xfe, 0x61, 0x28, 0x80, 0xfc, 0x5a, 0x01, 0xa6, 0x1c, 0xd7, 0xa4, 0x1d, 0x6a, 0x53, 0x23,
+ 0x70, 0xbd, 0xf9, 0xe2, 0x8d, 0xd2, 0xcd, 0xe6, 0xd2, 0x37, 0xc7, 0x96, 0x98, 0xf1, 0x44, 0x0b,
+ 0xf7, 0x62, 0x02, 0x6e, 0x39, 0x81, 0x77, 0xdc, 0xbe, 0xfc, 0xbd, 0x93, 0xd6, 0x0b, 0xa7, 0x27,
+ 0xad, 0xa9, 0x38, 0x09, 0x13, 0x2d, 0x21, 0xbb, 0xd0, 0x0c, 0x5c, 0x9b, 0x75, 0x99, 0xe5, 0x3a,
+ 0xfe, 0x7c, 0x89, 0x37, 0xec, 0xfa, 0x82, 0xe8, 0x6a, 0x26, 0x7e, 0x81, 0x8d, 0xb1, 0x85, 0xa3,
+ 0xd7, 0x16, 0x76, 0x42, 0xb6, 0xf6, 0x25, 0x09, 0xdc, 0x8c, 0xca, 0x7c, 0x8c, 0xe3, 0x10, 0x0a,
+ 0xb3, 0x3e, 0x35, 0x06, 0x9e, 0x15, 0x1c, 0xaf, 0xb8, 0x4e, 0x40, 0x1f, 0x05, 0xf3, 0x65, 0xde,
+ 0xcb, 0x5f, 0xcc, 0x82, 0xde, 0x76, 0xcd, 0x4e, 0x92, 0xbb, 0x7d, 0xe9, 0xf4, 0xa4, 0x35, 0x9b,
+ 0x2a, 0xc4, 0x34, 0x26, 0x71, 0x60, 0xce, 0xea, 0xe9, 0x5d, 0xba, 0x3d, 0xb0, 0xed, 0x0e, 0x35,
+ 0x3c, 0x1a, 0xf8, 0xf3, 0x15, 0xfe, 0x08, 0x37, 0xb3, 0xe4, 0x6c, 0xba, 0x86, 0x6e, 0xdf, 0xdf,
+ 0xfb, 0x80, 0x1a, 0x01, 0xd2, 0x7d, 0xea, 0x51, 0xc7, 0xa0, 0xed, 0x79, 0xf9, 0x30, 0x73, 0x1b,
+ 0x29, 0x24, 0x1c, 0xc2, 0x26, 0xeb, 0xf0, 0x62, 0xdf, 0xb3, 0x5c, 0xde, 0x04, 0x5b, 0xf7, 0xfd,
+ 0x7b, 0x7a, 0x8f, 0xce, 0x57, 0x6f, 0x14, 0x6e, 0x36, 0xda, 0x57, 0x25, 0xcc, 0x8b, 0xdb, 0x69,
+ 0x06, 0x1c, 0xae, 0x43, 0x6e, 0x42, 0x5d, 0x15, 0xce, 0xd7, 0x6e, 0x14, 0x6e, 0x56, 0xc4, 0xd8,
+ 0x51, 0x75, 0x31, 0xa4, 0x92, 0x35, 0xa8, 0xeb, 0xfb, 0xfb, 0x96, 0xc3, 0x38, 0xeb, 0xbc, 0x0b,
+ 0x5f, 0xc9, 0x7a, 0xb4, 0x65, 0xc9, 0x23, 0x70, 0xd4, 0x3f, 0x0c, 0xeb, 0x92, 0x3b, 0x40, 0x7c,
+ 0xea, 0x1d, 0x59, 0x06, 0x5d, 0x36, 0x0c, 0x77, 0xe0, 0x04, 0xbc, 0xed, 0x0d, 0xde, 0xf6, 0x6b,
+ 0xb2, 0xed, 0xa4, 0x33, 0xc4, 0x81, 0x19, 0xb5, 0xc8, 0xdb, 0x30, 0x27, 0xbf, 0xd5, 0xa8, 0x17,
+ 0x80, 0x23, 0x5d, 0x66, 0x1d, 0x89, 0x29, 0x1a, 0x0e, 0x71, 0x13, 0x13, 0x5e, 0xd1, 0x07, 0x81,
+ 0xdb, 0x63, 0x90, 0x49, 0xa1, 0x3b, 0xee, 0x21, 0x75, 0xe6, 0x9b, 0x37, 0x0a, 0x37, 0xeb, 0xed,
+ 0x1b, 0xa7, 0x27, 0xad, 0x57, 0x96, 0x9f, 0xc0, 0x87, 0x4f, 0x44, 0x21, 0xf7, 0xa1, 0x61, 0x3a,
+ 0xfe, 0xb6, 0x6b, 0x5b, 0xc6, 0xf1, 0xfc, 0x14, 0x6f, 0xe0, 0x6b, 0xf2, 0x51, 0x1b, 0xab, 0xf7,
+ 0x3a, 0x82, 0xf0, 0xf8, 0xa4, 0xf5, 0xca, 0xf0, 0x94, 0xba, 0x10, 0xd2, 0x31, 0xc2, 0x20, 0x5b,
+ 0x1c, 0x70, 0xc5, 0x75, 0xf6, 0xad, 0xee, 0xfc, 0x34, 0x7f, 0x1b, 0x37, 0x46, 0x0c, 0xe8, 0xd5,
+ 0x7b, 0x1d, 0xc1, 0xd7, 0x9e, 0x96, 0xe2, 0xc4, 0x5f, 0x8c, 0x10, 0x88, 0x09, 0x33, 0x6a, 0x32,
+ 0x5e, 0xb1, 0x75, 0xab, 0xe7, 0xcf, 0xcf, 0xf0, 0xc1, 0xfb, 0xa3, 0x23, 0x30, 0x31, 0xce, 0xdc,
+ 0xbe, 0x22, 0x1f, 0x65, 0x26, 0x51, 0xec, 0x63, 0x0a, 0xf3, 0xda, 0x5b, 0xf0, 0xe2, 0xd0, 0xdc,
+ 0x40, 0xe6, 0xa0, 0x74, 0x48, 0x8f, 0xf9, 0xd4, 0xd7, 0x40, 0xf6, 0x93, 0x5c, 0x86, 0xca, 0x91,
+ 0x6e, 0x0f, 0xe8, 0x7c, 0x91, 0x97, 0x89, 0x3f, 0x3f, 0x53, 0x7c, 0xb3, 0xa0, 0xfd, 0x8d, 0x12,
+ 0x4c, 0xa9, 0x19, 0xa7, 0x63, 0x39, 0x87, 0xe4, 0x1d, 0x28, 0xd9, 0x6e, 0x57, 0xce, 0x9b, 0x3f,
+ 0x37, 0xf6, 0x2c, 0xb6, 0xe9, 0x76, 0xdb, 0xb5, 0xd3, 0x93, 0x56, 0x69, 0xd3, 0xed, 0x22, 0x43,
+ 0x24, 0x06, 0x54, 0x0e, 0xf5, 0xfd, 0x43, 0x9d, 0xb7, 0xa1, 0xb9, 0xd4, 0x1e, 0x1b, 0xfa, 0x2e,
+ 0x43, 0x61, 0x6d, 0x6d, 0x37, 0x4e, 0x4f, 0x5a, 0x15, 0xfe, 0x17, 0x05, 0x36, 0x71, 0xa1, 0xb1,
+ 0x67, 0xeb, 0xc6, 0xe1, 0x81, 0x6b, 0xd3, 0xf9, 0x52, 0x4e, 0x41, 0x6d, 0x85, 0x24, 0x5e, 0x73,
+ 0xf8, 0x17, 0x23, 0x19, 0xc4, 0x80, 0xea, 0xc0, 0xf4, 0x2d, 0xe7, 0x50, 0xce, 0x81, 0x6f, 0x8d,
+ 0x2d, 0x6d, 0x77, 0x95, 0x3f, 0x13, 0x9c, 0x9e, 0xb4, 0xaa, 0xe2, 0x37, 0x4a, 0x68, 0xed, 0x0f,
+ 0xa6, 0x60, 0x46, 0xbd, 0xa4, 0x07, 0xd4, 0x0b, 0xe8, 0x23, 0x72, 0x03, 0xca, 0x0e, 0xfb, 0x34,
+ 0xf9, 0x4b, 0x6e, 0x4f, 0xc9, 0xe1, 0x52, 0xe6, 0x9f, 0x24, 0xa7, 0xb0, 0x96, 0x89, 0xa1, 0x22,
+ 0x3b, 0x7c, 0xfc, 0x96, 0x75, 0x38, 0x8c, 0x68, 0x99, 0xf8, 0x8d, 0x12, 0x9a, 0xbc, 0x07, 0x65,
+ 0xfe, 0xf0, 0xa2, 0xab, 0xbf, 0x3a, 0xbe, 0x08, 0xf6, 0xe8, 0x75, 0xf6, 0x04, 0xfc, 0xc1, 0x39,
+ 0x28, 0x1b, 0x8a, 0x03, 0x73, 0x5f, 0x76, 0xec, 0xcf, 0xe5, 0xe8, 0xd8, 0x35, 0x31, 0x14, 0x77,
+ 0x57, 0xd7, 0x90, 0x21, 0x92, 0xbf, 0x54, 0x80, 0x17, 0x0d, 0xd7, 0x09, 0x74, 0xa6, 0x67, 0xa8,
+ 0x45, 0x76, 0xbe, 0xc2, 0xe5, 0xdc, 0x19, 0x5b, 0xce, 0x4a, 0x1a, 0xb1, 0xfd, 0x12, 0x5b, 0x33,
+ 0x86, 0x8a, 0x71, 0x58, 0x36, 0xf9, 0x2b, 0x05, 0x78, 0x89, 0xcd, 0xe5, 0x43, 0xcc, 0x7c, 0x05,
+ 0x9a, 0x6c, 0xab, 0xae, 0x9e, 0x9e, 0xb4, 0x5e, 0xda, 0xc8, 0x12, 0x86, 0xd9, 0x6d, 0x60, 0xad,
+ 0xbb, 0xa4, 0x0f, 0xab, 0x25, 0x7c, 0x75, 0x6b, 0x2e, 0x6d, 0x4e, 0x52, 0xd5, 0x69, 0x7f, 0x4e,
+ 0x0e, 0xe5, 0x2c, 0xcd, 0x0e, 0xb3, 0x5a, 0x41, 0x6e, 0x41, 0xed, 0xc8, 0xb5, 0x07, 0x3d, 0xea,
+ 0xcf, 0xd7, 0xf9, 0x14, 0x7b, 0x2d, 0x6b, 0x8a, 0x7d, 0xc0, 0x59, 0xda, 0xb3, 0x12, 0xbe, 0x26,
+ 0xfe, 0xfb, 0xa8, 0xea, 0x12, 0x0b, 0xaa, 0xb6, 0xd5, 0xb3, 0x02, 0x9f, 0x2f, 0x9c, 0xcd, 0xa5,
+ 0x5b, 0x63, 0x3f, 0x96, 0xf8, 0x44, 0x37, 0x39, 0x98, 0xf8, 0x6a, 0xc4, 0x6f, 0x94, 0x02, 0xd8,
+ 0x54, 0xe8, 0x1b, 0xba, 0x2d, 0x16, 0xd6, 0xe6, 0xd2, 0xd7, 0xc6, 0xff, 0x6c, 0x18, 0x4a, 0x7b,
+ 0x5a, 0x3e, 0x53, 0x85, 0xff, 0x45, 0x81, 0x4d, 0x7e, 0x01, 0x66, 0x12, 0x6f, 0xd3, 0x9f, 0x6f,
+ 0xf2, 0xde, 0x79, 0x35, 0xab, 0x77, 0x42, 0xae, 0x68, 0xe5, 0x49, 0x8c, 0x10, 0x1f, 0x53, 0x60,
+ 0xe4, 0x2e, 0xd4, 0x7d, 0xcb, 0xa4, 0x86, 0xee, 0xf9, 0xf3, 0x53, 0x67, 0x01, 0x9e, 0x93, 0xc0,
+ 0xf5, 0x8e, 0xac, 0x86, 0x21, 0x00, 0x59, 0x00, 0xe8, 0xeb, 0x5e, 0x60, 0x09, 0x45, 0x75, 0x9a,
+ 0x2b, 0x4d, 0x33, 0xa7, 0x27, 0x2d, 0xd8, 0x0e, 0x4b, 0x31, 0xc6, 0xc1, 0xf8, 0x59, 0xdd, 0x0d,
+ 0xa7, 0x3f, 0x08, 0xc4, 0xc2, 0xda, 0x10, 0xfc, 0x9d, 0xb0, 0x14, 0x63, 0x1c, 0xe4, 0xb7, 0x0a,
+ 0xf0, 0xb9, 0xe8, 0xef, 0xf0, 0x47, 0x36, 0x3b, 0xf1, 0x8f, 0xac, 0x75, 0x7a, 0xd2, 0xfa, 0x5c,
+ 0x67, 0xb4, 0x48, 0x7c, 0x52, 0x7b, 0xc8, 0xc7, 0x05, 0x98, 0x19, 0xf4, 0x4d, 0x3d, 0xa0, 0x9d,
+ 0x80, 0xed, 0x78, 0xba, 0xc7, 0xf3, 0x73, 0xbc, 0x89, 0xeb, 0xe3, 0xcf, 0x82, 0x09, 0xb8, 0xe8,
+ 0x35, 0x27, 0xcb, 0x31, 0x25, 0x56, 0x7b, 0x07, 0xa6, 0x97, 0x07, 0xc1, 0x81, 0xeb, 0x59, 0x1f,
+ 0x72, 0xf5, 0x9f, 0xac, 0x41, 0x25, 0xe0, 0x6a, 0x9c, 0xd0, 0x10, 0xbe, 0x90, 0xf5, 0xd2, 0x85,
+ 0x4a, 0x7d, 0x97, 0x1e, 0x2b, 0xbd, 0x44, 0xac, 0xd4, 0x42, 0xad, 0x13, 0xd5, 0xb5, 0x3f, 0x53,
+ 0x80, 0x5a, 0x5b, 0x37, 0x0e, 0xdd, 0xfd, 0x7d, 0xf2, 0x2e, 0xd4, 0x2d, 0x27, 0xa0, 0xde, 0x91,
+ 0x6e, 0x4b, 0xd8, 0x85, 0x18, 0x6c, 0xb8, 0x21, 0x8c, 0x1e, 0x8f, 0xed, 0xbe, 0x98, 0xa0, 0xd5,
+ 0x81, 0xdc, 0xb5, 0x70, 0xcd, 0x78, 0x43, 0x62, 0x60, 0x88, 0x46, 0x5a, 0x50, 0xf1, 0x03, 0xda,
+ 0xf7, 0xf9, 0x1a, 0x38, 0x2d, 0x9a, 0xd1, 0x61, 0x05, 0x28, 0xca, 0xb5, 0xbf, 0x5e, 0x80, 0x46,
+ 0x5b, 0xf7, 0x2d, 0x83, 0x3d, 0x25, 0x59, 0x81, 0xf2, 0xc0, 0xa7, 0xde, 0xf9, 0x9e, 0x8d, 0x2f,
+ 0x5b, 0xbb, 0x3e, 0xf5, 0x90, 0x57, 0x26, 0xf7, 0xa1, 0xde, 0xd7, 0x7d, 0xff, 0xa1, 0xeb, 0x99,
+ 0x72, 0xe9, 0x3d, 0x23, 0x90, 0xd8, 0x26, 0xc8, 0xaa, 0x18, 0x82, 0x68, 0x4d, 0x88, 0x74, 0x0f,
+ 0xed, 0xf7, 0x0a, 0x70, 0xa9, 0x3d, 0xd8, 0xdf, 0xa7, 0x9e, 0xd4, 0x8a, 0xa5, 0xbe, 0x49, 0xa1,
+ 0xe2, 0x51, 0xd3, 0xf2, 0x65, 0xdb, 0x57, 0xc7, 0x1e, 0x28, 0xc8, 0x50, 0xa4, 0x7a, 0xcb, 0xfb,
+ 0x8b, 0x17, 0xa0, 0x40, 0x27, 0x03, 0x68, 0x7c, 0x40, 0xd9, 0x6e, 0x9c, 0xea, 0x3d, 0xf9, 0x74,
+ 0xb7, 0xc7, 0x16, 0x75, 0x87, 0x06, 0x1d, 0x8e, 0x14, 0xd7, 0xa6, 0xc3, 0x42, 0x8c, 0x24, 0x69,
+ 0xbf, 0x5d, 0x81, 0xa9, 0x15, 0xb7, 0xb7, 0x67, 0x39, 0xd4, 0xbc, 0x65, 0x76, 0x29, 0x79, 0x1f,
+ 0xca, 0xd4, 0xec, 0x52, 0xf9, 0xb4, 0xe3, 0x2b, 0x1e, 0x0c, 0x2c, 0x52, 0x9f, 0xd8, 0x3f, 0xe4,
+ 0xc0, 0x64, 0x13, 0x66, 0xf6, 0x3d, 0xb7, 0x27, 0xe6, 0xf2, 0x9d, 0xe3, 0xbe, 0xd4, 0x9d, 0xdb,
+ 0x3f, 0xaa, 0x3e, 0x9c, 0xb5, 0x04, 0xf5, 0xf1, 0x49, 0x0b, 0xa2, 0x7f, 0x98, 0xaa, 0x4b, 0xde,
+ 0x85, 0xf9, 0xa8, 0x24, 0x9c, 0xd4, 0x56, 0xd8, 0x76, 0x86, 0xeb, 0x4e, 0x95, 0xf6, 0x2b, 0xa7,
+ 0x27, 0xad, 0xf9, 0xb5, 0x11, 0x3c, 0x38, 0xb2, 0x36, 0x9b, 0x2a, 0xe6, 0x22, 0xa2, 0x58, 0x68,
+ 0xa4, 0xca, 0x34, 0xa1, 0x15, 0x8c, 0xef, 0xfb, 0xd6, 0x52, 0x22, 0x70, 0x48, 0x28, 0x59, 0x83,
+ 0xa9, 0xc0, 0x8d, 0xf5, 0x57, 0x85, 0xf7, 0x97, 0xa6, 0x0c, 0x15, 0x3b, 0xee, 0xc8, 0xde, 0x4a,
+ 0xd4, 0x23, 0x08, 0x57, 0xd4, 0xff, 0x54, 0x4f, 0x55, 0x79, 0x4f, 0x5d, 0x3b, 0x3d, 0x69, 0x5d,
+ 0xd9, 0xc9, 0xe4, 0xc0, 0x11, 0x35, 0xc9, 0x2f, 0x17, 0x60, 0x46, 0x91, 0x64, 0x1f, 0xd5, 0x26,
+ 0xd9, 0x47, 0x84, 0x8d, 0x88, 0x9d, 0x84, 0x00, 0x4c, 0x09, 0xd4, 0x7e, 0xa7, 0x0a, 0x8d, 0x70,
+ 0xaa, 0x27, 0x9f, 0x87, 0x0a, 0x37, 0x41, 0x48, 0x0d, 0x3e, 0x5c, 0xc3, 0xb9, 0xa5, 0x02, 0x05,
+ 0x8d, 0x7c, 0x01, 0x6a, 0x86, 0xdb, 0xeb, 0xe9, 0x8e, 0xc9, 0xcd, 0x4a, 0x8d, 0x76, 0x93, 0xa9,
+ 0x2e, 0x2b, 0xa2, 0x08, 0x15, 0x8d, 0xbc, 0x02, 0x65, 0xdd, 0xeb, 0x0a, 0x0b, 0x4f, 0x43, 0xcc,
+ 0x47, 0xcb, 0x5e, 0xd7, 0x47, 0x5e, 0x4a, 0xbe, 0x02, 0x25, 0xea, 0x1c, 0xcd, 0x97, 0x47, 0xeb,
+ 0x46, 0xb7, 0x9c, 0xa3, 0x07, 0xba, 0xd7, 0x6e, 0xca, 0x36, 0x94, 0x6e, 0x39, 0x47, 0xc8, 0xea,
+ 0x90, 0x4d, 0xa8, 0x51, 0xe7, 0x88, 0xbd, 0x7b, 0x69, 0x7a, 0xf9, 0x91, 0x11, 0xd5, 0x19, 0x8b,
+ 0xdc, 0x26, 0x84, 0x1a, 0x96, 0x2c, 0x46, 0x05, 0x41, 0xbe, 0x01, 0x53, 0x42, 0xd9, 0xda, 0x62,
+ 0xef, 0xc4, 0x9f, 0xaf, 0x72, 0xc8, 0xd6, 0x68, 0x6d, 0x8d, 0xf3, 0x45, 0xa6, 0xae, 0x58, 0xa1,
+ 0x8f, 0x09, 0x28, 0xf2, 0x0d, 0x68, 0xa8, 0x9d, 0xb1, 0x7a, 0xb3, 0x99, 0x56, 0x22, 0xb5, 0x9d,
+ 0x46, 0xfa, 0xad, 0x81, 0xe5, 0xd1, 0x1e, 0x75, 0x02, 0xbf, 0xfd, 0xa2, 0xb2, 0x1b, 0x28, 0xaa,
+ 0x8f, 0x11, 0x1a, 0xd9, 0x1b, 0x36, 0x77, 0x09, 0x5b, 0xcd, 0xe7, 0x47, 0xcc, 0xea, 0x63, 0xd8,
+ 0xba, 0xbe, 0x09, 0xb3, 0xa1, 0x3d, 0x4a, 0x9a, 0x34, 0x84, 0xf5, 0xe6, 0x4b, 0xac, 0xfa, 0x46,
+ 0x92, 0xf4, 0xf8, 0xa4, 0xf5, 0x6a, 0x86, 0x51, 0x23, 0x62, 0xc0, 0x34, 0x18, 0xf9, 0x10, 0x66,
+ 0x3c, 0xaa, 0x9b, 0x96, 0x43, 0x7d, 0x7f, 0xdb, 0x73, 0xf7, 0xf2, 0x6b, 0x9e, 0x1c, 0x45, 0x0c,
+ 0x7b, 0x4c, 0x20, 0x63, 0x4a, 0x12, 0x79, 0x08, 0xd3, 0xb6, 0x75, 0x44, 0x23, 0xd1, 0xcd, 0x89,
+ 0x88, 0x7e, 0xf1, 0xf4, 0xa4, 0x35, 0xbd, 0x19, 0x07, 0xc6, 0xa4, 0x1c, 0xed, 0xef, 0x54, 0x60,
+ 0x78, 0xf3, 0x95, 0x1c, 0x29, 0x85, 0x49, 0x8f, 0x94, 0xf4, 0x5b, 0x14, 0x6b, 0xc6, 0x9b, 0xb2,
+ 0xda, 0x04, 0xde, 0x64, 0xc6, 0x68, 0x2c, 0x4d, 0x7a, 0x34, 0x3e, 0x37, 0x13, 0xc6, 0xf0, 0xb0,
+ 0xad, 0x7e, 0x72, 0xc3, 0xb6, 0xf6, 0x8c, 0x86, 0xed, 0x77, 0xca, 0x30, 0xb3, 0xaa, 0xd3, 0x9e,
+ 0xeb, 0x3c, 0x75, 0xff, 0x5d, 0x78, 0x2e, 0xf6, 0xdf, 0x37, 0xa1, 0xee, 0xd1, 0xbe, 0x6d, 0x19,
+ 0xba, 0x50, 0xb3, 0xa5, 0xbd, 0x1b, 0x65, 0x19, 0x86, 0xd4, 0x11, 0x76, 0x97, 0xd2, 0x73, 0x69,
+ 0x77, 0x29, 0x7f, 0xf2, 0x76, 0x17, 0xed, 0x97, 0x8b, 0xc0, 0x55, 0x52, 0x72, 0x03, 0xca, 0x4c,
+ 0xdd, 0x4a, 0x5b, 0xfb, 0xf8, 0xd7, 0xc2, 0x29, 0xe4, 0x1a, 0x14, 0x03, 0x57, 0x4e, 0x37, 0x20,
+ 0xe9, 0xc5, 0x1d, 0x17, 0x8b, 0x81, 0x4b, 0x3e, 0x04, 0x30, 0x5c, 0xc7, 0xb4, 0x94, 0x1b, 0x28,
+ 0xdf, 0x83, 0xad, 0xb9, 0xde, 0x43, 0xdd, 0x33, 0x57, 0x42, 0x44, 0xb1, 0xf3, 0x8e, 0xfe, 0x63,
+ 0x4c, 0x1a, 0x79, 0x0b, 0xaa, 0xae, 0xb3, 0x36, 0xb0, 0x6d, 0xde, 0xa1, 0x8d, 0xf6, 0x8f, 0x9d,
+ 0x9e, 0xb4, 0xaa, 0xf7, 0x79, 0xc9, 0xe3, 0x93, 0xd6, 0x55, 0xb1, 0x93, 0x61, 0xff, 0xde, 0xf1,
+ 0xac, 0xc0, 0x72, 0xba, 0xe1, 0x46, 0x54, 0x56, 0xd3, 0x7e, 0xb5, 0x00, 0xcd, 0x35, 0xeb, 0x11,
+ 0x35, 0xdf, 0xb1, 0x1c, 0xd3, 0x7d, 0x48, 0x10, 0xaa, 0x36, 0x75, 0xba, 0xc1, 0xc1, 0x98, 0x3b,
+ 0x45, 0x61, 0x8f, 0xe1, 0x08, 0x28, 0x91, 0xc8, 0x22, 0x34, 0xc4, 0x3e, 0xc3, 0x72, 0xba, 0xbc,
+ 0x0f, 0xeb, 0xd1, 0x4c, 0xdf, 0x51, 0x04, 0x8c, 0x78, 0xb4, 0x63, 0x78, 0x71, 0xa8, 0x1b, 0x88,
+ 0x09, 0xe5, 0x40, 0xef, 0xaa, 0x45, 0x65, 0x6d, 0xec, 0x0e, 0xde, 0xd1, 0xbb, 0xb1, 0xce, 0xe5,
+ 0xda, 0xdc, 0x8e, 0xce, 0xb4, 0x39, 0x86, 0xae, 0xfd, 0x61, 0x01, 0xea, 0x6b, 0x03, 0xc7, 0xe0,
+ 0x9b, 0xf1, 0xa7, 0x5b, 0x81, 0x95, 0x6a, 0x58, 0xcc, 0x54, 0x0d, 0x07, 0x50, 0x3d, 0x7c, 0x18,
+ 0xaa, 0x8e, 0xcd, 0xa5, 0xad, 0xf1, 0x47, 0x85, 0x6c, 0xd2, 0xc2, 0x5d, 0x8e, 0x27, 0x9c, 0x94,
+ 0x33, 0xb2, 0x41, 0xd5, 0xbb, 0xef, 0x70, 0xa1, 0x52, 0xd8, 0xb5, 0xaf, 0x40, 0x33, 0xc6, 0x76,
+ 0x2e, 0x7f, 0xc5, 0xdf, 0x2d, 0x43, 0x75, 0xbd, 0xd3, 0x59, 0xde, 0xde, 0x20, 0xaf, 0x43, 0x53,
+ 0xfa, 0xaf, 0xee, 0x45, 0x7d, 0x10, 0xba, 0x2f, 0x3b, 0x11, 0x09, 0xe3, 0x7c, 0x4c, 0xf1, 0xf6,
+ 0xa8, 0x6e, 0xf7, 0xe4, 0xc7, 0x12, 0x2a, 0xde, 0xc8, 0x0a, 0x51, 0xd0, 0x88, 0x0e, 0x33, 0x6c,
+ 0x2f, 0xcf, 0xba, 0x50, 0xec, 0xd3, 0xe5, 0x67, 0x73, 0xc6, 0x9d, 0x3c, 0x5f, 0x60, 0x76, 0x13,
+ 0x00, 0x98, 0x02, 0x24, 0x6f, 0x42, 0x5d, 0x1f, 0x04, 0x07, 0x7c, 0xab, 0x24, 0xbe, 0x8d, 0x57,
+ 0xb8, 0x7b, 0x4f, 0x96, 0x3d, 0x3e, 0x69, 0x4d, 0xdd, 0xc5, 0xf6, 0xeb, 0xea, 0x3f, 0x86, 0xdc,
+ 0xac, 0x71, 0xca, 0x36, 0x20, 0x1b, 0x57, 0x39, 0x77, 0xe3, 0xb6, 0x13, 0x00, 0x98, 0x02, 0x24,
+ 0xef, 0xc1, 0xd4, 0x21, 0x3d, 0x0e, 0xf4, 0x3d, 0x29, 0xa0, 0x7a, 0x1e, 0x01, 0x73, 0x4c, 0x59,
+ 0xbf, 0x1b, 0xab, 0x8e, 0x09, 0x30, 0xe2, 0xc3, 0xe5, 0x43, 0xea, 0xed, 0x51, 0xcf, 0x95, 0x76,
+ 0x06, 0x29, 0xa4, 0x76, 0x1e, 0x21, 0xf3, 0xa7, 0x27, 0xad, 0xcb, 0x77, 0x33, 0x60, 0x30, 0x13,
+ 0x5c, 0xfb, 0xdf, 0x45, 0x98, 0x5d, 0x17, 0x01, 0x04, 0xae, 0x27, 0x34, 0x0f, 0x72, 0x15, 0x4a,
+ 0x5e, 0x7f, 0xc0, 0x47, 0x4e, 0x49, 0xb8, 0x08, 0x70, 0x7b, 0x17, 0x59, 0x19, 0x79, 0x17, 0xea,
+ 0xa6, 0x9c, 0x32, 0xa4, 0x99, 0x63, 0x2c, 0x93, 0x94, 0xfa, 0x87, 0x21, 0x1a, 0xdb, 0xd3, 0xf5,
+ 0xfc, 0x6e, 0xc7, 0xfa, 0x90, 0xca, 0x9d, 0x3f, 0xdf, 0xd3, 0x6d, 0x89, 0x22, 0x54, 0x34, 0xb6,
+ 0xaa, 0x1e, 0xd2, 0x63, 0xb1, 0xef, 0x2d, 0x47, 0xab, 0xea, 0x5d, 0x59, 0x86, 0x21, 0x95, 0xb4,
+ 0xd4, 0xc7, 0xc2, 0x46, 0x41, 0x59, 0xd8, 0x6c, 0x1e, 0xb0, 0x02, 0xf9, 0xdd, 0xb0, 0x29, 0xf3,
+ 0x03, 0x2b, 0x08, 0xa8, 0x27, 0x5f, 0xe3, 0x58, 0x53, 0xe6, 0x1d, 0x8e, 0x80, 0x12, 0x89, 0xfc,
+ 0x04, 0x34, 0x38, 0x78, 0xdb, 0x76, 0xf7, 0xf8, 0x8b, 0x6b, 0x08, 0xeb, 0xcd, 0x03, 0x55, 0x88,
+ 0x11, 0x5d, 0xfb, 0xa3, 0x22, 0x5c, 0x59, 0xa7, 0x81, 0xd0, 0x6a, 0x56, 0x69, 0xdf, 0x76, 0x8f,
+ 0x99, 0x3e, 0x8d, 0xf4, 0x5b, 0xe4, 0x6d, 0x00, 0xcb, 0xdf, 0xeb, 0x1c, 0x19, 0xfc, 0x3b, 0x10,
+ 0xdf, 0xf0, 0x0d, 0xf9, 0x49, 0xc2, 0x46, 0xa7, 0x2d, 0x29, 0x8f, 0x13, 0xff, 0x30, 0x56, 0x27,
+ 0xda, 0x48, 0x17, 0x9f, 0xb0, 0x91, 0xee, 0x00, 0xf4, 0x23, 0xad, 0xbc, 0xc4, 0x39, 0x7f, 0x5a,
+ 0x89, 0x39, 0x8f, 0x42, 0x1e, 0x83, 0xc9, 0xa3, 0x27, 0x3b, 0x30, 0x67, 0xd2, 0x7d, 0x7d, 0x60,
+ 0x07, 0xe1, 0x4e, 0x42, 0x7e, 0xc4, 0x67, 0xdf, 0x8c, 0x84, 0xc1, 0x0d, 0xab, 0x29, 0x24, 0x1c,
+ 0xc2, 0xd6, 0xfe, 0x5e, 0x09, 0xae, 0xad, 0xd3, 0x20, 0xb4, 0xad, 0xc9, 0xd9, 0xb1, 0xd3, 0xa7,
+ 0x06, 0x7b, 0x0b, 0x1f, 0x17, 0xa0, 0x6a, 0xeb, 0x7b, 0xd4, 0x66, 0xab, 0x17, 0x7b, 0x9a, 0xf7,
+ 0xc7, 0x5e, 0x08, 0x46, 0x4b, 0x59, 0xd8, 0xe4, 0x12, 0x52, 0x4b, 0x83, 0x28, 0x44, 0x29, 0x9e,
+ 0x4d, 0xea, 0x86, 0x3d, 0xf0, 0x03, 0xea, 0x6d, 0xbb, 0x5e, 0x20, 0xf5, 0xc9, 0x70, 0x52, 0x5f,
+ 0x89, 0x48, 0x18, 0xe7, 0x23, 0x4b, 0x00, 0x86, 0x6d, 0x51, 0x27, 0xe0, 0xb5, 0xc4, 0x77, 0x45,
+ 0xd4, 0xfb, 0x5d, 0x09, 0x29, 0x18, 0xe3, 0x62, 0xa2, 0x7a, 0xae, 0x63, 0x05, 0xae, 0x10, 0x55,
+ 0x4e, 0x8a, 0xda, 0x8a, 0x48, 0x18, 0xe7, 0xe3, 0xd5, 0x68, 0xe0, 0x59, 0x86, 0xcf, 0xab, 0x55,
+ 0x52, 0xd5, 0x22, 0x12, 0xc6, 0xf9, 0xd8, 0x9a, 0x17, 0x7b, 0xfe, 0x73, 0xad, 0x79, 0xbf, 0xd9,
+ 0x80, 0xeb, 0x89, 0x6e, 0x0d, 0xf4, 0x80, 0xee, 0x0f, 0xec, 0x0e, 0x0d, 0xd4, 0x0b, 0x1c, 0x73,
+ 0x2d, 0xfc, 0xf3, 0xd1, 0x7b, 0x17, 0x61, 0x4b, 0xc6, 0x64, 0xde, 0xfb, 0x50, 0x03, 0xcf, 0xf4,
+ 0xee, 0x17, 0xa1, 0xe1, 0xe8, 0x81, 0xcf, 0x3f, 0x5c, 0xf9, 0x8d, 0x86, 0x6a, 0xd8, 0x3d, 0x45,
+ 0xc0, 0x88, 0x87, 0x6c, 0xc3, 0x65, 0xd9, 0xc5, 0xb7, 0x1e, 0xf5, 0x5d, 0x2f, 0xa0, 0x9e, 0xa8,
+ 0x2b, 0x97, 0x53, 0x59, 0xf7, 0xf2, 0x56, 0x06, 0x0f, 0x66, 0xd6, 0x24, 0x5b, 0x70, 0xc9, 0x10,
+ 0xa1, 0x1c, 0xd4, 0x76, 0x75, 0x53, 0x01, 0x0a, 0x53, 0x66, 0xb8, 0x35, 0x5a, 0x19, 0x66, 0xc1,
+ 0xac, 0x7a, 0xe9, 0xd1, 0x5c, 0x1d, 0x6b, 0x34, 0xd7, 0xc6, 0x19, 0xcd, 0xf5, 0xf1, 0x46, 0x73,
+ 0xe3, 0x6c, 0xa3, 0x99, 0xf5, 0x3c, 0x1b, 0x47, 0xd4, 0x63, 0xea, 0x89, 0x58, 0x61, 0x63, 0x91,
+ 0x42, 0x61, 0xcf, 0x77, 0x32, 0x78, 0x30, 0xb3, 0x26, 0xd9, 0x83, 0x6b, 0xa2, 0xfc, 0x96, 0x63,
+ 0x78, 0xc7, 0x7d, 0xb6, 0xf0, 0xc4, 0x70, 0x9b, 0x09, 0x5b, 0xf2, 0xb5, 0xce, 0x48, 0x4e, 0x7c,
+ 0x02, 0x0a, 0xf9, 0x59, 0x98, 0x16, 0x6f, 0x69, 0x4b, 0xef, 0x73, 0x58, 0x11, 0x37, 0xf4, 0x92,
+ 0x84, 0x9d, 0x5e, 0x89, 0x13, 0x31, 0xc9, 0x4b, 0x96, 0x61, 0xb6, 0x7f, 0x64, 0xb0, 0x9f, 0x1b,
+ 0xfb, 0xf7, 0x28, 0x35, 0xa9, 0xc9, 0x1d, 0x95, 0x8d, 0xf6, 0xcb, 0xca, 0xba, 0xb3, 0x9d, 0x24,
+ 0x63, 0x9a, 0x9f, 0xbc, 0x09, 0x53, 0x7e, 0xa0, 0x7b, 0x81, 0x34, 0xe0, 0xce, 0xcf, 0x88, 0xb8,
+ 0x2a, 0x65, 0xdf, 0xec, 0xc4, 0x68, 0x98, 0xe0, 0xcc, 0x5c, 0x2f, 0x66, 0x2f, 0x6e, 0xbd, 0xc8,
+ 0x33, 0x5b, 0xfd, 0xb3, 0x22, 0xdc, 0x58, 0xa7, 0xc1, 0x96, 0xeb, 0x48, 0xf3, 0x77, 0xd6, 0xb2,
+ 0x7f, 0x26, 0xeb, 0x77, 0x72, 0xd1, 0x2e, 0x4e, 0x74, 0xd1, 0x2e, 0x4d, 0x68, 0xd1, 0x2e, 0x5f,
+ 0xe0, 0xa2, 0xfd, 0x0f, 0x8a, 0xf0, 0x72, 0xa2, 0x27, 0xb7, 0x5d, 0x53, 0x4d, 0xf8, 0x9f, 0x75,
+ 0xe0, 0x19, 0x3a, 0xf0, 0xb1, 0xd0, 0x3b, 0xb9, 0x03, 0x33, 0xa5, 0xf1, 0x7c, 0x94, 0xd6, 0x78,
+ 0xde, 0xcb, 0xb3, 0xf2, 0x65, 0x48, 0x38, 0xd3, 0x8a, 0x77, 0x07, 0x88, 0x27, 0xdd, 0xad, 0xc2,
+ 0xf4, 0x13, 0x53, 0x7a, 0xc2, 0xc0, 0x4d, 0x1c, 0xe2, 0xc0, 0x8c, 0x5a, 0xa4, 0x03, 0x2f, 0xf9,
+ 0xd4, 0x09, 0x2c, 0x87, 0xda, 0x49, 0x38, 0xa1, 0x0d, 0xbd, 0x2a, 0xe1, 0x5e, 0xea, 0x64, 0x31,
+ 0x61, 0x76, 0xdd, 0x3c, 0xf3, 0xc0, 0xbf, 0x02, 0xae, 0x72, 0x8a, 0xae, 0x99, 0x98, 0xc6, 0xf2,
+ 0x71, 0x5a, 0x63, 0x79, 0x3f, 0xff, 0x7b, 0x1b, 0x4f, 0x5b, 0x59, 0x02, 0xe0, 0x6f, 0x21, 0xae,
+ 0xae, 0x84, 0x8b, 0x34, 0x86, 0x14, 0x8c, 0x71, 0xb1, 0x05, 0x48, 0xf5, 0x73, 0x5c, 0x53, 0x09,
+ 0x17, 0xa0, 0x4e, 0x9c, 0x88, 0x49, 0xde, 0x91, 0xda, 0x4e, 0x65, 0x6c, 0x6d, 0xe7, 0x0e, 0x90,
+ 0x84, 0xe1, 0x51, 0xe0, 0x55, 0x93, 0x71, 0xc3, 0x1b, 0x43, 0x1c, 0x98, 0x51, 0x6b, 0xc4, 0x50,
+ 0xae, 0x4d, 0x76, 0x28, 0xd7, 0xc7, 0x1f, 0xca, 0xe4, 0x7d, 0xb8, 0xca, 0x45, 0xc9, 0xfe, 0x49,
+ 0x02, 0x0b, 0xbd, 0xe7, 0x47, 0x24, 0xf0, 0x55, 0x1c, 0xc5, 0x88, 0xa3, 0x31, 0xd8, 0xfb, 0x31,
+ 0x3c, 0x6a, 0x32, 0xe1, 0xba, 0x3d, 0x5a, 0x27, 0x5a, 0xc9, 0xe0, 0xc1, 0xcc, 0x9a, 0x6c, 0x88,
+ 0x05, 0x6c, 0x18, 0xea, 0x7b, 0x36, 0x35, 0x65, 0xdc, 0x74, 0x38, 0xc4, 0x76, 0x36, 0x3b, 0x92,
+ 0x82, 0x31, 0xae, 0x2c, 0x35, 0x65, 0xea, 0x9c, 0x6a, 0xca, 0x3a, 0xb7, 0xd2, 0xef, 0x27, 0xb4,
+ 0x21, 0xa9, 0xeb, 0x84, 0x91, 0xf0, 0x2b, 0x69, 0x06, 0x1c, 0xae, 0xc3, 0xb5, 0x44, 0xc3, 0xb3,
+ 0xfa, 0x81, 0x9f, 0xc4, 0x9a, 0x49, 0x69, 0x89, 0x19, 0x3c, 0x98, 0x59, 0x93, 0xe9, 0xe7, 0x07,
+ 0x54, 0xb7, 0x83, 0x83, 0x24, 0xe0, 0x6c, 0x52, 0x3f, 0xbf, 0x3d, 0xcc, 0x82, 0x59, 0xf5, 0x32,
+ 0x17, 0xa4, 0xb9, 0xe7, 0x53, 0xad, 0xfa, 0x95, 0x12, 0x5c, 0x5d, 0xa7, 0x41, 0x18, 0x52, 0xf6,
+ 0x99, 0x19, 0xe5, 0x13, 0x30, 0xa3, 0xfc, 0x46, 0x05, 0x2e, 0xad, 0xd3, 0x60, 0x48, 0x1b, 0xfb,
+ 0xff, 0xb4, 0xfb, 0xb7, 0xe0, 0x52, 0x14, 0xc5, 0xd8, 0x09, 0x5c, 0x4f, 0xac, 0xe5, 0xa9, 0xdd,
+ 0x72, 0x67, 0x98, 0x05, 0xb3, 0xea, 0x91, 0x6f, 0xc0, 0xcb, 0x7c, 0xa9, 0x77, 0xba, 0xc2, 0x3e,
+ 0x2b, 0x8c, 0x09, 0xb1, 0x73, 0x38, 0x2d, 0x09, 0xf9, 0x72, 0x27, 0x9b, 0x0d, 0x47, 0xd5, 0x27,
+ 0xdf, 0x86, 0xa9, 0xbe, 0xd5, 0xa7, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0x77, 0xf0, 0xcf, 0x76, 0x0c,
+ 0x2c, 0xda, 0xc0, 0xc5, 0x4b, 0x31, 0x21, 0x30, 0x73, 0xa4, 0xd6, 0x2f, 0x70, 0xa4, 0xfe, 0x8f,
+ 0x22, 0xd4, 0xd6, 0x3d, 0x77, 0xd0, 0x6f, 0x1f, 0x93, 0x2e, 0x54, 0x1f, 0x72, 0xe7, 0x99, 0x74,
+ 0x4d, 0x8d, 0x7f, 0x12, 0x40, 0xf8, 0xe0, 0x22, 0x95, 0x48, 0xfc, 0x47, 0x09, 0xcf, 0x06, 0xf1,
+ 0x21, 0x3d, 0xa6, 0xa6, 0xf4, 0xa1, 0x85, 0x83, 0xf8, 0x2e, 0x2b, 0x44, 0x41, 0x23, 0x3d, 0x98,
+ 0xd5, 0x6d, 0xdb, 0x7d, 0x48, 0xcd, 0x4d, 0x3d, 0xe0, 0x7e, 0x6f, 0xe9, 0x5b, 0x39, 0xaf, 0x59,
+ 0x9a, 0x07, 0x33, 0x2c, 0x27, 0xa1, 0x30, 0x8d, 0x4d, 0x3e, 0x80, 0x9a, 0x1f, 0xb8, 0x9e, 0x52,
+ 0xb6, 0x9a, 0x4b, 0x2b, 0xe3, 0xbf, 0xf4, 0xf6, 0xd7, 0x3b, 0x02, 0x4a, 0xd8, 0xec, 0xe5, 0x1f,
+ 0x54, 0x02, 0xb4, 0x5f, 0x2f, 0x00, 0xdc, 0xde, 0xd9, 0xd9, 0x96, 0xee, 0x05, 0x13, 0xca, 0xfa,
+ 0x20, 0x74, 0x54, 0x8e, 0xef, 0x10, 0x4c, 0x04, 0xe0, 0x4a, 0x1f, 0xde, 0x20, 0x38, 0x40, 0x8e,
+ 0x4e, 0x7e, 0x1c, 0x6a, 0x52, 0x41, 0x96, 0xdd, 0x1e, 0xc6, 0x53, 0x48, 0x25, 0x1a, 0x15, 0x5d,
+ 0xfb, 0xdb, 0x45, 0x80, 0x0d, 0xd3, 0xa6, 0x1d, 0x75, 0x78, 0xa3, 0x11, 0x1c, 0x78, 0xd4, 0x3f,
+ 0x70, 0x6d, 0x73, 0x4c, 0x6f, 0x2a, 0xb7, 0xf9, 0xef, 0x28, 0x10, 0x8c, 0xf0, 0x88, 0x09, 0x53,
+ 0x7e, 0x40, 0xfb, 0x2a, 0x26, 0x77, 0x4c, 0x27, 0xca, 0x9c, 0xb0, 0x8b, 0x44, 0x38, 0x98, 0x40,
+ 0x25, 0x3a, 0x34, 0x2d, 0xc7, 0x10, 0x1f, 0x48, 0xfb, 0x78, 0xcc, 0x81, 0x34, 0xcb, 0x76, 0x1c,
+ 0x1b, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0xdf, 0x2d, 0xc2, 0x15, 0x2e, 0x8f, 0x35, 0x23, 0x11, 0x79,
+ 0x4b, 0xfe, 0xe4, 0xd0, 0x41, 0xd3, 0x3f, 0x7e, 0x36, 0xd1, 0xe2, 0x9c, 0xe2, 0x16, 0x0d, 0xf4,
+ 0x48, 0x9f, 0x8b, 0xca, 0x62, 0xa7, 0x4b, 0x07, 0x50, 0xf6, 0xd9, 0x7c, 0x25, 0x7a, 0xaf, 0x33,
+ 0xf6, 0x10, 0xca, 0x7e, 0x00, 0x3e, 0x7b, 0x85, 0x5e, 0x63, 0x3e, 0x6b, 0x71, 0x71, 0xe4, 0x4f,
+ 0x43, 0xd5, 0x0f, 0xf4, 0x60, 0xa0, 0x3e, 0xcd, 0xdd, 0x49, 0x0b, 0xe6, 0xe0, 0xd1, 0x3c, 0x22,
+ 0xfe, 0xa3, 0x14, 0xaa, 0xfd, 0x6e, 0x01, 0xae, 0x65, 0x57, 0xdc, 0xb4, 0xfc, 0x80, 0xfc, 0x89,
+ 0xa1, 0x6e, 0x3f, 0xe3, 0x1b, 0x67, 0xb5, 0x79, 0xa7, 0x87, 0x67, 0x11, 0x54, 0x49, 0xac, 0xcb,
+ 0x03, 0xa8, 0x58, 0x01, 0xed, 0xa9, 0xfd, 0xe5, 0xfd, 0x09, 0x3f, 0x7a, 0x6c, 0x69, 0x67, 0x52,
+ 0x50, 0x08, 0xd3, 0xbe, 0x53, 0x1c, 0xf5, 0xc8, 0x7c, 0xf9, 0xb0, 0x93, 0xd1, 0xdd, 0x77, 0xf3,
+ 0x45, 0x77, 0x27, 0x1b, 0x34, 0x1c, 0xe4, 0xfd, 0xa7, 0x86, 0x83, 0xbc, 0xef, 0xe7, 0x0f, 0xf2,
+ 0x4e, 0x75, 0xc3, 0xc8, 0x58, 0xef, 0x1f, 0x94, 0xe0, 0x95, 0x27, 0x0d, 0x1b, 0xb6, 0x9e, 0xc9,
+ 0xd1, 0x99, 0x77, 0x3d, 0x7b, 0xf2, 0x38, 0x24, 0x4b, 0x50, 0xe9, 0x1f, 0xe8, 0xbe, 0x52, 0xca,
+ 0xd4, 0x86, 0xa5, 0xb2, 0xcd, 0x0a, 0x1f, 0xb3, 0x49, 0x83, 0x2b, 0x73, 0xfc, 0x2f, 0x0a, 0x56,
+ 0x36, 0x1d, 0xf7, 0xa8, 0xef, 0x47, 0x36, 0x81, 0x70, 0x3a, 0xde, 0x12, 0xc5, 0xa8, 0xe8, 0x24,
+ 0x80, 0xaa, 0x30, 0x31, 0xcb, 0x95, 0x69, 0xfc, 0x40, 0xae, 0x8c, 0x03, 0x01, 0xd1, 0x43, 0x49,
+ 0x6f, 0x85, 0x94, 0x45, 0x16, 0xa0, 0x1c, 0x44, 0xe1, 0xd9, 0x6a, 0x6b, 0x5e, 0xce, 0xd0, 0x4f,
+ 0x39, 0x1f, 0xdb, 0xd8, 0xbb, 0x7b, 0xdc, 0xa8, 0x6e, 0x4a, 0xff, 0xb9, 0xe5, 0x3a, 0x5c, 0x21,
+ 0x2b, 0x45, 0x1b, 0xfb, 0xfb, 0x43, 0x1c, 0x98, 0x51, 0x4b, 0xfb, 0xb7, 0x75, 0xb8, 0x92, 0x3d,
+ 0x1e, 0x58, 0xbf, 0x1d, 0x51, 0xcf, 0x67, 0xd8, 0x85, 0x64, 0xbf, 0x3d, 0x10, 0xc5, 0xa8, 0xe8,
+ 0x9f, 0xea, 0x80, 0xb3, 0xdf, 0x28, 0xc0, 0x55, 0x4f, 0xfa, 0x88, 0x9e, 0x45, 0xd0, 0xd9, 0xab,
+ 0xc2, 0x9c, 0x31, 0x42, 0x20, 0x8e, 0x6e, 0x0b, 0xf9, 0x9b, 0x05, 0x98, 0xef, 0xa5, 0xec, 0x1c,
+ 0x17, 0x78, 0x56, 0x92, 0x9f, 0x7f, 0xd8, 0x1a, 0x21, 0x0f, 0x47, 0xb6, 0x84, 0x7c, 0x1b, 0x9a,
+ 0x7d, 0x36, 0x2e, 0xfc, 0x80, 0x3a, 0x86, 0x0a, 0x10, 0x1d, 0xff, 0x4b, 0xda, 0x8e, 0xb0, 0xc2,
+ 0xb3, 0x52, 0x5c, 0x3f, 0x88, 0x11, 0x30, 0x2e, 0xf1, 0x39, 0x3f, 0x1c, 0x79, 0x13, 0xea, 0x3e,
+ 0x0d, 0x02, 0xcb, 0xe9, 0x8a, 0xfd, 0x46, 0x43, 0x7c, 0x2b, 0x1d, 0x59, 0x86, 0x21, 0x95, 0xfc,
+ 0x04, 0x34, 0xb8, 0xcb, 0x69, 0xd9, 0xeb, 0xfa, 0xf3, 0x0d, 0x1e, 0x2e, 0x36, 0x2d, 0x02, 0xe0,
+ 0x64, 0x21, 0x46, 0x74, 0xf2, 0x25, 0x98, 0xda, 0xe3, 0x9f, 0xaf, 0x3c, 0x2f, 0x2f, 0x6c, 0x5c,
+ 0x5c, 0x5b, 0x6b, 0xc7, 0xca, 0x31, 0xc1, 0x45, 0x96, 0x00, 0x68, 0xe8, 0x97, 0x4b, 0xdb, 0xb3,
+ 0x22, 0x8f, 0x1d, 0xc6, 0xb8, 0xc8, 0xab, 0x50, 0x0a, 0x6c, 0x9f, 0xdb, 0xb0, 0xea, 0xd1, 0x16,
+ 0x74, 0x67, 0xb3, 0x83, 0xac, 0x5c, 0xfb, 0xa3, 0x02, 0xcc, 0xa6, 0x8e, 0x11, 0xb1, 0x2a, 0x03,
+ 0xcf, 0x96, 0xd3, 0x48, 0x58, 0x65, 0x17, 0x37, 0x91, 0x95, 0x93, 0xf7, 0xa5, 0x5a, 0x5e, 0xcc,
+ 0x99, 0x1a, 0xe4, 0x9e, 0x1e, 0xf8, 0x4c, 0x0f, 0x1f, 0xd2, 0xc8, 0xb9, 0x9b, 0x2f, 0x6a, 0x8f,
+ 0x5c, 0x07, 0x62, 0x6e, 0xbe, 0x88, 0x86, 0x09, 0xce, 0x94, 0xc1, 0xaf, 0x7c, 0x16, 0x83, 0x9f,
+ 0xf6, 0xab, 0xc5, 0x58, 0x0f, 0x48, 0xcd, 0xfe, 0x29, 0x3d, 0xf0, 0x45, 0xb6, 0x80, 0x86, 0x8b,
+ 0x7b, 0x23, 0xbe, 0xfe, 0xf1, 0xc5, 0x58, 0x52, 0xc9, 0x3b, 0xa2, 0xef, 0x4b, 0x39, 0x0f, 0x60,
+ 0xef, 0x6c, 0x76, 0x44, 0x74, 0x95, 0x7a, 0x6b, 0xe1, 0x2b, 0x28, 0x5f, 0xd0, 0x2b, 0xd0, 0xfe,
+ 0x45, 0x09, 0x9a, 0x77, 0xdc, 0xbd, 0x4f, 0x49, 0x04, 0x75, 0xf6, 0x32, 0x55, 0xfc, 0x04, 0x97,
+ 0xa9, 0x5d, 0x78, 0x39, 0x08, 0xec, 0x0e, 0x35, 0x5c, 0xc7, 0xf4, 0x97, 0xf7, 0x03, 0xea, 0xad,
+ 0x59, 0x8e, 0xe5, 0x1f, 0x50, 0x53, 0xba, 0x93, 0x3e, 0x77, 0x7a, 0xd2, 0x7a, 0x79, 0x67, 0x67,
+ 0x33, 0x8b, 0x05, 0x47, 0xd5, 0xe5, 0xd3, 0x86, 0x38, 0xf3, 0xc9, 0xcf, 0x44, 0xc9, 0x98, 0x1b,
+ 0x31, 0x6d, 0xc4, 0xca, 0x31, 0xc1, 0xa5, 0x7d, 0xb7, 0x08, 0x8d, 0x30, 0xe9, 0x03, 0xf9, 0x02,
+ 0xd4, 0xf6, 0x3c, 0xf7, 0x90, 0x7a, 0xc2, 0x73, 0x27, 0xcf, 0x44, 0xb5, 0x45, 0x11, 0x2a, 0x1a,
+ 0xf9, 0x3c, 0x54, 0x02, 0xb7, 0x6f, 0x19, 0x69, 0x83, 0xda, 0x0e, 0x2b, 0x44, 0x41, 0xbb, 0xb8,
+ 0x01, 0xfe, 0xc5, 0x84, 0x6a, 0xd7, 0x18, 0xa9, 0x8c, 0xbd, 0x07, 0x65, 0x5f, 0xf7, 0x6d, 0xb9,
+ 0x9e, 0xe6, 0xc8, 0x9f, 0xb0, 0xdc, 0xd9, 0x94, 0xf9, 0x13, 0x96, 0x3b, 0x9b, 0xc8, 0x41, 0xb5,
+ 0x3f, 0x28, 0x42, 0x53, 0xf4, 0x9b, 0x98, 0x15, 0x26, 0xd9, 0x73, 0x6f, 0xf1, 0x50, 0x0a, 0x7f,
+ 0xd0, 0xa3, 0x1e, 0x37, 0x33, 0xc9, 0x49, 0x2e, 0xee, 0x1f, 0x88, 0x88, 0x61, 0x38, 0x45, 0x54,
+ 0xa4, 0xba, 0xbe, 0x7c, 0x81, 0x5d, 0x5f, 0x39, 0x53, 0xd7, 0x57, 0x2f, 0xa2, 0xeb, 0x3f, 0x2e,
+ 0x42, 0x63, 0xd3, 0xda, 0xa7, 0xc6, 0xb1, 0x61, 0xf3, 0xd3, 0x9f, 0x26, 0xb5, 0x69, 0x40, 0xd7,
+ 0x3d, 0xdd, 0xa0, 0xdb, 0xd4, 0xb3, 0x78, 0x52, 0x24, 0xf6, 0x7d, 0xf0, 0x19, 0x48, 0x9e, 0xfe,
+ 0x5c, 0x1d, 0xc1, 0x83, 0x23, 0x6b, 0x93, 0x0d, 0x98, 0x32, 0xa9, 0x6f, 0x79, 0xd4, 0xdc, 0x8e,
+ 0x6d, 0x54, 0xbe, 0xa0, 0x96, 0x9a, 0xd5, 0x18, 0xed, 0xf1, 0x49, 0x6b, 0x5a, 0x19, 0x28, 0xc5,
+ 0x8e, 0x25, 0x51, 0x95, 0x7d, 0xf2, 0x7d, 0x7d, 0xe0, 0x67, 0xb5, 0x31, 0xf6, 0xc9, 0x6f, 0x67,
+ 0xb3, 0xe0, 0xa8, 0xba, 0x5a, 0x05, 0x4a, 0x9b, 0x6e, 0x57, 0xfb, 0x4e, 0x09, 0xc2, 0xec, 0x59,
+ 0xe4, 0xcf, 0x15, 0xa0, 0xa9, 0x3b, 0x8e, 0x1b, 0xc8, 0xcc, 0x54, 0xc2, 0x03, 0x8f, 0xb9, 0x93,
+ 0x74, 0x2d, 0x2c, 0x47, 0xa0, 0xc2, 0x79, 0x1b, 0x3a, 0x94, 0x63, 0x14, 0x8c, 0xcb, 0x26, 0x83,
+ 0x94, 0x3f, 0x79, 0x2b, 0x7f, 0x2b, 0xce, 0xe0, 0x3d, 0xbe, 0xf6, 0x35, 0x98, 0x4b, 0x37, 0xf6,
+ 0x3c, 0xee, 0xa0, 0x5c, 0x8e, 0xf9, 0x22, 0x40, 0x14, 0x53, 0xf2, 0x0c, 0x8c, 0x58, 0x56, 0xc2,
+ 0x88, 0x35, 0x7e, 0x0a, 0x83, 0xa8, 0xd1, 0x23, 0x0d, 0x57, 0xdf, 0x4a, 0x19, 0xae, 0x36, 0x26,
+ 0x21, 0xec, 0xc9, 0xc6, 0xaa, 0xbf, 0x55, 0x80, 0xb9, 0x88, 0x59, 0x9e, 0x85, 0xfe, 0x32, 0x4c,
+ 0x7b, 0x54, 0x37, 0xdb, 0x7a, 0x60, 0x1c, 0xf0, 0x50, 0xef, 0x02, 0x8f, 0xcd, 0xe6, 0xa7, 0xbf,
+ 0x30, 0x4e, 0xc0, 0x24, 0x1f, 0xd1, 0xa1, 0xc9, 0x0a, 0x76, 0xac, 0x1e, 0x75, 0x07, 0xc1, 0x98,
+ 0x56, 0x53, 0xbe, 0x61, 0xc1, 0x08, 0x06, 0xe3, 0x98, 0xda, 0x0f, 0x0a, 0x30, 0x13, 0x6f, 0xf0,
+ 0x85, 0x5b, 0xd4, 0x0e, 0x92, 0x16, 0xb5, 0x95, 0x09, 0xbc, 0x93, 0x11, 0x56, 0xb4, 0x8f, 0x20,
+ 0xfe, 0x68, 0xdc, 0x72, 0x16, 0x37, 0x16, 0x14, 0x9e, 0x68, 0x2c, 0xf8, 0xf4, 0x27, 0x4c, 0x1a,
+ 0xa5, 0xe5, 0x96, 0x9f, 0x63, 0x2d, 0xf7, 0x93, 0xcc, 0xba, 0x14, 0xcb, 0x1c, 0x54, 0xcd, 0x91,
+ 0x39, 0xa8, 0x17, 0x66, 0x0e, 0xaa, 0x4d, 0x6c, 0xd2, 0x39, 0x4b, 0xf6, 0xa0, 0xfa, 0x33, 0xcd,
+ 0x1e, 0xd4, 0xb8, 0xa8, 0xec, 0x41, 0x90, 0x37, 0x7b, 0xd0, 0x47, 0x05, 0x98, 0x31, 0x13, 0x27,
+ 0x66, 0xe5, 0x19, 0xf3, 0xf1, 0x97, 0x9a, 0xe4, 0x01, 0x5c, 0x71, 0x64, 0x2a, 0x59, 0x86, 0x29,
+ 0x91, 0x59, 0x39, 0x7b, 0xa6, 0x3e, 0x99, 0x9c, 0x3d, 0xbf, 0x5f, 0x8b, 0xaf, 0x48, 0xcf, 0xda,
+ 0x68, 0xfe, 0x46, 0xd2, 0x68, 0x7e, 0x23, 0x6d, 0x34, 0x9f, 0x8d, 0xc5, 0xb3, 0xc6, 0x0d, 0xe7,
+ 0x3f, 0x19, 0x9b, 0xa8, 0x4b, 0x3c, 0x5b, 0x4f, 0xf8, 0xce, 0x33, 0x26, 0xeb, 0x65, 0x98, 0x95,
+ 0xda, 0xab, 0x22, 0xf2, 0x59, 0x6e, 0x3a, 0x0a, 0x73, 0x5a, 0x4d, 0x92, 0x31, 0xcd, 0xcf, 0x04,
+ 0xfa, 0x2a, 0x69, 0xab, 0xd8, 0x2a, 0x44, 0x83, 0x4c, 0x25, 0x54, 0x0d, 0x39, 0xd8, 0xb6, 0xc2,
+ 0xa3, 0xba, 0x2f, 0x4d, 0xdf, 0xb1, 0x6d, 0x05, 0xf2, 0x52, 0x94, 0xd4, 0xb8, 0xfd, 0xbf, 0xf6,
+ 0x14, 0xfb, 0xbf, 0x0e, 0x4d, 0x5b, 0xf7, 0x03, 0xf1, 0x36, 0x4d, 0xf9, 0x39, 0xff, 0xb1, 0xb3,
+ 0x2d, 0xbc, 0x6c, 0x31, 0x8f, 0xb4, 0xdb, 0xcd, 0x08, 0x06, 0xe3, 0x98, 0xc4, 0x84, 0x29, 0xf6,
+ 0x97, 0x7f, 0xda, 0xe6, 0x72, 0x20, 0x53, 0x9b, 0x9d, 0x47, 0x46, 0x68, 0xb6, 0xda, 0x8c, 0xe1,
+ 0x60, 0x02, 0x75, 0x84, 0x8b, 0x00, 0xc6, 0x71, 0x11, 0x90, 0x9f, 0x15, 0x9a, 0xd3, 0x71, 0xf8,
+ 0x5a, 0x9b, 0xfc, 0xb5, 0x86, 0x21, 0x92, 0x18, 0x27, 0x62, 0x92, 0x97, 0x8d, 0x8a, 0x81, 0xec,
+ 0x06, 0x55, 0x7d, 0x2a, 0x39, 0x2a, 0x76, 0x93, 0x64, 0x4c, 0xf3, 0x93, 0x6d, 0xb8, 0x1c, 0x16,
+ 0xc5, 0x9b, 0x31, 0xcd, 0x71, 0xc2, 0x98, 0xb5, 0xdd, 0x0c, 0x1e, 0xcc, 0xac, 0xc9, 0x0f, 0x81,
+ 0x0c, 0x3c, 0x8f, 0x3a, 0xc1, 0x6d, 0xdd, 0x3f, 0x90, 0xc1, 0x6f, 0xd1, 0x21, 0x90, 0x88, 0x84,
+ 0x71, 0x3e, 0xb2, 0x04, 0x20, 0xe0, 0x78, 0xad, 0xd9, 0x64, 0x7c, 0xe9, 0x6e, 0x48, 0xc1, 0x18,
+ 0x97, 0xf6, 0x51, 0x03, 0x9a, 0xf7, 0xf4, 0xc0, 0x3a, 0xa2, 0xdc, 0x9f, 0x77, 0x31, 0x4e, 0x95,
+ 0xbf, 0x5a, 0x80, 0x2b, 0xc9, 0xa0, 0xcd, 0x0b, 0xf4, 0xac, 0xf0, 0x64, 0x3f, 0x98, 0x29, 0x0d,
+ 0x47, 0xb4, 0x82, 0xfb, 0x58, 0x86, 0x62, 0x40, 0x2f, 0xda, 0xc7, 0xd2, 0x19, 0x25, 0x10, 0x47,
+ 0xb7, 0xe5, 0xd3, 0xe2, 0x63, 0x79, 0xbe, 0xb3, 0x53, 0xa6, 0x3c, 0x40, 0xb5, 0xe7, 0xc6, 0x03,
+ 0x54, 0x7f, 0x2e, 0xd4, 0xee, 0x7e, 0xcc, 0x03, 0xd4, 0xc8, 0x19, 0x89, 0x24, 0xcf, 0x39, 0x08,
+ 0xb4, 0x51, 0x9e, 0x24, 0x9e, 0xa2, 0x40, 0x59, 0xe6, 0x99, 0xb6, 0xba, 0xa7, 0xfb, 0x96, 0x21,
+ 0xd5, 0x8e, 0x1c, 0xd9, 0x78, 0x55, 0x96, 0x3e, 0x11, 0xb0, 0xc0, 0xff, 0xa2, 0xc0, 0x8e, 0x92,
+ 0x12, 0x16, 0x73, 0x25, 0x25, 0x24, 0x2b, 0x50, 0x76, 0x0e, 0xe9, 0xf1, 0xf9, 0x0e, 0xfb, 0xf3,
+ 0x5d, 0xd8, 0xbd, 0xbb, 0xf4, 0x18, 0x79, 0x65, 0xed, 0xbb, 0x45, 0x00, 0xf6, 0xf8, 0x67, 0xf3,
+ 0xc5, 0xfc, 0x38, 0xd4, 0xfc, 0x01, 0xb7, 0x9a, 0x48, 0x85, 0x29, 0x0a, 0xdf, 0x12, 0xc5, 0xa8,
+ 0xe8, 0xe4, 0xf3, 0x50, 0xf9, 0xd6, 0x80, 0x0e, 0x54, 0x60, 0x41, 0xa8, 0xb8, 0x7f, 0x9d, 0x15,
+ 0xa2, 0xa0, 0x5d, 0x9c, 0x5d, 0x55, 0xf9, 0x6c, 0x2a, 0x17, 0xe5, 0xb3, 0x69, 0x40, 0xed, 0x9e,
+ 0xcb, 0xa3, 0x41, 0xb5, 0xff, 0x56, 0x04, 0x88, 0xa2, 0xed, 0xc8, 0xaf, 0x17, 0xe0, 0xa5, 0xf0,
+ 0x83, 0x0b, 0xc4, 0xfe, 0x8b, 0x27, 0xc0, 0xce, 0xed, 0xbf, 0xc9, 0xfa, 0xd8, 0xf9, 0x0c, 0xb4,
+ 0x9d, 0x25, 0x0e, 0xb3, 0x5b, 0x41, 0x10, 0xea, 0xb4, 0xd7, 0x0f, 0x8e, 0x57, 0x2d, 0x4f, 0x8e,
+ 0xc0, 0xcc, 0xa0, 0xce, 0x5b, 0x92, 0x47, 0x54, 0x95, 0x46, 0x02, 0xfe, 0x11, 0x29, 0x0a, 0x86,
+ 0x38, 0xe4, 0x00, 0xea, 0x8e, 0xfb, 0xbe, 0xcf, 0xba, 0x43, 0x0e, 0xc7, 0xb7, 0xc7, 0xef, 0x72,
+ 0xd1, 0xad, 0xc2, 0xde, 0x2f, 0xff, 0x60, 0xcd, 0x91, 0x9d, 0xfd, 0x6b, 0x45, 0xb8, 0x94, 0xd1,
+ 0x0f, 0xe4, 0x6d, 0x98, 0x93, 0x81, 0x8d, 0x51, 0x26, 0xf8, 0x42, 0x94, 0x09, 0xbe, 0x93, 0xa2,
+ 0xe1, 0x10, 0x37, 0x79, 0x1f, 0x40, 0x37, 0x0c, 0xea, 0xfb, 0x5b, 0xae, 0xa9, 0xf6, 0x03, 0x6f,
+ 0x31, 0xf5, 0x65, 0x39, 0x2c, 0x7d, 0x7c, 0xd2, 0xfa, 0xa9, 0xac, 0x58, 0xe5, 0x54, 0x3f, 0x47,
+ 0x15, 0x30, 0x06, 0x49, 0xbe, 0x09, 0x20, 0x36, 0xe1, 0x61, 0x3a, 0x85, 0xa7, 0x58, 0xae, 0x16,
+ 0x54, 0xb6, 0xae, 0x85, 0xaf, 0x0f, 0x74, 0x27, 0xb0, 0x82, 0x63, 0x91, 0xbd, 0xe6, 0x41, 0x88,
+ 0x82, 0x31, 0x44, 0xed, 0x9f, 0x16, 0xa1, 0xae, 0x6c, 0xe6, 0xcf, 0xc0, 0x50, 0xda, 0x4d, 0x18,
+ 0x4a, 0x27, 0x14, 0x9d, 0x9c, 0x65, 0x26, 0x75, 0x53, 0x66, 0xd2, 0xf5, 0xfc, 0xa2, 0x9e, 0x6c,
+ 0x24, 0xfd, 0xad, 0x22, 0xcc, 0x28, 0xd6, 0xbc, 0x26, 0xd2, 0xaf, 0xc2, 0xac, 0x88, 0x2a, 0xd8,
+ 0xd2, 0x1f, 0x89, 0x44, 0x3e, 0xbc, 0xc3, 0xca, 0x22, 0x20, 0xb8, 0x9d, 0x24, 0x61, 0x9a, 0x97,
+ 0x0d, 0x6b, 0x51, 0xb4, 0xcb, 0x36, 0x61, 0xc2, 0x0f, 0x29, 0xf6, 0x9b, 0x7c, 0x58, 0xb7, 0x53,
+ 0x34, 0x1c, 0xe2, 0x4e, 0xdb, 0x68, 0xcb, 0x17, 0x60, 0xa3, 0xfd, 0xf7, 0x05, 0x98, 0x8a, 0xfa,
+ 0xeb, 0xc2, 0x2d, 0xb4, 0xfb, 0x49, 0x0b, 0xed, 0x72, 0xee, 0xe1, 0x30, 0xc2, 0x3e, 0xfb, 0x17,
+ 0x6b, 0x90, 0x08, 0x92, 0x27, 0x7b, 0x70, 0xcd, 0xca, 0x0c, 0xf5, 0x8b, 0xcd, 0x36, 0xe1, 0xa9,
+ 0xef, 0x8d, 0x91, 0x9c, 0xf8, 0x04, 0x14, 0x32, 0x80, 0xfa, 0x11, 0xf5, 0x02, 0xcb, 0xa0, 0xea,
+ 0xf9, 0xd6, 0x73, 0xab, 0x64, 0xd2, 0x0a, 0x1d, 0xf6, 0xe9, 0x03, 0x29, 0x00, 0x43, 0x51, 0x64,
+ 0x0f, 0x2a, 0xd4, 0xec, 0x52, 0x95, 0x5a, 0x29, 0x67, 0x8a, 0xda, 0xb0, 0x3f, 0xd9, 0x3f, 0x1f,
+ 0x05, 0x34, 0xf1, 0xa1, 0x61, 0x2b, 0x2f, 0xa3, 0x1c, 0x87, 0xe3, 0x2b, 0x58, 0xa1, 0xbf, 0x32,
+ 0xca, 0xba, 0x10, 0x16, 0x61, 0x24, 0x87, 0x1c, 0x86, 0xe6, 0xce, 0xca, 0x84, 0x26, 0x8f, 0x27,
+ 0x18, 0x3b, 0x7d, 0x68, 0x3c, 0xd4, 0x03, 0xea, 0xf5, 0x74, 0xef, 0x50, 0xee, 0x36, 0xc6, 0x7f,
+ 0xc2, 0x77, 0x14, 0x52, 0xf4, 0x84, 0x61, 0x11, 0x46, 0x72, 0x88, 0x0b, 0x8d, 0x40, 0xaa, 0xcf,
+ 0xca, 0xa6, 0x3b, 0xbe, 0x50, 0xa5, 0x88, 0xfb, 0x32, 0x58, 0x5e, 0xfd, 0xc5, 0x48, 0x06, 0x39,
+ 0x4a, 0xe4, 0x33, 0x17, 0x59, 0xec, 0xdb, 0x39, 0x7c, 0x03, 0x12, 0x2a, 0x5a, 0x6e, 0xb2, 0xf3,
+ 0xa2, 0x6b, 0xff, 0xb3, 0x12, 0x4d, 0xcb, 0xcf, 0xda, 0x4e, 0xf8, 0xa5, 0xa4, 0x9d, 0xf0, 0x7a,
+ 0xda, 0x4e, 0x98, 0x72, 0x56, 0x9f, 0x3f, 0xbc, 0x36, 0x65, 0x5e, 0x2b, 0x5f, 0x80, 0x79, 0xed,
+ 0x35, 0x68, 0x1e, 0xf1, 0x99, 0x40, 0xe4, 0x69, 0xaa, 0xf0, 0x65, 0x84, 0xcf, 0xec, 0x0f, 0xa2,
+ 0x62, 0x8c, 0xf3, 0xb0, 0x2a, 0xf2, 0x06, 0x97, 0x30, 0xa5, 0xb1, 0xac, 0xd2, 0x89, 0x8a, 0x31,
+ 0xce, 0xc3, 0x23, 0xf3, 0x2c, 0xe7, 0x50, 0x54, 0xa8, 0xf1, 0x0a, 0x22, 0x32, 0x4f, 0x15, 0x62,
+ 0x44, 0x27, 0x37, 0xa1, 0x3e, 0x30, 0xf7, 0x05, 0x6f, 0x9d, 0xf3, 0x72, 0x0d, 0x73, 0x77, 0x75,
+ 0x4d, 0xe6, 0x8d, 0x52, 0x54, 0xd6, 0x92, 0x9e, 0xde, 0x57, 0x04, 0xbe, 0x37, 0x94, 0x2d, 0xd9,
+ 0x8a, 0x8a, 0x31, 0xce, 0x43, 0x7e, 0x06, 0x66, 0x3c, 0x6a, 0x0e, 0x0c, 0x1a, 0xd6, 0x02, 0x5e,
+ 0x4b, 0x26, 0xd4, 0x8c, 0x53, 0x30, 0xc5, 0x39, 0xc2, 0x48, 0xd8, 0x1c, 0xcb, 0x48, 0xf8, 0x35,
+ 0x98, 0x31, 0x3d, 0xdd, 0x72, 0xa8, 0x79, 0xdf, 0xe1, 0x11, 0x09, 0x32, 0x3e, 0x30, 0xb4, 0x90,
+ 0xaf, 0x26, 0xa8, 0x98, 0xe2, 0xd6, 0xfe, 0x65, 0x11, 0x2a, 0x22, 0xcd, 0xe7, 0x06, 0x5c, 0xb2,
+ 0x1c, 0x2b, 0xb0, 0x74, 0x7b, 0x95, 0xda, 0xfa, 0x71, 0x32, 0x2a, 0xe3, 0x65, 0xb6, 0xd1, 0xde,
+ 0x18, 0x26, 0x63, 0x56, 0x1d, 0xd6, 0x39, 0x81, 0x58, 0xbe, 0x15, 0x8a, 0xb0, 0xa3, 0x89, 0xdc,
+ 0xd0, 0x09, 0x0a, 0xa6, 0x38, 0x99, 0x32, 0xd4, 0xcf, 0x08, 0xb9, 0xe0, 0xca, 0x50, 0x32, 0xd0,
+ 0x22, 0xc9, 0xc7, 0x95, 0xf4, 0x01, 0x57, 0x88, 0xc3, 0x53, 0x38, 0x32, 0xaa, 0x4a, 0x28, 0xe9,
+ 0x29, 0x1a, 0x0e, 0x71, 0x33, 0x84, 0x7d, 0xdd, 0xb2, 0x07, 0x1e, 0x8d, 0x10, 0x2a, 0x11, 0xc2,
+ 0x5a, 0x8a, 0x86, 0x43, 0xdc, 0xda, 0x7f, 0x2f, 0x00, 0x19, 0x3e, 0x57, 0x40, 0x0e, 0xa0, 0xea,
+ 0x70, 0x5b, 0x64, 0xee, 0x94, 0xf4, 0x31, 0x93, 0xa6, 0x58, 0x24, 0x64, 0x81, 0xc4, 0x27, 0x0e,
+ 0xd4, 0xe9, 0xa3, 0x80, 0x7a, 0x4e, 0x78, 0xce, 0x68, 0x32, 0xe9, 0xef, 0xc5, 0xde, 0x4c, 0x22,
+ 0x63, 0x28, 0x43, 0xfb, 0xbd, 0x22, 0x34, 0x63, 0x7c, 0x4f, 0xdb, 0xe2, 0xf3, 0x54, 0x07, 0xc2,
+ 0x04, 0xb8, 0xeb, 0xd9, 0x72, 0xbe, 0x8b, 0xa5, 0x3a, 0x90, 0x24, 0xdc, 0xc4, 0x38, 0x1f, 0x59,
+ 0x02, 0xe8, 0xe9, 0x7e, 0x40, 0x3d, 0xae, 0x0b, 0xa5, 0x12, 0x0c, 0x6c, 0x85, 0x14, 0x8c, 0x71,
+ 0x91, 0x1b, 0xf2, 0x02, 0x83, 0x72, 0x32, 0x21, 0xe4, 0x88, 0xdb, 0x09, 0x2a, 0x13, 0xb8, 0x9d,
+ 0x80, 0x74, 0x61, 0x4e, 0xb5, 0x5a, 0x51, 0xcf, 0x97, 0x2e, 0x50, 0x0c, 0xd4, 0x14, 0x04, 0x0e,
+ 0x81, 0x6a, 0xdf, 0x2d, 0xc0, 0x74, 0xc2, 0x00, 0x25, 0x52, 0x39, 0xaa, 0x53, 0x31, 0x89, 0x54,
+ 0x8e, 0xb1, 0xc3, 0x2c, 0x5f, 0x84, 0xaa, 0xe8, 0xa0, 0x74, 0xb0, 0xab, 0xe8, 0x42, 0x94, 0x54,
+ 0xb6, 0xb2, 0x48, 0x13, 0x77, 0x7a, 0x65, 0x91, 0x36, 0x70, 0x54, 0x74, 0xe1, 0x39, 0x12, 0xad,
+ 0x93, 0x3d, 0x1d, 0xf3, 0x1c, 0x89, 0x72, 0x0c, 0x39, 0xb4, 0x7f, 0xc8, 0xdb, 0x1d, 0x78, 0xc7,
+ 0xe1, 0xce, 0xba, 0x0b, 0x35, 0x19, 0xe0, 0x28, 0x3f, 0x8d, 0xb7, 0x73, 0x58, 0xc5, 0x38, 0x8e,
+ 0x0c, 0xe5, 0xd3, 0x8d, 0xc3, 0xfb, 0xfb, 0xfb, 0xa8, 0xd0, 0xc9, 0x2d, 0x68, 0xb8, 0x8e, 0xfc,
+ 0x82, 0xe5, 0xe3, 0xff, 0x18, 0x5b, 0x39, 0xee, 0xab, 0xc2, 0xc7, 0x27, 0xad, 0x2b, 0xe1, 0x9f,
+ 0x44, 0x23, 0x31, 0xaa, 0xa9, 0xfd, 0xd9, 0x02, 0xbc, 0x84, 0xae, 0x6d, 0x5b, 0x4e, 0x37, 0xe9,
+ 0x7a, 0x24, 0x36, 0xcc, 0xf4, 0xf4, 0x47, 0xbb, 0x8e, 0x7e, 0xa4, 0x5b, 0xb6, 0xbe, 0x67, 0xd3,
+ 0xa7, 0xee, 0x8c, 0x07, 0x81, 0x65, 0x2f, 0x88, 0x0b, 0x1d, 0x17, 0x36, 0x9c, 0xe0, 0xbe, 0xd7,
+ 0x09, 0x3c, 0xcb, 0xe9, 0x8a, 0x59, 0x72, 0x2b, 0x81, 0x85, 0x29, 0x6c, 0xed, 0xf7, 0x4b, 0xc0,
+ 0x83, 0xec, 0xc8, 0x97, 0xa1, 0xd1, 0xa3, 0xc6, 0x81, 0xee, 0x58, 0xbe, 0x4a, 0x8a, 0x7b, 0x95,
+ 0x3d, 0xd7, 0x96, 0x2a, 0x7c, 0xcc, 0x5e, 0xc5, 0x72, 0x67, 0x93, 0x9f, 0x63, 0x89, 0x78, 0x89,
+ 0x01, 0xd5, 0xae, 0xef, 0xeb, 0x7d, 0x2b, 0x77, 0x8c, 0x87, 0x48, 0x42, 0x2a, 0xa6, 0x23, 0xf1,
+ 0x1b, 0x25, 0x34, 0x31, 0xa0, 0xd2, 0xb7, 0x75, 0xcb, 0xc9, 0x7d, 0x01, 0x19, 0x7b, 0x82, 0x6d,
+ 0x86, 0x24, 0x4c, 0x95, 0xfc, 0x27, 0x0a, 0x6c, 0x32, 0x80, 0xa6, 0x6f, 0x78, 0x7a, 0xcf, 0x3f,
+ 0xd0, 0x97, 0x5e, 0x7f, 0x23, 0xb7, 0xf2, 0x1f, 0x89, 0x12, 0xba, 0xc8, 0x0a, 0x2e, 0x6f, 0x75,
+ 0x6e, 0x2f, 0x2f, 0xbd, 0xfe, 0x06, 0xc6, 0xe5, 0xc4, 0xc5, 0xbe, 0xfe, 0xda, 0x92, 0x9c, 0x41,
+ 0x26, 0x2e, 0xf6, 0xf5, 0xd7, 0x96, 0x30, 0x2e, 0x47, 0xfb, 0x5f, 0x05, 0x68, 0x84, 0xbc, 0x64,
+ 0x17, 0x80, 0xcd, 0x65, 0x32, 0x6d, 0xe8, 0xb9, 0x2e, 0x6b, 0xe1, 0xd6, 0x9e, 0xdd, 0xb0, 0x32,
+ 0xc6, 0x80, 0x32, 0xf2, 0xaa, 0x16, 0x27, 0x9d, 0x57, 0x75, 0x11, 0x1a, 0x07, 0xba, 0x63, 0xfa,
+ 0x07, 0xfa, 0xa1, 0x98, 0xd2, 0x63, 0x99, 0x86, 0x6f, 0x2b, 0x02, 0x46, 0x3c, 0xda, 0x3f, 0xae,
+ 0x82, 0x08, 0xcc, 0x60, 0x93, 0x8e, 0x69, 0xf9, 0xe2, 0x64, 0x40, 0x81, 0xd7, 0x0c, 0x27, 0x9d,
+ 0x55, 0x59, 0x8e, 0x21, 0x07, 0xb9, 0x0a, 0xa5, 0x9e, 0xe5, 0x48, 0x0d, 0x84, 0x1b, 0x72, 0xb7,
+ 0x2c, 0x07, 0x59, 0x19, 0x27, 0xe9, 0x8f, 0xa4, 0x86, 0x21, 0x48, 0xfa, 0x23, 0x64, 0x65, 0xe4,
+ 0xab, 0x30, 0x6b, 0xbb, 0xee, 0x21, 0x9b, 0x3e, 0x94, 0x22, 0x22, 0xbc, 0xea, 0xdc, 0xb4, 0xb2,
+ 0x99, 0x24, 0x61, 0x9a, 0x97, 0xec, 0xc2, 0xcb, 0x1f, 0x52, 0xcf, 0x95, 0xf3, 0x65, 0xc7, 0xa6,
+ 0xb4, 0xaf, 0x60, 0x84, 0x6a, 0xcc, 0x43, 0x48, 0x7f, 0x3e, 0x9b, 0x05, 0x47, 0xd5, 0xe5, 0xc1,
+ 0xe8, 0xba, 0xd7, 0xa5, 0xc1, 0xb6, 0xe7, 0x32, 0xdd, 0xc5, 0x72, 0xba, 0x0a, 0xb6, 0x1a, 0xc1,
+ 0xee, 0x64, 0xb3, 0xe0, 0xa8, 0xba, 0xe4, 0x5d, 0x98, 0x17, 0x24, 0xa1, 0xb6, 0x2c, 0x8b, 0x69,
+ 0xc6, 0xb2, 0xd5, 0xbd, 0x9d, 0xd3, 0xc2, 0x5f, 0xb6, 0x33, 0x82, 0x07, 0x47, 0xd6, 0x26, 0x77,
+ 0x60, 0x4e, 0x79, 0x4b, 0xb7, 0xa9, 0xd7, 0x09, 0x83, 0x75, 0xa6, 0xdb, 0xd7, 0x4f, 0x4f, 0x5a,
+ 0xd7, 0x56, 0x69, 0xdf, 0xa3, 0x46, 0xdc, 0xeb, 0xac, 0xb8, 0x70, 0xa8, 0x1e, 0x41, 0xb8, 0xc2,
+ 0x23, 0x72, 0x76, 0xfb, 0x2b, 0xae, 0x6b, 0x9b, 0xee, 0x43, 0x47, 0x3d, 0xbb, 0x50, 0xd8, 0xb9,
+ 0x83, 0xb4, 0x93, 0xc9, 0x81, 0x23, 0x6a, 0xb2, 0x27, 0xe7, 0x94, 0x55, 0xf7, 0xa1, 0x93, 0x46,
+ 0x85, 0xe8, 0xc9, 0x3b, 0x23, 0x78, 0x70, 0x64, 0x6d, 0xb2, 0x06, 0x24, 0xfd, 0x04, 0xbb, 0x7d,
+ 0xe9, 0xc2, 0xbf, 0x22, 0x32, 0x00, 0xa5, 0xa9, 0x98, 0x51, 0x83, 0x6c, 0xc2, 0xe5, 0x74, 0x29,
+ 0x13, 0x27, 0xbd, 0xf9, 0x3c, 0xf7, 0x2f, 0x66, 0xd0, 0x31, 0xb3, 0x96, 0xf6, 0x4f, 0x8a, 0x30,
+ 0x9d, 0x48, 0x19, 0xf1, 0xdc, 0x1d, 0xcd, 0x67, 0x9b, 0x87, 0x9e, 0xdf, 0xdd, 0x58, 0xbd, 0x4d,
+ 0x75, 0x93, 0x7a, 0x77, 0xa9, 0x4a, 0xef, 0x21, 0x96, 0xc5, 0x04, 0x05, 0x53, 0x9c, 0x64, 0x1f,
+ 0x2a, 0xc2, 0x4f, 0x90, 0xf7, 0xda, 0x1f, 0xd5, 0x47, 0xdc, 0x59, 0x20, 0xef, 0xca, 0x72, 0x3d,
+ 0x8a, 0x02, 0x5e, 0x0b, 0x60, 0x2a, 0xce, 0xc1, 0x26, 0x92, 0x48, 0xed, 0xad, 0x25, 0x54, 0xde,
+ 0x0d, 0x28, 0x05, 0xc1, 0xb8, 0x87, 0xfe, 0x85, 0xdf, 0x69, 0x67, 0x13, 0x19, 0x86, 0xb6, 0xcf,
+ 0xde, 0x9d, 0xef, 0x5b, 0xae, 0x23, 0x33, 0xc0, 0xef, 0x42, 0x4d, 0xee, 0x9e, 0xc6, 0x4c, 0x5a,
+ 0xc0, 0x75, 0x25, 0x65, 0x76, 0x55, 0x58, 0xda, 0x7f, 0x28, 0x42, 0x23, 0x34, 0x93, 0x9c, 0x21,
+ 0xb3, 0xba, 0x0b, 0x8d, 0x30, 0xa2, 0x30, 0xf7, 0x9d, 0xa6, 0x51, 0xa0, 0x1b, 0xdf, 0xd9, 0x87,
+ 0x7f, 0x31, 0x92, 0x11, 0x8f, 0x56, 0x2c, 0xe5, 0x88, 0x56, 0xec, 0x43, 0x2d, 0xf0, 0xac, 0x6e,
+ 0x57, 0xee, 0x12, 0xf2, 0x84, 0x2b, 0x86, 0xdd, 0xb5, 0x23, 0x00, 0x65, 0xcf, 0x8a, 0x3f, 0xa8,
+ 0xc4, 0x68, 0x1f, 0xc0, 0x5c, 0x9a, 0x93, 0xab, 0xd0, 0xc6, 0x01, 0x35, 0x07, 0xb6, 0xea, 0xe3,
+ 0x48, 0x85, 0x96, 0xe5, 0x18, 0x72, 0x90, 0x9b, 0x50, 0x67, 0xaf, 0xe9, 0x43, 0xd7, 0x51, 0x6a,
+ 0x2c, 0xdf, 0x8d, 0xec, 0xc8, 0x32, 0x0c, 0xa9, 0xda, 0x7f, 0x2d, 0xc1, 0xd5, 0xc8, 0xd8, 0xb5,
+ 0xa5, 0x3b, 0x7a, 0xf7, 0x0c, 0x17, 0x59, 0x7e, 0x76, 0x0c, 0xec, 0xbc, 0xd7, 0x63, 0x94, 0x9e,
+ 0x83, 0xeb, 0x31, 0xfe, 0x4f, 0x11, 0x78, 0xf4, 0x33, 0xf9, 0x36, 0x4c, 0xe9, 0xb1, 0x3b, 0x8c,
+ 0xe5, 0xeb, 0xbc, 0x95, 0xfb, 0x75, 0xf2, 0x20, 0xeb, 0x30, 0x00, 0x2e, 0x5e, 0x8a, 0x09, 0x81,
+ 0xc4, 0x85, 0xfa, 0xbe, 0x6e, 0xdb, 0x4c, 0x17, 0xca, 0xed, 0xbc, 0x4b, 0x08, 0xe7, 0xc3, 0x7c,
+ 0x4d, 0x42, 0x63, 0x28, 0x84, 0x7c, 0x54, 0x80, 0x69, 0x2f, 0xbe, 0x5d, 0x93, 0x2f, 0x24, 0x4f,
+ 0x68, 0x47, 0x0c, 0x2d, 0x1e, 0x6e, 0x17, 0xdf, 0x13, 0x26, 0x65, 0x6a, 0xff, 0xa5, 0x00, 0xd3,
+ 0x1d, 0xdb, 0x32, 0x2d, 0xa7, 0x7b, 0x81, 0xb7, 0x73, 0xdc, 0x87, 0x8a, 0x6f, 0x5b, 0x26, 0x1d,
+ 0x73, 0x35, 0x11, 0xeb, 0x18, 0x03, 0x40, 0x81, 0x93, 0xbc, 0xee, 0xa3, 0x74, 0x86, 0xeb, 0x3e,
+ 0xfe, 0xb0, 0x0a, 0x32, 0x8e, 0x9f, 0x0c, 0xa0, 0xd1, 0x55, 0xb7, 0x08, 0xc8, 0x67, 0xbc, 0x9d,
+ 0x23, 0x03, 0x65, 0xe2, 0x3e, 0x02, 0x31, 0xf7, 0x87, 0x85, 0x18, 0x49, 0x22, 0x34, 0x79, 0x79,
+ 0xf6, 0x6a, 0xce, 0xcb, 0xb3, 0x85, 0xb8, 0xe1, 0xeb, 0xb3, 0x75, 0x28, 0x1f, 0x04, 0x41, 0x5f,
+ 0x0e, 0xa6, 0xf1, 0x0f, 0x6a, 0x44, 0x49, 0x90, 0x84, 0x4e, 0xc4, 0xfe, 0x23, 0x87, 0x66, 0x22,
+ 0x1c, 0x3d, 0xbc, 0xa2, 0x70, 0x25, 0x57, 0x18, 0x49, 0x5c, 0x04, 0xfb, 0x8f, 0x1c, 0x9a, 0xfc,
+ 0x22, 0x34, 0x03, 0x4f, 0x77, 0xfc, 0x7d, 0xd7, 0xeb, 0x51, 0x4f, 0xee, 0x51, 0xd7, 0x72, 0xdc,
+ 0x1f, 0xbd, 0x13, 0xa1, 0x09, 0x93, 0x6c, 0xa2, 0x08, 0xe3, 0xd2, 0xc8, 0x21, 0xd4, 0x07, 0xa6,
+ 0x68, 0x98, 0x34, 0x83, 0x2d, 0xe7, 0xb9, 0x12, 0x3c, 0x16, 0x24, 0xa2, 0xfe, 0x61, 0x28, 0x20,
+ 0x79, 0x1b, 0x67, 0x6d, 0x52, 0xb7, 0x71, 0xc6, 0x47, 0x63, 0x56, 0x86, 0x16, 0xd2, 0x93, 0x7a,
+ 0xad, 0xd3, 0x95, 0x31, 0x6e, 0x6b, 0xb9, 0x55, 0x4e, 0x21, 0xb2, 0x19, 0xea, 0xc6, 0x4e, 0x17,
+ 0x95, 0x0c, 0xad, 0x07, 0xd2, 0x77, 0x44, 0x8c, 0xc4, 0x4d, 0x46, 0xe2, 0xd8, 0xe0, 0xe2, 0xd9,
+ 0xe6, 0x83, 0xf0, 0x4a, 0x9d, 0x58, 0x26, 0xf5, 0xcc, 0x2b, 0x8b, 0xb4, 0xff, 0x58, 0x84, 0xd2,
+ 0xce, 0x66, 0x47, 0x64, 0x47, 0xe5, 0x77, 0xa3, 0xd1, 0xce, 0xa1, 0xd5, 0x7f, 0x40, 0x3d, 0x6b,
+ 0xff, 0x58, 0x6e, 0xbd, 0x63, 0xd9, 0x51, 0xd3, 0x1c, 0x98, 0x51, 0x8b, 0xbc, 0x07, 0x53, 0x86,
+ 0xbe, 0x42, 0xbd, 0x60, 0x1c, 0xc3, 0x02, 0x3f, 0x1f, 0xbd, 0xb2, 0x1c, 0x55, 0xc7, 0x04, 0x18,
+ 0xd9, 0x05, 0x30, 0x22, 0xe8, 0xd2, 0xb9, 0xcd, 0x21, 0x31, 0xe0, 0x18, 0x10, 0x41, 0x68, 0x1c,
+ 0x32, 0x56, 0x8e, 0x5a, 0x3e, 0x0f, 0x2a, 0x1f, 0x39, 0x77, 0x55, 0x5d, 0x8c, 0x60, 0x34, 0x07,
+ 0xa6, 0x13, 0xd7, 0x1b, 0x91, 0xaf, 0x40, 0xdd, 0xed, 0xc7, 0xa6, 0xd3, 0x06, 0x8f, 0xa6, 0xad,
+ 0xdf, 0x97, 0x65, 0x8f, 0x4f, 0x5a, 0xd3, 0x9b, 0x6e, 0xd7, 0x32, 0x54, 0x01, 0x86, 0xec, 0x44,
+ 0x83, 0x2a, 0x3f, 0xd4, 0xa8, 0x2e, 0x37, 0xe2, 0x6b, 0x07, 0xbf, 0x7f, 0xc4, 0x47, 0x49, 0xd1,
+ 0x7e, 0xa9, 0x0c, 0x91, 0xc7, 0x95, 0xf8, 0x50, 0x15, 0x87, 0x36, 0xe4, 0xcc, 0x7d, 0xa1, 0xe7,
+ 0x43, 0xa4, 0x28, 0xd2, 0x85, 0xd2, 0x07, 0xee, 0x5e, 0xee, 0x89, 0x3b, 0x96, 0xcd, 0x40, 0xd8,
+ 0xca, 0x62, 0x05, 0xc8, 0x24, 0x90, 0xbf, 0x56, 0x80, 0x17, 0xfd, 0xb4, 0xea, 0x2b, 0x87, 0x03,
+ 0xe6, 0xd7, 0xf1, 0xd3, 0xca, 0xb4, 0x0c, 0x7b, 0x1e, 0x45, 0xc6, 0xe1, 0xb6, 0xb0, 0xfe, 0x17,
+ 0xae, 0x50, 0x39, 0x9c, 0xd6, 0x73, 0x5e, 0xbe, 0x9a, 0xec, 0xff, 0x64, 0x19, 0x4a, 0x51, 0xda,
+ 0xaf, 0x14, 0xa1, 0x19, 0x9b, 0xad, 0x73, 0xdf, 0x99, 0xf5, 0x28, 0x75, 0x67, 0xd6, 0xf6, 0xf8,
+ 0x91, 0x01, 0x51, 0xab, 0x2e, 0xfa, 0xda, 0xac, 0x7f, 0x5e, 0x84, 0xd2, 0xee, 0xea, 0x5a, 0x72,
+ 0xd3, 0x5a, 0x78, 0x06, 0x9b, 0xd6, 0x03, 0xa8, 0xed, 0x0d, 0x2c, 0x3b, 0xb0, 0x9c, 0xdc, 0xf9,
+ 0x56, 0xd4, 0x15, 0x63, 0xd2, 0xd7, 0x21, 0x50, 0x51, 0xc1, 0x93, 0x2e, 0xd4, 0xba, 0x22, 0xe1,
+ 0x65, 0xee, 0x78, 0x49, 0x99, 0x38, 0x53, 0x08, 0x92, 0x7f, 0x50, 0xa1, 0x6b, 0xc7, 0x50, 0xdd,
+ 0x5d, 0x95, 0x6a, 0xff, 0xb3, 0xed, 0x4d, 0xed, 0x17, 0x21, 0xd4, 0x02, 0x9e, 0xbd, 0xf0, 0xdf,
+ 0x29, 0x40, 0x52, 0xf1, 0x79, 0xf6, 0xa3, 0xe9, 0x30, 0x3d, 0x9a, 0x56, 0x27, 0xf1, 0xf1, 0x65,
+ 0x0f, 0x28, 0xed, 0xdf, 0x15, 0x20, 0x75, 0xd2, 0x8e, 0xbc, 0x21, 0x73, 0xa7, 0x25, 0x03, 0xd3,
+ 0x54, 0xee, 0x34, 0x92, 0xe4, 0x8e, 0xe5, 0x50, 0xfb, 0x98, 0x6d, 0xd7, 0xe2, 0x0e, 0x34, 0xd9,
+ 0xfc, 0x7b, 0xe3, 0x6f, 0xd7, 0xb2, 0xdc, 0x71, 0x32, 0x78, 0x32, 0x4e, 0xc2, 0xa4, 0x5c, 0xed,
+ 0x1f, 0x15, 0xa1, 0xfa, 0xcc, 0x0e, 0xfe, 0xd3, 0x44, 0x3c, 0xeb, 0x4a, 0xce, 0xd9, 0x7e, 0x64,
+ 0x34, 0x6b, 0x2f, 0x15, 0xcd, 0x9a, 0xf7, 0x4e, 0xef, 0xa7, 0xc4, 0xb2, 0xfe, 0x9b, 0x02, 0xc8,
+ 0xb5, 0x66, 0xc3, 0xf1, 0x03, 0xdd, 0x31, 0x28, 0x31, 0xc2, 0x85, 0x2d, 0x6f, 0xd0, 0x94, 0x0c,
+ 0x2c, 0x14, 0xba, 0x0c, 0xff, 0xad, 0x16, 0x32, 0xf2, 0x93, 0x50, 0x3f, 0x70, 0xfd, 0x80, 0x2f,
+ 0x5e, 0xc5, 0xa4, 0xc9, 0xec, 0xb6, 0x2c, 0xc7, 0x90, 0x23, 0xed, 0xce, 0xae, 0x8c, 0x76, 0x67,
+ 0x6b, 0xbf, 0x59, 0x84, 0xa9, 0x4f, 0x4b, 0xf6, 0x82, 0xac, 0xe8, 0xdf, 0x52, 0xce, 0xe8, 0xdf,
+ 0xf2, 0x79, 0xa2, 0x7f, 0xb5, 0xef, 0x17, 0x00, 0x9e, 0x59, 0xea, 0x04, 0x33, 0x19, 0x98, 0x9b,
+ 0x7b, 0x5c, 0x65, 0x87, 0xe5, 0xfe, 0xfd, 0x8a, 0x7a, 0x24, 0x1e, 0x94, 0xfb, 0x71, 0x01, 0x66,
+ 0xf4, 0x44, 0xa0, 0x6b, 0x6e, 0x7d, 0x39, 0x15, 0x37, 0x1b, 0xc6, 0x69, 0x25, 0xcb, 0x31, 0x25,
+ 0x96, 0xbc, 0x19, 0xa5, 0xed, 0xbe, 0x17, 0x0d, 0xfb, 0xa1, 0x7c, 0xdb, 0x5c, 0x77, 0x4b, 0x70,
+ 0x3e, 0x25, 0xb0, 0xb8, 0x34, 0x91, 0xc0, 0xe2, 0xf8, 0x91, 0xc9, 0xf2, 0x13, 0x8f, 0x4c, 0x1e,
+ 0x41, 0x63, 0xdf, 0x73, 0x7b, 0x3c, 0x76, 0x57, 0x5e, 0x8c, 0x7d, 0x2b, 0xc7, 0x42, 0xd9, 0xdb,
+ 0xb3, 0x1c, 0x6a, 0xf2, 0xb8, 0xe0, 0xd0, 0x70, 0xb5, 0xa6, 0xf0, 0x31, 0x12, 0xc5, 0x6d, 0xfd,
+ 0xae, 0x90, 0x5a, 0x9d, 0xa4, 0xd4, 0x70, 0x2e, 0xd9, 0x11, 0xe8, 0xa8, 0xc4, 0x24, 0xe3, 0x75,
+ 0x6b, 0xcf, 0x26, 0x5e, 0x57, 0xfb, 0x0b, 0x35, 0x35, 0x81, 0x3d, 0x77, 0x19, 0x62, 0x3f, 0x3b,
+ 0xe8, 0xde, 0xa5, 0x43, 0xa7, 0xd0, 0xeb, 0xcf, 0xf0, 0x14, 0x7a, 0x63, 0x32, 0xa7, 0xd0, 0x21,
+ 0xdf, 0x29, 0xf4, 0xe6, 0x84, 0x4e, 0xa1, 0x4f, 0x4d, 0xea, 0x14, 0xfa, 0xf4, 0x58, 0xa7, 0xd0,
+ 0x67, 0xce, 0x74, 0x0a, 0xfd, 0xa4, 0x04, 0xa9, 0xcd, 0xf8, 0x67, 0x8e, 0xb7, 0xff, 0xa7, 0x1c,
+ 0x6f, 0xdf, 0x29, 0x42, 0x34, 0x11, 0x9f, 0x33, 0x30, 0xe9, 0x5d, 0xa8, 0xf7, 0xf4, 0x47, 0x3c,
+ 0x70, 0x3a, 0xcf, 0xc5, 0xca, 0x5b, 0x12, 0x03, 0x43, 0x34, 0xe2, 0x03, 0x58, 0xe1, 0xe5, 0x06,
+ 0xb9, 0x5d, 0x18, 0xd1, 0x3d, 0x09, 0xc2, 0x48, 0x1a, 0xfd, 0xc7, 0x98, 0x18, 0xed, 0x5f, 0x17,
+ 0x41, 0xde, 0x82, 0x41, 0x28, 0x54, 0xf6, 0xad, 0x47, 0xd4, 0xcc, 0x1d, 0xee, 0x1c, 0xbb, 0xee,
+ 0x5e, 0xf8, 0x68, 0x78, 0x01, 0x0a, 0x74, 0x6e, 0x7c, 0x17, 0x3e, 0x37, 0xd9, 0x7f, 0x39, 0x8c,
+ 0xef, 0x71, 0xdf, 0x9d, 0x34, 0xbe, 0x8b, 0x22, 0x54, 0x32, 0x84, 0xad, 0x9f, 0x87, 0x5f, 0xe4,
+ 0x76, 0x31, 0x26, 0xc2, 0x38, 0x94, 0xad, 0xdf, 0x17, 0x69, 0x28, 0xa4, 0x8c, 0xf6, 0x2f, 0x7c,
+ 0xef, 0x87, 0xd7, 0x5f, 0xf8, 0xfe, 0x0f, 0xaf, 0xbf, 0xf0, 0x83, 0x1f, 0x5e, 0x7f, 0xe1, 0x97,
+ 0x4e, 0xaf, 0x17, 0xbe, 0x77, 0x7a, 0xbd, 0xf0, 0xfd, 0xd3, 0xeb, 0x85, 0x1f, 0x9c, 0x5e, 0x2f,
+ 0xfc, 0xa7, 0xd3, 0xeb, 0x85, 0xbf, 0xfc, 0x9f, 0xaf, 0xbf, 0xf0, 0xf3, 0x5f, 0x8e, 0x9a, 0xb0,
+ 0xa8, 0x9a, 0xb0, 0xa8, 0x04, 0x2e, 0xf6, 0x0f, 0xbb, 0x8b, 0xac, 0x09, 0x51, 0x89, 0x6a, 0xc2,
+ 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xea, 0x57, 0x1d, 0x48, 0x12, 0x9e, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -4053,6 +4091,30 @@ func (m *Container) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ if m.LivenessProbe != nil {
+ {
+ size, err := m.LivenessProbe.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x5a
+ }
+ if m.ReadinessProbe != nil {
+ {
+ size, err := m.ReadinessProbe.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x52
+ }
if m.ImagePullPolicy != nil {
i -= len(*m.ImagePullPolicy)
copy(dAtA[i:], *m.ImagePullPolicy)
@@ -4170,6 +4232,30 @@ func (m *ContainerTemplate) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ if m.LivenessProbe != nil {
+ {
+ size, err := m.LivenessProbe.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x3a
+ }
+ if m.ReadinessProbe != nil {
+ {
+ size, err := m.ReadinessProbe.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x32
+ }
if len(m.EnvFrom) > 0 {
for iNdEx := len(m.EnvFrom) - 1; iNdEx >= 0; iNdEx-- {
{
@@ -7380,6 +7466,54 @@ func (m *PipelineStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *Probe) 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 *Probe) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Probe) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if m.FailureThreshold != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.FailureThreshold))
+ i--
+ dAtA[i] = 0x28
+ }
+ if m.SuccessThreshold != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.SuccessThreshold))
+ i--
+ dAtA[i] = 0x20
+ }
+ if m.PeriodSeconds != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.PeriodSeconds))
+ i--
+ dAtA[i] = 0x18
+ }
+ if m.TimeoutSeconds != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.TimeoutSeconds))
+ i--
+ dAtA[i] = 0x10
+ }
+ if m.InitialDelaySeconds != nil {
+ i = encodeVarintGenerated(dAtA, i, uint64(*m.InitialDelaySeconds))
+ i--
+ dAtA[i] = 0x8
+ }
+ return len(dAtA) - i, nil
+}
+
func (m *RedisBufferService) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -9715,6 +9849,14 @@ func (m *Container) Size() (n int) {
l = len(*m.ImagePullPolicy)
n += 1 + l + sovGenerated(uint64(l))
}
+ if m.ReadinessProbe != nil {
+ l = m.ReadinessProbe.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ if m.LivenessProbe != nil {
+ l = m.LivenessProbe.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
return n
}
@@ -9744,6 +9886,14 @@ func (m *ContainerTemplate) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
}
}
+ if m.ReadinessProbe != nil {
+ l = m.ReadinessProbe.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ if m.LivenessProbe != nil {
+ l = m.LivenessProbe.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
return n
}
@@ -10892,6 +11042,30 @@ func (m *PipelineStatus) Size() (n int) {
return n
}
+func (m *Probe) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.InitialDelaySeconds != nil {
+ n += 1 + sovGenerated(uint64(*m.InitialDelaySeconds))
+ }
+ if m.TimeoutSeconds != nil {
+ n += 1 + sovGenerated(uint64(*m.TimeoutSeconds))
+ }
+ if m.PeriodSeconds != nil {
+ n += 1 + sovGenerated(uint64(*m.PeriodSeconds))
+ }
+ if m.SuccessThreshold != nil {
+ n += 1 + sovGenerated(uint64(*m.SuccessThreshold))
+ }
+ if m.FailureThreshold != nil {
+ n += 1 + sovGenerated(uint64(*m.FailureThreshold))
+ }
+ return n
+}
+
func (m *RedisBufferService) Size() (n int) {
if m == nil {
return 0
@@ -11838,6 +12012,8 @@ func (this *Container) String() string {
`Resources:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Resources), "ResourceRequirements", "v1.ResourceRequirements", 1), `&`, ``, 1) + `,`,
`SecurityContext:` + strings.Replace(fmt.Sprintf("%v", this.SecurityContext), "SecurityContext", "v1.SecurityContext", 1) + `,`,
`ImagePullPolicy:` + valueToStringGenerated(this.ImagePullPolicy) + `,`,
+ `ReadinessProbe:` + strings.Replace(this.ReadinessProbe.String(), "Probe", "Probe", 1) + `,`,
+ `LivenessProbe:` + strings.Replace(this.LivenessProbe.String(), "Probe", "Probe", 1) + `,`,
`}`,
}, "")
return s
@@ -11862,6 +12038,8 @@ func (this *ContainerTemplate) String() string {
`SecurityContext:` + strings.Replace(fmt.Sprintf("%v", this.SecurityContext), "SecurityContext", "v1.SecurityContext", 1) + `,`,
`Env:` + repeatedStringForEnv + `,`,
`EnvFrom:` + repeatedStringForEnvFrom + `,`,
+ `ReadinessProbe:` + strings.Replace(this.ReadinessProbe.String(), "Probe", "Probe", 1) + `,`,
+ `LivenessProbe:` + strings.Replace(this.LivenessProbe.String(), "Probe", "Probe", 1) + `,`,
`}`,
}, "")
return s
@@ -12679,6 +12857,20 @@ func (this *PipelineStatus) String() string {
}, "")
return s
}
+func (this *Probe) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&Probe{`,
+ `InitialDelaySeconds:` + valueToStringGenerated(this.InitialDelaySeconds) + `,`,
+ `TimeoutSeconds:` + valueToStringGenerated(this.TimeoutSeconds) + `,`,
+ `PeriodSeconds:` + valueToStringGenerated(this.PeriodSeconds) + `,`,
+ `SuccessThreshold:` + valueToStringGenerated(this.SuccessThreshold) + `,`,
+ `FailureThreshold:` + valueToStringGenerated(this.FailureThreshold) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func (this *RedisBufferService) String() string {
if this == nil {
return "nil"
@@ -15624,6 +15816,78 @@ func (m *Container) Unmarshal(dAtA []byte) error {
s := k8s_io_api_core_v1.PullPolicy(dAtA[iNdEx:postIndex])
m.ImagePullPolicy = &s
iNdEx = postIndex
+ case 10:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ReadinessProbe", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.ReadinessProbe == nil {
+ m.ReadinessProbe = &Probe{}
+ }
+ if err := m.ReadinessProbe.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 11:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field LivenessProbe", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.LivenessProbe == nil {
+ m.LivenessProbe = &Probe{}
+ }
+ if err := m.LivenessProbe.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -15843,6 +16107,78 @@ func (m *ContainerTemplate) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 6:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ReadinessProbe", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.ReadinessProbe == nil {
+ m.ReadinessProbe = &Probe{}
+ }
+ if err := m.ReadinessProbe.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 7:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field LivenessProbe", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.LivenessProbe == nil {
+ m.LivenessProbe = &Probe{}
+ }
+ if err := m.LivenessProbe.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -26300,6 +26636,156 @@ func (m *PipelineStatus) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *Probe) 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 ErrIntOverflowGenerated
+ }
+ 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: Probe: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: Probe: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field InitialDelaySeconds", wireType)
+ }
+ var v int32
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= int32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.InitialDelaySeconds = &v
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field TimeoutSeconds", wireType)
+ }
+ var v int32
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= int32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.TimeoutSeconds = &v
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field PeriodSeconds", wireType)
+ }
+ var v int32
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= int32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.PeriodSeconds = &v
+ case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field SuccessThreshold", wireType)
+ }
+ var v int32
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= int32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.SuccessThreshold = &v
+ case 5:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field FailureThreshold", wireType)
+ }
+ var v int32
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= int32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.FailureThreshold = &v
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *RedisBufferService) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 70936b9d47..035f4cf46b 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -304,6 +304,12 @@ message Container {
// +optional
optional string imagePullPolicy = 9;
+
+ // +optional
+ optional Probe readinessProbe = 10;
+
+ // +optional
+ optional Probe livenessProbe = 11;
}
// ContainerTemplate defines customized spec for a container
@@ -322,6 +328,12 @@ message ContainerTemplate {
// +optional
repeated k8s.io.api.core.v1.EnvFromSource envFrom = 5;
+
+ // +optional
+ optional Probe readinessProbe = 6;
+
+ // +optional
+ optional Probe livenessProbe = 7;
}
message DaemonTemplate {
@@ -1240,6 +1252,33 @@ message PipelineStatus {
optional bool drainedOnPause = 12;
}
+// Probe is used to customize the configuration for Readiness and Liveness probes.
+message Probe {
+ // Number of seconds after the container has started before liveness probes are initiated.
+ // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+ // +optional
+ optional int32 initialDelaySeconds = 1;
+
+ // Number of seconds after which the probe times out.
+ // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+ // +optional
+ optional int32 timeoutSeconds = 2;
+
+ // How often (in seconds) to perform the probe.
+ // +optional
+ optional int32 periodSeconds = 3;
+
+ // Minimum consecutive successes for the probe to be considered successful after having failed.
+ // Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+ // +optional
+ optional int32 successThreshold = 4;
+
+ // Minimum consecutive failures for the probe to be considered failed after having succeeded.
+ // Defaults to 3. Minimum value is 1.
+ // +optional
+ optional int32 failureThreshold = 5;
+}
+
message RedisBufferService {
// Native brings up a native Redis service
optional NativeRedis native = 1;
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index 934d497878..e3b8e28f64 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -345,6 +345,18 @@ func (mv MonoVertex) GetPodSpec(req GetMonoVertexPodSpecReq) (*corev1.PodSpec, e
volumeMounts: volumeMounts,
})
+ var readyzInitDeploy, readyzPeriodSeconds, readyzTimeoutSeconds, readyzFailureThreshold int32 = NumaContainerReadyzInitialDelaySeconds, NumaContainerReadyzPeriodSeconds, NumaContainerReadyzTimeoutSeconds, NumaContainerReadyzFailureThreshold
+ var liveZInitDeploy, liveZPeriodSeconds, liveZTimeoutSeconds, liveZFailureThreshold int32 = NumaContainerLivezInitialDelaySeconds, NumaContainerLivezPeriodSeconds, NumaContainerLivezTimeoutSeconds, NumaContainerLivezFailureThreshold
+ if x := mv.Spec.ContainerTemplate; x != nil {
+ readyzInitDeploy = GetProbeInitialDelaySecondsOr(x.ReadinessProbe, readyzInitDeploy)
+ readyzPeriodSeconds = GetProbePeriodSecondsOr(x.ReadinessProbe, readyzPeriodSeconds)
+ readyzTimeoutSeconds = GetProbeTimeoutSecondsOr(x.ReadinessProbe, readyzTimeoutSeconds)
+ readyzFailureThreshold = GetProbeFailureThresholdOr(x.ReadinessProbe, readyzFailureThreshold)
+ liveZInitDeploy = GetProbeInitialDelaySecondsOr(x.LivenessProbe, liveZInitDeploy)
+ liveZPeriodSeconds = GetProbePeriodSecondsOr(x.LivenessProbe, liveZPeriodSeconds)
+ liveZTimeoutSeconds = GetProbeTimeoutSecondsOr(x.LivenessProbe, liveZTimeoutSeconds)
+ liveZFailureThreshold = GetProbeFailureThresholdOr(x.LivenessProbe, liveZFailureThreshold)
+ }
containers[0].ReadinessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
@@ -353,9 +365,10 @@ func (mv MonoVertex) GetPodSpec(req GetMonoVertexPodSpecReq) (*corev1.PodSpec, e
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 3,
- PeriodSeconds: 3,
- TimeoutSeconds: 1,
+ InitialDelaySeconds: readyzInitDeploy,
+ PeriodSeconds: readyzPeriodSeconds,
+ TimeoutSeconds: readyzTimeoutSeconds,
+ FailureThreshold: readyzFailureThreshold,
}
containers[0].LivenessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
@@ -365,9 +378,10 @@ func (mv MonoVertex) GetPodSpec(req GetMonoVertexPodSpecReq) (*corev1.PodSpec, e
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 20,
- PeriodSeconds: 60,
- TimeoutSeconds: 30,
+ InitialDelaySeconds: liveZInitDeploy,
+ PeriodSeconds: liveZPeriodSeconds,
+ TimeoutSeconds: liveZTimeoutSeconds,
+ FailureThreshold: liveZFailureThreshold,
}
containers[0].Ports = []corev1.ContainerPort{
{Name: MonoVertexMetricsPortName, ContainerPort: MonoVertexMetricsPort},
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
index 665acd8b32..ae6a62c09d 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
@@ -34,6 +34,20 @@ var (
Namespace: "default",
},
Spec: MonoVertexSpec{
+ ContainerTemplate: &ContainerTemplate{
+ ReadinessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](24),
+ PeriodSeconds: ptr.To[int32](25),
+ FailureThreshold: ptr.To[int32](2),
+ TimeoutSeconds: ptr.To[int32](21),
+ },
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](14),
+ PeriodSeconds: ptr.To[int32](15),
+ FailureThreshold: ptr.To[int32](1),
+ TimeoutSeconds: ptr.To[int32](11),
+ },
+ },
Scale: Scale{
Min: ptr.To[int32](2),
Max: ptr.To[int32](4),
@@ -195,6 +209,16 @@ func TestMonoVertexGetPodSpec(t *testing.T) {
}
assert.Contains(t, envNames, "ENV_VAR_NAME")
assert.Contains(t, envNames, EnvMonoVertexObject)
+ assert.NotNil(t, podSpec.Containers[0].ReadinessProbe)
+ assert.Equal(t, int32(24), podSpec.Containers[0].ReadinessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(25), podSpec.Containers[0].ReadinessProbe.PeriodSeconds)
+ assert.Equal(t, int32(2), podSpec.Containers[0].ReadinessProbe.FailureThreshold)
+ assert.Equal(t, int32(21), podSpec.Containers[0].ReadinessProbe.TimeoutSeconds)
+ assert.NotNil(t, podSpec.Containers[0].LivenessProbe)
+ assert.Equal(t, int32(14), podSpec.Containers[0].LivenessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(15), podSpec.Containers[0].LivenessProbe.PeriodSeconds)
+ assert.Equal(t, int32(1), podSpec.Containers[0].LivenessProbe.FailureThreshold)
+ assert.Equal(t, int32(11), podSpec.Containers[0].LivenessProbe.TimeoutSeconds)
})
}
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index 5987162567..bd59769897 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -89,6 +89,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.PipelineList": schema_pkg_apis_numaflow_v1alpha1_PipelineList(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.PipelineSpec": schema_pkg_apis_numaflow_v1alpha1_PipelineSpec(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.PipelineStatus": schema_pkg_apis_numaflow_v1alpha1_PipelineStatus(ref),
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Probe": schema_pkg_apis_numaflow_v1alpha1_Probe(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisBufferService": schema_pkg_apis_numaflow_v1alpha1_RedisBufferService(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisConfig": schema_pkg_apis_numaflow_v1alpha1_RedisConfig(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.RedisSettings": schema_pkg_apis_numaflow_v1alpha1_RedisSettings(ref),
@@ -890,11 +891,21 @@ func schema_pkg_apis_numaflow_v1alpha1_Container(ref common.ReferenceCallback) c
Format: "",
},
},
+ "readinessProbe": {
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Probe"),
+ },
+ },
+ "livenessProbe": {
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Probe"),
+ },
+ },
},
},
},
Dependencies: []string{
- "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeMount"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Probe", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeMount"},
}
}
@@ -948,11 +959,21 @@ func schema_pkg_apis_numaflow_v1alpha1_ContainerTemplate(ref common.ReferenceCal
},
},
},
+ "readinessProbe": {
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Probe"),
+ },
+ },
+ "livenessProbe": {
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Probe"),
+ },
+ },
},
},
},
Dependencies: []string{
- "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Probe", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext"},
}
}
@@ -4171,6 +4192,54 @@ func schema_pkg_apis_numaflow_v1alpha1_PipelineStatus(ref common.ReferenceCallba
}
}
+func schema_pkg_apis_numaflow_v1alpha1_Probe(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Probe is used to customize the configuration for Readiness and Liveness probes.",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "initialDelaySeconds": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ Type: []string{"integer"},
+ Format: "int32",
+ },
+ },
+ "timeoutSeconds": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Number of seconds after which the probe times out. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ Type: []string{"integer"},
+ Format: "int32",
+ },
+ },
+ "periodSeconds": {
+ SchemaProps: spec.SchemaProps{
+ Description: "How often (in seconds) to perform the probe.",
+ Type: []string{"integer"},
+ Format: "int32",
+ },
+ },
+ "successThreshold": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ Type: []string{"integer"},
+ Format: "int32",
+ },
+ },
+ "failureThreshold": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ Type: []string{"integer"},
+ Format: "int32",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
func schema_pkg_apis_numaflow_v1alpha1_RedisBufferService(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
diff --git a/pkg/apis/numaflow/v1alpha1/probe.go b/pkg/apis/numaflow/v1alpha1/probe.go
new file mode 100644
index 0000000000..44c2801f94
--- /dev/null
+++ b/pkg/apis/numaflow/v1alpha1/probe.go
@@ -0,0 +1,74 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+// Probe is used to customize the configuration for Readiness and Liveness probes.
+type Probe struct {
+ // Number of seconds after the container has started before liveness probes are initiated.
+ // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+ // +optional
+ InitialDelaySeconds *int32 `json:"initialDelaySeconds,omitempty" protobuf:"varint,1,opt,name=initialDelaySeconds"`
+ // Number of seconds after which the probe times out.
+ // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+ // +optional
+ TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty" protobuf:"varint,2,opt,name=timeoutSeconds"`
+ // How often (in seconds) to perform the probe.
+ // +optional
+ PeriodSeconds *int32 `json:"periodSeconds,omitempty" protobuf:"varint,3,opt,name=periodSeconds"`
+ // Minimum consecutive successes for the probe to be considered successful after having failed.
+ // Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+ // +optional
+ SuccessThreshold *int32 `json:"successThreshold,omitempty" protobuf:"varint,4,opt,name=successThreshold"`
+ // Minimum consecutive failures for the probe to be considered failed after having succeeded.
+ // Defaults to 3. Minimum value is 1.
+ // +optional
+ FailureThreshold *int32 `json:"failureThreshold,omitempty" protobuf:"varint,5,opt,name=failureThreshold"`
+}
+
+func GetProbeInitialDelaySecondsOr(probe *Probe, defaultValue int32) int32 {
+ if probe == nil || probe.InitialDelaySeconds == nil {
+ return defaultValue
+ }
+ return *probe.InitialDelaySeconds
+}
+
+func GetProbeTimeoutSecondsOr(probe *Probe, defaultValue int32) int32 {
+ if probe == nil || probe.TimeoutSeconds == nil {
+ return defaultValue
+ }
+ return *probe.TimeoutSeconds
+}
+
+func GetProbePeriodSecondsOr(probe *Probe, defaultValue int32) int32 {
+ if probe == nil || probe.PeriodSeconds == nil {
+ return defaultValue
+ }
+ return *probe.PeriodSeconds
+}
+
+func GetProbeSuccessThresholdOr(probe *Probe, defaultValue int32) int32 {
+ if probe == nil || probe.SuccessThreshold == nil {
+ return defaultValue
+ }
+ return *probe.SuccessThreshold
+}
+func GetProbeFailureThresholdOr(probe *Probe, defaultValue int32) int32 {
+ if probe == nil || probe.FailureThreshold == nil {
+ return defaultValue
+ }
+ return *probe.FailureThreshold
+}
diff --git a/pkg/apis/numaflow/v1alpha1/probe_test.go b/pkg/apis/numaflow/v1alpha1/probe_test.go
new file mode 100644
index 0000000000..8383944451
--- /dev/null
+++ b/pkg/apis/numaflow/v1alpha1/probe_test.go
@@ -0,0 +1,199 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "k8s.io/utils/ptr"
+)
+
+func TestGetProbeInitialDelaySecondsOr(t *testing.T) {
+ tests := []struct {
+ name string
+ probe *Probe
+ defaultValue int32
+ expected int32
+ }{
+ {
+ name: "nil probe",
+ probe: nil,
+ defaultValue: 10,
+ expected: 10,
+ },
+ {
+ name: "nil InitialDelaySeconds",
+ probe: &Probe{},
+ defaultValue: 5,
+ expected: 5,
+ },
+ {
+ name: "non-nil InitialDelaySeconds",
+ probe: &Probe{InitialDelaySeconds: ptr.To[int32](15)},
+ defaultValue: 10,
+ expected: 15,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := GetProbeInitialDelaySecondsOr(tt.probe, tt.defaultValue)
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
+func TestGetProbeTimeoutSeconds(t *testing.T) {
+ tests := []struct {
+ name string
+ probe *Probe
+ defaultValue int32
+ expected int32
+ }{
+ {
+ name: "nil probe",
+ probe: nil,
+ defaultValue: 5,
+ expected: 5,
+ },
+ {
+ name: "nil TimeoutSeconds",
+ probe: &Probe{},
+ defaultValue: 3,
+ expected: 3,
+ },
+ {
+ name: "non-nil TimeoutSeconds",
+ probe: &Probe{TimeoutSeconds: ptr.To[int32](8)},
+ defaultValue: 5,
+ expected: 8,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := GetProbeTimeoutSecondsOr(tt.probe, tt.defaultValue)
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
+func TestGetProbePeriodSeconds(t *testing.T) {
+ tests := []struct {
+ name string
+ probe *Probe
+ defaultValue int32
+ expected int32
+ }{
+ {
+ name: "nil probe",
+ probe: nil,
+ defaultValue: 10,
+ expected: 10,
+ },
+ {
+ name: "nil PeriodSeconds",
+ probe: &Probe{},
+ defaultValue: 15,
+ expected: 15,
+ },
+ {
+ name: "non-nil PeriodSeconds",
+ probe: &Probe{PeriodSeconds: ptr.To[int32](20)},
+ defaultValue: 10,
+ expected: 20,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := GetProbePeriodSecondsOr(tt.probe, tt.defaultValue)
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
+func TestGetProbeSuccessThreshold(t *testing.T) {
+ tests := []struct {
+ name string
+ probe *Probe
+ defaultValue int32
+ expected int32
+ }{
+ {
+ name: "nil probe",
+ probe: nil,
+ defaultValue: 1,
+ expected: 1,
+ },
+ {
+ name: "nil SuccessThreshold",
+ probe: &Probe{},
+ defaultValue: 2,
+ expected: 2,
+ },
+ {
+ name: "non-nil SuccessThreshold",
+ probe: &Probe{SuccessThreshold: ptr.To[int32](3)},
+ defaultValue: 1,
+ expected: 3,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := GetProbeSuccessThresholdOr(tt.probe, tt.defaultValue)
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
+func TestGetProbeFailureThreshold(t *testing.T) {
+ tests := []struct {
+ name string
+ probe *Probe
+ defaultValue int32
+ expected int32
+ }{
+ {
+ name: "nil probe",
+ probe: nil,
+ defaultValue: 3,
+ expected: 3,
+ },
+ {
+ name: "nil FailureThreshold",
+ probe: &Probe{},
+ defaultValue: 5,
+ expected: 5,
+ },
+ {
+ name: "non-nil FailureThreshold",
+ probe: &Probe{FailureThreshold: ptr.To[int32](7)},
+ defaultValue: 3,
+ expected: 7,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := GetProbeFailureThresholdOr(tt.probe, tt.defaultValue)
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
diff --git a/pkg/apis/numaflow/v1alpha1/sink.go b/pkg/apis/numaflow/v1alpha1/sink.go
index d596a079e6..d35f323e9e 100644
--- a/pkg/apis/numaflow/v1alpha1/sink.go
+++ b/pkg/apis/numaflow/v1alpha1/sink.go
@@ -92,9 +92,10 @@ func (s Sink) getUDSinkContainer(mainContainerReq getContainerReq) corev1.Contai
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 30,
- PeriodSeconds: 60,
- TimeoutSeconds: 30,
+ InitialDelaySeconds: GetProbeInitialDelaySecondsOr(x.LivenessProbe, UDContainerLivezInitialDelaySeconds),
+ PeriodSeconds: GetProbePeriodSecondsOr(x.LivenessProbe, UDContainerLivezPeriodSeconds),
+ TimeoutSeconds: GetProbeTimeoutSecondsOr(x.LivenessProbe, UDContainerLivezTimeoutSeconds),
+ FailureThreshold: GetProbeFailureThresholdOr(x.LivenessProbe, UDContainerLivezFailureThreshold),
}
return container
}
@@ -126,9 +127,10 @@ func (s Sink) getFallbackUDSinkContainer(mainContainerReq getContainerReq) corev
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 30,
- PeriodSeconds: 60,
- TimeoutSeconds: 30,
+ InitialDelaySeconds: GetProbeInitialDelaySecondsOr(x.LivenessProbe, UDContainerLivezInitialDelaySeconds),
+ PeriodSeconds: GetProbePeriodSecondsOr(x.LivenessProbe, UDContainerLivezPeriodSeconds),
+ TimeoutSeconds: GetProbeTimeoutSecondsOr(x.LivenessProbe, UDContainerLivezTimeoutSeconds),
+ FailureThreshold: GetProbeFailureThresholdOr(x.LivenessProbe, UDContainerLivezFailureThreshold),
}
return container
}
diff --git a/pkg/apis/numaflow/v1alpha1/sink_test.go b/pkg/apis/numaflow/v1alpha1/sink_test.go
index 0fe7f002af..f63cb751bd 100644
--- a/pkg/apis/numaflow/v1alpha1/sink_test.go
+++ b/pkg/apis/numaflow/v1alpha1/sink_test.go
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
resource "k8s.io/apimachinery/pkg/api/resource"
+ "k8s.io/utils/ptr"
)
func Test_Sink_getContainers(t *testing.T) {
@@ -52,6 +53,12 @@ func Test_Sink_getUDSinkContainer(t *testing.T) {
EnvFrom: []corev1.EnvFromSource{{ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{Name: "test-cm"},
}}},
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](10),
+ TimeoutSeconds: ptr.To[int32](15),
+ PeriodSeconds: ptr.To[int32](14),
+ FailureThreshold: ptr.To[int32](5),
+ },
},
},
},
@@ -78,6 +85,10 @@ func Test_Sink_getUDSinkContainer(t *testing.T) {
})
assert.Equal(t, testImagePullPolicy, c.ImagePullPolicy)
assert.True(t, c.LivenessProbe != nil)
+ assert.Equal(t, int32(10), c.LivenessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(15), c.LivenessProbe.TimeoutSeconds)
+ assert.Equal(t, int32(14), c.LivenessProbe.PeriodSeconds)
+ assert.Equal(t, int32(5), c.LivenessProbe.FailureThreshold)
}
func Test_Sink_getFallbackUDSinkContainer(t *testing.T) {
@@ -103,6 +114,12 @@ func Test_Sink_getFallbackUDSinkContainer(t *testing.T) {
EnvFrom: []corev1.EnvFromSource{{ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{Name: "test-cm"},
}}},
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](20),
+ TimeoutSeconds: ptr.To[int32](25),
+ PeriodSeconds: ptr.To[int32](24),
+ FailureThreshold: ptr.To[int32](10),
+ },
},
},
},
@@ -122,6 +139,11 @@ func Test_Sink_getFallbackUDSinkContainer(t *testing.T) {
envs[e.Name] = e.Value
}
assert.Equal(t, envs[EnvUDContainerType], UDContainerFallbackSink)
+ assert.True(t, c.LivenessProbe != nil)
+ assert.Equal(t, int32(20), c.LivenessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(25), c.LivenessProbe.TimeoutSeconds)
+ assert.Equal(t, int32(24), c.LivenessProbe.PeriodSeconds)
+ assert.Equal(t, int32(10), c.LivenessProbe.FailureThreshold)
x.UDSink.Container.ImagePullPolicy = &testImagePullPolicy
c = x.getUDSinkContainer(getContainerReq{
image: "main-image",
diff --git a/pkg/apis/numaflow/v1alpha1/source.go b/pkg/apis/numaflow/v1alpha1/source.go
index bc78831ff6..b89016d9c2 100644
--- a/pkg/apis/numaflow/v1alpha1/source.go
+++ b/pkg/apis/numaflow/v1alpha1/source.go
@@ -102,6 +102,14 @@ func (s Source) getUDTransformerContainer(mainContainerReq getContainerReq) core
}
}
container := c.build()
+
+ var initialDelaySeconds, periodSeconds, timeoutSeconds, failureThreshold int32 = UDContainerLivezInitialDelaySeconds, UDContainerLivezPeriodSeconds, UDContainerLivezTimeoutSeconds, UDContainerLivezFailureThreshold
+ if x := s.UDTransformer.Container; x != nil {
+ initialDelaySeconds = GetProbeInitialDelaySecondsOr(x.LivenessProbe, initialDelaySeconds)
+ periodSeconds = GetProbePeriodSecondsOr(x.LivenessProbe, periodSeconds)
+ timeoutSeconds = GetProbeTimeoutSecondsOr(x.LivenessProbe, timeoutSeconds)
+ failureThreshold = GetProbeFailureThresholdOr(x.LivenessProbe, failureThreshold)
+ }
container.LivenessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
@@ -110,9 +118,10 @@ func (s Source) getUDTransformerContainer(mainContainerReq getContainerReq) core
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 30,
- PeriodSeconds: 60,
- TimeoutSeconds: 30,
+ InitialDelaySeconds: initialDelaySeconds,
+ PeriodSeconds: periodSeconds,
+ TimeoutSeconds: timeoutSeconds,
+ FailureThreshold: failureThreshold,
}
return container
}
@@ -139,6 +148,14 @@ func (s Source) getUDSourceContainer(mainContainerReq getContainerReq) corev1.Co
}
}
container := c.build()
+
+ var initialDelaySeconds, periodSeconds, timeoutSeconds, failureThreshold int32 = UDContainerLivezInitialDelaySeconds, UDContainerLivezPeriodSeconds, UDContainerLivezTimeoutSeconds, UDContainerLivezFailureThreshold
+ if x := s.UDSource.Container; x != nil {
+ initialDelaySeconds = GetProbeInitialDelaySecondsOr(x.LivenessProbe, initialDelaySeconds)
+ periodSeconds = GetProbePeriodSecondsOr(x.LivenessProbe, periodSeconds)
+ timeoutSeconds = GetProbeTimeoutSecondsOr(x.LivenessProbe, timeoutSeconds)
+ failureThreshold = GetProbeFailureThresholdOr(x.LivenessProbe, failureThreshold)
+ }
container.LivenessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
@@ -147,9 +164,10 @@ func (s Source) getUDSourceContainer(mainContainerReq getContainerReq) corev1.Co
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 30,
- PeriodSeconds: 60,
- TimeoutSeconds: 30,
+ InitialDelaySeconds: initialDelaySeconds,
+ PeriodSeconds: periodSeconds,
+ TimeoutSeconds: timeoutSeconds,
+ FailureThreshold: failureThreshold,
}
return container
}
diff --git a/pkg/apis/numaflow/v1alpha1/source_test.go b/pkg/apis/numaflow/v1alpha1/source_test.go
index d762eef84a..01f26f2cd6 100644
--- a/pkg/apis/numaflow/v1alpha1/source_test.go
+++ b/pkg/apis/numaflow/v1alpha1/source_test.go
@@ -23,12 +23,36 @@ import (
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
+ "k8s.io/utils/ptr"
)
var testImagePullPolicy = corev1.PullNever
func TestSource_getContainers(t *testing.T) {
x := Source{
+ UDSource: &UDSource{
+ Container: &Container{
+ Image: "my-image-s",
+ VolumeMounts: []corev1.VolumeMount{{Name: "my-vm"}},
+ Command: []string{"my-cmd-s"},
+ Args: []string{"my-arg-s"},
+ Env: []corev1.EnvVar{{Name: "my-envvar-s"}},
+ EnvFrom: []corev1.EnvFromSource{{ConfigMapRef: &corev1.ConfigMapEnvSource{
+ LocalObjectReference: corev1.LocalObjectReference{Name: "test-cm"},
+ }}},
+ Resources: corev1.ResourceRequirements{
+ Requests: map[corev1.ResourceName]resource.Quantity{
+ "cpu": resource.MustParse("2"),
+ },
+ },
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](10),
+ TimeoutSeconds: ptr.To[int32](15),
+ PeriodSeconds: ptr.To[int32](14),
+ FailureThreshold: ptr.To[int32](5),
+ },
+ },
+ },
UDTransformer: &UDTransformer{
Container: &Container{
Image: "my-image",
@@ -44,6 +68,12 @@ func TestSource_getContainers(t *testing.T) {
"cpu": resource.MustParse("2"),
},
},
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](20),
+ TimeoutSeconds: ptr.To[int32](25),
+ PeriodSeconds: ptr.To[int32](24),
+ FailureThreshold: ptr.To[int32](5),
+ },
},
},
}
@@ -51,8 +81,33 @@ func TestSource_getContainers(t *testing.T) {
image: "main-image",
})
assert.NoError(t, err)
- assert.Equal(t, 2, len(c))
+ assert.Equal(t, 3, len(c))
assert.Equal(t, "main-image", c[0].Image)
+
+ assert.Equal(t, x.UDSource.Container.Image, c[2].Image)
+ assert.Contains(t, c[2].VolumeMounts, c[2].VolumeMounts[0])
+ assert.Equal(t, x.UDSource.Container.Command, c[2].Command)
+ assert.Equal(t, x.UDSource.Container.Args, c[2].Args)
+ envsUDSource := map[string]string{}
+ for _, e := range c[2].Env {
+ envsUDSource[e.Name] = e.Value
+ }
+ assert.Equal(t, envsUDSource[EnvUDContainerType], UDContainerSource)
+ assert.Equal(t, x.UDSource.Container.EnvFrom, c[2].EnvFrom)
+ assert.Equal(t, corev1.ResourceRequirements{Requests: map[corev1.ResourceName]resource.Quantity{"cpu": resource.MustParse("2")}}, c[2].Resources)
+ assert.Equal(t, c[0].ImagePullPolicy, c[2].ImagePullPolicy)
+ assert.NotNil(t, c[1].LivenessProbe)
+ assert.Equal(t, int32(10), c[2].LivenessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(15), c[2].LivenessProbe.TimeoutSeconds)
+ assert.Equal(t, int32(14), c[2].LivenessProbe.PeriodSeconds)
+ assert.Equal(t, int32(5), c[2].LivenessProbe.FailureThreshold)
+ x.UDSource.Container.ImagePullPolicy = &testImagePullPolicy
+ c, _ = x.getContainers(getContainerReq{
+ image: "main-image",
+ imagePullPolicy: corev1.PullAlways,
+ })
+ assert.Equal(t, testImagePullPolicy, c[2].ImagePullPolicy)
+
assert.Equal(t, x.UDTransformer.Container.Image, c[1].Image)
assert.Contains(t, c[1].VolumeMounts, c[1].VolumeMounts[0])
assert.Equal(t, x.UDTransformer.Container.Command, c[1].Command)
@@ -65,6 +120,11 @@ func TestSource_getContainers(t *testing.T) {
assert.Equal(t, x.UDTransformer.Container.EnvFrom, c[1].EnvFrom)
assert.Equal(t, corev1.ResourceRequirements{Requests: map[corev1.ResourceName]resource.Quantity{"cpu": resource.MustParse("2")}}, c[1].Resources)
assert.Equal(t, c[0].ImagePullPolicy, c[1].ImagePullPolicy)
+ assert.NotNil(t, c[1].LivenessProbe)
+ assert.Equal(t, int32(20), c[1].LivenessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(25), c[1].LivenessProbe.TimeoutSeconds)
+ assert.Equal(t, int32(24), c[1].LivenessProbe.PeriodSeconds)
+ assert.Equal(t, int32(5), c[1].LivenessProbe.FailureThreshold)
x.UDTransformer.Container.ImagePullPolicy = &testImagePullPolicy
c, _ = x.getContainers(getContainerReq{
image: "main-image",
diff --git a/pkg/apis/numaflow/v1alpha1/udf.go b/pkg/apis/numaflow/v1alpha1/udf.go
index c29d54b2cb..23e9bcf085 100644
--- a/pkg/apis/numaflow/v1alpha1/udf.go
+++ b/pkg/apis/numaflow/v1alpha1/udf.go
@@ -98,6 +98,14 @@ func (in UDF) getUDFContainer(mainContainerReq getContainerReq) corev1.Container
}
c = c.appendEnv(corev1.EnvVar{Name: EnvUDContainerType, Value: UDContainerFunction})
container := c.build()
+
+ var initialDelaySeconds, periodSeconds, timeoutSeconds, failureThreshold int32 = UDContainerLivezInitialDelaySeconds, UDContainerLivezPeriodSeconds, UDContainerLivezTimeoutSeconds, UDContainerLivezFailureThreshold
+ if x := in.Container; x != nil {
+ initialDelaySeconds = GetProbeInitialDelaySecondsOr(x.LivenessProbe, initialDelaySeconds)
+ periodSeconds = GetProbePeriodSecondsOr(x.LivenessProbe, periodSeconds)
+ timeoutSeconds = GetProbeTimeoutSecondsOr(x.LivenessProbe, timeoutSeconds)
+ failureThreshold = GetProbeFailureThresholdOr(x.LivenessProbe, failureThreshold)
+ }
container.LivenessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
@@ -106,9 +114,10 @@ func (in UDF) getUDFContainer(mainContainerReq getContainerReq) corev1.Container
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 30,
- PeriodSeconds: 60,
- TimeoutSeconds: 30,
+ InitialDelaySeconds: initialDelaySeconds,
+ PeriodSeconds: periodSeconds,
+ TimeoutSeconds: timeoutSeconds,
+ FailureThreshold: failureThreshold,
}
return container
}
diff --git a/pkg/apis/numaflow/v1alpha1/udf_test.go b/pkg/apis/numaflow/v1alpha1/udf_test.go
index f96e9c3756..0bb8e3862a 100644
--- a/pkg/apis/numaflow/v1alpha1/udf_test.go
+++ b/pkg/apis/numaflow/v1alpha1/udf_test.go
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
+ "k8s.io/utils/ptr"
)
func TestUDF_getContainers(t *testing.T) {
@@ -40,6 +41,12 @@ func TestUDF_getContainers(t *testing.T) {
"cpu": resource.MustParse("2"),
},
},
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](10),
+ TimeoutSeconds: ptr.To[int32](15),
+ PeriodSeconds: ptr.To[int32](14),
+ FailureThreshold: ptr.To[int32](5),
+ },
},
}
c, err := x.getContainers(getContainerReq{
@@ -68,6 +75,10 @@ func TestUDF_getContainers(t *testing.T) {
})
assert.Equal(t, testImagePullPolicy, c[1].ImagePullPolicy)
assert.True(t, c[1].LivenessProbe != nil)
+ assert.Equal(t, int32(10), c[1].LivenessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(15), c[1].LivenessProbe.TimeoutSeconds)
+ assert.Equal(t, int32(14), c[1].LivenessProbe.PeriodSeconds)
+ assert.Equal(t, int32(5), c[1].LivenessProbe.FailureThreshold)
}
func Test_getUDFContainer(t *testing.T) {
diff --git a/pkg/apis/numaflow/v1alpha1/user_defined_container.go b/pkg/apis/numaflow/v1alpha1/user_defined_container.go
index 369a6d3eb3..5e8746097e 100644
--- a/pkg/apis/numaflow/v1alpha1/user_defined_container.go
+++ b/pkg/apis/numaflow/v1alpha1/user_defined_container.go
@@ -38,4 +38,8 @@ type Container struct {
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty" protobuf:"bytes,8,opt,name=securityContext"`
// +optional
ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty" protobuf:"bytes,9,opt,name=imagePullPolicy,casttype=PullPolicy"`
+ // +optional
+ ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,10,opt,name=readinessProbe"`
+ // +optional
+ LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"`
}
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types.go b/pkg/apis/numaflow/v1alpha1/vertex_types.go
index 965e9f4bcc..f403d074c3 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types.go
@@ -253,6 +253,18 @@ func (v Vertex) GetPodSpec(req GetVertexPodSpecReq) (*corev1.PodSpec, error) {
return nil, err
}
+ var readyzInitDeploy, readyzPeriodSeconds, readyzTimeoutSeconds, readyzFailureThreshold int32 = NumaContainerReadyzInitialDelaySeconds, NumaContainerReadyzPeriodSeconds, NumaContainerReadyzTimeoutSeconds, NumaContainerReadyzFailureThreshold
+ var liveZInitDeploy, liveZPeriodSeconds, liveZTimeoutSeconds, liveZFailureThreshold int32 = NumaContainerLivezInitialDelaySeconds, NumaContainerLivezPeriodSeconds, NumaContainerLivezTimeoutSeconds, NumaContainerLivezFailureThreshold
+ if x := v.Spec.ContainerTemplate; x != nil {
+ readyzInitDeploy = GetProbeInitialDelaySecondsOr(x.ReadinessProbe, readyzInitDeploy)
+ readyzPeriodSeconds = GetProbePeriodSecondsOr(x.ReadinessProbe, readyzPeriodSeconds)
+ readyzTimeoutSeconds = GetProbeTimeoutSecondsOr(x.ReadinessProbe, readyzTimeoutSeconds)
+ readyzFailureThreshold = GetProbeFailureThresholdOr(x.ReadinessProbe, readyzFailureThreshold)
+ liveZInitDeploy = GetProbeInitialDelaySecondsOr(x.LivenessProbe, liveZInitDeploy)
+ liveZPeriodSeconds = GetProbePeriodSecondsOr(x.LivenessProbe, liveZPeriodSeconds)
+ liveZTimeoutSeconds = GetProbeTimeoutSecondsOr(x.LivenessProbe, liveZTimeoutSeconds)
+ liveZFailureThreshold = GetProbeFailureThresholdOr(x.LivenessProbe, liveZFailureThreshold)
+ }
containers[0].ReadinessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
@@ -261,10 +273,12 @@ func (v Vertex) GetPodSpec(req GetVertexPodSpecReq) (*corev1.PodSpec, error) {
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 3,
- PeriodSeconds: 3,
- TimeoutSeconds: 1,
+ InitialDelaySeconds: readyzInitDeploy,
+ PeriodSeconds: readyzPeriodSeconds,
+ TimeoutSeconds: readyzTimeoutSeconds,
+ FailureThreshold: readyzFailureThreshold,
}
+
containers[0].LivenessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
@@ -273,9 +287,10 @@ func (v Vertex) GetPodSpec(req GetVertexPodSpecReq) (*corev1.PodSpec, error) {
Scheme: corev1.URISchemeHTTPS,
},
},
- InitialDelaySeconds: 20,
- PeriodSeconds: 60,
- TimeoutSeconds: 30,
+ InitialDelaySeconds: liveZInitDeploy,
+ PeriodSeconds: liveZPeriodSeconds,
+ TimeoutSeconds: liveZTimeoutSeconds,
+ FailureThreshold: liveZFailureThreshold,
}
containers[0].Ports = []corev1.ContainerPort{
{Name: VertexMetricsPortName, ContainerPort: VertexMetricsPort},
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
index 1f5572c424..83384cf624 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
@@ -286,6 +286,20 @@ func TestGetPodSpec(t *testing.T) {
t.Run("test sink", func(t *testing.T) {
testObj := testVertex.DeepCopy()
testObj.Spec.Sink = &Sink{}
+ testObj.Spec.ContainerTemplate = &ContainerTemplate{
+ ReadinessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](24),
+ PeriodSeconds: ptr.To[int32](25),
+ FailureThreshold: ptr.To[int32](2),
+ TimeoutSeconds: ptr.To[int32](21),
+ },
+ LivenessProbe: &Probe{
+ InitialDelaySeconds: ptr.To[int32](14),
+ PeriodSeconds: ptr.To[int32](15),
+ FailureThreshold: ptr.To[int32](2),
+ TimeoutSeconds: ptr.To[int32](11),
+ },
+ }
s, err := testObj.GetPodSpec(req)
assert.NoError(t, err)
assert.Equal(t, 1, len(s.Containers))
@@ -294,10 +308,20 @@ func TestGetPodSpec(t *testing.T) {
assert.Equal(t, corev1.PullIfNotPresent, s.Containers[0].ImagePullPolicy)
assert.NotNil(t, s.Containers[0].ReadinessProbe)
assert.NotNil(t, s.Containers[0].ReadinessProbe.HTTPGet)
+ assert.Equal(t, "/readyz", s.Containers[0].ReadinessProbe.HTTPGet.Path)
+ assert.Equal(t, int32(24), s.Containers[0].ReadinessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(25), s.Containers[0].ReadinessProbe.PeriodSeconds)
+ assert.Equal(t, int32(2), s.Containers[0].ReadinessProbe.FailureThreshold)
+ assert.Equal(t, int32(21), s.Containers[0].ReadinessProbe.TimeoutSeconds)
assert.Equal(t, corev1.URISchemeHTTPS, s.Containers[0].ReadinessProbe.HTTPGet.Scheme)
assert.Equal(t, VertexMetricsPort, s.Containers[0].ReadinessProbe.HTTPGet.Port.IntValue())
assert.NotNil(t, s.Containers[0].LivenessProbe)
assert.NotNil(t, s.Containers[0].LivenessProbe.HTTPGet)
+ assert.Equal(t, "/livez", s.Containers[0].LivenessProbe.HTTPGet.Path)
+ assert.Equal(t, int32(14), s.Containers[0].LivenessProbe.InitialDelaySeconds)
+ assert.Equal(t, int32(15), s.Containers[0].LivenessProbe.PeriodSeconds)
+ assert.Equal(t, int32(2), s.Containers[0].LivenessProbe.FailureThreshold)
+ assert.Equal(t, int32(11), s.Containers[0].LivenessProbe.TimeoutSeconds)
assert.Equal(t, corev1.URISchemeHTTPS, s.Containers[0].LivenessProbe.HTTPGet.Scheme)
assert.Equal(t, VertexMetricsPort, s.Containers[0].LivenessProbe.HTTPGet.Port.IntValue())
assert.Equal(t, 1, len(s.Containers[0].Ports))
diff --git a/pkg/shared/clients/nats/test/server.go b/pkg/shared/clients/nats/test/server.go
index af3c25036e..556a7390c2 100644
--- a/pkg/shared/clients/nats/test/server.go
+++ b/pkg/shared/clients/nats/test/server.go
@@ -27,6 +27,7 @@ import (
func RunNatsServer(t *testing.T) *server.Server {
t.Helper()
opts := natstestserver.DefaultTestOptions
+ opts.Port = 4223
return natstestserver.RunServer(&opts)
}
diff --git a/pkg/sources/nats/nats_test.go b/pkg/sources/nats/nats_test.go
index 739d215018..db4a0c25b7 100644
--- a/pkg/sources/nats/nats_test.go
+++ b/pkg/sources/nats/nats_test.go
@@ -68,7 +68,7 @@ func Test_Single(t *testing.T) {
server := natstest.RunNatsServer(t)
defer server.Shutdown()
- url := "127.0.0.1"
+ url := server.ClientURL()
testSubject := "test-single"
testQueue := "test-queue-single"
vi := testVertex(t, url, testSubject, testQueue, "test-host", 0)
@@ -112,9 +112,9 @@ func Test_Multiple(t *testing.T) {
server := natstest.RunNatsServer(t)
defer server.Shutdown()
- url := "127.0.0.1"
testSubject := "test-multiple"
testQueue := "test-queue-multiple"
+ url := server.ClientURL()
v1 := testVertex(t, url, testSubject, testQueue, "test-host1", 0)
ns1, err := newInstance(t, v1)
assert.NoError(t, err)
diff --git a/rust/numaflow-models/src/models/container.rs b/rust/numaflow-models/src/models/container.rs
index 69f3206a00..3599d6a243 100644
--- a/rust/numaflow-models/src/models/container.rs
+++ b/rust/numaflow-models/src/models/container.rs
@@ -32,6 +32,10 @@ pub struct Container {
pub image: Option,
#[serde(rename = "imagePullPolicy", skip_serializing_if = "Option::is_none")]
pub image_pull_policy: Option,
+ #[serde(rename = "livenessProbe", skip_serializing_if = "Option::is_none")]
+ pub liveness_probe: Option>,
+ #[serde(rename = "readinessProbe", skip_serializing_if = "Option::is_none")]
+ pub readiness_probe: Option>,
#[serde(rename = "resources", skip_serializing_if = "Option::is_none")]
pub resources: Option,
#[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")]
@@ -50,6 +54,8 @@ impl Container {
env_from: None,
image: None,
image_pull_policy: None,
+ liveness_probe: None,
+ readiness_probe: None,
resources: None,
security_context: None,
volume_mounts: None,
diff --git a/rust/numaflow-models/src/models/container_template.rs b/rust/numaflow-models/src/models/container_template.rs
index 71bd4e068c..2b478cadf1 100644
--- a/rust/numaflow-models/src/models/container_template.rs
+++ b/rust/numaflow-models/src/models/container_template.rs
@@ -26,6 +26,10 @@ pub struct ContainerTemplate {
pub env_from: Option>,
#[serde(rename = "imagePullPolicy", skip_serializing_if = "Option::is_none")]
pub image_pull_policy: Option,
+ #[serde(rename = "livenessProbe", skip_serializing_if = "Option::is_none")]
+ pub liveness_probe: Option>,
+ #[serde(rename = "readinessProbe", skip_serializing_if = "Option::is_none")]
+ pub readiness_probe: Option>,
#[serde(rename = "resources", skip_serializing_if = "Option::is_none")]
pub resources: Option,
#[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")]
@@ -39,6 +43,8 @@ impl ContainerTemplate {
env: None,
env_from: None,
image_pull_policy: None,
+ liveness_probe: None,
+ readiness_probe: None,
resources: None,
security_context: None,
}
diff --git a/rust/numaflow-models/src/models/mod.rs b/rust/numaflow-models/src/models/mod.rs
index 29846575d2..423e21fd9c 100644
--- a/rust/numaflow-models/src/models/mod.rs
+++ b/rust/numaflow-models/src/models/mod.rs
@@ -120,6 +120,8 @@ pub mod pipeline_spec;
pub use self::pipeline_spec::PipelineSpec;
pub mod pipeline_status;
pub use self::pipeline_status::PipelineStatus;
+pub mod probe;
+pub use self::probe::Probe;
pub mod redis_buffer_service;
pub use self::redis_buffer_service::RedisBufferService;
pub mod redis_config;
diff --git a/rust/numaflow-models/src/models/probe.rs b/rust/numaflow-models/src/models/probe.rs
new file mode 100644
index 0000000000..9980e9ea79
--- /dev/null
+++ b/rust/numaflow-models/src/models/probe.rs
@@ -0,0 +1,54 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by Openapi Generator. DO NOT EDIT.
+
+/// Probe : Probe is used to customize the configuration for Readiness and Liveness probes.
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct Probe {
+ /// Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+ #[serde(rename = "failureThreshold", skip_serializing_if = "Option::is_none")]
+ pub failure_threshold: Option,
+ /// Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+ #[serde(
+ rename = "initialDelaySeconds",
+ skip_serializing_if = "Option::is_none"
+ )]
+ pub initial_delay_seconds: Option,
+ /// How often (in seconds) to perform the probe.
+ #[serde(rename = "periodSeconds", skip_serializing_if = "Option::is_none")]
+ pub period_seconds: Option,
+ /// Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+ #[serde(rename = "successThreshold", skip_serializing_if = "Option::is_none")]
+ pub success_threshold: Option,
+ /// Number of seconds after which the probe times out. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+ #[serde(rename = "timeoutSeconds", skip_serializing_if = "Option::is_none")]
+ pub timeout_seconds: Option,
+}
+
+impl Probe {
+ /// Probe is used to customize the configuration for Readiness and Liveness probes.
+ pub fn new() -> Probe {
+ Probe {
+ failure_threshold: None,
+ initial_delay_seconds: None,
+ period_seconds: None,
+ success_threshold: None,
+ timeout_seconds: None,
+ }
+ }
+}
diff --git a/test/e2e/functional_test.go b/test/e2e/functional_test.go
index 2d0989ac7e..1c72af9229 100644
--- a/test/e2e/functional_test.go
+++ b/test/e2e/functional_test.go
@@ -316,12 +316,12 @@ func (s *FunctionalSuite) TestFallbackSink() {
defer w.DeletePipelineAndWait()
pipelineName := "simple-fallback"
- // send a message to the pipeline
- w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("fallback-message")))
-
// wait for all the pods to come up
w.Expect().VertexPodsRunning()
+ // send a message to the pipeline
+ w.SendMessageTo(pipelineName, "in", NewHttpPostRequest().WithBody([]byte("fallback-message")))
+
w.Expect().RedisSinkContains("simple-fallback-output", "fallback-message")
}
diff --git a/test/fixtures/util.go b/test/fixtures/util.go
index c88d28d1af..13a4026384 100644
--- a/test/fixtures/util.go
+++ b/test/fixtures/util.go
@@ -310,7 +310,7 @@ func WaitForVertexPodRunning(kubeClient kubernetes.Interface, vertexClient flowp
}
ok = ok && len(podList.Items) > 0 && len(podList.Items) == vertexList.Items[0].GetReplicas() // pod number should equal to desired replicas
for _, p := range podList.Items {
- ok = ok && p.Status.Phase == corev1.PodRunning
+ ok = ok && isPodReady(p)
}
if ok {
return nil
@@ -319,6 +319,18 @@ func WaitForVertexPodRunning(kubeClient kubernetes.Interface, vertexClient flowp
}
}
+func isPodReady(pod corev1.Pod) bool {
+ if pod.Status.Phase != corev1.PodRunning {
+ return false
+ }
+ for _, c := range pod.Status.ContainerStatuses {
+ if !c.Ready {
+ return false
+ }
+ }
+ return true
+}
+
func WaitForVertexPodScalingTo(kubeClient kubernetes.Interface, vertexClient flowpkg.VertexInterface, namespace, pipelineName, vertexName string, timeout time.Duration, size int) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
From 69143417de0b18a9645ae70af0eb6e2dbb7aa914 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Thu, 19 Sep 2024 13:35:31 -0700
Subject: [PATCH 063/188] chore: suppress some misleading logs from raters
(#2075)
Signed-off-by: Derek Wang
---
pkg/daemon/server/service/rater/rater.go | 5 ++---
pkg/mvtxdaemon/server/service/rater/rater.go | 10 +++++-----
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/pkg/daemon/server/service/rater/rater.go b/pkg/daemon/server/service/rater/rater.go
index b858cc968f..86b2ad10de 100644
--- a/pkg/daemon/server/service/rater/rater.go
+++ b/pkg/daemon/server/service/rater/rater.go
@@ -218,12 +218,11 @@ func sleep(ctx context.Context, duration time.Duration) {
// since a pod can read from multiple partitions, we will return a map of partition to read count.
func (r *Rater) getPodReadCounts(vertexName, podName string) *PodReadCount {
readTotalMetricName := "forwarder_data_read_total"
-
// scrape the read total metric from pod metric port
url := fmt.Sprintf("https://%s.%s.%s.svc:%v/metrics", podName, r.pipeline.Name+"-"+vertexName+"-headless", r.pipeline.Namespace, v1alpha1.VertexMetricsPort)
resp, err := r.httpClient.Get(url)
if err != nil {
- r.log.Errorf("[vertex name %s, pod name %s]: failed reading the metrics endpoint, %v", vertexName, podName, err.Error())
+ r.log.Warnf("[vertex name %s, pod name %s]: failed reading the metrics endpoint, the pod might have been scaled down: %v", vertexName, podName, err.Error())
return nil
}
defer resp.Body.Close()
@@ -255,7 +254,7 @@ func (r *Rater) getPodReadCounts(vertexName, podName string) *PodReadCount {
podReadCount := &PodReadCount{podName, partitionReadCount}
return podReadCount
} else {
- r.log.Errorf("[vertex name %s, pod name %s]: failed getting the read total metric, the metric is not available.", vertexName, podName)
+ r.log.Infof("[vertex name %s, pod name %s]: Metric %q is unavailable, the pod might haven't started processing data", vertexName, podName, readTotalMetricName)
return nil
}
}
diff --git a/pkg/mvtxdaemon/server/service/rater/rater.go b/pkg/mvtxdaemon/server/service/rater/rater.go
index d160edd838..19a3ee87dc 100644
--- a/pkg/mvtxdaemon/server/service/rater/rater.go
+++ b/pkg/mvtxdaemon/server/service/rater/rater.go
@@ -33,7 +33,7 @@ import (
)
const CountWindow = time.Second * 10
-const MonoVtxReadMetricName = "monovtx_read_total"
+const monoVtxReadMetricName = "monovtx_read_total"
// MonoVtxRatable is the interface for the Rater struct.
type MonoVtxRatable interface {
@@ -161,7 +161,7 @@ func (r *Rater) getPodReadCounts(podName string) *PodReadCount {
url := fmt.Sprintf("https://%s.%s.%s.svc:%v/metrics", podName, headlessServiceName, r.monoVertex.Namespace, v1alpha1.MonoVertexMetricsPort)
resp, err := r.httpClient.Get(url)
if err != nil {
- r.log.Errorf("[MonoVertex name %s, pod name %s]: failed reading the metrics endpoint, %v", r.monoVertex.Name, podName, err.Error())
+ r.log.Warnf("[Pod name %s]: failed reading the metrics endpoint, the pod might have been scaled down: %v", podName, err.Error())
return nil
}
defer resp.Body.Close()
@@ -169,11 +169,11 @@ func (r *Rater) getPodReadCounts(podName string) *PodReadCount {
textParser := expfmt.TextParser{}
result, err := textParser.TextToMetricFamilies(resp.Body)
if err != nil {
- r.log.Errorf("[MonoVertex name %s, pod name %s]: failed parsing to prometheus metric families, %v", r.monoVertex.Name, podName, err.Error())
+ r.log.Errorf("[Pod name %s]: failed parsing to prometheus metric families, %v", podName, err.Error())
return nil
}
- if value, ok := result[MonoVtxReadMetricName]; ok && value != nil && len(value.GetMetric()) > 0 {
+ if value, ok := result[monoVtxReadMetricName]; ok && value != nil && len(value.GetMetric()) > 0 {
metricsList := value.GetMetric()
// Each pod should be emitting only one metric with this name, so we should be able to take the first value
// from the results safely.
@@ -182,7 +182,7 @@ func (r *Rater) getPodReadCounts(podName string) *PodReadCount {
podReadCount := &PodReadCount{podName, metricsList[0].Untyped.GetValue()}
return podReadCount
} else {
- r.log.Errorf("[MonoVertex name %s, pod name %s]: failed getting the read total metric, the metric is not available.", r.monoVertex.Name, podName)
+ r.log.Infof("[Pod name %s]: Metric %q is unavailable, the pod might haven't started processing data", podName, monoVtxReadMetricName)
return nil
}
}
From ed543ad2e7824f3e6b508de5b07ba08e1d7d9b66 Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Thu, 19 Sep 2024 17:06:19 -0400
Subject: [PATCH 064/188] fix: support version compatibility check for
pre-release versions (#2069)
Signed-off-by: Keran Yang
---
pkg/sdkclient/serverinfo/serverinfo.go | 6 +-
pkg/sdkclient/serverinfo/serverinfo_test.go | 234 ++++++++-
pkg/sdkclient/serverinfo/types.go | 75 ++-
pkg/shared/clients/nats/test/server.go | 4 +-
rust/monovertex/src/server_info.rs | 508 +++++++++++++++-----
5 files changed, 677 insertions(+), 150 deletions(-)
diff --git a/pkg/sdkclient/serverinfo/serverinfo.go b/pkg/sdkclient/serverinfo/serverinfo.go
index 932ab2ff50..d01acd1cda 100644
--- a/pkg/sdkclient/serverinfo/serverinfo.go
+++ b/pkg/sdkclient/serverinfo/serverinfo.go
@@ -170,7 +170,7 @@ func checkNumaflowCompatibility(numaflowVersion string, minNumaflowVersion strin
numaflowConstraint := fmt.Sprintf(">= %s", minNumaflowVersion)
if err = checkConstraint(numaflowVersionSemVer, numaflowConstraint); err != nil {
return fmt.Errorf("numaflow version %s must be upgraded to at least %s, in order to work with current SDK version: %w",
- numaflowVersionSemVer.String(), minNumaflowVersion, err)
+ numaflowVersionSemVer.String(), humanReadable(minNumaflowVersion), err)
}
return nil
}
@@ -193,7 +193,7 @@ func checkSDKCompatibility(sdkVersion string, sdkLanguage Language, minSupported
if !c.Check(sdkVersionPEP440) {
return fmt.Errorf("SDK version %s must be upgraded to at least %s, in order to work with current numaflow version: %w",
- sdkVersionPEP440.String(), sdkRequiredVersion, err)
+ sdkVersionPEP440.String(), humanReadable(sdkRequiredVersion), err)
}
} else {
sdkVersionSemVer, err := semver.NewVersion(sdkVersion)
@@ -203,7 +203,7 @@ func checkSDKCompatibility(sdkVersion string, sdkLanguage Language, minSupported
if err := checkConstraint(sdkVersionSemVer, sdkConstraint); err != nil {
return fmt.Errorf("SDK version %s must be upgraded to at least %s, in order to work with current numaflow version: %w",
- sdkVersionSemVer.String(), sdkRequiredVersion, err)
+ sdkVersionSemVer.String(), humanReadable(sdkRequiredVersion), err)
}
}
}
diff --git a/pkg/sdkclient/serverinfo/serverinfo_test.go b/pkg/sdkclient/serverinfo/serverinfo_test.go
index e96919e243..ad7f06e690 100644
--- a/pkg/sdkclient/serverinfo/serverinfo_test.go
+++ b/pkg/sdkclient/serverinfo/serverinfo_test.go
@@ -97,12 +97,57 @@ func Test_CheckNumaflowCompatibility(t *testing.T) {
errMessage string
}{
{
- name: "Test with incompatible numaflow version",
+ name: "Test with incompatible numaflow version, min is a stable version 1.1.7",
numaflowVersion: "v1.1.6",
- minNumaflowVersion: "1.1.7",
+ minNumaflowVersion: "1.1.7-z",
shouldErr: true,
errMessage: "numaflow version 1.1.6 must be upgraded to at least 1.1.7, in order to work with current SDK version",
},
+ {
+ name: "Test with compatible numaflow version - min is a stable version 1.1.6",
+ numaflowVersion: "1.1.7",
+ minNumaflowVersion: "1.1.6-z",
+ shouldErr: false,
+ },
+ {
+ name: "Test with incompatible numaflow version - min is a stable version 1.1.7, numaflow version is a pre-release version",
+ numaflowVersion: "v1.1.7-rc1",
+ minNumaflowVersion: "1.1.7-z",
+ shouldErr: true,
+ errMessage: "numaflow version 1.1.7-rc1 must be upgraded to at least 1.1.7, in order to work with current SDK version",
+ },
+ {
+ name: "Test with compatible numaflow version - min is a stable version 1.1.6, numaflow version is a pre-release version",
+ numaflowVersion: "1.1.7-rc1",
+ minNumaflowVersion: "1.1.6-z",
+ shouldErr: false,
+ },
+ {
+ name: "Test with incompatible numaflow version, min is a rc version 1.1.7-rc1",
+ numaflowVersion: "v1.1.6",
+ minNumaflowVersion: "1.1.7-rc1",
+ shouldErr: true,
+ errMessage: "numaflow version 1.1.6 must be upgraded to at least 1.1.7-rc1, in order to work with current SDK version",
+ },
+ {
+ name: "Test with compatible numaflow version - min is a rc version 1.1.6-rc1",
+ numaflowVersion: "1.1.7",
+ minNumaflowVersion: "1.1.6-rc1",
+ shouldErr: false,
+ },
+ {
+ name: "Test with incompatible numaflow version - min is a rc version 1.1.7-rc2, numaflow version is a pre-release version",
+ numaflowVersion: "v1.1.7-rc1",
+ minNumaflowVersion: "1.1.7-rc2",
+ shouldErr: true,
+ errMessage: "numaflow version 1.1.7-rc1 must be upgraded to at least 1.1.7-rc2, in order to work with current SDK version",
+ },
+ {
+ name: "Test with compatible numaflow version - min is a rc version 1.1.6-rc2, numaflow version is a pre-release version",
+ numaflowVersion: "1.1.6-rc2",
+ minNumaflowVersion: "1.1.6-rc2",
+ shouldErr: false,
+ },
{
name: "Test with empty MinimumNumaflowVersion field",
numaflowVersion: "1.1.7",
@@ -111,10 +156,17 @@ func Test_CheckNumaflowCompatibility(t *testing.T) {
errMessage: "server info does not contain minimum numaflow version. Upgrade to newer SDK version",
},
{
- name: "Test with compatible numaflow version",
- numaflowVersion: "1.1.7",
- minNumaflowVersion: "1.1.6",
- shouldErr: false,
+ name: "Test with invalid numaflow version",
+ numaflowVersion: "",
+ minNumaflowVersion: "1.1.7",
+ shouldErr: true,
+ errMessage: "error parsing numaflow version: Invalid Semantic Version",
+ },
+ {
+ name: "Test with empty min numaflow version",
+ numaflowVersion: "1.1.7",
+ shouldErr: true,
+ errMessage: "server info does not contain minimum numaflow version. Upgrade to newer SDK version",
},
}
for _, tt := range tests {
@@ -130,12 +182,13 @@ func Test_CheckNumaflowCompatibility(t *testing.T) {
}
}
-func Test_CheckSDKCompatibility(t *testing.T) {
+// this test suite is to test SDK compatibility check when all the minimum-supported versions are stable releases
+func Test_CheckSDKCompatibility_MinimumBeingStableReleases(t *testing.T) {
var testMinimumSupportedSDKVersions = sdkConstraints{
- Go: "0.6.0-0",
- Python: "0.6.0a",
- Java: "0.6.0-0",
- Rust: "0.1.0",
+ Python: "0.6.0rc100",
+ Go: "0.6.0-z",
+ Java: "0.6.0-z",
+ Rust: "0.1.0-z",
}
tests := []struct {
name string
@@ -146,37 +199,57 @@ func Test_CheckSDKCompatibility(t *testing.T) {
errMessage string
}{
{
- name: "Test with incompatible Python version",
+ name: "python pre-release version is lower than minimum supported version",
sdkVersion: "v0.5.3a1",
sdkLanguage: Python,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: true,
- errMessage: "SDK version 0.5.3a1 must be upgraded to at least 0.6.0a, in order to work with current numaflow version",
+ errMessage: "SDK version 0.5.3a1 must be upgraded to at least 0.6.0, in order to work with current numaflow version",
},
{
- name: "Test with compatible Python version",
- sdkVersion: "v0.6.0a2",
+ name: "python pre-release version is compatible with minimum supported version",
+ sdkVersion: "v0.6.3a1",
sdkLanguage: Python,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: false,
},
{
- name: "Test with incompatible Java version",
- sdkVersion: "v0.4.3",
- sdkLanguage: Java,
+ name: "python stable release version is compatible with minimum supported version",
+ sdkVersion: "v0.6.0",
+ sdkLanguage: Python,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
+ },
+ {
+ name: "python stable release version is lower than minimum supported version",
+ sdkVersion: "v0.5.3",
+ sdkLanguage: Python,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: true,
- errMessage: "SDK version 0.4.3 must be upgraded to at least 0.6.0-0, in order to work with current numaflow version",
+ errMessage: "SDK version 0.5.3 must be upgraded to at least 0.6.0, in order to work with current numaflow version",
+ },
+ {
+ name: "java release version is compatible with minimum supported version",
+ sdkVersion: "v0.7.3",
+ sdkLanguage: Java,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
},
{
- name: "Test with compatible Go version",
- sdkVersion: "v0.6.0-rc2",
+ name: "golang rc release version is compatible with minimum supported version",
+ sdkVersion: "v0.6.2-rc2",
sdkLanguage: Go,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: false,
+ }, {
+ name: "rust pre-release version is compatible with minimum supported version",
+ sdkVersion: "v0.1.2-0.20240913163521-4910018031a7",
+ sdkLanguage: Rust,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
},
{
- name: "Test with incompatible Rust version",
+ name: "rust release version is lower than minimum supported version",
sdkVersion: "v0.0.3",
sdkLanguage: Rust,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
@@ -184,12 +257,125 @@ func Test_CheckSDKCompatibility(t *testing.T) {
errMessage: "SDK version 0.0.3 must be upgraded to at least 0.1.0, in order to work with current numaflow version",
},
{
- name: "Test with compatible Rust version",
- sdkVersion: "v0.1.1",
+ name: "java rc release version is lower than minimum supported version",
+ sdkVersion: "v0.6.0-rc1",
+ sdkLanguage: Java,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.6.0-rc1 must be upgraded to at least 0.6.0, in order to work with current numaflow version",
+ },
+ {
+ name: "golang pre-release version is lower than minimum supported version",
+ sdkVersion: "v0.6.0-0.20240913163521-4910018031a7",
+ sdkLanguage: Go,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.6.0-0.20240913163521-4910018031a7 must be upgraded to at least 0.6.0, in order to work with current numaflow version",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := checkSDKCompatibility(tt.sdkVersion, tt.sdkLanguage, tt.minimumSupportedSDKVersions)
+ if tt.shouldErr {
+ assert.Error(t, err, "Expected error")
+ assert.Contains(t, err.Error(), tt.errMessage)
+ } else {
+ assert.NoError(t, err, "Expected no error")
+ }
+ })
+ }
+}
+
+// this test suite is to test SDK compatibility check when all the minimum-supported versions are pre-releases
+func Test_CheckSDKCompatibility_MinimumBeingPreReleases(t *testing.T) {
+ var testMinimumSupportedSDKVersions = sdkConstraints{
+ Python: "0.6.0b1",
+ Go: "0.6.0-rc2",
+ Java: "0.6.0-rc2",
+ Rust: "0.1.0-rc3",
+ }
+ tests := []struct {
+ name string
+ sdkVersion string
+ sdkLanguage Language
+ minimumSupportedSDKVersions sdkConstraints
+ shouldErr bool
+ errMessage string
+ }{
+ {
+ name: "python pre-release version is lower than minimum supported version",
+ sdkVersion: "v0.5.3a1",
+ sdkLanguage: Python,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.5.3a1 must be upgraded to at least 0.6.0b1, in order to work with current numaflow version",
+ },
+ {
+ name: "python pre-release version is compatible with minimum supported version",
+ sdkVersion: "v0.6.3a1",
+ sdkLanguage: Python,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
+ },
+ {
+ name: "python stable release version is compatible with minimum supported version",
+ sdkVersion: "v0.6.0",
+ sdkLanguage: Python,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
+ },
+ {
+ name: "python stable release version is lower than minimum supported version",
+ sdkVersion: "v0.5.3",
+ sdkLanguage: Python,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.5.3 must be upgraded to at least 0.6.0b1, in order to work with current numaflow version",
+ },
+ {
+ name: "java release version is compatible with minimum supported version",
+ sdkVersion: "v0.7.3",
+ sdkLanguage: Java,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
+ },
+ {
+ name: "golang rc release version is compatible with minimum supported version",
+ sdkVersion: "v0.6.2-rc2",
+ sdkLanguage: Go,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: false,
+ }, {
+ name: "rust pre-release version is compatible with minimum supported version",
+ sdkVersion: "v0.1.2-0.20240913163521-4910018031a7",
sdkLanguage: Rust,
minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
shouldErr: false,
},
+ {
+ name: "rust release version is lower than minimum supported version",
+ sdkVersion: "v0.0.3",
+ sdkLanguage: Rust,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.0.3 must be upgraded to at least 0.1.0-rc3, in order to work with current numaflow version",
+ },
+ {
+ name: "java rc release version is lower than minimum supported version",
+ sdkVersion: "v0.6.0-rc1",
+ sdkLanguage: Java,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.6.0-rc1 must be upgraded to at least 0.6.0-rc2, in order to work with current numaflow version",
+ },
+ {
+ name: "golang pre-release version is lower than minimum supported version",
+ sdkVersion: "v0.6.0-0.20240913163521-4910018031a7",
+ sdkLanguage: Go,
+ minimumSupportedSDKVersions: testMinimumSupportedSDKVersions,
+ shouldErr: true,
+ errMessage: "SDK version 0.6.0-0.20240913163521-4910018031a7 must be upgraded to at least 0.6.0-rc2, in order to work with current numaflow version",
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/sdkclient/serverinfo/types.go b/pkg/sdkclient/serverinfo/types.go
index fc8fdd9b81..9e4a152d03 100644
--- a/pkg/sdkclient/serverinfo/types.go
+++ b/pkg/sdkclient/serverinfo/types.go
@@ -16,6 +16,8 @@ limitations under the License.
package serverinfo
+import "strings"
+
type Language string
const (
@@ -27,11 +29,76 @@ const (
type sdkConstraints map[Language]string
+/*
+minimumSupportedSDKVersions is the minimum supported version of each SDK for the current numaflow version.
+It is used to check if the SDK is compatible with the current numaflow version.
+
+NOTE: when updating it, please also update MINIMUM_SUPPORTED_SDK_VERSIONS for mono vertex at rust/monovertex/server_info.rs
+
+Python SDK versioning follows PEP 440 (https://www.python.org/dev/peps/pep-0440/).
+The other SDKs follow the semver versioning scheme (https://semver.org/).
+
+How to update this map:
+
+There are two types of releases, one is the stable release and the other is the pre-release.
+Below are the typical formats of the versioning scheme:
+
+ +------------------+-------------------------+-----------------------------+
+ | | PEP 440 | semver |
+ +------------------+-------------------------+-----------------------------+
+ | stable | 0.8.0 | 0.8.0 |
+ +------------------+-------------------------+-----------------------------+
+ | pre-release | 0.8.0a1, | 0.8.0-rc1, |
+ | | 0.8.0b3, | 0.8.0-0.20240913163521, |
+ | | or 0.8.0rc1 | etc. |
+ +------------------+-------------------------+-----------------------------+
+
+There are two cases to consider when updating the map:
+
+1. The minimum supported version is a pre-release version.
+In this case, directly put the exact pre-release version in the map.
+E.g., if the minimum supported version is "0.8.0-rc1", then put "0.8.0-rc1" for java, go, rust.
+"0.8.0b1", "0.8.0b1" for python.
+2. The minimum supported version is a stable version.
+In this case, put (almost) the largest available pre-release version of the stable version in the map.
+This is because the go semver library considers pre-releases to be invalid if the constraint range does not include pre-releases.
+Therefore, we have to put a pre-release version of the stable version in the map and choose the largest one.
+For python, we use "rc100" as the largest pre-release version. For go, rust, we use "-z" as the largest pre-release version.
+E.g., if the minimum supported version is "0.8.0", then put "0.8.0-z" for java, go, rust, "0.8.0rc100" for python.
+A constraint ">=0.8.0-z" will match any pre-release version of 0.8.0, including "0.8.0-rc1", "0.8.0-rc2", etc.
+
+More details about version comparison can be found in the PEP 440 and semver documentation.
+*/
var minimumSupportedSDKVersions = sdkConstraints{
- Go: "0.8.0",
- Python: "0.8.0",
- Java: "0.8.0",
- Rust: "0.1.0",
+ // meaning the minimum supported python SDK version is 0.8.0
+ Python: "0.8.0rc100",
+ // meaning the minimum supported go SDK version is 0.8.0
+ Go: "0.8.0-z",
+ // meaning the minimum supported java SDK version is 0.8.0
+ Java: "0.8.0-z",
+ // meaning the minimum supported rust SDK version is 0.1.0
+ Rust: "0.1.0-z",
+}
+
+// humanReadable returns the human-readable minimum supported version.
+// it's used for logging purposes.
+// it translates the version we used in the constraints to the real minimum supported version.
+// e.g., if the given version is "0.8.0rc100", human-readable version is "0.8.0".
+// if the given version is "0.8.0-z", "0.8.0".
+// if "0.8.0-rc1", "0.8.0-rc1".
+func humanReadable(ver string) string {
+ if ver == "" {
+ return ""
+ }
+ // semver
+ if strings.HasSuffix(ver, "-z") {
+ return ver[:len(ver)-2]
+ }
+ // PEP 440
+ if strings.HasSuffix(ver, "rc100") {
+ return ver[:len(ver)-5]
+ }
+ return ver
}
type Protocol string
diff --git a/pkg/shared/clients/nats/test/server.go b/pkg/shared/clients/nats/test/server.go
index 556a7390c2..b64db98645 100644
--- a/pkg/shared/clients/nats/test/server.go
+++ b/pkg/shared/clients/nats/test/server.go
@@ -27,7 +27,7 @@ import (
func RunNatsServer(t *testing.T) *server.Server {
t.Helper()
opts := natstestserver.DefaultTestOptions
- opts.Port = 4223
+ opts.Port = -1 // Use random port to avoid conflicts
return natstestserver.RunServer(&opts)
}
@@ -35,7 +35,7 @@ func RunNatsServer(t *testing.T) *server.Server {
func RunJetStreamServer(t *testing.T) *server.Server {
t.Helper()
opts := natstestserver.DefaultTestOptions
- opts.Port = -1 // Random port
+ opts.Port = -1 // Use random port to avoid conflicts
opts.JetStream = true
storeDir, err := os.MkdirTemp("", "")
if err != nil {
diff --git a/rust/monovertex/src/server_info.rs b/rust/monovertex/src/server_info.rs
index 225218b158..98484869fd 100644
--- a/rust/monovertex/src/server_info.rs
+++ b/rust/monovertex/src/server_info.rs
@@ -77,24 +77,6 @@ pub async fn check_for_server_compatibility(
Ok(())
}
-/// Checks if the given version meets the specified constraint.
-fn check_constraint(version: &Version, constraint: &str) -> error::Result<()> {
- // Parse the given constraint as a semantic version requirement
- let version_req = VersionReq::parse(constraint).map_err(|e| {
- Error::ServerInfoError(format!(
- "Error parsing constraint: {}, constraint string: {}",
- e, constraint
- ))
- })?;
-
- // Check if the provided version satisfies the parsed constraint
- if !version_req.matches(version) {
- return Err(Error::ServerInfoError("invalid version".to_string()));
- }
-
- Ok(())
-}
-
/// Checks if the current numaflow version is compatible with the given minimum numaflow version.
fn check_numaflow_compatibility(
numaflow_version: &str,
@@ -105,8 +87,11 @@ fn check_numaflow_compatibility(
return Err(Error::ServerInfoError("invalid version".to_string()));
}
+ // Strip the 'v' prefix if present.
+ let numaflow_version_stripped = numaflow_version.trim_start_matches('v');
+
// Parse the provided numaflow version as a semantic version
- let numaflow_version_semver = Version::parse(numaflow_version)
+ let numaflow_version_semver = Version::parse(numaflow_version_stripped)
.map_err(|e| Error::ServerInfoError(format!("Error parsing Numaflow version: {}", e)))?;
// Create a version constraint based on the minimum numaflow version
@@ -114,7 +99,7 @@ fn check_numaflow_compatibility(
check_constraint(&numaflow_version_semver, &numaflow_constraint).map_err(|e| {
Error::ServerInfoError(format!(
"numaflow version {} must be upgraded to at least {}, in order to work with current SDK version {}",
- numaflow_version_semver, min_numaflow_version, e
+ numaflow_version_semver, human_readable(min_numaflow_version), e
))
})
}
@@ -141,7 +126,7 @@ fn check_sdk_compatibility(
if !specifiers.contains(&sdk_version_pep440) {
return Err(Error::ServerInfoError(format!(
"SDK version {} must be upgraded to at least {}, in order to work with the current numaflow version",
- sdk_version_pep440, sdk_required_version
+ sdk_version_pep440, human_readable(sdk_required_version)
)));
}
} else {
@@ -156,7 +141,7 @@ fn check_sdk_compatibility(
check_constraint(&sdk_version_semver, &sdk_constraint).map_err(|_| {
Error::ServerInfoError(format!(
"SDK version {} must be upgraded to at least {}, in order to work with the current numaflow version",
- sdk_version_semver, sdk_required_version
+ sdk_version_semver, human_readable(sdk_required_version)
))
})?;
}
@@ -176,6 +161,86 @@ fn check_sdk_compatibility(
Ok(())
}
+// human_readable returns the human-readable minimum supported version.
+// it's used for logging purposes.
+// it translates the version we used in the constraints to the real minimum supported version.
+// e.g., if the given version is "0.8.0rc100", human-readable version is "0.8.0".
+// if the given version is "0.8.0-z", "0.8.0".
+// if "0.8.0-rc1", "0.8.0-rc1".
+fn human_readable(ver: &str) -> String {
+ if ver.is_empty() {
+ return String::new();
+ }
+ // semver
+ if ver.ends_with("-z") {
+ return ver[..ver.len() - 2].to_string();
+ }
+ // PEP 440
+ if ver.ends_with("rc100") {
+ return ver[..ver.len() - 5].to_string();
+ }
+ ver.to_string()
+}
+
+/// Checks if the given version meets the specified constraint.
+fn check_constraint(version: &Version, constraint: &str) -> error::Result<()> {
+ let binding = version.to_string();
+ // extract the major.minor.patch version
+ let mmp_version = Version::parse(binding.split('-').next().unwrap_or_default()).map_err(|e| {
+ Error::ServerInfoError(format!("Error parsing version: {}, version string: {}", e, binding))
+ })?;
+ let mmp_ver_str_constraint = trim_after_dash(constraint.trim_start_matches(">="));
+ let mmp_ver_constraint = format!(">={}", mmp_ver_str_constraint);
+
+ // "-z" is used to indicate the minimum supported version is a stable version
+ // the reason why we choose the letter z is that it can represent the largest pre-release version.
+ // e.g., 0.8.0-z means the minimum supported version is 0.8.0.
+ if constraint.contains("-z") {
+ if !version.to_string().starts_with(mmp_ver_str_constraint) {
+ // if the version is prefixed with a different mmp version,
+ // rust semver lib can't figure out the correct order.
+ // to work around, we compare the mmp version only.
+ // e.g., rust semver doesn't treat 0.9.0-rc* as larger than 0.8.0.
+ // to work around, instead of comparing 0.9.0-rc* with 0.8.0,
+ // we compare 0.9.0 with 0.8.0.
+ return check_constraint(&mmp_version, &mmp_ver_constraint);
+ }
+ return check_constraint(version, &mmp_ver_constraint);
+ } else if constraint.contains("-") {
+ // if the constraint doesn't contain "-z", but contains "-", it's a pre-release version.
+ if !version.to_string().starts_with(mmp_ver_str_constraint) {
+ // similar reason as above, we compare the mmp version only.
+ return check_constraint(&mmp_version, &mmp_ver_constraint);
+ }
+ }
+
+ // TODO - remove all the extra check above once rust semver handles pre-release comparison the same way as golang.
+ // https://github.com/dtolnay/semver/issues/323
+
+ // Parse the given constraint as a semantic version requirement
+ let version_req = VersionReq::parse(constraint).map_err(|e| {
+ Error::ServerInfoError(format!(
+ "Error parsing constraint: {}, constraint string: {}",
+ e, constraint
+ ))
+ })?;
+
+ // Check if the provided version satisfies the parsed constraint
+ if !version_req.matches(version) {
+ return Err(Error::ServerInfoError("invalid version".to_string()));
+ }
+
+ Ok(())
+}
+
+fn trim_after_dash(input: &str) -> &str {
+ if let Some(pos) = input.find('-') {
+ &input[..pos]
+ } else {
+ input
+ }
+}
+
/// Reads the server info file and returns the parsed ServerInfo struct.
/// The cancellation token is used to stop ready-check of server_info file in case it is missing.
/// This cancellation token is closed via the global shutdown handler.
@@ -257,11 +322,12 @@ mod version {
static MINIMUM_SUPPORTED_SDK_VERSIONS: Lazy = Lazy::new(|| {
// TODO: populate this from a static file and make it part of the release process
// the value of the map matches `minimumSupportedSDKVersions` in pkg/sdkclient/serverinfo/types.go
+ // please follow the instruction there to update the value
let mut m = HashMap::new();
- m.insert("go".to_string(), "0.8.0".to_string());
- m.insert("python".to_string(), "0.8.0".to_string());
- m.insert("java".to_string(), "0.8.0".to_string());
- m.insert("rust".to_string(), "0.1.0".to_string());
+ m.insert("go".to_string(), "0.8.0-z".to_string());
+ m.insert("python".to_string(), "0.8.0rc100".to_string());
+ m.insert("java".to_string(), "0.8.0-z".to_string());
+ m.insert("rust".to_string(), "0.1.0-z".to_string());
m
});
@@ -397,22 +463,32 @@ mod tests {
Ok(())
}
- // Helper function to create a SdkConstraints struct
- fn create_sdk_constraints() -> version::SdkConstraints {
+ // Helper function to create a SdkConstraints struct with minimum supported SDK versions all being stable releases
+ fn create_sdk_constraints_stable_versions() -> SdkConstraints {
let mut constraints = HashMap::new();
- constraints.insert("python".to_string(), "1.2.0".to_string());
- constraints.insert("java".to_string(), "2.0.0".to_string());
- constraints.insert("go".to_string(), "0.10.0".to_string());
- constraints.insert("rust".to_string(), "0.1.0".to_string());
+ constraints.insert("python".to_string(), "1.2.0rc100".to_string());
+ constraints.insert("java".to_string(), "2.0.0-z".to_string());
+ constraints.insert("go".to_string(), "0.10.0-z".to_string());
+ constraints.insert("rust".to_string(), "0.1.0-z".to_string());
+ constraints
+ }
+
+ // Helper function to create a SdkConstraints struct with minimum supported SDK versions all being pre-releases
+ fn create_sdk_constraints_pre_release_versions() -> SdkConstraints {
+ let mut constraints = HashMap::new();
+ constraints.insert("python".to_string(), "1.2.0b2".to_string());
+ constraints.insert("java".to_string(), "2.0.0-rc2".to_string());
+ constraints.insert("go".to_string(), "0.10.0-rc2".to_string());
+ constraints.insert("rust".to_string(), "0.1.0-rc3".to_string());
constraints
}
#[tokio::test]
- async fn test_sdk_compatibility_python_valid() {
- let sdk_version = "v1.3.0";
+ async fn test_sdk_compatibility_min_stable_python_stable_release_valid() {
+ let sdk_version = "1.3.0";
let sdk_language = "python";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
@@ -420,23 +496,53 @@ mod tests {
}
#[tokio::test]
- async fn test_sdk_compatibility_python_invalid() {
+ async fn test_sdk_compatibility_min_stable_python_stable_release_invalid() {
let sdk_version = "1.1.0";
let sdk_language = "python";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 1.1.0 must be upgraded to at least 1.2.0, in order to work with the current numaflow version"));
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_stable_python_pre_release_valid() {
+ let sdk_version = "v1.3.0a1";
+ let sdk_language = "python";
+
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_ok());
}
#[tokio::test]
- async fn test_sdk_compatibility_java_valid() {
+ async fn test_sdk_compatibility_min_stable_python_pre_release_invalid() {
+ let sdk_version = "1.1.0a1";
+ let sdk_language = "python";
+
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 1.1.0a1 must be upgraded to at least 1.2.0, in order to work with the current numaflow version"));
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_stable_java_stable_release_valid() {
let sdk_version = "v2.1.0";
let sdk_language = "java";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
@@ -444,23 +550,26 @@ mod tests {
}
#[tokio::test]
- async fn test_sdk_compatibility_java_invalid() {
- let sdk_version = "1.5.0";
+ async fn test_sdk_compatibility_min_stable_java_rc_release_invalid() {
+ let sdk_version = "2.0.0-rc1";
let sdk_language = "java";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 2.0.0-rc1 must be upgraded to at least 2.0.0, in order to work with the current numaflow version"));
}
#[tokio::test]
- async fn test_sdk_compatibility_go_valid() {
- let sdk_version = "0.11.0";
+ async fn test_sdk_compatibility_min_stable_go_rc_release_valid() {
+ let sdk_version = "0.11.0-rc2";
let sdk_language = "go";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
@@ -468,23 +577,26 @@ mod tests {
}
#[tokio::test]
- async fn test_sdk_compatibility_go_invalid() {
- let sdk_version = "0.9.0";
+ async fn test_sdk_compatibility_min_stable_go_pre_release_invalid() {
+ let sdk_version = "0.10.0-0.20240913163521-4910018031a7";
let sdk_language = "go";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 0.10.0-0.20240913163521-4910018031a7 must be upgraded to at least 0.10.0, in order to work with the current numaflow version"));
}
#[tokio::test]
- async fn test_sdk_compatibility_rust_valid() {
- let sdk_version = "v0.1.0";
+ async fn test_sdk_compatibility_min_stable_rust_pre_release_valid() {
+ let sdk_version = "v0.1.1-0.20240913163521-4910018031a7";
let sdk_language = "rust";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
@@ -492,21 +604,227 @@ mod tests {
}
#[tokio::test]
- async fn test_sdk_compatibility_rust_invalid() {
+ async fn test_sdk_compatibility_min_stable_rust_stable_release_invalid() {
let sdk_version = "0.0.9";
let sdk_language = "rust";
- let min_supported_sdk_versions = create_sdk_constraints();
+ let min_supported_sdk_versions = create_sdk_constraints_stable_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "ServerInfoError Error - SDK version 0.0.9 must be upgraded to at least 0.1.0, in order to work with the current numaflow version"));
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_python_stable_release_valid() {
+ let sdk_version = "1.3.0";
+ let sdk_language = "python";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_python_stable_release_invalid() {
+ let sdk_version = "1.1.0";
+ let sdk_language = "python";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 1.1.0 must be upgraded to at least 1.2.0b2, in order to work with the current numaflow version"));
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_python_pre_release_valid() {
+ let sdk_version = "v1.3.0a1";
+ let sdk_language = "python";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_python_pre_release_invalid() {
+ let sdk_version = "1.2.0a1";
+ let sdk_language = "python";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
let result =
check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 1.2.0a1 must be upgraded to at least 1.2.0b2, in order to work with the current numaflow version"));
}
#[tokio::test]
- async fn test_numaflow_compatibility_valid() {
- let numaflow_version = "1.4.0";
- let min_numaflow_version = "1.3.0";
+ async fn test_sdk_compatibility_min_pre_release_java_stable_release_valid() {
+ let sdk_version = "v2.1.0";
+ let sdk_language = "java";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_java_rc_release_invalid() {
+ let sdk_version = "2.0.0-rc1";
+ let sdk_language = "java";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 2.0.0-rc1 must be upgraded to at least 2.0.0-rc2, in order to work with the current numaflow version"));
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_go_rc_release_valid() {
+ let sdk_version = "0.11.0-rc2";
+ let sdk_language = "go";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_go_pre_release_invalid() {
+ let sdk_version = "0.10.0-0.20240913163521-4910018031a7";
+ let sdk_language = "go";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "SDK version 0.10.0-0.20240913163521-4910018031a7 must be upgraded to at least 0.10.0-rc2, in order to work with the current numaflow version"));
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_rust_pre_release_valid() {
+ let sdk_version = "v0.1.1-0.20240913163521-4910018031a7";
+ let sdk_language = "rust";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_sdk_compatibility_min_pre_release_rust_stable_release_invalid() {
+ let sdk_version = "0.0.9";
+ let sdk_language = "rust";
+
+ let min_supported_sdk_versions = create_sdk_constraints_pre_release_versions();
+ let result =
+ check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
+
+ assert!(result.is_err());
+ assert!(
+ result.unwrap_err().to_string().contains(
+ "ServerInfoError Error - SDK version 0.0.9 must be upgraded to at least 0.1.0-rc3, in order to work with the current numaflow version"));
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_invalid_version_string() {
+ let numaflow_version = "v1.abc.7";
+ let min_numaflow_version = "1.1.6-z";
+
+ let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
+
+ assert!(result.is_err());
+ assert!(result.unwrap_err().to_string().contains(
+ "Error parsing Numaflow version: unexpected character 'a' while parsing minor version number"));
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_min_stable_version_stable_valid() {
+ let numaflow_version = "v1.1.7";
+ let min_numaflow_version = "1.1.6-z";
+
+ let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_min_stable_version_stable_invalid() {
+ let numaflow_version = "v1.1.6";
+ let min_numaflow_version = "1.1.7-z";
+
+ let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
+
+ assert!(result.is_err());
+ assert!(result.unwrap_err().to_string().contains(
+ "numaflow version 1.1.6 must be upgraded to at least 1.1.7, in order to work with current SDK version"));
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_min_stable_version_pre_release_valid() {
+ let numaflow_version = "1.1.7-rc1";
+ let min_numaflow_version = "1.1.6-z";
+
+ let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_min_stable_version_pre_release_invalid() {
+ let numaflow_version = "v1.1.6-rc1";
+ let min_numaflow_version = "1.1.6-z";
+
+ let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
+
+ assert!(result.is_err());
+ assert!(result.unwrap_err().to_string().contains(
+ "numaflow version 1.1.6-rc1 must be upgraded to at least 1.1.6, in order to work with current SDK version"));
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_min_rc_version_stable_invalid() {
+ let numaflow_version = "v1.1.6";
+ let min_numaflow_version = "1.1.7-rc1";
+
+ let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
+
+ assert!(result.is_err());
+ assert!(result.unwrap_err().to_string().contains(
+ "numaflow version 1.1.6 must be upgraded to at least 1.1.7-rc1, in order to work with current SDK version"));
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_min_rc_version_stable_valid() {
+ let numaflow_version = "1.1.7";
+ let min_numaflow_version = "1.1.6-rc1";
let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
@@ -514,13 +832,25 @@ mod tests {
}
#[tokio::test]
- async fn test_numaflow_compatibility_invalid() {
- let numaflow_version = "1.2.0";
- let min_numaflow_version = "1.3.0";
+ async fn test_numaflow_compatibility_min_rc_version_pre_release_valid() {
+ let numaflow_version = "1.1.7-rc3";
+ let min_numaflow_version = "1.1.7-rc2";
+
+ let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_numaflow_compatibility_min_rc_version_pre_release_invalid() {
+ let numaflow_version = "v1.1.6-rc1";
+ let min_numaflow_version = "1.1.6-rc2";
let result = check_numaflow_compatibility(numaflow_version, min_numaflow_version);
assert!(result.is_err());
+ assert!(result.unwrap_err().to_string().contains(
+ "numaflow version 1.1.6-rc1 must be upgraded to at least 1.1.6-rc2, in order to work with current SDK version"));
}
#[tokio::test]
@@ -591,7 +921,7 @@ mod tests {
#[tokio::test]
async fn test_read_server_info_success() {
// Create a temporary directory
- let dir = tempfile::tempdir().unwrap();
+ let dir = tempdir().unwrap();
let file_path = dir.path().join("server_info.txt");
let cln_token = CancellationToken::new();
@@ -632,7 +962,7 @@ mod tests {
#[tokio::test]
async fn test_read_server_info_retry_limit() {
// Create a temporary directory
- let dir = tempfile::tempdir().unwrap();
+ let dir = tempdir().unwrap();
let file_path = dir.path().join("server_info.txt");
// Write a partial test file not ending with END marker
@@ -676,60 +1006,4 @@ mod tests {
let _parsed_server_info: ServerInfo =
serde_json::from_str(&json_data).expect("Failed to parse JSON");
}
-
- #[test]
- fn test_sdk_compatibility_go_version_with_v_prefix() {
- let sdk_version = "v0.11.0";
- let sdk_language = "go";
-
- let mut min_supported_sdk_versions = HashMap::new();
- min_supported_sdk_versions.insert("go".to_string(), "0.10.0".to_string());
-
- let result =
- check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
-
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_sdk_compatibility_go_version_without_v_prefix() {
- let sdk_version = "0.11.0";
- let sdk_language = "go";
-
- let mut min_supported_sdk_versions = HashMap::new();
- min_supported_sdk_versions.insert("go".to_string(), "0.10.0".to_string());
-
- let result =
- check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
-
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_sdk_compatibility_go_version_with_v_prefix_invalid() {
- let sdk_version = "v0.9.0";
- let sdk_language = "go";
-
- let mut min_supported_sdk_versions = HashMap::new();
- min_supported_sdk_versions.insert("go".to_string(), "0.10.0".to_string());
-
- let result =
- check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
-
- assert!(result.is_err());
- }
-
- #[test]
- fn test_sdk_compatibility_go_version_without_v_prefix_invalid() {
- let sdk_version = "0.9.0";
- let sdk_language = "go";
-
- let mut min_supported_sdk_versions = HashMap::new();
- min_supported_sdk_versions.insert("go".to_string(), "0.10.0".to_string());
-
- let result =
- check_sdk_compatibility(sdk_version, sdk_language, &min_supported_sdk_versions);
-
- assert!(result.is_err());
- }
}
From 669dc186a0d885df92716b627ded236fab7476e7 Mon Sep 17 00:00:00 2001
From: Julie Vogelman
Date: Fri, 20 Sep 2024 14:09:44 -0700
Subject: [PATCH 065/188] Fix: Use Merge patch rather than json patch for
`pause-timestamp` annotation apply (#2078)
Signed-off-by: Julie Vogelman
---
pkg/reconciler/pipeline/controller.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pkg/reconciler/pipeline/controller.go b/pkg/reconciler/pipeline/controller.go
index 5776c2873c..29dab84526 100644
--- a/pkg/reconciler/pipeline/controller.go
+++ b/pkg/reconciler/pipeline/controller.go
@@ -831,8 +831,8 @@ func (r *pipelineReconciler) resumePipeline(ctx context.Context, pl *dfv1.Pipeli
func (r *pipelineReconciler) pausePipeline(ctx context.Context, pl *dfv1.Pipeline) (bool, error) {
// check that annotations / pause timestamp annotation exist
if pl.GetAnnotations() == nil || pl.GetAnnotations()[dfv1.KeyPauseTimestamp] == "" {
- patchJson := `[{"op": "add", "path": "` + pauseTimestampPath + `", "value": "` + time.Now().Format(time.RFC3339) + `"}]`
- if err := r.client.Patch(ctx, pl, client.RawPatch(types.JSONPatchType, []byte(patchJson))); err != nil && !apierrors.IsNotFound(err) {
+ patchJson := `{"metadata":{"annotations":{"` + dfv1.KeyPauseTimestamp + `":"` + time.Now().Format(time.RFC3339) + `"}}}`
+ if err := r.client.Patch(ctx, pl, client.RawPatch(types.MergePatchType, []byte(patchJson))); err != nil && !apierrors.IsNotFound(err) {
return true, err
}
}
From 40e960a44184c876173e6bdf69b216df6296bf73 Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Mon, 23 Sep 2024 08:25:36 +0530
Subject: [PATCH 066/188] feat: Bidirectional Streaming for User Defined Source
(#2056)
Signed-off-by: Yashash H L
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
.github/workflows/ci.yaml | 8 +-
.github/workflows/nightly-build.yml | 5 +-
.github/workflows/release.yml | 4 +-
go.mod | 27 +-
go.sum | 54 ++-
pkg/apis/proto/source/v1/source.proto | 59 ++-
pkg/daemon/client/grpc_daemon_client.go | 2 +-
pkg/daemon/client/grpc_daemon_client_test.go | 7 -
pkg/mvtxdaemon/client/grpc_client.go | 2 +-
.../pbq/wal/unaligned/fs/compactor_test.go | 3 -
pkg/sdkclient/grpc/grpc_utils.go | 6 +-
pkg/sdkclient/reducer/client.go | 2 +-
pkg/sdkclient/source/client.go | 162 ++++++--
pkg/sdkclient/source/client_test.go | 105 +++--
pkg/sdkclient/source/interface.go | 2 +-
pkg/sources/source.go | 2 +-
pkg/sources/udsource/grpc_udsource.go | 14 +-
pkg/sources/udsource/grpc_udsource_test.go | 113 +++---
pkg/sources/udsource/user_defined_source.go | 4 -
rust/Cargo.lock | 174 +++++++-
rust/monovertex/Cargo.toml | 7 +-
rust/monovertex/proto/source.proto | 59 ++-
rust/monovertex/src/config.rs | 66 ++-
rust/monovertex/src/forwarder.rs | 369 +++++++++--------
rust/monovertex/src/lib.rs | 376 ++++++++----------
rust/monovertex/src/message.rs | 25 +-
rust/monovertex/src/metrics.rs | 222 ++++++++++-
rust/monovertex/src/server_info.rs | 37 +-
rust/monovertex/src/shared.rs | 20 +-
rust/monovertex/src/sink.rs | 104 +----
rust/monovertex/src/source.rs | 282 +++++++------
rust/monovertex/src/startup.rs | 349 ++++++++++++++++
rust/monovertex/src/transformer.rs | 88 +---
rust/servesink/Cargo.toml | 4 +-
rust/serving/src/app/tracker.rs | 2 +-
rust/src/bin/main.rs | 4 +-
.../mono-vertex-with-transformer.yaml | 2 +
.../testdata/simple-source-go.yaml | 1 -
.../testdata/simple-source-java.yaml | 5 +-
.../testdata/simple-source-python.yaml | 3 +-
.../testdata/simple-source-rust.yaml | 26 ++
test/udsource-e2e/udsource_test.go | 12 +-
42 files changed, 1832 insertions(+), 986 deletions(-)
create mode 100644 rust/monovertex/src/startup.rs
create mode 100644 test/udsource-e2e/testdata/simple-source-rust.yaml
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 9eaa4de89b..e9861ca408 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -113,8 +113,8 @@ jobs:
with:
tool: grcov
- - name: Install Protobuf Compiler
- run: sudo apt-get install -y protobuf-compiler
+ - name: Install Protoc
+ uses: arduino/setup-protoc@v3
- name: Test Rust
working-directory: ./rust
@@ -150,7 +150,7 @@ jobs:
- run: git diff --exit-code
build-rust-amd64:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./rust
@@ -169,6 +169,8 @@ jobs:
uses: mozilla-actions/sccache-action@v0.0.5
- name: Install dependencies
run: sudo apt-get install -y protobuf-compiler
+ - name: Print Protoc version
+ run: protoc --version
- name: Build binary
run: RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-gnu
- name: Rename binary
diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml
index 51bdb6bfbc..ed6a898b83 100644
--- a/.github/workflows/nightly-build.yml
+++ b/.github/workflows/nightly-build.yml
@@ -41,7 +41,7 @@ jobs:
path: dist
build-rust-amd64:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./rust
@@ -71,7 +71,7 @@ jobs:
path: rust/numaflow-rs-linux-amd64
build-rust-arm64:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./rust
@@ -160,4 +160,3 @@ jobs:
- name: Container build and push with arm64/amd64
run: |
IMAGE_NAMESPACE=${{ secrets.QUAYIO_ORG }} VERSION=${{ steps.version.outputs.VERSION }} DOCKER_PUSH=true DOCKER_BUILD_ARGS="--label \"quay.expires-after=30d\"" make image-multi
-
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index dcab109e0e..84cd8f533d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,7 +39,7 @@ jobs:
path: dist
build-rust-amd64:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./rust
@@ -62,7 +62,7 @@ jobs:
path: rust/numaflow-rs-linux-amd64
build-rust-arm64:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./rust
diff --git a/go.mod b/go.mod
index 9f54c88017..c251e8216b 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.22
require (
github.com/IBM/sarama v1.43.2
- github.com/Masterminds/semver/v3 v3.2.1
+ github.com/Masterminds/semver/v3 v3.3.0
github.com/Masterminds/sprig/v3 v3.2.3
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0
github.com/antonmedv/expr v1.9.0
@@ -32,7 +32,7 @@ require (
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe
github.com/nats-io/nats-server/v2 v2.10.20
github.com/nats-io/nats.go v1.37.0
- github.com/numaproj/numaflow-go v0.8.0
+ github.com/numaproj/numaflow-go v0.8.2-0.20240918054944-0fd13d430793
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_model v0.5.0
github.com/prometheus/common v0.45.0
@@ -48,13 +48,13 @@ require (
go.uber.org/goleak v1.3.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.26.0
- golang.org/x/crypto v0.26.0
- golang.org/x/net v0.25.0
- golang.org/x/oauth2 v0.20.0
+ golang.org/x/crypto v0.27.0
+ golang.org/x/net v0.29.0
+ golang.org/x/oauth2 v0.21.0
golang.org/x/sync v0.8.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
- google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
- google.golang.org/grpc v1.59.0
+ google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117
+ google.golang.org/grpc v1.66.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
google.golang.org/protobuf v1.34.2
k8s.io/api v0.29.2
@@ -80,7 +80,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.11.3 // indirect
- github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -113,7 +113,7 @@ require (
github.com/go-playground/validator/v10 v10.19.0 // indirect
github.com/gobuffalo/flect v0.2.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
- github.com/golang/glog v1.1.2 // indirect
+ github.com/golang/glog v1.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
@@ -201,14 +201,13 @@ require (
golang.org/x/arch v0.7.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/mod v0.17.0 // indirect
- golang.org/x/sys v0.24.0 // indirect
- golang.org/x/term v0.23.0 // indirect
- golang.org/x/text v0.17.0 // indirect
+ golang.org/x/sys v0.25.0 // indirect
+ golang.org/x/term v0.24.0 // indirect
+ golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
- google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
diff --git a/go.sum b/go.sum
index 77784a6a8d..722bd72ee3 100644
--- a/go.sum
+++ b/go.sum
@@ -53,8 +53,8 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
-github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
+github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -94,8 +94,8 @@ github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf5
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
@@ -249,8 +249,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
-github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
+github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
+github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -485,8 +485,8 @@ github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDm
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/numaproj/numaflow-go v0.8.0 h1:1Pp0AMLXkmUPlvFjKeY3a9X+OLU8oN1OQWxD9jLg8Uo=
-github.com/numaproj/numaflow-go v0.8.0/go.mod h1:WoMt31+h3up202zTRI8c/qe42B8UbvwLe2mJH0MAlhI=
+github.com/numaproj/numaflow-go v0.8.2-0.20240918054944-0fd13d430793 h1:kUQw1LsUvmTjqFfcia6DZOxy8qCQwvdY0TpOnR8w3Xg=
+github.com/numaproj/numaflow-go v0.8.2-0.20240918054944-0fd13d430793/go.mod h1:g4JZOyUPhjfhv+kR0sX5d8taw/dasgKPXLvQBi39mJ4=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -687,8 +687,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
-golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
+golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
+golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -774,8 +774,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
-golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
+golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -788,8 +788,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
-golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -863,15 +863,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
-golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
+golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
-golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
+golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
+golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -884,8 +884,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
-golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
+golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1025,12 +1025,10 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
-google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
-google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
-google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
+google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1049,8 +1047,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
-google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
+google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
diff --git a/pkg/apis/proto/source/v1/source.proto b/pkg/apis/proto/source/v1/source.proto
index 0fd0bdbb37..7dc1a67412 100644
--- a/pkg/apis/proto/source/v1/source.proto
+++ b/pkg/apis/proto/source/v1/source.proto
@@ -26,16 +26,19 @@ package source.v1;
service Source {
// Read returns a stream of datum responses.
- // The size of the returned ReadResponse is less than or equal to the num_records specified in ReadRequest.
- // If the request timeout is reached on server side, the returned ReadResponse will contain all the datum that have been read (which could be an empty list).
- rpc ReadFn(ReadRequest) returns (stream ReadResponse);
+ // The size of the returned responses is less than or equal to the num_records specified in each ReadRequest.
+ // If the request timeout is reached on the server side, the returned responses will contain all the datum that have been read (which could be an empty list).
+ // The server will continue to read and respond to subsequent ReadRequests until the client closes the stream.
+ // Once it has sent all the datum, the server will send a ReadResponse with the end of transmission flag set to true.
+ rpc ReadFn(stream ReadRequest) returns (stream ReadResponse);
- // AckFn acknowledges a list of datum offsets.
+ // AckFn acknowledges a stream of datum offsets.
// When AckFn is called, it implicitly indicates that the datum stream has been processed by the source vertex.
// The caller (numa) expects the AckFn to be successful, and it does not expect any errors.
// If there are some irrecoverable errors when the callee (UDSource) is processing the AckFn request,
- // then it is best to crash because there are no other retry mechanisms possible.
- rpc AckFn(AckRequest) returns (AckResponse);
+ // then it is best to crash because there are no other retry mechanisms possible.
+ // Clients sends n requests and expects n responses.
+ rpc AckFn(stream AckRequest) returns (stream AckResponse);
// PendingFn returns the number of pending records at the user defined source.
rpc PendingFn(google.protobuf.Empty) returns (PendingResponse);
@@ -47,6 +50,14 @@ service Source {
rpc IsReady(google.protobuf.Empty) returns (ReadyResponse);
}
+/*
+ * Handshake message between client and server to indicate the start of transmission.
+ */
+message Handshake {
+ // Required field indicating the start of transmission.
+ bool sot = 1;
+}
+
/*
* ReadRequest is the request for reading datum stream from user defined source.
*/
@@ -61,6 +72,7 @@ message ReadRequest {
}
// Required field indicating the request.
Request request = 1;
+ optional Handshake handshake = 2;
}
/*
@@ -84,8 +96,31 @@ message ReadResponse {
// e.g. Kafka and Redis Stream message usually include information about the headers.
map headers = 5;
}
+ message Status {
+ // Code to indicate the status of the response.
+ enum Code {
+ SUCCESS = 0;
+ FAILURE = 1;
+ }
+
+ // Error to indicate the error type. If the code is FAILURE, then the error field will be populated.
+ enum Error {
+ UNACKED = 0;
+ OTHER = 1;
+ }
+
+ // End of transmission flag.
+ bool eot = 1;
+ Code code = 2;
+ optional Error error = 3;
+ optional string msg = 4;
+ }
// Required field holding the result.
Result result = 1;
+ // Status of the response. Holds the end of transmission flag and the status code.
+ Status status = 2;
+ // Handshake message between client and server to indicate the start of transmission.
+ optional Handshake handshake = 3;
}
/*
@@ -94,14 +129,12 @@ message ReadResponse {
*/
message AckRequest {
message Request {
- // Required field holding a list of offsets to be acknowledged.
- // The offsets must be strictly corresponding to the previously read batch,
- // meaning the offsets must be in the same order as the datum responses in the ReadResponse.
- // By enforcing ordering, we can save deserialization effort on the server side, assuming the server keeps a local copy of the raw/un-serialized offsets.
- repeated Offset offsets = 1;
+ // Required field holding the offset to be acked
+ Offset offset = 1;
}
// Required field holding the request. The list will be ordered and will have the same order as the original Read response.
Request request = 1;
+ optional Handshake handshake = 2;
}
/*
@@ -121,6 +154,8 @@ message AckResponse {
}
// Required field holding the result.
Result result = 1;
+ // Handshake message between client and server to indicate the start of transmission.
+ optional Handshake handshake = 2;
}
/*
@@ -169,4 +204,4 @@ message Offset {
// It is useful for sources that have multiple partitions. e.g. Kafka.
// If the partition_id is not specified, it is assumed that the source has a single partition.
int32 partition_id = 2;
-}
+}
\ No newline at end of file
diff --git a/pkg/daemon/client/grpc_daemon_client.go b/pkg/daemon/client/grpc_daemon_client.go
index fa30ec0c4e..6b1d0040db 100644
--- a/pkg/daemon/client/grpc_daemon_client.go
+++ b/pkg/daemon/client/grpc_daemon_client.go
@@ -37,7 +37,7 @@ func NewGRPCDaemonServiceClient(address string) (DaemonClient, error) {
config := &tls.Config{
InsecureSkipVerify: true,
}
- conn, err := grpc.Dial(address, grpc.WithTransportCredentials(credentials.NewTLS(config)))
+ conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(credentials.NewTLS(config)))
if err != nil {
return nil, err
}
diff --git a/pkg/daemon/client/grpc_daemon_client_test.go b/pkg/daemon/client/grpc_daemon_client_test.go
index d1bca6e18a..905d963822 100644
--- a/pkg/daemon/client/grpc_daemon_client_test.go
+++ b/pkg/daemon/client/grpc_daemon_client_test.go
@@ -467,11 +467,4 @@ func TestNewGRPCDaemonServiceClient(t *testing.T) {
err = client.Close()
assert.NoError(t, err)
})
-
- t.Run("empty address", func(t *testing.T) {
- address := ""
- client, err := NewGRPCDaemonServiceClient(address)
- assert.Error(t, err)
- assert.Nil(t, client)
- })
}
diff --git a/pkg/mvtxdaemon/client/grpc_client.go b/pkg/mvtxdaemon/client/grpc_client.go
index cd6fdbb455..c398b138c2 100644
--- a/pkg/mvtxdaemon/client/grpc_client.go
+++ b/pkg/mvtxdaemon/client/grpc_client.go
@@ -38,7 +38,7 @@ func NewGRPCClient(address string) (MonoVertexDaemonClient, error) {
config := &tls.Config{
InsecureSkipVerify: true,
}
- conn, err := grpc.Dial(address, grpc.WithTransportCredentials(credentials.NewTLS(config)))
+ conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(credentials.NewTLS(config)))
if err != nil {
return nil, err
}
diff --git a/pkg/reduce/pbq/wal/unaligned/fs/compactor_test.go b/pkg/reduce/pbq/wal/unaligned/fs/compactor_test.go
index 1c451507cb..a811921efd 100644
--- a/pkg/reduce/pbq/wal/unaligned/fs/compactor_test.go
+++ b/pkg/reduce/pbq/wal/unaligned/fs/compactor_test.go
@@ -392,9 +392,6 @@ func TestCompactor_ContextClose(t *testing.T) {
}
time.Sleep(3 * time.Second)
err = c.Stop()
- if err != nil {
- println(err.Error())
- }
assert.NoError(t, err)
}
diff --git a/pkg/sdkclient/grpc/grpc_utils.go b/pkg/sdkclient/grpc/grpc_utils.go
index 42fba83e86..293ba8e8d7 100644
--- a/pkg/sdkclient/grpc/grpc_utils.go
+++ b/pkg/sdkclient/grpc/grpc_utils.go
@@ -48,7 +48,7 @@ func ConnectToServer(udsSockAddr string, serverInfo *serverinfo.ServerInfo, maxM
return nil, fmt.Errorf("failed to start Multiproc Client: %w", err)
}
- conn, err = grpc.Dial(
+ conn, err = grpc.NewClient(
fmt.Sprintf("%s:///%s", resolver.CustScheme, resolver.CustServiceName),
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig": [{"round_robin":{}}]}`),
grpc.WithTransportCredentials(insecure.NewCredentials()),
@@ -58,12 +58,12 @@ func ConnectToServer(udsSockAddr string, serverInfo *serverinfo.ServerInfo, maxM
sockAddr = getUdsSockAddr(udsSockAddr)
log.Println("UDS Client:", sockAddr)
- conn, err = grpc.Dial(sockAddr, grpc.WithTransportCredentials(insecure.NewCredentials()),
+ conn, err = grpc.NewClient(sockAddr, grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMessageSize), grpc.MaxCallSendMsgSize(maxMessageSize)))
}
if err != nil {
- return nil, fmt.Errorf("failed to execute grpc.Dial(%q): %w", sockAddr, err)
+ return nil, fmt.Errorf("failed to execute grpc.NewClient(%q): %w", sockAddr, err)
}
return conn, nil
diff --git a/pkg/sdkclient/reducer/client.go b/pkg/sdkclient/reducer/client.go
index 6825fdc4c2..b728a715c1 100644
--- a/pkg/sdkclient/reducer/client.go
+++ b/pkg/sdkclient/reducer/client.go
@@ -64,7 +64,7 @@ func NewFromClient(c reducepb.ReduceClient) (Client, error) {
}
// CloseConn closes the grpc client connection.
-func (c *client) CloseConn(ctx context.Context) error {
+func (c *client) CloseConn(context.Context) error {
return c.conn.Close()
}
diff --git a/pkg/sdkclient/source/client.go b/pkg/sdkclient/source/client.go
index 39b96a13ed..550c888d66 100644
--- a/pkg/sdkclient/source/client.go
+++ b/pkg/sdkclient/source/client.go
@@ -18,8 +18,9 @@ package source
import (
"context"
+ "errors"
"fmt"
- "io"
+ "time"
sourcepb "github.com/numaproj/numaflow-go/pkg/apis/proto/source/v1"
"google.golang.org/grpc"
@@ -28,19 +29,22 @@ import (
"github.com/numaproj/numaflow/pkg/sdkclient"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
"github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/shared/logging"
)
// client contains the grpc connection and the grpc client.
type client struct {
- conn *grpc.ClientConn
- grpcClt sourcepb.SourceClient
+ conn *grpc.ClientConn
+ grpcClt sourcepb.SourceClient
+ readStream sourcepb.Source_ReadFnClient
+ ackStream sourcepb.Source_AckFnClient
}
var _ Client = (*client)(nil)
-func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(ctx context.Context, serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.SourceAddr)
-
+ var logger = logging.FromContext(ctx)
for _, inputOption := range inputOptions {
inputOption(opts)
}
@@ -54,16 +58,102 @@ func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (C
c.conn = conn
c.grpcClt = sourcepb.NewSourceClient(conn)
+
+ // wait until the server is ready
+waitUntilReady:
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, fmt.Errorf("failed to connect to the server: %v", ctx.Err())
+ default:
+ ready, _ := c.IsReady(ctx, &emptypb.Empty{})
+ if ready {
+ break waitUntilReady
+ } else {
+ logger.Warnw("source client is not ready")
+ time.Sleep(100 * time.Millisecond)
+ }
+ }
+ }
+
+ c.readStream, err = c.grpcClt.ReadFn(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create read stream: %v", err)
+ }
+
+ c.ackStream, err = c.grpcClt.AckFn(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create ack stream: %v", err)
+ }
+
+ // Send handshake request for read stream
+ readHandshakeRequest := &sourcepb.ReadRequest{
+ Handshake: &sourcepb.Handshake{
+ Sot: true,
+ },
+ }
+ if err := c.readStream.Send(readHandshakeRequest); err != nil {
+ return nil, fmt.Errorf("failed to send read handshake request: %v", err)
+ }
+
+ // Wait for handshake response for read stream
+ readHandshakeResponse, err := c.readStream.Recv()
+ if err != nil {
+ return nil, fmt.Errorf("failed to receive read handshake response: %v", err)
+ }
+ if readHandshakeResponse.GetHandshake() == nil || !readHandshakeResponse.GetHandshake().GetSot() {
+ return nil, fmt.Errorf("invalid read handshake response")
+ }
+
+ // Send handshake request for ack stream
+ ackHandshakeRequest := &sourcepb.AckRequest{
+ Handshake: &sourcepb.Handshake{
+ Sot: true,
+ },
+ }
+ if err := c.ackStream.Send(ackHandshakeRequest); err != nil {
+ return nil, fmt.Errorf("failed to send ack handshake request: %v", err)
+ }
+
+ // Wait for handshake response for ack stream
+ ackHandshakeResponse, err := c.ackStream.Recv()
+ if err != nil {
+ return nil, fmt.Errorf("failed to receive ack handshake response: %v", err)
+ }
+ if ackHandshakeResponse.GetHandshake() == nil || !ackHandshakeResponse.GetHandshake().GetSot() {
+ return nil, fmt.Errorf("invalid ack handshake response")
+ }
+
return c, nil
}
// NewFromClient creates a new client object from the grpc client. This is used for testing.
-func NewFromClient(c sourcepb.SourceClient) (Client, error) {
- return &client{grpcClt: c}, nil
+func NewFromClient(ctx context.Context, srcClient sourcepb.SourceClient, inputOptions ...sdkclient.Option) (Client, error) {
+ var opts = sdkclient.DefaultOptions(sdkclient.SourceAddr)
+
+ for _, inputOption := range inputOptions {
+ inputOption(opts)
+ }
+
+ c := new(client)
+ c.grpcClt = srcClient
+
+ c.readStream, _ = c.grpcClt.ReadFn(ctx)
+ c.ackStream, _ = c.grpcClt.AckFn(ctx)
+
+ return c, nil
}
// CloseConn closes the grpc client connection.
-func (c *client) CloseConn(ctx context.Context) error {
+func (c *client) CloseConn(context.Context) error {
+ err := c.readStream.CloseSend()
+ if err != nil {
+ return err
+ }
+ err = c.ackStream.CloseSend()
+ if err != nil {
+ return err
+ }
return c.conn.Close()
}
@@ -76,33 +166,51 @@ func (c *client) IsReady(ctx context.Context, in *emptypb.Empty) (bool, error) {
return resp.GetReady(), nil
}
-// ReadFn reads data from the source.
-func (c *client) ReadFn(ctx context.Context, req *sourcepb.ReadRequest, datumCh chan<- *sourcepb.ReadResponse) error {
- stream, err := c.grpcClt.ReadFn(ctx, req)
+func (c *client) ReadFn(_ context.Context, req *sourcepb.ReadRequest, datumCh chan<- *sourcepb.ReadResponse) error {
+ err := c.readStream.Send(req)
if err != nil {
- return fmt.Errorf("failed to execute c.grpcClt.ReadFn(): %w", err)
+ return fmt.Errorf("failed to send read request: %v", err)
}
+
for {
- select {
- case <-ctx.Done():
- return ctx.Err()
- default:
- var resp *sourcepb.ReadResponse
- resp, err = stream.Recv()
- if err == io.EOF {
- return nil
- }
- if err != nil {
- return err
- }
- datumCh <- resp
+ resp, err := c.readStream.Recv()
+ // we don't need an EOF check because we only close the stream during shutdown.
+ if errors.Is(err, context.Canceled) {
+ break
+ }
+ if err != nil {
+ return fmt.Errorf("failed to receive read response: %v", err)
}
+ if resp.GetStatus().GetEot() {
+ break
+ }
+ datumCh <- resp
}
+ return nil
}
// AckFn acknowledges the data from the source.
-func (c *client) AckFn(ctx context.Context, req *sourcepb.AckRequest) (*sourcepb.AckResponse, error) {
- return c.grpcClt.AckFn(ctx, req)
+func (c *client) AckFn(_ context.Context, reqs []*sourcepb.AckRequest) ([]*sourcepb.AckResponse, error) {
+ // Send the ack request
+ for _, req := range reqs {
+ err := c.ackStream.Send(req)
+ if err != nil {
+ return nil, fmt.Errorf("failed to send ack request: %v", err)
+ }
+ }
+
+ responses := make([]*sourcepb.AckResponse, len(reqs))
+ for i := 0; i < len(reqs); i++ {
+ // Wait for the ack response
+ resp, err := c.ackStream.Recv()
+ // we don't need an EOF check because we only close the stream during shutdown.
+ if err != nil {
+ return nil, fmt.Errorf("failed to receive ack response: %v", err)
+ }
+ responses[i] = resp
+ }
+
+ return responses, nil
}
// PendingFn returns the number of pending data from the source.
diff --git a/pkg/sdkclient/source/client_test.go b/pkg/sdkclient/source/client_test.go
index 450394b877..d19e3e8737 100644
--- a/pkg/sdkclient/source/client_test.go
+++ b/pkg/sdkclient/source/client_test.go
@@ -19,9 +19,7 @@ package source
import (
"context"
"fmt"
- "io"
"reflect"
- "sync"
"testing"
"time"
@@ -61,11 +59,7 @@ func TestIsReady(t *testing.T) {
mockClient.EXPECT().IsReady(gomock.Any(), gomock.Any()).Return(&sourcepb.ReadyResponse{Ready: true}, nil)
mockClient.EXPECT().IsReady(gomock.Any(), gomock.Any()).Return(&sourcepb.ReadyResponse{Ready: false}, fmt.Errorf("mock connection refused"))
- testClient, err := NewFromClient(mockClient)
- assert.NoError(t, err)
- reflect.DeepEqual(testClient, &client{
- grpcClt: mockClient,
- })
+ testClient := client{grpcClt: mockClient}
ready, err := testClient.IsReady(ctx, &emptypb.Empty{})
assert.True(t, ready)
@@ -100,16 +94,28 @@ func TestReadFn(t *testing.T) {
for i := 0; i < numRecords; i++ {
mockStreamClient.EXPECT().Recv().Return(expectedResp, nil)
}
- mockStreamClient.EXPECT().Recv().Return(expectedResp, io.EOF)
+
+ eotResponse := &sourcepb.ReadResponse{
+ Status: &sourcepb.ReadResponse_Status{
+ Eot: true,
+ Code: 0,
+ },
+ }
+ mockStreamClient.EXPECT().Recv().Return(eotResponse, nil)
mockStreamClient.EXPECT().CloseSend().Return(nil).AnyTimes()
- mockClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(mockStreamClient, nil)
- testClient, err := NewFromClient(mockClient)
- assert.NoError(t, err)
- assert.True(t, reflect.DeepEqual(testClient, &client{
- grpcClt: mockClient,
- }))
+ request := &sourcepb.ReadRequest{
+ Request: &sourcepb.ReadRequest_Request{
+ NumRecords: uint64(numRecords),
+ },
+ }
+ mockStreamClient.EXPECT().Send(request).Return(nil)
+
+ testClient := &client{
+ grpcClt: mockClient,
+ readStream: mockStreamClient,
+ }
responseCh := make(chan *sourcepb.ReadResponse)
@@ -127,18 +133,12 @@ func TestReadFn(t *testing.T) {
}
}()
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
- err = testClient.ReadFn(ctx, &sourcepb.ReadRequest{
- Request: &sourcepb.ReadRequest_Request{
- NumRecords: uint64(numRecords),
- },
- }, responseCh)
- assert.NoError(t, err)
- }()
- wg.Wait()
+ err := testClient.ReadFn(ctx, &sourcepb.ReadRequest{
+ Request: &sourcepb.ReadRequest_Request{
+ NumRecords: uint64(numRecords),
+ },
+ }, responseCh)
+ assert.NoError(t, err)
close(responseCh)
}
@@ -150,22 +150,47 @@ func TestAckFn(t *testing.T) {
defer ctrl.Finish()
mockClient := sourcemock.NewMockSourceClient(ctrl)
- mockClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(&sourcepb.AckResponse{}, nil)
- mockClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(&sourcepb.AckResponse{}, fmt.Errorf("mock connection refused"))
+ mockStream := sourcemock.NewMockSource_AckFnClient(ctrl)
- testClient, err := NewFromClient(mockClient)
+ // Handshake request and response
+ mockStream.EXPECT().Send(&sourcepb.AckRequest{
+ Handshake: &sourcepb.Handshake{
+ Sot: true,
+ },
+ }).Return(nil)
+ mockStream.EXPECT().Recv().Return(&sourcepb.AckResponse{
+ Handshake: &sourcepb.Handshake{
+ Sot: true,
+ },
+ }, nil)
+
+ // Ack request and response
+ mockStream.EXPECT().Send(gomock.Any()).Return(nil)
+ mockStream.EXPECT().Recv().Return(&sourcepb.AckResponse{}, nil)
+
+ testClient := client{
+ grpcClt: mockClient,
+ ackStream: mockStream,
+ }
+
+ // Perform handshake
+ ackHandshakeRequest := &sourcepb.AckRequest{
+ Handshake: &sourcepb.Handshake{
+ Sot: true,
+ },
+ }
+ err := testClient.ackStream.Send(ackHandshakeRequest)
assert.NoError(t, err)
- reflect.DeepEqual(testClient, &client{
- grpcClt: mockClient,
- })
- ack, err := testClient.AckFn(ctx, &sourcepb.AckRequest{})
+ ackHandshakeResponse, err := testClient.ackStream.Recv()
assert.NoError(t, err)
- assert.Equal(t, &sourcepb.AckResponse{}, ack)
+ assert.NotNil(t, ackHandshakeResponse.GetHandshake())
+ assert.True(t, ackHandshakeResponse.GetHandshake().GetSot())
- ack, err = testClient.AckFn(ctx, &sourcepb.AckRequest{})
- assert.EqualError(t, err, "mock connection refused")
- assert.Equal(t, &sourcepb.AckResponse{}, ack)
+ // Test AckFn
+ ack, err := testClient.AckFn(ctx, []*sourcepb.AckRequest{{}})
+ assert.NoError(t, err)
+ assert.Equal(t, []*sourcepb.AckResponse{{}}, ack)
}
func TestPendingFn(t *testing.T) {
@@ -183,11 +208,9 @@ func TestPendingFn(t *testing.T) {
}, nil)
mockClient.EXPECT().PendingFn(gomock.Any(), gomock.Any()).Return(&sourcepb.PendingResponse{}, fmt.Errorf("mock connection refused"))
- testClient, err := NewFromClient(mockClient)
- assert.NoError(t, err)
- reflect.DeepEqual(testClient, &client{
+ testClient := client{
grpcClt: mockClient,
- })
+ }
pending, err := testClient.PendingFn(ctx, &emptypb.Empty{})
assert.NoError(t, err)
diff --git a/pkg/sdkclient/source/interface.go b/pkg/sdkclient/source/interface.go
index ea897b8207..cc26f2cd95 100644
--- a/pkg/sdkclient/source/interface.go
+++ b/pkg/sdkclient/source/interface.go
@@ -32,7 +32,7 @@ type Client interface {
// ReadFn reads messages from the udsource.
ReadFn(ctx context.Context, req *sourcepb.ReadRequest, datumCh chan<- *sourcepb.ReadResponse) error
// AckFn acknowledges messages from the udsource.
- AckFn(ctx context.Context, req *sourcepb.AckRequest) (*sourcepb.AckResponse, error)
+ AckFn(ctx context.Context, req []*sourcepb.AckRequest) ([]*sourcepb.AckResponse, error)
// PendingFn returns the number of pending messages from the udsource.
PendingFn(ctx context.Context, req *emptypb.Empty) (*sourcepb.PendingResponse, error)
// PartitionsFn returns the list of partitions from the udsource.
diff --git a/pkg/sources/source.go b/pkg/sources/source.go
index e206d76dbc..0b3e23a94b 100644
--- a/pkg/sources/source.go
+++ b/pkg/sources/source.go
@@ -201,7 +201,7 @@ func (sp *SourceProcessor) Start(ctx context.Context) error {
return err
}
- srcClient, err := sourceclient.New(serverInfo, sdkclient.WithMaxMessageSize(maxMessageSize))
+ srcClient, err := sourceclient.New(ctx, serverInfo, sdkclient.WithMaxMessageSize(maxMessageSize))
if err != nil {
return fmt.Errorf("failed to create a new gRPC client: %w", err)
}
diff --git a/pkg/sources/udsource/grpc_udsource.go b/pkg/sources/udsource/grpc_udsource.go
index 525326651f..8d0389a2ee 100644
--- a/pkg/sources/udsource/grpc_udsource.go
+++ b/pkg/sources/udsource/grpc_udsource.go
@@ -175,12 +175,16 @@ func (u *GRPCBasedUDSource) ApplyAckFn(ctx context.Context, offsets []isb.Offset
for i, offset := range offsets {
rOffsets[i] = ConvertToUserDefinedSourceOffset(offset)
}
- var r = &sourcepb.AckRequest{
- Request: &sourcepb.AckRequest_Request{
- Offsets: rOffsets,
- },
+ ackRequests := make([]*sourcepb.AckRequest, len(rOffsets))
+ for i, offset := range rOffsets {
+ var r = &sourcepb.AckRequest{
+ Request: &sourcepb.AckRequest_Request{
+ Offset: offset,
+ },
+ }
+ ackRequests[i] = r
}
- _, err := u.client.AckFn(ctx, r)
+ _, err := u.client.AckFn(ctx, ackRequests)
return err
}
diff --git a/pkg/sources/udsource/grpc_udsource_test.go b/pkg/sources/udsource/grpc_udsource_test.go
index bf7a486fad..e0a0ab4ca5 100644
--- a/pkg/sources/udsource/grpc_udsource_test.go
+++ b/pkg/sources/udsource/grpc_udsource_test.go
@@ -20,7 +20,6 @@ import (
"context"
"errors"
"fmt"
- "io"
"testing"
"time"
@@ -31,8 +30,6 @@ import (
"go.uber.org/goleak"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
- "google.golang.org/protobuf/proto"
- "google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/numaproj/numaflow/pkg/isb"
@@ -43,24 +40,8 @@ func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
-type rpcMsg struct {
- msg proto.Message
-}
-
-func (r *rpcMsg) Matches(msg interface{}) bool {
- m, ok := msg.(proto.Message)
- if !ok {
- return false
- }
- return proto.Equal(m, r.msg)
-}
-
-func (r *rpcMsg) String() string {
- return fmt.Sprintf("is %s", r.msg)
-}
-
-func NewMockUDSgRPCBasedUDSource(mockClient *sourcemock.MockSourceClient) *GRPCBasedUDSource {
- c, _ := sourceclient.NewFromClient(mockClient)
+func NewMockUDSgRPCBasedUDSource(ctx context.Context, mockClient *sourcemock.MockSourceClient) *GRPCBasedUDSource {
+ c, _ := sourceclient.NewFromClient(ctx, mockClient)
return &GRPCBasedUDSource{
vertexName: "testVertex",
pipelineName: "testPipeline",
@@ -75,6 +56,8 @@ func Test_gRPCBasedUDSource_WaitUntilReadyWithMockClient(t *testing.T) {
mockClient := sourcemock.NewMockSourceClient(ctrl)
mockClient.EXPECT().IsReady(gomock.Any(), gomock.Any()).Return(&sourcepb.ReadyResponse{Ready: true}, nil)
+ mockClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(nil, nil)
+ mockClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(nil, nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -85,7 +68,7 @@ func Test_gRPCBasedUDSource_WaitUntilReadyWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockClient)
err := u.WaitUntilReady(ctx)
assert.NoError(t, err)
}
@@ -103,6 +86,8 @@ func Test_gRPCBasedUDSource_ApplyPendingWithMockClient(t *testing.T) {
mockSourceClient := sourcemock.NewMockSourceClient(ctrl)
mockSourceClient.EXPECT().PendingFn(gomock.Any(), gomock.Any()).Return(testResponse, nil).AnyTimes()
+ mockSourceClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(nil, nil)
+ mockSourceClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(nil, nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -113,7 +98,7 @@ func Test_gRPCBasedUDSource_ApplyPendingWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockSourceClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockSourceClient)
count, err := u.ApplyPendingFn(ctx)
assert.NoError(t, err)
assert.Equal(t, int64(123), count)
@@ -131,6 +116,8 @@ func Test_gRPCBasedUDSource_ApplyPendingWithMockClient(t *testing.T) {
mockSourceClient := sourcemock.NewMockSourceClient(ctrl)
mockSourceClient.EXPECT().PendingFn(gomock.Any(), gomock.Any()).Return(testResponse, nil).AnyTimes()
+ mockSourceClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(nil, nil)
+ mockSourceClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(nil, nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -141,7 +128,7 @@ func Test_gRPCBasedUDSource_ApplyPendingWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockSourceClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockSourceClient)
count, err := u.ApplyPendingFn(ctx)
assert.NoError(t, err)
assert.Equal(t, isb.PendingNotAvailable, count)
@@ -159,6 +146,8 @@ func Test_gRPCBasedUDSource_ApplyPendingWithMockClient(t *testing.T) {
mockSourceErrClient := sourcemock.NewMockSourceClient(ctrl)
mockSourceErrClient.EXPECT().PendingFn(gomock.Any(), gomock.Any()).Return(testResponse, fmt.Errorf("mock udsource pending error")).AnyTimes()
+ mockSourceErrClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(nil, nil)
+ mockSourceErrClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(nil, nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -169,7 +158,7 @@ func Test_gRPCBasedUDSource_ApplyPendingWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockSourceErrClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockSourceErrClient)
count, err := u.ApplyPendingFn(ctx)
assert.Equal(t, isb.PendingNotAvailable, count)
@@ -183,13 +172,8 @@ func Test_gRPCBasedUDSource_ApplyReadWithMockClient(t *testing.T) {
defer ctrl.Finish()
mockClient := sourcemock.NewMockSourceClient(ctrl)
mockReadClient := sourcemock.NewMockSource_ReadFnClient(ctrl)
-
- req := &sourcepb.ReadRequest{
- Request: &sourcepb.ReadRequest_Request{
- NumRecords: 1,
- TimeoutInMs: 1000,
- },
- }
+ mockClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(mockReadClient, nil)
+ mockClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(nil, nil)
offset := &sourcepb.Offset{Offset: []byte(`test_offset`), PartitionId: 0}
@@ -202,10 +186,18 @@ func Test_gRPCBasedUDSource_ApplyReadWithMockClient(t *testing.T) {
Keys: []string{"test_key"},
},
}
-
mockReadClient.EXPECT().Recv().Return(expectedResponse, nil).Times(1)
- mockReadClient.EXPECT().Recv().Return(nil, io.EOF).Times(1)
- mockClient.EXPECT().ReadFn(gomock.Any(), &rpcMsg{msg: req}).Return(mockReadClient, nil)
+
+ eotResponse := &sourcepb.ReadResponse{Status: &sourcepb.ReadResponse_Status{Eot: true}}
+ mockReadClient.EXPECT().Recv().Return(eotResponse, nil).Times(1)
+
+ req := &sourcepb.ReadRequest{
+ Request: &sourcepb.ReadRequest_Request{
+ NumRecords: 1,
+ TimeoutInMs: 1000,
+ },
+ }
+ mockReadClient.EXPECT().Send(req).Return(nil).Times(1)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -216,7 +208,7 @@ func Test_gRPCBasedUDSource_ApplyReadWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockClient)
readMessages, err := u.ApplyReadFn(ctx, 1, time.Millisecond*1000)
assert.NoError(t, err)
assert.Equal(t, 1, len(readMessages))
@@ -232,6 +224,8 @@ func Test_gRPCBasedUDSource_ApplyReadWithMockClient(t *testing.T) {
mockClient := sourcemock.NewMockSourceClient(ctrl)
mockReadClient := sourcemock.NewMockSource_ReadFnClient(ctrl)
+ mockClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(mockReadClient, nil)
+ mockClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(nil, nil)
req := &sourcepb.ReadRequest{
Request: &sourcepb.ReadRequest_Request{
@@ -239,6 +233,7 @@ func Test_gRPCBasedUDSource_ApplyReadWithMockClient(t *testing.T) {
TimeoutInMs: 1000,
},
}
+ mockReadClient.EXPECT().Send(req).Return(nil).Times(1)
var TestEventTime = time.Unix(1661169600, 0).UTC()
expectedResponse := &sourcepb.ReadResponse{
@@ -249,10 +244,7 @@ func Test_gRPCBasedUDSource_ApplyReadWithMockClient(t *testing.T) {
Keys: []string{"test_key"},
},
}
-
mockReadClient.EXPECT().Recv().Return(expectedResponse, errors.New("mock error for read")).AnyTimes()
- mockClient.EXPECT().ReadFn(gomock.Any(), &rpcMsg{msg: req}).Return(mockReadClient, nil)
-
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
go func() {
@@ -262,7 +254,7 @@ func Test_gRPCBasedUDSource_ApplyReadWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockClient)
readMessages, err := u.ApplyReadFn(ctx, 1, time.Millisecond*1000)
assert.Error(t, err)
assert.Equal(t, 0, len(readMessages))
@@ -278,16 +270,25 @@ func Test_gRPCBasedUDSource_ApplyAckWithMockClient(t *testing.T) {
offset2 := &sourcepb.Offset{Offset: []byte("test-offset-2"), PartitionId: 0}
mockClient := sourcemock.NewMockSourceClient(ctrl)
- req := &sourcepb.AckRequest{
+ mockAckClient := sourcemock.NewMockSource_AckFnClient(ctrl)
+ mockClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(nil, nil)
+ mockClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(mockAckClient, nil)
+
+ req1 := &sourcepb.AckRequest{
+ Request: &sourcepb.AckRequest_Request{
+ Offset: offset1,
+ },
+ }
+
+ req2 := &sourcepb.AckRequest{
Request: &sourcepb.AckRequest_Request{
- Offsets: []*sourcepb.Offset{
- offset1,
- offset2,
- },
+ Offset: offset2,
},
}
- mockClient.EXPECT().AckFn(gomock.Any(), &rpcMsg{msg: req}).Return(&sourcepb.AckResponse{Result: &sourcepb.AckResponse_Result{Success: &emptypb.Empty{}}}, nil).AnyTimes()
+ mockAckClient.EXPECT().Send(req1).Return(nil).Times(1)
+ mockAckClient.EXPECT().Send(req2).Return(nil).Times(1)
+ mockAckClient.EXPECT().Recv().Return(&sourcepb.AckResponse{}, nil).Times(2)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -298,7 +299,7 @@ func Test_gRPCBasedUDSource_ApplyAckWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockClient)
err := u.ApplyAckFn(ctx, []isb.Offset{
NewUserDefinedSourceOffset(offset1),
NewUserDefinedSourceOffset(offset2),
@@ -314,16 +315,18 @@ func Test_gRPCBasedUDSource_ApplyAckWithMockClient(t *testing.T) {
offset2 := &sourcepb.Offset{Offset: []byte("test-offset-2"), PartitionId: 0}
mockClient := sourcemock.NewMockSourceClient(ctrl)
- req := &sourcepb.AckRequest{
+ mockAckClient := sourcemock.NewMockSource_AckFnClient(ctrl)
+ mockClient.EXPECT().ReadFn(gomock.Any(), gomock.Any()).Return(nil, nil)
+ mockClient.EXPECT().AckFn(gomock.Any(), gomock.Any()).Return(mockAckClient, nil)
+
+ req1 := &sourcepb.AckRequest{
Request: &sourcepb.AckRequest_Request{
- Offsets: []*sourcepb.Offset{
- offset1,
- offset2,
- },
+ Offset: offset1,
},
}
- mockClient.EXPECT().AckFn(gomock.Any(), &rpcMsg{msg: req}).Return(nil, status.New(codes.DeadlineExceeded, "mock test err").Err())
+ mockAckClient.EXPECT().Send(req1).Return(status.New(codes.DeadlineExceeded, "mock test err").Err()).Times(1)
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
go func() {
@@ -333,11 +336,11 @@ func Test_gRPCBasedUDSource_ApplyAckWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSource(mockClient)
+ u := NewMockUDSgRPCBasedUDSource(ctx, mockClient)
err := u.ApplyAckFn(ctx, []isb.Offset{
NewUserDefinedSourceOffset(offset1),
NewUserDefinedSourceOffset(offset2),
})
- assert.ErrorIs(t, err, status.New(codes.DeadlineExceeded, "mock test err").Err())
+ assert.Equal(t, err.Error(), fmt.Sprintf("failed to send ack request: %s", status.New(codes.DeadlineExceeded, "mock test err").Err()))
})
}
diff --git a/pkg/sources/udsource/user_defined_source.go b/pkg/sources/udsource/user_defined_source.go
index ac1b2debb5..5ba77019a1 100644
--- a/pkg/sources/udsource/user_defined_source.go
+++ b/pkg/sources/udsource/user_defined_source.go
@@ -62,10 +62,6 @@ func NewUserDefinedSource(ctx context.Context, vertexInstance *dfv1.VertexInstan
}
}
- if err != nil {
- u.logger.Errorw("Error instantiating the forwarder", zap.Error(err))
- return nil, err
- }
return u, nil
}
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 7748607ca5..624d5f14a8 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -43,9 +43,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.88"
+version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356"
+checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
[[package]]
name = "arc-swap"
@@ -307,7 +307,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"shlex",
"syn",
"which",
@@ -351,18 +351,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
-version = "1.7.1"
+version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
+checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
dependencies = [
"serde",
]
[[package]]
name = "cc"
-version = "1.1.18"
+version = "1.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476"
+checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
dependencies = [
"jobserver",
"libc",
@@ -1098,6 +1098,7 @@ dependencies = [
"tokio",
"tokio-rustls 0.26.0",
"tower-service",
+ "webpki-roots 0.26.5",
]
[[package]]
@@ -1135,9 +1136,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
-version = "0.1.60"
+version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -1476,6 +1477,7 @@ dependencies = [
"chrono",
"hyper-util",
"kube",
+ "log",
"numaflow 0.1.1",
"numaflow-models",
"once_cell",
@@ -1603,7 +1605,7 @@ dependencies = [
[[package]]
name = "numaflow"
version = "0.1.1"
-source = "git+https://github.com/numaproj/numaflow-rs.git?branch=main#d3afabd2fff1d070bb3fd79866c0389f009556b3"
+source = "git+https://github.com/numaproj/numaflow-rs.git?branch=handshake#baecc88456f317b08bc869f82596e2b746cf798b"
dependencies = [
"chrono",
"futures-util",
@@ -1628,7 +1630,7 @@ version = "0.0.0-pre"
dependencies = [
"k8s-openapi",
"kube",
- "reqwest",
+ "reqwest 0.11.27",
"serde",
"serde_derive",
"serde_json",
@@ -1966,6 +1968,54 @@ dependencies = [
"prost",
]
+[[package]]
+name = "quinn"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684"
+dependencies = [
+ "bytes",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.0.0",
+ "rustls 0.23.13",
+ "socket2",
+ "thiserror",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6"
+dependencies = [
+ "bytes",
+ "rand",
+ "ring",
+ "rustc-hash 2.0.0",
+ "rustls 0.23.13",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b"
+dependencies = [
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "quote"
version = "1.0.37"
@@ -2134,10 +2184,52 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "webpki-roots",
+ "webpki-roots 0.25.4",
"winreg",
]
+[[package]]
+name = "reqwest"
+version = "0.12.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.4.1",
+ "hyper-rustls 0.27.3",
+ "hyper-util",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls 0.23.13",
+ "rustls-pemfile 2.1.3",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.1",
+ "tokio",
+ "tokio-rustls 0.26.0",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots 0.26.5",
+ "windows-registry",
+]
+
[[package]]
name = "ring"
version = "0.17.8"
@@ -2187,6 +2279,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+[[package]]
+name = "rustc-hash"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
+
[[package]]
name = "rustc_version"
version = "0.4.1"
@@ -2497,7 +2595,7 @@ name = "servesink"
version = "0.1.0"
dependencies = [
"numaflow 0.1.1",
- "reqwest",
+ "reqwest 0.12.7",
"tokio",
"tonic",
"tracing",
@@ -2678,6 +2776,9 @@ name = "sync_wrapper"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+dependencies = [
+ "futures-core",
+]
[[package]]
name = "system-configuration"
@@ -2906,9 +3007,9 @@ dependencies = [
[[package]]
name = "toml_edit"
-version = "0.22.20"
+version = "0.22.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
+checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
dependencies = [
"indexmap 2.5.0",
"serde",
@@ -3137,9 +3238,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-normalization"
-version = "0.1.23"
+version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
dependencies = [
"tinyvec",
]
@@ -3305,6 +3406,15 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+[[package]]
+name = "webpki-roots"
+version = "0.26.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "which"
version = "4.4.2"
@@ -3348,6 +3458,36 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "windows-registry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+dependencies = [
+ "windows-result",
+ "windows-strings",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+dependencies = [
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-sys"
version = "0.48.0"
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index 01eb5afafd..da75a4c8c8 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -6,14 +6,14 @@ edition = "2021"
[dependencies]
axum = "0.7.5"
axum-server = { version = "0.7.1", features = ["tls-rustls"] }
-tonic = "0.12.1"
+tonic = "0.12.2"
bytes = "1.7.1"
thiserror = "1.0.63"
tokio = { version = "1.39.3", features = ["full"] }
tracing = "0.1.40"
tokio-util = "0.7.11"
tokio-stream = "0.1.15"
-prost = "0.13.1"
+prost = "0.13.2"
prost-types = "0.13.1"
chrono = "0.4.31"
base64 = "0.22.1"
@@ -34,10 +34,11 @@ backoff = { path = "../backoff" }
parking_lot = "0.12.3"
prometheus-client = "0.22.3"
kube = "0.94.0"
+log = "0.4.22"
[dev-dependencies]
tempfile = "3.11.0"
-numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
+numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "handshake" }
[build-dependencies]
tonic-build = "0.12.1"
diff --git a/rust/monovertex/proto/source.proto b/rust/monovertex/proto/source.proto
index 131cc36d30..69ff154127 100644
--- a/rust/monovertex/proto/source.proto
+++ b/rust/monovertex/proto/source.proto
@@ -7,16 +7,19 @@ package source.v1;
service Source {
// Read returns a stream of datum responses.
- // The size of the returned ReadResponse is less than or equal to the num_records specified in ReadRequest.
- // If the request timeout is reached on server side, the returned ReadResponse will contain all the datum that have been read (which could be an empty list).
- rpc ReadFn(ReadRequest) returns (stream ReadResponse);
+ // The size of the returned responses is less than or equal to the num_records specified in each ReadRequest.
+ // If the request timeout is reached on the server side, the returned responses will contain all the datum that have been read (which could be an empty list).
+ // The server will continue to read and respond to subsequent ReadRequests until the client closes the stream.
+ // Once it has sent all the datum, the server will send a ReadResponse with the end of transmission flag set to true.
+ rpc ReadFn(stream ReadRequest) returns (stream ReadResponse);
- // AckFn acknowledges a list of datum offsets.
+ // AckFn acknowledges a stream of datum offsets.
// When AckFn is called, it implicitly indicates that the datum stream has been processed by the source vertex.
// The caller (numa) expects the AckFn to be successful, and it does not expect any errors.
// If there are some irrecoverable errors when the callee (UDSource) is processing the AckFn request,
- // then it is best to crash because there are no other retry mechanisms possible.
- rpc AckFn(AckRequest) returns (AckResponse);
+ // then it is best to crash because there are no other retry mechanisms possible.
+ // Clients sends n requests and expects n responses.
+ rpc AckFn(stream AckRequest) returns (stream AckResponse);
// PendingFn returns the number of pending records at the user defined source.
rpc PendingFn(google.protobuf.Empty) returns (PendingResponse);
@@ -28,6 +31,14 @@ service Source {
rpc IsReady(google.protobuf.Empty) returns (ReadyResponse);
}
+/*
+ * Handshake message between client and server to indicate the start of transmission.
+ */
+message Handshake {
+ // Required field indicating the start of transmission.
+ bool sot = 1;
+}
+
/*
* ReadRequest is the request for reading datum stream from user defined source.
*/
@@ -42,6 +53,7 @@ message ReadRequest {
}
// Required field indicating the request.
Request request = 1;
+ optional Handshake handshake = 2;
}
/*
@@ -65,8 +77,31 @@ message ReadResponse {
// e.g. Kafka and Redis Stream message usually include information about the headers.
map headers = 5;
}
+ message Status {
+ // Code to indicate the status of the response.
+ enum Code {
+ SUCCESS = 0;
+ FAILURE = 1;
+ }
+
+ // Error to indicate the error type. If the code is FAILURE, then the error field will be populated.
+ enum Error {
+ UNACKED = 0;
+ OTHER = 1;
+ }
+
+ // End of transmission flag.
+ bool eot = 1;
+ Code code = 2;
+ optional Error error = 3;
+ optional string msg = 4;
+ }
// Required field holding the result.
Result result = 1;
+ // Status of the response. Holds the end of transmission flag and the status code.
+ Status status = 2;
+ // Handshake message between client and server to indicate the start of transmission.
+ optional Handshake handshake = 3;
}
/*
@@ -75,14 +110,12 @@ message ReadResponse {
*/
message AckRequest {
message Request {
- // Required field holding a list of offsets to be acknowledged.
- // The offsets must be strictly corresponding to the previously read batch,
- // meaning the offsets must be in the same order as the datum responses in the ReadResponse.
- // By enforcing ordering, we can save deserialization effort on the server side, assuming the server keeps a local copy of the raw/un-serialized offsets.
- repeated Offset offsets = 1;
+ // Required field holding the offset to be acked
+ Offset offset = 1;
}
// Required field holding the request. The list will be ordered and will have the same order as the original Read response.
Request request = 1;
+ optional Handshake handshake = 2;
}
/*
@@ -102,6 +135,8 @@ message AckResponse {
}
// Required field holding the result.
Result result = 1;
+ // Handshake message between client and server to indicate the start of transmission.
+ optional Handshake handshake = 2;
}
/*
@@ -150,4 +185,4 @@ message Offset {
// It is useful for sources that have multiple partitions. e.g. Kafka.
// If the partition_id is not specified, it is assumed that the source has a single partition.
int32 partition_id = 2;
-}
\ No newline at end of file
+}
diff --git a/rust/monovertex/src/config.rs b/rust/monovertex/src/config.rs
index 81b115422f..5d245ed397 100644
--- a/rust/monovertex/src/config.rs
+++ b/rust/monovertex/src/config.rs
@@ -1,13 +1,20 @@
-use std::env;
-use std::sync::OnceLock;
-
+use crate::error::Error;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
-
use numaflow_models::models::{Backoff, MonoVertex, RetryStrategy};
+use std::env;
+use std::sync::OnceLock;
-use crate::error::Error;
+const DEFAULT_SOURCE_SOCKET: &str = "/var/run/numaflow/source.sock";
+const DEFAULT_SOURCE_SERVER_INFO_FILE: &str = "/var/run/numaflow/sourcer-server-info";
+const DEFAULT_SINK_SOCKET: &str = "/var/run/numaflow/sink.sock";
+const DEFAULT_FB_SINK_SOCKET: &str = "/var/run/numaflow/fb-sink.sock";
+const DEFAULT_SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/sinker-server-info";
+const DEFAULT_FB_SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/fb-sinker-server-info";
+const DEFAULT_TRANSFORMER_SOCKET: &str = "/var/run/numaflow/sourcetransform.sock";
+const DEFAULT_TRANSFORMER_SERVER_INFO_FILE: &str =
+ "/var/run/numaflow/sourcetransformer-server-info";
const ENV_MONO_VERTEX_OBJ: &str = "NUMAFLOW_MONO_VERTEX_OBJECT";
const ENV_GRPC_MAX_MESSAGE_SIZE: &str = "NUMAFLOW_GRPC_MAX_MESSAGE_SIZE";
const ENV_POD_REPLICA: &str = "NUMAFLOW_REPLICA";
@@ -77,15 +84,46 @@ pub struct Settings {
pub batch_size: u64,
pub timeout_in_ms: u32,
pub metrics_server_listen_port: u16,
- pub grpc_max_message_size: usize,
- pub is_transformer_enabled: bool,
- pub is_fallback_enabled: bool,
pub lag_check_interval_in_secs: u16,
pub lag_refresh_interval_in_secs: u16,
pub sink_max_retry_attempts: u16,
pub sink_retry_interval_in_ms: u32,
pub sink_retry_on_fail_strategy: OnFailureStrategy,
pub sink_default_retry_strategy: RetryStrategy,
+ pub sdk_config: SDKConfig,
+}
+
+#[derive(Debug, Clone)]
+pub struct SDKConfig {
+ pub grpc_max_message_size: usize,
+ pub is_transformer_enabled: bool,
+ pub is_fallback_enabled: bool,
+ pub source_socket_path: String,
+ pub sink_socket_path: String,
+ pub transformer_socket_path: String,
+ pub fallback_socket_path: String,
+ pub source_server_info_path: String,
+ pub sink_server_info_path: String,
+ pub transformer_server_info_path: String,
+ pub fallback_server_info_path: String,
+}
+
+impl Default for SDKConfig {
+ fn default() -> Self {
+ Self {
+ grpc_max_message_size: DEFAULT_GRPC_MAX_MESSAGE_SIZE,
+ is_transformer_enabled: false,
+ is_fallback_enabled: false,
+ source_socket_path: DEFAULT_SOURCE_SOCKET.to_string(),
+ sink_socket_path: DEFAULT_SINK_SOCKET.to_string(),
+ transformer_socket_path: DEFAULT_TRANSFORMER_SOCKET.to_string(),
+ fallback_socket_path: DEFAULT_FB_SINK_SOCKET.to_string(),
+ source_server_info_path: DEFAULT_SOURCE_SERVER_INFO_FILE.to_string(),
+ sink_server_info_path: DEFAULT_SINK_SERVER_INFO_FILE.to_string(),
+ transformer_server_info_path: DEFAULT_TRANSFORMER_SERVER_INFO_FILE.to_string(),
+ fallback_server_info_path: DEFAULT_FB_SINK_SERVER_INFO_FILE.to_string(),
+ }
+ }
}
impl Default for Settings {
@@ -106,15 +144,13 @@ impl Default for Settings {
batch_size: DEFAULT_BATCH_SIZE,
timeout_in_ms: DEFAULT_TIMEOUT_IN_MS,
metrics_server_listen_port: DEFAULT_METRICS_PORT,
- grpc_max_message_size: DEFAULT_GRPC_MAX_MESSAGE_SIZE,
- is_transformer_enabled: false,
- is_fallback_enabled: false,
lag_check_interval_in_secs: DEFAULT_LAG_CHECK_INTERVAL_IN_SECS,
lag_refresh_interval_in_secs: DEFAULT_LAG_REFRESH_INTERVAL_IN_SECS,
sink_max_retry_attempts: DEFAULT_MAX_SINK_RETRY_ATTEMPTS,
sink_retry_interval_in_ms: DEFAULT_SINK_RETRY_INTERVAL_IN_MS,
sink_retry_on_fail_strategy: DEFAULT_SINK_RETRY_ON_FAIL_STRATEGY,
sink_default_retry_strategy: default_retry_strategy,
+ sdk_config: Default::default(),
}
}
}
@@ -158,14 +194,14 @@ impl Settings {
.and_then(|metadata| metadata.name)
.ok_or_else(|| Error::ConfigError("Mono vertex name not found".to_string()))?;
- settings.is_transformer_enabled = mono_vertex_obj
+ settings.sdk_config.is_transformer_enabled = mono_vertex_obj
.spec
.source
.ok_or(Error::ConfigError("Source not found".to_string()))?
.transformer
.is_some();
- settings.is_fallback_enabled = mono_vertex_obj
+ settings.sdk_config.is_fallback_enabled = mono_vertex_obj
.spec
.sink
.as_deref()
@@ -211,7 +247,7 @@ impl Settings {
// check if the sink retry strategy is set to fallback and there is no fallback sink configured
// then we should return an error
if settings.sink_retry_on_fail_strategy == OnFailureStrategy::Fallback
- && !settings.is_fallback_enabled
+ && !settings.sdk_config.is_fallback_enabled
{
return Err(Error::ConfigError(
"Retry Strategy given as fallback but Fallback sink not configured"
@@ -221,7 +257,7 @@ impl Settings {
}
}
- settings.grpc_max_message_size = env::var(ENV_GRPC_MAX_MESSAGE_SIZE)
+ settings.sdk_config.grpc_max_message_size = env::var(ENV_GRPC_MAX_MESSAGE_SIZE)
.unwrap_or_else(|_| DEFAULT_GRPC_MAX_MESSAGE_SIZE.to_string())
.parse()
.map_err(|e| {
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index 1ba928123c..d60644b338 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -5,9 +5,10 @@ use crate::error::{Error, Result};
use crate::message::{Message, Offset};
use crate::metrics;
use crate::metrics::forward_metrics;
-use crate::sink::{proto, SinkClient};
-use crate::source::SourceClient;
-use crate::transformer::TransformerClient;
+use crate::sink::SinkWriter;
+use crate::sink_pb::Status::{Failure, Fallback, Success};
+use crate::source::{SourceAcker, SourceReader};
+use crate::transformer::SourceTransformer;
use chrono::Utc;
use tokio::task::JoinSet;
use tokio::time::sleep;
@@ -19,48 +20,52 @@ use tracing::{debug, info};
/// transformer is present, writing the messages to the sink, and then acknowledging the messages
/// back to the source.
pub(crate) struct Forwarder {
- source_client: SourceClient,
- sink_client: SinkClient,
- transformer_client: Option,
- fallback_client: Option,
+ source_reader: SourceReader,
+ source_acker: SourceAcker,
+ sink_writer: SinkWriter,
+ source_transformer: Option,
+ fb_sink_writer: Option,
cln_token: CancellationToken,
common_labels: Vec<(String, String)>,
}
/// ForwarderBuilder is used to build a Forwarder instance with optional fields.
pub(crate) struct ForwarderBuilder {
- source_client: SourceClient,
- sink_client: SinkClient,
+ source_reader: SourceReader,
+ source_acker: SourceAcker,
+ sink_writer: SinkWriter,
cln_token: CancellationToken,
- transformer_client: Option,
- fb_sink_client: Option,
+ source_transformer: Option,
+ fb_sink_writer: Option,
}
impl ForwarderBuilder {
/// Create a new builder with mandatory fields
pub(crate) fn new(
- source_client: SourceClient,
- sink_client: SinkClient,
+ source_reader: SourceReader,
+ source_acker: SourceAcker,
+ sink_writer: SinkWriter,
cln_token: CancellationToken,
) -> Self {
Self {
- source_client,
- sink_client,
+ source_reader,
+ source_acker,
+ sink_writer,
cln_token,
- transformer_client: None,
- fb_sink_client: None,
+ source_transformer: None,
+ fb_sink_writer: None,
}
}
/// Set the optional transformer client
- pub(crate) fn transformer_client(mut self, transformer_client: TransformerClient) -> Self {
- self.transformer_client = Some(transformer_client);
+ pub(crate) fn source_transformer(mut self, transformer_client: SourceTransformer) -> Self {
+ self.source_transformer = Some(transformer_client);
self
}
/// Set the optional fallback client
- pub(crate) fn fb_sink_client(mut self, fallback_client: SinkClient) -> Self {
- self.fb_sink_client = Some(fallback_client);
+ pub(crate) fn fallback_sink_writer(mut self, fallback_client: SinkWriter) -> Self {
+ self.fb_sink_writer = Some(fallback_client);
self
}
@@ -69,10 +74,11 @@ impl ForwarderBuilder {
pub(crate) fn build(self) -> Forwarder {
let common_labels = metrics::forward_metrics_labels().clone();
Forwarder {
- source_client: self.source_client,
- sink_client: self.sink_client,
- transformer_client: self.transformer_client,
- fallback_client: self.fb_sink_client,
+ source_reader: self.source_reader,
+ source_acker: self.source_acker,
+ sink_writer: self.sink_writer,
+ source_transformer: self.source_transformer,
+ fb_sink_writer: self.fb_sink_writer,
cln_token: self.cln_token,
common_labels,
}
@@ -86,6 +92,7 @@ impl Forwarder {
pub(crate) async fn start(&mut self) -> Result<()> {
let mut processed_msgs_count: usize = 0;
let mut last_forwarded_at = std::time::Instant::now();
+ info!("Forwarder has started");
loop {
let start_time = tokio::time::Instant::now();
if self.cln_token.is_cancelled() {
@@ -120,14 +127,19 @@ impl Forwarder {
async fn read_and_process_messages(&mut self) -> Result {
let start_time = tokio::time::Instant::now();
let messages = self
- .source_client
- .read_fn(config().batch_size, config().timeout_in_ms)
- .await?;
+ .source_reader
+ .read(config().batch_size, config().timeout_in_ms)
+ .await
+ .map_err(|e| {
+ Error::ForwarderError(format!("Failed to read messages from source {:?}", e))
+ })?;
+
debug!(
"Read batch size: {} and latency - {}ms",
messages.len(),
start_time.elapsed().as_millis()
);
+
forward_metrics()
.read_time
.get_or_create(&self.common_labels)
@@ -159,13 +171,27 @@ impl Forwarder {
.inc_by(bytes_count);
// Apply transformation if transformer is present
- let transformed_messages = self.apply_transformer(messages).await?;
+ let transformed_messages = self.apply_transformer(messages).await.map_err(|e| {
+ Error::ForwarderError(format!(
+ "Failed to apply transformation to messages {:?}",
+ e
+ ))
+ })?;
// Write the messages to the sink
- self.write_to_sink(transformed_messages).await?;
+ self.write_to_sink(transformed_messages)
+ .await
+ .map_err(|e| {
+ Error::ForwarderError(format!("Failed to write messages to sink {:?}", e))
+ })?;
// Acknowledge the messages back to the source
- self.acknowledge_messages(offsets).await?;
+ self.acknowledge_messages(offsets).await.map_err(|e| {
+ Error::ForwarderError(format!(
+ "Failed to acknowledge messages back to source {:?}",
+ e
+ ))
+ })?;
Ok(msg_count as usize)
}
@@ -173,7 +199,7 @@ impl Forwarder {
// Applies transformation to the messages if transformer is present
// we concurrently apply transformation to all the messages.
async fn apply_transformer(&self, messages: Vec) -> Result> {
- let Some(transformer_client) = &self.transformer_client else {
+ let Some(transformer_client) = &self.source_transformer else {
// return early if there is no transformer
return Ok(messages);
};
@@ -241,6 +267,13 @@ impl Forwarder {
}
Err(e) => Err(e)?,
}
+
+ // if we are shutting down, stop the retry
+ if self.cln_token.is_cancelled() {
+ return Err(Error::SinkError(
+ "Cancellation token triggered during retry".to_string(),
+ ));
+ }
}
// If after the retries we still have messages to process, handle the post retry failures
@@ -342,7 +375,7 @@ impl Forwarder {
messages_to_send: &mut Vec,
) -> Result {
let start_time = tokio::time::Instant::now();
- match self.sink_client.sink_fn(messages_to_send.clone()).await {
+ match self.sink_writer.sink_fn(messages_to_send.clone()).await {
Ok(response) => {
debug!("Sink latency - {}ms", start_time.elapsed().as_millis());
@@ -360,9 +393,9 @@ impl Forwarder {
// construct the error map for the failed messages
messages_to_send.retain(|msg| {
if let Some(result) = result_map.get(&msg.id) {
- return if result.status == proto::Status::Success as i32 {
+ return if result.status == Success as i32 {
false
- } else if result.status == proto::Status::Fallback as i32 {
+ } else if result.status == Fallback as i32 {
fallback_msgs.push(msg.clone()); // add to fallback messages
false
} else {
@@ -384,22 +417,22 @@ impl Forwarder {
.await;
// we need to retry
- return Ok(false);
+ Ok(false)
}
- Err(e) => return Err(e),
+ Err(e) => Err(e),
}
}
// Writes the fallback messages to the fallback sink
async fn handle_fallback_messages(&mut self, fallback_msgs: Vec) -> Result<()> {
- if self.fallback_client.is_none() {
+ if self.fb_sink_writer.is_none() {
return Err(Error::SinkError(
"Response contains fallback messages but no fallback sink is configured"
.to_string(),
));
}
- let fallback_client = self.fallback_client.as_mut().unwrap();
+ let fallback_client = self.fb_sink_writer.as_mut().unwrap();
let mut attempts = 0;
let mut fallback_error_map = HashMap::new();
// start with the original set of message to be sent.
@@ -440,12 +473,12 @@ impl Forwarder {
// construct the error map for the failed messages
messages_to_send.retain(|msg| {
if let Some(result) = result_map.get(&msg.id) {
- if result.status == proto::Status::Failure as i32 {
+ if result.status == Failure as i32 {
*fallback_error_map
.entry(result.err_msg.clone())
.or_insert(0) += 1;
true
- } else if result.status == proto::Status::Fallback as i32 {
+ } else if result.status == Fallback as i32 {
contains_fallback_status = true;
false
} else {
@@ -497,7 +530,7 @@ impl Forwarder {
let n = offsets.len();
let start_time = tokio::time::Instant::now();
- self.source_client.ack_fn(offsets).await?;
+ self.source_acker.ack(offsets).await?;
debug!("Ack latency - {}ms", start_time.elapsed().as_millis());
@@ -521,14 +554,18 @@ mod tests {
use chrono::Utc;
use numaflow::source::{Message, Offset, SourceReadRequest};
use numaflow::{sink, source, sourcetransform};
+ use tokio::sync::mpsc;
use tokio::sync::mpsc::Sender;
use tokio_util::sync::CancellationToken;
- use crate::error::Result;
use crate::forwarder::ForwarderBuilder;
- use crate::sink::{SinkClient, SinkConfig};
- use crate::source::{SourceClient, SourceConfig};
- use crate::transformer::{TransformerClient, TransformerConfig};
+ use crate::shared::create_rpc_channel;
+ use crate::sink::SinkWriter;
+ use crate::sink_pb::sink_client::SinkClient;
+ use crate::source::{SourceAcker, SourceReader};
+ use crate::source_pb::source_client::SourceClient;
+ use crate::sourcetransform_pb::source_transform_client::SourceTransformClient;
+ use crate::transformer::SourceTransformer;
struct SimpleSource {
yet_to_be_acked: std::sync::RwLock>,
@@ -570,13 +607,11 @@ mod tests {
.extend(message_offsets)
}
- async fn ack(&self, offsets: Vec) {
- for offset in offsets {
- self.yet_to_be_acked
- .write()
- .unwrap()
- .remove(&String::from_utf8(offset.offset).unwrap());
- }
+ async fn ack(&self, offset: Offset) {
+ self.yet_to_be_acked
+ .write()
+ .unwrap()
+ .remove(&String::from_utf8(offset.offset).unwrap());
}
async fn pending(&self) -> usize {
@@ -619,10 +654,7 @@ mod tests {
#[tonic::async_trait]
impl sink::Sinker for InMemorySink {
- async fn sink(
- &self,
- mut input: tokio::sync::mpsc::Receiver,
- ) -> Vec {
+ async fn sink(&self, mut input: mpsc::Receiver) -> Vec {
let mut responses: Vec = Vec::new();
while let Some(datum) = input.recv().await {
let response = match std::str::from_utf8(&datum.value) {
@@ -654,7 +686,7 @@ mod tests {
#[tokio::test]
async fn test_forwarder_source_sink() {
- let (sink_tx, mut sink_rx) = tokio::sync::mpsc::channel(10);
+ let (sink_tx, mut sink_rx) = mpsc::channel(10);
// Start the source server
let (source_shutdown_tx, source_shutdown_rx) = tokio::sync::oneshot::channel();
@@ -672,11 +704,6 @@ mod tests {
.await
.unwrap();
});
- let source_config = SourceConfig {
- socket_path: source_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Start the sink server
let (sink_shutdown_tx, sink_shutdown_rx) = tokio::sync::oneshot::channel();
@@ -694,11 +721,6 @@ mod tests {
.await
.unwrap();
});
- let sink_config = SinkConfig {
- socket_path: sink_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Start the transformer server
let (transformer_shutdown_tx, transformer_shutdown_rx) = tokio::sync::oneshot::channel();
@@ -716,51 +738,58 @@ mod tests {
.await
.unwrap();
});
- let transformer_config = TransformerConfig {
- socket_path: transformer_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Wait for the servers to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let cln_token = CancellationToken::new();
- let source_client = SourceClient::connect(source_config)
- .await
- .expect("failed to connect to source server");
-
- let sink_client = SinkClient::connect(sink_config)
- .await
- .expect("failed to connect to sink server");
-
- let transformer_client = TransformerClient::connect(transformer_config)
- .await
- .expect("failed to connect to transformer server");
-
- let mut forwarder = ForwarderBuilder::new(source_client, sink_client, cln_token.clone())
- .transformer_client(transformer_client)
- .build();
+ let source_reader = SourceReader::new(SourceClient::new(
+ create_rpc_channel(source_sock_file.clone()).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to source server");
+
+ let source_acker = SourceAcker::new(SourceClient::new(
+ create_rpc_channel(source_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to source server");
+
+ let sink_writer = SinkWriter::new(SinkClient::new(
+ create_rpc_channel(sink_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to sink server");
+
+ let transformer_client = SourceTransformer::new(SourceTransformClient::new(
+ create_rpc_channel(transformer_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to transformer server");
- let forwarder_handle = tokio::spawn(async move {
- forwarder.start().await.unwrap();
+ let mut forwarder =
+ ForwarderBuilder::new(source_reader, source_acker, sink_writer, cln_token.clone())
+ .source_transformer(transformer_client)
+ .build();
+
+ // Assert the received message in a different task
+ let assert_handle = tokio::spawn(async move {
+ let received_message = sink_rx.recv().await.unwrap();
+ assert_eq!(received_message.value, "test-message".as_bytes());
+ assert_eq!(
+ received_message.keys,
+ vec!["test-key-transformed".to_string()]
+ );
+ cln_token.cancel();
});
- // Receive messages from the sink
- let received_message = sink_rx.recv().await.unwrap();
- assert_eq!(received_message.value, "test-message".as_bytes());
- assert_eq!(
- received_message.keys,
- vec!["test-key-transformed".to_string()]
- );
+ forwarder.start().await.unwrap();
- // stop the forwarder
- cln_token.cancel();
- forwarder_handle
- .await
- .expect("failed to join forwarder task");
+ // Wait for the assertion task to complete
+ assert_handle.await.unwrap();
+ drop(forwarder);
// stop the servers
source_shutdown_tx
.send(())
@@ -821,11 +850,6 @@ mod tests {
.await
.unwrap();
});
- let source_config = SourceConfig {
- socket_path: source_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Start the sink server
let (sink_shutdown_tx, sink_shutdown_rx) = tokio::sync::oneshot::channel();
@@ -843,40 +867,45 @@ mod tests {
.await
.unwrap();
});
- let sink_config = SinkConfig {
- socket_path: sink_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Wait for the servers to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let cln_token = CancellationToken::new();
- let source_client = SourceClient::connect(source_config)
- .await
- .expect("failed to connect to source server");
+ let source_reader = SourceReader::new(SourceClient::new(
+ create_rpc_channel(source_sock_file.clone()).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to source server");
- let sink_client = SinkClient::connect(sink_config)
- .await
- .expect("failed to connect to sink server");
+ let source_acker = SourceAcker::new(SourceClient::new(
+ create_rpc_channel(source_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to source server");
+
+ let sink_writer = SinkWriter::new(SinkClient::new(
+ create_rpc_channel(sink_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to sink server");
let mut forwarder =
- ForwarderBuilder::new(source_client, sink_client, cln_token.clone()).build();
+ ForwarderBuilder::new(source_reader, source_acker, sink_writer, cln_token.clone())
+ .build();
- let forwarder_handle = tokio::spawn(async move {
- forwarder.start().await?;
- Result::<()>::Ok(())
+ let cancel_handle = tokio::spawn(async move {
+ tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
+ cln_token.cancel();
});
- // Set a timeout for the forwarder
- let timeout_duration = tokio::time::Duration::from_secs(1);
- // The future should not complete as we should be retrying
- let result = tokio::time::timeout(timeout_duration, forwarder_handle).await;
- assert!(result.is_err());
+ let forwarder_result = forwarder.start().await;
+ assert!(forwarder_result.is_err());
+ cancel_handle.await.unwrap();
// stop the servers
+ drop(forwarder);
source_shutdown_tx
.send(())
.expect("failed to send shutdown signal");
@@ -886,7 +915,7 @@ mod tests {
sink_shutdown_tx
.send(())
- .expect("failed to send shutdown signal");
+ .expect("failed to send sink shutdown signal");
sink_server_handle
.await
.expect("failed to join sink server task");
@@ -897,10 +926,7 @@ mod tests {
#[tonic::async_trait]
impl sink::Sinker for FallbackSender {
- async fn sink(
- &self,
- mut input: tokio::sync::mpsc::Receiver,
- ) -> Vec {
+ async fn sink(&self, mut input: mpsc::Receiver) -> Vec {
let mut responses = vec![];
while let Some(datum) = input.recv().await {
responses.append(&mut vec![sink::Response::fallback(datum.id)]);
@@ -911,7 +937,7 @@ mod tests {
#[tokio::test]
async fn test_fb_sink() {
- let (sink_tx, mut sink_rx) = tokio::sync::mpsc::channel(10);
+ let (sink_tx, mut sink_rx) = mpsc::channel(10);
// Start the source server
let (source_shutdown_tx, source_shutdown_rx) = tokio::sync::oneshot::channel();
@@ -929,11 +955,6 @@ mod tests {
.await
.unwrap();
});
- let source_config = SourceConfig {
- socket_path: source_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Start the primary sink server (which returns status fallback)
let (sink_shutdown_tx, sink_shutdown_rx) = tokio::sync::oneshot::channel();
@@ -951,11 +972,6 @@ mod tests {
.await
.unwrap();
});
- let sink_config = SinkConfig {
- socket_path: sink_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Start the fb sink server
let (fb_sink_shutdown_tx, fb_sink_shutdown_rx) = tokio::sync::oneshot::channel();
@@ -973,48 +989,53 @@ mod tests {
.await
.unwrap();
});
- let fb_sink_config = SinkConfig {
- socket_path: fb_sink_sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- };
// Wait for the servers to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let cln_token = CancellationToken::new();
- let source_client = SourceClient::connect(source_config)
- .await
- .expect("failed to connect to source server");
-
- let sink_client = SinkClient::connect(sink_config)
- .await
- .expect("failed to connect to sink server");
-
- let fb_sink_client = SinkClient::connect(fb_sink_config)
- .await
- .expect("failed to connect to fb sink server");
+ let source_reader = SourceReader::new(SourceClient::new(
+ create_rpc_channel(source_sock_file.clone()).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to source server");
+
+ let source_acker = SourceAcker::new(SourceClient::new(
+ create_rpc_channel(source_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to source server");
+
+ let sink_writer = SinkWriter::new(SinkClient::new(
+ create_rpc_channel(sink_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to sink server");
+
+ let fb_sink_writer = SinkWriter::new(SinkClient::new(
+ create_rpc_channel(fb_sink_sock_file).await.unwrap(),
+ ))
+ .await
+ .expect("failed to connect to fb sink server");
- let mut forwarder = ForwarderBuilder::new(source_client, sink_client, cln_token.clone())
- .fb_sink_client(fb_sink_client)
- .build();
-
- let forwarder_handle = tokio::spawn(async move {
- forwarder.start().await.unwrap();
+ let mut forwarder =
+ ForwarderBuilder::new(source_reader, source_acker, sink_writer, cln_token.clone())
+ .fallback_sink_writer(fb_sink_writer)
+ .build();
+
+ let assert_handle = tokio::spawn(async move {
+ let received_message = sink_rx.recv().await.unwrap();
+ assert_eq!(received_message.value, "test-message".as_bytes());
+ assert_eq!(received_message.keys, vec!["test-key".to_string()]);
+ cln_token.cancel();
});
- // We should receive the message in the fallback sink, since the primary sink returns status fallback
- let received_message = sink_rx.recv().await.unwrap();
- assert_eq!(received_message.value, "test-message".as_bytes());
- assert_eq!(received_message.keys, vec!["test-key".to_string()]);
+ forwarder.start().await.unwrap();
- // stop the forwarder
- cln_token.cancel();
- forwarder_handle
- .await
- .expect("failed to join forwarder task");
+ assert_handle.await.unwrap();
+ drop(forwarder);
// stop the servers
source_shutdown_tx
.send(())
diff --git a/rust/monovertex/src/lib.rs b/rust/monovertex/src/lib.rs
index af69199be2..01b07498a2 100644
--- a/rust/monovertex/src/lib.rs
+++ b/rust/monovertex/src/lib.rs
@@ -1,18 +1,23 @@
-pub(crate) use self::error::Result;
-use crate::config::config;
-pub(crate) use crate::error::Error;
-use crate::forwarder::ForwarderBuilder;
-use crate::metrics::{start_metrics_https_server, LagReaderBuilder, MetricsState};
-use crate::sink::{SinkClient, SinkConfig};
-use crate::source::{SourceClient, SourceConfig};
-use crate::transformer::{TransformerClient, TransformerConfig};
-use std::net::SocketAddr;
-use std::time::Duration;
+extern crate core;
+
use tokio::signal;
use tokio::task::JoinHandle;
-use tokio::time::sleep;
use tokio_util::sync::CancellationToken;
-use tracing::{error, info, warn};
+use tracing::{error, info};
+
+use crate::config::{config, SDKConfig};
+
+use crate::forwarder::ForwarderBuilder;
+use crate::metrics::MetricsState;
+use crate::shared::create_rpc_channel;
+use crate::sink::SinkWriter;
+use crate::sink_pb::sink_client::SinkClient;
+use crate::source::{SourceAcker, SourceReader};
+use crate::source_pb::source_client::SourceClient;
+use crate::sourcetransform_pb::source_transform_client::SourceTransformClient;
+use crate::transformer::SourceTransformer;
+
+pub(crate) use self::error::Result;
/// SourcerSinker orchestrates data movement from the Source to the Sink via the optional SourceTransformer.
/// The forward-a-chunk executes the following in an infinite loop till a shutdown signal is received:
@@ -20,54 +25,33 @@ use tracing::{error, info, warn};
/// - Invokes the SourceTransformer concurrently
/// - Calls the Sinker to write the batch to the Sink
/// - Send Acknowledgement back to the Source
-pub mod error;
-
-pub(crate) mod source;
-
-pub(crate) mod sink;
-
-pub(crate) mod transformer;
-
-pub(crate) mod forwarder;
-
-pub(crate) mod config;
-
-pub(crate) mod message;
-
-pub(crate) mod shared;
-
-mod server_info;
+mod error;
+pub(crate) use crate::error::Error;
+mod config;
+mod forwarder;
+mod message;
mod metrics;
+mod server_info;
+mod shared;
+mod sink;
+mod source;
+mod startup;
+mod transformer;
+
+pub(crate) mod source_pb {
+ tonic::include_proto!("source.v1");
+}
-pub async fn mono_vertex() {
- // Initialize the source, sink and transformer configurations
- // We are using the default configurations for now.
- let source_config = SourceConfig {
- max_message_size: config().grpc_max_message_size,
- ..Default::default()
- };
-
- let sink_config = SinkConfig {
- max_message_size: config().grpc_max_message_size,
- ..Default::default()
- };
-
- let transformer_config = if config().is_transformer_enabled {
- Some(TransformerConfig {
- max_message_size: config().grpc_max_message_size,
- ..Default::default()
- })
- } else {
- None
- };
+pub(crate) mod sink_pb {
+ tonic::include_proto!("sink.v1");
+}
- let fb_sink_config = if config().is_fallback_enabled {
- Some(SinkConfig::fallback_default())
- } else {
- None
- };
+pub(crate) mod sourcetransform_pb {
+ tonic::include_proto!("sourcetransformer.v1");
+}
+pub async fn mono_vertex() -> Result<()> {
let cln_token = CancellationToken::new();
let shutdown_cln_token = cln_token.clone();
@@ -79,15 +63,7 @@ pub async fn mono_vertex() {
});
// Run the forwarder with cancellation token.
- if let Err(e) = init(
- source_config,
- sink_config,
- transformer_config,
- fb_sink_config,
- cln_token,
- )
- .await
- {
+ if let Err(e) = start_forwarder(cln_token, config().sdk_config.clone()).await {
error!("Application error: {:?}", e);
// abort the signal handler task since we have an error and we are shutting down
@@ -97,6 +73,7 @@ pub async fn mono_vertex() {
}
info!("Gracefully Exiting...");
+ Ok(())
}
async fn shutdown_signal() {
@@ -121,105 +98,104 @@ async fn shutdown_signal() {
}
}
-/// forwards a chunk of data from the source to the sink via an optional transformer.
-/// It takes an optional custom_shutdown_rx for shutting down the forwarder, useful for testing.
-pub async fn init(
- source_config: SourceConfig,
- sink_config: SinkConfig,
- transformer_config: Option,
- fb_sink_config: Option,
- cln_token: CancellationToken,
-) -> Result<()> {
- server_info::check_for_server_compatibility(&source_config.server_info_file, cln_token.clone())
- .await
- .map_err(|e| {
- warn!("Error waiting for source server info file: {:?}", e);
- Error::ForwarderError(format!("Error waiting for source server info file: {}", e))
- })?;
- let mut source_client = SourceClient::connect(source_config).await?;
-
- server_info::check_for_server_compatibility(&sink_config.server_info_file, cln_token.clone())
- .await
- .map_err(|e| {
- warn!("Error waiting for sink server info file: {:?}", e);
- Error::ForwarderError(format!("Error waiting for sink server info file: {}", e))
- })?;
-
- let mut sink_client = SinkClient::connect(sink_config).await?;
-
- let mut transformer_client = if let Some(config) = transformer_config {
- server_info::check_for_server_compatibility(&config.server_info_file, cln_token.clone())
- .await
- .map_err(|e| {
- warn!("Error waiting for transformer server info file: {:?}", e);
- Error::ForwarderError(format!("Error waiting for transformer server info file: {}", e))
- })?;
- Some(TransformerClient::connect(config).await?)
+async fn start_forwarder(cln_token: CancellationToken, sdk_config: SDKConfig) -> Result<()> {
+ // make sure that we have compatibility with the server
+ startup::check_compatibility(
+ &cln_token,
+ sdk_config.source_server_info_path.into(),
+ sdk_config.sink_server_info_path.into(),
+ if sdk_config.is_transformer_enabled {
+ Some(sdk_config.transformer_server_info_path.into())
+ } else {
+ None
+ },
+ if sdk_config.is_fallback_enabled {
+ Some(sdk_config.fallback_server_info_path.into())
+ } else {
+ None
+ },
+ )
+ .await?;
+
+ let mut source_grpc_client =
+ SourceClient::new(create_rpc_channel(sdk_config.source_socket_path.into()).await?)
+ .max_encoding_message_size(sdk_config.grpc_max_message_size)
+ .max_encoding_message_size(sdk_config.grpc_max_message_size);
+
+ let mut sink_grpc_client =
+ SinkClient::new(create_rpc_channel(sdk_config.sink_socket_path.into()).await?)
+ .max_encoding_message_size(sdk_config.grpc_max_message_size)
+ .max_encoding_message_size(sdk_config.grpc_max_message_size);
+
+ let mut transformer_grpc_client = if sdk_config.is_transformer_enabled {
+ let transformer_grpc_client = SourceTransformClient::new(
+ create_rpc_channel(sdk_config.transformer_socket_path.into()).await?,
+ )
+ .max_encoding_message_size(sdk_config.grpc_max_message_size)
+ .max_encoding_message_size(sdk_config.grpc_max_message_size);
+
+ Some(transformer_grpc_client.clone())
} else {
None
};
- let mut fb_sink_client = if let Some(config) = fb_sink_config {
- server_info::check_for_server_compatibility(&config.server_info_file, cln_token.clone())
- .await
- .map_err(|e| {
- warn!("Error waiting for fallback sink server info file: {:?}", e);
- Error::ForwarderError(format!("Error waiting for fallback sink server info file: {}", e))
- })?;
- Some(SinkClient::connect(config).await?)
+ let mut fb_sink_grpc_client = if sdk_config.is_fallback_enabled {
+ let fb_sink_grpc_client =
+ SinkClient::new(create_rpc_channel(sdk_config.fallback_socket_path.into()).await?)
+ .max_encoding_message_size(sdk_config.grpc_max_message_size)
+ .max_encoding_message_size(sdk_config.grpc_max_message_size);
+
+ Some(fb_sink_grpc_client.clone())
} else {
None
};
// readiness check for all the ud containers
- wait_until_ready(
- &mut source_client,
- &mut sink_client,
- &mut transformer_client,
- &mut fb_sink_client,
+ startup::wait_until_ready(
+ cln_token.clone(),
+ &mut source_grpc_client,
+ &mut sink_grpc_client,
+ &mut transformer_grpc_client,
+ &mut fb_sink_grpc_client,
)
.await?;
- // Start the metrics server, which server the prometheus metrics.
- let metrics_addr: SocketAddr = format!("0.0.0.0:{}", &config().metrics_server_listen_port)
- .parse()
- .expect("Invalid address");
-
// Start the metrics server in a separate background async spawn,
// This should be running throughout the lifetime of the application, hence the handle is not
// joined.
let metrics_state = MetricsState {
- source_client: source_client.clone(),
- sink_client: sink_client.clone(),
- transformer_client: transformer_client.clone(),
- fb_sink_client: fb_sink_client.clone(),
+ source_client: source_grpc_client.clone(),
+ sink_client: sink_grpc_client.clone(),
+ transformer_client: transformer_grpc_client.clone(),
+ fb_sink_client: fb_sink_grpc_client.clone(),
};
- tokio::spawn(async move {
- if let Err(e) = start_metrics_https_server(metrics_addr, metrics_state).await {
- error!("Metrics server error: {:?}", e);
- }
- });
+
+ // start the metrics server
+ // FIXME: what to do with the handle
+ startup::start_metrics_server(metrics_state).await;
// start the lag reader to publish lag metrics
- let mut lag_reader = LagReaderBuilder::new(source_client.clone())
- .lag_checking_interval(Duration::from_secs(
- config().lag_check_interval_in_secs.into(),
- ))
- .refresh_interval(Duration::from_secs(
- config().lag_refresh_interval_in_secs.into(),
- ))
- .build();
+ let mut lag_reader = startup::create_lag_reader(source_grpc_client.clone()).await;
lag_reader.start().await;
// build the forwarder
- let mut forwarder_builder = ForwarderBuilder::new(source_client, sink_client, cln_token);
+ let source_reader = SourceReader::new(source_grpc_client.clone()).await?;
+ let source_acker = SourceAcker::new(source_grpc_client.clone()).await?;
+ let sink_writer = SinkWriter::new(sink_grpc_client.clone()).await?;
+
+ let mut forwarder_builder =
+ ForwarderBuilder::new(source_reader, source_acker, sink_writer, cln_token);
+
// add transformer if exists
- if let Some(transformer_client) = transformer_client {
- forwarder_builder = forwarder_builder.transformer_client(transformer_client);
+ if let Some(transformer_grpc_client) = transformer_grpc_client {
+ let transformer = SourceTransformer::new(transformer_grpc_client).await?;
+ forwarder_builder = forwarder_builder.source_transformer(transformer);
}
+
// add fallback sink if exists
- if let Some(fb_sink_client) = fb_sink_client {
- forwarder_builder = forwarder_builder.fb_sink_client(fb_sink_client);
+ if let Some(fb_sink_grpc_client) = fb_sink_grpc_client {
+ let fallback_writer = SinkWriter::new(fb_sink_grpc_client).await?;
+ forwarder_builder = forwarder_builder.fallback_sink_writer(fallback_writer);
}
// build the final forwarder
let mut forwarder = forwarder_builder.build();
@@ -231,71 +207,24 @@ pub async fn init(
Ok(())
}
-async fn wait_until_ready(
- source_client: &mut SourceClient,
- sink_client: &mut SinkClient,
- transformer_client: &mut Option,
- fb_sink_client: &mut Option,
-) -> Result<()> {
- loop {
- let source_ready = source_client.is_ready().await;
- if !source_ready {
- info!("UDSource is not ready, waiting...");
- }
-
- let sink_ready = sink_client.is_ready().await;
- if !sink_ready {
- info!("UDSink is not ready, waiting...");
- }
-
- let transformer_ready = if let Some(client) = transformer_client {
- let ready = client.is_ready().await;
- if !ready {
- info!("UDTransformer is not ready, waiting...");
- }
- ready
- } else {
- true
- };
-
- let fb_sink_ready = if let Some(client) = fb_sink_client {
- let ready = client.is_ready().await;
- if !ready {
- info!("Fallback Sink is not ready, waiting...");
- }
- ready
- } else {
- true
- };
-
- if source_ready && sink_ready && transformer_ready && fb_sink_ready {
- break;
- }
-
- sleep(Duration::from_secs(1)).await;
- }
-
- Ok(())
-}
-
#[cfg(test)]
mod tests {
- use std::env;
-
+ use crate::config::SDKConfig;
+ use crate::server_info::ServerInfo;
+ use crate::{error, start_forwarder};
use numaflow::source::{Message, Offset, SourceReadRequest};
use numaflow::{sink, source};
+ use std::fs::File;
+ use std::io::Write;
use tokio::sync::mpsc::Sender;
use tokio_util::sync::CancellationToken;
- use crate::sink::SinkConfig;
- use crate::source::SourceConfig;
-
struct SimpleSource;
#[tonic::async_trait]
impl source::Sourcer for SimpleSource {
async fn read(&self, _: SourceReadRequest, _: Sender) {}
- async fn ack(&self, _: Vec) {}
+ async fn ack(&self, _: Offset) {}
async fn pending(&self) -> usize {
0
@@ -317,12 +246,32 @@ mod tests {
vec![]
}
}
+
+ async fn write_server_info(file_path: &str, server_info: &ServerInfo) -> error::Result<()> {
+ let serialized = serde_json::to_string(server_info).unwrap();
+ let mut file = File::create(file_path).unwrap();
+ file.write_all(serialized.as_bytes()).unwrap();
+ file.write_all(b"U+005C__END__").unwrap();
+ Ok(())
+ }
+
#[tokio::test]
async fn run_forwarder() {
let (src_shutdown_tx, src_shutdown_rx) = tokio::sync::oneshot::channel();
let tmp_dir = tempfile::TempDir::new().unwrap();
let src_sock_file = tmp_dir.path().join("source.sock");
let src_info_file = tmp_dir.path().join("source-server-info");
+ let server_info_obj = ServerInfo {
+ protocol: "uds".to_string(),
+ language: "rust".to_string(),
+ minimum_numaflow_version: "0.1.0".to_string(),
+ version: "0.1.0".to_string(),
+ metadata: None,
+ };
+
+ write_server_info(src_info_file.to_str().unwrap(), &server_info_obj)
+ .await
+ .unwrap();
let server_info = src_info_file.clone();
let server_socket = src_sock_file.clone();
@@ -334,17 +283,16 @@ mod tests {
.await
.unwrap();
});
- let source_config = SourceConfig {
- socket_path: src_sock_file.to_str().unwrap().to_string(),
- server_info_file: src_info_file.to_str().unwrap().to_string(),
- max_message_size: 100,
- };
let (sink_shutdown_tx, sink_shutdown_rx) = tokio::sync::oneshot::channel();
let tmp_dir = tempfile::TempDir::new().unwrap();
let sink_sock_file = tmp_dir.path().join("sink.sock");
let sink_server_info = tmp_dir.path().join("sink-server-info");
+ write_server_info(sink_server_info.to_str().unwrap(), &server_info_obj)
+ .await
+ .unwrap();
+
let server_socket = sink_sock_file.clone();
let server_info = sink_server_info.clone();
let sink_server_handle = tokio::spawn(async move {
@@ -355,37 +303,31 @@ mod tests {
.await
.unwrap();
});
- let sink_config = SinkConfig {
- socket_path: sink_sock_file.to_str().unwrap().to_string(),
- server_info_file: sink_server_info.to_str().unwrap().to_string(),
- max_message_size: 100,
- };
// wait for the servers to start
// FIXME: we need to have a better way, this is flaky
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
- unsafe {
- env::set_var("SOURCE_SOCKET", src_sock_file.to_str().unwrap());
- env::set_var("SINK_SOCKET", sink_sock_file.to_str().unwrap());
- }
-
let cln_token = CancellationToken::new();
- let forwarder_cln_token = cln_token.clone();
- let forwarder_handle = tokio::spawn(async move {
- let result =
- super::init(source_config, sink_config, None, None, forwarder_cln_token).await;
- assert!(result.is_ok());
+ let token_clone = cln_token.clone();
+ tokio::spawn(async move {
+ // FIXME: we need to have a better way, this is flaky
+ tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
+ token_clone.cancel();
});
- // wait for the forwarder to start
- // FIXME: we need to have a better way, this is flaky
- tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
+ let sdk_config = SDKConfig {
+ source_socket_path: src_sock_file.to_str().unwrap().to_string(),
+ sink_socket_path: sink_sock_file.to_str().unwrap().to_string(),
+ source_server_info_path: src_info_file.to_str().unwrap().to_string(),
+ sink_server_info_path: sink_server_info.to_str().unwrap().to_string(),
+ grpc_max_message_size: 1024,
+ ..Default::default()
+ };
- // stop the forwarder
- cln_token.cancel();
- forwarder_handle.await.unwrap();
+ let result = start_forwarder(cln_token.clone(), sdk_config).await;
+ assert!(result.is_ok());
// stop the source and sink servers
src_shutdown_tx.send(()).unwrap();
diff --git a/rust/monovertex/src/message.rs b/rust/monovertex/src/message.rs
index 6df0874948..403c377ec4 100644
--- a/rust/monovertex/src/message.rs
+++ b/rust/monovertex/src/message.rs
@@ -6,9 +6,10 @@ use chrono::{DateTime, Utc};
use crate::error::Error;
use crate::shared::{prost_timestamp_from_utc, utc_from_timestamp};
-use crate::sink::proto;
-use crate::source::proto::read_response;
-use crate::transformer::proto::SourceTransformRequest;
+use crate::sink_pb::SinkRequest;
+use crate::source_pb;
+use crate::source_pb::{AckRequest, read_response};
+use crate::sourcetransform_pb::SourceTransformRequest;
/// A message that is sent from the source to the sink.
#[derive(Debug, Clone)]
@@ -36,6 +37,22 @@ pub(crate) struct Offset {
pub(crate) partition_id: i32,
}
+impl From for AckRequest {
+ fn from(offset: Offset) -> Self {
+ Self {
+ request: Some(source_pb::ack_request::Request {
+ offset: Some(source_pb::Offset {
+ offset: BASE64_STANDARD
+ .decode(offset.offset)
+ .expect("we control the encoding, so this should never fail"),
+ partition_id: offset.partition_id,
+ }),
+ }),
+ handshake: None,
+ }
+ }
+}
+
/// Convert the [`Message`] to [`SourceTransformRequest`]
impl From for SourceTransformRequest {
fn from(message: Message) -> Self {
@@ -74,7 +91,7 @@ impl TryFrom for Message {
}
/// Convert [`Message`] to [`proto::SinkRequest`]
-impl From for proto::SinkRequest {
+impl From for SinkRequest {
fn from(message: Message) -> Self {
Self {
keys: message.keys,
diff --git a/rust/monovertex/src/metrics.rs b/rust/monovertex/src/metrics.rs
index 375e7c071c..fc6ab7a0b0 100644
--- a/rust/monovertex/src/metrics.rs
+++ b/rust/monovertex/src/metrics.rs
@@ -9,12 +9,6 @@ use axum::http::{Response, StatusCode};
use axum::response::IntoResponse;
use axum::{routing::get, Router};
use axum_server::tls_rustls::RustlsConfig;
-use prometheus_client::encoding::text::encode;
-use prometheus_client::metrics::counter::Counter;
-use prometheus_client::metrics::family::Family;
-use prometheus_client::metrics::gauge::Gauge;
-use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
-use prometheus_client::registry::Registry;
use rcgen::{generate_simple_self_signed, CertifiedKey};
use tokio::net::{TcpListener, ToSocketAddrs};
use tokio::sync::Mutex;
@@ -24,9 +18,17 @@ use tracing::{debug, error, info};
use crate::config::config;
use crate::error::Error;
-use crate::sink::SinkClient;
-use crate::source::SourceClient;
-use crate::transformer::TransformerClient;
+use crate::sink_pb::sink_client::SinkClient;
+use crate::source_pb::source_client::SourceClient;
+use crate::sourcetransform_pb::source_transform_client::SourceTransformClient;
+use prometheus_client::encoding::text::encode;
+use prometheus_client::metrics::counter::Counter;
+use prometheus_client::metrics::family::Family;
+use prometheus_client::metrics::gauge::Gauge;
+use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
+use prometheus_client::registry::Registry;
+use tonic::transport::Channel;
+use tonic::Request;
// Define the labels for the metrics
// Note: Please keep consistent with the definitions in MonoVertex daemon
@@ -60,10 +62,10 @@ const SINK_TIME: &str = "monovtx_sink_time";
#[derive(Clone)]
pub(crate) struct MetricsState {
- pub source_client: SourceClient,
- pub sink_client: SinkClient,
- pub transformer_client: Option,
- pub fb_sink_client: Option,
+ pub source_client: SourceClient,
+ pub sink_client: SinkClient,
+ pub transformer_client: Option>,
+ pub fb_sink_client: Option>,
}
/// The global register of all metrics.
@@ -324,22 +326,27 @@ async fn livez() -> impl IntoResponse {
}
async fn sidecar_livez(State(mut state): State) -> impl IntoResponse {
- if !state.source_client.is_ready().await {
+ if state
+ .source_client
+ .is_ready(Request::new(()))
+ .await
+ .is_err()
+ {
error!("Source client is not available");
return StatusCode::SERVICE_UNAVAILABLE;
}
- if !state.sink_client.is_ready().await {
+ if state.sink_client.is_ready(Request::new(())).await.is_err() {
error!("Sink client is not available");
return StatusCode::SERVICE_UNAVAILABLE;
}
if let Some(mut transformer_client) = state.transformer_client {
- if !transformer_client.is_ready().await {
+ if transformer_client.is_ready(Request::new(())).await.is_err() {
error!("Transformer client is not available");
return StatusCode::SERVICE_UNAVAILABLE;
}
}
if let Some(mut fb_sink_client) = state.fb_sink_client {
- if !fb_sink_client.is_ready().await {
+ if fb_sink_client.is_ready(Request::new(())).await.is_err() {
error!("Fallback sink client is not available");
return StatusCode::SERVICE_UNAVAILABLE;
}
@@ -359,7 +366,7 @@ struct TimestampedPending {
/// and exposing the metrics. It maintains a list of pending stats and ensures that
/// only the most recent entries are kept.
pub(crate) struct LagReader {
- source_client: SourceClient,
+ source_client: SourceClient,
lag_checking_interval: Duration,
refresh_interval: Duration,
buildup_handle: Option>,
@@ -369,13 +376,13 @@ pub(crate) struct LagReader {
/// LagReaderBuilder is used to build a `LagReader` instance.
pub(crate) struct LagReaderBuilder {
- source_client: SourceClient,
+ source_client: SourceClient,
lag_checking_interval: Option,
refresh_interval: Option,
}
impl LagReaderBuilder {
- pub(crate) fn new(source_client: SourceClient) -> Self {
+ pub(crate) fn new(source_client: SourceClient) -> Self {
Self {
source_client,
lag_checking_interval: None,
@@ -448,14 +455,14 @@ impl Drop for LagReader {
/// Periodically checks the pending messages from the source client and build the pending stats.
async fn build_pending_info(
- mut source_client: SourceClient,
+ mut source_client: SourceClient,
lag_checking_interval: Duration,
pending_stats: Arc>>,
) {
let mut ticker = time::interval(lag_checking_interval);
loop {
ticker.tick().await;
- match source_client.pending_fn().await {
+ match fetch_pending(&mut source_client).await {
Ok(pending) => {
if pending != -1 {
let mut stats = pending_stats.lock().await;
@@ -477,6 +484,17 @@ async fn build_pending_info(
}
}
+async fn fetch_pending(source_client: &mut SourceClient) -> crate::error::Result {
+ let request = Request::new(());
+ let response = source_client
+ .pending_fn(request)
+ .await?
+ .into_inner()
+ .result
+ .map_or(-1, |r| r.count); // default to -1(unavailable)
+ Ok(response)
+}
+
// Periodically exposes the pending metrics by calculating the average pending messages over different intervals.
async fn expose_pending_metrics(
refresh_interval: Duration,
@@ -539,3 +557,163 @@ async fn calculate_pending(
}
// TODO add tests
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::metrics::MetricsState;
+ use crate::shared::create_rpc_channel;
+ use numaflow::source::{Message, Offset, SourceReadRequest};
+ use numaflow::{sink, source, sourcetransform};
+ use std::net::SocketAddr;
+ use tokio::sync::mpsc::Sender;
+
+ struct SimpleSource;
+ #[tonic::async_trait]
+ impl source::Sourcer for SimpleSource {
+ async fn read(&self, _: SourceReadRequest, _: Sender) {}
+
+ async fn ack(&self, _: Offset) {}
+
+ async fn pending(&self) -> usize {
+ 0
+ }
+
+ async fn partitions(&self) -> Option> {
+ None
+ }
+ }
+
+ struct SimpleSink;
+
+ #[tonic::async_trait]
+ impl sink::Sinker for SimpleSink {
+ async fn sink(
+ &self,
+ _input: tokio::sync::mpsc::Receiver,
+ ) -> Vec {
+ vec![]
+ }
+ }
+
+ struct NowCat;
+
+ #[tonic::async_trait]
+ impl sourcetransform::SourceTransformer for NowCat {
+ async fn transform(
+ &self,
+ _input: sourcetransform::SourceTransformRequest,
+ ) -> Vec {
+ vec![]
+ }
+ }
+
+ #[tokio::test]
+ async fn test_start_metrics_https_server() {
+ let (src_shutdown_tx, src_shutdown_rx) = tokio::sync::oneshot::channel();
+ let tmp_dir = tempfile::TempDir::new().unwrap();
+ let src_sock_file = tmp_dir.path().join("source.sock");
+ let src_info_file = tmp_dir.path().join("source-server-info");
+
+ let server_info = src_info_file.clone();
+ let server_socket = src_sock_file.clone();
+ let src_server_handle = tokio::spawn(async move {
+ source::Server::new(SimpleSource)
+ .with_socket_file(server_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(src_shutdown_rx)
+ .await
+ .unwrap();
+ });
+
+ let (sink_shutdown_tx, sink_shutdown_rx) = tokio::sync::oneshot::channel();
+ let (fb_sink_shutdown_tx, fb_sink_shutdown_rx) = tokio::sync::oneshot::channel();
+ let tmp_dir = tempfile::TempDir::new().unwrap();
+ let sink_sock_file = tmp_dir.path().join("sink.sock");
+ let sink_server_info = tmp_dir.path().join("sink-server-info");
+ let fb_sink_sock_file = tmp_dir.path().join("fallback-sink.sock");
+ let fb_sink_server_info = tmp_dir.path().join("fallback-sink-server-info");
+
+ let server_socket = sink_sock_file.clone();
+ let server_info = sink_server_info.clone();
+ let sink_server_handle = tokio::spawn(async move {
+ sink::Server::new(SimpleSink)
+ .with_socket_file(server_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(sink_shutdown_rx)
+ .await
+ .unwrap();
+ });
+ let fb_server_socket = fb_sink_sock_file.clone();
+ let fb_server_info = fb_sink_server_info.clone();
+ let fb_sink_server_handle = tokio::spawn(async move {
+ sink::Server::new(SimpleSink)
+ .with_socket_file(fb_server_socket)
+ .with_server_info_file(fb_server_info)
+ .start_with_shutdown(fb_sink_shutdown_rx)
+ .await
+ .unwrap();
+ });
+
+ // start the transformer server
+ let (transformer_shutdown_tx, transformer_shutdown_rx) = tokio::sync::oneshot::channel();
+ let sock_file = tmp_dir.path().join("sourcetransform.sock");
+ let server_info_file = tmp_dir.path().join("sourcetransformer-server-info");
+
+ let server_info = server_info_file.clone();
+ let server_socket = sock_file.clone();
+ let transformer_handle = tokio::spawn(async move {
+ sourcetransform::Server::new(NowCat)
+ .with_socket_file(server_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(transformer_shutdown_rx)
+ .await
+ .expect("server failed");
+ });
+
+ // wait for the servers to start
+ // FIXME: we need to have a better way, this is flaky
+ tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
+ let metrics_state = MetricsState {
+ source_client: SourceClient::new(create_rpc_channel(src_sock_file).await.unwrap()),
+ sink_client: SinkClient::new(create_rpc_channel(sink_sock_file).await.unwrap()),
+ transformer_client: Some(SourceTransformClient::new(
+ create_rpc_channel(sock_file).await.unwrap(),
+ )),
+ fb_sink_client: Some(SinkClient::new(
+ create_rpc_channel(fb_sink_sock_file).await.unwrap(),
+ )),
+ };
+
+ let addr: SocketAddr = "127.0.0.1:9091".parse().unwrap();
+ let metrics_state_clone = metrics_state.clone();
+ let server_handle = tokio::spawn(async move {
+ start_metrics_https_server(addr, metrics_state_clone)
+ .await
+ .unwrap();
+ });
+
+ // invoke the sidecar-livez endpoint
+ let response = sidecar_livez(State(metrics_state)).await;
+ assert_eq!(response.into_response().status(), StatusCode::NO_CONTENT);
+
+ // invoke the livez endpoint
+ let response = livez().await;
+ assert_eq!(response.into_response().status(), StatusCode::NO_CONTENT);
+
+ // invoke the metrics endpoint
+ let response = metrics_handler().await;
+ assert_eq!(response.into_response().status(), StatusCode::OK);
+
+ // Stop the servers
+ server_handle.abort();
+ src_shutdown_tx.send(()).unwrap();
+ sink_shutdown_tx.send(()).unwrap();
+ fb_sink_shutdown_tx.send(()).unwrap();
+ transformer_shutdown_tx.send(()).unwrap();
+ src_server_handle.await.unwrap();
+ sink_server_handle.await.unwrap();
+ fb_sink_server_handle.await.unwrap();
+ transformer_handle.await.unwrap();
+ }
+}
diff --git a/rust/monovertex/src/server_info.rs b/rust/monovertex/src/server_info.rs
index 98484869fd..35495097b8 100644
--- a/rust/monovertex/src/server_info.rs
+++ b/rust/monovertex/src/server_info.rs
@@ -1,5 +1,6 @@
use std::collections::HashMap;
use std::fs;
+use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
@@ -21,21 +22,21 @@ const END: &str = "U+005C__END__";
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct ServerInfo {
#[serde(default)]
- protocol: String,
+ pub(crate) protocol: String,
#[serde(default)]
- language: String,
+ pub(crate) language: String,
#[serde(default)]
- minimum_numaflow_version: String,
+ pub(crate) minimum_numaflow_version: String,
#[serde(default)]
- version: String,
+ pub(crate) version: String,
#[serde(default)]
- metadata: Option>, // Metadata is optional
+ pub(crate) metadata: Option>, // Metadata is optional
}
/// check_for_server_compatibility waits until the server info file is ready and check whether the
/// server is compatible with Numaflow.
-pub async fn check_for_server_compatibility(
- file_path: &str,
+pub(crate) async fn check_for_server_compatibility(
+ file_path: PathBuf,
cln_token: CancellationToken,
) -> error::Result<()> {
// Read the server info file
@@ -186,9 +187,13 @@ fn human_readable(ver: &str) -> String {
fn check_constraint(version: &Version, constraint: &str) -> error::Result<()> {
let binding = version.to_string();
// extract the major.minor.patch version
- let mmp_version = Version::parse(binding.split('-').next().unwrap_or_default()).map_err(|e| {
- Error::ServerInfoError(format!("Error parsing version: {}, version string: {}", e, binding))
- })?;
+ let mmp_version =
+ Version::parse(binding.split('-').next().unwrap_or_default()).map_err(|e| {
+ Error::ServerInfoError(format!(
+ "Error parsing version: {}, version string: {}",
+ e, binding
+ ))
+ })?;
let mmp_ver_str_constraint = trim_after_dash(constraint.trim_start_matches(">="));
let mmp_ver_constraint = format!(">={}", mmp_ver_str_constraint);
@@ -245,7 +250,7 @@ fn trim_after_dash(input: &str) -> &str {
/// The cancellation token is used to stop ready-check of server_info file in case it is missing.
/// This cancellation token is closed via the global shutdown handler.
async fn read_server_info(
- file_path: &str,
+ file_path: PathBuf,
cln_token: CancellationToken,
) -> error::Result {
// Infinite loop to keep checking until the file is ready
@@ -255,14 +260,14 @@ async fn read_server_info(
}
// Check if the file exists and has content
- if let Ok(metadata) = fs::metadata(file_path) {
+ if let Ok(metadata) = fs::metadata(file_path.as_path()) {
if metadata.len() > 0 {
// Break out of the loop if the file is ready (has content)
break;
}
}
// Log message indicating the file is not ready and sleep for 1 second before checking again
- info!("Server info file {} is not ready, waiting...", file_path);
+ info!("Server info file {:?} is not ready, waiting...", file_path);
sleep(Duration::from_secs(1)).await;
}
@@ -271,7 +276,7 @@ async fn read_server_info(
let contents;
loop {
// Attempt to read the file
- match fs::read_to_string(file_path) {
+ match fs::read_to_string(file_path.as_path()) {
Ok(data) => {
if data.ends_with(END) {
// If the file ends with the END marker, trim it and break out of the loop
@@ -944,7 +949,7 @@ mod tests {
let _ = write_server_info(&server_info, file_path.to_str().unwrap()).await;
// Call the read_server_info function
- let result = read_server_info(file_path.to_str().unwrap(), cln_token).await;
+ let result = read_server_info(file_path, cln_token).await;
assert!(result.is_ok(), "Expected Ok, got {:?}", result);
let server_info = result.unwrap();
@@ -973,7 +978,7 @@ mod tests {
let _drop_guard = cln_token.clone().drop_guard();
// Call the read_server_info function
- let result = read_server_info(file_path.to_str().unwrap(), cln_token).await;
+ let result = read_server_info(file_path, cln_token).await;
assert!(result.is_err(), "Expected Err, got {:?}", result);
let error = result.unwrap_err();
diff --git a/rust/monovertex/src/shared.rs b/rust/monovertex/src/shared.rs
index 2c63244647..2ce22ba803 100644
--- a/rust/monovertex/src/shared.rs
+++ b/rust/monovertex/src/shared.rs
@@ -1,13 +1,14 @@
use std::path::PathBuf;
+use crate::error::Error;
+use backoff::retry::Retry;
+use backoff::strategy::fixed;
use chrono::{DateTime, TimeZone, Timelike, Utc};
use prost_types::Timestamp;
use tokio::net::UnixStream;
use tonic::transport::{Channel, Endpoint, Uri};
use tower::service_fn;
-use crate::error::Error;
-
pub(crate) fn utc_from_timestamp(t: Option) -> DateTime {
t.map_or(Utc.timestamp_nanos(-1), |t| {
DateTime::from_timestamp(t.seconds, t.nanos as u32).unwrap_or(Utc.timestamp_nanos(-1))
@@ -21,6 +22,21 @@ pub(crate) fn prost_timestamp_from_utc(t: DateTime) -> Option {
})
}
+pub(crate) async fn create_rpc_channel(socket_path: PathBuf) -> crate::error::Result {
+ const RECONNECT_INTERVAL: u64 = 1000;
+ const MAX_RECONNECT_ATTEMPTS: usize = 5;
+
+ let interval = fixed::Interval::from_millis(RECONNECT_INTERVAL).take(MAX_RECONNECT_ATTEMPTS);
+
+ let channel = Retry::retry(
+ interval,
+ || async { connect_with_uds(socket_path.clone()).await },
+ |_: &Error| true,
+ )
+ .await?;
+ Ok(channel)
+}
+
pub(crate) async fn connect_with_uds(uds_path: PathBuf) -> Result {
let channel = Endpoint::try_from("http://[::]:50051")
.map_err(|e| Error::ConnectionError(format!("Failed to create endpoint: {:?}", e)))?
diff --git a/rust/monovertex/src/sink.rs b/rust/monovertex/src/sink.rs
index fb82273fb6..a2088a8c2f 100644
--- a/rust/monovertex/src/sink.rs
+++ b/rust/monovertex/src/sink.rs
@@ -1,78 +1,21 @@
-use crate::config::config;
-use crate::error::{Error, Result};
+use crate::error::Result;
use crate::message::Message;
-use crate::shared::connect_with_uds;
-use backoff::retry::Retry;
-use backoff::strategy::fixed;
+use crate::sink_pb::sink_client::SinkClient;
+use crate::sink_pb::{SinkRequest, SinkResponse};
use tonic::transport::Channel;
-use tonic::Request;
-
-pub mod proto {
- tonic::include_proto!("sink.v1");
-}
-
-const RECONNECT_INTERVAL: u64 = 1000;
-const MAX_RECONNECT_ATTEMPTS: usize = 5;
-const SINK_SOCKET: &str = "/var/run/numaflow/sink.sock";
-const FB_SINK_SOCKET: &str = "/var/run/numaflow/fb-sink.sock";
-
-const SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/sinker-server-info";
-const FB_SINK_SERVER_INFO_FILE: &str = "/var/run/numaflow/fb-sinker-server-info";
-
-/// SinkConfig is the configuration for the sink server.
-#[derive(Debug, Clone)]
-pub struct SinkConfig {
- pub socket_path: String,
- pub server_info_file: String,
- pub max_message_size: usize,
-}
-
-impl Default for SinkConfig {
- fn default() -> Self {
- SinkConfig {
- socket_path: SINK_SOCKET.to_string(),
- server_info_file: SINK_SERVER_INFO_FILE.to_string(),
- max_message_size: config().grpc_max_message_size,
- }
- }
-}
-
-impl SinkConfig {
- /// default config for fallback sink
- pub(crate) fn fallback_default() -> Self {
- SinkConfig {
- max_message_size: config().grpc_max_message_size,
- socket_path: FB_SINK_SOCKET.to_string(),
- server_info_file: FB_SINK_SERVER_INFO_FILE.to_string(),
- }
- }
-}
+/// SinkWriter writes messages to a sink.
#[derive(Clone)]
-/// SinkClient is a client to interact with the sink server.
-pub struct SinkClient {
- client: proto::sink_client::SinkClient,
+pub struct SinkWriter {
+ client: SinkClient,
}
-impl SinkClient {
- pub(crate) async fn connect(config: SinkConfig) -> Result {
- let interval =
- fixed::Interval::from_millis(RECONNECT_INTERVAL).take(MAX_RECONNECT_ATTEMPTS);
-
- let channel = Retry::retry(
- interval,
- || async { connect_with_uds(config.socket_path.clone().into()).await },
- |_: &Error| true,
- )
- .await?;
-
- let client = proto::sink_client::SinkClient::new(channel)
- .max_decoding_message_size(config.max_message_size)
- .max_encoding_message_size(config.max_message_size);
+impl SinkWriter {
+ pub(crate) async fn new(client: SinkClient) -> Result {
Ok(Self { client })
}
- pub(crate) async fn sink_fn(&mut self, messages: Vec) -> Result {
+ pub(crate) async fn sink_fn(&mut self, messages: Vec) -> Result {
// create a channel with at least size
let (tx, rx) = tokio::sync::mpsc::channel(if messages.is_empty() {
1
@@ -80,7 +23,7 @@ impl SinkClient {
messages.len()
});
- let requests: Vec =
+ let requests: Vec =
messages.into_iter().map(|message| message.into()).collect();
tokio::spawn(async move {
@@ -99,10 +42,6 @@ impl SinkClient {
Ok(response)
}
-
- pub(crate) async fn is_ready(&mut self) -> bool {
- self.client.is_ready(Request::new(())).await.is_ok()
- }
}
#[cfg(test)]
@@ -111,9 +50,9 @@ mod tests {
use numaflow::sink;
use tracing::info;
- use crate::message::Offset;
-
use super::*;
+ use crate::message::Offset;
+ use crate::shared::create_rpc_channel;
struct Logger;
#[tonic::async_trait]
@@ -139,7 +78,7 @@ mod tests {
}
}
#[tokio::test]
- async fn sink_operations() {
+ async fn sink_operations() -> Result<()> {
// start the server
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel();
let tmp_dir = tempfile::TempDir::new().unwrap();
@@ -160,13 +99,10 @@ mod tests {
// wait for the server to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
- let mut sink_client = SinkClient::connect(SinkConfig {
- socket_path: sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- })
- .await
- .expect("failed to connect to sink server");
+ let mut sink_client =
+ SinkWriter::new(SinkClient::new(create_rpc_channel(sock_file).await?))
+ .await
+ .expect("failed to connect to sink server");
let messages = vec![
Message {
@@ -193,15 +129,13 @@ mod tests {
},
];
- let ready_response = sink_client.is_ready().await;
- assert!(ready_response);
-
- let response = sink_client.sink_fn(messages).await.unwrap();
+ let response = sink_client.sink_fn(messages).await?;
assert_eq!(response.results.len(), 2);
shutdown_tx
.send(())
.expect("failed to send shutdown signal");
server_handle.await.expect("failed to join server task");
+ Ok(())
}
}
diff --git a/rust/monovertex/src/source.rs b/rust/monovertex/src/source.rs
index 681b0beb58..fdfde1b6d4 100644
--- a/rust/monovertex/src/source.rs
+++ b/rust/monovertex/src/source.rs
@@ -1,146 +1,176 @@
-use crate::error::{Error, Result};
+use crate::config::config;
+use crate::error::Error::SourceError;
+use crate::error::Result;
use crate::message::{Message, Offset};
-use crate::shared::connect_with_uds;
-use backoff::retry::Retry;
-use backoff::strategy::fixed;
-use base64::prelude::BASE64_STANDARD;
-use base64::Engine;
-use tokio_stream::StreamExt;
+use crate::source_pb;
+use crate::source_pb::source_client::SourceClient;
+use crate::source_pb::{
+ ack_response, read_request, AckRequest, AckResponse, ReadRequest, ReadResponse,
+};
+use log::info;
+use tokio::sync::mpsc;
+use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::Channel;
-use tonic::Request;
+use tonic::{Request, Streaming};
-pub mod proto {
- tonic::include_proto!("source.v1");
-}
-const RECONNECT_INTERVAL: u64 = 1000;
-const MAX_RECONNECT_ATTEMPTS: usize = 5;
-const SOURCE_SOCKET: &str = "/var/run/numaflow/source.sock";
-const SOURCE_SERVER_INFO_FILE: &str = "/var/run/numaflow/sourcer-server-info";
-
-/// SourceConfig is the configuration for the source server.
-#[derive(Debug, Clone)]
-pub struct SourceConfig {
- pub socket_path: String,
- pub server_info_file: String,
- pub max_message_size: usize,
-}
-
-impl Default for SourceConfig {
- fn default() -> Self {
- SourceConfig {
- socket_path: SOURCE_SOCKET.to_string(),
- server_info_file: SOURCE_SERVER_INFO_FILE.to_string(),
- max_message_size: 64 * 1024 * 1024, // 64 MB
- }
- }
+/// SourceReader reads messages from a source.
+#[derive(Debug)]
+pub(crate) struct SourceReader {
+ read_tx: mpsc::Sender,
+ resp_stream: Streaming,
}
-/// SourceClient is a client to interact with the source server.
-#[derive(Debug, Clone)]
-pub(crate) struct SourceClient {
- client: proto::source_client::SourceClient,
-}
-
-impl SourceClient {
- pub(crate) async fn connect(config: SourceConfig) -> Result {
- let interval =
- fixed::Interval::from_millis(RECONNECT_INTERVAL).take(MAX_RECONNECT_ATTEMPTS);
-
- let channel = Retry::retry(
- interval,
- || async { connect_with_uds(config.socket_path.clone().into()).await },
- |_: &Error| true,
- )
- .await?;
+impl SourceReader {
+ pub(crate) async fn new(mut client: SourceClient) -> Result {
+ let (read_tx, read_rx) = mpsc::channel(config().batch_size as usize);
+ let read_stream = ReceiverStream::new(read_rx);
+
+ // do a handshake for read with the server before we start sending read requests
+ let handshake_request = ReadRequest {
+ request: None,
+ handshake: Some(source_pb::Handshake { sot: true }),
+ };
+ read_tx
+ .send(handshake_request)
+ .await
+ .map_err(|e| SourceError(format!("failed to send handshake request: {}", e)))?;
- let client = proto::source_client::SourceClient::new(channel)
- .max_encoding_message_size(config.max_message_size)
- .max_decoding_message_size(config.max_message_size);
+ let mut resp_stream = client
+ .read_fn(Request::new(read_stream))
+ .await?
+ .into_inner();
+
+ // first response from the server will be the handshake response. We need to check if the
+ // server has accepted the handshake.
+ let handshake_response = resp_stream.message().await?.ok_or(SourceError(
+ "failed to receive handshake response".to_string(),
+ ))?;
+ // handshake cannot to None during the initial phase and it has to set `sot` to true.
+ if handshake_response.handshake.map_or(true, |h| !h.sot) {
+ return Err(SourceError("invalid handshake response".to_string()));
+ }
- Ok(Self { client })
+ Ok(Self {
+ read_tx,
+ resp_stream,
+ })
}
- pub(crate) async fn read_fn(
+ pub(crate) async fn read(
&mut self,
num_records: u64,
timeout_in_ms: u32,
) -> Result> {
- let request = Request::new(proto::ReadRequest {
- request: Some(proto::read_request::Request {
+ let request = ReadRequest {
+ request: Some(read_request::Request {
num_records,
timeout_in_ms,
}),
- });
+ handshake: None,
+ };
+
+ self.read_tx
+ .send(request)
+ .await
+ .map_err(|e| SourceError(e.to_string()))?;
- let mut stream = self.client.read_fn(request).await?.into_inner();
let mut messages = Vec::with_capacity(num_records as usize);
- while let Some(response) = stream.next().await {
- let result = response?
+ while let Some(response) = self.resp_stream.message().await? {
+ if response.status.map_or(false, |status| status.eot) {
+ break;
+ }
+
+ let result = response
.result
- .ok_or_else(|| Error::SourceError("Empty message".to_string()))?;
+ .ok_or_else(|| SourceError("Empty message".to_string()))?;
messages.push(result.try_into()?);
}
-
Ok(messages)
}
+}
- pub(crate) async fn ack_fn(&mut self, offsets: Vec) -> Result {
- let offsets = offsets
- .into_iter()
- .map(|offset| proto::Offset {
- offset: BASE64_STANDARD
- .decode(offset.offset)
- .expect("we control the encoding, so this should never fail"),
- partition_id: offset.partition_id,
- })
- .collect();
-
- let request = Request::new(proto::AckRequest {
- request: Some(proto::ack_request::Request { offsets }),
- });
+/// SourceAcker acks the messages from a source.
+#[derive(Debug)]
+pub(crate) struct SourceAcker {
+ ack_tx: mpsc::Sender,
+ ack_resp_stream: Streaming,
+}
- Ok(self.client.ack_fn(request).await?.into_inner())
- }
+impl SourceAcker {
+ pub(crate) async fn new(mut client: SourceClient) -> Result {
+ let (ack_tx, ack_rx) = mpsc::channel(config().batch_size as usize);
+ let ack_stream = ReceiverStream::new(ack_rx);
+
+ // do a handshake for ack with the server before we start sending ack requests
+ let ack_handshake_request = AckRequest {
+ request: None,
+ handshake: Some(source_pb::Handshake { sot: true }),
+ };
+ ack_tx
+ .send(ack_handshake_request)
+ .await
+ .map_err(|e| SourceError(format!("failed to send ack handshake request: {}", e)))?;
+
+ let mut ack_resp_stream = client.ack_fn(Request::new(ack_stream)).await?.into_inner();
+
+ // first response from the server will be the handshake response. We need to check if the
+ // server has accepted the handshake.
+ let ack_handshake_response = ack_resp_stream.message().await?.ok_or(SourceError(
+ "failed to receive ack handshake response".to_string(),
+ ))?;
+ // handshake cannot to None during the initial phase and it has to set `sot` to true.
+ if ack_handshake_response.handshake.map_or(true, |h| !h.sot) {
+ return Err(SourceError("invalid ack handshake response".to_string()));
+ }
- pub(crate) async fn pending_fn(&mut self) -> Result {
- let request = Request::new(());
- let response = self
- .client
- .pending_fn(request)
- .await?
- .into_inner()
- .result
- .map_or(-1, |r| r.count); // default to -1(unavailable)
- Ok(response)
+ Ok(Self {
+ ack_tx,
+ ack_resp_stream,
+ })
}
- #[allow(dead_code)]
- // TODO: remove dead_code
- pub(crate) async fn partitions_fn(&mut self) -> Result> {
- let request = Request::new(());
- let response = self.client.partitions_fn(request).await?.into_inner();
- Ok(response.result.map_or(vec![], |r| r.partitions))
- }
+ pub(crate) async fn ack(&mut self, offsets: Vec) -> Result {
+ let n = offsets.len();
+
+ // send n ack requests
+ for offset in offsets {
+ let request = offset.into();
+ self.ack_tx
+ .send(request)
+ .await
+ .map_err(|e| SourceError(e.to_string()))?;
+ }
- pub(crate) async fn is_ready(&mut self) -> bool {
- self.client.is_ready(Request::new(())).await.is_ok()
+ // make sure we get n responses for the n requests.
+ for _ in 0..n {
+ let _ = self
+ .ack_resp_stream
+ .message()
+ .await?
+ .ok_or(SourceError("failed to receive ack response".to_string()))?;
+ }
+
+ Ok(AckResponse {
+ result: Some(ack_response::Result { success: Some(()) }),
+ handshake: None,
+ })
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
- use std::error::Error;
+ use crate::shared::create_rpc_channel;
+ use crate::source::{SourceAcker, SourceReader};
+ use crate::source_pb::source_client::SourceClient;
use chrono::Utc;
use numaflow::source;
use numaflow::source::{Message, Offset, SourceReadRequest};
use tokio::sync::mpsc::Sender;
- use crate::source::{SourceClient, SourceConfig};
-
struct SimpleSource {
num: usize,
yet_to_ack: std::sync::RwLock>,
@@ -180,13 +210,11 @@ mod tests {
self.yet_to_ack.write().unwrap().extend(message_offsets)
}
- async fn ack(&self, offsets: Vec) {
- for offset in offsets {
- self.yet_to_ack
- .write()
- .unwrap()
- .remove(&String::from_utf8(offset.offset).unwrap());
- }
+ async fn ack(&self, offset: Offset) {
+ self.yet_to_ack
+ .write()
+ .unwrap()
+ .remove(&String::from_utf8(offset.offset).unwrap());
}
async fn pending(&self) -> usize {
@@ -199,7 +227,7 @@ mod tests {
}
#[tokio::test]
- async fn source_operations() -> Result<(), Box> {
+ async fn source_operations() {
// start the server
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel();
let tmp_dir = tempfile::TempDir::new().unwrap();
@@ -214,43 +242,43 @@ mod tests {
.with_server_info_file(server_info)
.start_with_shutdown(shutdown_rx)
.await
- .unwrap();
+ .unwrap()
});
// wait for the server to start
// TODO: flaky
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
- let mut source_client = SourceClient::connect(SourceConfig {
- socket_path: sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- })
+ let mut source_reader = SourceReader::new(SourceClient::new(
+ create_rpc_channel(sock_file.clone()).await.unwrap(),
+ ))
.await
- .expect("failed to connect to source server");
+ .map_err(|e| panic!("failed to create source reader: {:?}", e))
+ .unwrap();
- let response = source_client.is_ready().await;
- assert!(response);
+ let mut source_acker = SourceAcker::new(SourceClient::new(
+ create_rpc_channel(sock_file).await.unwrap(),
+ ))
+ .await
+ .map_err(|e| panic!("failed to create source acker: {:?}", e))
+ .unwrap();
- let messages = source_client.read_fn(5, 1000).await.unwrap();
+ let messages = source_reader.read(5, 1000).await.unwrap();
assert_eq!(messages.len(), 5);
- let response = source_client
- .ack_fn(messages.iter().map(|m| m.offset.clone()).collect())
+ let response = source_acker
+ .ack(messages.iter().map(|m| m.offset.clone()).collect())
.await
.unwrap();
assert!(response.result.unwrap().success.is_some());
- let pending = source_client.pending_fn().await.unwrap();
- assert_eq!(pending, 0);
-
- let partitions = source_client.partitions_fn().await.unwrap();
- assert_eq!(partitions, vec![2]);
-
+ // we need to drop the client, because if there are any in-flight requests
+ // server fails to shut down. https://github.com/numaproj/numaflow-rs/issues/85
+ drop(source_reader);
+ drop(source_acker);
shutdown_tx
.send(())
.expect("failed to send shutdown signal");
server_handle.await.expect("failed to join server task");
- Ok(())
}
}
diff --git a/rust/monovertex/src/startup.rs b/rust/monovertex/src/startup.rs
new file mode 100644
index 0000000000..2614d045b7
--- /dev/null
+++ b/rust/monovertex/src/startup.rs
@@ -0,0 +1,349 @@
+use std::net::SocketAddr;
+use std::path::PathBuf;
+use std::time::Duration;
+
+use crate::config::config;
+use crate::error::Error;
+use crate::metrics::{start_metrics_https_server, LagReader, LagReaderBuilder, MetricsState};
+use crate::sink_pb::sink_client::SinkClient;
+use crate::source_pb::source_client::SourceClient;
+use crate::sourcetransform_pb::source_transform_client::SourceTransformClient;
+use crate::{error, server_info};
+
+use tokio::task::JoinHandle;
+use tokio::time::sleep;
+use tokio_util::sync::CancellationToken;
+use tonic::transport::Channel;
+use tonic::Request;
+use tracing::{info, warn};
+
+pub(crate) async fn check_compatibility(
+ cln_token: &CancellationToken,
+ source_file_path: PathBuf,
+ sink_file_path: PathBuf,
+ transformer_file_path: Option,
+ fb_sink_file_path: Option,
+) -> error::Result<()> {
+ server_info::check_for_server_compatibility(source_file_path, cln_token.clone())
+ .await
+ .map_err(|e| {
+ warn!("Error waiting for source server info file: {:?}", e);
+ Error::ForwarderError("Error waiting for server info file".to_string())
+ })?;
+
+ server_info::check_for_server_compatibility(sink_file_path, cln_token.clone())
+ .await
+ .map_err(|e| {
+ error!("Error waiting for sink server info file: {:?}", e);
+ Error::ForwarderError("Error waiting for server info file".to_string())
+ })?;
+
+ if let Some(transformer_path) = transformer_file_path {
+ server_info::check_for_server_compatibility(transformer_path, cln_token.clone())
+ .await
+ .map_err(|e| {
+ error!("Error waiting for transformer server info file: {:?}", e);
+ Error::ForwarderError("Error waiting for server info file".to_string())
+ })?;
+ }
+
+ if let Some(fb_sink_path) = fb_sink_file_path {
+ server_info::check_for_server_compatibility(fb_sink_path, cln_token.clone())
+ .await
+ .map_err(|e| {
+ warn!("Error waiting for fallback sink server info file: {:?}", e);
+ Error::ForwarderError("Error waiting for server info file".to_string())
+ })?;
+ }
+ Ok(())
+}
+
+pub(crate) async fn start_metrics_server(metrics_state: MetricsState) -> JoinHandle<()> {
+ tokio::spawn(async {
+ // Start the metrics server, which server the prometheus metrics.
+ let metrics_addr: SocketAddr = format!("0.0.0.0:{}", &config().metrics_server_listen_port)
+ .parse()
+ .expect("Invalid address");
+
+ if let Err(e) = start_metrics_https_server(metrics_addr, metrics_state).await {
+ error!("Metrics server error: {:?}", e);
+ }
+ })
+}
+
+pub(crate) async fn create_lag_reader(lag_reader_grpc_client: SourceClient) -> LagReader {
+ LagReaderBuilder::new(lag_reader_grpc_client)
+ .lag_checking_interval(Duration::from_secs(
+ config().lag_check_interval_in_secs.into(),
+ ))
+ .refresh_interval(Duration::from_secs(
+ config().lag_refresh_interval_in_secs.into(),
+ ))
+ .build()
+}
+
+pub(crate) async fn wait_until_ready(
+ cln_token: CancellationToken,
+ source_client: &mut SourceClient,
+ sink_client: &mut SinkClient,
+ transformer_client: &mut Option>,
+ fb_sink_client: &mut Option>,
+) -> error::Result<()> {
+ loop {
+ if cln_token.is_cancelled() {
+ return Err(Error::ForwarderError(
+ "Cancellation token is cancelled".to_string(),
+ ));
+ }
+ let source_ready = source_client.is_ready(Request::new(())).await.is_ok();
+ if !source_ready {
+ info!("UDSource is not ready, waiting...");
+ }
+
+ let sink_ready = sink_client.is_ready(Request::new(())).await.is_ok();
+ if !sink_ready {
+ info!("UDSink is not ready, waiting...");
+ }
+
+ let transformer_ready = if let Some(client) = transformer_client {
+ let ready = client.is_ready(Request::new(())).await.is_ok();
+ if !ready {
+ info!("UDTransformer is not ready, waiting...");
+ }
+ ready
+ } else {
+ true
+ };
+
+ let fb_sink_ready = if let Some(client) = fb_sink_client {
+ let ready = client.is_ready(Request::new(())).await.is_ok();
+ if !ready {
+ info!("Fallback Sink is not ready, waiting...");
+ }
+ ready
+ } else {
+ true
+ };
+
+ if source_ready && sink_ready && transformer_ready && fb_sink_ready {
+ break;
+ }
+
+ sleep(Duration::from_secs(1)).await;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::server_info::ServerInfo;
+ use crate::shared::create_rpc_channel;
+ use numaflow::source::{Message, Offset, SourceReadRequest};
+ use numaflow::{sink, source, sourcetransform};
+ use std::fs::File;
+ use std::io::Write;
+ use tempfile::tempdir;
+ use tokio::sync::mpsc;
+ use tokio::sync::mpsc::Sender;
+ use tokio_util::sync::CancellationToken;
+
+ async fn write_server_info(file_path: &str, server_info: &ServerInfo) -> error::Result<()> {
+ let serialized = serde_json::to_string(server_info).unwrap();
+ let mut file = File::create(file_path).unwrap();
+ file.write_all(serialized.as_bytes()).unwrap();
+ file.write_all(b"U+005C__END__").unwrap();
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_check_compatibility_success() {
+ let dir = tempdir().unwrap();
+ let source_file_path = dir.path().join("source_server_info.json");
+ let sink_file_path = dir.path().join("sink_server_info.json");
+ let transformer_file_path = dir.path().join("transformer_server_info.json");
+ let fb_sink_file_path = dir.path().join("fb_sink_server_info.json");
+
+ let server_info = ServerInfo {
+ protocol: "uds".to_string(),
+ language: "rust".to_string(),
+ minimum_numaflow_version: "0.1.0".to_string(),
+ version: "0.1.0".to_string(),
+ metadata: None,
+ };
+
+ write_server_info(source_file_path.to_str().unwrap(), &server_info)
+ .await
+ .unwrap();
+ write_server_info(sink_file_path.to_str().unwrap(), &server_info)
+ .await
+ .unwrap();
+ write_server_info(transformer_file_path.to_str().unwrap(), &server_info)
+ .await
+ .unwrap();
+ write_server_info(fb_sink_file_path.to_str().unwrap(), &server_info)
+ .await
+ .unwrap();
+
+ let cln_token = CancellationToken::new();
+ let result =
+ check_compatibility(&cln_token, source_file_path, sink_file_path, None, None).await;
+
+ assert!(result.is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_check_compatibility_failure() {
+ let cln_token = CancellationToken::new();
+ let dir = tempdir().unwrap();
+ let source_file_path = dir.path().join("source_server_info.json");
+ let sink_file_path = dir.path().join("sink_server_info.json");
+ let transformer_file_path = dir.path().join("transformer_server_info.json");
+ let fb_sink_file_path = dir.path().join("fb_sink_server_info.json");
+
+ // do not write server info files to simulate failure
+ // cancel the token after 100ms to simulate cancellation
+ let token = cln_token.clone();
+ let handle = tokio::spawn(async move {
+ sleep(Duration::from_millis(100)).await;
+ token.cancel();
+ });
+ let result = check_compatibility(
+ &cln_token,
+ source_file_path,
+ sink_file_path,
+ Some(transformer_file_path),
+ Some(fb_sink_file_path),
+ )
+ .await;
+
+ assert!(result.is_err());
+ handle.await.unwrap();
+ }
+
+ struct SimpleSource {}
+
+ #[tonic::async_trait]
+ impl source::Sourcer for SimpleSource {
+ async fn read(&self, _request: SourceReadRequest, _transmitter: Sender) {}
+
+ async fn ack(&self, _offset: Offset) {}
+
+ async fn pending(&self) -> usize {
+ 0
+ }
+
+ async fn partitions(&self) -> Option> {
+ Some(vec![0])
+ }
+ }
+
+ struct SimpleTransformer;
+ #[tonic::async_trait]
+ impl sourcetransform::SourceTransformer for SimpleTransformer {
+ async fn transform(
+ &self,
+ _input: sourcetransform::SourceTransformRequest,
+ ) -> Vec {
+ vec![]
+ }
+ }
+
+ struct InMemorySink {}
+
+ #[tonic::async_trait]
+ impl sink::Sinker for InMemorySink {
+ async fn sink(&self, mut _input: mpsc::Receiver) -> Vec {
+ vec![]
+ }
+ }
+
+ #[tokio::test]
+ async fn test_wait_until_ready() {
+ // Start the source server
+ let (source_shutdown_tx, source_shutdown_rx) = tokio::sync::oneshot::channel();
+ let tmp_dir = tempfile::TempDir::new().unwrap();
+ let source_sock_file = tmp_dir.path().join("source.sock");
+ let server_info_file = tmp_dir.path().join("source-server-info");
+
+ let server_info = server_info_file.clone();
+ let source_socket = source_sock_file.clone();
+ let source_server_handle = tokio::spawn(async move {
+ source::Server::new(SimpleSource {})
+ .with_socket_file(source_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(source_shutdown_rx)
+ .await
+ .unwrap();
+ });
+
+ // Start the sink server
+ let (sink_shutdown_tx, sink_shutdown_rx) = tokio::sync::oneshot::channel();
+ let sink_tmp_dir = tempfile::TempDir::new().unwrap();
+ let sink_sock_file = sink_tmp_dir.path().join("sink.sock");
+ let server_info_file = sink_tmp_dir.path().join("sink-server-info");
+
+ let server_info = server_info_file.clone();
+ let sink_socket = sink_sock_file.clone();
+ let sink_server_handle = tokio::spawn(async move {
+ sink::Server::new(InMemorySink {})
+ .with_socket_file(sink_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(sink_shutdown_rx)
+ .await
+ .unwrap();
+ });
+
+ // Start the transformer server
+ let (transformer_shutdown_tx, transformer_shutdown_rx) = tokio::sync::oneshot::channel();
+ let tmp_dir = tempfile::TempDir::new().unwrap();
+ let transformer_sock_file = tmp_dir.path().join("transformer.sock");
+ let server_info_file = tmp_dir.path().join("transformer-server-info");
+
+ let server_info = server_info_file.clone();
+ let transformer_socket = transformer_sock_file.clone();
+ let transformer_server_handle = tokio::spawn(async move {
+ sourcetransform::Server::new(SimpleTransformer {})
+ .with_socket_file(transformer_socket)
+ .with_server_info_file(server_info)
+ .start_with_shutdown(transformer_shutdown_rx)
+ .await
+ .unwrap();
+ });
+
+ // Wait for the servers to start
+ sleep(Duration::from_millis(100)).await;
+
+ let mut source_grpc_client =
+ SourceClient::new(create_rpc_channel(source_sock_file.clone()).await.unwrap());
+ let mut sink_grpc_client =
+ SinkClient::new(create_rpc_channel(sink_sock_file.clone()).await.unwrap());
+ let mut transformer_grpc_client = Some(SourceTransformClient::new(
+ create_rpc_channel(transformer_sock_file.clone())
+ .await
+ .unwrap(),
+ ));
+
+ let mut fb_sink_grpc_client = None;
+
+ let cln_token = CancellationToken::new();
+ let result = wait_until_ready(
+ cln_token,
+ &mut source_grpc_client,
+ &mut sink_grpc_client,
+ &mut transformer_grpc_client,
+ &mut fb_sink_grpc_client,
+ )
+ .await;
+ assert!(result.is_ok());
+
+ source_shutdown_tx.send(()).unwrap();
+ sink_shutdown_tx.send(()).unwrap();
+ transformer_shutdown_tx.send(()).unwrap();
+
+ source_server_handle.await.unwrap();
+ sink_server_handle.await.unwrap();
+ transformer_server_handle.await.unwrap();
+ }
+}
diff --git a/rust/monovertex/src/transformer.rs b/rust/monovertex/src/transformer.rs
index f891a851fc..f7797b5d7d 100644
--- a/rust/monovertex/src/transformer.rs
+++ b/rust/monovertex/src/transformer.rs
@@ -1,61 +1,20 @@
-use crate::error::{Error, Result};
+use crate::error::Result;
use crate::message::Message;
-use crate::shared::{connect_with_uds, utc_from_timestamp};
-use crate::transformer::proto::SourceTransformRequest;
-use backoff::retry::Retry;
-use backoff::strategy::fixed;
+use crate::shared::utc_from_timestamp;
+use crate::sourcetransform_pb::source_transform_client::SourceTransformClient;
+use crate::sourcetransform_pb::SourceTransformRequest;
use tonic::transport::Channel;
-use tonic::Request;
-
-pub mod proto {
- tonic::include_proto!("sourcetransformer.v1");
-}
const DROP: &str = "U+005C__DROP__";
-const RECONNECT_INTERVAL: u64 = 1000;
-const MAX_RECONNECT_ATTEMPTS: usize = 5;
-const TRANSFORMER_SOCKET: &str = "/var/run/numaflow/sourcetransform.sock";
-const TRANSFORMER_SERVER_INFO_FILE: &str = "/var/run/numaflow/sourcetransformer-server-info";
-
-/// TransformerConfig is the configuration for the transformer server.
-#[derive(Debug, Clone)]
-pub struct TransformerConfig {
- pub socket_path: String,
- pub server_info_file: String,
- pub max_message_size: usize,
-}
-
-impl Default for TransformerConfig {
- fn default() -> Self {
- TransformerConfig {
- socket_path: TRANSFORMER_SOCKET.to_string(),
- server_info_file: TRANSFORMER_SERVER_INFO_FILE.to_string(),
- max_message_size: 64 * 1024 * 1024, // 64 MB
- }
- }
-}
/// TransformerClient is a client to interact with the transformer server.
#[derive(Clone)]
-pub struct TransformerClient {
- client: proto::source_transform_client::SourceTransformClient,
+pub struct SourceTransformer {
+ client: SourceTransformClient,
}
-impl TransformerClient {
- pub(crate) async fn connect(config: TransformerConfig) -> Result {
- let interval =
- fixed::Interval::from_millis(RECONNECT_INTERVAL).take(MAX_RECONNECT_ATTEMPTS);
-
- let channel = Retry::retry(
- interval,
- || async { connect_with_uds(config.socket_path.clone().into()).await },
- |_: &Error| true,
- )
- .await?;
-
- let client = proto::source_transform_client::SourceTransformClient::new(channel)
- .max_decoding_message_size(config.max_message_size)
- .max_encoding_message_size(config.max_message_size);
+impl SourceTransformer {
+ pub(crate) async fn new(client: SourceTransformClient) -> Result {
Ok(Self { client })
}
@@ -92,21 +51,18 @@ impl TransformerClient {
Ok(Some(messages))
}
-
- pub(crate) async fn is_ready(&mut self) -> bool {
- self.client.is_ready(Request::new(())).await.is_ok()
- }
}
#[cfg(test)]
mod tests {
use std::error::Error;
+ use crate::shared::create_rpc_channel;
+ use crate::sourcetransform_pb::source_transform_client::SourceTransformClient;
+ use crate::transformer::SourceTransformer;
use numaflow::sourcetransform;
use tempfile::TempDir;
- use crate::transformer::{TransformerClient, TransformerConfig};
-
struct NowCat;
#[tonic::async_trait]
@@ -143,11 +99,9 @@ mod tests {
// wait for the server to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
- let mut client = TransformerClient::connect(TransformerConfig {
- socket_path: sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- })
+ let mut client = SourceTransformer::new(SourceTransformClient::new(
+ create_rpc_channel(sock_file).await?,
+ ))
.await?;
let message = crate::message::Message {
@@ -162,9 +116,6 @@ mod tests {
headers: Default::default(),
};
- let resp = client.is_ready().await;
- assert!(resp);
-
let resp = client.transform_fn(message).await?;
assert!(resp.is_some());
assert_eq!(resp.unwrap().len(), 1);
@@ -212,11 +163,9 @@ mod tests {
// wait for the server to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
- let mut client = TransformerClient::connect(TransformerConfig {
- socket_path: sock_file.to_str().unwrap().to_string(),
- server_info_file: server_info_file.to_str().unwrap().to_string(),
- max_message_size: 4 * 1024 * 1024,
- })
+ let mut client = SourceTransformer::new(SourceTransformClient::new(
+ create_rpc_channel(sock_file).await?,
+ ))
.await?;
let message = crate::message::Message {
@@ -231,9 +180,6 @@ mod tests {
headers: Default::default(),
};
- let resp = client.is_ready().await;
- assert!(resp);
-
let resp = client.transform_fn(message).await?;
assert!(resp.is_none());
diff --git a/rust/servesink/Cargo.toml b/rust/servesink/Cargo.toml
index e820030494..90a7c44696 100644
--- a/rust/servesink/Cargo.toml
+++ b/rust/servesink/Cargo.toml
@@ -6,11 +6,11 @@ edition = "2021"
[dependencies]
tonic = "0.12.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
-numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
+numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "handshake" }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
[dependencies.reqwest]
-version = "^0.11"
+version = "0.12.7"
default-features = false
features = ["rustls-tls"]
\ No newline at end of file
diff --git a/rust/serving/src/app/tracker.rs b/rust/serving/src/app/tracker.rs
index 85d3c2b76d..12420f948c 100644
--- a/rust/serving/src/app/tracker.rs
+++ b/rust/serving/src/app/tracker.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::app::callback::CallbackRequest;
-use crate::pipeline::{Edge, PipelineDCG, OperatorType};
+use crate::pipeline::{Edge, OperatorType, PipelineDCG};
use crate::Error;
fn compare_slice(operator: &OperatorType, a: &[String], b: &[String]) -> bool {
diff --git a/rust/src/bin/main.rs b/rust/src/bin/main.rs
index fdbd58a6a0..0b000dc032 100644
--- a/rust/src/bin/main.rs
+++ b/rust/src/bin/main.rs
@@ -30,7 +30,9 @@ async fn main() {
info!("Error running servesink: {}", e);
}
} else if args.contains(&"--monovertex".to_string()) {
- monovertex::mono_vertex().await;
+ if let Err(e) = monovertex::mono_vertex().await {
+ error!("Error running monovertex: {}", e);
+ }
} else {
error!("Invalid argument. Use --serve, --servesink, or --monovertex.");
}
diff --git a/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml b/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
index acdb0b29f6..e491448505 100644
--- a/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
+++ b/test/monovertex-e2e/testdata/mono-vertex-with-transformer.yaml
@@ -3,6 +3,8 @@ kind: MonoVertex
metadata:
name: transformer-mono-vertex
spec:
+ scale:
+ min: 1
source:
udsource:
container:
diff --git a/test/udsource-e2e/testdata/simple-source-go.yaml b/test/udsource-e2e/testdata/simple-source-go.yaml
index 28e27af1f6..65c6472479 100644
--- a/test/udsource-e2e/testdata/simple-source-go.yaml
+++ b/test/udsource-e2e/testdata/simple-source-go.yaml
@@ -11,7 +11,6 @@ spec:
# A simple user-defined source for e2e testing
# See https://github.com/numaproj/numaflow-go/tree/main/pkg/sourcer/examples/simple_source
image: quay.io/numaio/numaflow-go/source-simple-source:stable
- imagePullPolicy: Always
limits:
readBatchSize: 500
scale:
diff --git a/test/udsource-e2e/testdata/simple-source-java.yaml b/test/udsource-e2e/testdata/simple-source-java.yaml
index 2d030bc06b..4c883b94b3 100644
--- a/test/udsource-e2e/testdata/simple-source-java.yaml
+++ b/test/udsource-e2e/testdata/simple-source-java.yaml
@@ -5,16 +5,19 @@ metadata:
spec:
vertices:
- name: in
+ scale:
+ min: 1
source:
udsource:
container:
# A simple user-defined source for e2e testing
# See https://github.com/numaproj/numaflow-java/tree/main/examples/src/main/java/io/numaproj/numaflow/examples/source/simple
image: quay.io/numaio/numaflow-java/source-simple-source:stable
- imagePullPolicy: Always
limits:
readBatchSize: 500
- name: out
+ scale:
+ min: 1
sink:
log: {}
edges:
diff --git a/test/udsource-e2e/testdata/simple-source-python.yaml b/test/udsource-e2e/testdata/simple-source-python.yaml
index 47bc7a175b..9862b63bb6 100644
--- a/test/udsource-e2e/testdata/simple-source-python.yaml
+++ b/test/udsource-e2e/testdata/simple-source-python.yaml
@@ -5,13 +5,14 @@ metadata:
spec:
vertices:
- name: in
+ scale:
+ min: 1
source:
udsource:
container:
# A simple user-defined source for e2e testing
# See https://github.com/numaproj/numaflow-python/tree/main/examples/source/simple_source
image: quay.io/numaio/numaflow-python/simple-source:stable
- imagePullPolicy: Always
limits:
readBatchSize: 500
- name: out
diff --git a/test/udsource-e2e/testdata/simple-source-rust.yaml b/test/udsource-e2e/testdata/simple-source-rust.yaml
new file mode 100644
index 0000000000..5a2d670710
--- /dev/null
+++ b/test/udsource-e2e/testdata/simple-source-rust.yaml
@@ -0,0 +1,26 @@
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: Pipeline
+metadata:
+ name: simple-source-rust
+spec:
+ vertices:
+ - name: in
+ source:
+ udsource:
+ container:
+ # A simple user-defined source for e2e testing
+ # https://github.com/numaproj/numaflow-rs/tree/main/examples/simple-source
+ image: quay.io/numaio/numaflow-rs/simple-source:stable
+ limits:
+ readBatchSize: 500
+ scale:
+ min: 1
+ - name: out
+ sink:
+ log: {}
+ scale:
+ min: 1
+ max: 1
+ edges:
+ - from: in
+ to: out
diff --git a/test/udsource-e2e/udsource_test.go b/test/udsource-e2e/udsource_test.go
index ef5a9ffacf..8d3de49c74 100644
--- a/test/udsource-e2e/udsource_test.go
+++ b/test/udsource-e2e/udsource_test.go
@@ -50,12 +50,16 @@ func (s *UserDefinedSourceSuite) testSimpleSourcePython() {
s.testSimpleSource("python", false)
}
+func (s *UserDefinedSourceSuite) testSimpleSourceRust() {
+ s.testSimpleSource("rust", false)
+}
+
func (s *UserDefinedSourceSuite) TestUDSource() {
var wg sync.WaitGroup
- wg.Add(3)
+ wg.Add(4)
go func() {
defer wg.Done()
- s.testSimpleSourcePython()
+ // s.testSimpleSourcePython() // FIXME: python udsource
}()
go func() {
defer wg.Done()
@@ -65,6 +69,10 @@ func (s *UserDefinedSourceSuite) TestUDSource() {
defer wg.Done()
s.testSimpleSourceGo()
}()
+ go func() {
+ defer wg.Done()
+ s.testSimpleSourceRust()
+ }()
wg.Wait()
}
From b4f9278570f67cba3d85fffe7ca287c5b00da489 Mon Sep 17 00:00:00 2001
From: Derek Wang
Date: Sun, 22 Sep 2024 21:59:57 -0700
Subject: [PATCH 067/188] fix: rollback codegen script (#2079)
---
hack/update-codegen.sh | 6 +-
.../v1alpha1/zz_generated.deepcopy.go | 62 +++++++++++++++++++
2 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh
index 1137a938cc..3ab861b73b 100755
--- a/hack/update-codegen.sh
+++ b/hack/update-codegen.sh
@@ -17,13 +17,15 @@ cd "${FAKE_REPOPATH}"
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${FAKE_REPOPATH}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
+chmod +x ${CODEGEN_PKG}/*.sh
+
subheader "running codegen"
-bash -x ${CODEGEN_PKG}/kube_codegen.sh "deepcopy" \
+bash -x ${CODEGEN_PKG}/generate-groups.sh "deepcopy" \
github.com/numaproj/numaflow/pkg/client github.com/numaproj/numaflow/pkg/apis \
"numaflow:v1alpha1" \
--go-header-file hack/boilerplate/boilerplate.go.txt
-bash -x ${CODEGEN_PKG}/kube_codegen.sh "client,informer,lister" \
+bash -x ${CODEGEN_PKG}/generate-groups.sh "client,informer,lister" \
github.com/numaproj/numaflow/pkg/client github.com/numaproj/numaflow/pkg/apis \
"numaflow:v1alpha1" \
--plural-exceptions="Vertex:Vertices,MonoVertex:MonoVertices" \
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index b6325ce920..49b93292ff 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -212,6 +212,7 @@ func (in *AbstractVertex) DeepCopyInto(out *AbstractVertex) {
*out = new(ContainerTemplate)
(*in).DeepCopyInto(*out)
}
+ in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy)
return
}
@@ -422,6 +423,16 @@ func (in *Container) DeepCopyInto(out *Container) {
*out = new(v1.PullPolicy)
**out = **in
}
+ if in.ReadinessProbe != nil {
+ in, out := &in.ReadinessProbe, &out.ReadinessProbe
+ *out = new(Probe)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.LivenessProbe != nil {
+ in, out := &in.LivenessProbe, &out.LivenessProbe
+ *out = new(Probe)
+ (*in).DeepCopyInto(*out)
+ }
return
}
@@ -458,6 +469,16 @@ func (in *ContainerTemplate) DeepCopyInto(out *ContainerTemplate) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
+ if in.ReadinessProbe != nil {
+ in, out := &in.ReadinessProbe, &out.ReadinessProbe
+ *out = new(Probe)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.LivenessProbe != nil {
+ in, out := &in.LivenessProbe, &out.LivenessProbe
+ *out = new(Probe)
+ (*in).DeepCopyInto(*out)
+ }
return
}
@@ -1917,6 +1938,47 @@ func (in *PipelineStatus) DeepCopy() *PipelineStatus {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Probe) DeepCopyInto(out *Probe) {
+ *out = *in
+ if in.InitialDelaySeconds != nil {
+ in, out := &in.InitialDelaySeconds, &out.InitialDelaySeconds
+ *out = new(int32)
+ **out = **in
+ }
+ if in.TimeoutSeconds != nil {
+ in, out := &in.TimeoutSeconds, &out.TimeoutSeconds
+ *out = new(int32)
+ **out = **in
+ }
+ if in.PeriodSeconds != nil {
+ in, out := &in.PeriodSeconds, &out.PeriodSeconds
+ *out = new(int32)
+ **out = **in
+ }
+ if in.SuccessThreshold != nil {
+ in, out := &in.SuccessThreshold, &out.SuccessThreshold
+ *out = new(int32)
+ **out = **in
+ }
+ if in.FailureThreshold != nil {
+ in, out := &in.FailureThreshold, &out.FailureThreshold
+ *out = new(int32)
+ **out = **in
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Probe.
+func (in *Probe) DeepCopy() *Probe {
+ if in == nil {
+ return nil
+ }
+ out := new(Probe)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RedisBufferService) DeepCopyInto(out *RedisBufferService) {
*out = *in
From b8c61debb784db0eaae31352138fd9b447f9d151 Mon Sep 17 00:00:00 2001
From: Vigith Maurice
Date: Mon, 23 Sep 2024 09:09:40 -0700
Subject: [PATCH 068/188] chore: fix numaflow-rs (#2081)
Signed-off-by: Vigith Maurice
---
rust/Cargo.lock | 10 +++++-----
rust/monovertex/Cargo.toml | 2 +-
rust/servesink/Cargo.toml | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 624d5f14a8..81b0a5149b 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1098,7 +1098,7 @@ dependencies = [
"tokio",
"tokio-rustls 0.26.0",
"tower-service",
- "webpki-roots 0.26.5",
+ "webpki-roots 0.26.6",
]
[[package]]
@@ -1605,7 +1605,7 @@ dependencies = [
[[package]]
name = "numaflow"
version = "0.1.1"
-source = "git+https://github.com/numaproj/numaflow-rs.git?branch=handshake#baecc88456f317b08bc869f82596e2b746cf798b"
+source = "git+https://github.com/numaproj/numaflow-rs.git?branch=main#362f2b0a0705c34ce3693b8714885dfbae7843e8"
dependencies = [
"chrono",
"futures-util",
@@ -2226,7 +2226,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "webpki-roots 0.26.5",
+ "webpki-roots 0.26.6",
"windows-registry",
]
@@ -3408,9 +3408,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
-version = "0.26.5"
+version = "0.26.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
+checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958"
dependencies = [
"rustls-pki-types",
]
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index da75a4c8c8..ce973f79ac 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -38,7 +38,7 @@ log = "0.4.22"
[dev-dependencies]
tempfile = "3.11.0"
-numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "handshake" }
+numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
[build-dependencies]
tonic-build = "0.12.1"
diff --git a/rust/servesink/Cargo.toml b/rust/servesink/Cargo.toml
index 90a7c44696..7b037c7208 100644
--- a/rust/servesink/Cargo.toml
+++ b/rust/servesink/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
tonic = "0.12.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
-numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "handshake" }
+numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
From 6d1ebd04f2089c81bd8e0c5e763cd7c363cb7623 Mon Sep 17 00:00:00 2001
From: Sidhant Kohli
Date: Mon, 23 Sep 2024 19:47:26 -0700
Subject: [PATCH 069/188] feat: add pause for monovertex (#2077)
Signed-off-by: Sidhant Kohli
---
api/json-schema/schema.json | 13 +
api/openapi-spec/swagger.json | 13 +
.../numaflow.numaproj.io_monovertices.yaml | 15 +
config/install.yaml | 15 +
config/namespace-install.yaml | 15 +
docs/APIs.md | 106 ++
pkg/apis/numaflow/v1alpha1/generated.pb.go | 1315 ++++++++++-------
pkg/apis/numaflow/v1alpha1/generated.proto | 12 +
.../numaflow/v1alpha1/mono_vertex_types.go | 39 +-
.../v1alpha1/mono_vertex_types_test.go | 32 +
.../numaflow/v1alpha1/openapi_generated.go | 29 +-
pkg/apis/numaflow/v1alpha1/pipeline_types.go | 4 +-
.../numaflow/v1alpha1/pipeline_types_test.go | 2 +-
.../v1alpha1/zz_generated.deepcopy.go | 17 +
pkg/reconciler/monovertex/controller.go | 14 +-
pkg/reconciler/monovertex/controller_test.go | 3 +-
pkg/reconciler/monovertex/scaling/scaling.go | 10 +-
rust/numaflow-models/src/models/mod.rs | 2 +
.../src/models/mono_vertex_lifecycle.rs | 32 +
.../src/models/mono_vertex_spec.rs | 3 +
20 files changed, 1118 insertions(+), 573 deletions(-)
create mode 100644 rust/numaflow-models/src/models/mono_vertex_lifecycle.rs
diff --git a/api/json-schema/schema.json b/api/json-schema/schema.json
index 24ca429580..27258328ee 100644
--- a/api/json-schema/schema.json
+++ b/api/json-schema/schema.json
@@ -19132,6 +19132,15 @@
],
"type": "object"
},
+ "io.numaproj.numaflow.v1alpha1.MonoVertexLifecycle": {
+ "properties": {
+ "desiredPhase": {
+ "description": "DesiredPhase used to bring the pipeline from current phase to desired phase",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
"io.numaproj.numaflow.v1alpha1.MonoVertexLimits": {
"properties": {
"readBatchSize": {
@@ -19213,6 +19222,10 @@
},
"type": "array"
},
+ "lifecycle": {
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.MonoVertexLifecycle",
+ "description": "Lifecycle defines the Lifecycle properties of a MonoVertex"
+ },
"limits": {
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.MonoVertexLimits",
"description": "Limits define the limitations such as read batch size for the mono vertex."
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index e2e29fbabf..4bf6fb8758 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -19127,6 +19127,15 @@
}
}
},
+ "io.numaproj.numaflow.v1alpha1.MonoVertexLifecycle": {
+ "type": "object",
+ "properties": {
+ "desiredPhase": {
+ "description": "DesiredPhase used to bring the pipeline from current phase to desired phase",
+ "type": "string"
+ }
+ }
+ },
"io.numaproj.numaflow.v1alpha1.MonoVertexLimits": {
"type": "object",
"properties": {
@@ -19209,6 +19218,10 @@
"$ref": "#/definitions/io.k8s.api.core.v1.Container"
}
},
+ "lifecycle": {
+ "description": "Lifecycle defines the Lifecycle properties of a MonoVertex",
+ "$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.MonoVertexLifecycle"
+ },
"limits": {
"description": "Limits define the limitations such as read batch size for the mono vertex.",
"$ref": "#/definitions/io.numaproj.numaflow.v1alpha1.MonoVertexLimits"
diff --git a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
index 4d8a23ba14..e4b27b015c 100644
--- a/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
+++ b/config/base/crds/full/numaflow.numaproj.io_monovertices.yaml
@@ -2299,6 +2299,21 @@ spec:
- name
type: object
type: array
+ lifecycle:
+ default:
+ desiredPhase: Running
+ properties:
+ desiredPhase:
+ default: Running
+ enum:
+ - ""
+ - Running
+ - Failed
+ - Pausing
+ - Paused
+ - Deleting
+ type: string
+ type: object
limits:
properties:
readBatchSize:
diff --git a/config/install.yaml b/config/install.yaml
index c6551b513a..367657d66f 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -5195,6 +5195,21 @@ spec:
- name
type: object
type: array
+ lifecycle:
+ default:
+ desiredPhase: Running
+ properties:
+ desiredPhase:
+ default: Running
+ enum:
+ - ""
+ - Running
+ - Failed
+ - Pausing
+ - Paused
+ - Deleting
+ type: string
+ type: object
limits:
properties:
readBatchSize:
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 07fc13628d..48c6d0677c 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -5195,6 +5195,21 @@ spec:
- name
type: object
type: array
+ lifecycle:
+ default:
+ desiredPhase: Running
+ properties:
+ desiredPhase:
+ default: Running
+ enum:
+ - ""
+ - Running
+ - Failed
+ - Pausing
+ - Paused
+ - Deleting
+ type: string
+ type: object
limits:
properties:
readBatchSize:
diff --git a/docs/APIs.md b/docs/APIs.md
index 7b698883e7..eb9b923b4f 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -5893,6 +5893,27 @@ The strategy to use to replace existing pods with new ones.
+
+
+
+
+lifecycle
+
+MonoVertexLifecycle
+
+
+
+
+(Optional)
+
+
+Lifecycle defines the Lifecycle properties of a MonoVertex
+
+
+
+
+
+
@@ -5919,6 +5940,69 @@ MonoVertexStatus
+
+
+MonoVertexLifecycle
+
+
+
+
+(Appears on:
+MonoVertexSpec )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+
+
+
+Description
+
+
+
+
+
+
+
+
+
+
+
+
+desiredPhase
+
+MonoVertexPhase
+
+
+
+
+(Optional)
+
+
+DesiredPhase used to bring the pipeline from current phase to desired
+phase
+
+
+
+
+
+
+
+
+
+
MonoVertexLimits
@@ -6010,6 +6094,7 @@ MonoVertexPhase (string
alias)
(Appears on:
+MonoVertexLifecycle ,
MonoVertexStatus )
@@ -6282,6 +6367,27 @@ The strategy to use to replace existing pods with new ones.
+
+
+
+
+lifecycle
+
+MonoVertexLifecycle
+
+
+
+
+(Optional)
+
+
+Lifecycle defines the Lifecycle properties of a MonoVertex
+
+
+
+
+
+
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index 138962c4c2..8905ee00c2 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -1282,10 +1282,38 @@ func (m *MonoVertex) XXX_DiscardUnknown() {
var xxx_messageInfo_MonoVertex proto.InternalMessageInfo
+func (m *MonoVertexLifecycle) Reset() { *m = MonoVertexLifecycle{} }
+func (*MonoVertexLifecycle) ProtoMessage() {}
+func (*MonoVertexLifecycle) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0d1b17d3865563, []int{44}
+}
+func (m *MonoVertexLifecycle) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MonoVertexLifecycle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+}
+func (m *MonoVertexLifecycle) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MonoVertexLifecycle.Merge(m, src)
+}
+func (m *MonoVertexLifecycle) XXX_Size() int {
+ return m.Size()
+}
+func (m *MonoVertexLifecycle) XXX_DiscardUnknown() {
+ xxx_messageInfo_MonoVertexLifecycle.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MonoVertexLifecycle proto.InternalMessageInfo
+
func (m *MonoVertexLimits) Reset() { *m = MonoVertexLimits{} }
func (*MonoVertexLimits) ProtoMessage() {}
func (*MonoVertexLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{44}
+ return fileDescriptor_9d0d1b17d3865563, []int{45}
}
func (m *MonoVertexLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1313,7 +1341,7 @@ var xxx_messageInfo_MonoVertexLimits proto.InternalMessageInfo
func (m *MonoVertexList) Reset() { *m = MonoVertexList{} }
func (*MonoVertexList) ProtoMessage() {}
func (*MonoVertexList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{45}
+ return fileDescriptor_9d0d1b17d3865563, []int{46}
}
func (m *MonoVertexList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1341,7 +1369,7 @@ var xxx_messageInfo_MonoVertexList proto.InternalMessageInfo
func (m *MonoVertexSpec) Reset() { *m = MonoVertexSpec{} }
func (*MonoVertexSpec) ProtoMessage() {}
func (*MonoVertexSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{46}
+ return fileDescriptor_9d0d1b17d3865563, []int{47}
}
func (m *MonoVertexSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1369,7 +1397,7 @@ var xxx_messageInfo_MonoVertexSpec proto.InternalMessageInfo
func (m *MonoVertexStatus) Reset() { *m = MonoVertexStatus{} }
func (*MonoVertexStatus) ProtoMessage() {}
func (*MonoVertexStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{47}
+ return fileDescriptor_9d0d1b17d3865563, []int{48}
}
func (m *MonoVertexStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1397,7 +1425,7 @@ var xxx_messageInfo_MonoVertexStatus proto.InternalMessageInfo
func (m *NativeRedis) Reset() { *m = NativeRedis{} }
func (*NativeRedis) ProtoMessage() {}
func (*NativeRedis) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{48}
+ return fileDescriptor_9d0d1b17d3865563, []int{49}
}
func (m *NativeRedis) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1425,7 +1453,7 @@ var xxx_messageInfo_NativeRedis proto.InternalMessageInfo
func (m *NatsAuth) Reset() { *m = NatsAuth{} }
func (*NatsAuth) ProtoMessage() {}
func (*NatsAuth) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{49}
+ return fileDescriptor_9d0d1b17d3865563, []int{50}
}
func (m *NatsAuth) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1453,7 +1481,7 @@ var xxx_messageInfo_NatsAuth proto.InternalMessageInfo
func (m *NatsSource) Reset() { *m = NatsSource{} }
func (*NatsSource) ProtoMessage() {}
func (*NatsSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{50}
+ return fileDescriptor_9d0d1b17d3865563, []int{51}
}
func (m *NatsSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1481,7 +1509,7 @@ var xxx_messageInfo_NatsSource proto.InternalMessageInfo
func (m *NoStore) Reset() { *m = NoStore{} }
func (*NoStore) ProtoMessage() {}
func (*NoStore) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{51}
+ return fileDescriptor_9d0d1b17d3865563, []int{52}
}
func (m *NoStore) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1509,7 +1537,7 @@ var xxx_messageInfo_NoStore proto.InternalMessageInfo
func (m *PBQStorage) Reset() { *m = PBQStorage{} }
func (*PBQStorage) ProtoMessage() {}
func (*PBQStorage) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{52}
+ return fileDescriptor_9d0d1b17d3865563, []int{53}
}
func (m *PBQStorage) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1537,7 +1565,7 @@ var xxx_messageInfo_PBQStorage proto.InternalMessageInfo
func (m *PersistenceStrategy) Reset() { *m = PersistenceStrategy{} }
func (*PersistenceStrategy) ProtoMessage() {}
func (*PersistenceStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{53}
+ return fileDescriptor_9d0d1b17d3865563, []int{54}
}
func (m *PersistenceStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1565,7 +1593,7 @@ var xxx_messageInfo_PersistenceStrategy proto.InternalMessageInfo
func (m *Pipeline) Reset() { *m = Pipeline{} }
func (*Pipeline) ProtoMessage() {}
func (*Pipeline) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{54}
+ return fileDescriptor_9d0d1b17d3865563, []int{55}
}
func (m *Pipeline) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1593,7 +1621,7 @@ var xxx_messageInfo_Pipeline proto.InternalMessageInfo
func (m *PipelineLimits) Reset() { *m = PipelineLimits{} }
func (*PipelineLimits) ProtoMessage() {}
func (*PipelineLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{55}
+ return fileDescriptor_9d0d1b17d3865563, []int{56}
}
func (m *PipelineLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1621,7 +1649,7 @@ var xxx_messageInfo_PipelineLimits proto.InternalMessageInfo
func (m *PipelineList) Reset() { *m = PipelineList{} }
func (*PipelineList) ProtoMessage() {}
func (*PipelineList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{56}
+ return fileDescriptor_9d0d1b17d3865563, []int{57}
}
func (m *PipelineList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1649,7 +1677,7 @@ var xxx_messageInfo_PipelineList proto.InternalMessageInfo
func (m *PipelineSpec) Reset() { *m = PipelineSpec{} }
func (*PipelineSpec) ProtoMessage() {}
func (*PipelineSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{57}
+ return fileDescriptor_9d0d1b17d3865563, []int{58}
}
func (m *PipelineSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1677,7 +1705,7 @@ var xxx_messageInfo_PipelineSpec proto.InternalMessageInfo
func (m *PipelineStatus) Reset() { *m = PipelineStatus{} }
func (*PipelineStatus) ProtoMessage() {}
func (*PipelineStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{58}
+ return fileDescriptor_9d0d1b17d3865563, []int{59}
}
func (m *PipelineStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1705,7 +1733,7 @@ var xxx_messageInfo_PipelineStatus proto.InternalMessageInfo
func (m *Probe) Reset() { *m = Probe{} }
func (*Probe) ProtoMessage() {}
func (*Probe) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{59}
+ return fileDescriptor_9d0d1b17d3865563, []int{60}
}
func (m *Probe) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1733,7 +1761,7 @@ var xxx_messageInfo_Probe proto.InternalMessageInfo
func (m *RedisBufferService) Reset() { *m = RedisBufferService{} }
func (*RedisBufferService) ProtoMessage() {}
func (*RedisBufferService) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{60}
+ return fileDescriptor_9d0d1b17d3865563, []int{61}
}
func (m *RedisBufferService) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1761,7 +1789,7 @@ var xxx_messageInfo_RedisBufferService proto.InternalMessageInfo
func (m *RedisConfig) Reset() { *m = RedisConfig{} }
func (*RedisConfig) ProtoMessage() {}
func (*RedisConfig) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{61}
+ return fileDescriptor_9d0d1b17d3865563, []int{62}
}
func (m *RedisConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1789,7 +1817,7 @@ var xxx_messageInfo_RedisConfig proto.InternalMessageInfo
func (m *RedisSettings) Reset() { *m = RedisSettings{} }
func (*RedisSettings) ProtoMessage() {}
func (*RedisSettings) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{62}
+ return fileDescriptor_9d0d1b17d3865563, []int{63}
}
func (m *RedisSettings) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1817,7 +1845,7 @@ var xxx_messageInfo_RedisSettings proto.InternalMessageInfo
func (m *RetryStrategy) Reset() { *m = RetryStrategy{} }
func (*RetryStrategy) ProtoMessage() {}
func (*RetryStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{63}
+ return fileDescriptor_9d0d1b17d3865563, []int{64}
}
func (m *RetryStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1845,7 +1873,7 @@ var xxx_messageInfo_RetryStrategy proto.InternalMessageInfo
func (m *RollingUpdateStrategy) Reset() { *m = RollingUpdateStrategy{} }
func (*RollingUpdateStrategy) ProtoMessage() {}
func (*RollingUpdateStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{64}
+ return fileDescriptor_9d0d1b17d3865563, []int{65}
}
func (m *RollingUpdateStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1873,7 +1901,7 @@ var xxx_messageInfo_RollingUpdateStrategy proto.InternalMessageInfo
func (m *SASL) Reset() { *m = SASL{} }
func (*SASL) ProtoMessage() {}
func (*SASL) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{65}
+ return fileDescriptor_9d0d1b17d3865563, []int{66}
}
func (m *SASL) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1901,7 +1929,7 @@ var xxx_messageInfo_SASL proto.InternalMessageInfo
func (m *SASLPlain) Reset() { *m = SASLPlain{} }
func (*SASLPlain) ProtoMessage() {}
func (*SASLPlain) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{66}
+ return fileDescriptor_9d0d1b17d3865563, []int{67}
}
func (m *SASLPlain) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1929,7 +1957,7 @@ var xxx_messageInfo_SASLPlain proto.InternalMessageInfo
func (m *Scale) Reset() { *m = Scale{} }
func (*Scale) ProtoMessage() {}
func (*Scale) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{67}
+ return fileDescriptor_9d0d1b17d3865563, []int{68}
}
func (m *Scale) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1957,7 +1985,7 @@ var xxx_messageInfo_Scale proto.InternalMessageInfo
func (m *ServingSource) Reset() { *m = ServingSource{} }
func (*ServingSource) ProtoMessage() {}
func (*ServingSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{68}
+ return fileDescriptor_9d0d1b17d3865563, []int{69}
}
func (m *ServingSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1985,7 +2013,7 @@ var xxx_messageInfo_ServingSource proto.InternalMessageInfo
func (m *ServingStore) Reset() { *m = ServingStore{} }
func (*ServingStore) ProtoMessage() {}
func (*ServingStore) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{69}
+ return fileDescriptor_9d0d1b17d3865563, []int{70}
}
func (m *ServingStore) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2013,7 +2041,7 @@ var xxx_messageInfo_ServingStore proto.InternalMessageInfo
func (m *SessionWindow) Reset() { *m = SessionWindow{} }
func (*SessionWindow) ProtoMessage() {}
func (*SessionWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{70}
+ return fileDescriptor_9d0d1b17d3865563, []int{71}
}
func (m *SessionWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2041,7 +2069,7 @@ var xxx_messageInfo_SessionWindow proto.InternalMessageInfo
func (m *SideInput) Reset() { *m = SideInput{} }
func (*SideInput) ProtoMessage() {}
func (*SideInput) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{71}
+ return fileDescriptor_9d0d1b17d3865563, []int{72}
}
func (m *SideInput) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2069,7 +2097,7 @@ var xxx_messageInfo_SideInput proto.InternalMessageInfo
func (m *SideInputTrigger) Reset() { *m = SideInputTrigger{} }
func (*SideInputTrigger) ProtoMessage() {}
func (*SideInputTrigger) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{72}
+ return fileDescriptor_9d0d1b17d3865563, []int{73}
}
func (m *SideInputTrigger) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2097,7 +2125,7 @@ var xxx_messageInfo_SideInputTrigger proto.InternalMessageInfo
func (m *SideInputsManagerTemplate) Reset() { *m = SideInputsManagerTemplate{} }
func (*SideInputsManagerTemplate) ProtoMessage() {}
func (*SideInputsManagerTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{73}
+ return fileDescriptor_9d0d1b17d3865563, []int{74}
}
func (m *SideInputsManagerTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2125,7 +2153,7 @@ var xxx_messageInfo_SideInputsManagerTemplate proto.InternalMessageInfo
func (m *Sink) Reset() { *m = Sink{} }
func (*Sink) ProtoMessage() {}
func (*Sink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{74}
+ return fileDescriptor_9d0d1b17d3865563, []int{75}
}
func (m *Sink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2153,7 +2181,7 @@ var xxx_messageInfo_Sink proto.InternalMessageInfo
func (m *SlidingWindow) Reset() { *m = SlidingWindow{} }
func (*SlidingWindow) ProtoMessage() {}
func (*SlidingWindow) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{75}
+ return fileDescriptor_9d0d1b17d3865563, []int{76}
}
func (m *SlidingWindow) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2181,7 +2209,7 @@ var xxx_messageInfo_SlidingWindow proto.InternalMessageInfo
func (m *Source) Reset() { *m = Source{} }
func (*Source) ProtoMessage() {}
func (*Source) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{76}
+ return fileDescriptor_9d0d1b17d3865563, []int{77}
}
func (m *Source) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2209,7 +2237,7 @@ var xxx_messageInfo_Source proto.InternalMessageInfo
func (m *Status) Reset() { *m = Status{} }
func (*Status) ProtoMessage() {}
func (*Status) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{77}
+ return fileDescriptor_9d0d1b17d3865563, []int{78}
}
func (m *Status) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2237,7 +2265,7 @@ var xxx_messageInfo_Status proto.InternalMessageInfo
func (m *TLS) Reset() { *m = TLS{} }
func (*TLS) ProtoMessage() {}
func (*TLS) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{78}
+ return fileDescriptor_9d0d1b17d3865563, []int{79}
}
func (m *TLS) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2265,7 +2293,7 @@ var xxx_messageInfo_TLS proto.InternalMessageInfo
func (m *TagConditions) Reset() { *m = TagConditions{} }
func (*TagConditions) ProtoMessage() {}
func (*TagConditions) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{79}
+ return fileDescriptor_9d0d1b17d3865563, []int{80}
}
func (m *TagConditions) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2293,7 +2321,7 @@ var xxx_messageInfo_TagConditions proto.InternalMessageInfo
func (m *Templates) Reset() { *m = Templates{} }
func (*Templates) ProtoMessage() {}
func (*Templates) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{80}
+ return fileDescriptor_9d0d1b17d3865563, []int{81}
}
func (m *Templates) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2321,7 +2349,7 @@ var xxx_messageInfo_Templates proto.InternalMessageInfo
func (m *Transformer) Reset() { *m = Transformer{} }
func (*Transformer) ProtoMessage() {}
func (*Transformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{81}
+ return fileDescriptor_9d0d1b17d3865563, []int{82}
}
func (m *Transformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2349,7 +2377,7 @@ var xxx_messageInfo_Transformer proto.InternalMessageInfo
func (m *UDF) Reset() { *m = UDF{} }
func (*UDF) ProtoMessage() {}
func (*UDF) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{82}
+ return fileDescriptor_9d0d1b17d3865563, []int{83}
}
func (m *UDF) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2377,7 +2405,7 @@ var xxx_messageInfo_UDF proto.InternalMessageInfo
func (m *UDSink) Reset() { *m = UDSink{} }
func (*UDSink) ProtoMessage() {}
func (*UDSink) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{83}
+ return fileDescriptor_9d0d1b17d3865563, []int{84}
}
func (m *UDSink) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2405,7 +2433,7 @@ var xxx_messageInfo_UDSink proto.InternalMessageInfo
func (m *UDSource) Reset() { *m = UDSource{} }
func (*UDSource) ProtoMessage() {}
func (*UDSource) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{84}
+ return fileDescriptor_9d0d1b17d3865563, []int{85}
}
func (m *UDSource) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2433,7 +2461,7 @@ var xxx_messageInfo_UDSource proto.InternalMessageInfo
func (m *UDTransformer) Reset() { *m = UDTransformer{} }
func (*UDTransformer) ProtoMessage() {}
func (*UDTransformer) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{85}
+ return fileDescriptor_9d0d1b17d3865563, []int{86}
}
func (m *UDTransformer) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2461,7 +2489,7 @@ var xxx_messageInfo_UDTransformer proto.InternalMessageInfo
func (m *UpdateStrategy) Reset() { *m = UpdateStrategy{} }
func (*UpdateStrategy) ProtoMessage() {}
func (*UpdateStrategy) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{86}
+ return fileDescriptor_9d0d1b17d3865563, []int{87}
}
func (m *UpdateStrategy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2489,7 +2517,7 @@ var xxx_messageInfo_UpdateStrategy proto.InternalMessageInfo
func (m *Vertex) Reset() { *m = Vertex{} }
func (*Vertex) ProtoMessage() {}
func (*Vertex) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{87}
+ return fileDescriptor_9d0d1b17d3865563, []int{88}
}
func (m *Vertex) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2517,7 +2545,7 @@ var xxx_messageInfo_Vertex proto.InternalMessageInfo
func (m *VertexInstance) Reset() { *m = VertexInstance{} }
func (*VertexInstance) ProtoMessage() {}
func (*VertexInstance) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{88}
+ return fileDescriptor_9d0d1b17d3865563, []int{89}
}
func (m *VertexInstance) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2545,7 +2573,7 @@ var xxx_messageInfo_VertexInstance proto.InternalMessageInfo
func (m *VertexLimits) Reset() { *m = VertexLimits{} }
func (*VertexLimits) ProtoMessage() {}
func (*VertexLimits) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{89}
+ return fileDescriptor_9d0d1b17d3865563, []int{90}
}
func (m *VertexLimits) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2573,7 +2601,7 @@ var xxx_messageInfo_VertexLimits proto.InternalMessageInfo
func (m *VertexList) Reset() { *m = VertexList{} }
func (*VertexList) ProtoMessage() {}
func (*VertexList) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{90}
+ return fileDescriptor_9d0d1b17d3865563, []int{91}
}
func (m *VertexList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2601,7 +2629,7 @@ var xxx_messageInfo_VertexList proto.InternalMessageInfo
func (m *VertexSpec) Reset() { *m = VertexSpec{} }
func (*VertexSpec) ProtoMessage() {}
func (*VertexSpec) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{91}
+ return fileDescriptor_9d0d1b17d3865563, []int{92}
}
func (m *VertexSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2629,7 +2657,7 @@ var xxx_messageInfo_VertexSpec proto.InternalMessageInfo
func (m *VertexStatus) Reset() { *m = VertexStatus{} }
func (*VertexStatus) ProtoMessage() {}
func (*VertexStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{92}
+ return fileDescriptor_9d0d1b17d3865563, []int{93}
}
func (m *VertexStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2657,7 +2685,7 @@ var xxx_messageInfo_VertexStatus proto.InternalMessageInfo
func (m *VertexTemplate) Reset() { *m = VertexTemplate{} }
func (*VertexTemplate) ProtoMessage() {}
func (*VertexTemplate) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{93}
+ return fileDescriptor_9d0d1b17d3865563, []int{94}
}
func (m *VertexTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2685,7 +2713,7 @@ var xxx_messageInfo_VertexTemplate proto.InternalMessageInfo
func (m *Watermark) Reset() { *m = Watermark{} }
func (*Watermark) ProtoMessage() {}
func (*Watermark) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{94}
+ return fileDescriptor_9d0d1b17d3865563, []int{95}
}
func (m *Watermark) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2713,7 +2741,7 @@ var xxx_messageInfo_Watermark proto.InternalMessageInfo
func (m *Window) Reset() { *m = Window{} }
func (*Window) ProtoMessage() {}
func (*Window) Descriptor() ([]byte, []int) {
- return fileDescriptor_9d0d1b17d3865563, []int{95}
+ return fileDescriptor_9d0d1b17d3865563, []int{96}
}
func (m *Window) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -2791,6 +2819,7 @@ func init() {
proto.RegisterMapType((map[string]string)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Metadata.AnnotationsEntry")
proto.RegisterMapType((map[string]string)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.Metadata.LabelsEntry")
proto.RegisterType((*MonoVertex)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.MonoVertex")
+ proto.RegisterType((*MonoVertexLifecycle)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.MonoVertexLifecycle")
proto.RegisterType((*MonoVertexLimits)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.MonoVertexLimits")
proto.RegisterType((*MonoVertexList)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.MonoVertexList")
proto.RegisterType((*MonoVertexSpec)(nil), "github.com.numaproj.numaflow.pkg.apis.numaflow.v1alpha1.MonoVertexSpec")
@@ -2851,510 +2880,513 @@ func init() {
}
var fileDescriptor_9d0d1b17d3865563 = []byte{
- // 8046 bytes of a gzipped FileDescriptorProto
+ // 8084 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0x57,
0x76, 0x9e, 0xfa, 0xbf, 0xfb, 0x34, 0xff, 0x74, 0x67, 0x34, 0xe2, 0xcc, 0x4a, 0xd3, 0xe3, 0x5a,
- 0xef, 0x7a, 0x1c, 0xdb, 0x64, 0x44, 0xaf, 0xb4, 0x5a, 0xdb, 0xbb, 0x12, 0x9b, 0x1c, 0x72, 0x38,
+ 0xef, 0xee, 0x38, 0xb6, 0xc9, 0x88, 0x5e, 0x69, 0xb5, 0xb6, 0x77, 0x25, 0x36, 0x39, 0xe4, 0x50,
0x43, 0xce, 0x70, 0x4f, 0x93, 0x23, 0xad, 0x15, 0xaf, 0x52, 0xac, 0xba, 0x6c, 0x96, 0x58, 0x5d,
- 0xd5, 0x5b, 0x55, 0xcd, 0x19, 0xca, 0x09, 0xd6, 0xb6, 0x12, 0x68, 0x83, 0x24, 0x48, 0xe0, 0x27,
- 0x03, 0x81, 0x13, 0x24, 0x08, 0xe0, 0x07, 0xc3, 0x79, 0x08, 0xb2, 0x79, 0x08, 0x90, 0x1f, 0x07,
- 0x41, 0xb2, 0xf9, 0x5f, 0x04, 0x01, 0xb2, 0x79, 0x21, 0xb2, 0x0c, 0xf2, 0x90, 0x00, 0x0e, 0x8c,
- 0x18, 0x89, 0x9d, 0x81, 0x11, 0x07, 0xf7, 0xaf, 0xfe, 0xba, 0x7a, 0x86, 0xec, 0x6a, 0x8e, 0x46,
- 0x89, 0xde, 0xba, 0xef, 0x39, 0xf7, 0x3b, 0xb7, 0x6e, 0xdd, 0xba, 0xf7, 0xdc, 0x73, 0xce, 0x3d,
- 0x17, 0xd6, 0xbb, 0x56, 0x70, 0x30, 0xd8, 0x5b, 0x30, 0xdc, 0xde, 0xa2, 0x33, 0xe8, 0xe9, 0x7d,
- 0xcf, 0xfd, 0x80, 0xff, 0xd8, 0xb7, 0xdd, 0x87, 0x8b, 0xfd, 0xc3, 0xee, 0xa2, 0xde, 0xb7, 0xfc,
- 0xa8, 0xe4, 0xe8, 0x35, 0xdd, 0xee, 0x1f, 0xe8, 0xaf, 0x2d, 0x76, 0xa9, 0x43, 0x3d, 0x3d, 0xa0,
- 0xe6, 0x42, 0xdf, 0x73, 0x03, 0x97, 0x7c, 0x39, 0x02, 0x5a, 0x50, 0x40, 0x0b, 0xaa, 0xda, 0x42,
- 0xff, 0xb0, 0xbb, 0xc0, 0x80, 0xa2, 0x12, 0x05, 0x74, 0xed, 0xa7, 0x62, 0x2d, 0xe8, 0xba, 0x5d,
- 0x77, 0x91, 0xe3, 0xed, 0x0d, 0xf6, 0xf9, 0x3f, 0xfe, 0x87, 0xff, 0x12, 0x72, 0xae, 0x69, 0x87,
- 0x6f, 0xfa, 0x0b, 0x96, 0xcb, 0x9a, 0xb5, 0x68, 0xb8, 0x1e, 0x5d, 0x3c, 0x1a, 0x6a, 0xcb, 0xb5,
- 0x2f, 0x45, 0x3c, 0x3d, 0xdd, 0x38, 0xb0, 0x1c, 0xea, 0x1d, 0xab, 0x67, 0x59, 0xf4, 0xa8, 0xef,
- 0x0e, 0x3c, 0x83, 0x9e, 0xab, 0x96, 0xbf, 0xd8, 0xa3, 0x81, 0x9e, 0x25, 0x6b, 0x71, 0x54, 0x2d,
- 0x6f, 0xe0, 0x04, 0x56, 0x6f, 0x58, 0xcc, 0x1b, 0x4f, 0xab, 0xe0, 0x1b, 0x07, 0xb4, 0xa7, 0x0f,
- 0xd5, 0xfb, 0xe9, 0x51, 0xf5, 0x06, 0x81, 0x65, 0x2f, 0x5a, 0x4e, 0xe0, 0x07, 0x5e, 0xba, 0x92,
- 0xf6, 0xdb, 0x00, 0x97, 0x96, 0xf7, 0xfc, 0xc0, 0xd3, 0x8d, 0x60, 0xdb, 0x35, 0x77, 0x68, 0xaf,
- 0x6f, 0xeb, 0x01, 0x25, 0x87, 0x50, 0x67, 0x0f, 0x64, 0xea, 0x81, 0x3e, 0x5f, 0xb8, 0x51, 0xb8,
- 0xd9, 0x5c, 0x5a, 0x5e, 0x18, 0xf3, 0x05, 0x2e, 0x6c, 0x49, 0xa0, 0xf6, 0xd4, 0xe9, 0x49, 0xab,
- 0xae, 0xfe, 0x61, 0x28, 0x80, 0xfc, 0x5a, 0x01, 0xa6, 0x1c, 0xd7, 0xa4, 0x1d, 0x6a, 0x53, 0x23,
- 0x70, 0xbd, 0xf9, 0xe2, 0x8d, 0xd2, 0xcd, 0xe6, 0xd2, 0x37, 0xc7, 0x96, 0x98, 0xf1, 0x44, 0x0b,
- 0xf7, 0x62, 0x02, 0x6e, 0x39, 0x81, 0x77, 0xdc, 0xbe, 0xfc, 0xbd, 0x93, 0xd6, 0x0b, 0xa7, 0x27,
- 0xad, 0xa9, 0x38, 0x09, 0x13, 0x2d, 0x21, 0xbb, 0xd0, 0x0c, 0x5c, 0x9b, 0x75, 0x99, 0xe5, 0x3a,
- 0xfe, 0x7c, 0x89, 0x37, 0xec, 0xfa, 0x82, 0xe8, 0x6a, 0x26, 0x7e, 0x81, 0x8d, 0xb1, 0x85, 0xa3,
- 0xd7, 0x16, 0x76, 0x42, 0xb6, 0xf6, 0x25, 0x09, 0xdc, 0x8c, 0xca, 0x7c, 0x8c, 0xe3, 0x10, 0x0a,
- 0xb3, 0x3e, 0x35, 0x06, 0x9e, 0x15, 0x1c, 0xaf, 0xb8, 0x4e, 0x40, 0x1f, 0x05, 0xf3, 0x65, 0xde,
- 0xcb, 0x5f, 0xcc, 0x82, 0xde, 0x76, 0xcd, 0x4e, 0x92, 0xbb, 0x7d, 0xe9, 0xf4, 0xa4, 0x35, 0x9b,
- 0x2a, 0xc4, 0x34, 0x26, 0x71, 0x60, 0xce, 0xea, 0xe9, 0x5d, 0xba, 0x3d, 0xb0, 0xed, 0x0e, 0x35,
- 0x3c, 0x1a, 0xf8, 0xf3, 0x15, 0xfe, 0x08, 0x37, 0xb3, 0xe4, 0x6c, 0xba, 0x86, 0x6e, 0xdf, 0xdf,
- 0xfb, 0x80, 0x1a, 0x01, 0xd2, 0x7d, 0xea, 0x51, 0xc7, 0xa0, 0xed, 0x79, 0xf9, 0x30, 0x73, 0x1b,
- 0x29, 0x24, 0x1c, 0xc2, 0x26, 0xeb, 0xf0, 0x62, 0xdf, 0xb3, 0x5c, 0xde, 0x04, 0x5b, 0xf7, 0xfd,
- 0x7b, 0x7a, 0x8f, 0xce, 0x57, 0x6f, 0x14, 0x6e, 0x36, 0xda, 0x57, 0x25, 0xcc, 0x8b, 0xdb, 0x69,
- 0x06, 0x1c, 0xae, 0x43, 0x6e, 0x42, 0x5d, 0x15, 0xce, 0xd7, 0x6e, 0x14, 0x6e, 0x56, 0xc4, 0xd8,
- 0x51, 0x75, 0x31, 0xa4, 0x92, 0x35, 0xa8, 0xeb, 0xfb, 0xfb, 0x96, 0xc3, 0x38, 0xeb, 0xbc, 0x0b,
- 0x5f, 0xc9, 0x7a, 0xb4, 0x65, 0xc9, 0x23, 0x70, 0xd4, 0x3f, 0x0c, 0xeb, 0x92, 0x3b, 0x40, 0x7c,
- 0xea, 0x1d, 0x59, 0x06, 0x5d, 0x36, 0x0c, 0x77, 0xe0, 0x04, 0xbc, 0xed, 0x0d, 0xde, 0xf6, 0x6b,
- 0xb2, 0xed, 0xa4, 0x33, 0xc4, 0x81, 0x19, 0xb5, 0xc8, 0xdb, 0x30, 0x27, 0xbf, 0xd5, 0xa8, 0x17,
- 0x80, 0x23, 0x5d, 0x66, 0x1d, 0x89, 0x29, 0x1a, 0x0e, 0x71, 0x13, 0x13, 0x5e, 0xd1, 0x07, 0x81,
- 0xdb, 0x63, 0x90, 0x49, 0xa1, 0x3b, 0xee, 0x21, 0x75, 0xe6, 0x9b, 0x37, 0x0a, 0x37, 0xeb, 0xed,
- 0x1b, 0xa7, 0x27, 0xad, 0x57, 0x96, 0x9f, 0xc0, 0x87, 0x4f, 0x44, 0x21, 0xf7, 0xa1, 0x61, 0x3a,
- 0xfe, 0xb6, 0x6b, 0x5b, 0xc6, 0xf1, 0xfc, 0x14, 0x6f, 0xe0, 0x6b, 0xf2, 0x51, 0x1b, 0xab, 0xf7,
- 0x3a, 0x82, 0xf0, 0xf8, 0xa4, 0xf5, 0xca, 0xf0, 0x94, 0xba, 0x10, 0xd2, 0x31, 0xc2, 0x20, 0x5b,
- 0x1c, 0x70, 0xc5, 0x75, 0xf6, 0xad, 0xee, 0xfc, 0x34, 0x7f, 0x1b, 0x37, 0x46, 0x0c, 0xe8, 0xd5,
- 0x7b, 0x1d, 0xc1, 0xd7, 0x9e, 0x96, 0xe2, 0xc4, 0x5f, 0x8c, 0x10, 0x88, 0x09, 0x33, 0x6a, 0x32,
- 0x5e, 0xb1, 0x75, 0xab, 0xe7, 0xcf, 0xcf, 0xf0, 0xc1, 0xfb, 0xa3, 0x23, 0x30, 0x31, 0xce, 0xdc,
- 0xbe, 0x22, 0x1f, 0x65, 0x26, 0x51, 0xec, 0x63, 0x0a, 0xf3, 0xda, 0x5b, 0xf0, 0xe2, 0xd0, 0xdc,
- 0x40, 0xe6, 0xa0, 0x74, 0x48, 0x8f, 0xf9, 0xd4, 0xd7, 0x40, 0xf6, 0x93, 0x5c, 0x86, 0xca, 0x91,
- 0x6e, 0x0f, 0xe8, 0x7c, 0x91, 0x97, 0x89, 0x3f, 0x3f, 0x53, 0x7c, 0xb3, 0xa0, 0xfd, 0x8d, 0x12,
- 0x4c, 0xa9, 0x19, 0xa7, 0x63, 0x39, 0x87, 0xe4, 0x1d, 0x28, 0xd9, 0x6e, 0x57, 0xce, 0x9b, 0x3f,
- 0x37, 0xf6, 0x2c, 0xb6, 0xe9, 0x76, 0xdb, 0xb5, 0xd3, 0x93, 0x56, 0x69, 0xd3, 0xed, 0x22, 0x43,
- 0x24, 0x06, 0x54, 0x0e, 0xf5, 0xfd, 0x43, 0x9d, 0xb7, 0xa1, 0xb9, 0xd4, 0x1e, 0x1b, 0xfa, 0x2e,
- 0x43, 0x61, 0x6d, 0x6d, 0x37, 0x4e, 0x4f, 0x5a, 0x15, 0xfe, 0x17, 0x05, 0x36, 0x71, 0xa1, 0xb1,
- 0x67, 0xeb, 0xc6, 0xe1, 0x81, 0x6b, 0xd3, 0xf9, 0x52, 0x4e, 0x41, 0x6d, 0x85, 0x24, 0x5e, 0x73,
- 0xf8, 0x17, 0x23, 0x19, 0xc4, 0x80, 0xea, 0xc0, 0xf4, 0x2d, 0xe7, 0x50, 0xce, 0x81, 0x6f, 0x8d,
- 0x2d, 0x6d, 0x77, 0x95, 0x3f, 0x13, 0x9c, 0x9e, 0xb4, 0xaa, 0xe2, 0x37, 0x4a, 0x68, 0xed, 0x0f,
- 0xa6, 0x60, 0x46, 0xbd, 0xa4, 0x07, 0xd4, 0x0b, 0xe8, 0x23, 0x72, 0x03, 0xca, 0x0e, 0xfb, 0x34,
- 0xf9, 0x4b, 0x6e, 0x4f, 0xc9, 0xe1, 0x52, 0xe6, 0x9f, 0x24, 0xa7, 0xb0, 0x96, 0x89, 0xa1, 0x22,
- 0x3b, 0x7c, 0xfc, 0x96, 0x75, 0x38, 0x8c, 0x68, 0x99, 0xf8, 0x8d, 0x12, 0x9a, 0xbc, 0x07, 0x65,
- 0xfe, 0xf0, 0xa2, 0xab, 0xbf, 0x3a, 0xbe, 0x08, 0xf6, 0xe8, 0x75, 0xf6, 0x04, 0xfc, 0xc1, 0x39,
- 0x28, 0x1b, 0x8a, 0x03, 0x73, 0x5f, 0x76, 0xec, 0xcf, 0xe5, 0xe8, 0xd8, 0x35, 0x31, 0x14, 0x77,
- 0x57, 0xd7, 0x90, 0x21, 0x92, 0xbf, 0x54, 0x80, 0x17, 0x0d, 0xd7, 0x09, 0x74, 0xa6, 0x67, 0xa8,
- 0x45, 0x76, 0xbe, 0xc2, 0xe5, 0xdc, 0x19, 0x5b, 0xce, 0x4a, 0x1a, 0xb1, 0xfd, 0x12, 0x5b, 0x33,
- 0x86, 0x8a, 0x71, 0x58, 0x36, 0xf9, 0x2b, 0x05, 0x78, 0x89, 0xcd, 0xe5, 0x43, 0xcc, 0x7c, 0x05,
- 0x9a, 0x6c, 0xab, 0xae, 0x9e, 0x9e, 0xb4, 0x5e, 0xda, 0xc8, 0x12, 0x86, 0xd9, 0x6d, 0x60, 0xad,
- 0xbb, 0xa4, 0x0f, 0xab, 0x25, 0x7c, 0x75, 0x6b, 0x2e, 0x6d, 0x4e, 0x52, 0xd5, 0x69, 0x7f, 0x4e,
- 0x0e, 0xe5, 0x2c, 0xcd, 0x0e, 0xb3, 0x5a, 0x41, 0x6e, 0x41, 0xed, 0xc8, 0xb5, 0x07, 0x3d, 0xea,
- 0xcf, 0xd7, 0xf9, 0x14, 0x7b, 0x2d, 0x6b, 0x8a, 0x7d, 0xc0, 0x59, 0xda, 0xb3, 0x12, 0xbe, 0x26,
- 0xfe, 0xfb, 0xa8, 0xea, 0x12, 0x0b, 0xaa, 0xb6, 0xd5, 0xb3, 0x02, 0x9f, 0x2f, 0x9c, 0xcd, 0xa5,
- 0x5b, 0x63, 0x3f, 0x96, 0xf8, 0x44, 0x37, 0x39, 0x98, 0xf8, 0x6a, 0xc4, 0x6f, 0x94, 0x02, 0xd8,
- 0x54, 0xe8, 0x1b, 0xba, 0x2d, 0x16, 0xd6, 0xe6, 0xd2, 0xd7, 0xc6, 0xff, 0x6c, 0x18, 0x4a, 0x7b,
- 0x5a, 0x3e, 0x53, 0x85, 0xff, 0x45, 0x81, 0x4d, 0x7e, 0x01, 0x66, 0x12, 0x6f, 0xd3, 0x9f, 0x6f,
- 0xf2, 0xde, 0x79, 0x35, 0xab, 0x77, 0x42, 0xae, 0x68, 0xe5, 0x49, 0x8c, 0x10, 0x1f, 0x53, 0x60,
- 0xe4, 0x2e, 0xd4, 0x7d, 0xcb, 0xa4, 0x86, 0xee, 0xf9, 0xf3, 0x53, 0x67, 0x01, 0x9e, 0x93, 0xc0,
- 0xf5, 0x8e, 0xac, 0x86, 0x21, 0x00, 0x59, 0x00, 0xe8, 0xeb, 0x5e, 0x60, 0x09, 0x45, 0x75, 0x9a,
- 0x2b, 0x4d, 0x33, 0xa7, 0x27, 0x2d, 0xd8, 0x0e, 0x4b, 0x31, 0xc6, 0xc1, 0xf8, 0x59, 0xdd, 0x0d,
- 0xa7, 0x3f, 0x08, 0xc4, 0xc2, 0xda, 0x10, 0xfc, 0x9d, 0xb0, 0x14, 0x63, 0x1c, 0xe4, 0xb7, 0x0a,
- 0xf0, 0xb9, 0xe8, 0xef, 0xf0, 0x47, 0x36, 0x3b, 0xf1, 0x8f, 0xac, 0x75, 0x7a, 0xd2, 0xfa, 0x5c,
- 0x67, 0xb4, 0x48, 0x7c, 0x52, 0x7b, 0xc8, 0xc7, 0x05, 0x98, 0x19, 0xf4, 0x4d, 0x3d, 0xa0, 0x9d,
- 0x80, 0xed, 0x78, 0xba, 0xc7, 0xf3, 0x73, 0xbc, 0x89, 0xeb, 0xe3, 0xcf, 0x82, 0x09, 0xb8, 0xe8,
- 0x35, 0x27, 0xcb, 0x31, 0x25, 0x56, 0x7b, 0x07, 0xa6, 0x97, 0x07, 0xc1, 0x81, 0xeb, 0x59, 0x1f,
- 0x72, 0xf5, 0x9f, 0xac, 0x41, 0x25, 0xe0, 0x6a, 0x9c, 0xd0, 0x10, 0xbe, 0x90, 0xf5, 0xd2, 0x85,
- 0x4a, 0x7d, 0x97, 0x1e, 0x2b, 0xbd, 0x44, 0xac, 0xd4, 0x42, 0xad, 0x13, 0xd5, 0xb5, 0x3f, 0x53,
- 0x80, 0x5a, 0x5b, 0x37, 0x0e, 0xdd, 0xfd, 0x7d, 0xf2, 0x2e, 0xd4, 0x2d, 0x27, 0xa0, 0xde, 0x91,
- 0x6e, 0x4b, 0xd8, 0x85, 0x18, 0x6c, 0xb8, 0x21, 0x8c, 0x1e, 0x8f, 0xed, 0xbe, 0x98, 0xa0, 0xd5,
- 0x81, 0xdc, 0xb5, 0x70, 0xcd, 0x78, 0x43, 0x62, 0x60, 0x88, 0x46, 0x5a, 0x50, 0xf1, 0x03, 0xda,
- 0xf7, 0xf9, 0x1a, 0x38, 0x2d, 0x9a, 0xd1, 0x61, 0x05, 0x28, 0xca, 0xb5, 0xbf, 0x5e, 0x80, 0x46,
- 0x5b, 0xf7, 0x2d, 0x83, 0x3d, 0x25, 0x59, 0x81, 0xf2, 0xc0, 0xa7, 0xde, 0xf9, 0x9e, 0x8d, 0x2f,
- 0x5b, 0xbb, 0x3e, 0xf5, 0x90, 0x57, 0x26, 0xf7, 0xa1, 0xde, 0xd7, 0x7d, 0xff, 0xa1, 0xeb, 0x99,
- 0x72, 0xe9, 0x3d, 0x23, 0x90, 0xd8, 0x26, 0xc8, 0xaa, 0x18, 0x82, 0x68, 0x4d, 0x88, 0x74, 0x0f,
- 0xed, 0xf7, 0x0a, 0x70, 0xa9, 0x3d, 0xd8, 0xdf, 0xa7, 0x9e, 0xd4, 0x8a, 0xa5, 0xbe, 0x49, 0xa1,
- 0xe2, 0x51, 0xd3, 0xf2, 0x65, 0xdb, 0x57, 0xc7, 0x1e, 0x28, 0xc8, 0x50, 0xa4, 0x7a, 0xcb, 0xfb,
- 0x8b, 0x17, 0xa0, 0x40, 0x27, 0x03, 0x68, 0x7c, 0x40, 0xd9, 0x6e, 0x9c, 0xea, 0x3d, 0xf9, 0x74,
- 0xb7, 0xc7, 0x16, 0x75, 0x87, 0x06, 0x1d, 0x8e, 0x14, 0xd7, 0xa6, 0xc3, 0x42, 0x8c, 0x24, 0x69,
- 0xbf, 0x5d, 0x81, 0xa9, 0x15, 0xb7, 0xb7, 0x67, 0x39, 0xd4, 0xbc, 0x65, 0x76, 0x29, 0x79, 0x1f,
- 0xca, 0xd4, 0xec, 0x52, 0xf9, 0xb4, 0xe3, 0x2b, 0x1e, 0x0c, 0x2c, 0x52, 0x9f, 0xd8, 0x3f, 0xe4,
- 0xc0, 0x64, 0x13, 0x66, 0xf6, 0x3d, 0xb7, 0x27, 0xe6, 0xf2, 0x9d, 0xe3, 0xbe, 0xd4, 0x9d, 0xdb,
- 0x3f, 0xaa, 0x3e, 0x9c, 0xb5, 0x04, 0xf5, 0xf1, 0x49, 0x0b, 0xa2, 0x7f, 0x98, 0xaa, 0x4b, 0xde,
- 0x85, 0xf9, 0xa8, 0x24, 0x9c, 0xd4, 0x56, 0xd8, 0x76, 0x86, 0xeb, 0x4e, 0x95, 0xf6, 0x2b, 0xa7,
- 0x27, 0xad, 0xf9, 0xb5, 0x11, 0x3c, 0x38, 0xb2, 0x36, 0x9b, 0x2a, 0xe6, 0x22, 0xa2, 0x58, 0x68,
- 0xa4, 0xca, 0x34, 0xa1, 0x15, 0x8c, 0xef, 0xfb, 0xd6, 0x52, 0x22, 0x70, 0x48, 0x28, 0x59, 0x83,
- 0xa9, 0xc0, 0x8d, 0xf5, 0x57, 0x85, 0xf7, 0x97, 0xa6, 0x0c, 0x15, 0x3b, 0xee, 0xc8, 0xde, 0x4a,
- 0xd4, 0x23, 0x08, 0x57, 0xd4, 0xff, 0x54, 0x4f, 0x55, 0x79, 0x4f, 0x5d, 0x3b, 0x3d, 0x69, 0x5d,
- 0xd9, 0xc9, 0xe4, 0xc0, 0x11, 0x35, 0xc9, 0x2f, 0x17, 0x60, 0x46, 0x91, 0x64, 0x1f, 0xd5, 0x26,
- 0xd9, 0x47, 0x84, 0x8d, 0x88, 0x9d, 0x84, 0x00, 0x4c, 0x09, 0xd4, 0x7e, 0xa7, 0x0a, 0x8d, 0x70,
- 0xaa, 0x27, 0x9f, 0x87, 0x0a, 0x37, 0x41, 0x48, 0x0d, 0x3e, 0x5c, 0xc3, 0xb9, 0xa5, 0x02, 0x05,
- 0x8d, 0x7c, 0x01, 0x6a, 0x86, 0xdb, 0xeb, 0xe9, 0x8e, 0xc9, 0xcd, 0x4a, 0x8d, 0x76, 0x93, 0xa9,
- 0x2e, 0x2b, 0xa2, 0x08, 0x15, 0x8d, 0xbc, 0x02, 0x65, 0xdd, 0xeb, 0x0a, 0x0b, 0x4f, 0x43, 0xcc,
- 0x47, 0xcb, 0x5e, 0xd7, 0x47, 0x5e, 0x4a, 0xbe, 0x02, 0x25, 0xea, 0x1c, 0xcd, 0x97, 0x47, 0xeb,
- 0x46, 0xb7, 0x9c, 0xa3, 0x07, 0xba, 0xd7, 0x6e, 0xca, 0x36, 0x94, 0x6e, 0x39, 0x47, 0xc8, 0xea,
- 0x90, 0x4d, 0xa8, 0x51, 0xe7, 0x88, 0xbd, 0x7b, 0x69, 0x7a, 0xf9, 0x91, 0x11, 0xd5, 0x19, 0x8b,
- 0xdc, 0x26, 0x84, 0x1a, 0x96, 0x2c, 0x46, 0x05, 0x41, 0xbe, 0x01, 0x53, 0x42, 0xd9, 0xda, 0x62,
- 0xef, 0xc4, 0x9f, 0xaf, 0x72, 0xc8, 0xd6, 0x68, 0x6d, 0x8d, 0xf3, 0x45, 0xa6, 0xae, 0x58, 0xa1,
- 0x8f, 0x09, 0x28, 0xf2, 0x0d, 0x68, 0xa8, 0x9d, 0xb1, 0x7a, 0xb3, 0x99, 0x56, 0x22, 0xb5, 0x9d,
- 0x46, 0xfa, 0xad, 0x81, 0xe5, 0xd1, 0x1e, 0x75, 0x02, 0xbf, 0xfd, 0xa2, 0xb2, 0x1b, 0x28, 0xaa,
- 0x8f, 0x11, 0x1a, 0xd9, 0x1b, 0x36, 0x77, 0x09, 0x5b, 0xcd, 0xe7, 0x47, 0xcc, 0xea, 0x63, 0xd8,
- 0xba, 0xbe, 0x09, 0xb3, 0xa1, 0x3d, 0x4a, 0x9a, 0x34, 0x84, 0xf5, 0xe6, 0x4b, 0xac, 0xfa, 0x46,
- 0x92, 0xf4, 0xf8, 0xa4, 0xf5, 0x6a, 0x86, 0x51, 0x23, 0x62, 0xc0, 0x34, 0x18, 0xf9, 0x10, 0x66,
- 0x3c, 0xaa, 0x9b, 0x96, 0x43, 0x7d, 0x7f, 0xdb, 0x73, 0xf7, 0xf2, 0x6b, 0x9e, 0x1c, 0x45, 0x0c,
- 0x7b, 0x4c, 0x20, 0x63, 0x4a, 0x12, 0x79, 0x08, 0xd3, 0xb6, 0x75, 0x44, 0x23, 0xd1, 0xcd, 0x89,
- 0x88, 0x7e, 0xf1, 0xf4, 0xa4, 0x35, 0xbd, 0x19, 0x07, 0xc6, 0xa4, 0x1c, 0xed, 0xef, 0x54, 0x60,
- 0x78, 0xf3, 0x95, 0x1c, 0x29, 0x85, 0x49, 0x8f, 0x94, 0xf4, 0x5b, 0x14, 0x6b, 0xc6, 0x9b, 0xb2,
- 0xda, 0x04, 0xde, 0x64, 0xc6, 0x68, 0x2c, 0x4d, 0x7a, 0x34, 0x3e, 0x37, 0x13, 0xc6, 0xf0, 0xb0,
- 0xad, 0x7e, 0x72, 0xc3, 0xb6, 0xf6, 0x8c, 0x86, 0xed, 0x77, 0xca, 0x30, 0xb3, 0xaa, 0xd3, 0x9e,
- 0xeb, 0x3c, 0x75, 0xff, 0x5d, 0x78, 0x2e, 0xf6, 0xdf, 0x37, 0xa1, 0xee, 0xd1, 0xbe, 0x6d, 0x19,
- 0xba, 0x50, 0xb3, 0xa5, 0xbd, 0x1b, 0x65, 0x19, 0x86, 0xd4, 0x11, 0x76, 0x97, 0xd2, 0x73, 0x69,
- 0x77, 0x29, 0x7f, 0xf2, 0x76, 0x17, 0xed, 0x97, 0x8b, 0xc0, 0x55, 0x52, 0x72, 0x03, 0xca, 0x4c,
- 0xdd, 0x4a, 0x5b, 0xfb, 0xf8, 0xd7, 0xc2, 0x29, 0xe4, 0x1a, 0x14, 0x03, 0x57, 0x4e, 0x37, 0x20,
- 0xe9, 0xc5, 0x1d, 0x17, 0x8b, 0x81, 0x4b, 0x3e, 0x04, 0x30, 0x5c, 0xc7, 0xb4, 0x94, 0x1b, 0x28,
- 0xdf, 0x83, 0xad, 0xb9, 0xde, 0x43, 0xdd, 0x33, 0x57, 0x42, 0x44, 0xb1, 0xf3, 0x8e, 0xfe, 0x63,
- 0x4c, 0x1a, 0x79, 0x0b, 0xaa, 0xae, 0xb3, 0x36, 0xb0, 0x6d, 0xde, 0xa1, 0x8d, 0xf6, 0x8f, 0x9d,
- 0x9e, 0xb4, 0xaa, 0xf7, 0x79, 0xc9, 0xe3, 0x93, 0xd6, 0x55, 0xb1, 0x93, 0x61, 0xff, 0xde, 0xf1,
- 0xac, 0xc0, 0x72, 0xba, 0xe1, 0x46, 0x54, 0x56, 0xd3, 0x7e, 0xb5, 0x00, 0xcd, 0x35, 0xeb, 0x11,
- 0x35, 0xdf, 0xb1, 0x1c, 0xd3, 0x7d, 0x48, 0x10, 0xaa, 0x36, 0x75, 0xba, 0xc1, 0xc1, 0x98, 0x3b,
- 0x45, 0x61, 0x8f, 0xe1, 0x08, 0x28, 0x91, 0xc8, 0x22, 0x34, 0xc4, 0x3e, 0xc3, 0x72, 0xba, 0xbc,
- 0x0f, 0xeb, 0xd1, 0x4c, 0xdf, 0x51, 0x04, 0x8c, 0x78, 0xb4, 0x63, 0x78, 0x71, 0xa8, 0x1b, 0x88,
- 0x09, 0xe5, 0x40, 0xef, 0xaa, 0x45, 0x65, 0x6d, 0xec, 0x0e, 0xde, 0xd1, 0xbb, 0xb1, 0xce, 0xe5,
- 0xda, 0xdc, 0x8e, 0xce, 0xb4, 0x39, 0x86, 0xae, 0xfd, 0x61, 0x01, 0xea, 0x6b, 0x03, 0xc7, 0xe0,
- 0x9b, 0xf1, 0xa7, 0x5b, 0x81, 0x95, 0x6a, 0x58, 0xcc, 0x54, 0x0d, 0x07, 0x50, 0x3d, 0x7c, 0x18,
- 0xaa, 0x8e, 0xcd, 0xa5, 0xad, 0xf1, 0x47, 0x85, 0x6c, 0xd2, 0xc2, 0x5d, 0x8e, 0x27, 0x9c, 0x94,
- 0x33, 0xb2, 0x41, 0xd5, 0xbb, 0xef, 0x70, 0xa1, 0x52, 0xd8, 0xb5, 0xaf, 0x40, 0x33, 0xc6, 0x76,
- 0x2e, 0x7f, 0xc5, 0xdf, 0x2d, 0x43, 0x75, 0xbd, 0xd3, 0x59, 0xde, 0xde, 0x20, 0xaf, 0x43, 0x53,
- 0xfa, 0xaf, 0xee, 0x45, 0x7d, 0x10, 0xba, 0x2f, 0x3b, 0x11, 0x09, 0xe3, 0x7c, 0x4c, 0xf1, 0xf6,
- 0xa8, 0x6e, 0xf7, 0xe4, 0xc7, 0x12, 0x2a, 0xde, 0xc8, 0x0a, 0x51, 0xd0, 0x88, 0x0e, 0x33, 0x6c,
- 0x2f, 0xcf, 0xba, 0x50, 0xec, 0xd3, 0xe5, 0x67, 0x73, 0xc6, 0x9d, 0x3c, 0x5f, 0x60, 0x76, 0x13,
- 0x00, 0x98, 0x02, 0x24, 0x6f, 0x42, 0x5d, 0x1f, 0x04, 0x07, 0x7c, 0xab, 0x24, 0xbe, 0x8d, 0x57,
- 0xb8, 0x7b, 0x4f, 0x96, 0x3d, 0x3e, 0x69, 0x4d, 0xdd, 0xc5, 0xf6, 0xeb, 0xea, 0x3f, 0x86, 0xdc,
- 0xac, 0x71, 0xca, 0x36, 0x20, 0x1b, 0x57, 0x39, 0x77, 0xe3, 0xb6, 0x13, 0x00, 0x98, 0x02, 0x24,
- 0xef, 0xc1, 0xd4, 0x21, 0x3d, 0x0e, 0xf4, 0x3d, 0x29, 0xa0, 0x7a, 0x1e, 0x01, 0x73, 0x4c, 0x59,
- 0xbf, 0x1b, 0xab, 0x8e, 0x09, 0x30, 0xe2, 0xc3, 0xe5, 0x43, 0xea, 0xed, 0x51, 0xcf, 0x95, 0x76,
- 0x06, 0x29, 0xa4, 0x76, 0x1e, 0x21, 0xf3, 0xa7, 0x27, 0xad, 0xcb, 0x77, 0x33, 0x60, 0x30, 0x13,
- 0x5c, 0xfb, 0xdf, 0x45, 0x98, 0x5d, 0x17, 0x01, 0x04, 0xae, 0x27, 0x34, 0x0f, 0x72, 0x15, 0x4a,
- 0x5e, 0x7f, 0xc0, 0x47, 0x4e, 0x49, 0xb8, 0x08, 0x70, 0x7b, 0x17, 0x59, 0x19, 0x79, 0x17, 0xea,
- 0xa6, 0x9c, 0x32, 0xa4, 0x99, 0x63, 0x2c, 0x93, 0x94, 0xfa, 0x87, 0x21, 0x1a, 0xdb, 0xd3, 0xf5,
- 0xfc, 0x6e, 0xc7, 0xfa, 0x90, 0xca, 0x9d, 0x3f, 0xdf, 0xd3, 0x6d, 0x89, 0x22, 0x54, 0x34, 0xb6,
- 0xaa, 0x1e, 0xd2, 0x63, 0xb1, 0xef, 0x2d, 0x47, 0xab, 0xea, 0x5d, 0x59, 0x86, 0x21, 0x95, 0xb4,
- 0xd4, 0xc7, 0xc2, 0x46, 0x41, 0x59, 0xd8, 0x6c, 0x1e, 0xb0, 0x02, 0xf9, 0xdd, 0xb0, 0x29, 0xf3,
- 0x03, 0x2b, 0x08, 0xa8, 0x27, 0x5f, 0xe3, 0x58, 0x53, 0xe6, 0x1d, 0x8e, 0x80, 0x12, 0x89, 0xfc,
- 0x04, 0x34, 0x38, 0x78, 0xdb, 0x76, 0xf7, 0xf8, 0x8b, 0x6b, 0x08, 0xeb, 0xcd, 0x03, 0x55, 0x88,
- 0x11, 0x5d, 0xfb, 0xa3, 0x22, 0x5c, 0x59, 0xa7, 0x81, 0xd0, 0x6a, 0x56, 0x69, 0xdf, 0x76, 0x8f,
- 0x99, 0x3e, 0x8d, 0xf4, 0x5b, 0xe4, 0x6d, 0x00, 0xcb, 0xdf, 0xeb, 0x1c, 0x19, 0xfc, 0x3b, 0x10,
- 0xdf, 0xf0, 0x0d, 0xf9, 0x49, 0xc2, 0x46, 0xa7, 0x2d, 0x29, 0x8f, 0x13, 0xff, 0x30, 0x56, 0x27,
- 0xda, 0x48, 0x17, 0x9f, 0xb0, 0x91, 0xee, 0x00, 0xf4, 0x23, 0xad, 0xbc, 0xc4, 0x39, 0x7f, 0x5a,
- 0x89, 0x39, 0x8f, 0x42, 0x1e, 0x83, 0xc9, 0xa3, 0x27, 0x3b, 0x30, 0x67, 0xd2, 0x7d, 0x7d, 0x60,
- 0x07, 0xe1, 0x4e, 0x42, 0x7e, 0xc4, 0x67, 0xdf, 0x8c, 0x84, 0xc1, 0x0d, 0xab, 0x29, 0x24, 0x1c,
- 0xc2, 0xd6, 0xfe, 0x5e, 0x09, 0xae, 0xad, 0xd3, 0x20, 0xb4, 0xad, 0xc9, 0xd9, 0xb1, 0xd3, 0xa7,
- 0x06, 0x7b, 0x0b, 0x1f, 0x17, 0xa0, 0x6a, 0xeb, 0x7b, 0xd4, 0x66, 0xab, 0x17, 0x7b, 0x9a, 0xf7,
- 0xc7, 0x5e, 0x08, 0x46, 0x4b, 0x59, 0xd8, 0xe4, 0x12, 0x52, 0x4b, 0x83, 0x28, 0x44, 0x29, 0x9e,
- 0x4d, 0xea, 0x86, 0x3d, 0xf0, 0x03, 0xea, 0x6d, 0xbb, 0x5e, 0x20, 0xf5, 0xc9, 0x70, 0x52, 0x5f,
- 0x89, 0x48, 0x18, 0xe7, 0x23, 0x4b, 0x00, 0x86, 0x6d, 0x51, 0x27, 0xe0, 0xb5, 0xc4, 0x77, 0x45,
- 0xd4, 0xfb, 0x5d, 0x09, 0x29, 0x18, 0xe3, 0x62, 0xa2, 0x7a, 0xae, 0x63, 0x05, 0xae, 0x10, 0x55,
- 0x4e, 0x8a, 0xda, 0x8a, 0x48, 0x18, 0xe7, 0xe3, 0xd5, 0x68, 0xe0, 0x59, 0x86, 0xcf, 0xab, 0x55,
- 0x52, 0xd5, 0x22, 0x12, 0xc6, 0xf9, 0xd8, 0x9a, 0x17, 0x7b, 0xfe, 0x73, 0xad, 0x79, 0xbf, 0xd9,
- 0x80, 0xeb, 0x89, 0x6e, 0x0d, 0xf4, 0x80, 0xee, 0x0f, 0xec, 0x0e, 0x0d, 0xd4, 0x0b, 0x1c, 0x73,
- 0x2d, 0xfc, 0xf3, 0xd1, 0x7b, 0x17, 0x61, 0x4b, 0xc6, 0x64, 0xde, 0xfb, 0x50, 0x03, 0xcf, 0xf4,
- 0xee, 0x17, 0xa1, 0xe1, 0xe8, 0x81, 0xcf, 0x3f, 0x5c, 0xf9, 0x8d, 0x86, 0x6a, 0xd8, 0x3d, 0x45,
- 0xc0, 0x88, 0x87, 0x6c, 0xc3, 0x65, 0xd9, 0xc5, 0xb7, 0x1e, 0xf5, 0x5d, 0x2f, 0xa0, 0x9e, 0xa8,
- 0x2b, 0x97, 0x53, 0x59, 0xf7, 0xf2, 0x56, 0x06, 0x0f, 0x66, 0xd6, 0x24, 0x5b, 0x70, 0xc9, 0x10,
- 0xa1, 0x1c, 0xd4, 0x76, 0x75, 0x53, 0x01, 0x0a, 0x53, 0x66, 0xb8, 0x35, 0x5a, 0x19, 0x66, 0xc1,
- 0xac, 0x7a, 0xe9, 0xd1, 0x5c, 0x1d, 0x6b, 0x34, 0xd7, 0xc6, 0x19, 0xcd, 0xf5, 0xf1, 0x46, 0x73,
- 0xe3, 0x6c, 0xa3, 0x99, 0xf5, 0x3c, 0x1b, 0x47, 0xd4, 0x63, 0xea, 0x89, 0x58, 0x61, 0x63, 0x91,
- 0x42, 0x61, 0xcf, 0x77, 0x32, 0x78, 0x30, 0xb3, 0x26, 0xd9, 0x83, 0x6b, 0xa2, 0xfc, 0x96, 0x63,
- 0x78, 0xc7, 0x7d, 0xb6, 0xf0, 0xc4, 0x70, 0x9b, 0x09, 0x5b, 0xf2, 0xb5, 0xce, 0x48, 0x4e, 0x7c,
- 0x02, 0x0a, 0xf9, 0x59, 0x98, 0x16, 0x6f, 0x69, 0x4b, 0xef, 0x73, 0x58, 0x11, 0x37, 0xf4, 0x92,
- 0x84, 0x9d, 0x5e, 0x89, 0x13, 0x31, 0xc9, 0x4b, 0x96, 0x61, 0xb6, 0x7f, 0x64, 0xb0, 0x9f, 0x1b,
- 0xfb, 0xf7, 0x28, 0x35, 0xa9, 0xc9, 0x1d, 0x95, 0x8d, 0xf6, 0xcb, 0xca, 0xba, 0xb3, 0x9d, 0x24,
- 0x63, 0x9a, 0x9f, 0xbc, 0x09, 0x53, 0x7e, 0xa0, 0x7b, 0x81, 0x34, 0xe0, 0xce, 0xcf, 0x88, 0xb8,
- 0x2a, 0x65, 0xdf, 0xec, 0xc4, 0x68, 0x98, 0xe0, 0xcc, 0x5c, 0x2f, 0x66, 0x2f, 0x6e, 0xbd, 0xc8,
- 0x33, 0x5b, 0xfd, 0xb3, 0x22, 0xdc, 0x58, 0xa7, 0xc1, 0x96, 0xeb, 0x48, 0xf3, 0x77, 0xd6, 0xb2,
- 0x7f, 0x26, 0xeb, 0x77, 0x72, 0xd1, 0x2e, 0x4e, 0x74, 0xd1, 0x2e, 0x4d, 0x68, 0xd1, 0x2e, 0x5f,
- 0xe0, 0xa2, 0xfd, 0x0f, 0x8a, 0xf0, 0x72, 0xa2, 0x27, 0xb7, 0x5d, 0x53, 0x4d, 0xf8, 0x9f, 0x75,
- 0xe0, 0x19, 0x3a, 0xf0, 0xb1, 0xd0, 0x3b, 0xb9, 0x03, 0x33, 0xa5, 0xf1, 0x7c, 0x94, 0xd6, 0x78,
- 0xde, 0xcb, 0xb3, 0xf2, 0x65, 0x48, 0x38, 0xd3, 0x8a, 0x77, 0x07, 0x88, 0x27, 0xdd, 0xad, 0xc2,
- 0xf4, 0x13, 0x53, 0x7a, 0xc2, 0xc0, 0x4d, 0x1c, 0xe2, 0xc0, 0x8c, 0x5a, 0xa4, 0x03, 0x2f, 0xf9,
- 0xd4, 0x09, 0x2c, 0x87, 0xda, 0x49, 0x38, 0xa1, 0x0d, 0xbd, 0x2a, 0xe1, 0x5e, 0xea, 0x64, 0x31,
- 0x61, 0x76, 0xdd, 0x3c, 0xf3, 0xc0, 0xbf, 0x02, 0xae, 0x72, 0x8a, 0xae, 0x99, 0x98, 0xc6, 0xf2,
- 0x71, 0x5a, 0x63, 0x79, 0x3f, 0xff, 0x7b, 0x1b, 0x4f, 0x5b, 0x59, 0x02, 0xe0, 0x6f, 0x21, 0xae,
- 0xae, 0x84, 0x8b, 0x34, 0x86, 0x14, 0x8c, 0x71, 0xb1, 0x05, 0x48, 0xf5, 0x73, 0x5c, 0x53, 0x09,
- 0x17, 0xa0, 0x4e, 0x9c, 0x88, 0x49, 0xde, 0x91, 0xda, 0x4e, 0x65, 0x6c, 0x6d, 0xe7, 0x0e, 0x90,
- 0x84, 0xe1, 0x51, 0xe0, 0x55, 0x93, 0x71, 0xc3, 0x1b, 0x43, 0x1c, 0x98, 0x51, 0x6b, 0xc4, 0x50,
- 0xae, 0x4d, 0x76, 0x28, 0xd7, 0xc7, 0x1f, 0xca, 0xe4, 0x7d, 0xb8, 0xca, 0x45, 0xc9, 0xfe, 0x49,
- 0x02, 0x0b, 0xbd, 0xe7, 0x47, 0x24, 0xf0, 0x55, 0x1c, 0xc5, 0x88, 0xa3, 0x31, 0xd8, 0xfb, 0x31,
- 0x3c, 0x6a, 0x32, 0xe1, 0xba, 0x3d, 0x5a, 0x27, 0x5a, 0xc9, 0xe0, 0xc1, 0xcc, 0x9a, 0x6c, 0x88,
- 0x05, 0x6c, 0x18, 0xea, 0x7b, 0x36, 0x35, 0x65, 0xdc, 0x74, 0x38, 0xc4, 0x76, 0x36, 0x3b, 0x92,
- 0x82, 0x31, 0xae, 0x2c, 0x35, 0x65, 0xea, 0x9c, 0x6a, 0xca, 0x3a, 0xb7, 0xd2, 0xef, 0x27, 0xb4,
- 0x21, 0xa9, 0xeb, 0x84, 0x91, 0xf0, 0x2b, 0x69, 0x06, 0x1c, 0xae, 0xc3, 0xb5, 0x44, 0xc3, 0xb3,
- 0xfa, 0x81, 0x9f, 0xc4, 0x9a, 0x49, 0x69, 0x89, 0x19, 0x3c, 0x98, 0x59, 0x93, 0xe9, 0xe7, 0x07,
- 0x54, 0xb7, 0x83, 0x83, 0x24, 0xe0, 0x6c, 0x52, 0x3f, 0xbf, 0x3d, 0xcc, 0x82, 0x59, 0xf5, 0x32,
- 0x17, 0xa4, 0xb9, 0xe7, 0x53, 0xad, 0xfa, 0x95, 0x12, 0x5c, 0x5d, 0xa7, 0x41, 0x18, 0x52, 0xf6,
- 0x99, 0x19, 0xe5, 0x13, 0x30, 0xa3, 0xfc, 0x46, 0x05, 0x2e, 0xad, 0xd3, 0x60, 0x48, 0x1b, 0xfb,
- 0xff, 0xb4, 0xfb, 0xb7, 0xe0, 0x52, 0x14, 0xc5, 0xd8, 0x09, 0x5c, 0x4f, 0xac, 0xe5, 0xa9, 0xdd,
- 0x72, 0x67, 0x98, 0x05, 0xb3, 0xea, 0x91, 0x6f, 0xc0, 0xcb, 0x7c, 0xa9, 0x77, 0xba, 0xc2, 0x3e,
- 0x2b, 0x8c, 0x09, 0xb1, 0x73, 0x38, 0x2d, 0x09, 0xf9, 0x72, 0x27, 0x9b, 0x0d, 0x47, 0xd5, 0x27,
- 0xdf, 0x86, 0xa9, 0xbe, 0xd5, 0xa7, 0xb6, 0xe5, 0x70, 0xfd, 0x2c, 0x77, 0xf0, 0xcf, 0x76, 0x0c,
- 0x2c, 0xda, 0xc0, 0xc5, 0x4b, 0x31, 0x21, 0x30, 0x73, 0xa4, 0xd6, 0x2f, 0x70, 0xa4, 0xfe, 0x8f,
- 0x22, 0xd4, 0xd6, 0x3d, 0x77, 0xd0, 0x6f, 0x1f, 0x93, 0x2e, 0x54, 0x1f, 0x72, 0xe7, 0x99, 0x74,
- 0x4d, 0x8d, 0x7f, 0x12, 0x40, 0xf8, 0xe0, 0x22, 0x95, 0x48, 0xfc, 0x47, 0x09, 0xcf, 0x06, 0xf1,
- 0x21, 0x3d, 0xa6, 0xa6, 0xf4, 0xa1, 0x85, 0x83, 0xf8, 0x2e, 0x2b, 0x44, 0x41, 0x23, 0x3d, 0x98,
- 0xd5, 0x6d, 0xdb, 0x7d, 0x48, 0xcd, 0x4d, 0x3d, 0xe0, 0x7e, 0x6f, 0xe9, 0x5b, 0x39, 0xaf, 0x59,
- 0x9a, 0x07, 0x33, 0x2c, 0x27, 0xa1, 0x30, 0x8d, 0x4d, 0x3e, 0x80, 0x9a, 0x1f, 0xb8, 0x9e, 0x52,
- 0xb6, 0x9a, 0x4b, 0x2b, 0xe3, 0xbf, 0xf4, 0xf6, 0xd7, 0x3b, 0x02, 0x4a, 0xd8, 0xec, 0xe5, 0x1f,
- 0x54, 0x02, 0xb4, 0x5f, 0x2f, 0x00, 0xdc, 0xde, 0xd9, 0xd9, 0x96, 0xee, 0x05, 0x13, 0xca, 0xfa,
- 0x20, 0x74, 0x54, 0x8e, 0xef, 0x10, 0x4c, 0x04, 0xe0, 0x4a, 0x1f, 0xde, 0x20, 0x38, 0x40, 0x8e,
- 0x4e, 0x7e, 0x1c, 0x6a, 0x52, 0x41, 0x96, 0xdd, 0x1e, 0xc6, 0x53, 0x48, 0x25, 0x1a, 0x15, 0x5d,
- 0xfb, 0xdb, 0x45, 0x80, 0x0d, 0xd3, 0xa6, 0x1d, 0x75, 0x78, 0xa3, 0x11, 0x1c, 0x78, 0xd4, 0x3f,
- 0x70, 0x6d, 0x73, 0x4c, 0x6f, 0x2a, 0xb7, 0xf9, 0xef, 0x28, 0x10, 0x8c, 0xf0, 0x88, 0x09, 0x53,
- 0x7e, 0x40, 0xfb, 0x2a, 0x26, 0x77, 0x4c, 0x27, 0xca, 0x9c, 0xb0, 0x8b, 0x44, 0x38, 0x98, 0x40,
- 0x25, 0x3a, 0x34, 0x2d, 0xc7, 0x10, 0x1f, 0x48, 0xfb, 0x78, 0xcc, 0x81, 0x34, 0xcb, 0x76, 0x1c,
- 0x1b, 0x11, 0x0c, 0xc6, 0x31, 0xb5, 0xdf, 0x2d, 0xc2, 0x15, 0x2e, 0x8f, 0x35, 0x23, 0x11, 0x79,
- 0x4b, 0xfe, 0xe4, 0xd0, 0x41, 0xd3, 0x3f, 0x7e, 0x36, 0xd1, 0xe2, 0x9c, 0xe2, 0x16, 0x0d, 0xf4,
- 0x48, 0x9f, 0x8b, 0xca, 0x62, 0xa7, 0x4b, 0x07, 0x50, 0xf6, 0xd9, 0x7c, 0x25, 0x7a, 0xaf, 0x33,
- 0xf6, 0x10, 0xca, 0x7e, 0x00, 0x3e, 0x7b, 0x85, 0x5e, 0x63, 0x3e, 0x6b, 0x71, 0x71, 0xe4, 0x4f,
- 0x43, 0xd5, 0x0f, 0xf4, 0x60, 0xa0, 0x3e, 0xcd, 0xdd, 0x49, 0x0b, 0xe6, 0xe0, 0xd1, 0x3c, 0x22,
- 0xfe, 0xa3, 0x14, 0xaa, 0xfd, 0x6e, 0x01, 0xae, 0x65, 0x57, 0xdc, 0xb4, 0xfc, 0x80, 0xfc, 0x89,
- 0xa1, 0x6e, 0x3f, 0xe3, 0x1b, 0x67, 0xb5, 0x79, 0xa7, 0x87, 0x67, 0x11, 0x54, 0x49, 0xac, 0xcb,
- 0x03, 0xa8, 0x58, 0x01, 0xed, 0xa9, 0xfd, 0xe5, 0xfd, 0x09, 0x3f, 0x7a, 0x6c, 0x69, 0x67, 0x52,
- 0x50, 0x08, 0xd3, 0xbe, 0x53, 0x1c, 0xf5, 0xc8, 0x7c, 0xf9, 0xb0, 0x93, 0xd1, 0xdd, 0x77, 0xf3,
- 0x45, 0x77, 0x27, 0x1b, 0x34, 0x1c, 0xe4, 0xfd, 0xa7, 0x86, 0x83, 0xbc, 0xef, 0xe7, 0x0f, 0xf2,
- 0x4e, 0x75, 0xc3, 0xc8, 0x58, 0xef, 0x1f, 0x94, 0xe0, 0x95, 0x27, 0x0d, 0x1b, 0xb6, 0x9e, 0xc9,
- 0xd1, 0x99, 0x77, 0x3d, 0x7b, 0xf2, 0x38, 0x24, 0x4b, 0x50, 0xe9, 0x1f, 0xe8, 0xbe, 0x52, 0xca,
- 0xd4, 0x86, 0xa5, 0xb2, 0xcd, 0x0a, 0x1f, 0xb3, 0x49, 0x83, 0x2b, 0x73, 0xfc, 0x2f, 0x0a, 0x56,
- 0x36, 0x1d, 0xf7, 0xa8, 0xef, 0x47, 0x36, 0x81, 0x70, 0x3a, 0xde, 0x12, 0xc5, 0xa8, 0xe8, 0x24,
- 0x80, 0xaa, 0x30, 0x31, 0xcb, 0x95, 0x69, 0xfc, 0x40, 0xae, 0x8c, 0x03, 0x01, 0xd1, 0x43, 0x49,
- 0x6f, 0x85, 0x94, 0x45, 0x16, 0xa0, 0x1c, 0x44, 0xe1, 0xd9, 0x6a, 0x6b, 0x5e, 0xce, 0xd0, 0x4f,
- 0x39, 0x1f, 0xdb, 0xd8, 0xbb, 0x7b, 0xdc, 0xa8, 0x6e, 0x4a, 0xff, 0xb9, 0xe5, 0x3a, 0x5c, 0x21,
- 0x2b, 0x45, 0x1b, 0xfb, 0xfb, 0x43, 0x1c, 0x98, 0x51, 0x4b, 0xfb, 0xb7, 0x75, 0xb8, 0x92, 0x3d,
- 0x1e, 0x58, 0xbf, 0x1d, 0x51, 0xcf, 0x67, 0xd8, 0x85, 0x64, 0xbf, 0x3d, 0x10, 0xc5, 0xa8, 0xe8,
- 0x9f, 0xea, 0x80, 0xb3, 0xdf, 0x28, 0xc0, 0x55, 0x4f, 0xfa, 0x88, 0x9e, 0x45, 0xd0, 0xd9, 0xab,
- 0xc2, 0x9c, 0x31, 0x42, 0x20, 0x8e, 0x6e, 0x0b, 0xf9, 0x9b, 0x05, 0x98, 0xef, 0xa5, 0xec, 0x1c,
- 0x17, 0x78, 0x56, 0x92, 0x9f, 0x7f, 0xd8, 0x1a, 0x21, 0x0f, 0x47, 0xb6, 0x84, 0x7c, 0x1b, 0x9a,
- 0x7d, 0x36, 0x2e, 0xfc, 0x80, 0x3a, 0x86, 0x0a, 0x10, 0x1d, 0xff, 0x4b, 0xda, 0x8e, 0xb0, 0xc2,
- 0xb3, 0x52, 0x5c, 0x3f, 0x88, 0x11, 0x30, 0x2e, 0xf1, 0x39, 0x3f, 0x1c, 0x79, 0x13, 0xea, 0x3e,
- 0x0d, 0x02, 0xcb, 0xe9, 0x8a, 0xfd, 0x46, 0x43, 0x7c, 0x2b, 0x1d, 0x59, 0x86, 0x21, 0x95, 0xfc,
- 0x04, 0x34, 0xb8, 0xcb, 0x69, 0xd9, 0xeb, 0xfa, 0xf3, 0x0d, 0x1e, 0x2e, 0x36, 0x2d, 0x02, 0xe0,
- 0x64, 0x21, 0x46, 0x74, 0xf2, 0x25, 0x98, 0xda, 0xe3, 0x9f, 0xaf, 0x3c, 0x2f, 0x2f, 0x6c, 0x5c,
- 0x5c, 0x5b, 0x6b, 0xc7, 0xca, 0x31, 0xc1, 0x45, 0x96, 0x00, 0x68, 0xe8, 0x97, 0x4b, 0xdb, 0xb3,
- 0x22, 0x8f, 0x1d, 0xc6, 0xb8, 0xc8, 0xab, 0x50, 0x0a, 0x6c, 0x9f, 0xdb, 0xb0, 0xea, 0xd1, 0x16,
- 0x74, 0x67, 0xb3, 0x83, 0xac, 0x5c, 0xfb, 0xa3, 0x02, 0xcc, 0xa6, 0x8e, 0x11, 0xb1, 0x2a, 0x03,
- 0xcf, 0x96, 0xd3, 0x48, 0x58, 0x65, 0x17, 0x37, 0x91, 0x95, 0x93, 0xf7, 0xa5, 0x5a, 0x5e, 0xcc,
- 0x99, 0x1a, 0xe4, 0x9e, 0x1e, 0xf8, 0x4c, 0x0f, 0x1f, 0xd2, 0xc8, 0xb9, 0x9b, 0x2f, 0x6a, 0x8f,
- 0x5c, 0x07, 0x62, 0x6e, 0xbe, 0x88, 0x86, 0x09, 0xce, 0x94, 0xc1, 0xaf, 0x7c, 0x16, 0x83, 0x9f,
- 0xf6, 0xab, 0xc5, 0x58, 0x0f, 0x48, 0xcd, 0xfe, 0x29, 0x3d, 0xf0, 0x45, 0xb6, 0x80, 0x86, 0x8b,
- 0x7b, 0x23, 0xbe, 0xfe, 0xf1, 0xc5, 0x58, 0x52, 0xc9, 0x3b, 0xa2, 0xef, 0x4b, 0x39, 0x0f, 0x60,
- 0xef, 0x6c, 0x76, 0x44, 0x74, 0x95, 0x7a, 0x6b, 0xe1, 0x2b, 0x28, 0x5f, 0xd0, 0x2b, 0xd0, 0xfe,
- 0x45, 0x09, 0x9a, 0x77, 0xdc, 0xbd, 0x4f, 0x49, 0x04, 0x75, 0xf6, 0x32, 0x55, 0xfc, 0x04, 0x97,
- 0xa9, 0x5d, 0x78, 0x39, 0x08, 0xec, 0x0e, 0x35, 0x5c, 0xc7, 0xf4, 0x97, 0xf7, 0x03, 0xea, 0xad,
- 0x59, 0x8e, 0xe5, 0x1f, 0x50, 0x53, 0xba, 0x93, 0x3e, 0x77, 0x7a, 0xd2, 0x7a, 0x79, 0x67, 0x67,
- 0x33, 0x8b, 0x05, 0x47, 0xd5, 0xe5, 0xd3, 0x86, 0x38, 0xf3, 0xc9, 0xcf, 0x44, 0xc9, 0x98, 0x1b,
- 0x31, 0x6d, 0xc4, 0xca, 0x31, 0xc1, 0xa5, 0x7d, 0xb7, 0x08, 0x8d, 0x30, 0xe9, 0x03, 0xf9, 0x02,
- 0xd4, 0xf6, 0x3c, 0xf7, 0x90, 0x7a, 0xc2, 0x73, 0x27, 0xcf, 0x44, 0xb5, 0x45, 0x11, 0x2a, 0x1a,
- 0xf9, 0x3c, 0x54, 0x02, 0xb7, 0x6f, 0x19, 0x69, 0x83, 0xda, 0x0e, 0x2b, 0x44, 0x41, 0xbb, 0xb8,
- 0x01, 0xfe, 0xc5, 0x84, 0x6a, 0xd7, 0x18, 0xa9, 0x8c, 0xbd, 0x07, 0x65, 0x5f, 0xf7, 0x6d, 0xb9,
- 0x9e, 0xe6, 0xc8, 0x9f, 0xb0, 0xdc, 0xd9, 0x94, 0xf9, 0x13, 0x96, 0x3b, 0x9b, 0xc8, 0x41, 0xb5,
- 0x3f, 0x28, 0x42, 0x53, 0xf4, 0x9b, 0x98, 0x15, 0x26, 0xd9, 0x73, 0x6f, 0xf1, 0x50, 0x0a, 0x7f,
- 0xd0, 0xa3, 0x1e, 0x37, 0x33, 0xc9, 0x49, 0x2e, 0xee, 0x1f, 0x88, 0x88, 0x61, 0x38, 0x45, 0x54,
- 0xa4, 0xba, 0xbe, 0x7c, 0x81, 0x5d, 0x5f, 0x39, 0x53, 0xd7, 0x57, 0x2f, 0xa2, 0xeb, 0x3f, 0x2e,
- 0x42, 0x63, 0xd3, 0xda, 0xa7, 0xc6, 0xb1, 0x61, 0xf3, 0xd3, 0x9f, 0x26, 0xb5, 0x69, 0x40, 0xd7,
- 0x3d, 0xdd, 0xa0, 0xdb, 0xd4, 0xb3, 0x78, 0x52, 0x24, 0xf6, 0x7d, 0xf0, 0x19, 0x48, 0x9e, 0xfe,
- 0x5c, 0x1d, 0xc1, 0x83, 0x23, 0x6b, 0x93, 0x0d, 0x98, 0x32, 0xa9, 0x6f, 0x79, 0xd4, 0xdc, 0x8e,
- 0x6d, 0x54, 0xbe, 0xa0, 0x96, 0x9a, 0xd5, 0x18, 0xed, 0xf1, 0x49, 0x6b, 0x5a, 0x19, 0x28, 0xc5,
- 0x8e, 0x25, 0x51, 0x95, 0x7d, 0xf2, 0x7d, 0x7d, 0xe0, 0x67, 0xb5, 0x31, 0xf6, 0xc9, 0x6f, 0x67,
- 0xb3, 0xe0, 0xa8, 0xba, 0x5a, 0x05, 0x4a, 0x9b, 0x6e, 0x57, 0xfb, 0x4e, 0x09, 0xc2, 0xec, 0x59,
- 0xe4, 0xcf, 0x15, 0xa0, 0xa9, 0x3b, 0x8e, 0x1b, 0xc8, 0xcc, 0x54, 0xc2, 0x03, 0x8f, 0xb9, 0x93,
- 0x74, 0x2d, 0x2c, 0x47, 0xa0, 0xc2, 0x79, 0x1b, 0x3a, 0x94, 0x63, 0x14, 0x8c, 0xcb, 0x26, 0x83,
- 0x94, 0x3f, 0x79, 0x2b, 0x7f, 0x2b, 0xce, 0xe0, 0x3d, 0xbe, 0xf6, 0x35, 0x98, 0x4b, 0x37, 0xf6,
- 0x3c, 0xee, 0xa0, 0x5c, 0x8e, 0xf9, 0x22, 0x40, 0x14, 0x53, 0xf2, 0x0c, 0x8c, 0x58, 0x56, 0xc2,
- 0x88, 0x35, 0x7e, 0x0a, 0x83, 0xa8, 0xd1, 0x23, 0x0d, 0x57, 0xdf, 0x4a, 0x19, 0xae, 0x36, 0x26,
- 0x21, 0xec, 0xc9, 0xc6, 0xaa, 0xbf, 0x55, 0x80, 0xb9, 0x88, 0x59, 0x9e, 0x85, 0xfe, 0x32, 0x4c,
- 0x7b, 0x54, 0x37, 0xdb, 0x7a, 0x60, 0x1c, 0xf0, 0x50, 0xef, 0x02, 0x8f, 0xcd, 0xe6, 0xa7, 0xbf,
- 0x30, 0x4e, 0xc0, 0x24, 0x1f, 0xd1, 0xa1, 0xc9, 0x0a, 0x76, 0xac, 0x1e, 0x75, 0x07, 0xc1, 0x98,
- 0x56, 0x53, 0xbe, 0x61, 0xc1, 0x08, 0x06, 0xe3, 0x98, 0xda, 0x0f, 0x0a, 0x30, 0x13, 0x6f, 0xf0,
- 0x85, 0x5b, 0xd4, 0x0e, 0x92, 0x16, 0xb5, 0x95, 0x09, 0xbc, 0x93, 0x11, 0x56, 0xb4, 0x8f, 0x20,
- 0xfe, 0x68, 0xdc, 0x72, 0x16, 0x37, 0x16, 0x14, 0x9e, 0x68, 0x2c, 0xf8, 0xf4, 0x27, 0x4c, 0x1a,
- 0xa5, 0xe5, 0x96, 0x9f, 0x63, 0x2d, 0xf7, 0x93, 0xcc, 0xba, 0x14, 0xcb, 0x1c, 0x54, 0xcd, 0x91,
- 0x39, 0xa8, 0x17, 0x66, 0x0e, 0xaa, 0x4d, 0x6c, 0xd2, 0x39, 0x4b, 0xf6, 0xa0, 0xfa, 0x33, 0xcd,
- 0x1e, 0xd4, 0xb8, 0xa8, 0xec, 0x41, 0x90, 0x37, 0x7b, 0xd0, 0x47, 0x05, 0x98, 0x31, 0x13, 0x27,
- 0x66, 0xe5, 0x19, 0xf3, 0xf1, 0x97, 0x9a, 0xe4, 0x01, 0x5c, 0x71, 0x64, 0x2a, 0x59, 0x86, 0x29,
- 0x91, 0x59, 0x39, 0x7b, 0xa6, 0x3e, 0x99, 0x9c, 0x3d, 0xbf, 0x5f, 0x8b, 0xaf, 0x48, 0xcf, 0xda,
- 0x68, 0xfe, 0x46, 0xd2, 0x68, 0x7e, 0x23, 0x6d, 0x34, 0x9f, 0x8d, 0xc5, 0xb3, 0xc6, 0x0d, 0xe7,
- 0x3f, 0x19, 0x9b, 0xa8, 0x4b, 0x3c, 0x5b, 0x4f, 0xf8, 0xce, 0x33, 0x26, 0xeb, 0x65, 0x98, 0x95,
- 0xda, 0xab, 0x22, 0xf2, 0x59, 0x6e, 0x3a, 0x0a, 0x73, 0x5a, 0x4d, 0x92, 0x31, 0xcd, 0xcf, 0x04,
- 0xfa, 0x2a, 0x69, 0xab, 0xd8, 0x2a, 0x44, 0x83, 0x4c, 0x25, 0x54, 0x0d, 0x39, 0xd8, 0xb6, 0xc2,
- 0xa3, 0xba, 0x2f, 0x4d, 0xdf, 0xb1, 0x6d, 0x05, 0xf2, 0x52, 0x94, 0xd4, 0xb8, 0xfd, 0xbf, 0xf6,
- 0x14, 0xfb, 0xbf, 0x0e, 0x4d, 0x5b, 0xf7, 0x03, 0xf1, 0x36, 0x4d, 0xf9, 0x39, 0xff, 0xb1, 0xb3,
- 0x2d, 0xbc, 0x6c, 0x31, 0x8f, 0xb4, 0xdb, 0xcd, 0x08, 0x06, 0xe3, 0x98, 0xc4, 0x84, 0x29, 0xf6,
- 0x97, 0x7f, 0xda, 0xe6, 0x72, 0x20, 0x53, 0x9b, 0x9d, 0x47, 0x46, 0x68, 0xb6, 0xda, 0x8c, 0xe1,
- 0x60, 0x02, 0x75, 0x84, 0x8b, 0x00, 0xc6, 0x71, 0x11, 0x90, 0x9f, 0x15, 0x9a, 0xd3, 0x71, 0xf8,
- 0x5a, 0x9b, 0xfc, 0xb5, 0x86, 0x21, 0x92, 0x18, 0x27, 0x62, 0x92, 0x97, 0x8d, 0x8a, 0x81, 0xec,
- 0x06, 0x55, 0x7d, 0x2a, 0x39, 0x2a, 0x76, 0x93, 0x64, 0x4c, 0xf3, 0x93, 0x6d, 0xb8, 0x1c, 0x16,
- 0xc5, 0x9b, 0x31, 0xcd, 0x71, 0xc2, 0x98, 0xb5, 0xdd, 0x0c, 0x1e, 0xcc, 0xac, 0xc9, 0x0f, 0x81,
- 0x0c, 0x3c, 0x8f, 0x3a, 0xc1, 0x6d, 0xdd, 0x3f, 0x90, 0xc1, 0x6f, 0xd1, 0x21, 0x90, 0x88, 0x84,
- 0x71, 0x3e, 0xb2, 0x04, 0x20, 0xe0, 0x78, 0xad, 0xd9, 0x64, 0x7c, 0xe9, 0x6e, 0x48, 0xc1, 0x18,
- 0x97, 0xf6, 0x51, 0x03, 0x9a, 0xf7, 0xf4, 0xc0, 0x3a, 0xa2, 0xdc, 0x9f, 0x77, 0x31, 0x4e, 0x95,
- 0xbf, 0x5a, 0x80, 0x2b, 0xc9, 0xa0, 0xcd, 0x0b, 0xf4, 0xac, 0xf0, 0x64, 0x3f, 0x98, 0x29, 0x0d,
- 0x47, 0xb4, 0x82, 0xfb, 0x58, 0x86, 0x62, 0x40, 0x2f, 0xda, 0xc7, 0xd2, 0x19, 0x25, 0x10, 0x47,
- 0xb7, 0xe5, 0xd3, 0xe2, 0x63, 0x79, 0xbe, 0xb3, 0x53, 0xa6, 0x3c, 0x40, 0xb5, 0xe7, 0xc6, 0x03,
- 0x54, 0x7f, 0x2e, 0xd4, 0xee, 0x7e, 0xcc, 0x03, 0xd4, 0xc8, 0x19, 0x89, 0x24, 0xcf, 0x39, 0x08,
- 0xb4, 0x51, 0x9e, 0x24, 0x9e, 0xa2, 0x40, 0x59, 0xe6, 0x99, 0xb6, 0xba, 0xa7, 0xfb, 0x96, 0x21,
- 0xd5, 0x8e, 0x1c, 0xd9, 0x78, 0x55, 0x96, 0x3e, 0x11, 0xb0, 0xc0, 0xff, 0xa2, 0xc0, 0x8e, 0x92,
- 0x12, 0x16, 0x73, 0x25, 0x25, 0x24, 0x2b, 0x50, 0x76, 0x0e, 0xe9, 0xf1, 0xf9, 0x0e, 0xfb, 0xf3,
- 0x5d, 0xd8, 0xbd, 0xbb, 0xf4, 0x18, 0x79, 0x65, 0xed, 0xbb, 0x45, 0x00, 0xf6, 0xf8, 0x67, 0xf3,
- 0xc5, 0xfc, 0x38, 0xd4, 0xfc, 0x01, 0xb7, 0x9a, 0x48, 0x85, 0x29, 0x0a, 0xdf, 0x12, 0xc5, 0xa8,
- 0xe8, 0xe4, 0xf3, 0x50, 0xf9, 0xd6, 0x80, 0x0e, 0x54, 0x60, 0x41, 0xa8, 0xb8, 0x7f, 0x9d, 0x15,
- 0xa2, 0xa0, 0x5d, 0x9c, 0x5d, 0x55, 0xf9, 0x6c, 0x2a, 0x17, 0xe5, 0xb3, 0x69, 0x40, 0xed, 0x9e,
- 0xcb, 0xa3, 0x41, 0xb5, 0xff, 0x56, 0x04, 0x88, 0xa2, 0xed, 0xc8, 0xaf, 0x17, 0xe0, 0xa5, 0xf0,
- 0x83, 0x0b, 0xc4, 0xfe, 0x8b, 0x27, 0xc0, 0xce, 0xed, 0xbf, 0xc9, 0xfa, 0xd8, 0xf9, 0x0c, 0xb4,
- 0x9d, 0x25, 0x0e, 0xb3, 0x5b, 0x41, 0x10, 0xea, 0xb4, 0xd7, 0x0f, 0x8e, 0x57, 0x2d, 0x4f, 0x8e,
- 0xc0, 0xcc, 0xa0, 0xce, 0x5b, 0x92, 0x47, 0x54, 0x95, 0x46, 0x02, 0xfe, 0x11, 0x29, 0x0a, 0x86,
- 0x38, 0xe4, 0x00, 0xea, 0x8e, 0xfb, 0xbe, 0xcf, 0xba, 0x43, 0x0e, 0xc7, 0xb7, 0xc7, 0xef, 0x72,
- 0xd1, 0xad, 0xc2, 0xde, 0x2f, 0xff, 0x60, 0xcd, 0x91, 0x9d, 0xfd, 0x6b, 0x45, 0xb8, 0x94, 0xd1,
- 0x0f, 0xe4, 0x6d, 0x98, 0x93, 0x81, 0x8d, 0x51, 0x26, 0xf8, 0x42, 0x94, 0x09, 0xbe, 0x93, 0xa2,
- 0xe1, 0x10, 0x37, 0x79, 0x1f, 0x40, 0x37, 0x0c, 0xea, 0xfb, 0x5b, 0xae, 0xa9, 0xf6, 0x03, 0x6f,
- 0x31, 0xf5, 0x65, 0x39, 0x2c, 0x7d, 0x7c, 0xd2, 0xfa, 0xa9, 0xac, 0x58, 0xe5, 0x54, 0x3f, 0x47,
- 0x15, 0x30, 0x06, 0x49, 0xbe, 0x09, 0x20, 0x36, 0xe1, 0x61, 0x3a, 0x85, 0xa7, 0x58, 0xae, 0x16,
- 0x54, 0xb6, 0xae, 0x85, 0xaf, 0x0f, 0x74, 0x27, 0xb0, 0x82, 0x63, 0x91, 0xbd, 0xe6, 0x41, 0x88,
- 0x82, 0x31, 0x44, 0xed, 0x9f, 0x16, 0xa1, 0xae, 0x6c, 0xe6, 0xcf, 0xc0, 0x50, 0xda, 0x4d, 0x18,
- 0x4a, 0x27, 0x14, 0x9d, 0x9c, 0x65, 0x26, 0x75, 0x53, 0x66, 0xd2, 0xf5, 0xfc, 0xa2, 0x9e, 0x6c,
- 0x24, 0xfd, 0xad, 0x22, 0xcc, 0x28, 0xd6, 0xbc, 0x26, 0xd2, 0xaf, 0xc2, 0xac, 0x88, 0x2a, 0xd8,
- 0xd2, 0x1f, 0x89, 0x44, 0x3e, 0xbc, 0xc3, 0xca, 0x22, 0x20, 0xb8, 0x9d, 0x24, 0x61, 0x9a, 0x97,
- 0x0d, 0x6b, 0x51, 0xb4, 0xcb, 0x36, 0x61, 0xc2, 0x0f, 0x29, 0xf6, 0x9b, 0x7c, 0x58, 0xb7, 0x53,
- 0x34, 0x1c, 0xe2, 0x4e, 0xdb, 0x68, 0xcb, 0x17, 0x60, 0xa3, 0xfd, 0xf7, 0x05, 0x98, 0x8a, 0xfa,
- 0xeb, 0xc2, 0x2d, 0xb4, 0xfb, 0x49, 0x0b, 0xed, 0x72, 0xee, 0xe1, 0x30, 0xc2, 0x3e, 0xfb, 0x17,
- 0x6b, 0x90, 0x08, 0x92, 0x27, 0x7b, 0x70, 0xcd, 0xca, 0x0c, 0xf5, 0x8b, 0xcd, 0x36, 0xe1, 0xa9,
- 0xef, 0x8d, 0x91, 0x9c, 0xf8, 0x04, 0x14, 0x32, 0x80, 0xfa, 0x11, 0xf5, 0x02, 0xcb, 0xa0, 0xea,
- 0xf9, 0xd6, 0x73, 0xab, 0x64, 0xd2, 0x0a, 0x1d, 0xf6, 0xe9, 0x03, 0x29, 0x00, 0x43, 0x51, 0x64,
- 0x0f, 0x2a, 0xd4, 0xec, 0x52, 0x95, 0x5a, 0x29, 0x67, 0x8a, 0xda, 0xb0, 0x3f, 0xd9, 0x3f, 0x1f,
- 0x05, 0x34, 0xf1, 0xa1, 0x61, 0x2b, 0x2f, 0xa3, 0x1c, 0x87, 0xe3, 0x2b, 0x58, 0xa1, 0xbf, 0x32,
- 0xca, 0xba, 0x10, 0x16, 0x61, 0x24, 0x87, 0x1c, 0x86, 0xe6, 0xce, 0xca, 0x84, 0x26, 0x8f, 0x27,
- 0x18, 0x3b, 0x7d, 0x68, 0x3c, 0xd4, 0x03, 0xea, 0xf5, 0x74, 0xef, 0x50, 0xee, 0x36, 0xc6, 0x7f,
- 0xc2, 0x77, 0x14, 0x52, 0xf4, 0x84, 0x61, 0x11, 0x46, 0x72, 0x88, 0x0b, 0x8d, 0x40, 0xaa, 0xcf,
- 0xca, 0xa6, 0x3b, 0xbe, 0x50, 0xa5, 0x88, 0xfb, 0x32, 0x58, 0x5e, 0xfd, 0xc5, 0x48, 0x06, 0x39,
- 0x4a, 0xe4, 0x33, 0x17, 0x59, 0xec, 0xdb, 0x39, 0x7c, 0x03, 0x12, 0x2a, 0x5a, 0x6e, 0xb2, 0xf3,
- 0xa2, 0x6b, 0xff, 0xb3, 0x12, 0x4d, 0xcb, 0xcf, 0xda, 0x4e, 0xf8, 0xa5, 0xa4, 0x9d, 0xf0, 0x7a,
- 0xda, 0x4e, 0x98, 0x72, 0x56, 0x9f, 0x3f, 0xbc, 0x36, 0x65, 0x5e, 0x2b, 0x5f, 0x80, 0x79, 0xed,
- 0x35, 0x68, 0x1e, 0xf1, 0x99, 0x40, 0xe4, 0x69, 0xaa, 0xf0, 0x65, 0x84, 0xcf, 0xec, 0x0f, 0xa2,
- 0x62, 0x8c, 0xf3, 0xb0, 0x2a, 0xf2, 0x06, 0x97, 0x30, 0xa5, 0xb1, 0xac, 0xd2, 0x89, 0x8a, 0x31,
- 0xce, 0xc3, 0x23, 0xf3, 0x2c, 0xe7, 0x50, 0x54, 0xa8, 0xf1, 0x0a, 0x22, 0x32, 0x4f, 0x15, 0x62,
- 0x44, 0x27, 0x37, 0xa1, 0x3e, 0x30, 0xf7, 0x05, 0x6f, 0x9d, 0xf3, 0x72, 0x0d, 0x73, 0x77, 0x75,
- 0x4d, 0xe6, 0x8d, 0x52, 0x54, 0xd6, 0x92, 0x9e, 0xde, 0x57, 0x04, 0xbe, 0x37, 0x94, 0x2d, 0xd9,
- 0x8a, 0x8a, 0x31, 0xce, 0x43, 0x7e, 0x06, 0x66, 0x3c, 0x6a, 0x0e, 0x0c, 0x1a, 0xd6, 0x02, 0x5e,
- 0x4b, 0x26, 0xd4, 0x8c, 0x53, 0x30, 0xc5, 0x39, 0xc2, 0x48, 0xd8, 0x1c, 0xcb, 0x48, 0xf8, 0x35,
- 0x98, 0x31, 0x3d, 0xdd, 0x72, 0xa8, 0x79, 0xdf, 0xe1, 0x11, 0x09, 0x32, 0x3e, 0x30, 0xb4, 0x90,
- 0xaf, 0x26, 0xa8, 0x98, 0xe2, 0xd6, 0xfe, 0x65, 0x11, 0x2a, 0x22, 0xcd, 0xe7, 0x06, 0x5c, 0xb2,
- 0x1c, 0x2b, 0xb0, 0x74, 0x7b, 0x95, 0xda, 0xfa, 0x71, 0x32, 0x2a, 0xe3, 0x65, 0xb6, 0xd1, 0xde,
- 0x18, 0x26, 0x63, 0x56, 0x1d, 0xd6, 0x39, 0x81, 0x58, 0xbe, 0x15, 0x8a, 0xb0, 0xa3, 0x89, 0xdc,
- 0xd0, 0x09, 0x0a, 0xa6, 0x38, 0x99, 0x32, 0xd4, 0xcf, 0x08, 0xb9, 0xe0, 0xca, 0x50, 0x32, 0xd0,
- 0x22, 0xc9, 0xc7, 0x95, 0xf4, 0x01, 0x57, 0x88, 0xc3, 0x53, 0x38, 0x32, 0xaa, 0x4a, 0x28, 0xe9,
- 0x29, 0x1a, 0x0e, 0x71, 0x33, 0x84, 0x7d, 0xdd, 0xb2, 0x07, 0x1e, 0x8d, 0x10, 0x2a, 0x11, 0xc2,
- 0x5a, 0x8a, 0x86, 0x43, 0xdc, 0xda, 0x7f, 0x2f, 0x00, 0x19, 0x3e, 0x57, 0x40, 0x0e, 0xa0, 0xea,
- 0x70, 0x5b, 0x64, 0xee, 0x94, 0xf4, 0x31, 0x93, 0xa6, 0x58, 0x24, 0x64, 0x81, 0xc4, 0x27, 0x0e,
- 0xd4, 0xe9, 0xa3, 0x80, 0x7a, 0x4e, 0x78, 0xce, 0x68, 0x32, 0xe9, 0xef, 0xc5, 0xde, 0x4c, 0x22,
- 0x63, 0x28, 0x43, 0xfb, 0xbd, 0x22, 0x34, 0x63, 0x7c, 0x4f, 0xdb, 0xe2, 0xf3, 0x54, 0x07, 0xc2,
- 0x04, 0xb8, 0xeb, 0xd9, 0x72, 0xbe, 0x8b, 0xa5, 0x3a, 0x90, 0x24, 0xdc, 0xc4, 0x38, 0x1f, 0x59,
- 0x02, 0xe8, 0xe9, 0x7e, 0x40, 0x3d, 0xae, 0x0b, 0xa5, 0x12, 0x0c, 0x6c, 0x85, 0x14, 0x8c, 0x71,
- 0x91, 0x1b, 0xf2, 0x02, 0x83, 0x72, 0x32, 0x21, 0xe4, 0x88, 0xdb, 0x09, 0x2a, 0x13, 0xb8, 0x9d,
- 0x80, 0x74, 0x61, 0x4e, 0xb5, 0x5a, 0x51, 0xcf, 0x97, 0x2e, 0x50, 0x0c, 0xd4, 0x14, 0x04, 0x0e,
- 0x81, 0x6a, 0xdf, 0x2d, 0xc0, 0x74, 0xc2, 0x00, 0x25, 0x52, 0x39, 0xaa, 0x53, 0x31, 0x89, 0x54,
- 0x8e, 0xb1, 0xc3, 0x2c, 0x5f, 0x84, 0xaa, 0xe8, 0xa0, 0x74, 0xb0, 0xab, 0xe8, 0x42, 0x94, 0x54,
- 0xb6, 0xb2, 0x48, 0x13, 0x77, 0x7a, 0x65, 0x91, 0x36, 0x70, 0x54, 0x74, 0xe1, 0x39, 0x12, 0xad,
- 0x93, 0x3d, 0x1d, 0xf3, 0x1c, 0x89, 0x72, 0x0c, 0x39, 0xb4, 0x7f, 0xc8, 0xdb, 0x1d, 0x78, 0xc7,
- 0xe1, 0xce, 0xba, 0x0b, 0x35, 0x19, 0xe0, 0x28, 0x3f, 0x8d, 0xb7, 0x73, 0x58, 0xc5, 0x38, 0x8e,
- 0x0c, 0xe5, 0xd3, 0x8d, 0xc3, 0xfb, 0xfb, 0xfb, 0xa8, 0xd0, 0xc9, 0x2d, 0x68, 0xb8, 0x8e, 0xfc,
- 0x82, 0xe5, 0xe3, 0xff, 0x18, 0x5b, 0x39, 0xee, 0xab, 0xc2, 0xc7, 0x27, 0xad, 0x2b, 0xe1, 0x9f,
- 0x44, 0x23, 0x31, 0xaa, 0xa9, 0xfd, 0xd9, 0x02, 0xbc, 0x84, 0xae, 0x6d, 0x5b, 0x4e, 0x37, 0xe9,
- 0x7a, 0x24, 0x36, 0xcc, 0xf4, 0xf4, 0x47, 0xbb, 0x8e, 0x7e, 0xa4, 0x5b, 0xb6, 0xbe, 0x67, 0xd3,
- 0xa7, 0xee, 0x8c, 0x07, 0x81, 0x65, 0x2f, 0x88, 0x0b, 0x1d, 0x17, 0x36, 0x9c, 0xe0, 0xbe, 0xd7,
- 0x09, 0x3c, 0xcb, 0xe9, 0x8a, 0x59, 0x72, 0x2b, 0x81, 0x85, 0x29, 0x6c, 0xed, 0xf7, 0x4b, 0xc0,
- 0x83, 0xec, 0xc8, 0x97, 0xa1, 0xd1, 0xa3, 0xc6, 0x81, 0xee, 0x58, 0xbe, 0x4a, 0x8a, 0x7b, 0x95,
- 0x3d, 0xd7, 0x96, 0x2a, 0x7c, 0xcc, 0x5e, 0xc5, 0x72, 0x67, 0x93, 0x9f, 0x63, 0x89, 0x78, 0x89,
- 0x01, 0xd5, 0xae, 0xef, 0xeb, 0x7d, 0x2b, 0x77, 0x8c, 0x87, 0x48, 0x42, 0x2a, 0xa6, 0x23, 0xf1,
- 0x1b, 0x25, 0x34, 0x31, 0xa0, 0xd2, 0xb7, 0x75, 0xcb, 0xc9, 0x7d, 0x01, 0x19, 0x7b, 0x82, 0x6d,
- 0x86, 0x24, 0x4c, 0x95, 0xfc, 0x27, 0x0a, 0x6c, 0x32, 0x80, 0xa6, 0x6f, 0x78, 0x7a, 0xcf, 0x3f,
- 0xd0, 0x97, 0x5e, 0x7f, 0x23, 0xb7, 0xf2, 0x1f, 0x89, 0x12, 0xba, 0xc8, 0x0a, 0x2e, 0x6f, 0x75,
- 0x6e, 0x2f, 0x2f, 0xbd, 0xfe, 0x06, 0xc6, 0xe5, 0xc4, 0xc5, 0xbe, 0xfe, 0xda, 0x92, 0x9c, 0x41,
- 0x26, 0x2e, 0xf6, 0xf5, 0xd7, 0x96, 0x30, 0x2e, 0x47, 0xfb, 0x5f, 0x05, 0x68, 0x84, 0xbc, 0x64,
- 0x17, 0x80, 0xcd, 0x65, 0x32, 0x6d, 0xe8, 0xb9, 0x2e, 0x6b, 0xe1, 0xd6, 0x9e, 0xdd, 0xb0, 0x32,
- 0xc6, 0x80, 0x32, 0xf2, 0xaa, 0x16, 0x27, 0x9d, 0x57, 0x75, 0x11, 0x1a, 0x07, 0xba, 0x63, 0xfa,
- 0x07, 0xfa, 0xa1, 0x98, 0xd2, 0x63, 0x99, 0x86, 0x6f, 0x2b, 0x02, 0x46, 0x3c, 0xda, 0x3f, 0xae,
- 0x82, 0x08, 0xcc, 0x60, 0x93, 0x8e, 0x69, 0xf9, 0xe2, 0x64, 0x40, 0x81, 0xd7, 0x0c, 0x27, 0x9d,
- 0x55, 0x59, 0x8e, 0x21, 0x07, 0xb9, 0x0a, 0xa5, 0x9e, 0xe5, 0x48, 0x0d, 0x84, 0x1b, 0x72, 0xb7,
- 0x2c, 0x07, 0x59, 0x19, 0x27, 0xe9, 0x8f, 0xa4, 0x86, 0x21, 0x48, 0xfa, 0x23, 0x64, 0x65, 0xe4,
- 0xab, 0x30, 0x6b, 0xbb, 0xee, 0x21, 0x9b, 0x3e, 0x94, 0x22, 0x22, 0xbc, 0xea, 0xdc, 0xb4, 0xb2,
- 0x99, 0x24, 0x61, 0x9a, 0x97, 0xec, 0xc2, 0xcb, 0x1f, 0x52, 0xcf, 0x95, 0xf3, 0x65, 0xc7, 0xa6,
- 0xb4, 0xaf, 0x60, 0x84, 0x6a, 0xcc, 0x43, 0x48, 0x7f, 0x3e, 0x9b, 0x05, 0x47, 0xd5, 0xe5, 0xc1,
- 0xe8, 0xba, 0xd7, 0xa5, 0xc1, 0xb6, 0xe7, 0x32, 0xdd, 0xc5, 0x72, 0xba, 0x0a, 0xb6, 0x1a, 0xc1,
- 0xee, 0x64, 0xb3, 0xe0, 0xa8, 0xba, 0xe4, 0x5d, 0x98, 0x17, 0x24, 0xa1, 0xb6, 0x2c, 0x8b, 0x69,
- 0xc6, 0xb2, 0xd5, 0xbd, 0x9d, 0xd3, 0xc2, 0x5f, 0xb6, 0x33, 0x82, 0x07, 0x47, 0xd6, 0x26, 0x77,
- 0x60, 0x4e, 0x79, 0x4b, 0xb7, 0xa9, 0xd7, 0x09, 0x83, 0x75, 0xa6, 0xdb, 0xd7, 0x4f, 0x4f, 0x5a,
- 0xd7, 0x56, 0x69, 0xdf, 0xa3, 0x46, 0xdc, 0xeb, 0xac, 0xb8, 0x70, 0xa8, 0x1e, 0x41, 0xb8, 0xc2,
- 0x23, 0x72, 0x76, 0xfb, 0x2b, 0xae, 0x6b, 0x9b, 0xee, 0x43, 0x47, 0x3d, 0xbb, 0x50, 0xd8, 0xb9,
- 0x83, 0xb4, 0x93, 0xc9, 0x81, 0x23, 0x6a, 0xb2, 0x27, 0xe7, 0x94, 0x55, 0xf7, 0xa1, 0x93, 0x46,
- 0x85, 0xe8, 0xc9, 0x3b, 0x23, 0x78, 0x70, 0x64, 0x6d, 0xb2, 0x06, 0x24, 0xfd, 0x04, 0xbb, 0x7d,
- 0xe9, 0xc2, 0xbf, 0x22, 0x32, 0x00, 0xa5, 0xa9, 0x98, 0x51, 0x83, 0x6c, 0xc2, 0xe5, 0x74, 0x29,
- 0x13, 0x27, 0xbd, 0xf9, 0x3c, 0xf7, 0x2f, 0x66, 0xd0, 0x31, 0xb3, 0x96, 0xf6, 0x4f, 0x8a, 0x30,
- 0x9d, 0x48, 0x19, 0xf1, 0xdc, 0x1d, 0xcd, 0x67, 0x9b, 0x87, 0x9e, 0xdf, 0xdd, 0x58, 0xbd, 0x4d,
- 0x75, 0x93, 0x7a, 0x77, 0xa9, 0x4a, 0xef, 0x21, 0x96, 0xc5, 0x04, 0x05, 0x53, 0x9c, 0x64, 0x1f,
- 0x2a, 0xc2, 0x4f, 0x90, 0xf7, 0xda, 0x1f, 0xd5, 0x47, 0xdc, 0x59, 0x20, 0xef, 0xca, 0x72, 0x3d,
- 0x8a, 0x02, 0x5e, 0x0b, 0x60, 0x2a, 0xce, 0xc1, 0x26, 0x92, 0x48, 0xed, 0xad, 0x25, 0x54, 0xde,
- 0x0d, 0x28, 0x05, 0xc1, 0xb8, 0x87, 0xfe, 0x85, 0xdf, 0x69, 0x67, 0x13, 0x19, 0x86, 0xb6, 0xcf,
- 0xde, 0x9d, 0xef, 0x5b, 0xae, 0x23, 0x33, 0xc0, 0xef, 0x42, 0x4d, 0xee, 0x9e, 0xc6, 0x4c, 0x5a,
- 0xc0, 0x75, 0x25, 0x65, 0x76, 0x55, 0x58, 0xda, 0x7f, 0x28, 0x42, 0x23, 0x34, 0x93, 0x9c, 0x21,
- 0xb3, 0xba, 0x0b, 0x8d, 0x30, 0xa2, 0x30, 0xf7, 0x9d, 0xa6, 0x51, 0xa0, 0x1b, 0xdf, 0xd9, 0x87,
- 0x7f, 0x31, 0x92, 0x11, 0x8f, 0x56, 0x2c, 0xe5, 0x88, 0x56, 0xec, 0x43, 0x2d, 0xf0, 0xac, 0x6e,
- 0x57, 0xee, 0x12, 0xf2, 0x84, 0x2b, 0x86, 0xdd, 0xb5, 0x23, 0x00, 0x65, 0xcf, 0x8a, 0x3f, 0xa8,
- 0xc4, 0x68, 0x1f, 0xc0, 0x5c, 0x9a, 0x93, 0xab, 0xd0, 0xc6, 0x01, 0x35, 0x07, 0xb6, 0xea, 0xe3,
- 0x48, 0x85, 0x96, 0xe5, 0x18, 0x72, 0x90, 0x9b, 0x50, 0x67, 0xaf, 0xe9, 0x43, 0xd7, 0x51, 0x6a,
- 0x2c, 0xdf, 0x8d, 0xec, 0xc8, 0x32, 0x0c, 0xa9, 0xda, 0x7f, 0x2d, 0xc1, 0xd5, 0xc8, 0xd8, 0xb5,
- 0xa5, 0x3b, 0x7a, 0xf7, 0x0c, 0x17, 0x59, 0x7e, 0x76, 0x0c, 0xec, 0xbc, 0xd7, 0x63, 0x94, 0x9e,
- 0x83, 0xeb, 0x31, 0xfe, 0x4f, 0x11, 0x78, 0xf4, 0x33, 0xf9, 0x36, 0x4c, 0xe9, 0xb1, 0x3b, 0x8c,
- 0xe5, 0xeb, 0xbc, 0x95, 0xfb, 0x75, 0xf2, 0x20, 0xeb, 0x30, 0x00, 0x2e, 0x5e, 0x8a, 0x09, 0x81,
- 0xc4, 0x85, 0xfa, 0xbe, 0x6e, 0xdb, 0x4c, 0x17, 0xca, 0xed, 0xbc, 0x4b, 0x08, 0xe7, 0xc3, 0x7c,
- 0x4d, 0x42, 0x63, 0x28, 0x84, 0x7c, 0x54, 0x80, 0x69, 0x2f, 0xbe, 0x5d, 0x93, 0x2f, 0x24, 0x4f,
- 0x68, 0x47, 0x0c, 0x2d, 0x1e, 0x6e, 0x17, 0xdf, 0x13, 0x26, 0x65, 0x6a, 0xff, 0xa5, 0x00, 0xd3,
- 0x1d, 0xdb, 0x32, 0x2d, 0xa7, 0x7b, 0x81, 0xb7, 0x73, 0xdc, 0x87, 0x8a, 0x6f, 0x5b, 0x26, 0x1d,
- 0x73, 0x35, 0x11, 0xeb, 0x18, 0x03, 0x40, 0x81, 0x93, 0xbc, 0xee, 0xa3, 0x74, 0x86, 0xeb, 0x3e,
- 0xfe, 0xb0, 0x0a, 0x32, 0x8e, 0x9f, 0x0c, 0xa0, 0xd1, 0x55, 0xb7, 0x08, 0xc8, 0x67, 0xbc, 0x9d,
- 0x23, 0x03, 0x65, 0xe2, 0x3e, 0x02, 0x31, 0xf7, 0x87, 0x85, 0x18, 0x49, 0x22, 0x34, 0x79, 0x79,
- 0xf6, 0x6a, 0xce, 0xcb, 0xb3, 0x85, 0xb8, 0xe1, 0xeb, 0xb3, 0x75, 0x28, 0x1f, 0x04, 0x41, 0x5f,
- 0x0e, 0xa6, 0xf1, 0x0f, 0x6a, 0x44, 0x49, 0x90, 0x84, 0x4e, 0xc4, 0xfe, 0x23, 0x87, 0x66, 0x22,
- 0x1c, 0x3d, 0xbc, 0xa2, 0x70, 0x25, 0x57, 0x18, 0x49, 0x5c, 0x04, 0xfb, 0x8f, 0x1c, 0x9a, 0xfc,
- 0x22, 0x34, 0x03, 0x4f, 0x77, 0xfc, 0x7d, 0xd7, 0xeb, 0x51, 0x4f, 0xee, 0x51, 0xd7, 0x72, 0xdc,
- 0x1f, 0xbd, 0x13, 0xa1, 0x09, 0x93, 0x6c, 0xa2, 0x08, 0xe3, 0xd2, 0xc8, 0x21, 0xd4, 0x07, 0xa6,
- 0x68, 0x98, 0x34, 0x83, 0x2d, 0xe7, 0xb9, 0x12, 0x3c, 0x16, 0x24, 0xa2, 0xfe, 0x61, 0x28, 0x20,
- 0x79, 0x1b, 0x67, 0x6d, 0x52, 0xb7, 0x71, 0xc6, 0x47, 0x63, 0x56, 0x86, 0x16, 0xd2, 0x93, 0x7a,
- 0xad, 0xd3, 0x95, 0x31, 0x6e, 0x6b, 0xb9, 0x55, 0x4e, 0x21, 0xb2, 0x19, 0xea, 0xc6, 0x4e, 0x17,
- 0x95, 0x0c, 0xad, 0x07, 0xd2, 0x77, 0x44, 0x8c, 0xc4, 0x4d, 0x46, 0xe2, 0xd8, 0xe0, 0xe2, 0xd9,
- 0xe6, 0x83, 0xf0, 0x4a, 0x9d, 0x58, 0x26, 0xf5, 0xcc, 0x2b, 0x8b, 0xb4, 0xff, 0x58, 0x84, 0xd2,
- 0xce, 0x66, 0x47, 0x64, 0x47, 0xe5, 0x77, 0xa3, 0xd1, 0xce, 0xa1, 0xd5, 0x7f, 0x40, 0x3d, 0x6b,
- 0xff, 0x58, 0x6e, 0xbd, 0x63, 0xd9, 0x51, 0xd3, 0x1c, 0x98, 0x51, 0x8b, 0xbc, 0x07, 0x53, 0x86,
- 0xbe, 0x42, 0xbd, 0x60, 0x1c, 0xc3, 0x02, 0x3f, 0x1f, 0xbd, 0xb2, 0x1c, 0x55, 0xc7, 0x04, 0x18,
- 0xd9, 0x05, 0x30, 0x22, 0xe8, 0xd2, 0xb9, 0xcd, 0x21, 0x31, 0xe0, 0x18, 0x10, 0x41, 0x68, 0x1c,
- 0x32, 0x56, 0x8e, 0x5a, 0x3e, 0x0f, 0x2a, 0x1f, 0x39, 0x77, 0x55, 0x5d, 0x8c, 0x60, 0x34, 0x07,
- 0xa6, 0x13, 0xd7, 0x1b, 0x91, 0xaf, 0x40, 0xdd, 0xed, 0xc7, 0xa6, 0xd3, 0x06, 0x8f, 0xa6, 0xad,
- 0xdf, 0x97, 0x65, 0x8f, 0x4f, 0x5a, 0xd3, 0x9b, 0x6e, 0xd7, 0x32, 0x54, 0x01, 0x86, 0xec, 0x44,
- 0x83, 0x2a, 0x3f, 0xd4, 0xa8, 0x2e, 0x37, 0xe2, 0x6b, 0x07, 0xbf, 0x7f, 0xc4, 0x47, 0x49, 0xd1,
- 0x7e, 0xa9, 0x0c, 0x91, 0xc7, 0x95, 0xf8, 0x50, 0x15, 0x87, 0x36, 0xe4, 0xcc, 0x7d, 0xa1, 0xe7,
- 0x43, 0xa4, 0x28, 0xd2, 0x85, 0xd2, 0x07, 0xee, 0x5e, 0xee, 0x89, 0x3b, 0x96, 0xcd, 0x40, 0xd8,
- 0xca, 0x62, 0x05, 0xc8, 0x24, 0x90, 0xbf, 0x56, 0x80, 0x17, 0xfd, 0xb4, 0xea, 0x2b, 0x87, 0x03,
- 0xe6, 0xd7, 0xf1, 0xd3, 0xca, 0xb4, 0x0c, 0x7b, 0x1e, 0x45, 0xc6, 0xe1, 0xb6, 0xb0, 0xfe, 0x17,
- 0xae, 0x50, 0x39, 0x9c, 0xd6, 0x73, 0x5e, 0xbe, 0x9a, 0xec, 0xff, 0x64, 0x19, 0x4a, 0x51, 0xda,
- 0xaf, 0x14, 0xa1, 0x19, 0x9b, 0xad, 0x73, 0xdf, 0x99, 0xf5, 0x28, 0x75, 0x67, 0xd6, 0xf6, 0xf8,
- 0x91, 0x01, 0x51, 0xab, 0x2e, 0xfa, 0xda, 0xac, 0x7f, 0x5e, 0x84, 0xd2, 0xee, 0xea, 0x5a, 0x72,
- 0xd3, 0x5a, 0x78, 0x06, 0x9b, 0xd6, 0x03, 0xa8, 0xed, 0x0d, 0x2c, 0x3b, 0xb0, 0x9c, 0xdc, 0xf9,
- 0x56, 0xd4, 0x15, 0x63, 0xd2, 0xd7, 0x21, 0x50, 0x51, 0xc1, 0x93, 0x2e, 0xd4, 0xba, 0x22, 0xe1,
- 0x65, 0xee, 0x78, 0x49, 0x99, 0x38, 0x53, 0x08, 0x92, 0x7f, 0x50, 0xa1, 0x6b, 0xc7, 0x50, 0xdd,
- 0x5d, 0x95, 0x6a, 0xff, 0xb3, 0xed, 0x4d, 0xed, 0x17, 0x21, 0xd4, 0x02, 0x9e, 0xbd, 0xf0, 0xdf,
- 0x29, 0x40, 0x52, 0xf1, 0x79, 0xf6, 0xa3, 0xe9, 0x30, 0x3d, 0x9a, 0x56, 0x27, 0xf1, 0xf1, 0x65,
- 0x0f, 0x28, 0xed, 0xdf, 0x15, 0x20, 0x75, 0xd2, 0x8e, 0xbc, 0x21, 0x73, 0xa7, 0x25, 0x03, 0xd3,
- 0x54, 0xee, 0x34, 0x92, 0xe4, 0x8e, 0xe5, 0x50, 0xfb, 0x98, 0x6d, 0xd7, 0xe2, 0x0e, 0x34, 0xd9,
- 0xfc, 0x7b, 0xe3, 0x6f, 0xd7, 0xb2, 0xdc, 0x71, 0x32, 0x78, 0x32, 0x4e, 0xc2, 0xa4, 0x5c, 0xed,
- 0x1f, 0x15, 0xa1, 0xfa, 0xcc, 0x0e, 0xfe, 0xd3, 0x44, 0x3c, 0xeb, 0x4a, 0xce, 0xd9, 0x7e, 0x64,
- 0x34, 0x6b, 0x2f, 0x15, 0xcd, 0x9a, 0xf7, 0x4e, 0xef, 0xa7, 0xc4, 0xb2, 0xfe, 0x9b, 0x02, 0xc8,
- 0xb5, 0x66, 0xc3, 0xf1, 0x03, 0xdd, 0x31, 0x28, 0x31, 0xc2, 0x85, 0x2d, 0x6f, 0xd0, 0x94, 0x0c,
- 0x2c, 0x14, 0xba, 0x0c, 0xff, 0xad, 0x16, 0x32, 0xf2, 0x93, 0x50, 0x3f, 0x70, 0xfd, 0x80, 0x2f,
- 0x5e, 0xc5, 0xa4, 0xc9, 0xec, 0xb6, 0x2c, 0xc7, 0x90, 0x23, 0xed, 0xce, 0xae, 0x8c, 0x76, 0x67,
- 0x6b, 0xbf, 0x59, 0x84, 0xa9, 0x4f, 0x4b, 0xf6, 0x82, 0xac, 0xe8, 0xdf, 0x52, 0xce, 0xe8, 0xdf,
- 0xf2, 0x79, 0xa2, 0x7f, 0xb5, 0xef, 0x17, 0x00, 0x9e, 0x59, 0xea, 0x04, 0x33, 0x19, 0x98, 0x9b,
- 0x7b, 0x5c, 0x65, 0x87, 0xe5, 0xfe, 0xfd, 0x8a, 0x7a, 0x24, 0x1e, 0x94, 0xfb, 0x71, 0x01, 0x66,
- 0xf4, 0x44, 0xa0, 0x6b, 0x6e, 0x7d, 0x39, 0x15, 0x37, 0x1b, 0xc6, 0x69, 0x25, 0xcb, 0x31, 0x25,
- 0x96, 0xbc, 0x19, 0xa5, 0xed, 0xbe, 0x17, 0x0d, 0xfb, 0xa1, 0x7c, 0xdb, 0x5c, 0x77, 0x4b, 0x70,
- 0x3e, 0x25, 0xb0, 0xb8, 0x34, 0x91, 0xc0, 0xe2, 0xf8, 0x91, 0xc9, 0xf2, 0x13, 0x8f, 0x4c, 0x1e,
- 0x41, 0x63, 0xdf, 0x73, 0x7b, 0x3c, 0x76, 0x57, 0x5e, 0x8c, 0x7d, 0x2b, 0xc7, 0x42, 0xd9, 0xdb,
- 0xb3, 0x1c, 0x6a, 0xf2, 0xb8, 0xe0, 0xd0, 0x70, 0xb5, 0xa6, 0xf0, 0x31, 0x12, 0xc5, 0x6d, 0xfd,
- 0xae, 0x90, 0x5a, 0x9d, 0xa4, 0xd4, 0x70, 0x2e, 0xd9, 0x11, 0xe8, 0xa8, 0xc4, 0x24, 0xe3, 0x75,
- 0x6b, 0xcf, 0x26, 0x5e, 0x57, 0xfb, 0x0b, 0x35, 0x35, 0x81, 0x3d, 0x77, 0x19, 0x62, 0x3f, 0x3b,
- 0xe8, 0xde, 0xa5, 0x43, 0xa7, 0xd0, 0xeb, 0xcf, 0xf0, 0x14, 0x7a, 0x63, 0x32, 0xa7, 0xd0, 0x21,
- 0xdf, 0x29, 0xf4, 0xe6, 0x84, 0x4e, 0xa1, 0x4f, 0x4d, 0xea, 0x14, 0xfa, 0xf4, 0x58, 0xa7, 0xd0,
- 0x67, 0xce, 0x74, 0x0a, 0xfd, 0xa4, 0x04, 0xa9, 0xcd, 0xf8, 0x67, 0x8e, 0xb7, 0xff, 0xa7, 0x1c,
- 0x6f, 0xdf, 0x29, 0x42, 0x34, 0x11, 0x9f, 0x33, 0x30, 0xe9, 0x5d, 0xa8, 0xf7, 0xf4, 0x47, 0x3c,
- 0x70, 0x3a, 0xcf, 0xc5, 0xca, 0x5b, 0x12, 0x03, 0x43, 0x34, 0xe2, 0x03, 0x58, 0xe1, 0xe5, 0x06,
- 0xb9, 0x5d, 0x18, 0xd1, 0x3d, 0x09, 0xc2, 0x48, 0x1a, 0xfd, 0xc7, 0x98, 0x18, 0xed, 0x5f, 0x17,
- 0x41, 0xde, 0x82, 0x41, 0x28, 0x54, 0xf6, 0xad, 0x47, 0xd4, 0xcc, 0x1d, 0xee, 0x1c, 0xbb, 0xee,
- 0x5e, 0xf8, 0x68, 0x78, 0x01, 0x0a, 0x74, 0x6e, 0x7c, 0x17, 0x3e, 0x37, 0xd9, 0x7f, 0x39, 0x8c,
- 0xef, 0x71, 0xdf, 0x9d, 0x34, 0xbe, 0x8b, 0x22, 0x54, 0x32, 0x84, 0xad, 0x9f, 0x87, 0x5f, 0xe4,
- 0x76, 0x31, 0x26, 0xc2, 0x38, 0x94, 0xad, 0xdf, 0x17, 0x69, 0x28, 0xa4, 0x8c, 0xf6, 0x2f, 0x7c,
- 0xef, 0x87, 0xd7, 0x5f, 0xf8, 0xfe, 0x0f, 0xaf, 0xbf, 0xf0, 0x83, 0x1f, 0x5e, 0x7f, 0xe1, 0x97,
- 0x4e, 0xaf, 0x17, 0xbe, 0x77, 0x7a, 0xbd, 0xf0, 0xfd, 0xd3, 0xeb, 0x85, 0x1f, 0x9c, 0x5e, 0x2f,
- 0xfc, 0xa7, 0xd3, 0xeb, 0x85, 0xbf, 0xfc, 0x9f, 0xaf, 0xbf, 0xf0, 0xf3, 0x5f, 0x8e, 0x9a, 0xb0,
- 0xa8, 0x9a, 0xb0, 0xa8, 0x04, 0x2e, 0xf6, 0x0f, 0xbb, 0x8b, 0xac, 0x09, 0x51, 0x89, 0x6a, 0xc2,
- 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xea, 0x57, 0x1d, 0x48, 0x12, 0x9e, 0x00, 0x00,
+ 0xd5, 0x5b, 0x55, 0xcd, 0x19, 0xca, 0x31, 0xd6, 0xde, 0x4d, 0xa0, 0x0d, 0x92, 0x20, 0x81, 0x9f,
+ 0x0c, 0x04, 0x4e, 0x90, 0x20, 0x80, 0x1f, 0x0c, 0xe7, 0x21, 0xc8, 0xe6, 0x21, 0x40, 0x7e, 0x1c,
+ 0x04, 0xc9, 0xe6, 0x7f, 0x11, 0x04, 0x88, 0xf2, 0x42, 0x64, 0x19, 0xe4, 0x21, 0x01, 0x1c, 0x18,
+ 0x31, 0x12, 0x3b, 0x03, 0x23, 0x1b, 0xdc, 0xbf, 0xfa, 0xeb, 0xea, 0x19, 0xb2, 0xab, 0x39, 0x1a,
+ 0xc5, 0x7a, 0xeb, 0xbe, 0xe7, 0xdc, 0xef, 0xdc, 0xba, 0x75, 0xeb, 0xde, 0x73, 0xcf, 0x39, 0xf7,
+ 0x5c, 0x58, 0xef, 0x5a, 0xc1, 0xc1, 0x60, 0x6f, 0xc1, 0x70, 0x7b, 0x8b, 0xce, 0xa0, 0xa7, 0xf7,
+ 0x3d, 0xf7, 0x7d, 0xfe, 0x63, 0xdf, 0x76, 0x1f, 0x2c, 0xf6, 0x0f, 0xbb, 0x8b, 0x7a, 0xdf, 0xf2,
+ 0xa3, 0x92, 0xa3, 0x57, 0x74, 0xbb, 0x7f, 0xa0, 0xbf, 0xb2, 0xd8, 0xa5, 0x0e, 0xf5, 0xf4, 0x80,
+ 0x9a, 0x0b, 0x7d, 0xcf, 0x0d, 0x5c, 0xf2, 0xa5, 0x08, 0x68, 0x41, 0x01, 0x2d, 0xa8, 0x6a, 0x0b,
+ 0xfd, 0xc3, 0xee, 0x02, 0x03, 0x8a, 0x4a, 0x14, 0xd0, 0xb5, 0x9f, 0x8e, 0xb5, 0xa0, 0xeb, 0x76,
+ 0xdd, 0x45, 0x8e, 0xb7, 0x37, 0xd8, 0xe7, 0xff, 0xf8, 0x1f, 0xfe, 0x4b, 0xc8, 0xb9, 0xa6, 0x1d,
+ 0xbe, 0xee, 0x2f, 0x58, 0x2e, 0x6b, 0xd6, 0xa2, 0xe1, 0x7a, 0x74, 0xf1, 0x68, 0xa8, 0x2d, 0xd7,
+ 0xbe, 0x18, 0xf1, 0xf4, 0x74, 0xe3, 0xc0, 0x72, 0xa8, 0x77, 0xac, 0x9e, 0x65, 0xd1, 0xa3, 0xbe,
+ 0x3b, 0xf0, 0x0c, 0x7a, 0xae, 0x5a, 0xfe, 0x62, 0x8f, 0x06, 0x7a, 0x96, 0xac, 0xc5, 0x51, 0xb5,
+ 0xbc, 0x81, 0x13, 0x58, 0xbd, 0x61, 0x31, 0xaf, 0x3d, 0xa9, 0x82, 0x6f, 0x1c, 0xd0, 0x9e, 0x3e,
+ 0x54, 0xef, 0x67, 0x46, 0xd5, 0x1b, 0x04, 0x96, 0xbd, 0x68, 0x39, 0x81, 0x1f, 0x78, 0xe9, 0x4a,
+ 0xda, 0xef, 0x00, 0x5c, 0x5a, 0xde, 0xf3, 0x03, 0x4f, 0x37, 0x82, 0x6d, 0xd7, 0xdc, 0xa1, 0xbd,
+ 0xbe, 0xad, 0x07, 0x94, 0x1c, 0x42, 0x9d, 0x3d, 0x90, 0xa9, 0x07, 0xfa, 0x7c, 0xe1, 0x46, 0xe1,
+ 0x66, 0x73, 0x69, 0x79, 0x61, 0xcc, 0x17, 0xb8, 0xb0, 0x25, 0x81, 0xda, 0x53, 0xa7, 0x27, 0xad,
+ 0xba, 0xfa, 0x87, 0xa1, 0x00, 0xf2, 0xeb, 0x05, 0x98, 0x72, 0x5c, 0x93, 0x76, 0xa8, 0x4d, 0x8d,
+ 0xc0, 0xf5, 0xe6, 0x8b, 0x37, 0x4a, 0x37, 0x9b, 0x4b, 0xdf, 0x18, 0x5b, 0x62, 0xc6, 0x13, 0x2d,
+ 0xdc, 0x8d, 0x09, 0xb8, 0xe5, 0x04, 0xde, 0x71, 0xfb, 0xf2, 0xf7, 0x4f, 0x5a, 0xcf, 0x9d, 0x9e,
+ 0xb4, 0xa6, 0xe2, 0x24, 0x4c, 0xb4, 0x84, 0xec, 0x42, 0x33, 0x70, 0x6d, 0xd6, 0x65, 0x96, 0xeb,
+ 0xf8, 0xf3, 0x25, 0xde, 0xb0, 0xeb, 0x0b, 0xa2, 0xab, 0x99, 0xf8, 0x05, 0x36, 0xc6, 0x16, 0x8e,
+ 0x5e, 0x59, 0xd8, 0x09, 0xd9, 0xda, 0x97, 0x24, 0x70, 0x33, 0x2a, 0xf3, 0x31, 0x8e, 0x43, 0x28,
+ 0xcc, 0xfa, 0xd4, 0x18, 0x78, 0x56, 0x70, 0xbc, 0xe2, 0x3a, 0x01, 0x7d, 0x18, 0xcc, 0x97, 0x79,
+ 0x2f, 0x7f, 0x3e, 0x0b, 0x7a, 0xdb, 0x35, 0x3b, 0x49, 0xee, 0xf6, 0xa5, 0xd3, 0x93, 0xd6, 0x6c,
+ 0xaa, 0x10, 0xd3, 0x98, 0xc4, 0x81, 0x39, 0xab, 0xa7, 0x77, 0xe9, 0xf6, 0xc0, 0xb6, 0x3b, 0xd4,
+ 0xf0, 0x68, 0xe0, 0xcf, 0x57, 0xf8, 0x23, 0xdc, 0xcc, 0x92, 0xb3, 0xe9, 0x1a, 0xba, 0x7d, 0x6f,
+ 0xef, 0x7d, 0x6a, 0x04, 0x48, 0xf7, 0xa9, 0x47, 0x1d, 0x83, 0xb6, 0xe7, 0xe5, 0xc3, 0xcc, 0x6d,
+ 0xa4, 0x90, 0x70, 0x08, 0x9b, 0xac, 0xc3, 0xf3, 0x7d, 0xcf, 0x72, 0x79, 0x13, 0x6c, 0xdd, 0xf7,
+ 0xef, 0xea, 0x3d, 0x3a, 0x5f, 0xbd, 0x51, 0xb8, 0xd9, 0x68, 0x5f, 0x95, 0x30, 0xcf, 0x6f, 0xa7,
+ 0x19, 0x70, 0xb8, 0x0e, 0xb9, 0x09, 0x75, 0x55, 0x38, 0x5f, 0xbb, 0x51, 0xb8, 0x59, 0x11, 0x63,
+ 0x47, 0xd5, 0xc5, 0x90, 0x4a, 0xd6, 0xa0, 0xae, 0xef, 0xef, 0x5b, 0x0e, 0xe3, 0xac, 0xf3, 0x2e,
+ 0x7c, 0x29, 0xeb, 0xd1, 0x96, 0x25, 0x8f, 0xc0, 0x51, 0xff, 0x30, 0xac, 0x4b, 0xde, 0x02, 0xe2,
+ 0x53, 0xef, 0xc8, 0x32, 0xe8, 0xb2, 0x61, 0xb8, 0x03, 0x27, 0xe0, 0x6d, 0x6f, 0xf0, 0xb6, 0x5f,
+ 0x93, 0x6d, 0x27, 0x9d, 0x21, 0x0e, 0xcc, 0xa8, 0x45, 0xde, 0x84, 0x39, 0xf9, 0xad, 0x46, 0xbd,
+ 0x00, 0x1c, 0xe9, 0x32, 0xeb, 0x48, 0x4c, 0xd1, 0x70, 0x88, 0x9b, 0x98, 0xf0, 0x92, 0x3e, 0x08,
+ 0xdc, 0x1e, 0x83, 0x4c, 0x0a, 0xdd, 0x71, 0x0f, 0xa9, 0x33, 0xdf, 0xbc, 0x51, 0xb8, 0x59, 0x6f,
+ 0xdf, 0x38, 0x3d, 0x69, 0xbd, 0xb4, 0xfc, 0x18, 0x3e, 0x7c, 0x2c, 0x0a, 0xb9, 0x07, 0x0d, 0xd3,
+ 0xf1, 0xb7, 0x5d, 0xdb, 0x32, 0x8e, 0xe7, 0xa7, 0x78, 0x03, 0x5f, 0x91, 0x8f, 0xda, 0x58, 0xbd,
+ 0xdb, 0x11, 0x84, 0x47, 0x27, 0xad, 0x97, 0x86, 0xa7, 0xd4, 0x85, 0x90, 0x8e, 0x11, 0x06, 0xd9,
+ 0xe2, 0x80, 0x2b, 0xae, 0xb3, 0x6f, 0x75, 0xe7, 0xa7, 0xf9, 0xdb, 0xb8, 0x31, 0x62, 0x40, 0xaf,
+ 0xde, 0xed, 0x08, 0xbe, 0xf6, 0xb4, 0x14, 0x27, 0xfe, 0x62, 0x84, 0x40, 0x4c, 0x98, 0x51, 0x93,
+ 0xf1, 0x8a, 0xad, 0x5b, 0x3d, 0x7f, 0x7e, 0x86, 0x0f, 0xde, 0x1f, 0x1f, 0x81, 0x89, 0x71, 0xe6,
+ 0xf6, 0x15, 0xf9, 0x28, 0x33, 0x89, 0x62, 0x1f, 0x53, 0x98, 0xd7, 0xde, 0x80, 0xe7, 0x87, 0xe6,
+ 0x06, 0x32, 0x07, 0xa5, 0x43, 0x7a, 0xcc, 0xa7, 0xbe, 0x06, 0xb2, 0x9f, 0xe4, 0x32, 0x54, 0x8e,
+ 0x74, 0x7b, 0x40, 0xe7, 0x8b, 0xbc, 0x4c, 0xfc, 0xf9, 0xd9, 0xe2, 0xeb, 0x05, 0xed, 0x6f, 0x96,
+ 0x60, 0x4a, 0xcd, 0x38, 0x1d, 0xcb, 0x39, 0x24, 0x6f, 0x43, 0xc9, 0x76, 0xbb, 0x72, 0xde, 0xfc,
+ 0xf9, 0xb1, 0x67, 0xb1, 0x4d, 0xb7, 0xdb, 0xae, 0x9d, 0x9e, 0xb4, 0x4a, 0x9b, 0x6e, 0x17, 0x19,
+ 0x22, 0x31, 0xa0, 0x72, 0xa8, 0xef, 0x1f, 0xea, 0xbc, 0x0d, 0xcd, 0xa5, 0xf6, 0xd8, 0xd0, 0x77,
+ 0x18, 0x0a, 0x6b, 0x6b, 0xbb, 0x71, 0x7a, 0xd2, 0xaa, 0xf0, 0xbf, 0x28, 0xb0, 0x89, 0x0b, 0x8d,
+ 0x3d, 0x5b, 0x37, 0x0e, 0x0f, 0x5c, 0x9b, 0xce, 0x97, 0x72, 0x0a, 0x6a, 0x2b, 0x24, 0xf1, 0x9a,
+ 0xc3, 0xbf, 0x18, 0xc9, 0x20, 0x06, 0x54, 0x07, 0xa6, 0x6f, 0x39, 0x87, 0x72, 0x0e, 0x7c, 0x63,
+ 0x6c, 0x69, 0xbb, 0xab, 0xfc, 0x99, 0xe0, 0xf4, 0xa4, 0x55, 0x15, 0xbf, 0x51, 0x42, 0x6b, 0x7f,
+ 0x38, 0x05, 0x33, 0xea, 0x25, 0xdd, 0xa7, 0x5e, 0x40, 0x1f, 0x92, 0x1b, 0x50, 0x76, 0xd8, 0xa7,
+ 0xc9, 0x5f, 0x72, 0x7b, 0x4a, 0x0e, 0x97, 0x32, 0xff, 0x24, 0x39, 0x85, 0xb5, 0x4c, 0x0c, 0x15,
+ 0xd9, 0xe1, 0xe3, 0xb7, 0xac, 0xc3, 0x61, 0x44, 0xcb, 0xc4, 0x6f, 0x94, 0xd0, 0xe4, 0x5d, 0x28,
+ 0xf3, 0x87, 0x17, 0x5d, 0xfd, 0x95, 0xf1, 0x45, 0xb0, 0x47, 0xaf, 0xb3, 0x27, 0xe0, 0x0f, 0xce,
+ 0x41, 0xd9, 0x50, 0x1c, 0x98, 0xfb, 0xb2, 0x63, 0x7f, 0x3e, 0x47, 0xc7, 0xae, 0x89, 0xa1, 0xb8,
+ 0xbb, 0xba, 0x86, 0x0c, 0x91, 0xfc, 0xe5, 0x02, 0x3c, 0x6f, 0xb8, 0x4e, 0xa0, 0x33, 0x3d, 0x43,
+ 0x2d, 0xb2, 0xf3, 0x15, 0x2e, 0xe7, 0xad, 0xb1, 0xe5, 0xac, 0xa4, 0x11, 0xdb, 0x2f, 0xb0, 0x35,
+ 0x63, 0xa8, 0x18, 0x87, 0x65, 0x93, 0xbf, 0x5a, 0x80, 0x17, 0xd8, 0x5c, 0x3e, 0xc4, 0xcc, 0x57,
+ 0xa0, 0xc9, 0xb6, 0xea, 0xea, 0xe9, 0x49, 0xeb, 0x85, 0x8d, 0x2c, 0x61, 0x98, 0xdd, 0x06, 0xd6,
+ 0xba, 0x4b, 0xfa, 0xb0, 0x5a, 0xc2, 0x57, 0xb7, 0xe6, 0xd2, 0xe6, 0x24, 0x55, 0x9d, 0xf6, 0x67,
+ 0xe4, 0x50, 0xce, 0xd2, 0xec, 0x30, 0xab, 0x15, 0xe4, 0x16, 0xd4, 0x8e, 0x5c, 0x7b, 0xd0, 0xa3,
+ 0xfe, 0x7c, 0x9d, 0x4f, 0xb1, 0xd7, 0xb2, 0xa6, 0xd8, 0xfb, 0x9c, 0xa5, 0x3d, 0x2b, 0xe1, 0x6b,
+ 0xe2, 0xbf, 0x8f, 0xaa, 0x2e, 0xb1, 0xa0, 0x6a, 0x5b, 0x3d, 0x2b, 0xf0, 0xf9, 0xc2, 0xd9, 0x5c,
+ 0xba, 0x35, 0xf6, 0x63, 0x89, 0x4f, 0x74, 0x93, 0x83, 0x89, 0xaf, 0x46, 0xfc, 0x46, 0x29, 0x80,
+ 0x4d, 0x85, 0xbe, 0xa1, 0xdb, 0x62, 0x61, 0x6d, 0x2e, 0x7d, 0x75, 0xfc, 0xcf, 0x86, 0xa1, 0xb4,
+ 0xa7, 0xe5, 0x33, 0x55, 0xf8, 0x5f, 0x14, 0xd8, 0xe4, 0x17, 0x61, 0x26, 0xf1, 0x36, 0xfd, 0xf9,
+ 0x26, 0xef, 0x9d, 0x97, 0xb3, 0x7a, 0x27, 0xe4, 0x8a, 0x56, 0x9e, 0xc4, 0x08, 0xf1, 0x31, 0x05,
+ 0x46, 0xee, 0x40, 0xdd, 0xb7, 0x4c, 0x6a, 0xe8, 0x9e, 0x3f, 0x3f, 0x75, 0x16, 0xe0, 0x39, 0x09,
+ 0x5c, 0xef, 0xc8, 0x6a, 0x18, 0x02, 0x90, 0x05, 0x80, 0xbe, 0xee, 0x05, 0x96, 0x50, 0x54, 0xa7,
+ 0xb9, 0xd2, 0x34, 0x73, 0x7a, 0xd2, 0x82, 0xed, 0xb0, 0x14, 0x63, 0x1c, 0x8c, 0x9f, 0xd5, 0xdd,
+ 0x70, 0xfa, 0x83, 0x40, 0x2c, 0xac, 0x0d, 0xc1, 0xdf, 0x09, 0x4b, 0x31, 0xc6, 0x41, 0x7e, 0xbb,
+ 0x00, 0x9f, 0x89, 0xfe, 0x0e, 0x7f, 0x64, 0xb3, 0x13, 0xff, 0xc8, 0x5a, 0xa7, 0x27, 0xad, 0xcf,
+ 0x74, 0x46, 0x8b, 0xc4, 0xc7, 0xb5, 0x87, 0x7c, 0x58, 0x80, 0x99, 0x41, 0xdf, 0xd4, 0x03, 0xda,
+ 0x09, 0xd8, 0x8e, 0xa7, 0x7b, 0x3c, 0x3f, 0xc7, 0x9b, 0xb8, 0x3e, 0xfe, 0x2c, 0x98, 0x80, 0x8b,
+ 0x5e, 0x73, 0xb2, 0x1c, 0x53, 0x62, 0xb5, 0xb7, 0x61, 0x7a, 0x79, 0x10, 0x1c, 0xb8, 0x9e, 0xf5,
+ 0x01, 0x57, 0xff, 0xc9, 0x1a, 0x54, 0x02, 0xae, 0xc6, 0x09, 0x0d, 0xe1, 0x73, 0x59, 0x2f, 0x5d,
+ 0xa8, 0xd4, 0x77, 0xe8, 0xb1, 0xd2, 0x4b, 0xc4, 0x4a, 0x2d, 0xd4, 0x3a, 0x51, 0x5d, 0xfb, 0xb3,
+ 0x05, 0xa8, 0xb5, 0x75, 0xe3, 0xd0, 0xdd, 0xdf, 0x27, 0xef, 0x40, 0xdd, 0x72, 0x02, 0xea, 0x1d,
+ 0xe9, 0xb6, 0x84, 0x5d, 0x88, 0xc1, 0x86, 0x1b, 0xc2, 0xe8, 0xf1, 0xd8, 0xee, 0x8b, 0x09, 0x5a,
+ 0x1d, 0xc8, 0x5d, 0x0b, 0xd7, 0x8c, 0x37, 0x24, 0x06, 0x86, 0x68, 0xa4, 0x05, 0x15, 0x3f, 0xa0,
+ 0x7d, 0x9f, 0xaf, 0x81, 0xd3, 0xa2, 0x19, 0x1d, 0x56, 0x80, 0xa2, 0x5c, 0xfb, 0x1b, 0x05, 0x68,
+ 0xb4, 0x75, 0xdf, 0x32, 0xd8, 0x53, 0x92, 0x15, 0x28, 0x0f, 0x7c, 0xea, 0x9d, 0xef, 0xd9, 0xf8,
+ 0xb2, 0xb5, 0xeb, 0x53, 0x0f, 0x79, 0x65, 0x72, 0x0f, 0xea, 0x7d, 0xdd, 0xf7, 0x1f, 0xb8, 0x9e,
+ 0x29, 0x97, 0xde, 0x33, 0x02, 0x89, 0x6d, 0x82, 0xac, 0x8a, 0x21, 0x88, 0xd6, 0x84, 0x48, 0xf7,
+ 0xd0, 0x7e, 0xbf, 0x00, 0x97, 0xda, 0x83, 0xfd, 0x7d, 0xea, 0x49, 0xad, 0x58, 0xea, 0x9b, 0x14,
+ 0x2a, 0x1e, 0x35, 0x2d, 0x5f, 0xb6, 0x7d, 0x75, 0xec, 0x81, 0x82, 0x0c, 0x45, 0xaa, 0xb7, 0xbc,
+ 0xbf, 0x78, 0x01, 0x0a, 0x74, 0x32, 0x80, 0xc6, 0xfb, 0x94, 0xed, 0xc6, 0xa9, 0xde, 0x93, 0x4f,
+ 0x77, 0x7b, 0x6c, 0x51, 0x6f, 0xd1, 0xa0, 0xc3, 0x91, 0xe2, 0xda, 0x74, 0x58, 0x88, 0x91, 0x24,
+ 0xed, 0x77, 0x2a, 0x30, 0xb5, 0xe2, 0xf6, 0xf6, 0x2c, 0x87, 0x9a, 0xb7, 0xcc, 0x2e, 0x25, 0xef,
+ 0x41, 0x99, 0x9a, 0x5d, 0x2a, 0x9f, 0x76, 0x7c, 0xc5, 0x83, 0x81, 0x45, 0xea, 0x13, 0xfb, 0x87,
+ 0x1c, 0x98, 0x6c, 0xc2, 0xcc, 0xbe, 0xe7, 0xf6, 0xc4, 0x5c, 0xbe, 0x73, 0xdc, 0x97, 0xba, 0x73,
+ 0xfb, 0xc7, 0xd5, 0x87, 0xb3, 0x96, 0xa0, 0x3e, 0x3a, 0x69, 0x41, 0xf4, 0x0f, 0x53, 0x75, 0xc9,
+ 0x3b, 0x30, 0x1f, 0x95, 0x84, 0x93, 0xda, 0x0a, 0xdb, 0xce, 0x70, 0xdd, 0xa9, 0xd2, 0x7e, 0xe9,
+ 0xf4, 0xa4, 0x35, 0xbf, 0x36, 0x82, 0x07, 0x47, 0xd6, 0x66, 0x53, 0xc5, 0x5c, 0x44, 0x14, 0x0b,
+ 0x8d, 0x54, 0x99, 0x26, 0xb4, 0x82, 0xf1, 0x7d, 0xdf, 0x5a, 0x4a, 0x04, 0x0e, 0x09, 0x25, 0x6b,
+ 0x30, 0x15, 0xb8, 0xb1, 0xfe, 0xaa, 0xf0, 0xfe, 0xd2, 0x94, 0xa1, 0x62, 0xc7, 0x1d, 0xd9, 0x5b,
+ 0x89, 0x7a, 0x04, 0xe1, 0x8a, 0xfa, 0x9f, 0xea, 0xa9, 0x2a, 0xef, 0xa9, 0x6b, 0xa7, 0x27, 0xad,
+ 0x2b, 0x3b, 0x99, 0x1c, 0x38, 0xa2, 0x26, 0xf9, 0xd5, 0x02, 0xcc, 0x28, 0x92, 0xec, 0xa3, 0xda,
+ 0x24, 0xfb, 0x88, 0xb0, 0x11, 0xb1, 0x93, 0x10, 0x80, 0x29, 0x81, 0xda, 0xef, 0x56, 0xa1, 0x11,
+ 0x4e, 0xf5, 0xe4, 0xb3, 0x50, 0xe1, 0x26, 0x08, 0xa9, 0xc1, 0x87, 0x6b, 0x38, 0xb7, 0x54, 0xa0,
+ 0xa0, 0x91, 0xcf, 0x41, 0xcd, 0x70, 0x7b, 0x3d, 0xdd, 0x31, 0xb9, 0x59, 0xa9, 0xd1, 0x6e, 0x32,
+ 0xd5, 0x65, 0x45, 0x14, 0xa1, 0xa2, 0x91, 0x97, 0xa0, 0xac, 0x7b, 0x5d, 0x61, 0xe1, 0x69, 0x88,
+ 0xf9, 0x68, 0xd9, 0xeb, 0xfa, 0xc8, 0x4b, 0xc9, 0x97, 0xa1, 0x44, 0x9d, 0xa3, 0xf9, 0xf2, 0x68,
+ 0xdd, 0xe8, 0x96, 0x73, 0x74, 0x5f, 0xf7, 0xda, 0x4d, 0xd9, 0x86, 0xd2, 0x2d, 0xe7, 0x08, 0x59,
+ 0x1d, 0xb2, 0x09, 0x35, 0xea, 0x1c, 0xb1, 0x77, 0x2f, 0x4d, 0x2f, 0x3f, 0x36, 0xa2, 0x3a, 0x63,
+ 0x91, 0xdb, 0x84, 0x50, 0xc3, 0x92, 0xc5, 0xa8, 0x20, 0xc8, 0xd7, 0x61, 0x4a, 0x28, 0x5b, 0x5b,
+ 0xec, 0x9d, 0xf8, 0xf3, 0x55, 0x0e, 0xd9, 0x1a, 0xad, 0xad, 0x71, 0xbe, 0xc8, 0xd4, 0x15, 0x2b,
+ 0xf4, 0x31, 0x01, 0x45, 0xbe, 0x0e, 0x0d, 0xb5, 0x33, 0x56, 0x6f, 0x36, 0xd3, 0x4a, 0xa4, 0xb6,
+ 0xd3, 0x48, 0xbf, 0x39, 0xb0, 0x3c, 0xda, 0xa3, 0x4e, 0xe0, 0xb7, 0x9f, 0x57, 0x76, 0x03, 0x45,
+ 0xf5, 0x31, 0x42, 0x23, 0x7b, 0xc3, 0xe6, 0x2e, 0x61, 0xab, 0xf9, 0xec, 0x88, 0x59, 0x7d, 0x0c,
+ 0x5b, 0xd7, 0x37, 0x60, 0x36, 0xb4, 0x47, 0x49, 0x93, 0x86, 0xb0, 0xde, 0x7c, 0x91, 0x55, 0xdf,
+ 0x48, 0x92, 0x1e, 0x9d, 0xb4, 0x5e, 0xce, 0x30, 0x6a, 0x44, 0x0c, 0x98, 0x06, 0x23, 0x1f, 0xc0,
+ 0x8c, 0x47, 0x75, 0xd3, 0x72, 0xa8, 0xef, 0x6f, 0x7b, 0xee, 0x5e, 0x7e, 0xcd, 0x93, 0xa3, 0x88,
+ 0x61, 0x8f, 0x09, 0x64, 0x4c, 0x49, 0x22, 0x0f, 0x60, 0xda, 0xb6, 0x8e, 0x68, 0x24, 0xba, 0x39,
+ 0x11, 0xd1, 0xcf, 0x9f, 0x9e, 0xb4, 0xa6, 0x37, 0xe3, 0xc0, 0x98, 0x94, 0xa3, 0xfd, 0xdd, 0x0a,
+ 0x0c, 0x6f, 0xbe, 0x92, 0x23, 0xa5, 0x30, 0xe9, 0x91, 0x92, 0x7e, 0x8b, 0x62, 0xcd, 0x78, 0x5d,
+ 0x56, 0x9b, 0xc0, 0x9b, 0xcc, 0x18, 0x8d, 0xa5, 0x49, 0x8f, 0xc6, 0x67, 0x66, 0xc2, 0x18, 0x1e,
+ 0xb6, 0xd5, 0x8f, 0x6f, 0xd8, 0xd6, 0x9e, 0xd2, 0xb0, 0xfd, 0x6e, 0x19, 0x66, 0x56, 0x75, 0xda,
+ 0x73, 0x9d, 0x27, 0xee, 0xbf, 0x0b, 0xcf, 0xc4, 0xfe, 0xfb, 0x26, 0xd4, 0x3d, 0xda, 0xb7, 0x2d,
+ 0x43, 0x17, 0x6a, 0xb6, 0xb4, 0x77, 0xa3, 0x2c, 0xc3, 0x90, 0x3a, 0xc2, 0xee, 0x52, 0x7a, 0x26,
+ 0xed, 0x2e, 0xe5, 0x8f, 0xdf, 0xee, 0xa2, 0xfd, 0x6a, 0x11, 0xb8, 0x4a, 0x4a, 0x6e, 0x40, 0x99,
+ 0xa9, 0x5b, 0x69, 0x6b, 0x1f, 0xff, 0x5a, 0x38, 0x85, 0x5c, 0x83, 0x62, 0xe0, 0xca, 0xe9, 0x06,
+ 0x24, 0xbd, 0xb8, 0xe3, 0x62, 0x31, 0x70, 0xc9, 0x07, 0x00, 0x86, 0xeb, 0x98, 0x96, 0x72, 0x03,
+ 0xe5, 0x7b, 0xb0, 0x35, 0xd7, 0x7b, 0xa0, 0x7b, 0xe6, 0x4a, 0x88, 0x28, 0x76, 0xde, 0xd1, 0x7f,
+ 0x8c, 0x49, 0x23, 0x6f, 0x40, 0xd5, 0x75, 0xd6, 0x06, 0xb6, 0xcd, 0x3b, 0xb4, 0xd1, 0xfe, 0xc2,
+ 0xe9, 0x49, 0xab, 0x7a, 0x8f, 0x97, 0x3c, 0x3a, 0x69, 0x5d, 0x15, 0x3b, 0x19, 0xf6, 0xef, 0x6d,
+ 0xcf, 0x0a, 0x2c, 0xa7, 0x1b, 0x6e, 0x44, 0x65, 0x35, 0xed, 0xd7, 0x0a, 0xd0, 0x5c, 0xb3, 0x1e,
+ 0x52, 0xf3, 0x6d, 0xcb, 0x31, 0xdd, 0x07, 0x04, 0xa1, 0x6a, 0x53, 0xa7, 0x1b, 0x1c, 0x8c, 0xb9,
+ 0x53, 0x14, 0xf6, 0x18, 0x8e, 0x80, 0x12, 0x89, 0x2c, 0x42, 0x43, 0xec, 0x33, 0x2c, 0xa7, 0xcb,
+ 0xfb, 0xb0, 0x1e, 0xcd, 0xf4, 0x1d, 0x45, 0xc0, 0x88, 0x47, 0x3b, 0x86, 0xe7, 0x87, 0xba, 0x81,
+ 0x98, 0x50, 0x0e, 0xf4, 0xae, 0x5a, 0x54, 0xd6, 0xc6, 0xee, 0xe0, 0x1d, 0xbd, 0x1b, 0xeb, 0x5c,
+ 0xae, 0xcd, 0xed, 0xe8, 0x4c, 0x9b, 0x63, 0xe8, 0xda, 0x1f, 0x15, 0xa0, 0xbe, 0x36, 0x70, 0x0c,
+ 0xbe, 0x19, 0x7f, 0xb2, 0x15, 0x58, 0xa9, 0x86, 0xc5, 0x4c, 0xd5, 0x70, 0x00, 0xd5, 0xc3, 0x07,
+ 0xa1, 0xea, 0xd8, 0x5c, 0xda, 0x1a, 0x7f, 0x54, 0xc8, 0x26, 0x2d, 0xdc, 0xe1, 0x78, 0xc2, 0x49,
+ 0x39, 0x23, 0x1b, 0x54, 0xbd, 0xf3, 0x36, 0x17, 0x2a, 0x85, 0x5d, 0xfb, 0x32, 0x34, 0x63, 0x6c,
+ 0xe7, 0xf2, 0x57, 0xfc, 0xbd, 0x32, 0x54, 0xd7, 0x3b, 0x9d, 0xe5, 0xed, 0x0d, 0xf2, 0x2a, 0x34,
+ 0xa5, 0xff, 0xea, 0x6e, 0xd4, 0x07, 0xa1, 0xfb, 0xb2, 0x13, 0x91, 0x30, 0xce, 0xc7, 0x14, 0x6f,
+ 0x8f, 0xea, 0x76, 0x4f, 0x7e, 0x2c, 0xa1, 0xe2, 0x8d, 0xac, 0x10, 0x05, 0x8d, 0xe8, 0x30, 0xc3,
+ 0xf6, 0xf2, 0xac, 0x0b, 0xc5, 0x3e, 0x5d, 0x7e, 0x36, 0x67, 0xdc, 0xc9, 0xf3, 0x05, 0x66, 0x37,
+ 0x01, 0x80, 0x29, 0x40, 0xf2, 0x3a, 0xd4, 0xf5, 0x41, 0x70, 0xc0, 0xb7, 0x4a, 0xe2, 0xdb, 0x78,
+ 0x89, 0xbb, 0xf7, 0x64, 0xd9, 0xa3, 0x93, 0xd6, 0xd4, 0x1d, 0x6c, 0xbf, 0xaa, 0xfe, 0x63, 0xc8,
+ 0xcd, 0x1a, 0xa7, 0x6c, 0x03, 0xb2, 0x71, 0x95, 0x73, 0x37, 0x6e, 0x3b, 0x01, 0x80, 0x29, 0x40,
+ 0xf2, 0x2e, 0x4c, 0x1d, 0xd2, 0xe3, 0x40, 0xdf, 0x93, 0x02, 0xaa, 0xe7, 0x11, 0x30, 0xc7, 0x94,
+ 0xf5, 0x3b, 0xb1, 0xea, 0x98, 0x00, 0x23, 0x3e, 0x5c, 0x3e, 0xa4, 0xde, 0x1e, 0xf5, 0x5c, 0x69,
+ 0x67, 0x90, 0x42, 0x6a, 0xe7, 0x11, 0x32, 0x7f, 0x7a, 0xd2, 0xba, 0x7c, 0x27, 0x03, 0x06, 0x33,
+ 0xc1, 0xb5, 0xff, 0x53, 0x84, 0xd9, 0x75, 0x11, 0x40, 0xe0, 0x7a, 0x42, 0xf3, 0x20, 0x57, 0xa1,
+ 0xe4, 0xf5, 0x07, 0x7c, 0xe4, 0x94, 0x84, 0x8b, 0x00, 0xb7, 0x77, 0x91, 0x95, 0x91, 0x77, 0xa0,
+ 0x6e, 0xca, 0x29, 0x43, 0x9a, 0x39, 0xc6, 0x32, 0x49, 0xa9, 0x7f, 0x18, 0xa2, 0xb1, 0x3d, 0x5d,
+ 0xcf, 0xef, 0x76, 0xac, 0x0f, 0xa8, 0xdc, 0xf9, 0xf3, 0x3d, 0xdd, 0x96, 0x28, 0x42, 0x45, 0x63,
+ 0xab, 0xea, 0x21, 0x3d, 0x16, 0xfb, 0xde, 0x72, 0xb4, 0xaa, 0xde, 0x91, 0x65, 0x18, 0x52, 0x49,
+ 0x4b, 0x7d, 0x2c, 0x6c, 0x14, 0x94, 0x85, 0xcd, 0xe6, 0x3e, 0x2b, 0x90, 0xdf, 0x0d, 0x9b, 0x32,
+ 0xdf, 0xb7, 0x82, 0x80, 0x7a, 0xf2, 0x35, 0x8e, 0x35, 0x65, 0xbe, 0xc5, 0x11, 0x50, 0x22, 0x91,
+ 0x9f, 0x84, 0x06, 0x07, 0x6f, 0xdb, 0xee, 0x1e, 0x7f, 0x71, 0x0d, 0x61, 0xbd, 0xb9, 0xaf, 0x0a,
+ 0x31, 0xa2, 0x6b, 0x3f, 0x2a, 0xc2, 0x95, 0x75, 0x1a, 0x08, 0xad, 0x66, 0x95, 0xf6, 0x6d, 0xf7,
+ 0x98, 0xe9, 0xd3, 0x48, 0xbf, 0x49, 0xde, 0x04, 0xb0, 0xfc, 0xbd, 0xce, 0x91, 0xc1, 0xbf, 0x03,
+ 0xf1, 0x0d, 0xdf, 0x90, 0x9f, 0x24, 0x6c, 0x74, 0xda, 0x92, 0xf2, 0x28, 0xf1, 0x0f, 0x63, 0x75,
+ 0xa2, 0x8d, 0x74, 0xf1, 0x31, 0x1b, 0xe9, 0x0e, 0x40, 0x3f, 0xd2, 0xca, 0x4b, 0x9c, 0xf3, 0x67,
+ 0x94, 0x98, 0xf3, 0x28, 0xe4, 0x31, 0x98, 0x3c, 0x7a, 0xb2, 0x03, 0x73, 0x26, 0xdd, 0xd7, 0x07,
+ 0x76, 0x10, 0xee, 0x24, 0xe4, 0x47, 0x7c, 0xf6, 0xcd, 0x48, 0x18, 0xdc, 0xb0, 0x9a, 0x42, 0xc2,
+ 0x21, 0x6c, 0xed, 0xef, 0x97, 0xe0, 0xda, 0x3a, 0x0d, 0x42, 0xdb, 0x9a, 0x9c, 0x1d, 0x3b, 0x7d,
+ 0x6a, 0xb0, 0xb7, 0xf0, 0x61, 0x01, 0xaa, 0xb6, 0xbe, 0x47, 0x6d, 0xb6, 0x7a, 0xb1, 0xa7, 0x79,
+ 0x6f, 0xec, 0x85, 0x60, 0xb4, 0x94, 0x85, 0x4d, 0x2e, 0x21, 0xb5, 0x34, 0x88, 0x42, 0x94, 0xe2,
+ 0xd9, 0xa4, 0x6e, 0xd8, 0x03, 0x3f, 0xa0, 0xde, 0xb6, 0xeb, 0x05, 0x52, 0x9f, 0x0c, 0x27, 0xf5,
+ 0x95, 0x88, 0x84, 0x71, 0x3e, 0xb2, 0x04, 0x60, 0xd8, 0x16, 0x75, 0x02, 0x5e, 0x4b, 0x7c, 0x57,
+ 0x44, 0xbd, 0xdf, 0x95, 0x90, 0x82, 0x31, 0x2e, 0x26, 0xaa, 0xe7, 0x3a, 0x56, 0xe0, 0x0a, 0x51,
+ 0xe5, 0xa4, 0xa8, 0xad, 0x88, 0x84, 0x71, 0x3e, 0x5e, 0x8d, 0x06, 0x9e, 0x65, 0xf8, 0xbc, 0x5a,
+ 0x25, 0x55, 0x2d, 0x22, 0x61, 0x9c, 0x8f, 0xad, 0x79, 0xb1, 0xe7, 0x3f, 0xd7, 0x9a, 0xf7, 0x5b,
+ 0x0d, 0xb8, 0x9e, 0xe8, 0xd6, 0x40, 0x0f, 0xe8, 0xfe, 0xc0, 0xee, 0xd0, 0x40, 0xbd, 0xc0, 0x31,
+ 0xd7, 0xc2, 0xbf, 0x10, 0xbd, 0x77, 0x11, 0xb6, 0x64, 0x4c, 0xe6, 0xbd, 0x0f, 0x35, 0xf0, 0x4c,
+ 0xef, 0x7e, 0x11, 0x1a, 0x8e, 0x1e, 0xf8, 0xfc, 0xc3, 0x95, 0xdf, 0x68, 0xa8, 0x86, 0xdd, 0x55,
+ 0x04, 0x8c, 0x78, 0xc8, 0x36, 0x5c, 0x96, 0x5d, 0x7c, 0xeb, 0x61, 0xdf, 0xf5, 0x02, 0xea, 0x89,
+ 0xba, 0x72, 0x39, 0x95, 0x75, 0x2f, 0x6f, 0x65, 0xf0, 0x60, 0x66, 0x4d, 0xb2, 0x05, 0x97, 0x0c,
+ 0x11, 0xca, 0x41, 0x6d, 0x57, 0x37, 0x15, 0xa0, 0x30, 0x65, 0x86, 0x5b, 0xa3, 0x95, 0x61, 0x16,
+ 0xcc, 0xaa, 0x97, 0x1e, 0xcd, 0xd5, 0xb1, 0x46, 0x73, 0x6d, 0x9c, 0xd1, 0x5c, 0x1f, 0x6f, 0x34,
+ 0x37, 0xce, 0x36, 0x9a, 0x59, 0xcf, 0xb3, 0x71, 0x44, 0x3d, 0xa6, 0x9e, 0x88, 0x15, 0x36, 0x16,
+ 0x29, 0x14, 0xf6, 0x7c, 0x27, 0x83, 0x07, 0x33, 0x6b, 0x92, 0x3d, 0xb8, 0x26, 0xca, 0x6f, 0x39,
+ 0x86, 0x77, 0xdc, 0x67, 0x0b, 0x4f, 0x0c, 0xb7, 0x99, 0xb0, 0x25, 0x5f, 0xeb, 0x8c, 0xe4, 0xc4,
+ 0xc7, 0xa0, 0x90, 0x9f, 0x83, 0x69, 0xf1, 0x96, 0xb6, 0xf4, 0x3e, 0x87, 0x15, 0x71, 0x43, 0x2f,
+ 0x48, 0xd8, 0xe9, 0x95, 0x38, 0x11, 0x93, 0xbc, 0x64, 0x19, 0x66, 0xfb, 0x47, 0x06, 0xfb, 0xb9,
+ 0xb1, 0x7f, 0x97, 0x52, 0x93, 0x9a, 0xdc, 0x51, 0xd9, 0x68, 0xbf, 0xa8, 0xac, 0x3b, 0xdb, 0x49,
+ 0x32, 0xa6, 0xf9, 0xc9, 0xeb, 0x30, 0xe5, 0x07, 0xba, 0x17, 0x48, 0x03, 0xee, 0xfc, 0x8c, 0x88,
+ 0xab, 0x52, 0xf6, 0xcd, 0x4e, 0x8c, 0x86, 0x09, 0xce, 0xcc, 0xf5, 0x62, 0xf6, 0xe2, 0xd6, 0x8b,
+ 0x3c, 0xb3, 0xd5, 0x3f, 0x2f, 0xc2, 0x8d, 0x75, 0x1a, 0x6c, 0xb9, 0x8e, 0x34, 0x7f, 0x67, 0x2d,
+ 0xfb, 0x67, 0xb2, 0x7e, 0x27, 0x17, 0xed, 0xe2, 0x44, 0x17, 0xed, 0xd2, 0x84, 0x16, 0xed, 0xf2,
+ 0x05, 0x2e, 0xda, 0xff, 0xb0, 0x08, 0x2f, 0x26, 0x7a, 0x72, 0xdb, 0x35, 0xd5, 0x84, 0xff, 0x69,
+ 0x07, 0x9e, 0xa1, 0x03, 0x1f, 0x09, 0xbd, 0x93, 0x3b, 0x30, 0x53, 0x1a, 0xcf, 0x77, 0xd2, 0x1a,
+ 0xcf, 0xbb, 0x79, 0x56, 0xbe, 0x0c, 0x09, 0x67, 0x5a, 0xf1, 0xde, 0x02, 0xe2, 0x49, 0x77, 0xab,
+ 0x30, 0xfd, 0xc4, 0x94, 0x9e, 0x30, 0x70, 0x13, 0x87, 0x38, 0x30, 0xa3, 0x16, 0xe9, 0xc0, 0x0b,
+ 0x3e, 0x75, 0x02, 0xcb, 0xa1, 0x76, 0x12, 0x4e, 0x68, 0x43, 0x2f, 0x4b, 0xb8, 0x17, 0x3a, 0x59,
+ 0x4c, 0x98, 0x5d, 0x37, 0xcf, 0x3c, 0xf0, 0xaf, 0x81, 0xab, 0x9c, 0xa2, 0x6b, 0x26, 0xa6, 0xb1,
+ 0x7c, 0x98, 0xd6, 0x58, 0xde, 0xcb, 0xff, 0xde, 0xc6, 0xd3, 0x56, 0x96, 0x00, 0xf8, 0x5b, 0x88,
+ 0xab, 0x2b, 0xe1, 0x22, 0x8d, 0x21, 0x05, 0x63, 0x5c, 0x6c, 0x01, 0x52, 0xfd, 0x1c, 0xd7, 0x54,
+ 0xc2, 0x05, 0xa8, 0x13, 0x27, 0x62, 0x92, 0x77, 0xa4, 0xb6, 0x53, 0x19, 0x5b, 0xdb, 0x79, 0x0b,
+ 0x48, 0xc2, 0xf0, 0x28, 0xf0, 0xaa, 0xc9, 0xb8, 0xe1, 0x8d, 0x21, 0x0e, 0xcc, 0xa8, 0x35, 0x62,
+ 0x28, 0xd7, 0x26, 0x3b, 0x94, 0xeb, 0xe3, 0x0f, 0x65, 0xf2, 0x1e, 0x5c, 0xe5, 0xa2, 0x64, 0xff,
+ 0x24, 0x81, 0x85, 0xde, 0xf3, 0x63, 0x12, 0xf8, 0x2a, 0x8e, 0x62, 0xc4, 0xd1, 0x18, 0xec, 0xfd,
+ 0x18, 0x1e, 0x35, 0x99, 0x70, 0xdd, 0x1e, 0xad, 0x13, 0xad, 0x64, 0xf0, 0x60, 0x66, 0x4d, 0x36,
+ 0xc4, 0x02, 0x36, 0x0c, 0xf5, 0x3d, 0x9b, 0x9a, 0x32, 0x6e, 0x3a, 0x1c, 0x62, 0x3b, 0x9b, 0x1d,
+ 0x49, 0xc1, 0x18, 0x57, 0x96, 0x9a, 0x32, 0x75, 0x4e, 0x35, 0x65, 0x9d, 0x5b, 0xe9, 0xf7, 0x13,
+ 0xda, 0x90, 0xd4, 0x75, 0xc2, 0x48, 0xf8, 0x95, 0x34, 0x03, 0x0e, 0xd7, 0xe1, 0x5a, 0xa2, 0xe1,
+ 0x59, 0xfd, 0xc0, 0x4f, 0x62, 0xcd, 0xa4, 0xb4, 0xc4, 0x0c, 0x1e, 0xcc, 0xac, 0xc9, 0xf4, 0xf3,
+ 0x03, 0xaa, 0xdb, 0xc1, 0x41, 0x12, 0x70, 0x36, 0xa9, 0x9f, 0xdf, 0x1e, 0x66, 0xc1, 0xac, 0x7a,
+ 0x99, 0x0b, 0xd2, 0xdc, 0xb3, 0xa9, 0x56, 0x7d, 0xbb, 0x04, 0x57, 0xd7, 0x69, 0x10, 0x86, 0x94,
+ 0x7d, 0x6a, 0x46, 0xf9, 0x18, 0xcc, 0x28, 0xbf, 0x59, 0x81, 0x4b, 0xeb, 0x34, 0x18, 0xd2, 0xc6,
+ 0xfe, 0x98, 0x76, 0xff, 0x16, 0x5c, 0x8a, 0xa2, 0x18, 0x3b, 0x81, 0xeb, 0x89, 0xb5, 0x3c, 0xb5,
+ 0x5b, 0xee, 0x0c, 0xb3, 0x60, 0x56, 0x3d, 0xf2, 0x75, 0x78, 0x91, 0x2f, 0xf5, 0x4e, 0x57, 0xd8,
+ 0x67, 0x85, 0x31, 0x21, 0x76, 0x0e, 0xa7, 0x25, 0x21, 0x5f, 0xec, 0x64, 0xb3, 0xe1, 0xa8, 0xfa,
+ 0xe4, 0x5b, 0x30, 0xd5, 0xb7, 0xfa, 0xd4, 0xb6, 0x1c, 0xae, 0x9f, 0xe5, 0x0e, 0xfe, 0xd9, 0x8e,
+ 0x81, 0x45, 0x1b, 0xb8, 0x78, 0x29, 0x26, 0x04, 0x66, 0x8e, 0xd4, 0xfa, 0x05, 0x8e, 0xd4, 0xff,
+ 0x59, 0x84, 0xda, 0xba, 0xe7, 0x0e, 0xfa, 0xed, 0x63, 0xd2, 0x85, 0xea, 0x03, 0xee, 0x3c, 0x93,
+ 0xae, 0xa9, 0xf1, 0x4f, 0x02, 0x08, 0x1f, 0x5c, 0xa4, 0x12, 0x89, 0xff, 0x28, 0xe1, 0xd9, 0x20,
+ 0x3e, 0xa4, 0xc7, 0xd4, 0x94, 0x3e, 0xb4, 0x70, 0x10, 0xdf, 0x61, 0x85, 0x28, 0x68, 0xa4, 0x07,
+ 0xb3, 0xba, 0x6d, 0xbb, 0x0f, 0xa8, 0xb9, 0xa9, 0x07, 0xdc, 0xef, 0x2d, 0x7d, 0x2b, 0xe7, 0x35,
+ 0x4b, 0xf3, 0x60, 0x86, 0xe5, 0x24, 0x14, 0xa6, 0xb1, 0xc9, 0xfb, 0x50, 0xf3, 0x03, 0xd7, 0x53,
+ 0xca, 0x56, 0x73, 0x69, 0x65, 0xfc, 0x97, 0xde, 0xfe, 0x5a, 0x47, 0x40, 0x09, 0x9b, 0xbd, 0xfc,
+ 0x83, 0x4a, 0x80, 0xf6, 0x1b, 0x05, 0x80, 0xdb, 0x3b, 0x3b, 0xdb, 0xd2, 0xbd, 0x60, 0x42, 0x59,
+ 0x1f, 0x84, 0x8e, 0xca, 0xf1, 0x1d, 0x82, 0x89, 0x00, 0x5c, 0xe9, 0xc3, 0x1b, 0x04, 0x07, 0xc8,
+ 0xd1, 0xc9, 0x4f, 0x40, 0x4d, 0x2a, 0xc8, 0xb2, 0xdb, 0xc3, 0x78, 0x0a, 0xa9, 0x44, 0xa3, 0xa2,
+ 0x6b, 0x7f, 0xa7, 0x08, 0xb0, 0x61, 0xda, 0xb4, 0xa3, 0x0e, 0x6f, 0x34, 0x82, 0x03, 0x8f, 0xfa,
+ 0x07, 0xae, 0x6d, 0x8e, 0xe9, 0x4d, 0xe5, 0x36, 0xff, 0x1d, 0x05, 0x82, 0x11, 0x1e, 0x31, 0x61,
+ 0xca, 0x0f, 0x68, 0x5f, 0xc5, 0xe4, 0x8e, 0xe9, 0x44, 0x99, 0x13, 0x76, 0x91, 0x08, 0x07, 0x13,
+ 0xa8, 0x44, 0x87, 0xa6, 0xe5, 0x18, 0xe2, 0x03, 0x69, 0x1f, 0x8f, 0x39, 0x90, 0x66, 0xd9, 0x8e,
+ 0x63, 0x23, 0x82, 0xc1, 0x38, 0xa6, 0xf6, 0x7b, 0x45, 0xb8, 0xc2, 0xe5, 0xb1, 0x66, 0x24, 0x22,
+ 0x6f, 0xc9, 0x9f, 0x1e, 0x3a, 0x68, 0xfa, 0x27, 0xcf, 0x26, 0x5a, 0x9c, 0x53, 0xdc, 0xa2, 0x81,
+ 0x1e, 0xe9, 0x73, 0x51, 0x59, 0xec, 0x74, 0xe9, 0x00, 0xca, 0x3e, 0x9b, 0xaf, 0x44, 0xef, 0x75,
+ 0xc6, 0x1e, 0x42, 0xd9, 0x0f, 0xc0, 0x67, 0xaf, 0xd0, 0x6b, 0xcc, 0x67, 0x2d, 0x2e, 0x8e, 0xfc,
+ 0x32, 0x54, 0xfd, 0x40, 0x0f, 0x06, 0xea, 0xd3, 0xdc, 0x9d, 0xb4, 0x60, 0x0e, 0x1e, 0xcd, 0x23,
+ 0xe2, 0x3f, 0x4a, 0xa1, 0xda, 0xef, 0x15, 0xe0, 0x5a, 0x76, 0xc5, 0x4d, 0xcb, 0x0f, 0xc8, 0x9f,
+ 0x1a, 0xea, 0xf6, 0x33, 0xbe, 0x71, 0x56, 0x9b, 0x77, 0x7a, 0x78, 0x16, 0x41, 0x95, 0xc4, 0xba,
+ 0x3c, 0x80, 0x8a, 0x15, 0xd0, 0x9e, 0xda, 0x5f, 0xde, 0x9b, 0xf0, 0xa3, 0xc7, 0x96, 0x76, 0x26,
+ 0x05, 0x85, 0x30, 0xed, 0xbb, 0xc5, 0x51, 0x8f, 0xcc, 0x97, 0x0f, 0x3b, 0x19, 0xdd, 0x7d, 0x27,
+ 0x5f, 0x74, 0x77, 0xb2, 0x41, 0xc3, 0x41, 0xde, 0x7f, 0x66, 0x38, 0xc8, 0xfb, 0x5e, 0xfe, 0x20,
+ 0xef, 0x54, 0x37, 0x8c, 0x8c, 0xf5, 0xfe, 0xa8, 0x04, 0x2f, 0x3d, 0x6e, 0xd8, 0xb0, 0xf5, 0x4c,
+ 0x8e, 0xce, 0xbc, 0xeb, 0xd9, 0xe3, 0xc7, 0x21, 0x59, 0x82, 0x4a, 0xff, 0x40, 0xf7, 0x95, 0x52,
+ 0xa6, 0x36, 0x2c, 0x95, 0x6d, 0x56, 0xf8, 0x88, 0x4d, 0x1a, 0x5c, 0x99, 0xe3, 0x7f, 0x51, 0xb0,
+ 0xb2, 0xe9, 0xb8, 0x47, 0x7d, 0x3f, 0xb2, 0x09, 0x84, 0xd3, 0xf1, 0x96, 0x28, 0x46, 0x45, 0x27,
+ 0x01, 0x54, 0x85, 0x89, 0x59, 0xae, 0x4c, 0xe3, 0x07, 0x72, 0x65, 0x1c, 0x08, 0x88, 0x1e, 0x4a,
+ 0x7a, 0x2b, 0xa4, 0x2c, 0xb2, 0x00, 0xe5, 0x20, 0x0a, 0xcf, 0x56, 0x5b, 0xf3, 0x72, 0x86, 0x7e,
+ 0xca, 0xf9, 0xd8, 0xc6, 0xde, 0xdd, 0xe3, 0x46, 0x75, 0x53, 0xfa, 0xcf, 0x2d, 0xd7, 0xe1, 0x0a,
+ 0x59, 0x29, 0xda, 0xd8, 0xdf, 0x1b, 0xe2, 0xc0, 0x8c, 0x5a, 0xda, 0xbf, 0xab, 0xc3, 0x95, 0xec,
+ 0xf1, 0xc0, 0xfa, 0xed, 0x88, 0x7a, 0x3e, 0xc3, 0x2e, 0x24, 0xfb, 0xed, 0xbe, 0x28, 0x46, 0x45,
+ 0xff, 0x44, 0x07, 0x9c, 0xfd, 0x66, 0x01, 0xae, 0x7a, 0xd2, 0x47, 0xf4, 0x34, 0x82, 0xce, 0x5e,
+ 0x16, 0xe6, 0x8c, 0x11, 0x02, 0x71, 0x74, 0x5b, 0xc8, 0xdf, 0x2a, 0xc0, 0x7c, 0x2f, 0x65, 0xe7,
+ 0xb8, 0xc0, 0xb3, 0x92, 0xfc, 0xfc, 0xc3, 0xd6, 0x08, 0x79, 0x38, 0xb2, 0x25, 0xe4, 0x5b, 0xd0,
+ 0xec, 0xb3, 0x71, 0xe1, 0x07, 0xd4, 0x31, 0x54, 0x80, 0xe8, 0xf8, 0x5f, 0xd2, 0x76, 0x84, 0x15,
+ 0x9e, 0x95, 0xe2, 0xfa, 0x41, 0x8c, 0x80, 0x71, 0x89, 0xcf, 0xf8, 0xe1, 0xc8, 0x9b, 0x50, 0xf7,
+ 0x69, 0x10, 0x58, 0x4e, 0x57, 0xec, 0x37, 0x1a, 0xe2, 0x5b, 0xe9, 0xc8, 0x32, 0x0c, 0xa9, 0xe4,
+ 0x27, 0xa1, 0xc1, 0x5d, 0x4e, 0xcb, 0x5e, 0xd7, 0x9f, 0x6f, 0xf0, 0x70, 0xb1, 0x69, 0x11, 0x00,
+ 0x27, 0x0b, 0x31, 0xa2, 0x93, 0x2f, 0xc2, 0xd4, 0x1e, 0xff, 0x7c, 0xe5, 0x79, 0x79, 0x61, 0xe3,
+ 0xe2, 0xda, 0x5a, 0x3b, 0x56, 0x8e, 0x09, 0x2e, 0xb2, 0x04, 0x40, 0x43, 0xbf, 0x5c, 0xda, 0x9e,
+ 0x15, 0x79, 0xec, 0x30, 0xc6, 0x45, 0x5e, 0x86, 0x52, 0x60, 0xfb, 0xdc, 0x86, 0x55, 0x8f, 0xb6,
+ 0xa0, 0x3b, 0x9b, 0x1d, 0x64, 0xe5, 0xda, 0x8f, 0x0a, 0x30, 0x9b, 0x3a, 0x46, 0xc4, 0xaa, 0x0c,
+ 0x3c, 0x5b, 0x4e, 0x23, 0x61, 0x95, 0x5d, 0xdc, 0x44, 0x56, 0x4e, 0xde, 0x93, 0x6a, 0x79, 0x31,
+ 0x67, 0x6a, 0x90, 0xbb, 0x7a, 0xe0, 0x33, 0x3d, 0x7c, 0x48, 0x23, 0xe7, 0x6e, 0xbe, 0xa8, 0x3d,
+ 0x72, 0x1d, 0x88, 0xb9, 0xf9, 0x22, 0x1a, 0x26, 0x38, 0x53, 0x06, 0xbf, 0xf2, 0x59, 0x0c, 0x7e,
+ 0xda, 0xaf, 0x15, 0x63, 0x3d, 0x20, 0x35, 0xfb, 0x27, 0xf4, 0xc0, 0xe7, 0xd9, 0x02, 0x1a, 0x2e,
+ 0xee, 0x8d, 0xf8, 0xfa, 0xc7, 0x17, 0x63, 0x49, 0x25, 0x6f, 0x8b, 0xbe, 0x2f, 0xe5, 0x3c, 0x80,
+ 0xbd, 0xb3, 0xd9, 0x11, 0xd1, 0x55, 0xea, 0xad, 0x85, 0xaf, 0xa0, 0x7c, 0x41, 0xaf, 0x40, 0xfb,
+ 0x97, 0x25, 0x68, 0xbe, 0xe5, 0xee, 0x7d, 0x42, 0x22, 0xa8, 0xb3, 0x97, 0xa9, 0xe2, 0xc7, 0xb8,
+ 0x4c, 0xed, 0xc2, 0x8b, 0x41, 0x60, 0x77, 0xa8, 0xe1, 0x3a, 0xa6, 0xbf, 0xbc, 0x1f, 0x50, 0x6f,
+ 0xcd, 0x72, 0x2c, 0xff, 0x80, 0x9a, 0xd2, 0x9d, 0xf4, 0x99, 0xd3, 0x93, 0xd6, 0x8b, 0x3b, 0x3b,
+ 0x9b, 0x59, 0x2c, 0x38, 0xaa, 0x2e, 0x9f, 0x36, 0xc4, 0x99, 0x4f, 0x7e, 0x26, 0x4a, 0xc6, 0xdc,
+ 0x88, 0x69, 0x23, 0x56, 0x8e, 0x09, 0x2e, 0xed, 0x7b, 0x45, 0x68, 0x84, 0x49, 0x1f, 0xc8, 0xe7,
+ 0xa0, 0xb6, 0xe7, 0xb9, 0x87, 0xd4, 0x13, 0x9e, 0x3b, 0x79, 0x26, 0xaa, 0x2d, 0x8a, 0x50, 0xd1,
+ 0xc8, 0x67, 0xa1, 0x12, 0xb8, 0x7d, 0xcb, 0x48, 0x1b, 0xd4, 0x76, 0x58, 0x21, 0x0a, 0xda, 0xc5,
+ 0x0d, 0xf0, 0xcf, 0x27, 0x54, 0xbb, 0xc6, 0x48, 0x65, 0xec, 0x5d, 0x28, 0xfb, 0xba, 0x6f, 0xcb,
+ 0xf5, 0x34, 0x47, 0xfe, 0x84, 0xe5, 0xce, 0xa6, 0xcc, 0x9f, 0xb0, 0xdc, 0xd9, 0x44, 0x0e, 0xaa,
+ 0xfd, 0x61, 0x11, 0x9a, 0xa2, 0xdf, 0xc4, 0xac, 0x30, 0xc9, 0x9e, 0x7b, 0x83, 0x87, 0x52, 0xf8,
+ 0x83, 0x1e, 0xf5, 0xb8, 0x99, 0x49, 0x4e, 0x72, 0x71, 0xff, 0x40, 0x44, 0x0c, 0xc3, 0x29, 0xa2,
+ 0x22, 0xd5, 0xf5, 0xe5, 0x0b, 0xec, 0xfa, 0xca, 0x99, 0xba, 0xbe, 0x7a, 0x11, 0x5d, 0xff, 0x61,
+ 0x11, 0x1a, 0x9b, 0xd6, 0x3e, 0x35, 0x8e, 0x0d, 0x9b, 0x9f, 0xfe, 0x34, 0xa9, 0x4d, 0x03, 0xba,
+ 0xee, 0xe9, 0x06, 0xdd, 0xa6, 0x9e, 0xc5, 0x93, 0x22, 0xb1, 0xef, 0x83, 0xcf, 0x40, 0xf2, 0xf4,
+ 0xe7, 0xea, 0x08, 0x1e, 0x1c, 0x59, 0x9b, 0x6c, 0xc0, 0x94, 0x49, 0x7d, 0xcb, 0xa3, 0xe6, 0x76,
+ 0x6c, 0xa3, 0xf2, 0x39, 0xb5, 0xd4, 0xac, 0xc6, 0x68, 0x8f, 0x4e, 0x5a, 0xd3, 0xca, 0x40, 0x29,
+ 0x76, 0x2c, 0x89, 0xaa, 0xec, 0x93, 0xef, 0xeb, 0x03, 0x3f, 0xab, 0x8d, 0xb1, 0x4f, 0x7e, 0x3b,
+ 0x9b, 0x05, 0x47, 0xd5, 0xd5, 0x2a, 0x50, 0xda, 0x74, 0xbb, 0xda, 0x77, 0x4b, 0x10, 0x66, 0xcf,
+ 0x22, 0x7f, 0xbe, 0x00, 0x4d, 0xdd, 0x71, 0xdc, 0x40, 0x66, 0xa6, 0x12, 0x1e, 0x78, 0xcc, 0x9d,
+ 0xa4, 0x6b, 0x61, 0x39, 0x02, 0x15, 0xce, 0xdb, 0xd0, 0xa1, 0x1c, 0xa3, 0x60, 0x5c, 0x36, 0x19,
+ 0xa4, 0xfc, 0xc9, 0x5b, 0xf9, 0x5b, 0x71, 0x06, 0xef, 0xf1, 0xb5, 0xaf, 0xc2, 0x5c, 0xba, 0xb1,
+ 0xe7, 0x71, 0x07, 0xe5, 0x72, 0xcc, 0x17, 0x01, 0xa2, 0x98, 0x92, 0xa7, 0x60, 0xc4, 0xb2, 0x12,
+ 0x46, 0xac, 0xf1, 0x53, 0x18, 0x44, 0x8d, 0x1e, 0x69, 0xb8, 0xfa, 0x66, 0xca, 0x70, 0xb5, 0x31,
+ 0x09, 0x61, 0x8f, 0x37, 0x56, 0xed, 0xc1, 0xa5, 0x88, 0x37, 0xfa, 0xe6, 0xef, 0xa4, 0xbe, 0x4c,
+ 0xa1, 0x8b, 0x7d, 0x61, 0xc4, 0x97, 0x39, 0x1b, 0x0b, 0xf2, 0x19, 0xfe, 0x36, 0xb5, 0xbf, 0x5d,
+ 0x80, 0xb9, 0xb8, 0x10, 0x7e, 0xde, 0xfa, 0x4b, 0x30, 0xed, 0x51, 0xdd, 0x6c, 0xeb, 0x81, 0x71,
+ 0xc0, 0xc3, 0xc9, 0x0b, 0x3c, 0xfe, 0x9b, 0x9f, 0x30, 0xc3, 0x38, 0x01, 0x93, 0x7c, 0x44, 0x87,
+ 0x26, 0x2b, 0xd8, 0xb1, 0x7a, 0xd4, 0x1d, 0x04, 0x63, 0x5a, 0x66, 0xf9, 0xa6, 0x08, 0x23, 0x18,
+ 0x8c, 0x63, 0x6a, 0x1f, 0x15, 0x60, 0x26, 0xde, 0xe0, 0x0b, 0xb7, 0xda, 0x1d, 0x24, 0xad, 0x76,
+ 0x2b, 0x13, 0x78, 0xef, 0x23, 0x2c, 0x75, 0xdf, 0x6e, 0xc6, 0x1f, 0x8d, 0x5b, 0xe7, 0xe2, 0x06,
+ 0x89, 0xc2, 0x63, 0x0d, 0x12, 0x9f, 0xfc, 0xa4, 0x4c, 0xa3, 0x34, 0xe9, 0xf2, 0x33, 0xac, 0x49,
+ 0x7f, 0x9c, 0x99, 0x9d, 0x62, 0xd9, 0x89, 0xaa, 0x39, 0xb2, 0x13, 0xf5, 0xc2, 0xec, 0x44, 0xb5,
+ 0x89, 0x4d, 0x6c, 0x67, 0xc9, 0x50, 0x54, 0x7f, 0xaa, 0x19, 0x8a, 0x1a, 0x17, 0x95, 0xa1, 0x08,
+ 0xf2, 0x66, 0x28, 0xfa, 0x4e, 0x01, 0x66, 0xcc, 0xc4, 0xa9, 0x5c, 0x79, 0x8e, 0x7d, 0xfc, 0xe5,
+ 0x2c, 0x79, 0xc8, 0x57, 0x1c, 0xcb, 0x4a, 0x96, 0x61, 0x4a, 0x64, 0x56, 0x5e, 0xa0, 0xa9, 0x8f,
+ 0x25, 0x2f, 0x10, 0xf9, 0x65, 0x68, 0xd8, 0x6a, 0xad, 0x93, 0xd9, 0x12, 0x37, 0x27, 0x32, 0x24,
+ 0x25, 0x66, 0x14, 0xf9, 0x1f, 0x16, 0x61, 0x24, 0x51, 0xfb, 0x83, 0x5a, 0x7c, 0x41, 0x7c, 0xda,
+ 0x7e, 0x81, 0xd7, 0x92, 0x7e, 0x81, 0x1b, 0x69, 0xbf, 0xc0, 0xd0, 0x6a, 0x2e, 0x7d, 0x03, 0x3f,
+ 0x15, 0x5b, 0x27, 0x4a, 0x3c, 0x21, 0x51, 0x38, 0xe4, 0x32, 0xd6, 0x8a, 0x65, 0x98, 0x95, 0x4a,
+ 0x80, 0x22, 0xf2, 0x49, 0x76, 0x3a, 0x8a, 0xe4, 0x5a, 0x4d, 0x92, 0x31, 0xcd, 0xcf, 0x04, 0xfa,
+ 0x2a, 0x2f, 0xad, 0xd8, 0x0d, 0x45, 0x63, 0x5c, 0xe5, 0x8c, 0x0d, 0x39, 0xd8, 0xce, 0xc9, 0xa3,
+ 0xba, 0x2f, 0xad, 0xfb, 0xb1, 0x9d, 0x13, 0xf2, 0x52, 0x94, 0xd4, 0xb8, 0x8b, 0xa3, 0xf6, 0x04,
+ 0x17, 0x87, 0x0e, 0x4d, 0x5b, 0xf7, 0x03, 0x31, 0x98, 0x4c, 0x39, 0x9b, 0xfc, 0x89, 0xb3, 0xad,
+ 0xfb, 0x4c, 0x97, 0x88, 0x14, 0xf8, 0xcd, 0x08, 0x06, 0xe3, 0x98, 0xc4, 0x84, 0x29, 0xf6, 0x97,
+ 0xcf, 0x2c, 0xe6, 0x72, 0x20, 0xb3, 0xb7, 0x9d, 0x47, 0x46, 0x68, 0x99, 0xdb, 0x8c, 0xe1, 0x60,
+ 0x02, 0x75, 0x84, 0x17, 0x04, 0xc6, 0xf1, 0x82, 0x90, 0x9f, 0x13, 0x8a, 0xdb, 0x71, 0xf8, 0x5a,
+ 0x9b, 0xfc, 0xb5, 0x86, 0x51, 0xa0, 0x18, 0x27, 0x62, 0x92, 0x97, 0x8d, 0x8a, 0x81, 0xec, 0x06,
+ 0x55, 0x7d, 0x2a, 0x39, 0x2a, 0x76, 0x93, 0x64, 0x4c, 0xf3, 0x93, 0x6d, 0xb8, 0x1c, 0x16, 0xc5,
+ 0x9b, 0x31, 0xcd, 0x71, 0xc2, 0xb0, 0xbc, 0xdd, 0x0c, 0x1e, 0xcc, 0xac, 0xc9, 0xcf, 0xb9, 0x0c,
+ 0x3c, 0x8f, 0x3a, 0xc1, 0x6d, 0xdd, 0x3f, 0x90, 0xf1, 0x7d, 0xd1, 0x39, 0x97, 0x88, 0x84, 0x71,
+ 0x3e, 0xb2, 0x04, 0x20, 0xe0, 0x78, 0xad, 0xd9, 0x64, 0x08, 0xed, 0x6e, 0x48, 0xc1, 0x18, 0x97,
+ 0xf6, 0x9d, 0x06, 0x34, 0xef, 0xea, 0x81, 0x75, 0x44, 0xb9, 0xcb, 0xf2, 0x62, 0xfc, 0x46, 0x7f,
+ 0xad, 0x00, 0x57, 0x92, 0x71, 0xa9, 0x17, 0xe8, 0x3c, 0xe2, 0xf9, 0x8c, 0x30, 0x53, 0x1a, 0x8e,
+ 0x68, 0x05, 0x77, 0x23, 0x0d, 0x85, 0xb9, 0x5e, 0xb4, 0x1b, 0xa9, 0x33, 0x4a, 0x20, 0x8e, 0x6e,
+ 0xcb, 0x27, 0xc5, 0x8d, 0xf4, 0x6c, 0x27, 0xe0, 0x4c, 0x39, 0xb9, 0x6a, 0xcf, 0x8c, 0x93, 0xab,
+ 0xfe, 0x4c, 0x68, 0xfd, 0xfd, 0x98, 0x93, 0xab, 0x91, 0x33, 0xd8, 0x4a, 0x1e, 0xe5, 0x10, 0x68,
+ 0xa3, 0x9c, 0x65, 0x3c, 0x0b, 0x83, 0x72, 0x3e, 0x30, 0x65, 0x79, 0x4f, 0xf7, 0x2d, 0x43, 0xaa,
+ 0x1d, 0x39, 0x12, 0x0e, 0xab, 0x44, 0x84, 0x22, 0x26, 0x83, 0xff, 0x45, 0x81, 0x1d, 0xe5, 0x5d,
+ 0x2c, 0xe6, 0xca, 0xbb, 0x48, 0x56, 0xa0, 0xec, 0x1c, 0xd2, 0xe3, 0xf3, 0xe5, 0x33, 0xe0, 0x9b,
+ 0xc0, 0xbb, 0x77, 0xe8, 0x31, 0xf2, 0xca, 0xda, 0xf7, 0x8a, 0x00, 0xec, 0xf1, 0xcf, 0xe6, 0x6e,
+ 0xfa, 0x09, 0xa8, 0xf9, 0x03, 0x6e, 0x18, 0x92, 0x0a, 0x53, 0x14, 0xa1, 0x26, 0x8a, 0x51, 0xd1,
+ 0xc9, 0x67, 0xa1, 0xf2, 0xcd, 0x01, 0x1d, 0xa8, 0xd8, 0x89, 0x70, 0xdf, 0xf0, 0x35, 0x56, 0x88,
+ 0x82, 0x76, 0x71, 0xa6, 0x63, 0xe5, 0x96, 0xaa, 0x5c, 0x94, 0x5b, 0xaa, 0x01, 0xb5, 0xbb, 0x2e,
+ 0x0f, 0x78, 0xd5, 0xfe, 0x7b, 0x11, 0x20, 0x0a, 0x28, 0x24, 0xbf, 0x51, 0x80, 0x17, 0xc2, 0x0f,
+ 0x2e, 0x10, 0xdb, 0x3f, 0x9e, 0xe3, 0x3b, 0xb7, 0x8b, 0x2a, 0xeb, 0x63, 0xe7, 0x33, 0xd0, 0x76,
+ 0x96, 0x38, 0xcc, 0x6e, 0x05, 0x41, 0xa8, 0xd3, 0x5e, 0x3f, 0x38, 0x5e, 0xb5, 0x3c, 0x39, 0x02,
+ 0x33, 0xe3, 0x56, 0x6f, 0x49, 0x1e, 0x51, 0x55, 0xda, 0x28, 0xf8, 0x47, 0xa4, 0x28, 0x18, 0xe2,
+ 0x90, 0x03, 0xa8, 0x3b, 0xee, 0x7b, 0x3e, 0xeb, 0x0e, 0x39, 0x1c, 0xdf, 0x1c, 0xbf, 0xcb, 0x45,
+ 0xb7, 0x0a, 0x97, 0x86, 0xfc, 0x83, 0x35, 0x47, 0x76, 0xf6, 0xaf, 0x17, 0xe1, 0x52, 0x46, 0x3f,
+ 0x90, 0x37, 0x61, 0x4e, 0xc6, 0x6e, 0x46, 0xc9, 0xee, 0x0b, 0x51, 0xb2, 0xfb, 0x4e, 0x8a, 0x86,
+ 0x43, 0xdc, 0xe4, 0x3d, 0x00, 0xdd, 0x30, 0xa8, 0xef, 0x6f, 0xb9, 0xa6, 0xda, 0x0f, 0xbc, 0xc1,
+ 0xd4, 0x97, 0xe5, 0xb0, 0xf4, 0xd1, 0x49, 0xeb, 0xa7, 0xb3, 0xc2, 0xb1, 0x53, 0xfd, 0x1c, 0x55,
+ 0xc0, 0x18, 0x24, 0xf9, 0x06, 0x80, 0xb0, 0x01, 0x84, 0x19, 0x23, 0x9e, 0x60, 0x38, 0x5b, 0x50,
+ 0x09, 0xc9, 0x16, 0xbe, 0x36, 0xd0, 0x9d, 0xc0, 0x0a, 0x8e, 0x45, 0x82, 0x9e, 0xfb, 0x21, 0x0a,
+ 0xc6, 0x10, 0xb5, 0x7f, 0x56, 0x84, 0xba, 0x72, 0x0b, 0x3c, 0x05, 0x5b, 0x70, 0x37, 0x61, 0x0b,
+ 0x9e, 0x50, 0x00, 0x76, 0x96, 0x25, 0xd8, 0x4d, 0x59, 0x82, 0xd7, 0xf3, 0x8b, 0x7a, 0xbc, 0x1d,
+ 0xf8, 0xb7, 0x8b, 0x30, 0xa3, 0x58, 0xf3, 0x5a, 0x68, 0xbf, 0x02, 0xb3, 0x22, 0x70, 0x62, 0x4b,
+ 0x7f, 0x28, 0x72, 0x15, 0xf1, 0x0e, 0x2b, 0x8b, 0x98, 0xe7, 0x76, 0x92, 0x84, 0x69, 0x5e, 0x36,
+ 0xac, 0x45, 0xd1, 0x2e, 0xdb, 0x84, 0x09, 0x57, 0xab, 0xd8, 0x6f, 0xf2, 0x61, 0xdd, 0x4e, 0xd1,
+ 0x70, 0x88, 0x3b, 0x6d, 0x22, 0x2e, 0x5f, 0x80, 0x89, 0xf8, 0x3f, 0x14, 0x60, 0x2a, 0xea, 0xaf,
+ 0x0b, 0x37, 0x10, 0xef, 0x27, 0x0d, 0xc4, 0xcb, 0xb9, 0x87, 0xc3, 0x08, 0xf3, 0xf0, 0x5f, 0xaa,
+ 0x41, 0xe2, 0x1c, 0x00, 0xd9, 0x83, 0x6b, 0x56, 0x66, 0x34, 0x63, 0x6c, 0xb6, 0x09, 0x0f, 0xb6,
+ 0x6f, 0x8c, 0xe4, 0xc4, 0xc7, 0xa0, 0x90, 0x01, 0xd4, 0x8f, 0xa8, 0x17, 0x58, 0x06, 0x55, 0xcf,
+ 0xb7, 0x9e, 0x5b, 0x25, 0x93, 0x46, 0xf0, 0xb0, 0x4f, 0xef, 0x4b, 0x01, 0x18, 0x8a, 0x22, 0x7b,
+ 0x50, 0xa1, 0x66, 0x97, 0xaa, 0xec, 0x51, 0x39, 0xb3, 0xf0, 0x86, 0xfd, 0xc9, 0xfe, 0xf9, 0x28,
+ 0xa0, 0x89, 0x1f, 0x37, 0x34, 0x95, 0x73, 0x2a, 0x58, 0x67, 0x34, 0x2f, 0x91, 0xc3, 0xd0, 0xda,
+ 0x5a, 0x99, 0xd0, 0xe4, 0xf1, 0x18, 0x5b, 0xab, 0x0f, 0x8d, 0x07, 0x7a, 0x40, 0xbd, 0x9e, 0xee,
+ 0x1d, 0xca, 0xdd, 0xc6, 0xf8, 0x4f, 0xf8, 0xb6, 0x42, 0x8a, 0x9e, 0x30, 0x2c, 0xc2, 0x48, 0x0e,
+ 0x71, 0xa1, 0x11, 0x48, 0xf5, 0x59, 0x99, 0x94, 0xc7, 0x17, 0xaa, 0x14, 0x71, 0x5f, 0x9e, 0x07,
+ 0x50, 0x7f, 0x31, 0x92, 0x41, 0x8e, 0x12, 0x29, 0xdb, 0x45, 0xa2, 0xfe, 0x76, 0x0e, 0xd7, 0x84,
+ 0x84, 0x8a, 0x96, 0x9b, 0xec, 0xd4, 0xef, 0xda, 0xff, 0xaa, 0x44, 0xd3, 0xf2, 0xd3, 0xb6, 0x13,
+ 0x7e, 0x31, 0x69, 0x27, 0xbc, 0x9e, 0xb6, 0x13, 0xa6, 0xfc, 0xf1, 0xe7, 0x8f, 0x20, 0x4e, 0x99,
+ 0xd7, 0xca, 0x17, 0x60, 0x5e, 0x7b, 0x05, 0x9a, 0x47, 0x7c, 0x26, 0x10, 0xa9, 0xa8, 0x2a, 0x7c,
+ 0x19, 0xe1, 0x33, 0xfb, 0xfd, 0xa8, 0x18, 0xe3, 0x3c, 0xac, 0x8a, 0xbc, 0xa4, 0x26, 0xcc, 0xda,
+ 0x2c, 0xab, 0x74, 0xa2, 0x62, 0x8c, 0xf3, 0xf0, 0xe0, 0x43, 0xcb, 0x39, 0x14, 0x15, 0x6a, 0xbc,
+ 0x82, 0x08, 0x3e, 0x54, 0x85, 0x18, 0xd1, 0xc9, 0x4d, 0xa8, 0x0f, 0xcc, 0x7d, 0xc1, 0x5b, 0xe7,
+ 0xbc, 0x5c, 0xc3, 0xdc, 0x5d, 0x5d, 0x93, 0xa9, 0xb1, 0x14, 0x95, 0xb5, 0xa4, 0xa7, 0xf7, 0x15,
+ 0x81, 0xef, 0x0d, 0x65, 0x4b, 0xb6, 0xa2, 0x62, 0x8c, 0xf3, 0x90, 0x9f, 0x85, 0x19, 0x8f, 0x9a,
+ 0x03, 0x83, 0x86, 0xb5, 0x80, 0xd7, 0x92, 0x39, 0x43, 0xe3, 0x14, 0x4c, 0x71, 0x8e, 0x30, 0x12,
+ 0x36, 0xc7, 0x32, 0x12, 0x7e, 0x15, 0x66, 0x4c, 0x4f, 0xb7, 0x1c, 0x6a, 0xde, 0x73, 0x78, 0xd0,
+ 0x85, 0x0c, 0x81, 0x0c, 0x0d, 0xf4, 0xab, 0x09, 0x2a, 0xa6, 0xb8, 0xb5, 0x7f, 0x55, 0x84, 0x8a,
+ 0xc8, 0x64, 0xba, 0x01, 0x97, 0x2c, 0xc7, 0x0a, 0x2c, 0xdd, 0x5e, 0xa5, 0xb6, 0x7e, 0x9c, 0x0c,
+ 0x3c, 0x79, 0x91, 0x6d, 0xb4, 0x37, 0x86, 0xc9, 0x98, 0x55, 0x87, 0x75, 0x4e, 0x20, 0x96, 0x6f,
+ 0x85, 0x22, 0xec, 0x68, 0x22, 0xfd, 0x75, 0x82, 0x82, 0x29, 0x4e, 0xa6, 0x0c, 0xf5, 0x33, 0xa2,
+ 0x4a, 0xb8, 0x32, 0x94, 0x8c, 0x25, 0x49, 0xf2, 0x71, 0x25, 0x7d, 0xc0, 0x15, 0xe2, 0xf0, 0xa0,
+ 0x91, 0x0c, 0x1c, 0x13, 0x4a, 0x7a, 0x8a, 0x86, 0x43, 0xdc, 0x0c, 0x61, 0x5f, 0xb7, 0xec, 0x81,
+ 0x47, 0x23, 0x84, 0x4a, 0x84, 0xb0, 0x96, 0xa2, 0xe1, 0x10, 0xb7, 0xf6, 0x3f, 0x0a, 0x40, 0x86,
+ 0x8f, 0x4e, 0x90, 0x03, 0xa8, 0x3a, 0xdc, 0x16, 0x99, 0x3b, 0xeb, 0x7e, 0xcc, 0xa4, 0x29, 0x16,
+ 0x09, 0x59, 0x20, 0xf1, 0x89, 0x03, 0x75, 0xfa, 0x30, 0xa0, 0x9e, 0x13, 0x1e, 0xa5, 0x9a, 0x4c,
+ 0x86, 0x7f, 0xb1, 0x37, 0x93, 0xc8, 0x18, 0xca, 0xd0, 0x7e, 0xbf, 0x08, 0xcd, 0x18, 0xdf, 0x93,
+ 0xb6, 0xf8, 0x3c, 0x9b, 0x83, 0x30, 0x01, 0xee, 0x7a, 0xb6, 0x9c, 0xef, 0x62, 0xd9, 0x1c, 0x24,
+ 0x09, 0x37, 0x31, 0xce, 0x47, 0x96, 0x00, 0x7a, 0xba, 0x1f, 0x50, 0x8f, 0xeb, 0x42, 0xa9, 0x1c,
+ 0x0a, 0x5b, 0x21, 0x05, 0x63, 0x5c, 0xe4, 0x86, 0xbc, 0xa3, 0xa1, 0x9c, 0xcc, 0x79, 0x39, 0xe2,
+ 0x02, 0x86, 0xca, 0x04, 0x2e, 0x60, 0x20, 0x5d, 0x98, 0x53, 0xad, 0x56, 0xd4, 0xf3, 0x65, 0x44,
+ 0x14, 0x03, 0x35, 0x05, 0x81, 0x43, 0xa0, 0xda, 0xf7, 0x0a, 0x30, 0x9d, 0x30, 0x40, 0x89, 0x6c,
+ 0x95, 0xea, 0xe0, 0x4f, 0x22, 0x5b, 0x65, 0xec, 0xbc, 0xce, 0xe7, 0xa1, 0x2a, 0x3a, 0x28, 0x1d,
+ 0xcf, 0x2b, 0xba, 0x10, 0x25, 0x95, 0xad, 0x2c, 0xd2, 0xc4, 0x9d, 0x5e, 0x59, 0xa4, 0x0d, 0x1c,
+ 0x15, 0x5d, 0x78, 0x8e, 0x44, 0xeb, 0x64, 0x4f, 0xc7, 0x3c, 0x47, 0xa2, 0x1c, 0x43, 0x0e, 0xed,
+ 0x1f, 0xf1, 0x76, 0x07, 0xde, 0x71, 0xb8, 0xb3, 0xee, 0x42, 0x4d, 0xc6, 0x70, 0xca, 0x4f, 0xe3,
+ 0xcd, 0x1c, 0x56, 0x31, 0x8e, 0x23, 0xa3, 0x15, 0x75, 0xe3, 0xf0, 0xde, 0xfe, 0x3e, 0x2a, 0x74,
+ 0x72, 0x0b, 0x1a, 0xae, 0x23, 0xbf, 0x60, 0xf9, 0xf8, 0x5f, 0x60, 0x2b, 0xc7, 0x3d, 0x55, 0xf8,
+ 0xe8, 0xa4, 0x75, 0x25, 0xfc, 0x93, 0x68, 0x24, 0x46, 0x35, 0xb5, 0x3f, 0x57, 0x80, 0x17, 0xd0,
+ 0xb5, 0x6d, 0xcb, 0xe9, 0x26, 0x3d, 0x9f, 0xc4, 0x86, 0x99, 0x9e, 0xfe, 0x70, 0xd7, 0xd1, 0x8f,
+ 0x74, 0xcb, 0xd6, 0xf7, 0x6c, 0xfa, 0xc4, 0x9d, 0xf1, 0x20, 0xb0, 0xec, 0x05, 0x71, 0x67, 0xe5,
+ 0xc2, 0x86, 0x13, 0xdc, 0xf3, 0x3a, 0x81, 0x67, 0x39, 0x5d, 0x31, 0x4b, 0x6e, 0x25, 0xb0, 0x30,
+ 0x85, 0xad, 0xfd, 0x41, 0x09, 0x78, 0x1c, 0x21, 0xf9, 0x12, 0x34, 0x7a, 0xd4, 0x38, 0xd0, 0x1d,
+ 0xcb, 0x57, 0x79, 0x7f, 0xaf, 0xb2, 0xe7, 0xda, 0x52, 0x85, 0x8f, 0xd8, 0xab, 0x58, 0xee, 0x6c,
+ 0xf2, 0xa3, 0x3a, 0x11, 0x2f, 0x31, 0xa0, 0xda, 0xf5, 0x7d, 0xbd, 0x6f, 0xe5, 0x0e, 0x31, 0x11,
+ 0x79, 0x56, 0xc5, 0x74, 0x24, 0x7e, 0xa3, 0x84, 0x26, 0x06, 0x54, 0xfa, 0xb6, 0x6e, 0x39, 0xb9,
+ 0xef, 0x58, 0x63, 0x4f, 0xb0, 0xcd, 0x90, 0x84, 0xa9, 0x92, 0xff, 0x44, 0x81, 0x4d, 0x06, 0xd0,
+ 0xf4, 0x0d, 0x4f, 0xef, 0xf9, 0x07, 0xfa, 0xd2, 0xab, 0xaf, 0xe5, 0x56, 0xfe, 0x23, 0x51, 0x42,
+ 0x17, 0x59, 0xc1, 0xe5, 0xad, 0xce, 0xed, 0xe5, 0xa5, 0x57, 0x5f, 0xc3, 0xb8, 0x9c, 0xb8, 0xd8,
+ 0x57, 0x5f, 0x59, 0x92, 0x33, 0xc8, 0xc4, 0xc5, 0xbe, 0xfa, 0xca, 0x12, 0xc6, 0xe5, 0x68, 0xff,
+ 0xbb, 0x00, 0x8d, 0x90, 0x97, 0xec, 0x02, 0xb0, 0xb9, 0x4c, 0x66, 0x46, 0x3d, 0xd7, 0x7d, 0x34,
+ 0xdc, 0xda, 0xb3, 0x1b, 0x56, 0xc6, 0x18, 0x50, 0x46, 0xea, 0xd8, 0xe2, 0xa4, 0x53, 0xc7, 0x2e,
+ 0x42, 0xe3, 0x40, 0x77, 0x4c, 0xff, 0x40, 0x3f, 0x14, 0x53, 0x7a, 0x2c, 0x99, 0xf2, 0x6d, 0x45,
+ 0xc0, 0x88, 0x47, 0xfb, 0x27, 0x55, 0x10, 0x71, 0x21, 0x6c, 0xd2, 0x31, 0x2d, 0x5f, 0x1c, 0x7e,
+ 0x28, 0xf0, 0x9a, 0xe1, 0xa4, 0xb3, 0x2a, 0xcb, 0x31, 0xe4, 0x20, 0x57, 0xa1, 0xd4, 0xb3, 0x1c,
+ 0xa9, 0x81, 0x70, 0x43, 0xee, 0x96, 0xe5, 0x20, 0x2b, 0xe3, 0x24, 0xfd, 0xa1, 0xd4, 0x30, 0x04,
+ 0x49, 0x7f, 0x88, 0xac, 0x8c, 0x7c, 0x05, 0x66, 0x6d, 0xd7, 0x3d, 0x64, 0xd3, 0x87, 0x52, 0x44,
+ 0x84, 0x57, 0x9d, 0x9b, 0x56, 0x36, 0x93, 0x24, 0x4c, 0xf3, 0x92, 0x5d, 0x78, 0xf1, 0x03, 0xea,
+ 0xb9, 0x72, 0xbe, 0xec, 0xd8, 0x94, 0xf6, 0x15, 0x8c, 0x50, 0x8d, 0x79, 0x94, 0xec, 0x2f, 0x64,
+ 0xb3, 0xe0, 0xa8, 0xba, 0x3c, 0xde, 0x5e, 0xf7, 0xba, 0x34, 0xd8, 0xf6, 0x5c, 0xa6, 0xbb, 0x58,
+ 0x4e, 0x57, 0xc1, 0x56, 0x23, 0xd8, 0x9d, 0x6c, 0x16, 0x1c, 0x55, 0x97, 0xbc, 0x03, 0xf3, 0x82,
+ 0x24, 0xd4, 0x96, 0x65, 0x31, 0xcd, 0x58, 0xb6, 0xba, 0x9a, 0x74, 0x5a, 0xf8, 0xcb, 0x76, 0x46,
+ 0xf0, 0xe0, 0xc8, 0xda, 0xe4, 0x2d, 0x98, 0x53, 0xde, 0xd2, 0x6d, 0xea, 0x75, 0xc2, 0x58, 0xa1,
+ 0xe9, 0xf6, 0xf5, 0xd3, 0x93, 0xd6, 0xb5, 0x55, 0xda, 0xf7, 0xa8, 0x11, 0xf7, 0x3a, 0x2b, 0x2e,
+ 0x1c, 0xaa, 0x47, 0x10, 0xae, 0xf0, 0x80, 0xa0, 0xdd, 0xfe, 0x8a, 0xeb, 0xda, 0xa6, 0xfb, 0xc0,
+ 0x51, 0xcf, 0x2e, 0x14, 0x76, 0xee, 0x20, 0xed, 0x64, 0x72, 0xe0, 0x88, 0x9a, 0xec, 0xc9, 0x39,
+ 0x65, 0xd5, 0x7d, 0xe0, 0xa4, 0x51, 0x21, 0x7a, 0xf2, 0xce, 0x08, 0x1e, 0x1c, 0x59, 0x9b, 0xac,
+ 0x01, 0x49, 0x3f, 0xc1, 0x6e, 0x5f, 0xba, 0xf0, 0xaf, 0x88, 0x24, 0x47, 0x69, 0x2a, 0x66, 0xd4,
+ 0x20, 0x9b, 0x70, 0x39, 0x5d, 0xca, 0xc4, 0x49, 0x6f, 0x3e, 0x4f, 0x6f, 0x8c, 0x19, 0x74, 0xcc,
+ 0xac, 0xa5, 0xfd, 0xd3, 0x22, 0x4c, 0x27, 0xb2, 0x62, 0x3c, 0x73, 0xd9, 0x07, 0xd8, 0xe6, 0xa1,
+ 0xe7, 0x77, 0x37, 0x56, 0x6f, 0x53, 0xdd, 0xa4, 0xde, 0x1d, 0xaa, 0x32, 0x98, 0x88, 0x65, 0x31,
+ 0x41, 0xc1, 0x14, 0x27, 0xd9, 0x87, 0x8a, 0xf0, 0x13, 0xe4, 0xbd, 0xd9, 0x48, 0xf5, 0x11, 0x77,
+ 0x16, 0xc8, 0xeb, 0xc0, 0x5c, 0x8f, 0xa2, 0x80, 0xd7, 0x02, 0x98, 0x8a, 0x73, 0xb0, 0x89, 0x24,
+ 0x52, 0x7b, 0x6b, 0x09, 0x95, 0x77, 0x03, 0x4a, 0x41, 0x30, 0x6e, 0x5e, 0x03, 0xe1, 0x77, 0xda,
+ 0xd9, 0x44, 0x86, 0xa1, 0xed, 0xb3, 0x77, 0xe7, 0xfb, 0x96, 0xeb, 0xc8, 0x24, 0xf7, 0xbb, 0x50,
+ 0x93, 0xbb, 0xa7, 0x31, 0xf3, 0x32, 0x70, 0x5d, 0x49, 0x99, 0x5d, 0x15, 0x96, 0xf6, 0x1f, 0x8b,
+ 0xd0, 0x08, 0xcd, 0x24, 0x67, 0x48, 0x1e, 0xef, 0x42, 0x23, 0x0c, 0x68, 0xcc, 0x7d, 0x6d, 0x6b,
+ 0x14, 0x67, 0xc7, 0x77, 0xf6, 0xe1, 0x5f, 0x8c, 0x64, 0xc4, 0x83, 0x25, 0x4b, 0x39, 0x82, 0x25,
+ 0xfb, 0x50, 0x0b, 0x3c, 0xab, 0xdb, 0x95, 0xbb, 0x84, 0x3c, 0xd1, 0x92, 0x61, 0x77, 0xed, 0x08,
+ 0x40, 0xd9, 0xb3, 0xe2, 0x0f, 0x2a, 0x31, 0xda, 0xfb, 0x30, 0x97, 0xe6, 0xe4, 0x2a, 0xb4, 0x71,
+ 0x40, 0xcd, 0x81, 0xad, 0xfa, 0x38, 0x52, 0xa1, 0x65, 0x39, 0x86, 0x1c, 0xe4, 0x26, 0xd4, 0xd9,
+ 0x6b, 0xfa, 0xc0, 0x75, 0x94, 0x1a, 0xcb, 0x77, 0x23, 0x3b, 0xb2, 0x0c, 0x43, 0xaa, 0xf6, 0xdf,
+ 0x4a, 0x70, 0x35, 0x32, 0x76, 0x6d, 0xe9, 0x8e, 0xde, 0x3d, 0xc3, 0x5d, 0x9d, 0x9f, 0x9e, 0x74,
+ 0x3b, 0xef, 0x0d, 0x20, 0xa5, 0x67, 0xe0, 0x06, 0x90, 0xff, 0x5b, 0x04, 0x1e, 0x7c, 0x4d, 0xbe,
+ 0x05, 0x53, 0x7a, 0xec, 0x9a, 0x66, 0xf9, 0x3a, 0x6f, 0xe5, 0x7e, 0x9d, 0x3c, 0xc6, 0x3b, 0x0c,
+ 0x80, 0x8b, 0x97, 0x62, 0x42, 0x20, 0x71, 0xa1, 0xbe, 0xaf, 0xdb, 0x36, 0xd3, 0x85, 0x72, 0x3b,
+ 0xef, 0x12, 0xc2, 0xf9, 0x30, 0x5f, 0x93, 0xd0, 0x18, 0x0a, 0x21, 0xdf, 0x29, 0xc0, 0xb4, 0x17,
+ 0xdf, 0xae, 0xc9, 0x17, 0x92, 0x27, 0xb4, 0x23, 0x86, 0x16, 0x0f, 0xb7, 0x8b, 0xef, 0x09, 0x93,
+ 0x32, 0xb5, 0xff, 0x5a, 0x80, 0xe9, 0x8e, 0x6d, 0x99, 0x96, 0xd3, 0xbd, 0xc0, 0x0b, 0x48, 0xee,
+ 0x41, 0xc5, 0xb7, 0x2d, 0x93, 0x8e, 0xb9, 0x9a, 0x88, 0x75, 0x8c, 0x01, 0xa0, 0xc0, 0x49, 0xde,
+ 0x68, 0x52, 0x3a, 0xc3, 0x8d, 0x26, 0x7f, 0x54, 0x05, 0x79, 0x8c, 0x80, 0x0c, 0xa0, 0xd1, 0x55,
+ 0x17, 0x25, 0xc8, 0x67, 0xbc, 0x9d, 0x23, 0xc9, 0x66, 0xe2, 0xca, 0x05, 0x31, 0xf7, 0x87, 0x85,
+ 0x18, 0x49, 0x22, 0x34, 0x79, 0x3f, 0xf8, 0x6a, 0xce, 0xfb, 0xc1, 0x85, 0xb8, 0xe1, 0x1b, 0xc2,
+ 0x75, 0x28, 0x1f, 0x04, 0x41, 0x5f, 0x0e, 0xa6, 0xf1, 0xcf, 0x89, 0x44, 0x79, 0x9e, 0x84, 0x4e,
+ 0xc4, 0xfe, 0x23, 0x87, 0x66, 0x22, 0x1c, 0x3d, 0xbc, 0x85, 0x71, 0x25, 0x57, 0x18, 0x49, 0x5c,
+ 0x04, 0xfb, 0x8f, 0x1c, 0x9a, 0xfc, 0x12, 0x34, 0x03, 0x4f, 0x77, 0xfc, 0x7d, 0xd7, 0xeb, 0x51,
+ 0x4f, 0xee, 0x51, 0xd7, 0x72, 0x5c, 0x91, 0xbd, 0x13, 0xa1, 0x09, 0x93, 0x6c, 0xa2, 0x08, 0xe3,
+ 0xd2, 0xc8, 0x21, 0xd4, 0x07, 0xa6, 0x68, 0x98, 0x34, 0x83, 0x2d, 0xe7, 0xb9, 0xf5, 0x3c, 0x16,
+ 0x24, 0xa2, 0xfe, 0x61, 0x28, 0x20, 0x79, 0xe1, 0x68, 0x6d, 0x52, 0x17, 0x8e, 0xc6, 0x47, 0x63,
+ 0x56, 0x12, 0x1a, 0xd2, 0x93, 0x7a, 0xad, 0xd3, 0x95, 0x31, 0x6e, 0x6b, 0xb9, 0x55, 0x4e, 0x21,
+ 0xb2, 0x19, 0xea, 0xc6, 0x4e, 0x17, 0x95, 0x0c, 0xad, 0x07, 0xd2, 0x77, 0x44, 0x8c, 0xc4, 0x65,
+ 0x4d, 0xe2, 0x64, 0xe4, 0xe2, 0xd9, 0xe6, 0x83, 0xf0, 0xd6, 0xa0, 0x58, 0xb2, 0xf8, 0xcc, 0x5b,
+ 0x99, 0xb4, 0xff, 0x54, 0x84, 0xd2, 0xce, 0x66, 0x47, 0x24, 0x80, 0xe5, 0xd7, 0xbf, 0xd1, 0xce,
+ 0xa1, 0xd5, 0xbf, 0x4f, 0x3d, 0x6b, 0xff, 0x58, 0x6e, 0xbd, 0x63, 0x09, 0x60, 0xd3, 0x1c, 0x98,
+ 0x51, 0x8b, 0xbc, 0x0b, 0x53, 0x86, 0xbe, 0x42, 0xbd, 0x60, 0x1c, 0xc3, 0x02, 0x3f, 0x02, 0xbe,
+ 0xb2, 0x1c, 0x55, 0xc7, 0x04, 0x18, 0xd9, 0x05, 0x30, 0x22, 0xe8, 0xd2, 0xb9, 0xcd, 0x21, 0x31,
+ 0xe0, 0x18, 0x10, 0x41, 0x68, 0x1c, 0x32, 0x56, 0x8e, 0x5a, 0x3e, 0x0f, 0x2a, 0x1f, 0x39, 0x77,
+ 0x54, 0x5d, 0x8c, 0x60, 0x34, 0x07, 0xa6, 0x13, 0x37, 0x38, 0x91, 0x2f, 0x43, 0xdd, 0xed, 0xc7,
+ 0xa6, 0xd3, 0x06, 0x8f, 0xa6, 0xad, 0xdf, 0x93, 0x65, 0x8f, 0x4e, 0x5a, 0xd3, 0x9b, 0x6e, 0xd7,
+ 0x32, 0x54, 0x01, 0x86, 0xec, 0x44, 0x83, 0x2a, 0x3f, 0xb7, 0xa9, 0xee, 0x6f, 0xe2, 0x6b, 0x07,
+ 0xbf, 0x62, 0xc5, 0x47, 0x49, 0xd1, 0x7e, 0xa5, 0x0c, 0x91, 0xc7, 0x95, 0xf8, 0x50, 0x15, 0x67,
+ 0x46, 0xe4, 0xcc, 0x7d, 0xa1, 0xc7, 0x53, 0xa4, 0x28, 0xd2, 0x85, 0xd2, 0xfb, 0xee, 0x5e, 0xee,
+ 0x89, 0x3b, 0x96, 0xb0, 0x41, 0xd8, 0xca, 0x62, 0x05, 0xc8, 0x24, 0x90, 0xbf, 0x5e, 0x80, 0xe7,
+ 0xfd, 0xb4, 0xea, 0x2b, 0x87, 0x03, 0xe6, 0xd7, 0xf1, 0xd3, 0xca, 0xb4, 0x0c, 0x7b, 0x1e, 0x45,
+ 0xc6, 0xe1, 0xb6, 0xb0, 0xfe, 0x17, 0xae, 0x50, 0x39, 0x9c, 0xd6, 0x73, 0xde, 0x2f, 0x9b, 0xec,
+ 0xff, 0x64, 0x19, 0x4a, 0x51, 0xda, 0xb7, 0x8b, 0xd0, 0x8c, 0xcd, 0xd6, 0xb9, 0xaf, 0x05, 0x7b,
+ 0x98, 0xba, 0x16, 0x6c, 0x7b, 0xfc, 0xc8, 0x80, 0xa8, 0x55, 0x17, 0x7d, 0x33, 0xd8, 0xbf, 0x28,
+ 0x42, 0x69, 0x77, 0x75, 0x2d, 0xb9, 0x69, 0x2d, 0x3c, 0x85, 0x4d, 0xeb, 0x01, 0xd4, 0xf6, 0x06,
+ 0x96, 0x1d, 0x58, 0x4e, 0xee, 0x94, 0x32, 0xea, 0x16, 0x35, 0xe9, 0xeb, 0x10, 0xa8, 0xa8, 0xe0,
+ 0x49, 0x17, 0x6a, 0x5d, 0x91, 0xd3, 0x33, 0x77, 0xbc, 0xa4, 0xcc, 0x0d, 0x2a, 0x04, 0xc9, 0x3f,
+ 0xa8, 0xd0, 0xb5, 0x63, 0xa8, 0xee, 0xae, 0x4a, 0xb5, 0xff, 0xe9, 0xf6, 0xa6, 0xf6, 0x4b, 0x10,
+ 0x6a, 0x01, 0x4f, 0x5f, 0xf8, 0xef, 0x16, 0x20, 0xa9, 0xf8, 0x3c, 0xfd, 0xd1, 0x74, 0x98, 0x1e,
+ 0x4d, 0xab, 0x93, 0xf8, 0xf8, 0xb2, 0x07, 0x94, 0xf6, 0xef, 0x0b, 0x90, 0x3a, 0xe8, 0x47, 0x5e,
+ 0x93, 0xe9, 0xe1, 0x92, 0x81, 0x69, 0x2a, 0x3d, 0x1c, 0x49, 0x72, 0xc7, 0xd2, 0xc4, 0x7d, 0xc8,
+ 0xb6, 0x6b, 0x71, 0x07, 0x9a, 0x6c, 0xfe, 0xdd, 0xf1, 0xb7, 0x6b, 0x59, 0xee, 0x38, 0x19, 0x3c,
+ 0x19, 0x27, 0x61, 0x52, 0xae, 0xf6, 0x8f, 0x8b, 0x50, 0x7d, 0x6a, 0xb9, 0x0d, 0x68, 0x22, 0x9e,
+ 0x75, 0x25, 0xe7, 0x6c, 0x3f, 0x32, 0x9a, 0xb5, 0x97, 0x8a, 0x66, 0xcd, 0x7b, 0x6d, 0xf9, 0x13,
+ 0x62, 0x59, 0xff, 0x6d, 0x01, 0xe4, 0x5a, 0xb3, 0xe1, 0xf8, 0x81, 0xee, 0x18, 0x94, 0x18, 0xe1,
+ 0xc2, 0x96, 0x37, 0x68, 0x4a, 0x06, 0x16, 0x0a, 0x5d, 0x86, 0xff, 0x56, 0x0b, 0x19, 0xf9, 0x29,
+ 0xa8, 0x1f, 0xb8, 0x7e, 0xc0, 0x17, 0xaf, 0x62, 0xd2, 0x64, 0x76, 0x5b, 0x96, 0x63, 0xc8, 0x91,
+ 0x76, 0x67, 0x57, 0x46, 0xbb, 0xb3, 0xb5, 0xdf, 0x2a, 0xc2, 0xd4, 0x27, 0x25, 0x79, 0x42, 0x56,
+ 0xf4, 0x6f, 0x29, 0x67, 0xf4, 0x6f, 0xf9, 0x3c, 0xd1, 0xbf, 0xda, 0x0f, 0x0a, 0x00, 0x4f, 0x2d,
+ 0x73, 0x83, 0x99, 0x0c, 0xcc, 0xcd, 0x3d, 0xae, 0xb2, 0xc3, 0x72, 0xff, 0x41, 0x45, 0x3d, 0x12,
+ 0x0f, 0xca, 0xfd, 0xb0, 0x00, 0x33, 0x7a, 0x22, 0xd0, 0x35, 0xb7, 0xbe, 0x9c, 0x8a, 0x9b, 0x0d,
+ 0xe3, 0xb4, 0x92, 0xe5, 0x98, 0x12, 0x4b, 0x5e, 0x8f, 0x32, 0x93, 0xdf, 0x8d, 0x86, 0xfd, 0x50,
+ 0x4a, 0x71, 0xae, 0xbb, 0x25, 0x38, 0x9f, 0x10, 0x58, 0x5c, 0x9a, 0x48, 0x60, 0x71, 0xfc, 0xc8,
+ 0x64, 0xf9, 0xb1, 0x47, 0x26, 0x8f, 0xa0, 0xb1, 0xef, 0xb9, 0x3d, 0x1e, 0xbb, 0x2b, 0xef, 0xfe,
+ 0xbe, 0x95, 0x63, 0xa1, 0xec, 0xed, 0x59, 0x0e, 0x35, 0x79, 0x5c, 0x70, 0x68, 0xb8, 0x5a, 0x53,
+ 0xf8, 0x18, 0x89, 0xe2, 0xb6, 0x7e, 0x57, 0x48, 0xad, 0x4e, 0x52, 0x6a, 0x38, 0x97, 0xec, 0x08,
+ 0x74, 0x54, 0x62, 0x92, 0xf1, 0xba, 0xb5, 0xa7, 0x13, 0xaf, 0xab, 0xfd, 0xc5, 0x9a, 0x9a, 0xc0,
+ 0x9e, 0xb9, 0x24, 0xb8, 0x9f, 0x1e, 0x74, 0xef, 0xd2, 0xa1, 0x53, 0xe8, 0xf5, 0xa7, 0x78, 0x0a,
+ 0xbd, 0x31, 0x99, 0x53, 0xe8, 0x90, 0xef, 0x14, 0x7a, 0x73, 0x42, 0xa7, 0xd0, 0xa7, 0x26, 0x75,
+ 0x0a, 0x7d, 0x7a, 0xac, 0x53, 0xe8, 0x33, 0x67, 0x3a, 0x85, 0x7e, 0x52, 0x82, 0xd4, 0x66, 0xfc,
+ 0x53, 0xc7, 0xdb, 0xff, 0x57, 0x8e, 0xb7, 0xef, 0x16, 0x21, 0x9a, 0x88, 0xcf, 0x19, 0x98, 0xf4,
+ 0x0e, 0xd4, 0x7b, 0xfa, 0x43, 0x1e, 0x38, 0x9d, 0xe7, 0xee, 0xe8, 0x2d, 0x89, 0x81, 0x21, 0x1a,
+ 0xf1, 0x01, 0xac, 0xf0, 0xfe, 0x86, 0xdc, 0x2e, 0x8c, 0xe8, 0x2a, 0x08, 0x61, 0x24, 0x8d, 0xfe,
+ 0x63, 0x4c, 0x8c, 0xf6, 0x6f, 0x8a, 0x20, 0x2f, 0xfa, 0x20, 0x14, 0x2a, 0xfb, 0xd6, 0x43, 0x6a,
+ 0xe6, 0x0e, 0x77, 0x8e, 0xdd, 0xe8, 0x2f, 0x7c, 0x34, 0xbc, 0x00, 0x05, 0x3a, 0x37, 0xbe, 0x0b,
+ 0x9f, 0x9b, 0xec, 0xbf, 0x1c, 0xc6, 0xf7, 0xb8, 0xef, 0x4e, 0x1a, 0xdf, 0x45, 0x11, 0x2a, 0x19,
+ 0xc2, 0xd6, 0xcf, 0xc3, 0x2f, 0x72, 0xbb, 0x18, 0x13, 0x61, 0x1c, 0xca, 0xd6, 0xef, 0x8b, 0x34,
+ 0x14, 0x52, 0x46, 0xfb, 0x17, 0xbf, 0xff, 0xc3, 0xeb, 0xcf, 0xfd, 0xe0, 0x87, 0xd7, 0x9f, 0xfb,
+ 0xe8, 0x87, 0xd7, 0x9f, 0xfb, 0x95, 0xd3, 0xeb, 0x85, 0xef, 0x9f, 0x5e, 0x2f, 0xfc, 0xe0, 0xf4,
+ 0x7a, 0xe1, 0xa3, 0xd3, 0xeb, 0x85, 0xff, 0x7c, 0x7a, 0xbd, 0xf0, 0x57, 0xfe, 0xcb, 0xf5, 0xe7,
+ 0x7e, 0xe1, 0x4b, 0x51, 0x13, 0x16, 0x55, 0x13, 0x16, 0x95, 0xc0, 0xc5, 0xfe, 0x61, 0x77, 0x91,
+ 0x35, 0x21, 0x2a, 0x51, 0x4d, 0xf8, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xad, 0x0f, 0x6c, 0xf9,
+ 0xf5, 0x9e, 0x00, 0x00,
}
func (m *AbstractPodTemplate) Marshal() (dAtA []byte, err error) {
@@ -6393,6 +6425,34 @@ func (m *MonoVertex) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *MonoVertexLifecycle) 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 *MonoVertexLifecycle) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MonoVertexLifecycle) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ i -= len(m.DesiredPhase)
+ copy(dAtA[i:], m.DesiredPhase)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.DesiredPhase)))
+ i--
+ dAtA[i] = 0xa
+ return len(dAtA) - i, nil
+}
+
func (m *MonoVertexLimits) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -6500,6 +6560,16 @@ func (m *MonoVertexSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ {
+ size, err := m.Lifecycle.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x6a
{
size, err := m.UpdateStrategy.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
@@ -10653,6 +10723,17 @@ func (m *MonoVertex) Size() (n int) {
return n
}
+func (m *MonoVertexLifecycle) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.DesiredPhase)
+ n += 1 + l + sovGenerated(uint64(l))
+ return n
+}
+
func (m *MonoVertexLimits) Size() (n int) {
if m == nil {
return 0
@@ -10739,6 +10820,8 @@ func (m *MonoVertexSpec) Size() (n int) {
}
l = m.UpdateStrategy.Size()
n += 1 + l + sovGenerated(uint64(l))
+ l = m.Lifecycle.Size()
+ n += 1 + l + sovGenerated(uint64(l))
return n
}
@@ -12599,6 +12682,16 @@ func (this *MonoVertex) String() string {
}, "")
return s
}
+func (this *MonoVertexLifecycle) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&MonoVertexLifecycle{`,
+ `DesiredPhase:` + fmt.Sprintf("%v", this.DesiredPhase) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func (this *MonoVertexLimits) String() string {
if this == nil {
return "nil"
@@ -12658,6 +12751,7 @@ func (this *MonoVertexSpec) String() string {
`Sidecars:` + repeatedStringForSidecars + `,`,
`DaemonTemplate:` + strings.Replace(this.DaemonTemplate.String(), "DaemonTemplate", "DaemonTemplate", 1) + `,`,
`UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "UpdateStrategy", "UpdateStrategy", 1), `&`, ``, 1) + `,`,
+ `Lifecycle:` + strings.Replace(strings.Replace(this.Lifecycle.String(), "MonoVertexLifecycle", "MonoVertexLifecycle", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
@@ -23347,6 +23441,88 @@ func (m *MonoVertex) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *MonoVertexLifecycle) 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 ErrIntOverflowGenerated
+ }
+ 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: MonoVertexLifecycle: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MonoVertexLifecycle: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field DesiredPhase", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ 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 ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.DesiredPhase = MonoVertexPhase(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *MonoVertexLimits) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -24000,6 +24176,39 @@ func (m *MonoVertexSpec) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 13:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Lifecycle", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Lifecycle.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
diff --git a/pkg/apis/numaflow/v1alpha1/generated.proto b/pkg/apis/numaflow/v1alpha1/generated.proto
index 035f4cf46b..f96a526599 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -899,6 +899,13 @@ message MonoVertex {
optional MonoVertexStatus status = 3;
}
+message MonoVertexLifecycle {
+ // DesiredPhase used to bring the pipeline from current phase to desired phase
+ // +kubebuilder:default=Running
+ // +optional
+ optional string desiredPhase = 1;
+}
+
message MonoVertexLimits {
// Read batch size from the source.
// +kubebuilder:default=500
@@ -965,6 +972,11 @@ message MonoVertexSpec {
// +kubebuilder:default={"type": "RollingUpdate", "rollingUpdate": {"maxUnavailable": "25%"}}
// +optional
optional UpdateStrategy updateStrategy = 12;
+
+ // Lifecycle defines the Lifecycle properties of a MonoVertex
+ // +kubebuilder:default={"desiredPhase": Running}
+ // +optional
+ optional MonoVertexLifecycle lifecycle = 13;
}
message MonoVertexStatus {
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
index e3b8e28f64..d271c34144 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types.go
@@ -38,6 +38,7 @@ const (
MonoVertexPhaseUnknown MonoVertexPhase = ""
MonoVertexPhaseRunning MonoVertexPhase = "Running"
MonoVertexPhaseFailed MonoVertexPhase = "Failed"
+ MonoVertexPhasePaused MonoVertexPhase = "Paused"
// MonoVertexConditionDeployed has the status True when the MonoVertex
// has its sub resources created and deployed.
@@ -79,6 +80,10 @@ func (mv MonoVertex) getReplicas() int {
}
func (mv MonoVertex) CalculateReplicas() int {
+ // If we are pausing the MonoVertex then we should have the desired replicas as 0
+ if mv.Spec.Lifecycle.GetDesiredPhase() == MonoVertexPhasePaused {
+ return 0
+ }
desiredReplicas := mv.getReplicas()
// Don't allow replicas to be out of the range of min and max when auto scaling is enabled
if s := mv.Spec.Scale; !s.Disabled {
@@ -307,8 +312,7 @@ func (mv MonoVertex) simpleCopy() MonoVertex {
m.Spec.Limits.ReadTimeout = &metav1.Duration{Duration: DefaultReadTimeout}
}
m.Spec.UpdateStrategy = UpdateStrategy{}
- // TODO: lifecycle
- // mvVtxCopy.Spec.Lifecycle = Lifecycle{}
+ m.Spec.Lifecycle = MonoVertexLifecycle{}
return m
}
@@ -442,6 +446,10 @@ type MonoVertexSpec struct {
// +kubebuilder:default={"type": "RollingUpdate", "rollingUpdate": {"maxUnavailable": "25%"}}
// +optional
UpdateStrategy UpdateStrategy `json:"updateStrategy,omitempty" protobuf:"bytes,12,opt,name=updateStrategy"`
+ // Lifecycle defines the Lifecycle properties of a MonoVertex
+ // +kubebuilder:default={"desiredPhase": Running}
+ // +optional
+ Lifecycle MonoVertexLifecycle `json:"lifecycle,omitempty" protobuf:"bytes,13,opt,name=lifecycle"`
}
func (mvspec MonoVertexSpec) DeepCopyWithoutReplicas() MonoVertexSpec {
@@ -595,19 +603,23 @@ func (mvs *MonoVertexStatus) MarkPhaseRunning() {
mvs.MarkPhase(MonoVertexPhaseRunning, "", "")
}
+// MarkPhasePaused set the Pipeline has been paused.
+func (mvs *MonoVertexStatus) MarkPhasePaused() {
+ mvs.MarkPhase(MonoVertexPhasePaused, "", "MonoVertex paused")
+}
+
// IsHealthy indicates whether the MonoVertex is in healthy status
// It returns false if any issues exists
// True indicates that the MonoVertex is healthy
-// TODO: Add support for paused whenever added in MonoVtx?
func (mvs *MonoVertexStatus) IsHealthy() bool {
// check for the phase field first
switch mvs.Phase {
// Directly return an error if the phase is failed
case MonoVertexPhaseFailed:
return false
- // Check if the MonoVertex is ready if the phase is running,
+ // Check if the MonoVertex is ready if the phase is running or Paused,
// We check if all the required conditions are true for it to be healthy
- case MonoVertexPhaseRunning:
+ case MonoVertexPhaseRunning, MonoVertexPhasePaused:
return mvs.IsReady()
default:
return false
@@ -621,3 +633,20 @@ type MonoVertexList struct {
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Items []MonoVertex `json:"items" protobuf:"bytes,2,rep,name=items"`
}
+
+type MonoVertexLifecycle struct {
+ // DesiredPhase used to bring the pipeline from current phase to desired phase
+ // +kubebuilder:default=Running
+ // +optional
+ DesiredPhase MonoVertexPhase `json:"desiredPhase,omitempty" protobuf:"bytes,1,opt,name=desiredPhase"`
+}
+
+// GetDesiredPhase is used to fetch the desired lifecycle phase for a MonoVertex
+func (lc MonoVertexLifecycle) GetDesiredPhase() MonoVertexPhase {
+ switch lc.DesiredPhase {
+ case MonoVertexPhasePaused:
+ return MonoVertexPhasePaused
+ default:
+ return MonoVertexPhaseRunning
+ }
+}
diff --git a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
index ae6a62c09d..98bb801003 100644
--- a/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/mono_vertex_types_test.go
@@ -103,6 +103,22 @@ func TestMonoVertex_MarkPhaseRunning(t *testing.T) {
}
}
+func TestMonoVertex_MarkPhasePaused(t *testing.T) {
+ mvs := MonoVertexStatus{}
+ mvs.MarkPhasePaused()
+
+ if mvs.Phase != MonoVertexPhasePaused {
+ t.Errorf("MarkPhaseRunning did not set the Phase to Paused, got %v", mvs.Phase)
+ }
+}
+
+func TestMonoVertex_GetDesiredPhase(t *testing.T) {
+ lc := MonoVertexLifecycle{}
+ assert.Equal(t, MonoVertexPhaseRunning, lc.GetDesiredPhase())
+ lc.DesiredPhase = MonoVertexPhasePaused
+ assert.Equal(t, MonoVertexPhasePaused, lc.GetDesiredPhase())
+}
+
func TestMonoVertex_MarkDaemonUnHealthy(t *testing.T) {
mvs := MonoVertexStatus{}
mvs.MarkDaemonUnHealthy("reason", "message")
@@ -369,6 +385,22 @@ func TestMonoVertex_CalculateReplicas(t *testing.T) {
}
assert.Equal(t, 5, mv.CalculateReplicas())
})
+
+ t.Run("phase paused", func(t *testing.T) {
+ replicas := int32(10)
+ mv := MonoVertex{
+ Spec: MonoVertexSpec{
+ Lifecycle: MonoVertexLifecycle{DesiredPhase: MonoVertexPhasePaused},
+ Replicas: &replicas,
+ Scale: Scale{
+ Disabled: false,
+ Min: ptr.To[int32](2),
+ Max: ptr.To[int32](5),
+ },
+ },
+ }
+ assert.Equal(t, 0, mv.CalculateReplicas())
+ })
}
func TestMonoVertex_GetServiceObj(t *testing.T) {
diff --git a/pkg/apis/numaflow/v1alpha1/openapi_generated.go b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
index bd59769897..83186d25b7 100644
--- a/pkg/apis/numaflow/v1alpha1/openapi_generated.go
+++ b/pkg/apis/numaflow/v1alpha1/openapi_generated.go
@@ -74,6 +74,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Log": schema_pkg_apis_numaflow_v1alpha1_Log(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata": schema_pkg_apis_numaflow_v1alpha1_Metadata(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertex": schema_pkg_apis_numaflow_v1alpha1_MonoVertex(ref),
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLifecycle": schema_pkg_apis_numaflow_v1alpha1_MonoVertexLifecycle(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits": schema_pkg_apis_numaflow_v1alpha1_MonoVertexLimits(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexList": schema_pkg_apis_numaflow_v1alpha1_MonoVertexList(ref),
"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexSpec": schema_pkg_apis_numaflow_v1alpha1_MonoVertexSpec(ref),
@@ -3057,6 +3058,25 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertex(ref common.ReferenceCallback)
}
}
+func schema_pkg_apis_numaflow_v1alpha1_MonoVertexLifecycle(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "desiredPhase": {
+ SchemaProps: spec.SchemaProps{
+ Description: "DesiredPhase used to bring the pipeline from current phase to desired phase",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
func schema_pkg_apis_numaflow_v1alpha1_MonoVertexLimits(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -3369,11 +3389,18 @@ func schema_pkg_apis_numaflow_v1alpha1_MonoVertexSpec(ref common.ReferenceCallba
Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy"),
},
},
+ "lifecycle": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Lifecycle defines the Lifecycle properties of a MonoVertex",
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLifecycle"),
+ },
+ },
},
},
},
Dependencies: []string{
- "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.DaemonTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
+ "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.ContainerTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.DaemonTemplate", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Metadata", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLifecycle", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.MonoVertexLimits", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Scale", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Sink", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.Source", "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1.UpdateStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"},
}
}
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types.go b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
index ff8dfaf5e5..3307adf743 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types.go
@@ -783,13 +783,11 @@ func (pls *PipelineStatus) IsHealthy() bool {
switch pls.Phase {
case PipelinePhaseFailed:
return false
- case PipelinePhaseRunning:
+ case PipelinePhaseRunning, PipelinePhasePaused:
return pls.IsReady()
case PipelinePhaseDeleting, PipelinePhasePausing:
// Transient phases, return true
return true
- case PipelinePhasePaused:
- return true
default:
return false
}
diff --git a/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go b/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go
index d7e5c334b9..34835e7b07 100644
--- a/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/pipeline_types_test.go
@@ -572,7 +572,7 @@ func TestPipelineStatus_IsHealthy(t *testing.T) {
name: "Paused phase",
phase: PipelinePhasePaused,
ready: false,
- want: true,
+ want: false,
},
{
name: "Unknown phase",
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index 49b93292ff..c131980d96 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -1409,6 +1409,22 @@ func (in *MonoVertex) DeepCopyObject() runtime.Object {
return nil
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MonoVertexLifecycle) DeepCopyInto(out *MonoVertexLifecycle) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonoVertexLifecycle.
+func (in *MonoVertexLifecycle) DeepCopy() *MonoVertexLifecycle {
+ if in == nil {
+ return nil
+ }
+ out := new(MonoVertexLifecycle)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MonoVertexLimits) DeepCopyInto(out *MonoVertexLimits) {
*out = *in
@@ -1525,6 +1541,7 @@ func (in *MonoVertexSpec) DeepCopyInto(out *MonoVertexSpec) {
(*in).DeepCopyInto(*out)
}
in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy)
+ out.Lifecycle = in.Lifecycle
return
}
diff --git a/pkg/reconciler/monovertex/controller.go b/pkg/reconciler/monovertex/controller.go
index 3fbfb3c1ab..5e31ed6f28 100644
--- a/pkg/reconciler/monovertex/controller.go
+++ b/pkg/reconciler/monovertex/controller.go
@@ -122,8 +122,6 @@ func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.Mon
return ctrl.Result{}, err
}
- // TODO: handle lifecycle changes
-
if err := mr.orchestratePods(ctx, monoVtx); err != nil {
monoVtx.Status.MarkDeployFailed("OrchestratePodsFailed", err.Error())
mr.recorder.Eventf(monoVtx, corev1.EventTypeWarning, "OrchestratePodsFailed", "OrchestratePodsFailed: %s", err.Error())
@@ -132,13 +130,21 @@ func (mr *monoVertexReconciler) reconcile(ctx context.Context, monoVtx *dfv1.Mon
monoVtx.Status.MarkDeployed()
- // Mark it running before checking the status of the pods
- monoVtx.Status.MarkPhaseRunning()
+ // Update the phase based on the DesiredPhase from the lifecycle, this should encompass
+ // the Paused and running states.
+ originalPhase := monoVtx.Status.Phase
+ monoVtx.Status.MarkPhase(monoVtx.Spec.Lifecycle.GetDesiredPhase(), "", "")
+ // If the phase has changed, log the event
+ if monoVtx.Status.Phase != originalPhase {
+ log.Infow("Updated MonoVertex phase", zap.String("originalPhase", string(originalPhase)), zap.String("originalPhase", string(monoVtx.Status.Phase)))
+ mr.recorder.Eventf(monoVtx, corev1.EventTypeNormal, "UpdateMonoVertexPhase", "Updated MonoVertex phase from %s to %s", string(originalPhase), string(monoVtx.Status.Phase))
+ }
// Check children resource status
if err := mr.checkChildrenResourceStatus(ctx, monoVtx); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to check mono vertex children resource status, %w", err)
}
+
return ctrl.Result{}, nil
}
diff --git a/pkg/reconciler/monovertex/controller_test.go b/pkg/reconciler/monovertex/controller_test.go
index 8e1f179db4..c9c5c003c2 100644
--- a/pkg/reconciler/monovertex/controller_test.go
+++ b/pkg/reconciler/monovertex/controller_test.go
@@ -35,11 +35,12 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
+ "github.com/stretchr/testify/assert"
+
dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
"github.com/numaproj/numaflow/pkg/reconciler"
"github.com/numaproj/numaflow/pkg/reconciler/monovertex/scaling"
sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
- "github.com/stretchr/testify/assert"
)
const (
diff --git a/pkg/reconciler/monovertex/scaling/scaling.go b/pkg/reconciler/monovertex/scaling/scaling.go
index 0b35265190..7f9ee9ea84 100644
--- a/pkg/reconciler/monovertex/scaling/scaling.go
+++ b/pkg/reconciler/monovertex/scaling/scaling.go
@@ -171,11 +171,11 @@ func (s *Scaler) scaleOneMonoVertex(ctx context.Context, key string, worker int)
log.Infof("MonoVertex not in Running phase, skip scaling.")
return nil
}
- // TODO: lifecycle
- // if monoVtx.Spec.Lifecycle.GetDesiredPhase() != dfv1.MonoVertexPhaseRunning {
- // log.Info("MonoVertex is pausing, skip scaling.")
- // return nil
- // }
+
+ if monoVtx.Spec.Lifecycle.GetDesiredPhase() != dfv1.MonoVertexPhaseRunning {
+ log.Info("MonoVertex desiredPhase is not running, skip scaling.")
+ return nil
+ }
if int(monoVtx.Status.Replicas) != monoVtx.CalculateReplicas() {
log.Infof("MonoVertex %s might be under processing, replicas mismatch, skip scaling.", monoVtx.Name)
return nil
diff --git a/rust/numaflow-models/src/models/mod.rs b/rust/numaflow-models/src/models/mod.rs
index 423e21fd9c..bfbcd121d0 100644
--- a/rust/numaflow-models/src/models/mod.rs
+++ b/rust/numaflow-models/src/models/mod.rs
@@ -90,6 +90,8 @@ pub mod metadata;
pub use self::metadata::Metadata;
pub mod mono_vertex;
pub use self::mono_vertex::MonoVertex;
+pub mod mono_vertex_lifecycle;
+pub use self::mono_vertex_lifecycle::MonoVertexLifecycle;
pub mod mono_vertex_limits;
pub use self::mono_vertex_limits::MonoVertexLimits;
pub mod mono_vertex_list;
diff --git a/rust/numaflow-models/src/models/mono_vertex_lifecycle.rs b/rust/numaflow-models/src/models/mono_vertex_lifecycle.rs
new file mode 100644
index 0000000000..90d5b97422
--- /dev/null
+++ b/rust/numaflow-models/src/models/mono_vertex_lifecycle.rs
@@ -0,0 +1,32 @@
+/*
+Copyright 2022 The Numaproj Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by Openapi Generator. DO NOT EDIT.
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MonoVertexLifecycle {
+ /// DesiredPhase used to bring the pipeline from current phase to desired phase
+ #[serde(rename = "desiredPhase", skip_serializing_if = "Option::is_none")]
+ pub desired_phase: Option,
+}
+
+impl MonoVertexLifecycle {
+ pub fn new() -> MonoVertexLifecycle {
+ MonoVertexLifecycle {
+ desired_phase: None,
+ }
+ }
+}
diff --git a/rust/numaflow-models/src/models/mono_vertex_spec.rs b/rust/numaflow-models/src/models/mono_vertex_spec.rs
index 6d4068bee7..7cad8cc898 100644
--- a/rust/numaflow-models/src/models/mono_vertex_spec.rs
+++ b/rust/numaflow-models/src/models/mono_vertex_spec.rs
@@ -41,6 +41,8 @@ pub struct MonoVertexSpec {
/// List of customized init containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
#[serde(rename = "initContainers", skip_serializing_if = "Option::is_none")]
pub init_containers: Option>,
+ #[serde(rename = "lifecycle", skip_serializing_if = "Option::is_none")]
+ pub lifecycle: Option>,
#[serde(rename = "limits", skip_serializing_if = "Option::is_none")]
pub limits: Option>,
#[serde(rename = "metadata", skip_serializing_if = "Option::is_none")]
@@ -96,6 +98,7 @@ impl MonoVertexSpec {
dns_policy: None,
image_pull_secrets: None,
init_containers: None,
+ lifecycle: None,
limits: None,
metadata: None,
node_selector: None,
From 895a7780410b7bb5a43a4ab6f4dd55c1c145561f Mon Sep 17 00:00:00 2001
From: Keran Yang
Date: Wed, 25 Sep 2024 17:47:12 -0400
Subject: [PATCH 070/188] feat: container-type level version compatibility
check (#2087)
Signed-off-by: Keran Yang
---
pkg/sdkclient/const.go | 2 -
pkg/sdkclient/serverinfo/serverinfo.go | 26 +++++++-
pkg/sdkclient/serverinfo/serverinfo_test.go | 38 ++++++++---
pkg/sdkclient/serverinfo/types.go | 74 ++++++++++++++++++---
4 files changed, 115 insertions(+), 25 deletions(-)
diff --git a/pkg/sdkclient/const.go b/pkg/sdkclient/const.go
index b7ace077cf..54f88b66cb 100644
--- a/pkg/sdkclient/const.go
+++ b/pkg/sdkclient/const.go
@@ -38,7 +38,6 @@ const (
// Server information file configs
MapServerInfoFile = "/var/run/numaflow/mapper-server-info"
- MapStreamServerInfoFile = "/var/run/numaflow/mapstreamer-server-info"
ReduceServerInfoFile = "/var/run/numaflow/reducer-server-info"
ReduceStreamServerInfoFile = "/var/run/numaflow/reducestreamer-server-info"
SessionReduceServerInfoFile = "/var/run/numaflow/sessionreducer-server-info"
@@ -47,5 +46,4 @@ const (
FbSinkServerInfoFile = "/var/run/numaflow/fb-sinker-server-info"
SourceServerInfoFile = "/var/run/numaflow/sourcer-server-info"
SourceTransformerServerInfoFile = "/var/run/numaflow/sourcetransformer-server-info"
- BatchMapServerInfoFile = "/var/run/numaflow/batchmapper-server-info"
)
diff --git a/pkg/sdkclient/serverinfo/serverinfo.go b/pkg/sdkclient/serverinfo/serverinfo.go
index d01acd1cda..f94d83e072 100644
--- a/pkg/sdkclient/serverinfo/serverinfo.go
+++ b/pkg/sdkclient/serverinfo/serverinfo.go
@@ -67,6 +67,10 @@ func waitForServerInfo(timeout time.Duration, filePath string) (*ServerInfo, err
minNumaflowVersion := serverInfo.MinimumNumaflowVersion
sdkLanguage := serverInfo.Language
numaflowVersion := numaflow.GetVersion().Version
+ containerType, err := getContainerType(filePath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get container type: %w", err)
+ }
// If MinimumNumaflowVersion is empty, skip the numaflow compatibility check as there was an
// error writing server info file on the SDK side
@@ -87,7 +91,7 @@ func waitForServerInfo(timeout time.Duration, filePath string) (*ServerInfo, err
if sdkVersion == "" || sdkLanguage == "" {
log.Printf("warning: failed to get the SDK version/language, skipping SDK version compatibility check")
} else {
- if err := checkSDKCompatibility(sdkVersion, sdkLanguage, minimumSupportedSDKVersions); err != nil {
+ if err := checkSDKCompatibility(sdkVersion, sdkLanguage, containerType, minimumSupportedSDKVersions); err != nil {
return nil, fmt.Errorf("SDK version %s does not satisfy the minimum required by numaflow version %s: %w",
sdkVersion, numaflowVersion, err)
}
@@ -176,8 +180,11 @@ func checkNumaflowCompatibility(numaflowVersion string, minNumaflowVersion strin
}
// checkSDKCompatibility checks if the current SDK version is compatible with the numaflow version
-func checkSDKCompatibility(sdkVersion string, sdkLanguage Language, minSupportedSDKVersions sdkConstraints) error {
- if sdkRequiredVersion, ok := minSupportedSDKVersions[sdkLanguage]; ok {
+func checkSDKCompatibility(sdkVersion string, sdkLanguage Language, containerType ContainerType, minSupportedSDKVersions sdkConstraints) error {
+ if _, ok := minSupportedSDKVersions[sdkLanguage]; !ok {
+ return fmt.Errorf("SDK language %s is not supported", sdkLanguage)
+ }
+ if sdkRequiredVersion, ok := minSupportedSDKVersions[sdkLanguage][containerType]; ok {
sdkConstraint := fmt.Sprintf(">= %s", sdkRequiredVersion)
if sdkLanguage == Python {
// Python pre-releases/releases follow PEP440 specification which requires a different library for parsing
@@ -206,6 +213,19 @@ func checkSDKCompatibility(sdkVersion string, sdkLanguage Language, minSupported
sdkVersionSemVer.String(), humanReadable(sdkRequiredVersion), err)
}
}
+ } else {
+ return fmt.Errorf("SDK container type %s is not supported", containerType)
}
return nil
}
+
+// getContainerType returns the container type from the server info file path
+// serverInfoFilePath is in the format of "/var/run/numaflow/{ContainerType}-server-info"
+func getContainerType(serverInfoFilePath string) (ContainerType, error) {
+ splits := strings.Split(serverInfoFilePath, "/")
+ if containerType := strings.TrimSuffix(splits[len(splits)-1], "-server-info"); containerType == "" {
+ return "", fmt.Errorf("failed to get container type from server info file path: %s", serverInfoFilePath)
+ } else {
+ return ContainerType(containerType), nil
+ }
+}
diff --git a/pkg/sdkclient/serverinfo/serverinfo_test.go b/pkg/sdkclient/serverinfo/serverinfo_test.go
index ad7f06e690..105775de17 100644
--- a/pkg/sdkclient/serverinfo/serverinfo_test.go
+++ b/pkg/sdkclient/serverinfo/serverinfo_test.go
@@ -29,7 +29,7 @@ import (
)
func Test_SDKServerInfo(t *testing.T) {
- filepath := os.TempDir() + "/server-info"
+ filepath := os.TempDir() + "/sourcer-server-info"
defer os.Remove(filepath)
info := &ServerInfo{
Protocol: TCP,
@@ -185,10 +185,18 @@ func Test_CheckNumaflowCompatibility(t *testing.T) {
// this test suite is to test SDK compatibility check when all the minimum-supported versions are stable releases
func Test_CheckSDKCompatibility_MinimumBeingStableReleases(t *testing.T) {
var testMinimumSupportedSDKVersions = sdkConstraints{
- Python: "0.6.0rc100",
- Go: "0.6.0-z",
- Java: "0.6.0-z",
- Rust: "0.1.0-z",
+ Python: map[ContainerType]string{
+ sourcer: "0.6.0rc100",
+ },
+ Go: map[ContainerType]string{
+ sourcer: "0.6.0-z",
+ },
+ Java: map[ContainerType]string{
+ sourcer: "0.6.0-z",
+ },
+ Rust: map[ContainerType]string{
+ sourcer: "0.1.0-z",
+ },
}
tests := []struct {
name string
@@ -275,7 +283,7 @@ func Test_CheckSDKCompatibility_MinimumBeingStableReleases(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- err := checkSDKCompatibility(tt.sdkVersion, tt.sdkLanguage, tt.minimumSupportedSDKVersions)
+ err := checkSDKCompatibility(tt.sdkVersion, tt.sdkLanguage, sourcer, tt.minimumSupportedSDKVersions)
if tt.shouldErr {
assert.Error(t, err, "Expected error")
assert.Contains(t, err.Error(), tt.errMessage)
@@ -289,10 +297,18 @@ func Test_CheckSDKCompatibility_MinimumBeingStableReleases(t *testing.T) {
// this test suite is to test SDK compatibility check when all the minimum-supported versions are pre-releases
func Test_CheckSDKCompatibility_MinimumBeingPreReleases(t *testing.T) {
var testMinimumSupportedSDKVersions = sdkConstraints{
- Python: "0.6.0b1",
- Go: "0.6.0-rc2",
- Java: "0.6.0-rc2",
- Rust: "0.1.0-rc3",
+ Python: map[ContainerType]string{
+ sourcer: "0.6.0b1",
+ },
+ Go: map[ContainerType]string{
+ sourcer: "0.6.0-rc2",
+ },
+ Java: map[ContainerType]string{
+ sourcer: "0.6.0-rc2",
+ },
+ Rust: map[ContainerType]string{
+ sourcer: "0.1.0-rc3",
+ },
}
tests := []struct {
name string
@@ -379,7 +395,7 @@ func Test_CheckSDKCompatibility_MinimumBeingPreReleases(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- err := checkSDKCompatibility(tt.sdkVersion, tt.sdkLanguage, tt.minimumSupportedSDKVersions)
+ err := checkSDKCompatibility(tt.sdkVersion, tt.sdkLanguage, sourcer, tt.minimumSupportedSDKVersions)
if tt.shouldErr {
assert.Error(t, err, "Expected error")
assert.Contains(t, err.Error(), tt.errMessage)
diff --git a/pkg/sdkclient/serverinfo/types.go b/pkg/sdkclient/serverinfo/types.go
index 9e4a152d03..23f6e2394d 100644
--- a/pkg/sdkclient/serverinfo/types.go
+++ b/pkg/sdkclient/serverinfo/types.go
@@ -27,7 +27,23 @@ const (
Rust Language = "rust"
)
-type sdkConstraints map[Language]string
+type ContainerType string
+
+// the string content matches the corresponding server info file name.
+// DO NOT change it unless the server info file name is changed.
+const (
+ sourcer ContainerType = "sourcer"
+ sourcetransformer ContainerType = "sourcetransformer"
+ sinker ContainerType = "sinker"
+ mapper ContainerType = "mapper"
+ reducer ContainerType = "reducer"
+ reducestreamer ContainerType = "reducestreamer"
+ sessionreducer ContainerType = "sessionreducer"
+ sideinput ContainerType = "sideinput"
+ fbsinker ContainerType = "fb-sinker"
+)
+
+type sdkConstraints map[Language]map[ContainerType]string
/*
minimumSupportedSDKVersions is the minimum supported version of each SDK for the current numaflow version.
@@ -70,14 +86,54 @@ A constraint ">=0.8.0-z" will match any pre-release version of 0.8.0, including
More details about version comparison can be found in the PEP 440 and semver documentation.
*/
var minimumSupportedSDKVersions = sdkConstraints{
- // meaning the minimum supported python SDK version is 0.8.0
- Python: "0.8.0rc100",
- // meaning the minimum supported go SDK version is 0.8.0
- Go: "0.8.0-z",
- // meaning the minimum supported java SDK version is 0.8.0
- Java: "0.8.0-z",
- // meaning the minimum supported rust SDK version is 0.1.0
- Rust: "0.1.0-z",
+ Python: map[ContainerType]string{
+ // meaning the minimum supported python SDK version is 0.8.0
+ sourcer: "0.8.0rc100",
+ sourcetransformer: "0.8.0rc100",
+ sinker: "0.8.0rc100",
+ mapper: "0.8.0rc100",
+ reducer: "0.8.0rc100",
+ reducestreamer: "0.8.0rc100",
+ sessionreducer: "0.8.0rc100",
+ sideinput: "0.8.0rc100",
+ fbsinker: "0.8.0rc100",
+ },
+ Go: map[ContainerType]string{
+ // meaning the minimum supported go SDK version is 0.8.0
+ sourcer: "0.8.0-z",
+ sourcetransformer: "0.8.0-z",
+ sinker: "0.8.0-z",
+ mapper: "0.8.0-z",
+ reducer: "0.8.0-z",
+ reducestreamer: "0.8.0-z",
+ sessionreducer: "0.8.0-z",
+ sideinput: "0.8.0-z",
+ fbsinker: "0.8.0-z",
+ },
+ Java: map[ContainerType]string{
+ // meaning the minimum supported go SDK version is 0.8.0
+ sourcer: "0.8.0-z",
+ sourcetransformer: "0.8.0-z",
+ sinker: "0.8.0-z",
+ mapper: "0.8.0-z",
+ reducer: "0.8.0-z",
+ reducestreamer: "0.8.0-z",
+ sessionreducer: "0.8.0-z",
+ sideinput: "0.8.0-z",
+ fbsinker: "0.8.0-z",
+ },
+ Rust: map[ContainerType]string{
+ // meaning the minimum supported go SDK version is 0.1.0
+ sourcer: "0.1.0-z",
+ sourcetransformer: "0.1.0-z",
+ sinker: "0.1.0-z",
+ mapper: "0.1.0-z",
+ reducer: "0.1.0-z",
+ reducestreamer: "0.1.0-z",
+ sessionreducer: "0.1.0-z",
+ sideinput: "0.1.0-z",
+ fbsinker: "0.1.0-z",
+ },
}
// humanReadable returns the human-readable minimum supported version.
From 6cdec2d6d1325866e99a204389bc9dc460146cbf Mon Sep 17 00:00:00 2001
From: Yashash H L
Date: Fri, 27 Sep 2024 01:18:49 +0530
Subject: [PATCH 071/188] feat: Bidirectional Streaming for UDSink (#2080)
Signed-off-by: Yashash H L
Signed-off-by: Vigith Maurice
Co-authored-by: Vigith Maurice
---
go.mod | 2 +-
go.sum | 4 +-
pkg/apis/proto/sink/v1/sink.proto | 39 ++++--
pkg/sdkclient/sinker/client.go | 115 +++++++++++++++---
pkg/sdkclient/sinker/client_test.go | 34 ++++--
pkg/sdkclient/sinker/interface.go | 2 +-
pkg/sinks/sink.go | 8 +-
pkg/sinks/udsink/sink.go | 16 +--
pkg/sinks/udsink/udsink_grpc.go | 18 +--
pkg/sinks/udsink/udsink_grpc_test.go | 67 ++++++-----
rust/Cargo.lock | 138 ++++++++++++----------
rust/monovertex/Cargo.toml | 2 +-
rust/monovertex/proto/sink.proto | 55 ++++++---
rust/monovertex/src/forwarder.rs | 26 ++--
rust/monovertex/src/message.rs | 19 +--
rust/monovertex/src/sink.rs | 125 ++++++++++++++------
rust/monovertex/src/source.rs | 1 -
rust/servesink/Cargo.toml | 2 +-
test/map-e2e/map_test.go | 23 ++--
test/map-e2e/testdata/flatmap-batch.yaml | 55 ++++-----
test/map-e2e/testdata/flatmap-stream.yaml | 61 +++++-----
test/map-e2e/testdata/flatmap.yaml | 56 ++++-----
22 files changed, 546 insertions(+), 322 deletions(-)
diff --git a/go.mod b/go.mod
index c251e8216b..a015dcac65 100644
--- a/go.mod
+++ b/go.mod
@@ -32,7 +32,7 @@ require (
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe
github.com/nats-io/nats-server/v2 v2.10.20
github.com/nats-io/nats.go v1.37.0
- github.com/numaproj/numaflow-go v0.8.2-0.20240918054944-0fd13d430793
+ github.com/numaproj/numaflow-go v0.8.2-0.20240923064822-e16694a878d0
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_model v0.5.0
github.com/prometheus/common v0.45.0
diff --git a/go.sum b/go.sum
index 722bd72ee3..b17e994439 100644
--- a/go.sum
+++ b/go.sum
@@ -485,8 +485,8 @@ github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDm
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/numaproj/numaflow-go v0.8.2-0.20240918054944-0fd13d430793 h1:kUQw1LsUvmTjqFfcia6DZOxy8qCQwvdY0TpOnR8w3Xg=
-github.com/numaproj/numaflow-go v0.8.2-0.20240918054944-0fd13d430793/go.mod h1:g4JZOyUPhjfhv+kR0sX5d8taw/dasgKPXLvQBi39mJ4=
+github.com/numaproj/numaflow-go v0.8.2-0.20240923064822-e16694a878d0 h1:qPqZfJdPdsz4qymyzMSNICQe/xBnx9P/G3hRbC1DR7k=
+github.com/numaproj/numaflow-go v0.8.2-0.20240923064822-e16694a878d0/go.mod h1:g4JZOyUPhjfhv+kR0sX5d8taw/dasgKPXLvQBi39mJ4=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
diff --git a/pkg/apis/proto/sink/v1/sink.proto b/pkg/apis/proto/sink/v1/sink.proto
index caadaf66b6..255b9b6969 100644
--- a/pkg/apis/proto/sink/v1/sink.proto
+++ b/pkg/apis/proto/sink/v1/sink.proto
@@ -27,7 +27,7 @@ package sink.v1;
service Sink {
// SinkFn writes the request to a user defined sink.
- rpc SinkFn(stream SinkRequest) returns (SinkResponse);
+ rpc SinkFn(stream SinkRequest) returns (stream SinkResponse);
// IsReady is the heartbeat endpoint for gRPC.
rpc IsReady(google.protobuf.Empty) returns (ReadyResponse);
@@ -37,12 +37,32 @@ service Sink {
* SinkRequest represents a request element.
*/
message SinkRequest {
- repeated string keys = 1;
- bytes value = 2;
- google.protobuf.Timestamp event_time = 3;
- google.protobuf.Timestamp watermark = 4;
- string id = 5;
- map headers = 6;
+ message Request {
+ repeated string keys = 1;
+ bytes value = 2;
+ google.protobuf.Timestamp event_time = 3;
+ google.protobuf.Timestamp watermark = 4;
+ string id = 5;
+ map headers = 6;
+ }
+ message Status {
+ bool eot = 1;
+ }
+ // Required field indicating the request.
+ Request request = 1;
+ // Required field indicating the status of the request.
+ // If eot is set to true, it indicates the end of transmission.
+ Status status = 2;
+ // optional field indicating the handshake message.
+ optional Handshake handshake = 3;
+}
+
+/*
+ * Handshake message between client and server to indicate the start of transmission.
+ */
+message Handshake {
+ // Required field indicating the start of transmission.
+ bool sot = 1;
}
/**
@@ -73,5 +93,6 @@ message SinkResponse {
// err_msg is the error message, set it if success is set to false.
string err_msg = 3;
}
- repeated Result results = 1;
-}
+ Result result = 1;
+ optional Handshake handshake = 2;
+}
\ No newline at end of file
diff --git a/pkg/sdkclient/sinker/client.go b/pkg/sdkclient/sinker/client.go
index 67fe08557c..51c1273568 100644
--- a/pkg/sdkclient/sinker/client.go
+++ b/pkg/sdkclient/sinker/client.go
@@ -19,26 +19,31 @@ package sinker
import (
"context"
"fmt"
+ "time"
sinkpb "github.com/numaproj/numaflow-go/pkg/apis/proto/sink/v1"
+ "go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/numaproj/numaflow/pkg/sdkclient"
grpcutil "github.com/numaproj/numaflow/pkg/sdkclient/grpc"
"github.com/numaproj/numaflow/pkg/sdkclient/serverinfo"
+ "github.com/numaproj/numaflow/pkg/shared/logging"
)
// client contains the grpc connection and the grpc client.
type client struct {
- conn *grpc.ClientConn
- grpcClt sinkpb.SinkClient
+ conn *grpc.ClientConn
+ grpcClt sinkpb.SinkClient
+ sinkStream sinkpb.Sink_SinkFnClient
}
var _ Client = (*client)(nil)
-func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
+func New(ctx context.Context, serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (Client, error) {
var opts = sdkclient.DefaultOptions(sdkclient.SinkAddr)
+ var logger = logging.FromContext(ctx)
for _, inputOption := range inputOptions {
inputOption(opts)
@@ -53,12 +58,70 @@ func New(serverInfo *serverinfo.ServerInfo, inputOptions ...sdkclient.Option) (C
c.conn = conn
c.grpcClt = sinkpb.NewSinkClient(conn)
+
+ // Wait until the server is ready
+waitUntilReady:
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, fmt.Errorf("failed to connect to the server: %v", ctx.Err())
+ default:
+ ready, _ := c.IsReady(ctx, &emptypb.Empty{})
+ if ready {
+ break waitUntilReady
+ } else {
+ logger.Warnw("waiting for the server to be ready", zap.String("server", opts.UdsSockAddr()))
+ time.Sleep(100 * time.Millisecond)
+ }
+ }
+ }
+
+ // Create the sink stream
+ c.sinkStream, err = c.grpcClt.SinkFn(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create sink stream: %v", err)
+ }
+
+ // Perform handshake
+ handshakeRequest := &sinkpb.SinkRequest{
+ Handshake: &sinkpb.Handshake{
+ Sot: true,
+ },
+ }
+ if err := c.sinkStream.Send(handshakeRequest); err != nil {
+ return nil, fmt.Errorf("failed to send handshake request: %v", err)
+ }
+
+ handshakeResponse, err := c.sinkStream.Recv()
+ if err != nil {
+ return nil, fmt.Errorf("failed to receive handshake response: %v", err)
+ }
+ if handshakeResponse.GetHandshake() == nil || !handshakeResponse.GetHandshake().GetSot() {
+ return nil, fmt.Errorf("invalid handshake response")
+ }
+
return c, nil
}
// NewFromClient creates a new client object from a grpc client, which is useful for testing.
-func NewFromClient(c sinkpb.SinkClient) (Client, error) {
- return &client{grpcClt: c}, nil
+func NewFromClient(ctx context.Context, sinkClient sinkpb.SinkClient, inputOptions ...sdkclient.Option) (Client, error) {
+ var opts = sdkclient.DefaultOptions(sdkclient.SinkAddr)
+ var err error
+
+ for _, inputOption := range inputOptions {
+ inputOption(opts)
+ }
+
+ c := new(client)
+ c.grpcClt = sinkClient
+
+ // Create the sink stream
+ c.sinkStream, err = c.grpcClt.SinkFn(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create sink stream: %v", err)
+ }
+
+ return c, nil
}
// CloseConn closes the grpc client connection.
@@ -66,6 +129,10 @@ func (c *client) CloseConn(ctx context.Context) error {
if c.conn == nil {
return nil
}
+ err := c.sinkStream.CloseSend()
+ if err != nil {
+ return err
+ }
return c.conn.Close()
}
@@ -79,20 +146,34 @@ func (c *client) IsReady(ctx context.Context, in *emptypb.Empty) (bool, error) {
}
// SinkFn applies a function to a list of requests.
-func (c *client) SinkFn(ctx context.Context, requests []*sinkpb.SinkRequest) (*sinkpb.SinkResponse, error) {
- stream, err := c.grpcClt.SinkFn(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to execute c.grpcClt.SinkFn(): %w", err)
- }
- for _, datum := range requests {
- if err := stream.Send(datum); err != nil {
- return nil, fmt.Errorf("failed to execute stream.Send(%v): %w", datum, err)
+func (c *client) SinkFn(ctx context.Context, requests []*sinkpb.SinkRequest) ([]*sinkpb.SinkResponse, error) {
+ // Stream the array of sink requests
+ for _, req := range requests {
+ if err := c.sinkStream.Send(req); err != nil {
+ return nil, fmt.Errorf("failed to send sink request: %v", err)
}
}
- responseList, err := stream.CloseAndRecv()
- if err != nil {
- return nil, fmt.Errorf("failed to execute stream.CloseAndRecv(): %w", err)
+
+ // send eot request
+ eotRequest := &sinkpb.SinkRequest{
+ Status: &sinkpb.SinkRequest_Status{
+ Eot: true,
+ },
+ }
+
+ if err := c.sinkStream.Send(eotRequest); err != nil {
+ return nil, fmt.Errorf("failed to send eot request: %v", err)
+ }
+
+ // Wait for the corresponding responses
+ var responses []*sinkpb.SinkResponse
+ for i := 0; i < len(requests); i++ {
+ resp, err := c.sinkStream.Recv()
+ if err != nil {
+ return nil, fmt.Errorf("failed to receive sink response: %v", err)
+ }
+ responses = append(responses, resp)
}
- return responseList, nil
+ return responses, nil
}
diff --git a/pkg/sdkclient/sinker/client_test.go b/pkg/sdkclient/sinker/client_test.go
index fdc115a882..762a989fed 100644
--- a/pkg/sdkclient/sinker/client_test.go
+++ b/pkg/sdkclient/sinker/client_test.go
@@ -36,10 +36,12 @@ func TestClient_IsReady(t *testing.T) {
defer ctrl.Finish()
mockClient := sinkmock.NewMockSinkClient(ctrl)
+ mockStream := sinkmock.NewMockSink_SinkFnClient(ctrl)
+ mockClient.EXPECT().SinkFn(gomock.Any(), gomock.Any()).Return(mockStream, nil)
mockClient.EXPECT().IsReady(gomock.Any(), gomock.Any()).Return(&sinkpb.ReadyResponse{Ready: true}, nil)
mockClient.EXPECT().IsReady(gomock.Any(), gomock.Any()).Return(&sinkpb.ReadyResponse{Ready: false}, fmt.Errorf("mock connection refused"))
- testClient, err := NewFromClient(mockClient)
+ testClient, err := NewFromClient(ctx, mockClient)
assert.NoError(t, err)
reflect.DeepEqual(testClient, &client{
grpcClt: mockClient,
@@ -62,31 +64,37 @@ func TestClient_SinkFn(t *testing.T) {
mockSinkClient := sinkmock.NewMockSink_SinkFnClient(ctrl)
mockSinkClient.EXPECT().Send(gomock.Any()).Return(nil).AnyTimes()
- mockSinkClient.EXPECT().CloseAndRecv().Return(&sinkpb.SinkResponse{
- Results: []*sinkpb.SinkResponse_Result{
- {
- Id: "temp-id",
- Status: sinkpb.Status_SUCCESS,
- },
+ mockSinkClient.EXPECT().Recv().Return(&sinkpb.SinkResponse{
+ Result: &sinkpb.SinkResponse_Result{
+ Id: "temp-id",
+ Status: sinkpb.Status_SUCCESS,
},
}, nil)
mockClient := sinkmock.NewMockSinkClient(ctrl)
mockClient.EXPECT().SinkFn(gomock.Any(), gomock.Any()).Return(mockSinkClient, nil)
- testClient, err := NewFromClient(mockClient)
+ testClient, err := NewFromClient(ctx, mockClient)
assert.NoError(t, err)
reflect.DeepEqual(testClient, &client{
grpcClt: mockClient,
})
- response, err := testClient.SinkFn(ctx, []*sinkpb.SinkRequest{})
- assert.Equal(t, &sinkpb.SinkResponse{Results: []*sinkpb.SinkResponse_Result{
+ response, err := testClient.SinkFn(ctx, []*sinkpb.SinkRequest{
{
- Id: "temp-id",
- Status: sinkpb.Status_SUCCESS,
+ Request: &sinkpb.SinkRequest_Request{
+ Id: "temp-id",
+ },
+ },
+ })
+ assert.Equal(t, []*sinkpb.SinkResponse{
+ {
+ Result: &sinkpb.SinkResponse_Result{
+ Id: "temp-id",
+ Status: sinkpb.Status_SUCCESS,
+ },
},
- }}, response)
+ }, response)
assert.NoError(t, err)
}
diff --git a/pkg/sdkclient/sinker/interface.go b/pkg/sdkclient/sinker/interface.go
index 9f4aa0eb2f..489394fded 100644
--- a/pkg/sdkclient/sinker/interface.go
+++ b/pkg/sdkclient/sinker/interface.go
@@ -27,5 +27,5 @@ import (
type Client interface {
CloseConn(ctx context.Context) error
IsReady(ctx context.Context, in *emptypb.Empty) (bool, error)
- SinkFn(ctx context.Context, datumList []*sinkpb.SinkRequest) (*sinkpb.SinkResponse, error)
+ SinkFn(ctx context.Context, datumList []*sinkpb.SinkRequest) ([]*sinkpb.SinkResponse, error)
}
diff --git a/pkg/sinks/sink.go b/pkg/sinks/sink.go
index 413d232799..e15c02daa0 100644
--- a/pkg/sinks/sink.go
+++ b/pkg/sinks/sink.go
@@ -84,7 +84,7 @@ func (u *SinkProcessor) Start(ctx context.Context) error {
switch u.ISBSvcType {
case dfv1.ISBSvcTypeRedis:
redisClient := redisclient.NewInClusterRedisClient()
- readOptions := []redisclient.Option{}
+ var readOptions []redisclient.Option
if x := u.VertexInstance.Vertex.Spec.Limits; x != nil && x.ReadTimeout != nil {
readOptions = append(readOptions, redisclient.WithReadTimeOut(x.ReadTimeout.Duration))
}
@@ -155,7 +155,7 @@ func (u *SinkProcessor) Start(ctx context.Context) error {
return err
}
- sdkClient, err := sinkclient.New(serverInfo, sdkclient.WithMaxMessageSize(maxMessageSize))
+ sdkClient, err := sinkclient.New(ctx, serverInfo, sdkclient.WithMaxMessageSize(maxMessageSize))
if err != nil {
return fmt.Errorf("failed to create sdk client, %w", err)
}
@@ -184,7 +184,7 @@ func (u *SinkProcessor) Start(ctx context.Context) error {
return err
}
- sdkClient, err := sinkclient.New(serverInfo, sdkclient.WithMaxMessageSize(maxMessageSize), sdkclient.WithUdsSockAddr(sdkclient.FbSinkAddr))
+ sdkClient, err := sinkclient.New(ctx, serverInfo, sdkclient.WithMaxMessageSize(maxMessageSize), sdkclient.WithUdsSockAddr(sdkclient.FbSinkAddr))
if err != nil {
return fmt.Errorf("failed to create sdk client, %w", err)
}
@@ -292,7 +292,7 @@ func (u *SinkProcessor) Start(ctx context.Context) error {
// wait for all the forwarders to exit
finalWg.Wait()
- // close the from vertex wm stores
+ // close the fromVertex wm stores
// since we created the stores, we can close them
for _, wmStore := range fromVertexWmStores {
_ = wmStore.Close()
diff --git a/pkg/sinks/udsink/sink.go b/pkg/sinks/udsink/sink.go
index 36a7dc1219..fad85a925c 100644
--- a/pkg/sinks/udsink/sink.go
+++ b/pkg/sinks/udsink/sink.go
@@ -65,13 +65,15 @@ func (s *UserDefinedSink) Write(ctx context.Context, messages []isb.Message) ([]
msgs := make([]*sinkpb.SinkRequest, len(messages))
for i, m := range messages {
msgs[i] = &sinkpb.SinkRequest{
- Id: m.ID.String(),
- Value: m.Payload,
- Keys: m.Keys,
- EventTime: timestamppb.New(m.EventTime),
- // Watermark is only available in readmessage....
- Watermark: timestamppb.New(time.Time{}), // TODO: insert the correct watermark
- Headers: m.Headers,
+ Request: &sinkpb.SinkRequest_Request{
+ Id: m.ID.String(),
+ Value: m.Payload,
+ Keys: m.Keys,
+ EventTime: timestamppb.New(m.EventTime),
+ // Watermark is only available in readmessage....
+ Watermark: timestamppb.New(time.Time{}), // TODO: insert the correct watermark
+ Headers: m.Headers,
+ },
}
}
return nil, s.udsink.ApplySink(ctx, msgs)
diff --git a/pkg/sinks/udsink/udsink_grpc.go b/pkg/sinks/udsink/udsink_grpc.go
index 1b9c31131c..80f47d2675 100644
--- a/pkg/sinks/udsink/udsink_grpc.go
+++ b/pkg/sinks/udsink/udsink_grpc.go
@@ -91,7 +91,7 @@ func (u *UDSgRPCBasedUDSink) WaitUntilReady(ctx context.Context) error {
func (u *UDSgRPCBasedUDSink) ApplySink(ctx context.Context, requests []*sinkpb.SinkRequest) []error {
errs := make([]error, len(requests))
- response, err := u.client.SinkFn(ctx, requests)
+ responses, err := u.client.SinkFn(ctx, requests)
if err != nil {
for i := range requests {
errs[i] = &ApplyUDSinkErr{
@@ -106,24 +106,24 @@ func (u *UDSgRPCBasedUDSink) ApplySink(ctx context.Context, requests []*sinkpb.S
return errs
}
// Use ID to map the response messages, so that there's no strict requirement for the user-defined sink to return the response in order.
- resMap := make(map[string]*sinkpb.SinkResponse_Result)
- for _, res := range response.GetResults() {
- resMap[res.GetId()] = res
+ resMap := make(map[string]*sinkpb.SinkResponse)
+ for _, res := range responses {
+ resMap[res.Result.GetId()] = res
}
for i, m := range requests {
- if r, existing := resMap[m.GetId()]; !existing {
+ if r, existing := resMap[m.Request.GetId()]; !existing {
errs[i] = &NotFoundErr
} else {
- if r.GetStatus() == sinkpb.Status_FAILURE {
- if r.GetErrMsg() != "" {
+ if r.Result.GetStatus() == sinkpb.Status_FAILURE {
+ if r.Result.GetErrMsg() != "" {
errs[i] = &ApplyUDSinkErr{
UserUDSinkErr: true,
- Message: r.GetErrMsg(),
+ Message: r.Result.GetErrMsg(),
}
} else {
errs[i] = &UnknownUDSinkErr
}
- } else if r.GetStatus() == sinkpb.Status_FALLBACK {
+ } else if r.Result.GetStatus() == sinkpb.Status_FALLBACK {
errs[i] = &WriteToFallbackErr
} else {
errs[i] = nil
diff --git a/pkg/sinks/udsink/udsink_grpc_test.go b/pkg/sinks/udsink/udsink_grpc_test.go
index b367416cc5..96968fd9eb 100644
--- a/pkg/sinks/udsink/udsink_grpc_test.go
+++ b/pkg/sinks/udsink/udsink_grpc_test.go
@@ -19,7 +19,6 @@ package udsink
import (
"context"
"errors"
- "fmt"
"testing"
"time"
@@ -32,8 +31,8 @@ import (
sinkclient "github.com/numaproj/numaflow/pkg/sdkclient/sinker"
)
-func NewMockUDSgRPCBasedUDSink(mockClient *sinkmock.MockSinkClient) *UDSgRPCBasedUDSink {
- c, _ := sinkclient.NewFromClient(mockClient)
+func NewMockUDSgRPCBasedUDSink(ctx context.Context, mockClient *sinkmock.MockSinkClient) *UDSgRPCBasedUDSink {
+ c, _ := sinkclient.NewFromClient(ctx, mockClient)
return &UDSgRPCBasedUDSink{c}
}
@@ -42,6 +41,8 @@ func Test_gRPCBasedUDSink_WaitUntilReadyWithMockClient(t *testing.T) {
defer ctrl.Finish()
mockClient := sinkmock.NewMockSinkClient(ctrl)
+ mockStream := sinkmock.NewMockSink_SinkFnClient(ctrl)
+ mockClient.EXPECT().SinkFn(gomock.Any(), gomock.Any()).Return(mockStream, nil)
mockClient.EXPECT().IsReady(gomock.Any(), gomock.Any()).Return(&sinkpb.ReadyResponse{Ready: true}, nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
@@ -53,7 +54,7 @@ func Test_gRPCBasedUDSink_WaitUntilReadyWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSink(mockClient)
+ u := NewMockUDSgRPCBasedUDSink(ctx, mockClient)
err := u.WaitUntilReady(ctx)
assert.NoError(t, err)
}
@@ -66,16 +67,20 @@ func Test_gRPCBasedUDSink_ApplyWithMockClient(t *testing.T) {
testDatumList := []*sinkpb.SinkRequest{
{
- Id: "test_id_0",
- Value: []byte(`sink_message_success`),
- EventTime: timestamppb.New(time.Unix(1661169660, 0)),
- Watermark: timestamppb.New(time.Time{}),
+ Request: &sinkpb.SinkRequest_Request{
+ Id: "test_id_0",
+ Value: []byte(`sink_message_success`),
+ EventTime: timestamppb.New(time.Unix(1661169660, 0)),
+ Watermark: timestamppb.New(time.Time{}),
+ },
},
{
- Id: "test_id_1",
- Value: []byte(`sink_message_err`),
- EventTime: timestamppb.New(time.Unix(1661169660, 0)),
- Watermark: timestamppb.New(time.Time{}),
+ Request: &sinkpb.SinkRequest_Request{
+ Id: "test_id_1",
+ Value: []byte(`sink_message_err`),
+ EventTime: timestamppb.New(time.Unix(1661169660, 0)),
+ Watermark: timestamppb.New(time.Time{}),
+ },
},
}
testResponseList := []*sinkpb.SinkResponse_Result{
@@ -93,8 +98,11 @@ func Test_gRPCBasedUDSink_ApplyWithMockClient(t *testing.T) {
mockSinkClient := sinkmock.NewMockSink_SinkFnClient(ctrl)
mockSinkClient.EXPECT().Send(gomock.Any()).Return(nil).AnyTimes()
- mockSinkClient.EXPECT().CloseAndRecv().Return(&sinkpb.SinkResponse{
- Results: testResponseList,
+ mockSinkClient.EXPECT().Recv().Return(&sinkpb.SinkResponse{
+ Result: testResponseList[0],
+ }, nil)
+ mockSinkClient.EXPECT().Recv().Return(&sinkpb.SinkResponse{
+ Result: testResponseList[1],
}, nil)
mockClient := sinkmock.NewMockSinkClient(ctrl)
@@ -109,7 +117,7 @@ func Test_gRPCBasedUDSink_ApplyWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSink(mockClient)
+ u := NewMockUDSgRPCBasedUDSink(ctx, mockClient)
gotErrList := u.ApplySink(ctx, testDatumList)
assert.Equal(t, 2, len(gotErrList))
assert.Equal(t, nil, gotErrList[0])
@@ -127,16 +135,20 @@ func Test_gRPCBasedUDSink_ApplyWithMockClient(t *testing.T) {
testDatumList := []*sinkpb.SinkRequest{
{
- Id: "test_id_0",
- Value: []byte(`sink_message_grpc_err`),
- EventTime: timestamppb.New(time.Unix(1661169660, 0)),
- Watermark: timestamppb.New(time.Time{}),
+ Request: &sinkpb.SinkRequest_Request{
+ Id: "test_id_0",
+ Value: []byte(`sink_message_grpc_err`),
+ EventTime: timestamppb.New(time.Unix(1661169660, 0)),
+ Watermark: timestamppb.New(time.Time{}),
+ },
},
{
- Id: "test_id_1",
- Value: []byte(`sink_message_grpc_err`),
- EventTime: timestamppb.New(time.Unix(1661169660, 0)),
- Watermark: timestamppb.New(time.Time{}),
+ Request: &sinkpb.SinkRequest_Request{
+ Id: "test_id_1",
+ Value: []byte(`sink_message_grpc_err`),
+ EventTime: timestamppb.New(time.Unix(1661169660, 0)),
+ Watermark: timestamppb.New(time.Time{}),
+ },
},
}
@@ -144,7 +156,8 @@ func Test_gRPCBasedUDSink_ApplyWithMockClient(t *testing.T) {
mockSinkErrClient.EXPECT().Send(gomock.Any()).Return(nil).AnyTimes()
mockClient := sinkmock.NewMockSinkClient(ctrl)
- mockClient.EXPECT().SinkFn(gomock.Any(), gomock.Any()).Return(mockSinkErrClient, fmt.Errorf("mock SinkFn error"))
+ mockClient.EXPECT().SinkFn(gomock.Any(), gomock.Any()).Return(mockSinkErrClient, nil)
+ mockSinkErrClient.EXPECT().Recv().Return(nil, errors.New("mock SinkFn error")).AnyTimes()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -155,12 +168,12 @@ func Test_gRPCBasedUDSink_ApplyWithMockClient(t *testing.T) {
}
}()
- u := NewMockUDSgRPCBasedUDSink(mockClient)
+ u := NewMockUDSgRPCBasedUDSink(ctx, mockClient)
gotErrList := u.ApplySink(ctx, testDatumList)
expectedErrList := []error{
&ApplyUDSinkErr{
UserUDSinkErr: false,
- Message: "gRPC client.SinkFn failed, failed to execute c.grpcClt.SinkFn(): mock SinkFn error",
+ Message: "gRPC client.SinkFn failed, failed to receive sink response: mock SinkFn error",
InternalErr: InternalErr{
Flag: true,
MainCarDown: false,
@@ -168,7 +181,7 @@ func Test_gRPCBasedUDSink_ApplyWithMockClient(t *testing.T) {
},
&ApplyUDSinkErr{
UserUDSinkErr: false,
- Message: "gRPC client.SinkFn failed, failed to execute c.grpcClt.SinkFn(): mock SinkFn error",
+ Message: "gRPC client.SinkFn failed, failed to receive sink response: mock SinkFn error",
InternalErr: InternalErr{
Flag: true,
MainCarDown: false,
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 81b0a5149b..d6839447ba 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -110,9 +110,9 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.82"
+version = "0.1.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
+checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
@@ -145,9 +145,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
-version = "0.21.1"
+version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "234314bd569802ec87011d653d6815c6d7b9ffb969e9fee5b8b20ef860e8dce9"
+checksum = "b3ddc4a5b231dd6958b140ff3151b6412b3f4321fab354f399eec8f14b06df62"
dependencies = [
"bindgen",
"cc",
@@ -160,9 +160,9 @@ dependencies = [
[[package]]
name = "axum"
-version = "0.7.5"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
+checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec"
dependencies = [
"async-trait",
"axum-core",
@@ -186,7 +186,7 @@ dependencies = [
"serde_urlencoded",
"sync_wrapper 1.0.1",
"tokio",
- "tower",
+ "tower 0.5.1",
"tower-layer",
"tower-service",
"tracing",
@@ -194,9 +194,9 @@ dependencies = [
[[package]]
name = "axum-core"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00"
dependencies = [
"async-trait",
"bytes",
@@ -207,7 +207,7 @@ dependencies = [
"mime",
"pin-project-lite",
"rustversion",
- "sync_wrapper 0.1.2",
+ "sync_wrapper 1.0.1",
"tower-layer",
"tower-service",
"tracing",
@@ -215,11 +215,10 @@ dependencies = [
[[package]]
name = "axum-macros"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa"
+checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
dependencies = [
- "heck 0.4.1",
"proc-macro2",
"quote",
"syn",
@@ -245,7 +244,7 @@ dependencies = [
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.0",
- "tower",
+ "tower 0.4.13",
"tower-service",
]
@@ -907,12 +906,6 @@ dependencies = [
"http 1.1.0",
]
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
[[package]]
name = "heck"
version = "0.5.0"
@@ -1116,9 +1109,9 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba"
+checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
dependencies = [
"bytes",
"futures-channel",
@@ -1129,7 +1122,6 @@ dependencies = [
"pin-project-lite",
"socket2",
"tokio",
- "tower",
"tower-service",
"tracing",
]
@@ -1318,7 +1310,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-util",
- "tower",
+ "tower 0.4.13",
"tower-http",
"tracing",
]
@@ -1353,9 +1345,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
-version = "0.2.158"
+version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "libloading"
@@ -1498,7 +1490,7 @@ dependencies = [
"tokio-util",
"tonic",
"tonic-build",
- "tower",
+ "tower 0.4.13",
"tracing",
"tracing-subscriber",
"trait-variant",
@@ -1513,9 +1505,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]]
name = "nkeys"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2de02c883c178998da8d0c9816a88ef7ef5c58314dd1585c97a4a5679f3ab337"
+checksum = "9f49e787f4c61cbd0f9320b31cc26e58719f6aa5068e34697dd3aea361412fe3"
dependencies = [
"data-encoding",
"ed25519",
@@ -1605,7 +1597,7 @@ dependencies = [
[[package]]
name = "numaflow"
version = "0.1.1"
-source = "git+https://github.com/numaproj/numaflow-rs.git?branch=main#362f2b0a0705c34ce3693b8714885dfbae7843e8"
+source = "git+https://github.com/numaproj/numaflow-rs.git?rev=0c1682864a4b906fab52e149cfd7cacc679ce688#0c1682864a4b906fab52e149cfd7cacc679ce688"
dependencies = [
"chrono",
"futures-util",
@@ -1757,9 +1749,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
-version = "2.7.12"
+version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea"
+checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9"
dependencies = [
"memchr",
"thiserror",
@@ -1768,9 +1760,9 @@ dependencies = [
[[package]]
name = "pest_derive"
-version = "2.7.12"
+version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d"
+checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0"
dependencies = [
"pest",
"pest_generator",
@@ -1778,9 +1770,9 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.7.12"
+version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe"
+checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e"
dependencies = [
"pest",
"pest_meta",
@@ -1791,9 +1783,9 @@ dependencies = [
[[package]]
name = "pest_meta"
-version = "2.7.12"
+version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174"
+checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f"
dependencies = [
"once_cell",
"pest",
@@ -1854,9 +1846,9 @@ dependencies = [
[[package]]
name = "portable-atomic"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
+checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce"
[[package]]
name = "powerfmt"
@@ -1917,9 +1909,9 @@ dependencies = [
[[package]]
name = "prost"
-version = "0.13.2"
+version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995"
+checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
dependencies = [
"bytes",
"prost-derive",
@@ -1927,12 +1919,12 @@ dependencies = [
[[package]]
name = "prost-build"
-version = "0.13.2"
+version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302"
+checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15"
dependencies = [
"bytes",
- "heck 0.5.0",
+ "heck",
"itertools 0.13.0",
"log",
"multimap",
@@ -1948,9 +1940,9 @@ dependencies = [
[[package]]
name = "prost-derive"
-version = "0.13.2"
+version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac"
+checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5"
dependencies = [
"anyhow",
"itertools 0.13.0",
@@ -1961,9 +1953,9 @@ dependencies = [
[[package]]
name = "prost-types"
-version = "0.13.2"
+version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519"
+checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
dependencies = [
"prost",
]
@@ -2095,9 +2087,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.4"
+version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
+checksum = "62871f2d65009c0256aed1b9cfeeb8ac272833c404e13d53d400cd0dad7a2ac0"
dependencies = [
"bitflags 2.6.0",
]
@@ -2470,9 +2462,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.11.1"
+version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
+checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
dependencies = [
"core-foundation-sys",
"libc",
@@ -2625,7 +2617,7 @@ dependencies = [
"tempfile",
"thiserror",
"tokio",
- "tower",
+ "tower 0.4.13",
"tower-http",
"tracing",
"tracing-subscriber",
@@ -2816,18 +2808,18 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.63"
+version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.63"
+version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
@@ -3007,9 +2999,9 @@ dependencies = [
[[package]]
name = "toml_edit"
-version = "0.22.21"
+version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
"indexmap 2.5.0",
"serde",
@@ -3042,7 +3034,7 @@ dependencies = [
"socket2",
"tokio",
"tokio-stream",
- "tower",
+ "tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
@@ -3081,6 +3073,22 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "tower"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper 0.1.2",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "tower-http"
version = "0.5.2"
@@ -3253,9 +3261,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
-version = "0.1.13"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unsafe-libyaml"
@@ -3638,9 +3646,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
-version = "0.6.18"
+version = "0.6.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
+checksum = "c52ac009d615e79296318c1bcce2d422aaca15ad08515e344feeda07df67a587"
dependencies = [
"memchr",
]
diff --git a/rust/monovertex/Cargo.toml b/rust/monovertex/Cargo.toml
index ce973f79ac..f3644dc140 100644
--- a/rust/monovertex/Cargo.toml
+++ b/rust/monovertex/Cargo.toml
@@ -38,7 +38,7 @@ log = "0.4.22"
[dev-dependencies]
tempfile = "3.11.0"
-numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
+numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", rev = "0c1682864a4b906fab52e149cfd7cacc679ce688" }
[build-dependencies]
tonic-build = "0.12.1"
diff --git a/rust/monovertex/proto/sink.proto b/rust/monovertex/proto/sink.proto
index c413ea863b..300e570314 100644
--- a/rust/monovertex/proto/sink.proto
+++ b/rust/monovertex/proto/sink.proto
@@ -7,7 +7,7 @@ package sink.v1;
service Sink {
// SinkFn writes the request to a user defined sink.
- rpc SinkFn(stream SinkRequest) returns (SinkResponse);
+ rpc SinkFn(stream SinkRequest) returns (stream SinkResponse);
// IsReady is the heartbeat endpoint for gRPC.
rpc IsReady(google.protobuf.Empty) returns (ReadyResponse);
@@ -17,12 +17,32 @@ service Sink {
* SinkRequest represents a request element.
*/
message SinkRequest {
- repeated string keys = 1;
- bytes value = 2;
- google.protobuf.Timestamp event_time = 3;
- google.protobuf.Timestamp watermark = 4;
- string id = 5;
- map headers = 6;
+ message Request {
+ repeated string keys = 1;
+ bytes value = 2;
+ google.protobuf.Timestamp event_time = 3;
+ google.protobuf.Timestamp watermark = 4;
+ string id = 5;
+ map headers = 6;
+ }
+ message Status {
+ bool eot = 1;
+ }
+ // Required field indicating the request.
+ Request request = 1;
+ // Required field indicating the status of the request.
+ // If eot is set to true, it indicates the end of transmission.
+ Status status = 2;
+ // optional field indicating the handshake message.
+ optional Handshake handshake = 3;
+}
+
+/*
+ * Handshake message between client and server to indicate the start of transmission.
+ */
+message Handshake {
+ // Required field indicating the start of transmission.
+ bool sot = 1;
}
/**
@@ -32,6 +52,15 @@ message ReadyResponse {
bool ready = 1;
}
+/*
+ * Status is the status of the response.
+ */
+enum Status {
+ SUCCESS = 0;
+ FAILURE = 1;
+ FALLBACK = 2;
+}
+
/**
* SinkResponse is the individual response of each message written to the sink.
*/
@@ -44,14 +73,6 @@ message SinkResponse {
// err_msg is the error message, set it if success is set to false.
string err_msg = 3;
}
- repeated Result results = 1;
-}
-
-/*
- * Status is the status of the response.
- */
-enum Status {
- SUCCESS = 0;
- FAILURE = 1;
- FALLBACK = 2;
+ Result result = 1;
+ optional Handshake handshake = 2;
}
\ No newline at end of file
diff --git a/rust/monovertex/src/forwarder.rs b/rust/monovertex/src/forwarder.rs
index d60644b338..ac69c09903 100644
--- a/rust/monovertex/src/forwarder.rs
+++ b/rust/monovertex/src/forwarder.rs
@@ -381,11 +381,15 @@ impl Forwarder {
// create a map of id to result, since there is no strict requirement
// for the udsink to return the results in the same order as the requests
- let result_map: HashMap<_, _> = response
- .results
- .iter()
- .map(|result| (result.id.clone(), result))
- .collect();
+ let result_map = response
+ .into_iter()
+ .map(|resp| match resp.result {
+ Some(result) => Ok((result.id.clone(), result)),
+ None => Err(Error::SinkError(
+ "Response does not contain a result".to_string(),
+ )),
+ })
+ .collect::>>()?;
error_map.clear();
// drain all the messages that were successfully written
@@ -459,11 +463,15 @@ impl Forwarder {
// create a map of id to result, since there is no strict requirement
// for the udsink to return the results in the same order as the requests
- let result_map: HashMap<_, _> = fb_response
- .results
+ let result_map = fb_response
.iter()
- .map(|result| (result.id.clone(), result))
- .collect();
+ .map(|resp| match &resp.result {
+ Some(result) => Ok((result.id.clone(), result)),
+ None => Err(Error::SinkError(
+ "Response does not contain a result".to_string(),
+ )),
+ })
+ .collect::>>()?;
let mut contains_fallback_status = false;
diff --git a/rust/monovertex/src/message.rs b/rust/monovertex/src/message.rs
index 403c377ec4..d5cde5d2bc 100644
--- a/rust/monovertex/src/message.rs
+++ b/rust/monovertex/src/message.rs
@@ -6,9 +6,10 @@ use chrono::{DateTime, Utc};
use crate::error::Error;
use crate::shared::{prost_timestamp_from_utc, utc_from_timestamp};
+use crate::sink_pb::sink_request::Request;
use crate::sink_pb::SinkRequest;
use crate::source_pb;
-use crate::source_pb::{AckRequest, read_response};
+use crate::source_pb::{read_response, AckRequest};
use crate::sourcetransform_pb::SourceTransformRequest;
/// A message that is sent from the source to the sink.
@@ -94,12 +95,16 @@ impl TryFrom for Message {
impl From for SinkRequest {
fn from(message: Message) -> Self {
Self {
- keys: message.keys,
- value: message.value,
- event_time: prost_timestamp_from_utc(message.event_time),
- watermark: None,
- id: message.id,
- headers: message.headers,
+ request: Some(Request {
+ keys: message.keys,
+ value: message.value,
+ event_time: prost_timestamp_from_utc(message.event_time),
+ watermark: None,
+ id: message.id,
+ headers: message.headers,
+ }),
+ status: None,
+ handshake: None,
}
}
}
diff --git a/rust/monovertex/src/sink.rs b/rust/monovertex/src/sink.rs
index a2088a8c2f..873404d4c5 100644
--- a/rust/monovertex/src/sink.rs
+++ b/rust/monovertex/src/sink.rs
@@ -1,46 +1,98 @@
-use crate::error::Result;
+use crate::error::{Error, Result};
use crate::message::Message;
use crate::sink_pb::sink_client::SinkClient;
-use crate::sink_pb::{SinkRequest, SinkResponse};
+use crate::sink_pb::sink_request::Status;
+use crate::sink_pb::{Handshake, SinkRequest, SinkResponse};
+use tokio::sync::mpsc;
+use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::Channel;
+use tonic::{Request, Streaming};
+
+const DEFAULT_CHANNEL_SIZE: usize = 1000;
/// SinkWriter writes messages to a sink.
-#[derive(Clone)]
pub struct SinkWriter {
- client: SinkClient,
+ sink_tx: mpsc::Sender,
+ resp_stream: Streaming,
}
impl SinkWriter {
- pub(crate) async fn new(client: SinkClient) -> Result {
- Ok(Self { client })
- }
+ pub(crate) async fn new(mut client: SinkClient) -> Result {
+ let (sink_tx, sink_rx) = mpsc::channel(DEFAULT_CHANNEL_SIZE);
+ let sink_stream = ReceiverStream::new(sink_rx);
+
+ // Perform handshake with the server before sending any requests
+ let handshake_request = SinkRequest {
+ request: None,
+ status: None,
+ handshake: Some(Handshake { sot: true }),
+ };
+ sink_tx
+ .send(handshake_request)
+ .await
+ .map_err(|e| Error::SinkError(format!("failed to send handshake request: {}", e)))?;
+
+ let mut resp_stream = client
+ .sink_fn(Request::new(sink_stream))
+ .await?
+ .into_inner();
- pub(crate) async fn sink_fn(&mut self, messages: Vec) -> Result {
- // create a channel with at least size
- let (tx, rx) = tokio::sync::mpsc::channel(if messages.is_empty() {
- 1
- } else {
- messages.len()
- });
+ // First response from the server will be the handshake response. We need to check if the
+ // server has accepted the handshake.
+ let handshake_response = resp_stream.message().await?.ok_or(Error::SinkError(
+ "failed to receive handshake response".to_string(),
+ ))?;
+ // Handshake cannot be None during the initial phase and it has to set `sot` to true.
+ if handshake_response.handshake.map_or(true, |h| !h.sot) {
+ return Err(Error::SinkError("invalid handshake response".to_string()));
+ }
+
+ Ok(Self {
+ sink_tx,
+ resp_stream,
+ })
+ }
+
+ /// writes a set of messages to the sink.
+ pub(crate) async fn sink_fn(&mut self, messages: Vec) -> Result> {
let requests: Vec =
messages.into_iter().map(|message| message.into()).collect();
+ let num_requests = requests.len();
- tokio::spawn(async move {
- for request in requests {
- if tx.send(request).await.is_err() {
- break;
- }
- }
- });
-
- let response = self
- .client
- .sink_fn(tokio_stream::wrappers::ReceiverStream::new(rx))
- .await?
- .into_inner();
+ // write requests to the server
+ for request in requests {
+ self.sink_tx
+ .send(request)
+ .await
+ .map_err(|e| Error::SinkError(format!("failed to send request: {}", e)))?;
+ }
- Ok(response)
+ // send eot request to indicate the end of the stream
+ let eot_request = SinkRequest {
+ request: None,
+ status: Some(Status { eot: true }),
+ handshake: None,
+ };
+ self.sink_tx
+ .send(eot_request)
+ .await
+ .map_err(|e| Error::SinkError(format!("failed to send eot request: {}", e)))?;
+
+ // now that we have sent, we wait for responses!
+ // NOTE: this works now because the results are not streamed, as of today it will give the
+ // response only once it has read all the requests.
+ let mut responses = Vec::new();
+ for _ in 0..num_requests {
+ let response = self
+ .resp_stream
+ .message()
+ .await?
+ .ok_or(Error::SinkError("failed to receive response".to_string()))?;
+ responses.push(response);
+ };
+
+ Ok(responses)
}
}
@@ -57,10 +109,7 @@ mod tests {
struct Logger;
#[tonic::async_trait]
impl sink::Sinker for Logger {
- async fn sink(
- &self,
- mut input: tokio::sync::mpsc::Receiver,
- ) -> Vec {
+ async fn sink(&self, mut input: mpsc::Receiver) -> Vec {
let mut responses: Vec = Vec::new();
while let Some(datum) = input.recv().await {
let response = match std::str::from_utf8(&datum.value) {
@@ -87,13 +136,14 @@ mod tests {
let server_info = server_info_file.clone();
let server_socket = sock_file.clone();
+
let server_handle = tokio::spawn(async move {
sink::Server::new(Logger)
.with_socket_file(server_socket)
.with_server_info_file(server_info)
.start_with_shutdown(shutdown_rx)
.await
- .unwrap();
+ .expect("failed to start sink server");
});
// wait for the server to start
@@ -129,12 +179,17 @@ mod tests {
},
];
- let response = sink_client.sink_fn(messages).await?;
- assert_eq!(response.results.len(), 2);
+ let response = sink_client.sink_fn(messages.clone()).await?;
+ assert_eq!(response.len(), 2);
+ let response = sink_client.sink_fn(messages.clone()).await?;
+ assert_eq!(response.len(), 2);
+
+ drop(sink_client);
shutdown_tx
.send(())
.expect("failed to send shutdown signal");
+
server_handle.await.expect("failed to join server task");
Ok(())
}
diff --git a/rust/monovertex/src/source.rs b/rust/monovertex/src/source.rs
index fdfde1b6d4..6b3729d3d7 100644
--- a/rust/monovertex/src/source.rs
+++ b/rust/monovertex/src/source.rs
@@ -7,7 +7,6 @@ use crate::source_pb::source_client::SourceClient;
use crate::source_pb::{
ack_response, read_request, AckRequest, AckResponse, ReadRequest, ReadResponse,
};
-use log::info;
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::Channel;
diff --git a/rust/servesink/Cargo.toml b/rust/servesink/Cargo.toml
index 7b037c7208..a9a768ac6c 100644
--- a/rust/servesink/Cargo.toml
+++ b/rust/servesink/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
tonic = "0.12.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
-numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", branch = "main" }
+numaflow = { git = "https://github.com/numaproj/numaflow-rs.git", rev = "0c1682864a4b906fab52e149cfd7cacc679ce688" }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
diff --git a/test/map-e2e/map_test.go b/test/map-e2e/map_test.go
index a1d171a253..60821a869b 100644
--- a/test/map-e2e/map_test.go
+++ b/test/map-e2e/map_test.go
@@ -30,6 +30,7 @@ type MapSuite struct {
E2ESuite
}
+// FIXME(sink-streaming) python sdk
func (s *MapSuite) TestBatchMapUDFunctionAndSink() {
w := s.Given().Pipeline("@testdata/flatmap-batch.yaml").
When().
@@ -42,8 +43,8 @@ func (s *MapSuite) TestBatchMapUDFunctionAndSink() {
VertexPodLogContains("in", LogSourceVertexStarted).
VertexPodLogContains("go-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("go-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ //VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ //VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("rust-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("rust-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
@@ -54,7 +55,7 @@ func (s *MapSuite) TestBatchMapUDFunctionAndSink() {
w.Expect().
VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
+ //VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
VertexPodLogContains("rust-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
}
@@ -71,8 +72,8 @@ func (s *MapSuite) TestUDFunctionAndSink() {
VertexPodLogContains("in", LogSourceVertexStarted).
VertexPodLogContains("go-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("go-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ //VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ //VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
@@ -81,8 +82,8 @@ func (s *MapSuite) TestUDFunctionAndSink() {
w.Expect().
VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3)).
- VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
+ VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
+ //VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(3))
}
func (s *MapSuite) TestMapStreamUDFunctionAndSink() {
@@ -98,8 +99,8 @@ func (s *MapSuite) TestMapStreamUDFunctionAndSink() {
VertexPodLogContains("in", LogSourceVertexStarted).
VertexPodLogContains("go-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("go-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
- VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ //VertexPodLogContains("python-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
+ //VertexPodLogContains("python-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-split", LogUDFVertexStarted, PodLogCheckOptionWithContainer("numa")).
VertexPodLogContains("java-udsink", SinkVertexStarted, PodLogCheckOptionWithContainer("numa"))
@@ -110,8 +111,8 @@ func (s *MapSuite) TestMapStreamUDFunctionAndSink() {
VertexPodLogContains("go-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
w.Expect().
VertexPodLogContains("go-udsink-2", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
- w.Expect().
- VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
+ //w.Expect().
+ // VertexPodLogContains("python-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
w.Expect().
VertexPodLogContains("java-udsink", "hello", PodLogCheckOptionWithContainer("udsink"), PodLogCheckOptionWithCount(4))
}
diff --git a/test/map-e2e/testdata/flatmap-batch.yaml b/test/map-e2e/testdata/flatmap-batch.yaml
index f4a36213a6..780e1a59da 100644
--- a/test/map-e2e/testdata/flatmap-batch.yaml
+++ b/test/map-e2e/testdata/flatmap-batch.yaml
@@ -25,29 +25,30 @@ spec:
# https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/log
image: quay.io/numaio/numaflow-go/sink-log:stable
imagePullPolicy: Always
- - name: python-split
- scale:
- min: 1
- udf:
- container:
- args:
- - python
- - example.py
- # Split input message into an array with comma, https://github.com/numaproj/numaflow-python/tree/main/examples/batchmap/flatmap
- image: quay.io/numaio/numaflow-python/batch-map-flatmap:stable
- imagePullPolicy: Always
- - name: python-udsink
- scale:
- min: 1
- sink:
- udsink:
- container:
- args:
- - python
- - example.py
- # https://github.com/numaproj/numaflow-python/tree/main/examples/sink/log
- image: quay.io/numaio/numaflow-python/sink-log:stable
- imagePullPolicy: Always
+# FIXME(sink-streaming) python sdk
+# - name: python-split
+# scale:
+# min: 1
+# udf:
+# container:
+# args:
+# - python
+# - example.py
+# # Split input message into an array with comma, https://github.com/numaproj/numaflow-python/tree/main/examples/batchmap/flatmap
+# image: quay.io/numaio/numaflow-python/batch-map-flatmap:stable
+# imagePullPolicy: Always
+# - name: python-udsink
+# scale:
+# min: 1
+# sink:
+# udsink:
+# container:
+# args:
+# - python
+# - example.py
+# # https://github.com/numaproj/numaflow-python/tree/main/examples/sink/log
+# image: quay.io/numaio/numaflow-python/sink-log:stable
+# imagePullPolicy: Always
- name: rust-split
scale:
min: 1
@@ -87,10 +88,10 @@ spec:
to: go-split
- from: go-split
to: go-udsink
- - from: in
- to: python-split
- - from: python-split
- to: python-udsink
+# - from: in
+# to: python-split
+# - from: python-split
+# to: python-udsink
- from: in
to: rust-split
- from: rust-split
diff --git a/test/map-e2e/testdata/flatmap-stream.yaml b/test/map-e2e/testdata/flatmap-stream.yaml
index 503ffcaf3a..0c749d82f7 100644
--- a/test/map-e2e/testdata/flatmap-stream.yaml
+++ b/test/map-e2e/testdata/flatmap-stream.yaml
@@ -36,32 +36,33 @@ spec:
# https://github.com/numaproj/numaflow-go/tree/main/pkg/sinker/examples/log
image: quay.io/numaio/numaflow-go/sink-log:stable
imagePullPolicy: Always
- - name: python-split
- partitions: 3
- limits:
- readBatchSize: 1
- scale:
- min: 1
- udf:
- container:
- args:
- - python
- - example.py
- # Split input message into an array with comma, https://github.com/numaproj/numaflow-python/tree/main/examples/mapstream/flatmap_stream
- image: quay.io/numaio/numaflow-python/map-flatmap-stream:stable
- imagePullPolicy: Always
- - name: python-udsink
- scale:
- min: 1
- sink:
- udsink:
- container:
- args:
- - python
- - example.py
- # https://github.com/numaproj/numaflow-python/tree/main/examples/sink/log
- image: quay.io/numaio/numaflow-python/sink-log:stable
- imagePullPolicy: Always
+# FIXME(sink-streaming) python sdk
+# - name: python-split
+# partitions: 3
+# limits:
+# readBatchSize: 1
+# scale:
+# min: 1
+# udf:
+# container:
+# args:
+# - python
+# - example.py
+# # Split input message into an array with comma, https://github.com/numaproj/numaflow-python/tree/main/examples/mapstream/flatmap_stream
+# image: quay.io/numaio/numaflow-python/map-flatmap-stream:stable
+# imagePullPolicy: Always
+# - name: python-udsink
+# scale:
+# min: 1
+# sink:
+# udsink:
+# container:
+# args:
+# - python
+# - example.py
+# # https://github.com/numaproj/numaflow-python/tree/main/examples/sink/log
+# image: quay.io/numaio/numaflow-python/sink-log:stable
+# imagePullPolicy: Always
- name: java-split
partitions: 3
limits:
@@ -89,10 +90,10 @@ spec:
to: go-udsink
- from: go-split
to: go-udsink-2
- - from: in
- to: python-split
- - from: python-split
- to: python-udsink
+# - from: in
+# to: python-split
+# - from: python-split
+# to: python-udsink
- from: in
to: java-split
- from: java-split
diff --git a/test/map-e2e/testdata/flatmap.yaml b/test/map-e2e/testdata/flatmap.yaml
index da0799f874..5082d54b00 100644
--- a/test/map-e2e/testdata/flatmap.yaml
+++ b/test/map-e2e/testdata/flatmap.yaml
@@ -25,30 +25,30 @@ spec:
image: quay.io/numaio/numaflow-go/sink-log:stable
imagePullPolicy: Always
-# FIXME(fb-sink): python
- - name: python-split
- scale:
- min: 1
- udf:
- container:
- args:
- - python
- - example.py
- # Split input message into an array with comma, https://github.com/numaproj/numaflow-python/tree/main/examples/map/flatmap
- image: quay.io/numaio/numaflow-python/map-flatmap:stable
- imagePullPolicy: Always
- - name: python-udsink
- scale:
- min: 1
- sink:
- udsink:
- container:
- args:
- - python
- - example.py
- # https://github.com/numaproj/numaflow-python/tree/main/examples/sink/log
- image: quay.io/numaio/numaflow-python/sink-log:stable
- imagePullPolicy: Always
+# FIXME(sink-streaming) python sdk
+# - name: python-split
+# scale:
+# min: 1
+# udf:
+# container:
+# args:
+# - python
+# - example.py
+# # Split input message into an array with comma, https://github.com/numaproj/numaflow-python/tree/main/examples/map/flatmap
+# image: quay.io/numaio/numaflow-python/map-flatmap:stable
+# imagePullPolicy: Always
+# - name: python-udsink
+# scale:
+# min: 1
+# sink:
+# udsink:
+# container:
+# args:
+# - python
+# - example.py
+# # https://github.com/numaproj/numaflow-python/tree/main/examples/sink/log
+# image: quay.io/numaio/numaflow-python/sink-log:stable
+# imagePullPolicy: Always
- name: java-split
scale:
min: 1
@@ -71,10 +71,10 @@ spec:
to: go-split
- from: go-split
to: go-udsink
- - from: in
- to: python-split
- - from: python-split
- to: python-udsink
+# - from: in
+# to: python-split
+# - from: python-split
+# to: python-udsink
- from: in
to: java-split
- from: java-split
From 79e4e2c1b43973312b673cc6a27dc368214fa892 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 26 Sep 2024 16:45:08 -0700
Subject: [PATCH 072/188] docs: updated CHANGELOG.md (#2094)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
CHANGELOG.md | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbc4a1a7b7..21035aecaa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
# Changelog
+## v1.3.2 (2024-09-26)
+
+ * [cb7d17d4](https://github.com/numaproj/numaflow/commit/cb7d17d4f3e2ecfcf6a1aa413031f714c135983d) Update manifests to v1.3.2
+ * [816a8e74](https://github.com/numaproj/numaflow/commit/816a8e749c3f071b0c5e4c2ce97025c8138c6cbb) feat: container-type level version compatibility check (#2087)
+ * [9fae141e](https://github.com/numaproj/numaflow/commit/9fae141e686c99e95824b8bdcbec4d4e1bf04241) feat: add pause for monovertex (#2077)
+ * [fc59a3e0](https://github.com/numaproj/numaflow/commit/fc59a3e06f3e86947d9f905e1a728aa155f68bf4) fix: rollback codegen script (#2079)
+ * [82beda64](https://github.com/numaproj/numaflow/commit/82beda6462b4828f914ea70a07d8bed5f1302675) Fix: Use Merge patch rather than json patch for `pause-timestamp` annotation apply (#2078)
+ * [9c2e8f81](https://github.com/numaproj/numaflow/commit/9c2e8f812148d6cc45781762b6328a509225e747) fix: support version compatibility check for pre-release versions (#2069)
+ * [b0e60014](https://github.com/numaproj/numaflow/commit/b0e60014b3e98019fcc20edd668de90e542534a3) feat: allow customization on readyz and livez config (#2068)
+ * [88d2a7a3](https://github.com/numaproj/numaflow/commit/88d2a7a30ae9e1fd63799878f1a0e8b0650615e8) doc: example for PVC (#2067)
+ * [7726cf42](https://github.com/numaproj/numaflow/commit/7726cf4272a2f534202e4ec6ecd81c52e731dbf7) fix: skip updating phase for resource check (#2065)
+ * [782872f5](https://github.com/numaproj/numaflow/commit/782872f55c7fcfb0b1f4747ad71c71f0fc26280c) chore(deps): bump express from 4.19.2 to 4.21.0 in /ui (#2061)
+ * [234e19fc](https://github.com/numaproj/numaflow/commit/234e19fc84d8c4f3c29217ccc4deafb35e9182f1) fix: builtin transformer should keep the keys (#2047)
+ * [f7716aa2](https://github.com/numaproj/numaflow/commit/f7716aa2a6afd9cc3596d307c68125f77cf55f92) feat: rolling update for Pipeline Vertex (#2040)
+ * [db9337a2](https://github.com/numaproj/numaflow/commit/db9337a2cd00e0b19fda1f79793bb9f35eae9436) feat: rolling update for MonoVertex (#2029)
+ * [6f376414](https://github.com/numaproj/numaflow/commit/6f3764140cd86356a197c8d15cd6f7b7afc0a4a0) fix: pause lifecyle changes and add drained status (#2028)
+ * [754bc5e3](https://github.com/numaproj/numaflow/commit/754bc5e3646f52ed0784bdfc9810f6ad77c5ae2d) fix: Fix numaflow-rs binary location in image (#2050)
+
+### Contributors
+
+ * Derek Wang
+ * Julie Vogelman
+ * Keran Yang
+ * Sidhant Kohli
+ * Sreekanth
+ * Vigith Maurice
+ * dependabot[bot]
+
## v1.3.1 (2024-09-02)
* [a42d0063](https://github.com/numaproj/numaflow/commit/a42d0063caf53d6f4c01c2fb2f6f6f6f74a8f987) Update manifests to v1.3.1
From ef96cfff79536eac8339580b11de41719119c8e4 Mon Sep 17 00:00:00 2001
From: Derek Wang