Repository: numaproj/numaflow
Pull request #115 (2022-07-18 17:26:12)

feat: Numaflow autoscaling
This PR is mainly for auto-scaling implementation.

**Mechanism**

We have some basic metrics for a Vertex, and buffer it reads, such as `rate (tps)`, pending messages, available buffer length.

1. 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.
2. 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.

Parameters for tuning:

```yaml
 scale:
  disabled: false
  min: 0
  max: 100
  lookbackSeconds: 180
  cooldownSeconds: 90
  zeroReplicaSleepSeconds: 180
  targetProcessingSeconds: 3
  targetBufferUsage: 50
  replicasPerScale: 2
```
<!--

Before you push your changes:

* Run `make pre-push -B` to fix codegen and lint problems.

Then, you MUST:

* Sign-off your commit (otherwise the DCO check will fail).
* Use [a conventional commit message](https://www.conventionalcommits.org/en/v1.0.0/) (otherwise the commit message check will fail).

If you did not do this, reset all your commit and replace them with a single commit:

```
git reset HEAD~1 ;# change 1 to how many commits you made
git commit --signoff -m 'feat: my feat. Fixes #1234'
```

When creating your PR: 

* Make sure that "Fixes #" or "Closes #" is in both the PR title (for release notes) and description (to automatically link and close the issue).
* Say how you tested your changes. If you changed the UI, attach screenshots.
* Set your PR as a draft initially.
* Your PR needs to pass the required checks before it can be approved. 
* Once required tests have passed, mark your PR "Ready for review".

If changes were requested, once you've made them, you MUST dismiss the review to get it reviewed again.

-->



Patch:
diff --git a/config/base/crds/numaflow.numaproj.io_pipelines.yaml b/config/base/crds/numaflow.numaproj.io_pipelines.yaml
index 500beea4..f2451c40 100644
--- a/config/base/crds/numaflow.numaproj.io_pipelines.yaml
+++ b/config/base/crds/numaflow.numaproj.io_pipelines.yaml
@@ -1434,21 +1434,58 @@ spec:
                         if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/'
                       type: string
                     scale:
+                      description: Settings for autoscaling
                       properties:
+                        cooldownSeconds:
+                          description: Cooldown seconds after a scaling operation
+                            before another one.
+                          format: int32
+                          type: integer
+                        disabled:
+                          description: Whether to disable autoscaling. Set to "true"
+                            when using Kubernetes HPA or any other 3rd party autoscaling
+                            strategies.
+                          type: boolean
                         lookbackSeconds:
-                          default: 180
                           description: Lookback seconds to calculate the average pending
-                            messages and processing rate
+                            messages and processing rate.
                           format: int32
                           type: integer
                         max:
-                          default: 100
-                          description: Maximum replicas
+                          description: Maximum replicas.
                           format: int32
                           type: integer
                         min:
-                          default: 1
-                          description: Minimal replicas
+                          description: Minimum replicas.
+                          format: int32
+                          type: integer
+                        replicasPerScale:
+                          description: ReplicasPerScale defines maximum replicas can
+                            be scaled up or down at once. The is use to prevent too
+                            aggresive scaling operations
+                          format: int32
+                          type: integer
+                        targetBufferUsage:
+                          description: TargetBufferUsage is used to define the target
+                            pencentage of usage of the buffer to be read. 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 as only they
+                            have buffers to read.
+                          format: int32
+                          type: integer
+                        targetProcessingSeconds:
+                          description: 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.
+                          format: int32
+                          type: integer
+                        zeroReplicaSleepSeconds:
+                          description: After scaling down to 0, sleep how many seconds
+                            before scaling up to peek.
                           format: int32
                           type: integer
                       type: object
diff --git a/config/base/crds/numaflow.numaproj.io_vertices.yaml b/config/base/crds/numaflow.numaproj.io_vertices.yaml
index 29aa8a62..c1d0e993 100644
--- a/config/base/crds/numaflow.numaproj.io_vertices.yaml
+++ b/config/base/crds/numaflow.numaproj.io_vertices.yaml
@@ -1310,21 +1310,55 @@ spec:
                 format: int32
                 type: integer
               scale:
+                description: Settings for autoscaling
                 properties:
+                  cooldownSeconds:
+                    description: Cooldown seconds after a scaling operation before
+                      another one.
+                    format: int32
+                    type: integer
+                  disabled:
+                    description: Whether to disable autoscaling. Set to "true" when
+                      using Kubernetes HPA or any other 3rd party autoscaling strategies.
+                    type: boolean
                   lookbackSeconds:
-                    default: 180
                     description: Lookback seconds to calculate the average pending
-                      messages and processing rate
+                      messages and processing rate.
                     format: int32
                     type: integer
                   max:
-                    default: 100
-                    description: Maximum replicas
+                    description: Maximum replicas.
                     format: int32
                     type: integer
                   min:
-                    default: 1
-                    description: Minimal replicas
+                    description: Minimum replicas.
+                    format: int32
+                    type: integer
+                  replicasPerScale:
+                    description: ReplicasPerScale defines maximum replicas can be
+                      scaled up or down at once. The is use to prevent too aggresive
+                      scaling operations
+                    format: int32
+                    type: integer
+                  targetBufferUsage:
+                    description: TargetBufferUsage is used to define the target pencentage
+                      of usage of the buffer to be read. 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 as only they have buffers to read.
+                    format: int32
+                    type: integer
+                  targetProcessingSeconds:
+                    description: 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.
+                    format: int32
+                    type: integer
+                  zeroReplicaSleepSeconds:
+                    description: After scaling down to 0, sleep how many seconds before
+                      scaling up to peek.
                     format: int32
                     type: integer
                 type: object
diff --git a/config/install.yaml b/config/install.yaml
index 9657afe7..4e72aa32 100644
--- a/config/install.yaml
+++ b/config/install.yaml
@@ -6160,21 +6160,58 @@ spec:
                         if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/'
                       type: string
                     scale:
+                      description: Settings for autoscaling
                       properties:
+                        cooldownSeconds:
+                          description: Cooldown seconds after a scaling operation
+                            before another one.
+                          format: int32
+                          type: integer
+                        disabled:
+                          description: Whether to disable autoscaling. Set to "true"
+                            when using Kubernetes HPA or any other 3rd party autoscaling
+                            strategies.
+                          type: boolean
                         lookbackSeconds:
-                          default: 180
                           description: Lookback seconds to calculate the average pending
-                            messages and processing rate
+                            messages and processing rate.
                           format: int32
                           type: integer
                         max:
-                          default: 100
-                          description: Maximum replicas
+                          description: Maximum replicas.
                           format: int32
                           type: integer
                         min:
-                          default: 1
-                          description: Minimal replicas
+                          description: Minimum replicas.
+                          format: int32
+                          type: integer
+                        replicasPerScale:
+                          description: ReplicasPerScale defines maximum replicas can
+                            be scaled up or down at once. The is use to prevent too
+                            aggresive scaling operations
+                          format: int32
+                          type: integer
+                        targetBufferUsage:
+                          description: TargetBufferUsage is used to define the target
+                            pencentage of usage of the buffer to be read. 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 as only they
+                            have buffers to read.
+                          format: int32
+                          type: integer
+                        targetProcessingSeconds:
+                          description: 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.
+                          format: int32
+                          type: integer
+                        zeroReplicaSleepSeconds:
+                          description: After scaling down to 0, sleep how many seconds
+                            before scaling up to peek.
                           format: int32
                           type: integer
                       type: object
@@ -10048,21 +10085,55 @@ spec:
                 format: int32
                 type: integer
               scale:
+                description: Settings for autoscaling
                 properties:
+                  cooldownSeconds:
+                    description: Cooldown seconds after a scaling operation before
+                      another one.
+                    format: int32
+                    type: integer
+                  disabled:
+                    description: Whether to disable autoscaling. Set to "true" when
+                      using Kubernetes HPA or any other 3rd party autoscaling strategies.
+                    type: boolean
                   lookbackSeconds:
-                    default: 180
                     description: Lookback seconds to calculate the average pending
-                      messages and processing rate
+                      messages and processing rate.
                     format: int32
                     type: integer
                   max:
-                    default: 100
-                    description: Maximum replicas
+                    description: Maximum replicas.
                     format: int32
                     type: integer
                   min:
-                    default: 1
-                    description: Minimal replicas
+                    description: Minimum replicas.
+                    format: int32
+                    type: integer
+                  replicasPerScale:
+                    description: ReplicasPerScale defines maximum replicas can be
+                      scaled up or down at once. The is use to prevent too aggresive
+                      scaling operations
+                    format: int32
+                    type: integer
+                  targetBufferUsage:
+                    description: TargetBufferUsage is used to define the target pencentage
+                      of usage of the buffer to be read. 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 as only they have buffers to read.
+                    format: int32
+                    type: integer
+                  targetProcessingSeconds:
+                    description: 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.
+                    format: int32
+                    type: integer
+                  zeroReplicaSleepSeconds:
+                    description: After scaling down to 0, sleep how many seconds before
+                      scaling up to peek.
                     format: int32
                     type: integer
                 type: object
diff --git a/config/namespace-install.yaml b/config/namespace-install.yaml
index 617abcde..dffd1c25 100644
--- a/config/namespace-install.yaml
+++ b/config/namespace-install.yaml
@@ -6160,21 +6160,58 @@ spec:
                         if there is no default. More info: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/'
                       type: string
                     scale:
+                      description: Settings for autoscaling
                       properties:
+                        cooldownSeconds:
+                          description: Cooldown seconds after a scaling operation
+                            before another one.
+                          format: int32
+                          type: integer
+                        disabled:
+                          description: Whether to disable autoscaling. Set to "true"
+                            when using Kubernetes HPA or any other 3rd party autoscaling
+                            strategies.
+                          type: boolean
                         lookbackSeconds:
-                          default: 180
                           description: Lookback seconds to calculate the average pending
-                            messages and processing rate
+                            messages and processing rate.
                           format: int32
                           type: integer
                         max:
-                          default: 100
-                          description: Maximum replicas
+                          description: Maximum replicas.
                           format: int32
                           type: integer
                         min:
-                          default: 1
-                          description: Minimal replicas
+                          description: Minimum replicas.
+                          format: int32
+                          type: integer
+                        replicasPerScale:
+                          description: ReplicasPerScale defines maximum replicas can
+                            be scaled up or down at once. The is use to prevent too
+                            aggresive scaling operations
+                          format: int32
+                          type: integer
+                        targetBufferUsage:
+                          description: TargetBufferUsage is used to define the target
+                            pencentage of usage of the buffer to be read. 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 as only they
+                            have buffers to read.
+                          format: int32
+                          type: integer
+                        targetProcessingSeconds:
+                          description: 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.
+                          format: int32
+                          type: integer
+                        zeroReplicaSleepSeconds:
+                          description: After scaling down to 0, sleep how many seconds
+                            before scaling up to peek.
                           format: int32
                           type: integer
                       type: object
@@ -10048,21 +10085,55 @@ spec:
                 format: int32
                 type: integer
               scale:
+                description: Settings for autoscaling
                 properties:
+                  cooldownSeconds:
+                    description: Cooldown seconds after a scaling operation before
+                      another one.
+                    format: int32
+                    type: integer
+                  disabled:
+                    description: Whether to disable autoscaling. Set to "true" when
+                      using Kubernetes HPA or any other 3rd party autoscaling strategies.
+                    type: boolean
                   lookbackSeconds:
-                    default: 180
                     description: Lookback seconds to calculate the average pending
-                      messages and processing rate
+                      messages and processing rate.
                     format: int32
                     type: integer
                   max:
-                    default: 100
-                    description: Maximum replicas
+                    description: Maximum replicas.
                     format: int32
                     type: integer
                   min:
-                    default: 1
-                    description: Minimal replicas
+                    description: Minimum replicas.
+                    format: int32
+                    type: integer
+                  replicasPerScale:
+                    description: ReplicasPerScale defines maximum replicas can be
+                      scaled up or down at once. The is use to prevent too aggresive
+                      scaling operations
+                    format: int32
+                    type: integer
+                  targetBufferUsage:
+                    description: TargetBufferUsage is used to define the target pencentage
+                      of usage of the buffer to be read. 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 as only they have buffers to read.
+                    format: int32
+                    type: integer
+                  targetProcessingSeconds:
+                    description: 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.
+                    format: int32
+                    type: integer
+                  zeroReplicaSleepSeconds:
+                    description: After scaling down to 0, sleep how many seconds before
+                      scaling up to peek.
                     format: int32
                     type: integer
                 type: object
diff --git a/controllers/cmd/start.go b/controllers/cmd/start.go
index d75a4a0d..2c4d5ace 100644
--- a/controllers/cmd/start.go
+++ b/controllers/cmd/start.go
@@ -24,6 +24,7 @@ import (
 	isbsvcctrl "github.com/numaproj/numaflow/controllers/isbsvc"
 	plctrl "github.com/numaproj/numaflow/controllers/pipeline"
 	vertexctrl "github.com/numaproj/numaflow/controllers/vertex"
+	"github.com/numaproj/numaflow/controllers/vertex/scaling"
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	logging "github.com/numaproj/numaflow/pkg/shared/logging"
 	sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
@@ -35,7 +36,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/healthz"
-	"sigs.k8s.io/controller-runtime/pkg/manager/signals"
 	"sigs.k8s.io/controller-runtime/pkg/predicate"
 	"sigs.k8s.io/controller-runtime/pkg/source"
 )
@@ -156,8 +156,9 @@ func Start(namespaced bool, managedNamespace string) {
 	}
 
 	// Vertex controller
+	autoscaler := scaling.NewScaler(mgr.GetClient(), scaling.WithWorkers(20))
 	vertexController, err := controller.New(dfv1.ControllerVertex, mgr, controller.Options{
-		Reconciler: vertexctrl.NewReconciler(mgr.GetClient(), mgr.GetScheme(), config, image, logger),
+		Reconciler: vertexctrl.NewReconciler(mgr.GetClient(), mgr.GetScheme(), config, image, autoscaler, logger),
 	})
 	if err != nil {
 		logger.Fatalw("Unable to set up Vertex controller", zap.Error(err))
@@ -181,21 +182,11 @@ func Start(namespaced bool, managedNamespace string) {
 		logger.Fatalw("Unable to watch Services", zap.Error(err))
 	}
 
-	// ISB Svc watchdog
-	// watchdog, err := controller.New(dfv1.ControllerWatchdog, mgr, controller.Options{
-	// 	Reconciler: watchdogctrl.NewReconciler(mgr.GetClient(), mgr.GetScheme(), config, logger),
-	// })
-	// if err != nil {
-	// 	logger.Fatalw("Unable to set up Watchdog", zap.Error(err))
-	// }
-
-	// // Watch Pods
-	// if err := watchdog.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{OwnerType: &appv1.StatefulSet{}, IsController: true}); err != nil {
-	// 	logger.Fatalw("Unable to watch pods", zap.Error(err))
-	// }
+	ctx := ctrl.SetupSignalHandler()
+	go autoscaler.Start(logging.WithLogger(ctx, logging.NewLogger().Named("autoscaler")))
 
 	logger.Infow("Starting controller manager", "version", numaflow.GetVersion())
-	if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
+	if err := mgr.Start(ctx); err != nil {
 		logger.Fatalw("Unable to run controller manager", zap.Error(err))
 	}
 }
diff --git a/controllers/pipeline/validate.go b/controllers/pipeline/validate.go
index 5c363925..984acd65 100644
--- a/controllers/pipeline/validate.go
+++ b/controllers/pipeline/validate.go
@@ -2,6 +2,7 @@ package pipeline
 
 import (
 	"fmt"
+
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 )
 
@@ -119,15 +120,15 @@ func ValidatePipeline(pl *dfv1.Pipeline) error {
 }
 
 func validateVertex(v dfv1.AbstractVertex) error {
-	min, max := int32(1), int32(1)
+	min, max := int32(0), int32(dfv1.DefaultMaxReplicas)
 	if v.Scale.Min != nil {
 		min = *v.Scale.Min
 	}
 	if v.Scale.Max != nil {
 		max = *v.Scale.Max
 	}
-	if min < 1 {
-		return fmt.Errorf("vertex %q: min number of replicas should be greater than 0", v.Name) // Do not support scale to 0 now.
+	if min < 0 {
+		return fmt.Errorf("vertex %q: min number of replicas should not be smaller than 0", v.Name)
 	}
 	if min > max {
 		return fmt.Errorf("vertex %q: max number of replicas should be greater than or equal to min", v.Name)
diff --git a/controllers/vertex/controller.go b/controllers/vertex/controller.go
index 7fd53311..c4039abf 100644
--- a/controllers/vertex/controller.go
+++ b/controllers/vertex/controller.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/numaproj/numaflow/controllers"
+	"github.com/numaproj/numaflow/controllers/vertex/scaling"
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 	sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
@@ -32,10 +33,12 @@ type vertexReconciler struct {
 	config *controllers.GlobalConfig
 	image  string
 	logger *zap.SugaredLogger
+
+	scaler *scaling.Scaler
 }
 
-func NewReconciler(client client.Client, scheme *runtime.Scheme, config *controllers.GlobalConfig, image string, logger *zap.SugaredLogger) reconcile.Reconciler {
-	return &vertexReconciler{client: client, scheme: scheme, config: config, image: image, logger: logger}
+func NewReconciler(client client.Client, scheme *runtime.Scheme, config *controllers.GlobalConfig, image string, scaler *scaling.Scaler, logger *zap.SugaredLogger) reconcile.Reconciler {
+	return &vertexReconciler{client: client, scheme: scheme, config: config, image: image, scaler: scaler, logger: logger}
 }
 
 func (r *vertexReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
@@ -68,6 +71,7 @@ func (r *vertexReconciler) reconcile(ctx context.Context, vertex *dfv1.Vertex) (
 	log := logging.FromContext(ctx)
 	if !vertex.DeletionTimestamp.IsZero() {
 		log.Info("Deleting vertex")
+		r.scaler.StopWatching(fmt.Sprintf("%s/%s", vertex.Namespace, vertex.Name))
 		return ctrl.Result{}, nil
 	}
 
@@ -93,6 +97,10 @@ func (r *vertexReconciler) reconcile(ctx context.Context, vertex *dfv1.Vertex) (
 		return ctrl.Result{}, fmt.Errorf("isbsvc not ready")
 	}
 
+	if vertex.Scalable() { // Add to autoscaling watcher
+		r.scaler.StartWatching(fmt.Sprintf("%s/%s", vertex.Namespace, vertex.Name))
+	}
+
 	desiredReplicas := vertex.Spec.GetReplicas()
 	currentReplicas := int(vertex.Status.Replicas)
 	if currentReplicas != desiredReplicas || vertex.Status.Selector == "" {
@@ -294,7 +302,7 @@ func (r *vertexReconciler) buildPodSpec(vertex *dfv1.Vertex, pl *dfv1.Pipeline,
 			}
 			podSpec.Containers[0].Env = envs
 		}
-	} else if vertex.IsAnUDF() {
+	} else if vertex.IsUDF() {
 		// Add default UDF content-type to udf container
 		envs := []corev1.EnvVar{}
 		userDefined := false
diff --git a/controllers/vertex/scaling/doc.go b/controllers/vertex/scaling/doc.go
new file mode 100644
index 00000000..66a77727
--- /dev/null
+++ b/controllers/vertex/scaling/doc.go
@@ -0,0 +1,26 @@
+/*
+
+
+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 Numaflow.
+//
+// A workqueue is implemented in this package to watch vertices in the cluster,
+// calculate the desired replica number for each of them periodically, and
+// patch the vertex spec.
+//
+// Function StartWatching() and StopWatching() are also provided in the package,
+// so that vertices can be added into and removed from the workqueue.
+//
+package scaling
diff --git a/controllers/vertex/scaling/options.go b/controllers/vertex/scaling/options.go
new file mode 100644
index 00000000..205b8e60
--- /dev/null
+++ b/controllers/vertex/scaling/options.go
@@ -0,0 +1,23 @@
+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
+}
+
+type Option func(*options)
+
+func defaultOptions() *options {
+	return &options{
+		workers:      20,
+		taskInterval: 30000,
+	}
+}
+
+func WithWorkers(n int) Option {
+	return func(o *options) {
+		o.workers = n
+	}
+}
diff --git a/controllers/vertex/scaling/scaling.go b/controllers/vertex/scaling/scaling.go
new file mode 100644
index 00000000..9d99fdc8
--- /dev/null
+++ b/controllers/vertex/scaling/scaling.go
@@ -0,0 +1,339 @@
+package scaling
+
+import (
+	"container/list"
+	"context"
+	"encoding/json"
+	"fmt"
+	"math"
+	"strings"
+	"sync"
+	"time"
+
+	"go.uber.org/zap"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/utils/pointer"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+	daemonclient "github.com/numaproj/numaflow/pkg/daemon/client"
+	"github.com/numaproj/numaflow/pkg/isb"
+	"github.com/numaproj/numaflow/pkg/shared/logging"
+)
+
+type Scaler struct {
+	client    client.Client
+	vertexMap map[string]*list.Element
+	// List of the vertex namespaced name, format is "namespace/name"
+	vertexList *list.List
+	lock       *sync.RWMutex
+	options    *options
+}
+
+func NewScaler(client client.Client, opts ...Option) *Scaler {
+	s := &Scaler{
+		client:     client,
+		options:    defaultOptions(),
+		vertexMap:  make(map[string]*list.Element),
+		vertexList: list.New(),
+		lock:       new(sync.RWMutex),
+	}
+	for _, opt := range opts {
+		if opt != nil {
+			opt(s.options)
+		}
+	}
+	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.vertexMap[key]
+	return ok
+}
+
+// Length returns how many vetices are being watched for autoscaling
+func (s *Scaler) Length() int {
+	s.lock.RLock()
+	defer s.lock.RUnlock()
+	return s.vertexList.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.vertexMap[key]; !ok {
+		s.vertexMap[key] = s.vertexList.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.vertexMap[key]; ok {
+		_ = s.vertexList.Remove(e)
+		delete(s.vertexMap, 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 autoscaling worker %v", id)
+	for {
+		select {
+		case <-ctx.Done():
+			log.Infof("Stopped scaling worker %v", id)
+			return
+		case key := <-keyCh:
+			if err := s.scaleOneVertex(ctx, key, id); err != nil {
+				log.Errorw("Failed to scale a vertex", zap.String("vertex", key), zap.Error(err))
+			}
+		}
+	}
+}
+
+// scaleOneVertex implements the detailed logic of scaling up/down a vertex.
+//
+// For source vertices which have both rate and pending message information,
+//    desiredReplicas = currentReplicas * pending / (targetProcessingTime * rate)
+//
+// For UDF and sinks which have the read buffer information
+//    singleReplicaContribution = (totalAvailableBufferLength - pending) / currentReplicas
+//    desiredReplicas = targetAvailableBufferLength / singleReplicaContribution
+func (s *Scaler) scaleOneVertex(ctx context.Context, key string, worker int) error {
+	log := logging.FromContext(ctx).With("worker", fmt.Sprint(worker)).With("vertexKey", 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]
+	vertexFullName := strs[1]
+	vertex := &dfv1.Vertex{}
+	if err := s.client.Get(ctx, client.ObjectKey{Namespace: namespace, Name: vertexFullName}, vertex); err != nil {
+		if apierrors.IsNotFound(err) {
+			s.StopWatching(key)
+			log.Info("No corresponding Vertex found, stopped watching")
+			return nil
+		}
+		return fmt.Errorf("failed to query vertex object of key %q, %w", key, err)
+	}
+	if !vertex.GetDeletionTimestamp().IsZero() {
+		s.StopWatching(key)
+		log.Debug("Vertex being deleted")
+		return nil
+	}
+	if !vertex.Scalable() { // A vertex which is not scalable, such as HTTP source, or autoscaling disabled.
+		s.StopWatching(key) // Remove it in case it's watched.
+		return nil
+	}
+	if time.Since(vertex.Status.LastScaledAt.Time).Seconds() < float64(vertex.Spec.Scale.GetCooldownSeconds()) {
+		log.Debug("Cooldown period, 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) {
+			s.StopWatching(key)
+			log.Info("No corresponding Pipeline found, stopped watching")
+			return nil
+		}
+		return fmt.Errorf("failed to query Pipeline object of key %q, %w", key, err)
+	}
+	if !pl.GetDeletionTimestamp().IsZero() {
+		s.StopWatching(key)
+		log.Debug("Corresponding Pipeline being deleted")
+		return nil
+	}
+	if pl.Spec.Lifecycle.DesiredPhase != dfv1.PipelinePhaseRunning {
+		log.Debug("Corresponding Pipeline not in Running state")
+		return nil
+	}
+	if int(vertex.Status.Replicas) != vertex.Spec.GetReplicas() {
+		log.Debugf("Vertex %s might be under processing, replicas mismatch", vertex.Name)
+		return nil
+	}
+	if vertex.Status.Replicas == 0 { // Was scaled to 0
+		if seconds := time.Since(vertex.Status.LastScaledAt.Time).Seconds(); seconds >= float64(vertex.Spec.Scale.GetZeroReplicaSleepSeconds()) {
+			log.Debugf("Vertex %s has slept %v seconds, scaling up to peek", vertex.Name, seconds)
+			return s.patchVertexReplicas(ctx, vertex, 1)
+		} else {
+			log.Debugf("Vertex %q has slept %v seconds, hasn't reached zeroReplicaSleepSeconds (%v seconds)", vertex.Name, seconds, vertex.Spec.Scale.GetZeroReplicaSleepSeconds())
+			return nil
+		}
+	}
+	// TODO: cache it.
+	dClient, err := daemonclient.NewDaemonServiceClient(pl.GetDaemonServiceURL())
+	if err != nil {
+		return fmt.Errorf("failed to get daemon service client for pipeline %s, %w", pl.Name, err)
+	}
+	vMetrics, err := dClient.GetVertexMetrics(ctx, namespace, pl.Name, vertex.Spec.Name)
+	if err != nil {
+		return fmt.Errorf("failed to get metrics of vertex key %q, %w", key, err)
+	}
+	// Avg rate and pending for autoscaling are both in the map with key "default", see "pkg/metrics/metrics.go".
+	rate, existing := vMetrics.ProcessingRates["default"]
+	if !existing || rate < 0 || rate == isb.RateNotAvailable { // Rate not available
+		log.Debugf("Vertex %s has no rate information, skip scaling", vertex.Name)
+		return nil
+	}
+	pending, existing := vMetrics.Pendings["default"]
+	if !existing || pending < 0 || pending == isb.PendingNotAvailable {
+		// Pending not available, we don't do anything
+		log.Debugf("Vertex %s has no pending messages information, skip scaling", vertex.Name)
+		return nil
+	}
+	totalBufferLength := int64(0)
+	targetAvailableBufferLength := int64(0)
+	if !vertex.IsASource() { // Only non-source vertex has buffer to read
+		bufferName := vertex.GetFromBuffers()[0].Name
+		if bInfo, err := dClient.GetPipelineBuffer(ctx, pl.Name, bufferName); err != nil {
+			return fmt.Errorf("failed to get the read buffer information of vertex %q, %w", vertex.Name, err)
+		} else {
+			if bInfo.BufferLength == nil || bInfo.BufferUsageLimit == nil {
+				return fmt.Errorf("invalid read buffer information of vertex %q, length or usage limit is missing", vertex.Name)
+			}
+			totalBufferLength = int64(float64(*bInfo.BufferLength) * *bInfo.BufferUsageLimit)
+			targetAvailableBufferLength = int64(float64(*bInfo.BufferLength) * float64(vertex.Spec.Scale.GetTargetBufferUsage()) / 100)
+		}
+	}
+
+	desired := s.desiredReplicas(ctx, vertex, rate, pending, totalBufferLength, targetAvailableBufferLength)
+	log.Debugf("Calculated desired replica number of vertex %q is: %v", vertex.Name, desired)
+	current := int32(vertex.Spec.GetReplicas())
+	max := vertex.Spec.Scale.GetMaxReplicas()
+	min := vertex.Spec.Scale.GetMinReplicas()
+	if desired > max {
+		desired = max
+	}
+	if desired < min {
+		desired = min
+	}
+	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 {
+		diff := current - desired
+		if diff > maxAllowed {
+			diff = maxAllowed
+		}
+		return s.patchVertexReplicas(ctx, vertex, current-diff) // We scale down gradually
+	}
+	if desired > current {
+		diff := desired - current
+		if diff > maxAllowed {
+			diff = maxAllowed
+		}
+		return s.patchVertexReplicas(ctx, vertex, current+diff) // We scale up gradually
+	}
+	return nil
+}
+
+func (s *Scaler) desiredReplicas(ctx context.Context, vertex *dfv1.Vertex, rate float64, pending int64, totalBufferLength int64, targetAvailableBufferLength int64) int32 {
+	if rate == 0 && pending == 0 { // This could scale down to 0
+		return 0
+	}
+	if rate == 0 { // Something is wrong, we don't do anything.
+		return int32(vertex.Status.Replicas)
+	}
+	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)))
+		if desired == 0 {
+			desired = 1
+		}
+		return desired
+	} 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.
+		if pending >= totalBufferLength {
+			// Simply return current replica number + max allowed if the pending messages are more than available buffer length
+			return int32(vertex.Status.Replicas) + int32(vertex.Spec.Scale.GetReplicasPerScale())
+		}
+		singleReplicaContribution := float64(totalBufferLength-pending) / float64(vertex.Status.Replicas)
+		desired := int32(math.Round(float64(targetAvailableBufferLength) / singleReplicaContribution))
+		// TODO: Consider back pressure for UDF
+		if desired == 0 {
+			desired = 1
+		}
+		return desired
+	}
+}
+
+// Start function starts the autoscaling worker group.
+// Each worker keeps picking up scaling tasks (which contains vertex keys) to calculate the desired replicas,
+// and patch the vetex spec with the new replica number if needed.
+func (s *Scaler) Start(ctx context.Context) {
+	log := logging.FromContext(ctx)
+	log.Info("Starting autoscaler...")
+	keyCh := make(chan string)
+	ctx, cancel := context.WithCancel(ctx)
+	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.vertexList.Len() == 0 {
+			return
+		}
+		e := s.vertexList.Front()
+		if key, ok := e.Value.(string); ok {
+			s.vertexList.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 scaling job assigner")
+			return
+		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) patchVertexReplicas(ctx context.Context, vertex *dfv1.Vertex, desiredReplicas int32) error {
+	log := logging.FromContext(ctx)
+	origin := vertex.Spec.Replicas
+	vertex.Spec.Replicas = pointer.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) {
+		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("pipeline", vertex.Spec.PipelineName), zap.String("vertex", vertex.Spec.Name))
+	return nil
+}
diff --git a/controllers/watchdog/controller.go b/controllers/watchdog/controller.go
deleted file mode 100644
index b39cebd8..00000000
--- a/controllers/watchdog/controller.go
+++ /dev/null
@@ -1,240 +0,0 @@
-package watchdog
-
-import (
-	"context"
-	"fmt"
-	"strings"
-	"time"
-
-	"github.com/nats-io/jsm.go"
-	"github.com/nats-io/nats.go"
-	"go.uber.org/zap"
-	appv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/labels"
-	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/apimachinery/pkg/util/wait"
-	ctrl "sigs.k8s.io/controller-runtime"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-
-	"github.com/numaproj/numaflow/controllers"
-	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
-	"github.com/numaproj/numaflow/pkg/shared/logging"
-	sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
-)
-
-// watchdogReconciler is used to watch isb service pod changes.
-type watchdogReconciler struct {
-	client client.Client
-	scheme *runtime.Scheme
-
-	config *controllers.GlobalConfig
-	logger *zap.SugaredLogger
-}
-
-func NewReconciler(client client.Client, scheme *runtime.Scheme, config *controllers.GlobalConfig, logger *zap.SugaredLogger) reconcile.Reconciler {
-	return &watchdogReconciler{client: client, scheme: scheme, config: config, logger: logger}
-}
-
-func (r *watchdogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
-	sts := &appv1.StatefulSet{}
-	if err := r.client.Get(ctx, req.NamespacedName, sts); err != nil {
-		if apierrors.IsNotFound(err) {
-			return reconcile.Result{}, nil
-		}
-		r.logger.Errorw("Unable to get statefulset", zap.Any("request", req), zap.Error(err))
-		return ctrl.Result{}, err
-	}
-	isbSvcName, ok := sts.GetLabels()[dfv1.KeyISBSvcName]
-	if !ok {
-		// Not ISB Service StatefulSet
-		return reconcile.Result{}, nil
-	}
-	isbSvc := &dfv1.InterStepBufferService{}
-	if err := r.client.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: isbSvcName}, isbSvc); err != nil {
-		if apierrors.IsNotFound(err) {
-			return reconcile.Result{}, nil
-		}
-		r.logger.Errorw("Unable to get InterStepBufferService", zap.String("isbSvc", isbSvcName), zap.Error(err))
-		return ctrl.Result{}, err
-	}
-	if !isbSvc.DeletionTimestamp.IsZero() {
-		// Ignore ISB Svc deletion
-		return reconcile.Result{}, nil
-	}
-	log := r.logger.With("namespace", isbSvc.Namespace).With("isbSvc", isbSvc.Name)
-	ctx = logging.WithLogger(ctx, log)
-	if sts.GetCreationTimestamp().After(time.Now().Add(-5 * time.Minute)) {
-		// Ignore new created StatefulSet Pod changes,
-		// Use 5 minutes as a judgement rule
-		log.Info("Ignore new created StatefulSet pod changes")
-		return reconcile.Result{}, nil
-	}
-
-	// Find impacted pipeline names
-	pipelineNames := []string{}
-	vertexList := &dfv1.VertexList{}
-	if err := r.client.List(ctx, vertexList, &client.ListOptions{Namespace: isbSvc.Namespace}); err != nil {
-		log.Errorw("Failed to find vertex list, %w", err)
-		return ctrl.Result{}, err
-	}
-	for _, v := range vertexList.Items {
-		vertexISBName := dfv1.DefaultISBSvcName
-		if v.Spec.InterStepBufferServiceName != "" {
-			vertexISBName = v.Spec.InterStepBufferServiceName
-		}
-		if isbSvcName != vertexISBName {
-			continue
-		}
-		if !sharedutil.StringSliceContains(pipelineNames, v.Spec.PipelineName) {
-			pipelineNames = append(pipelineNames, v.Spec.PipelineName)
-		}
-	}
-	if len(pipelineNames) == 0 {
-		log.Infow("Not a pipeline impacted by ISB service pods change", zap.String("isbSvc", isbSvcName))
-		return ctrl.Result{}, nil
-	}
-	// Find all the source vertices of impacted pipelines
-	// Do not look for all the vertices with ISB service name because a pipeline may use different ISB service for its vertices in the future
-	selector, _ := labels.Parse(dfv1.KeyPipelineName + " in (" + strings.Join(pipelineNames, ",") + ")")
-	vList := &dfv1.VertexList{}
-	if err := r.client.List(ctx, vList, &client.ListOptions{Namespace: isbSvc.Namespace, LabelSelector: selector}); err != nil {
-		log.Errorw("Failed to find vertex list with pipeline names, %w", err)
-		return ctrl.Result{}, err
-	}
-	for _, v := range vList.Items {
-		if !v.IsASource() { // Only scale source vertices
-			continue
-		}
-		// TODO: Scale down source Vertex to 0 or 1.
-	}
-
-	lostHosts, err := r.getLostHosts(ctx, req.Namespace, isbSvcName, sts.Name, int(*sts.Spec.Replicas))
-	if err != nil {
-		log.Errorw("Failed to get lost pods, %w", err)
-		return ctrl.Result{}, err
-	}
-	if len(lostHosts) > 2 {
-		log.Warnf("Lost too many hosts: %d", len(lostHosts))
-	}
-
-	if isbSvc.Spec.Redis != nil { // Do nothing with Redis
-		return ctrl.Result{}, nil
-	}
-
-	jsConfig := isbSvc.Status.Config.JetStream
-	if jsConfig == nil {
-		log.Info("Not JetStream ISB Svc")
-		return ctrl.Result{}, nil
-	}
-	if isbSvc.Spec.JetStream.GetReplicas() <= 3 {
-		log.Info("Can not massage a JetStream InterStepBufferService with replicas <= 3")
-		return ctrl.Result{}, nil
-	}
-
-	userSecret := &corev1.Secret{}
-	if err := r.client.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: jsConfig.Auth.User.Name}, userSecret); err != nil {
-		return ctrl.Result{}, err
-	}
-	user := userSecret.StringData[jsConfig.Auth.User.Key]
-	passwordSecret := &corev1.Secret{}
-	if err := r.client.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: jsConfig.Auth.Password.Name}, passwordSecret); err != nil {
-		return ctrl.Result{}, err
-	}
-	password := passwordSecret.StringData[jsConfig.Auth.Password.Key]
-	go func(natsUrl, user, password string, lostHosts []string) {
-		if err := wait.ExponentialBackoffWithContext(ctx, sharedutil.DefaultRetryBackoff, func() (bool, error) {
-			err := massageJetStreamIsbSvc(ctx, natsUrl, user, password, lostHosts)
-			if err != nil {
-				log.Errorw("Failed to massage JetStream ISB Svc", zap.Error(err))
-				return false, err
-			}
-			return true, nil
-		}); err != nil {
-			log.Errorw("Failed to massage JetStream ISB Svc after retrying.", zap.Error(err))
-		}
-	}(jsConfig.URL, user, password, lostHosts)
-	return ctrl.Result{}, nil
-}
-
-func (r *watchdogReconciler) getLostHosts(ctx context.Context, namespace, isbSvcName, stsName string, replicas int) ([]string, error) {
-	podList := &corev1.PodList{}
-	selector, _ := labels.Parse(dfv1.KeyISBSvcName + "=" + isbSvcName + "," + dfv1.KeyManagedBy + "=" + dfv1.ControllerISBSvc)
-	if err := r.client.List(ctx, podList, &client.ListOptions{Namespace: namespace, LabelSelector: selector}); err != nil {
-		return nil, err
-	}
-	lost, good := []string{}, []string{}
-	for _, p := range podList.Items {
-		if !p.DeletionTimestamp.IsZero() { // Deleting
-			lost = append(lost, p.Name)
-			continue
-		}
-		if p.Status.Phase != corev1.PodRunning {
-			lost = append(lost, p.Name)
-			continue
-		}
-		allReady := true
-		for _, s := range p.Status.ContainerStatuses {
-			if !s.Ready {
-				allReady = false
-				break
-			}
-		}
-		if !allReady {
-			lost = append(lost, p.Name)
-			continue
-		}
-		good = append(good, p.Name)
-	}
-	for i := 0; i < replicas; i++ {
-		p := fmt.Sprintf("%s-%v", stsName, i)
-		// Not found in the pod list, which means it's been deleted before creating a new pod
-		if !sharedutil.StringSliceContains(lost, p) && !sharedutil.StringSliceContains(good, p) {
-			lost = append(lost, p)
-		}
-	}
-	return lost, nil
-}
-
-// massageJetStreamIsbSvc is used to do some management on the JetStream cluster.
-// Doing it directly in the controller instead of creating a k8s job for high efficiency
-func massageJetStreamIsbSvc(ctx context.Context, natsUrl, user, password string, lostHosts []string) error {
-	log := logging.FromContext(ctx)
-	opts := []nats.Option{
-		nats.UserInfo(user, password),
-	}
-
-	nc, err := nats.Connect(natsUrl, opts...)
-	if err != nil {
-		return fmt.Errorf("failed to connect to nats url=%s: %w", natsUrl, err)
-	}
-	defer nc.Close()
-	js, err := nc.JetStream()
-	if err != nil {
-		return fmt.Errorf("failed to get a js context from nats connection, %w", err)
-	}
-	mgr, err := jsm.New(nc, jsm.WithTimeout(2*time.Second))
-	if err != nil {
-		return fmt.Errorf("failed to get a jsm manager, %w", err)
-	}
-	for s := range js.StreamsInfo() {
-		stream, err := mgr.LoadStream(s.Config.Name)
-		if err != nil {
-			return fmt.Errorf("failed to load stream %q, %w", s.Config.Name, err)
-		}
-		// Only peer-remove replicas. When leader is down.
-		// In case it's picked as a replica, the pod creation triggered reconciliation will peer-remove it again.
-		for _, r := range s.Cluster.Replicas {
-			if sharedutil.StringSliceContains(lostHosts, r.Name) {
-				if err := stream.RemoveRAFTPeer(r.Name); err != nil {
-					return fmt.Errorf("failed to peer-remove %s, %w", r.Name, err)
-				}
-				log.Infof("Sent peer-remove request to remove %q from stream %q", r.Name, s.Config.Name)
-			}
-		}
-	}
-	return nil
-}
diff --git a/docs/APIs.md b/docs/APIs.md
index 11760779..08c4124c 100644
--- a/docs/APIs.md
+++ b/docs/APIs.md
@@ -239,6 +239,9 @@ vertices of a pipleine, will override pipeline level settings
 </td>
 <td>
 <em>(Optional)</em>
+<p>
+Settings for autoscaling
+</p>
 </td>
 </tr>
 </tbody>
@@ -2962,12 +2965,24 @@ Description
 <tbody>
 <tr>
 <td>
+<code>disabled</code></br> <em> bool </em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>
+Whether to disable autoscaling. Set to “true” when using Kubernetes HPA
+or any other 3rd party autoscaling strategies.
+</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>min</code></br> <em> int32 </em>
 </td>
 <td>
 <em>(Optional)</em>
 <p>
-Minimal replicas
+Minimum replicas.
 </p>
 </td>
 </tr>
@@ -2978,19 +2993,84 @@ Minimal replicas
 <td>
 <em>(Optional)</em>
 <p>
-Maximum replicas
+Maximum replicas.
 </p>
 </td>
 </tr>
 <tr>
 <td>
-<code>lookbackSeconds</code></br> <em> int32 </em>
+<code>lookbackSeconds</code></br> <em> uint32 </em>
 </td>
 <td>
 <em>(Optional)</em>
 <p>
 Lookback seconds to calculate the average pending messages and
-processing rate
+processing rate.
+</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>cooldownSeconds</code></br> <em> uint32 </em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>
+Cooldown seconds after a scaling operation before another one.
+</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>zeroReplicaSleepSeconds</code></br> <em> uint32 </em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>
+After scaling down to 0, sleep how many seconds before scaling up to
+peek.
+</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>targetProcessingSeconds</code></br> <em> uint32 </em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>
+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.
+</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>targetBufferUsage</code></br> <em> uint32 </em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>
+TargetBufferUsage is used to define the target pencentage of usage of
+the buffer to be read. 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 as only they have
+buffers to read.
+</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>replicasPerScale</code></br> <em> uint32 </em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>
+ReplicasPerScale defines maximum replicas can be scaled up or down at
+once. The is use to prevent too aggresive scaling operations
 </p>
 </td>
 </tr>
diff --git a/docs/autoscaling.md b/docs/autoscaling.md
index e8e226aa..61be6f7c 100644
--- a/docs/autoscaling.md
+++ b/docs/autoscaling.md
@@ -1,4 +1,4 @@
-# Auto Scaling
+# Autoscaling
 
 Numaflow is able to run with both `Horizontal Pod Autoscaling` and `Vertical Pod Autoscaling`.
 
@@ -12,13 +12,58 @@ Numaflow is able to run with both `Horizontal Pod Autoscaling` and `Vertical Pod
 
 ### Numaflow Autoscaling
 
-Numaflow provides autoscaling capability out of the box.
+Numaflow provides `0 - N` autoscaling capability out of the box, it's available for all the `UDF`, `Sink` and following `Source` vertices.
 
-More detail is coming soon.
+- Kafka
+
+Numaflow autoscaling is enabled by default, there are some parameters can be tuned to achieve better results.
+
+```yaml
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: Pipeline
+metadata:
+  name: my-pipeline
+spec:
+  vertices:
+    - name: my-vertex
+      scale:
+        disabled: false # Optional, defaults to false.
+        min: 0 # Optional, minimum replicas, defaults to 0.
+        max: 20 # Optional, maximum replicas, defaults to 50.
+        lookbackSeconds: 180 # Optional, defaults to 180.
+        cooldownSeconds: 90 # Optional, defaults to 90.
+        zeroReplicaSleepSeconds: 180 # Optional, defaults to 180.
+        targetProcessingSeconds: 3 # Optional, defaults to 0.
+        targetBufferUsage: 50 # Optional, defaults to 50.
+        replicasPerScale: 2 # Optional, defaults to 2.
+```
+
+- `disabled` - Whether to disable Numaflow autoscaling, defaults to `false`.
+- `min` - Minimum replicas, valid value could be an interger >= 0. Defaults to `0`, which means it could be scaled down to 0.
+- `max` - Maximum replicas, positive interger which should not be less than `min`, defaults to `50`. if `max` and `min` are the same, that will be the fixed replica number.
+- `lookbackSeconds` - How many seconds to lookback for vertex average processing rate (tps) and pending messages calculation, defaults to `180`. Rate and pending messages metrics are critical for autoscaling, you might need to tune this parameter a bit to see better results. For example, your data source only have 1 minute data input in every 5 minutes, and you don't want the vertices to be scaled down to `0`. In this case, you need to increase `lookbackSeconds` to cover all the 5 minutes, so that the calculated average rate and pending messages won't be `0` during the silent period, to prevent scaling down to 0 from happening.
+- `zeroReplicaSleepSeconds` - How many seconds it will wait after scaling down to `0`, defaults to `180`. Numaflow autoscaler periodically scales up a vertex pod to "peek" the incoming data, this is the period of time to wait before peeking.
+- `targetProcessingSeconds` - It 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. It defaults to `3`. This is only effective for the `Source` vertices which support autoscaling, typically increasing the value, which leads to lower processing rate, thus less replicas.
+- `targetBufferUsage` - Targeted buffer usage percentage, defaults to `50`. It is only effective for `UDF` and `Sink` vertices, it also determines how aggressive you want to do for autoscaling, increasing the value will bring more replicas.
+- `replicasPerScale` - How many maximum replicas will be scaled up or down at once, defaults to `2`.
+
+To disable Numaflow autoscaling, set `disabled: true` as following.
+
+```yaml
+apiVersion: numaflow.numaproj.io/v1alpha1
+kind: Pipeline
+metadata:
+  name: my-pipeline
+spec:
+  vertices:
+    - name: my-vertex
+      scale:
+        disabled: true
+```
 
 ### Kubernetes HPA
 
-[Kubernetes HPA](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) is supported in Numaflow for any type of Vertex. To use HPA, remember to point the `scaleTargetRef` to the vertex as below.
+[Kubernetes HPA](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) is supported in Numaflow for any type of Vertex. To use HPA, remember to point the `scaleTargetRef` to the vertex as below, and disable Numaflow autoscaling in your Pipeline spec.
 
 ```yaml
 apiVersion: autoscaling/v2beta1
@@ -45,9 +90,9 @@ Kubernetes HPA autoscaling is usful for those Source vertice not able to count p
 
 ### Third Party Autoscaling
 
-Third party autoscaling tools like [KEDA](https://keda.sh/) are also supported in Numaflow, which can be used to auto scale any type of vertex with the scalers it supports.
+Third party autoscaling tools like [KEDA](https://keda.sh/) are also supported in Numaflow, which can be used to autoscale any type of vertex with the scalers it supports.
 
-To use KEDA for vertex auto scaling, same as Kubernetes HPA, point the `scaleTargetRef` to your vertex.
+To use KEDA for vertex autoscaling, same as Kubernetes HPA, point the `scaleTargetRef` to your vertex, and disable Numaflow autoscaling in your Pipeline spec.
 
 ```yaml
 apiVersion: keda.sh/v1alpha1
diff --git a/examples/5-auto-scaling.yaml b/examples/5-auto-scaling.yaml
index 7704fc63..61cbb399 100644
--- a/examples/5-auto-scaling.yaml
+++ b/examples/5-auto-scaling.yaml
@@ -1,7 +1,7 @@
 apiVersion: numaflow.numaproj.io/v1alpha1
 kind: Pipeline
 metadata:
-  name: auto-scaling-pipeline
+  name: autoscaling-pipeline
 spec:
   vertices:
     - name: in
@@ -12,14 +12,14 @@ spec:
           duration: 1s
     - name: cat
       scale:
-        min: 2 # Minimal replicas
+        min: 2 # Minimum replicas
         max: 4 # Maximum replicas
       udf:
         builtin:
           name: cat # A builtin UDF which simply cats the message
     - name: out
       scale:
-        min: 3 # Minimal replicas
+        min: 3 # Minimum replicas
         max: 5 # Maximum replicas
       sink:
         # A simple log printing sink
diff --git a/go.mod b/go.mod
index e3fedd7e..43b0d36d 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,6 @@ require (
 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
 	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe
-	github.com/nats-io/jsm.go v0.0.31
 	github.com/nats-io/nats.go v1.16.0
 	github.com/numaproj/numaflow-go v0.0.1
 	github.com/prometheus/client_golang v1.12.1
@@ -61,7 +60,6 @@ require (
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
-	github.com/dustin/go-humanize v1.0.0 // indirect
 	github.com/eapache/go-resiliency v1.2.0 // indirect
 	github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
 	github.com/eapache/queue v1.1.0 // indirect
@@ -127,6 +125,7 @@ require (
 	github.com/moby/spdystream v0.2.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/nats-io/nats-server/v2 v2.7.5-0.20220415000625-a6b62f61a703 // indirect
 	github.com/nats-io/nkeys v0.3.0 // indirect
 	github.com/nats-io/nuid v1.0.1 // indirect
 	github.com/oklog/ulid v1.3.1 // indirect
@@ -161,7 +160,7 @@ require (
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	go.mongodb.org/mongo-driver v1.7.3 // indirect
 	go.uber.org/multierr v1.7.0 // indirect
-	golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
+	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
 	golang.org/x/mod v0.5.1 // indirect
 	golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
 	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
diff --git a/go.sum b/go.sum
index 1c9a04e3..9c16c6da 100644
--- a/go.sum
+++ b/go.sum
@@ -158,7 +158,6 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
 github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
 github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -586,7 +585,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A=
 github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
@@ -643,7 +641,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 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/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
 github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
@@ -680,14 +677,9 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nats-io/jsm.go v0.0.31 h1:ARmf+Kic6G8E5imOkfIhwEuBlEbxlW9nb2PRWx0XDUU=
-github.com/nats-io/jsm.go v0.0.31/go.mod h1:LVb1bL1houzsI3nySNW/Omhg+d2kKnMHrgpNGcRMp8M=
 github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I=
-github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
 github.com/nats-io/nats-server/v2 v2.7.5-0.20220415000625-a6b62f61a703 h1:d8siT+8VQ68hfqPqYZvpMrHIihlMVW3gGy+o2hRDCyg=
 github.com/nats-io/nats-server/v2 v2.7.5-0.20220415000625-a6b62f61a703/go.mod h1:5vic7C58BFEVltiZhs7Kq81q2WcEPhJPsmNv1FOrdv0=
-github.com/nats-io/nats.go v1.14.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
-github.com/nats-io/nats.go v1.14.1-0.20220412004736-c75dfd54b52c/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g=
 github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
@@ -972,9 +964,9 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 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=
@@ -1109,7 +1101,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1185,7 +1176,6 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/pkg/apis/numaflow/v1alpha1/const.go b/pkg/apis/numaflow/v1alpha1/const.go
index 54f09710..b3bee51b 100644
--- a/pkg/apis/numaflow/v1alpha1/const.go
+++ b/pkg/apis/numaflow/v1alpha1/const.go
@@ -102,12 +102,14 @@ const (
 
 	UDFApplierMessageKey = "x-numa-message-key" // The key in the UDF applier HTTP header used to pass the map-reduce key
 
-	// processing rate and pending prometheus metrics and labels
-	MetricPipelineLabel   = "pipeline"
-	MetricVertexLabel     = "vertex"
-	MetricPeriodLabel     = "period"
-	VertexProcessingRate  = "vertex_processing_rate"
-	VertexPendingMessages = "vertex_pending_messages"
+	// Auto scaling
+	DefaultLookbackSeconds         = 180 // Default lookback seconds for calculating avg rate and pending
+	DefaultCooldownSeconds         = 90  // Default cooldown seconds after a scaling operation
+	DefaultZeroReplicaSleepSeconds = 180 // Default sleep time in seconds after scaling down to 0, before peeking
+	DefaultMaxReplicas             = 50  // Default max replicas
+	DefaultTargetProcessingSeconds = 3   // Default targeted time in seconds to finish processing all the pending messages for a source
+	DefaultTargetBufferUsage       = 50  // Default targeted percentage of balanced buffer usage
+	DefaultReplicasPerScale        = 2   // Default maximum replicas to be scaled up or down at once
 )
 
 type ContentType string
diff --git a/pkg/apis/numaflow/v1alpha1/generated.pb.go b/pkg/apis/numaflow/v1alpha1/generated.pb.go
index c6cf25ad..204e3246 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.pb.go
+++ b/pkg/apis/numaflow/v1alpha1/generated.pb.go
@@ -1632,291 +1632,299 @@ func init() {
 }
 
 var fileDescriptor_9d0d1b17d3865563 = []byte{
-	// 4536 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5c, 0x5d, 0x6c, 0x1c, 0x49,
-	0x5e, 0x4f, 0xcf, 0x87, 0x3d, 0xf3, 0x1f, 0x3b, 0xb6, 0x2b, 0xd9, 0x30, 0x31, 0x59, 0x3b, 0xf4,
-	0x6a, 0x57, 0x01, 0xee, 0xc6, 0xac, 0xd9, 0xe3, 0x72, 0x70, 0x7b, 0xbb, 0x1e, 0x3b, 0x76, 0x9c,
-	0x8c, 0xb3, 0xa6, 0xc6, 0x4e, 0xd8, 0xdb, 0x13, 0xa1, 0xdc, 0x53, 0x33, 0xee, 0x9d, 0x9e, 0xee,
-	0xb9, 0xee, 0x6a, 0x27, 0x5e, 0xdd, 0x09, 0x24, 0x1e, 0x16, 0x04, 0xe8, 0x4e, 0xe2, 0x05, 0xe9,
-	0x04, 0xe2, 0xe1, 0x24, 0x04, 0xd2, 0xbd, 0x20, 0x90, 0x10, 0xe8, 0x24, 0x9e, 0xd0, 0xf2, 0x82,
-	0xf6, 0x01, 0x89, 0x45, 0x3a, 0x59, 0xb7, 0x46, 0xe2, 0x0d, 0x71, 0x68, 0xdf, 0x22, 0x24, 0x50,
-	0x7d, 0xf4, 0xe7, 0xf4, 0x38, 0xf6, 0x8c, 0xbd, 0x3c, 0x90, 0xb7, 0xe9, 0xfa, 0xff, 0xeb, 0xf7,
-	0xaf, 0xcf, 0xff, 0x57, 0x55, 0x0d, 0x6c, 0x74, 0x4c, 0xb6, 0xef, 0xef, 0xd5, 0x0c, 0xa7, 0xb7,
-	0x64, 0xfb, 0x3d, 0xd2, 0x77, 0x9d, 0xf7, 0xc5, 0x8f, 0xb6, 0xe5, 0x3c, 0x59, 0xea, 0x77, 0x3b,
-	0x4b, 0xa4, 0x6f, 0x7a, 0x51, 0xc9, 0xc1, 0xeb, 0xc4, 0xea, 0xef, 0x93, 0xd7, 0x97, 0x3a, 0xd4,
-	0xa6, 0x2e, 0x61, 0xb4, 0x55, 0xeb, 0xbb, 0x0e, 0x73, 0xd0, 0x97, 0x23, 0xa0, 0x5a, 0x00, 0x54,
-	0x0b, 0xaa, 0xd5, 0xfa, 0xdd, 0x4e, 0x8d, 0x03, 0x45, 0x25, 0x01, 0xd0, 0xfc, 0x17, 0x63, 0x2d,
-	0xe8, 0x38, 0x1d, 0x67, 0x49, 0xe0, 0xed, 0xf9, 0x6d, 0xf1, 0x25, 0x3e, 0xc4, 0x2f, 0x29, 0x67,
-	0x5e, 0xef, 0xde, 0xf6, 0x6a, 0xa6, 0xc3, 0x9b, 0xb5, 0x64, 0x38, 0x2e, 0x5d, 0x3a, 0x18, 0x68,
-	0xcb, 0xfc, 0x1b, 0x11, 0x4f, 0x8f, 0x18, 0xfb, 0xa6, 0x4d, 0xdd, 0xc3, 0xa0, 0x2f, 0x4b, 0x2e,
-	0xf5, 0x1c, 0xdf, 0x35, 0xe8, 0x99, 0x6a, 0x79, 0x4b, 0x3d, 0xca, 0x48, 0x96, 0xac, 0xa5, 0x61,
-	0xb5, 0x5c, 0xdf, 0x66, 0x66, 0x6f, 0x50, 0xcc, 0x2f, 0x3d, 0xaf, 0x82, 0x67, 0xec, 0xd3, 0x1e,
-	0x49, 0xd7, 0xd3, 0xff, 0x73, 0x0a, 0x2e, 0xaf, 0xec, 0x79, 0xcc, 0x25, 0x06, 0x7b, 0x48, 0x5d,
-	0x46, 0x9f, 0xa2, 0x9b, 0x50, 0xb0, 0x49, 0x8f, 0x56, 0xb5, 0x9b, 0xda, 0xad, 0x72, 0x7d, 0xea,
-	0xa3, 0xa3, 0xc5, 0x4b, 0xc7, 0x47, 0x8b, 0x85, 0x07, 0xa4, 0x47, 0xb1, 0xa0, 0x20, 0x03, 0x26,
-	0x64, 0x6f, 0xab, 0xf9, 0x9b, 0xda, 0xad, 0xca, 0xf2, 0x5b, 0xb5, 0x11, 0xa7, 0xa9, 0xd6, 0x14,
-	0x30, 0x75, 0x38, 0x3e, 0x5a, 0x9c, 0x90, 0xbf, 0xb1, 0x82, 0x46, 0xef, 0x41, 0xc1, 0x33, 0xed,
-	0x6e, 0xb5, 0x20, 0x44, 0xbc, 0x39, 0xba, 0x08, 0xd3, 0xee, 0xd6, 0x4b, 0xbc, 0x07, 0xfc, 0x17,
-	0x16, 0xa0, 0xe8, 0x3b, 0x1a, 0xcc, 0x19, 0x8e, 0xcd, 0x08, 0x1f, 0xa8, 0x1d, 0xda, 0xeb, 0x5b,
-	0x84, 0xd1, 0x6a, 0x51, 0x88, 0xba, 0x37, 0xb2, 0xa8, 0xd5, 0x34, 0x62, 0xfd, 0xa5, 0xe3, 0xa3,
-	0xc5, 0xb9, 0x81, 0x62, 0x3c, 0x28, 0x1b, 0x3d, 0x82, 0xbc, 0xdf, 0x6a, 0x57, 0x27, 0x44, 0x13,
-	0xbe, 0x3a, 0x72, 0x13, 0x76, 0xd7, 0xd6, 0xeb, 0x93, 0xc7, 0x47, 0x8b, 0xf9, 0xdd, 0xb5, 0x75,
-	0xcc, 0x11, 0x51, 0x17, 0x4a, 0x7c, 0x95, 0xb5, 0x08, 0x23, 0xd5, 0x49, 0x81, 0xbe, 0x32, 0x32,
-	0xfa, 0x96, 0x02, 0xaa, 0x4f, 0x1d, 0x1f, 0x2d, 0x96, 0x82, 0x2f, 0x1c, 0x0a, 0x40, 0x7f, 0xa8,
-	0xc1, 0x94, 0xed, 0xb4, 0x68, 0x93, 0x5a, 0xd4, 0x60, 0x8e, 0x5b, 0x2d, 0xdd, 0xcc, 0xdf, 0xaa,
-	0x2c, 0xbf, 0x3b, 0xb2, 0xc4, 0xe4, 0xda, 0xac, 0x3d, 0x88, 0x61, 0xdf, 0xb1, 0x99, 0x7b, 0x58,
-	0xbf, 0xaa, 0xd6, 0xe7, 0x54, 0x9c, 0x84, 0x13, 0x8d, 0x40, 0xbb, 0x50, 0x61, 0x8e, 0xc5, 0xd7,
-	0xbd, 0xe9, 0xd8, 0x5e, 0xb5, 0x2c, 0xda, 0xb4, 0x50, 0x93, 0x5b, 0x86, 0x4b, 0xae, 0xf1, 0x3d,
-	0x5f, 0x3b, 0x78, 0xbd, 0xb6, 0x13, 0xb2, 0xd5, 0xaf, 0x28, 0xe0, 0x4a, 0x54, 0xe6, 0xe1, 0x38,
-	0x0e, 0xa2, 0x30, 0xe3, 0x51, 0xc3, 0x77, 0x4d, 0x76, 0xc8, 0xa7, 0x98, 0x3e, 0x65, 0x55, 0x10,
-	0x03, 0xfc, 0x5a, 0x16, 0xf4, 0xb6, 0xd3, 0x6a, 0x26, 0xb9, 0xeb, 0x57, 0x8e, 0x8f, 0x16, 0x67,
-	0x52, 0x85, 0x38, 0x8d, 0x89, 0x6c, 0x98, 0x35, 0x7b, 0xa4, 0x43, 0xb7, 0x7d, 0xcb, 0x6a, 0x52,
-	0xc3, 0xa5, 0xcc, 0xab, 0x56, 0x44, 0x17, 0x6e, 0x65, 0xc9, 0x69, 0x38, 0x06, 0xb1, 0xde, 0xd9,
-	0x7b, 0x9f, 0x1a, 0x0c, 0xd3, 0x36, 0x75, 0xa9, 0x6d, 0xd0, 0x7a, 0x55, 0x75, 0x66, 0x76, 0x33,
-	0x85, 0x84, 0x07, 0xb0, 0xd1, 0x06, 0xcc, 0xf5, 0x5d, 0xd3, 0x11, 0x4d, 0xb0, 0x88, 0xe7, 0xf1,
-	0x8d, 0x5f, 0x9d, 0x12, 0xca, 0xe0, 0xba, 0x82, 0x99, 0xdb, 0x4e, 0x33, 0xe0, 0xc1, 0x3a, 0xe8,
-	0x16, 0x94, 0x82, 0xc2, 0xea, 0xf4, 0x4d, 0xed, 0x56, 0x51, 0x2e, 0x9b, 0xa0, 0x2e, 0x0e, 0xa9,
-	0x68, 0x1d, 0x4a, 0xa4, 0xdd, 0x36, 0x6d, 0xce, 0x79, 0x59, 0x0c, 0xe1, 0x8d, 0xac, 0xae, 0xad,
-	0x28, 0x1e, 0x89, 0x13, 0x7c, 0xe1, 0xb0, 0x2e, 0xba, 0x07, 0xc8, 0xa3, 0xee, 0x81, 0x69, 0xd0,
-	0x15, 0xc3, 0x70, 0x7c, 0x9b, 0x89, 0xb6, 0xcf, 0x88, 0xb6, 0xcf, 0xab, 0xb6, 0xa3, 0xe6, 0x00,
-	0x07, 0xce, 0xa8, 0x85, 0xee, 0xc0, 0xe4, 0x81, 0x63, 0xf9, 0x3d, 0xea, 0x55, 0x67, 0xc5, 0x68,
-	0xcf, 0x67, 0x35, 0xe9, 0xa1, 0x60, 0xa9, 0xcf, 0x28, 0xf0, 0x49, 0xf9, 0xed, 0xe1, 0xa0, 0x2e,
-	0x32, 0x61, 0xc2, 0x32, 0x7b, 0x26, 0xf3, 0xaa, 0x73, 0xa2, 0x63, 0x77, 0x46, 0xde, 0x0a, 0x72,
-	0x0b, 0x34, 0x04, 0x98, 0xd4, 0x98, 0xf2, 0x37, 0x56, 0x02, 0x90, 0x01, 0x45, 0xcf, 0x20, 0x16,
-	0xad, 0x22, 0x21, 0xe9, 0x6b, 0xa3, 0xab, 0x4c, 0x8e, 0x52, 0x9f, 0x56, 0x7d, 0x2a, 0x8a, 0x4f,
-	0x2c, 0xb1, 0xe7, 0xdf, 0x82, 0xb9, 0x81, 0x4d, 0x88, 0x66, 0x21, 0xdf, 0xa5, 0x87, 0xd2, 0x62,
-	0x60, 0xfe, 0x13, 0x5d, 0x85, 0xe2, 0x01, 0xb1, 0x7c, 0x5a, 0xcd, 0x89, 0x32, 0xf9, 0xf1, 0xcb,
-	0xb9, 0xdb, 0x9a, 0xfe, 0x08, 0xa6, 0x57, 0x7c, 0xb6, 0xef, 0xb8, 0xe6, 0x07, 0x62, 0x1f, 0xa1,
-	0x75, 0x28, 0x32, 0xa7, 0x4b, 0x6d, 0x51, 0xbd, 0xb2, 0xfc, 0x6a, 0xd6, 0x30, 0xcb, 0xb5, 0x79,
-	0x9f, 0x1e, 0x06, 0x72, 0xeb, 0x65, 0xde, 0xb2, 0x1d, 0x5e, 0x0f, 0xcb, 0xea, 0xfa, 0xd7, 0x61,
-	0xa2, 0xee, 0xb7, 0xdb, 0xd4, 0x3d, 0x85, 0x05, 0xab, 0x41, 0x81, 0x1d, 0xf6, 0x55, 0xeb, 0xc2,
-	0xa5, 0x51, 0xd8, 0x39, 0xec, 0xd3, 0x67, 0x47, 0x8b, 0x20, 0x71, 0xf8, 0x17, 0x16, 0x7c, 0xfa,
-	0x67, 0x1a, 0x5c, 0x91, 0x85, 0x6a, 0xf5, 0xac, 0x3a, 0x76, 0xdb, 0xec, 0x20, 0x0a, 0x45, 0x97,
-	0xb6, 0x4c, 0x4f, 0xb5, 0x7d, 0x6d, 0xe4, 0x21, 0xc7, 0x1c, 0x45, 0x82, 0xca, 0xae, 0x89, 0x02,
-	0x2c, 0xd1, 0x91, 0x0f, 0xe5, 0xf7, 0x29, 0xf3, 0x98, 0x4b, 0x49, 0x4f, 0xb4, 0xb9, 0xb2, 0x7c,
-	0x77, 0x64, 0x51, 0xf7, 0x28, 0x6b, 0x0a, 0x24, 0x25, 0x6e, 0xfa, 0xf8, 0x68, 0xb1, 0x1c, 0x16,
-	0xe2, 0x48, 0x92, 0xfe, 0xef, 0x39, 0x28, 0x87, 0xc6, 0x0b, 0xbd, 0x02, 0x45, 0xa1, 0x2b, 0xd4,
-	0xb0, 0x86, 0xcb, 0x43, 0xa8, 0x14, 0x2c, 0x69, 0xe8, 0x55, 0x98, 0x34, 0x9c, 0x5e, 0x8f, 0xd8,
-	0xad, 0x6a, 0xee, 0x66, 0xfe, 0x56, 0xb9, 0x5e, 0xe1, 0xbb, 0x62, 0x55, 0x16, 0xe1, 0x80, 0x86,
-	0x6e, 0x40, 0x81, 0xb8, 0x1d, 0xaf, 0x9a, 0x17, 0x3c, 0xc2, 0x3a, 0xaf, 0xb8, 0x1d, 0x0f, 0x8b,
-	0x52, 0xf4, 0x15, 0xc8, 0x53, 0xfb, 0xa0, 0x5a, 0x18, 0xbe, 0xed, 0xee, 0xd8, 0x07, 0x0f, 0x89,
-	0x5b, 0xaf, 0xa8, 0x36, 0xe4, 0xef, 0xd8, 0x07, 0x98, 0xd7, 0x41, 0xef, 0xc2, 0x94, 0xdc, 0x79,
-	0x5b, 0x7c, 0x23, 0x7b, 0xd5, 0xa2, 0xc0, 0x58, 0x1c, 0xbe, 0x75, 0x05, 0x5f, 0x64, 0x45, 0x62,
-	0x85, 0x1e, 0x4e, 0x40, 0xa1, 0x77, 0xa1, 0x1c, 0x78, 0x79, 0x9e, 0xb2, 0xd3, 0x99, 0x0a, 0x18,
-	0x2b, 0x26, 0x4c, 0xbf, 0xe9, 0x9b, 0x2e, 0xed, 0x51, 0x9b, 0x79, 0xf5, 0x39, 0x25, 0xa0, 0x1c,
-	0x50, 0x3d, 0x1c, 0xa1, 0xe9, 0xff, 0x95, 0x83, 0x41, 0x2f, 0x21, 0x29, 0x50, 0x3b, 0x4f, 0x81,
-	0x68, 0x0f, 0x66, 0x42, 0xbd, 0xbf, 0xed, 0x58, 0xa6, 0x71, 0xa8, 0xb6, 0xc2, 0x6d, 0x55, 0x6d,
-	0x66, 0x33, 0x49, 0x7e, 0x76, 0xb4, 0xf8, 0xf2, 0xa0, 0x8f, 0x5c, 0x8b, 0x18, 0x70, 0x1a, 0x90,
-	0xcb, 0x48, 0x9b, 0x47, 0xe9, 0x2e, 0xbe, 0x32, 0x64, 0x87, 0x8f, 0x60, 0x1b, 0x47, 0x5f, 0x29,
-	0xfa, 0x9f, 0xe7, 0xa0, 0x70, 0xa7, 0xd5, 0xa1, 0x5c, 0x5b, 0xb4, 0x5d, 0xa7, 0x97, 0xd6, 0x16,
-	0xeb, 0xae, 0xd3, 0xc3, 0x82, 0x82, 0xe6, 0x21, 0xc7, 0x1c, 0x35, 0x40, 0xa0, 0xe8, 0xb9, 0x1d,
-	0x07, 0xe7, 0x98, 0x83, 0x3e, 0x00, 0x30, 0x1c, 0xbb, 0x65, 0x4a, 0xd7, 0x22, 0x3f, 0xa6, 0x07,
-	0xb9, 0xee, 0xb8, 0x4f, 0x88, 0xdb, 0x5a, 0x0d, 0x11, 0xeb, 0x97, 0x8f, 0x8f, 0x16, 0x21, 0xfa,
-	0xc6, 0x31, 0x69, 0xa8, 0x13, 0xda, 0x16, 0xe9, 0x24, 0xaf, 0x8e, 0x2c, 0x97, 0x0f, 0xc4, 0x70,
-	0xcb, 0xa2, 0xff, 0x81, 0x06, 0x10, 0xb1, 0xa0, 0x37, 0x61, 0x66, 0x4f, 0x28, 0xc3, 0x2d, 0xf2,
-	0xb4, 0x41, 0xed, 0x0e, 0xdb, 0x17, 0x83, 0x57, 0x90, 0x93, 0x56, 0x4f, 0x92, 0x70, 0x9a, 0x17,
-	0xbd, 0x0d, 0xb3, 0xb2, 0x68, 0xd7, 0x23, 0x0a, 0x53, 0x0c, 0xee, 0x74, 0xfd, 0x2a, 0x77, 0x51,
-	0xea, 0x29, 0x1a, 0x1e, 0xe0, 0xd6, 0xdf, 0x80, 0xb9, 0x81, 0x91, 0x42, 0x8b, 0x50, 0xec, 0xd2,
-	0xc3, 0x4d, 0x6e, 0x47, 0xb8, 0x52, 0x11, 0x5a, 0xf4, 0x3e, 0x2f, 0xc0, 0xb2, 0x5c, 0xff, 0x6f,
-	0x0d, 0x4a, 0xeb, 0xbe, 0x6d, 0x08, 0xab, 0xf3, 0x7c, 0x1b, 0x11, 0xe8, 0xa8, 0x5c, 0xa6, 0x8e,
-	0xf2, 0x61, 0xa2, 0xfb, 0x24, 0xd4, 0x61, 0x95, 0xe5, 0xad, 0xd1, 0xe7, 0x5c, 0x35, 0xa9, 0x76,
-	0x5f, 0xe0, 0x49, 0xb7, 0xf6, 0xb2, 0x6a, 0xd0, 0xc4, 0xfd, 0x47, 0x42, 0xa8, 0x12, 0x36, 0xff,
-	0x15, 0xa8, 0xc4, 0xd8, 0xce, 0x64, 0x78, 0x7f, 0xa0, 0xc1, 0xcc, 0x86, 0x0c, 0xff, 0x1c, 0x57,
-	0x06, 0x5b, 0xe8, 0x3a, 0xe4, 0xdd, 0xbe, 0x2f, 0xea, 0xe7, 0x65, 0xdc, 0x80, 0xb7, 0x77, 0x31,
-	0x2f, 0x43, 0xbf, 0x06, 0xa5, 0x96, 0x2f, 0x5d, 0x5d, 0x65, 0x72, 0x6a, 0xb1, 0xfd, 0x15, 0x06,
-	0x99, 0x51, 0xcf, 0x78, 0x10, 0xc0, 0x77, 0xdc, 0x9a, 0xaa, 0x25, 0xbd, 0xb4, 0xe0, 0x0b, 0x87,
-	0x68, 0xdc, 0x46, 0xf4, 0xbc, 0x4e, 0xd3, 0xfc, 0x40, 0xc6, 0x8f, 0x45, 0x69, 0x23, 0xb6, 0x64,
-	0x11, 0x0e, 0x68, 0xfa, 0x77, 0x72, 0x70, 0x6d, 0x83, 0xb2, 0x35, 0x42, 0x7b, 0x8e, 0xbd, 0x46,
-	0xfb, 0x96, 0x73, 0xc8, 0x55, 0x1b, 0xa6, 0xdf, 0x44, 0x6f, 0x03, 0x98, 0xde, 0x5e, 0xf3, 0xc0,
-	0xe0, 0x26, 0x5a, 0x4d, 0xe1, 0x4d, 0x35, 0x62, 0xb0, 0xd9, 0xac, 0x2b, 0xca, 0xb3, 0xc4, 0x17,
-	0x8e, 0xd5, 0x89, 0x8c, 0x59, 0xee, 0x04, 0x63, 0xd6, 0x04, 0xe8, 0x47, 0x0a, 0x32, 0x2f, 0x38,
-	0x7f, 0x31, 0x10, 0x73, 0x16, 0xdd, 0x18, 0x83, 0x19, 0x47, 0x65, 0xfd, 0x6d, 0x1e, 0xe6, 0x37,
-	0x28, 0x0b, 0x6d, 0xb5, 0xf2, 0x45, 0x9a, 0x7d, 0x6a, 0xf0, 0x51, 0xf9, 0x50, 0x83, 0x09, 0x8b,
-	0xec, 0x51, 0xcb, 0x13, 0x5b, 0xa0, 0xb2, 0xfc, 0x78, 0xe4, 0x35, 0x39, 0x5c, 0x4a, 0xad, 0x21,
-	0x24, 0xa4, 0x56, 0xa9, 0x2c, 0xc4, 0x4a, 0x3c, 0xfa, 0x12, 0x54, 0x0c, 0xcb, 0xf7, 0x18, 0x75,
-	0xb7, 0x1d, 0x57, 0x6e, 0xee, 0x62, 0x14, 0x50, 0xad, 0x46, 0x24, 0x1c, 0xe7, 0x43, 0xcb, 0x00,
-	0x86, 0x65, 0x52, 0x9b, 0x89, 0x5a, 0x72, 0x6d, 0xa0, 0x60, 0xbc, 0x57, 0x43, 0x0a, 0x8e, 0x71,
-	0x71, 0x51, 0x3d, 0xc7, 0x36, 0x99, 0x23, 0x45, 0x15, 0x92, 0xa2, 0xb6, 0x22, 0x12, 0x8e, 0xf3,
-	0x89, 0x6a, 0x94, 0xb9, 0xa6, 0xe1, 0x89, 0x6a, 0xc5, 0x54, 0xb5, 0x88, 0x84, 0xe3, 0x7c, 0x7c,
-	0xfb, 0xc5, 0xfa, 0x7f, 0xa6, 0xed, 0xf7, 0x77, 0x25, 0x58, 0x48, 0x0c, 0x2b, 0x23, 0x8c, 0xb6,
-	0x7d, 0xab, 0x49, 0x59, 0x30, 0x81, 0x5f, 0x82, 0x8a, 0x0a, 0x44, 0x1e, 0x44, 0xaa, 0x29, 0x6c,
-	0x54, 0x33, 0x22, 0xe1, 0x38, 0x1f, 0xfa, 0xbd, 0x68, 0xde, 0x73, 0x62, 0xde, 0x8d, 0xf3, 0x99,
-	0xf7, 0x81, 0x06, 0x9e, 0x6a, 0xee, 0x97, 0xa0, 0x6c, 0x13, 0xe6, 0x89, 0x8d, 0xa4, 0xf6, 0x4c,
-	0xe8, 0x8b, 0x3c, 0x08, 0x08, 0x38, 0xe2, 0x41, 0xdb, 0x70, 0x55, 0x0d, 0xf1, 0x9d, 0xa7, 0x7d,
-	0xc7, 0x65, 0xd4, 0x95, 0x75, 0x0b, 0xa2, 0xee, 0x0d, 0x55, 0xf7, 0xea, 0x56, 0x06, 0x0f, 0xce,
-	0xac, 0x89, 0xb6, 0xe0, 0x8a, 0x21, 0x7c, 0x5b, 0x4c, 0x2d, 0x87, 0xb4, 0x02, 0xc0, 0xa2, 0x00,
-	0xfc, 0x69, 0x05, 0x78, 0x65, 0x75, 0x90, 0x05, 0x67, 0xd5, 0x4b, 0xaf, 0xe6, 0x89, 0x91, 0x56,
-	0xf3, 0xe4, 0x28, 0xab, 0xb9, 0x34, 0xda, 0x6a, 0x2e, 0x9f, 0x6e, 0x35, 0xf3, 0x91, 0xe7, 0xeb,
-	0x88, 0xba, 0x3c, 0x20, 0x93, 0x21, 0x96, 0x58, 0x78, 0x90, 0x1c, 0xf9, 0x66, 0x06, 0x0f, 0xce,
-	0xac, 0x89, 0xf6, 0x60, 0x5e, 0x96, 0xdf, 0xb1, 0x0d, 0xf7, 0xb0, 0xcf, 0xd5, 0x7d, 0x0c, 0xb7,
-	0x22, 0x70, 0x75, 0x85, 0x3b, 0xdf, 0x1c, 0xca, 0x89, 0x4f, 0x40, 0x41, 0xbf, 0x02, 0xd3, 0x72,
-	0x96, 0xb6, 0x48, 0x3f, 0x96, 0x9b, 0x78, 0x49, 0xc1, 0x4e, 0xaf, 0xc6, 0x89, 0x38, 0xc9, 0x8b,
-	0x56, 0x60, 0xa6, 0x7f, 0x60, 0xf0, 0x9f, 0x9b, 0xed, 0x07, 0x94, 0xb6, 0x68, 0x4b, 0xa4, 0x26,
-	0xca, 0xf5, 0x9f, 0x0a, 0x1c, 0xdf, 0xed, 0x24, 0x19, 0xa7, 0xf9, 0xd1, 0x6d, 0x98, 0xf2, 0x18,
-	0x71, 0x99, 0x0a, 0x6a, 0x44, 0xc2, 0xa2, 0x1c, 0x45, 0x10, 0xcd, 0x18, 0x0d, 0x27, 0x38, 0xc7,
-	0xd1, 0x1e, 0xcf, 0xa4, 0x31, 0x14, 0x51, 0x61, 0x4a, 0xed, 0xff, 0x76, 0x5a, 0xed, 0xbf, 0x37,
-	0xce, 0xf6, 0xcf, 0x90, 0x70, 0xaa, 0x6d, 0x7f, 0x0f, 0x90, 0xab, 0x62, 0x58, 0x19, 0xc6, 0xc4,
-	0x34, 0x7f, 0x98, 0x7a, 0xc1, 0x03, 0x1c, 0x38, 0xa3, 0x16, 0x6a, 0xc2, 0x4b, 0x1e, 0xb5, 0x99,
-	0x69, 0x53, 0x2b, 0x09, 0x27, 0x4d, 0xc2, 0xcb, 0x0a, 0xee, 0xa5, 0x66, 0x16, 0x13, 0xce, 0xae,
-	0x3b, 0xce, 0xe0, 0xff, 0xa8, 0x2c, 0xec, 0xae, 0x1c, 0x9a, 0x73, 0x53, 0xdb, 0x1f, 0xa6, 0xd5,
-	0xf6, 0xe3, 0xf1, 0xe7, 0x6d, 0x34, 0x95, 0xbd, 0x0c, 0x20, 0x66, 0x21, 0xae, 0xb3, 0x43, 0x4d,
-	0x85, 0x43, 0x0a, 0x8e, 0x71, 0xf1, 0x5d, 0x18, 0x8c, 0x73, 0x5c, 0x5d, 0x87, 0xbb, 0xb0, 0x19,
-	0x27, 0xe2, 0x24, 0xef, 0x50, 0x95, 0x5f, 0x1c, 0x59, 0xe5, 0xdf, 0x03, 0x64, 0xda, 0x26, 0x0b,
-	0xa7, 0x5c, 0xe2, 0x4d, 0x24, 0x33, 0x7f, 0x9b, 0x03, 0x1c, 0x38, 0xa3, 0xd6, 0x90, 0xa5, 0x3c,
-	0x79, 0xbe, 0x4b, 0xb9, 0x34, 0xfa, 0x52, 0x46, 0x8f, 0xe1, 0xba, 0x10, 0xa5, 0xc6, 0x27, 0x09,
-	0x2c, 0x95, 0xff, 0xcf, 0x28, 0xe0, 0xeb, 0x78, 0x18, 0x23, 0x1e, 0x8e, 0xc1, 0xe7, 0xc7, 0x70,
-	0x69, 0x8b, 0x0b, 0x27, 0xd6, 0x70, 0xc3, 0xb0, 0x9a, 0xc1, 0x83, 0x33, 0x6b, 0xf2, 0x25, 0xc6,
-	0xf8, 0x32, 0x24, 0x7b, 0x16, 0x6d, 0x09, 0x43, 0x50, 0x8a, 0x96, 0xd8, 0x4e, 0xa3, 0xa9, 0x28,
-	0x38, 0xc6, 0x95, 0xa5, 0xab, 0xa7, 0xce, 0xa8, 0xab, 0x37, 0xc4, 0x31, 0x4f, 0x3b, 0x61, 0x12,
-	0x94, 0xc2, 0x0f, 0x73, 0xd9, 0xab, 0x69, 0x06, 0x3c, 0x58, 0x47, 0x98, 0x4a, 0xc3, 0x35, 0xfb,
-	0xcc, 0x4b, 0x62, 0x5d, 0x4e, 0x99, 0xca, 0x0c, 0x1e, 0x9c, 0x59, 0x93, 0x3b, 0x29, 0xfb, 0x94,
-	0x58, 0x6c, 0x3f, 0x09, 0x38, 0x93, 0x74, 0x52, 0xee, 0x0e, 0xb2, 0xe0, 0xac, 0x7a, 0xe3, 0xa8,
-	0xb7, 0xdf, 0xcf, 0xc1, 0x95, 0x0d, 0xaa, 0x8e, 0x58, 0xb6, 0x9d, 0x56, 0xa0, 0xd7, 0xfe, 0x9f,
-	0x46, 0x59, 0x7f, 0xac, 0x01, 0xdc, 0xdd, 0xd9, 0xd9, 0x56, 0x21, 0x72, 0x0b, 0x0a, 0xc4, 0x57,
-	0x19, 0x8e, 0xca, 0xf2, 0xfa, 0xe8, 0x27, 0x59, 0xf1, 0xa4, 0xb7, 0x4a, 0x27, 0xf8, 0x6c, 0x1f,
-	0x0b, 0x74, 0xf4, 0xb3, 0x30, 0xa9, 0x6c, 0x83, 0x18, 0xab, 0x52, 0x74, 0xa2, 0xa0, 0xec, 0x07,
-	0x0e, 0xe8, 0xfa, 0x4f, 0x72, 0x70, 0x6d, 0xd3, 0x66, 0xd4, 0x6d, 0x32, 0xda, 0x4f, 0x24, 0xa5,
-	0xd1, 0x6f, 0xc4, 0xce, 0xfa, 0x64, 0x7b, 0x7f, 0xe1, 0x74, 0x31, 0xbb, 0x3c, 0x2f, 0xda, 0xa2,
-	0x8c, 0x44, 0xbb, 0x32, 0x2a, 0x8b, 0x1d, 0xf0, 0xf9, 0x50, 0xf0, 0xfa, 0xd4, 0x50, 0x19, 0x81,
-	0xe6, 0xc8, 0xa3, 0x91, 0xdd, 0x01, 0xbe, 0xf2, 0xa2, 0x5c, 0x8c, 0x58, 0x87, 0x42, 0x1c, 0xfa,
-	0x36, 0x4c, 0x78, 0x8c, 0x30, 0x3f, 0xc8, 0xb0, 0xed, 0x9e, 0xb7, 0x60, 0x01, 0x1e, 0x19, 0x48,
-	0xf9, 0x8d, 0x95, 0x50, 0xfd, 0x27, 0x1a, 0xcc, 0x67, 0x57, 0x6c, 0x98, 0x1e, 0x43, 0xdf, 0x18,
-	0x18, 0xf6, 0x53, 0xa6, 0x4a, 0x78, 0x6d, 0x31, 0xe8, 0xb3, 0x4a, 0x70, 0x29, 0x28, 0x89, 0x0d,
-	0x39, 0x83, 0xa2, 0xc9, 0x68, 0x2f, 0xf0, 0x12, 0xde, 0x39, 0xe7, 0xae, 0xc7, 0x76, 0x25, 0x97,
-	0x82, 0xa5, 0x30, 0xfd, 0xc3, 0xdc, 0xb0, 0x2e, 0xf3, 0x69, 0x41, 0xdd, 0xe4, 0xc1, 0xc7, 0xbd,
-	0xf1, 0x0e, 0x3e, 0xea, 0x7e, 0xac, 0x3d, 0x83, 0xc7, 0x1f, 0xdf, 0x1a, 0x3c, 0xfe, 0x78, 0x67,
-	0xfc, 0xe3, 0x8f, 0xd4, 0x28, 0x0c, 0x3d, 0x05, 0xf9, 0x51, 0x0e, 0x6e, 0x9c, 0xb4, 0x6a, 0x50,
-	0x27, 0x5c, 0x9c, 0xda, 0xb8, 0xd7, 0x21, 0x4e, 0x5c, 0x86, 0x68, 0x19, 0x8a, 0xfd, 0x7d, 0xe2,
-	0x05, 0xea, 0x34, 0xb0, 0x3a, 0xc5, 0x6d, 0x5e, 0xf8, 0xec, 0x68, 0xb1, 0x22, 0xd5, 0xb0, 0xf8,
-	0xc4, 0x92, 0x95, 0x2b, 0x96, 0x1e, 0xf5, 0xbc, 0xc8, 0xb1, 0x0b, 0x15, 0xcb, 0x96, 0x2c, 0xc6,
-	0x01, 0x1d, 0x31, 0x98, 0x90, 0xc1, 0x92, 0x4a, 0x27, 0x37, 0x46, 0xee, 0x47, 0xc6, 0x51, 0x59,
-	0xd4, 0x29, 0x15, 0x77, 0x2b, 0x59, 0xfa, 0x5f, 0x5e, 0x86, 0x6b, 0xd9, 0x73, 0xc2, 0xdb, 0x7e,
-	0x40, 0x5d, 0xcf, 0x74, 0x6c, 0x65, 0x7d, 0xa2, 0x63, 0x56, 0x59, 0x8c, 0x03, 0x3a, 0xba, 0x05,
-	0x25, 0x97, 0xf6, 0x2d, 0xd3, 0x20, 0x9e, 0x0a, 0x3a, 0x44, 0xf6, 0x11, 0xab, 0x32, 0x1c, 0x52,
-	0x87, 0x5c, 0xfd, 0xc8, 0xff, 0x1f, 0x5e, 0xfd, 0xf8, 0x33, 0x8d, 0xfb, 0x73, 0x32, 0xe3, 0x30,
-	0x50, 0x41, 0xcd, 0xc5, 0x79, 0xb6, 0xec, 0x65, 0xe9, 0x17, 0x0e, 0x11, 0x88, 0x87, 0xb7, 0x05,
-	0x7d, 0x5f, 0x83, 0x6a, 0x2f, 0xe5, 0x30, 0x5e, 0xe0, 0xed, 0x99, 0x1b, 0xc7, 0x47, 0x8b, 0xd5,
-	0xad, 0x21, 0xf2, 0xf0, 0xd0, 0x96, 0xa0, 0xdf, 0x84, 0x4a, 0x9f, 0xaf, 0x0b, 0x8f, 0x51, 0xdb,
-	0xa0, 0xea, 0xac, 0x6e, 0xf4, 0xd5, 0xbc, 0x1d, 0x61, 0x35, 0x99, 0x4b, 0x18, 0xed, 0x1c, 0xd6,
-	0x67, 0x78, 0x68, 0x17, 0x23, 0xe0, 0xb8, 0xc4, 0xc4, 0x9d, 0x9b, 0xad, 0x8b, 0xbe, 0x73, 0xf3,
-	0xbd, 0xec, 0x3b, 0x37, 0xe4, 0x9c, 0x35, 0xe4, 0x8b, 0xbb, 0x37, 0x2f, 0xee, 0xde, 0x7c, 0x5e,
-	0x77, 0x6f, 0x6e, 0x41, 0xc9, 0xa3, 0x8c, 0x99, 0x76, 0xc7, 0xab, 0xce, 0xca, 0x03, 0x3a, 0x2e,
-	0xb5, 0xa9, 0xca, 0x70, 0x48, 0x45, 0x3f, 0x0f, 0x65, 0x91, 0x62, 0x5b, 0x71, 0x3b, 0x5e, 0x75,
-	0x4e, 0x9c, 0xd4, 0x09, 0x4b, 0xde, 0x0c, 0x0a, 0x71, 0x44, 0x47, 0x6f, 0xc0, 0x94, 0x3c, 0x4a,
-	0x94, 0x26, 0x48, 0xdc, 0x93, 0x29, 0xd7, 0x67, 0xf9, 0x0a, 0xae, 0xc7, 0xca, 0x71, 0x82, 0x8b,
-	0x87, 0xae, 0x34, 0xcc, 0x43, 0x56, 0xaf, 0x24, 0x43, 0xd7, 0x28, 0x43, 0x89, 0x63, 0x5c, 0xe8,
-	0x65, 0xc8, 0x33, 0xcb, 0xab, 0x5e, 0x15, 0xcc, 0x61, 0x88, 0xb1, 0xd3, 0x68, 0x62, 0x5e, 0x3e,
-	0xfe, 0x25, 0x9a, 0xff, 0xd1, 0x60, 0x26, 0x75, 0x8f, 0x83, 0xcb, 0xf4, 0x5d, 0x4b, 0x59, 0xca,
-	0x50, 0xe6, 0x2e, 0x6e, 0x60, 0x5e, 0x8e, 0x1e, 0xab, 0x38, 0x26, 0x37, 0xa6, 0x3e, 0x7a, 0xb0,
-	0xb2, 0xd3, 0xe4, 0x81, 0xcb, 0x40, 0x08, 0x73, 0x3b, 0x35, 0xba, 0xf9, 0x64, 0x5e, 0xf4, 0xe4,
-	0x11, 0x8e, 0x25, 0x07, 0x0a, 0xa7, 0x49, 0x0e, 0xe8, 0x9f, 0x68, 0x50, 0xbe, 0x4f, 0xda, 0x5d,
-	0xd2, 0x34, 0xed, 0x2e, 0x7a, 0x15, 0x26, 0xf7, 0x5c, 0xa7, 0x4b, 0x5d, 0x4f, 0x9d, 0xfe, 0x8a,
-	0x23, 0xc5, 0xba, 0x2c, 0xc2, 0x01, 0x8d, 0xc7, 0xa3, 0xcc, 0xe9, 0x9b, 0x46, 0x3a, 0x1e, 0xdd,
-	0xe1, 0x85, 0x58, 0xd2, 0xd0, 0x23, 0x39, 0x77, 0xf9, 0x31, 0x6f, 0x62, 0xee, 0x34, 0x9a, 0xf2,
-	0x44, 0x35, 0x98, 0x75, 0xf4, 0x5a, 0xc2, 0xbf, 0x2a, 0x0f, 0xf5, 0x88, 0xbe, 0x97, 0x83, 0x8a,
-	0xec, 0x9a, 0x8c, 0x40, 0xcf, 0xb3, 0x73, 0x6f, 0x89, 0xe4, 0xb9, 0xe7, 0xf7, 0xa8, 0xbb, 0xe1,
-	0x3a, 0x7e, 0x5f, 0xcd, 0x52, 0x3c, 0x19, 0x12, 0x11, 0xc3, 0x04, 0x7a, 0x54, 0x14, 0x8c, 0x4e,
-	0xe1, 0x02, 0x47, 0xa7, 0x78, 0xe2, 0xe8, 0xfc, 0x95, 0x06, 0xe5, 0x86, 0xd9, 0xa6, 0xc6, 0xa1,
-	0x61, 0x51, 0xf4, 0x0d, 0xa8, 0xb6, 0xa8, 0x45, 0x19, 0xdd, 0x70, 0x89, 0x41, 0xb7, 0xa9, 0x6b,
-	0x0a, 0x55, 0xef, 0xd8, 0x2d, 0xe9, 0x8d, 0x17, 0xc3, 0x8c, 0x45, 0x75, 0x6d, 0x08, 0x1f, 0x1e,
-	0x8a, 0x80, 0x36, 0x61, 0xaa, 0x45, 0x3d, 0xd3, 0xa5, 0xad, 0xed, 0x98, 0xdf, 0xfd, 0x6a, 0xb0,
-	0xa4, 0xd7, 0x62, 0xb4, 0x67, 0x47, 0x8b, 0xd3, 0xdb, 0x66, 0x9f, 0x5a, 0xa6, 0x4d, 0xa5, 0x03,
-	0x9e, 0xa8, 0xaa, 0x17, 0x21, 0xdf, 0x70, 0x3a, 0xfa, 0xef, 0xe4, 0x21, 0xb4, 0xe1, 0xe8, 0x77,
-	0x35, 0xa8, 0x10, 0xdb, 0x76, 0x98, 0x32, 0x8e, 0x32, 0x7d, 0x8f, 0xc7, 0x76, 0x15, 0x6a, 0x2b,
-	0x11, 0xa8, 0xb4, 0xd4, 0xa1, 0x41, 0x8d, 0x51, 0x70, 0x5c, 0x36, 0xf2, 0x53, 0xc9, 0xe8, 0xad,
-	0xf1, 0x5b, 0x71, 0x8a, 0xd4, 0xf3, 0xfc, 0xd7, 0x60, 0x36, 0xdd, 0xd8, 0xb3, 0x28, 0xc2, 0x71,
-	0xd2, 0x5e, 0x7f, 0xaa, 0x41, 0x29, 0x50, 0x66, 0x68, 0x15, 0x0a, 0xbe, 0x47, 0xdd, 0xb3, 0xdd,
-	0x41, 0x14, 0x1a, 0x70, 0xd7, 0xa3, 0x2e, 0x16, 0x95, 0xd1, 0x3b, 0x50, 0xea, 0x13, 0xcf, 0x7b,
-	0xe2, 0xb8, 0x2d, 0xa5, 0x66, 0x4f, 0x09, 0x24, 0x6d, 0xb3, 0xaa, 0x8a, 0x43, 0x10, 0xfd, 0x87,
-	0xd3, 0x50, 0x79, 0x40, 0x98, 0x79, 0x40, 0x45, 0x3c, 0x7c, 0x31, 0x01, 0xd1, 0x9f, 0x68, 0x70,
-	0x2d, 0x99, 0xb9, 0xbe, 0xc0, 0xa8, 0x68, 0xfe, 0xf8, 0x68, 0xf1, 0x1a, 0xce, 0x94, 0x86, 0x87,
-	0xb4, 0x42, 0xc4, 0x47, 0x03, 0x89, 0xf0, 0x8b, 0x8e, 0x8f, 0x9a, 0xc3, 0x04, 0xe2, 0xe1, 0x6d,
-	0x79, 0x11, 0x1f, 0x8d, 0x10, 0x1f, 0x5d, 0xf8, 0x9b, 0x84, 0xef, 0x66, 0xc7, 0x47, 0x0f, 0x47,
-	0xf7, 0x80, 0xa2, 0x1d, 0xf9, 0x22, 0x28, 0x7a, 0x11, 0x14, 0x7d, 0x5e, 0x41, 0x51, 0x3f, 0x15,
-	0x14, 0x8d, 0x73, 0x18, 0xa1, 0x4e, 0xf9, 0x25, 0xda, 0xb0, 0xe0, 0x6a, 0xfc, 0x30, 0xe5, 0x8f,
-	0x72, 0x70, 0x25, 0x43, 0x3b, 0xa0, 0xb7, 0x61, 0xd6, 0x63, 0x8e, 0x4b, 0x3a, 0x34, 0x9a, 0x50,
-	0x69, 0xd0, 0xc4, 0x0d, 0xd0, 0x66, 0x8a, 0x86, 0x07, 0xb8, 0xd1, 0x63, 0x00, 0x62, 0x18, 0xd4,
-	0xf3, 0xb6, 0x9c, 0x56, 0xe0, 0x97, 0xbd, 0xc5, 0xc3, 0x85, 0x95, 0xb0, 0xf4, 0xd9, 0xd1, 0xe2,
-	0x17, 0xb3, 0x0e, 0x8c, 0x82, 0xf6, 0x30, 0x79, 0xd7, 0x3b, 0xaa, 0x80, 0x63, 0x90, 0xe8, 0xd7,
-	0x01, 0xe4, 0xed, 0xef, 0xf0, 0x9e, 0xe2, 0x73, 0xb2, 0xfa, 0xb5, 0xe0, 0x76, 0x75, 0xed, 0x57,
-	0x7d, 0x62, 0x33, 0xbe, 0x2a, 0xc4, 0xdd, 0xdd, 0x87, 0x21, 0x0a, 0x8e, 0x21, 0xea, 0xff, 0x90,
-	0x83, 0x52, 0xe0, 0x2f, 0x7e, 0x0e, 0xe7, 0x36, 0x9d, 0xc4, 0xb9, 0xcd, 0xe8, 0x8f, 0x50, 0x82,
-	0x26, 0x0f, 0x3d, 0xa9, 0x71, 0x52, 0x27, 0x35, 0x1b, 0xe3, 0x8b, 0x3a, 0xf9, 0x6c, 0xe6, 0x07,
-	0x39, 0xb8, 0x1c, 0xb0, 0xaa, 0xfb, 0xc9, 0x5f, 0x86, 0x69, 0x97, 0x92, 0x56, 0x9d, 0x30, 0x63,
-	0x5f, 0x4c, 0x9f, 0xbc, 0x9d, 0x3c, 0x77, 0x7c, 0xb4, 0x38, 0x8d, 0xe3, 0x04, 0x9c, 0xe4, 0xcb,
-	0xba, 0xd8, 0x9c, 0x1b, 0xf3, 0x62, 0x73, 0xfe, 0x2c, 0x17, 0x9b, 0x11, 0x81, 0x0a, 0x6f, 0xd1,
-	0x8e, 0xd9, 0xa3, 0x8e, 0xcf, 0x94, 0x6f, 0x73, 0xd6, 0x7b, 0xb7, 0xc2, 0xf6, 0xe2, 0x08, 0x06,
-	0xc7, 0x31, 0xf5, 0x7f, 0xd6, 0x60, 0x2a, 0x1a, 0xaf, 0x0b, 0x3f, 0xbd, 0x6a, 0x27, 0x4f, 0xaf,
-	0x56, 0xc6, 0x5e, 0x0e, 0x43, 0xce, 0xab, 0xbe, 0x5f, 0x8c, 0xba, 0x25, 0x4e, 0xa8, 0xf6, 0x60,
-	0xde, 0xcc, 0x3c, 0xb5, 0x89, 0x69, 0x9b, 0xf0, 0x2a, 0xda, 0xe6, 0x50, 0x4e, 0x7c, 0x02, 0x0a,
-	0xf2, 0xa1, 0x74, 0x40, 0x5d, 0x66, 0x1a, 0x34, 0xe8, 0xdf, 0xc6, 0x39, 0xbd, 0x74, 0x8c, 0xc6,
-	0xf4, 0xa1, 0x12, 0x80, 0x43, 0x51, 0x68, 0x0f, 0x8a, 0xb4, 0xd5, 0xa1, 0xc1, 0xd5, 0xf3, 0x37,
-	0xc7, 0xba, 0xf6, 0x1f, 0x8d, 0x27, 0xff, 0xf2, 0xb0, 0x84, 0x46, 0x1e, 0x94, 0xad, 0x20, 0xca,
-	0x56, 0xeb, 0xb0, 0x3e, 0xb2, 0x9c, 0x30, 0x5e, 0x8f, 0xae, 0x82, 0x86, 0x45, 0x38, 0x92, 0x83,
-	0xba, 0xe1, 0x83, 0x86, 0xe2, 0x39, 0x29, 0x8f, 0x13, 0x9e, 0xcb, 0x79, 0x50, 0x7e, 0x42, 0x18,
-	0x75, 0x7b, 0xc4, 0xed, 0x2a, 0x1f, 0x78, 0xf4, 0x1e, 0x3e, 0x0a, 0x90, 0xa2, 0x1e, 0x86, 0x45,
-	0x38, 0x92, 0xa3, 0xff, 0x30, 0xa6, 0xad, 0x3e, 0xef, 0xe3, 0xc3, 0x37, 0x92, 0xc7, 0x87, 0x0b,
-	0xe9, 0xe3, 0xc3, 0x54, 0xfe, 0xe2, 0xec, 0x07, 0x88, 0x04, 0x2a, 0x16, 0xf1, 0xd8, 0x6e, 0xbf,
-	0x45, 0x98, 0x4a, 0xe4, 0x55, 0x96, 0x7f, 0xee, 0x74, 0xca, 0x84, 0xab, 0xa7, 0xc8, 0xc5, 0x6d,
-	0x44, 0x30, 0x38, 0x8e, 0xa9, 0xff, 0x87, 0x06, 0x73, 0x03, 0x47, 0xc6, 0x68, 0x1f, 0x26, 0x6c,
-	0xe1, 0x94, 0x8f, 0xfd, 0x0e, 0x2f, 0xe6, 0xdb, 0xcb, 0x45, 0xa3, 0x0a, 0x14, 0x3e, 0xb2, 0xa1,
-	0x44, 0x9f, 0x32, 0xea, 0xda, 0xc4, 0x52, 0xb6, 0xf4, 0x7c, 0xde, 0xfc, 0x09, 0x17, 0xec, 0x8e,
-	0x42, 0xc6, 0xa1, 0x0c, 0xfd, 0xb3, 0x1c, 0x54, 0x62, 0x7c, 0xcf, 0x4b, 0xf2, 0x8a, 0xab, 0x88,
-	0x32, 0x3a, 0xdd, 0x75, 0x2d, 0x35, 0xd1, 0xb1, 0xab, 0x88, 0x8a, 0x84, 0x1b, 0x38, 0xce, 0x87,
-	0x96, 0x01, 0x7a, 0xc4, 0x63, 0xd4, 0x15, 0xba, 0x31, 0x75, 0x01, 0x70, 0x2b, 0xa4, 0xe0, 0x18,
-	0x17, 0xba, 0xa9, 0x32, 0x26, 0x85, 0xe4, 0x03, 0x9a, 0x21, 0xe9, 0x90, 0xe2, 0x39, 0xa4, 0x43,
-	0x50, 0x07, 0x66, 0x83, 0x56, 0x07, 0x54, 0xb5, 0x71, 0x4f, 0x09, 0x2c, 0xbd, 0xcb, 0x14, 0x04,
-	0x1e, 0x00, 0xd5, 0xff, 0x5a, 0x83, 0xe9, 0x84, 0x8b, 0x8c, 0x5e, 0x89, 0xdf, 0x77, 0x88, 0x25,
-	0x57, 0x13, 0xf7, 0x14, 0x5e, 0x83, 0x09, 0x39, 0x40, 0x6a, 0xe0, 0xc3, 0x8d, 0x28, 0x87, 0x10,
-	0x2b, 0x2a, 0xdf, 0x52, 0x2a, 0xfb, 0x92, 0xde, 0x52, 0x2a, 0x3d, 0x83, 0x03, 0x3a, 0xfa, 0x02,
-	0x77, 0xfa, 0x65, 0xeb, 0xd4, 0x48, 0x87, 0x86, 0x21, 0xe8, 0x07, 0x0e, 0x39, 0xf4, 0x6f, 0x81,
-	0x7c, 0xac, 0x8b, 0xae, 0x43, 0xbe, 0x67, 0xda, 0x2a, 0x03, 0x2a, 0xf2, 0xac, 0x5b, 0xa6, 0x8d,
-	0x79, 0x99, 0x20, 0x91, 0xa7, 0x2a, 0x27, 0x24, 0x49, 0xe4, 0x29, 0xe6, 0x65, 0xdc, 0xfd, 0xb1,
-	0x1c, 0xa7, 0xbb, 0x47, 0x8c, 0x6e, 0x90, 0x43, 0x95, 0x37, 0x6e, 0x85, 0xfb, 0xd3, 0x48, 0x92,
-	0x70, 0x9a, 0x57, 0xbc, 0xa8, 0x13, 0xd9, 0xf8, 0x47, 0x90, 0xb7, 0x9c, 0x8e, 0xda, 0x8b, 0xa3,
-	0xe7, 0x88, 0x1b, 0x4e, 0x47, 0x36, 0xb0, 0xe1, 0x74, 0x30, 0x47, 0x44, 0x06, 0x14, 0xbb, 0xa4,
-	0xdd, 0x25, 0x6a, 0xeb, 0x8d, 0xae, 0xae, 0xc3, 0x93, 0x03, 0xf5, 0x4c, 0x8c, 0x7f, 0x62, 0x89,
-	0x8d, 0x0c, 0x98, 0xf0, 0x5b, 0xe2, 0xaf, 0x27, 0xc6, 0xfd, 0x77, 0x8b, 0xdd, 0x35, 0x21, 0x42,
-	0xe8, 0x11, 0xf9, 0x1b, 0x2b, 0x68, 0xfd, 0x1f, 0x73, 0xa0, 0xfe, 0xf0, 0x02, 0xf9, 0x50, 0xee,
-	0x04, 0xcf, 0xb2, 0xd4, 0x98, 0xdd, 0x1d, 0xe3, 0x26, 0x70, 0xe2, 0x81, 0x97, 0x3c, 0x0c, 0x0b,
-	0x0b, 0x71, 0x24, 0x09, 0xd1, 0xe4, 0x58, 0xae, 0x8d, 0x39, 0x96, 0x52, 0xdc, 0xe0, 0x68, 0x12,
-	0x28, 0xec, 0x33, 0xd6, 0x57, 0x63, 0x39, 0xfa, 0x0b, 0xc5, 0xe8, 0x46, 0x9e, 0x4c, 0xbb, 0xf2,
-	0x6f, 0x2c, 0xa0, 0xf5, 0x1e, 0x28, 0x4b, 0x87, 0x8c, 0xc4, 0x63, 0x4c, 0x99, 0x4e, 0x5f, 0x3a,
-	0x9d, 0xfd, 0x09, 0x1f, 0x12, 0xc6, 0xde, 0x68, 0x64, 0xbe, 0xba, 0xd4, 0xff, 0x35, 0x07, 0xf9,
-	0x9d, 0x46, 0x53, 0x5e, 0x39, 0x16, 0xb9, 0x11, 0xda, 0xec, 0x9a, 0xfd, 0x87, 0xd4, 0x35, 0xdb,
-	0x32, 0x2e, 0x2e, 0xc5, 0xaf, 0x1c, 0xa7, 0x39, 0x70, 0x46, 0x2d, 0xf4, 0x1e, 0x4c, 0x19, 0x64,
-	0x95, 0xba, 0x4c, 0xea, 0xac, 0xb3, 0x65, 0x8f, 0xc5, 0x01, 0xe6, 0xea, 0x4a, 0x54, 0x1d, 0x27,
-	0xc0, 0xd0, 0x2e, 0x80, 0x11, 0x41, 0xe7, 0xcf, 0x02, 0x2d, 0x5f, 0x9f, 0x46, 0xc0, 0x31, 0x20,
-	0x84, 0xa1, 0xdc, 0xe5, 0xac, 0x02, 0xb5, 0x70, 0x16, 0x54, 0xb1, 0x28, 0xef, 0x07, 0x75, 0x71,
-	0x04, 0xa3, 0xff, 0x58, 0x83, 0xfc, 0xee, 0xda, 0x3a, 0x72, 0xa0, 0x1c, 0xde, 0x93, 0x51, 0x7b,
-	0xa2, 0x3e, 0x7e, 0xe2, 0x54, 0x0a, 0x0e, 0x3f, 0x71, 0x24, 0x03, 0xed, 0xc3, 0xe4, 0x9e, 0x6f,
-	0x5a, 0xcc, 0xb4, 0x45, 0x66, 0x69, 0x9c, 0x40, 0x25, 0x78, 0xcf, 0xa9, 0x8e, 0xe9, 0x24, 0x2a,
-	0x0e, 0xe0, 0xf5, 0x6f, 0x83, 0xd2, 0x05, 0xdc, 0x01, 0xbd, 0x88, 0x4e, 0x86, 0x0e, 0x68, 0x56,
-	0x47, 0xf5, 0xbf, 0xcf, 0xc1, 0x84, 0xfa, 0xa3, 0x9f, 0x8b, 0xcf, 0x3a, 0xd0, 0x44, 0xd6, 0x61,
-	0x75, 0xcc, 0xbf, 0xbe, 0x18, 0x9a, 0x73, 0xe8, 0xa5, 0x72, 0x0e, 0xe3, 0xfe, 0xc7, 0xc6, 0x73,
-	0x32, 0x0e, 0xff, 0xa4, 0xc1, 0x65, 0xc9, 0xb8, 0x69, 0x7b, 0x8c, 0xd8, 0x86, 0xf8, 0x47, 0xa4,
-	0x03, 0x51, 0x32, 0xb6, 0x0f, 0xaf, 0xc2, 0x3f, 0x61, 0x33, 0xe4, 0x6f, 0xac, 0xa0, 0xb9, 0x2f,
-	0xb0, 0xef, 0x78, 0x4c, 0x3c, 0x5b, 0xce, 0x25, 0x7d, 0x81, 0xbb, 0xaa, 0x1c, 0x87, 0x1c, 0x69,
-	0x27, 0xa3, 0x38, 0xdc, 0xc9, 0xd0, 0xff, 0x42, 0x83, 0xa9, 0xf8, 0xbf, 0x8b, 0x8c, 0x9e, 0x40,
-	0x49, 0xe5, 0x2f, 0x72, 0x17, 0x90, 0xbf, 0xf8, 0x58, 0x03, 0x08, 0x1a, 0x7b, 0xe1, 0xd9, 0x8b,
-	0x56, 0x32, 0x7b, 0x31, 0xf6, 0xb4, 0x66, 0xe7, 0x2e, 0xfe, 0xa6, 0x10, 0x74, 0x49, 0x64, 0x2e,
-	0x3e, 0xd4, 0xe0, 0x32, 0x49, 0x64, 0x03, 0x54, 0xcf, 0xce, 0x2d, 0xb9, 0x70, 0x4d, 0x35, 0x23,
-	0xf5, 0xd7, 0x5f, 0x38, 0x25, 0x16, 0xdd, 0x86, 0xa9, 0xbe, 0x8a, 0x09, 0x1f, 0x44, 0xab, 0x2e,
-	0x3c, 0xe1, 0xd8, 0x8e, 0xd1, 0x70, 0x82, 0xf3, 0x39, 0xd9, 0x97, 0xfc, 0xb9, 0x64, 0x5f, 0xe2,
-	0x47, 0x9c, 0x85, 0x13, 0x8f, 0x38, 0x6d, 0x28, 0xb7, 0x5d, 0xa7, 0x27, 0x12, 0x1c, 0xea, 0x2f,
-	0x41, 0xc6, 0x4c, 0x9a, 0x84, 0x4a, 0x76, 0x3d, 0xc0, 0xc5, 0x91, 0x08, 0x6e, 0x4d, 0x98, 0x23,
-	0xa5, 0x4d, 0x9c, 0x87, 0xb4, 0x70, 0xeb, 0xee, 0x48, 0x54, 0x1c, 0xc0, 0xeb, 0xff, 0x92, 0x0b,
-	0xb6, 0x6e, 0x33, 0x75, 0x47, 0x58, 0x1b, 0x72, 0x47, 0x58, 0x3d, 0xf3, 0x88, 0x87, 0xf8, 0xaf,
-	0xc1, 0x84, 0x4b, 0x89, 0xe7, 0xd8, 0xea, 0xc1, 0x54, 0xa8, 0xf8, 0xb0, 0x28, 0xc5, 0x8a, 0x1a,
-	0x4f, 0x05, 0xe4, 0x9e, 0x93, 0x0a, 0xf8, 0x42, 0x6c, 0x6e, 0x64, 0x0a, 0x34, 0xdc, 0x66, 0x19,
-	0xf3, 0x23, 0xa2, 0x1c, 0x75, 0x3a, 0x57, 0x4c, 0x47, 0x39, 0xea, 0x04, 0x2d, 0xe4, 0x40, 0x2d,
-	0x98, 0xb2, 0x88, 0xc7, 0x44, 0xa4, 0xd3, 0x5a, 0x61, 0x23, 0xe4, 0x19, 0xc2, 0x15, 0xdc, 0x88,
-	0xe1, 0xe0, 0x04, 0xaa, 0xfe, 0x55, 0x88, 0x32, 0x38, 0x68, 0x09, 0xca, 0x7d, 0xd7, 0xe9, 0x93,
-	0x0e, 0x61, 0x54, 0xb9, 0x78, 0xe1, 0x0a, 0xd8, 0x0e, 0x08, 0x38, 0xe2, 0xa9, 0xd7, 0x3e, 0xfa,
-	0x74, 0xe1, 0xd2, 0xc7, 0x9f, 0x2e, 0x5c, 0xfa, 0xe4, 0xd3, 0x85, 0x4b, 0xbf, 0x75, 0xbc, 0xa0,
-	0x7d, 0x74, 0xbc, 0xa0, 0x7d, 0x7c, 0xbc, 0xa0, 0x7d, 0x72, 0xbc, 0xa0, 0xfd, 0xf8, 0x78, 0x41,
-	0xfb, 0xee, 0xbf, 0x2d, 0x5c, 0xfa, 0x7a, 0x29, 0x98, 0xe6, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff,
-	0xad, 0xbf, 0x52, 0x2f, 0x32, 0x51, 0x00, 0x00,
+	// 4665 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5c, 0x5d, 0x6c, 0x24, 0x57,
+	0x56, 0x4e, 0xff, 0xd9, 0xdd, 0xa7, 0xed, 0xb1, 0x7d, 0x67, 0x32, 0xe9, 0x78, 0x13, 0xf7, 0xd0,
+	0x51, 0xa2, 0x01, 0x76, 0xdb, 0xc4, 0x64, 0xd9, 0x59, 0xd8, 0x6c, 0xe2, 0xb6, 0xc7, 0x8e, 0x67,
+	0xec, 0x89, 0x39, 0x6d, 0xcf, 0x90, 0x64, 0x45, 0xb8, 0xae, 0xbe, 0x6e, 0x57, 0xba, 0xba, 0xaa,
+	0xb7, 0xea, 0xb6, 0x67, 0x1c, 0xb1, 0x02, 0x89, 0x87, 0x80, 0x00, 0xed, 0x4a, 0xbc, 0x20, 0xad,
+	0x58, 0xf1, 0xb0, 0x12, 0x02, 0x69, 0x5f, 0x10, 0x48, 0x08, 0xb4, 0x12, 0x4f, 0x28, 0xbc, 0xa0,
+	0x3c, 0x20, 0x11, 0xa4, 0x95, 0xb5, 0x31, 0x12, 0x6f, 0x88, 0x45, 0xfb, 0x36, 0x42, 0x02, 0xdd,
+	0x9f, 0xfa, 0xed, 0x6a, 0x8f, 0xdd, 0x6d, 0x87, 0x07, 0xe6, 0xad, 0xeb, 0x9e, 0x73, 0xbe, 0x73,
+	0xeb, 0xfe, 0x9c, 0x7b, 0x7e, 0x6e, 0x35, 0xac, 0xb7, 0x4d, 0x7e, 0xd0, 0xdf, 0xab, 0x1b, 0x4e,
+	0x77, 0xd1, 0xee, 0x77, 0x69, 0xcf, 0x75, 0x3e, 0x90, 0x3f, 0xf6, 0x2d, 0xe7, 0xe1, 0x62, 0xaf,
+	0xd3, 0x5e, 0xa4, 0x3d, 0xd3, 0x0b, 0x5b, 0x0e, 0x5f, 0xa5, 0x56, 0xef, 0x80, 0xbe, 0xba, 0xd8,
+	0x66, 0x36, 0x73, 0x29, 0x67, 0xad, 0x7a, 0xcf, 0x75, 0xb8, 0x43, 0xbe, 0x12, 0x02, 0xd5, 0x7d,
+	0xa0, 0xba, 0x2f, 0x56, 0xef, 0x75, 0xda, 0x75, 0x01, 0x14, 0xb6, 0xf8, 0x40, 0xf3, 0x5f, 0x8a,
+	0xf4, 0xa0, 0xed, 0xb4, 0x9d, 0x45, 0x89, 0xb7, 0xd7, 0xdf, 0x97, 0x4f, 0xf2, 0x41, 0xfe, 0x52,
+	0x7a, 0xe6, 0x6b, 0x9d, 0x5b, 0x5e, 0xdd, 0x74, 0x44, 0xb7, 0x16, 0x0d, 0xc7, 0x65, 0x8b, 0x87,
+	0x03, 0x7d, 0x99, 0x7f, 0x2d, 0xe4, 0xe9, 0x52, 0xe3, 0xc0, 0xb4, 0x99, 0x7b, 0xe4, 0xbf, 0xcb,
+	0xa2, 0xcb, 0x3c, 0xa7, 0xef, 0x1a, 0xec, 0x5c, 0x52, 0xde, 0x62, 0x97, 0x71, 0x9a, 0xa6, 0x6b,
+	0x71, 0x98, 0x94, 0xdb, 0xb7, 0xb9, 0xd9, 0x1d, 0x54, 0xf3, 0x4b, 0x4f, 0x12, 0xf0, 0x8c, 0x03,
+	0xd6, 0xa5, 0x49, 0xb9, 0xda, 0x7f, 0x4e, 0xc1, 0x95, 0xe5, 0x3d, 0x8f, 0xbb, 0xd4, 0xe0, 0xf7,
+	0x99, 0xcb, 0xd9, 0x23, 0x72, 0x03, 0xf2, 0x36, 0xed, 0xb2, 0x4a, 0xe6, 0x46, 0xe6, 0x66, 0xa9,
+	0x31, 0xf5, 0xf1, 0x71, 0xf5, 0x99, 0x93, 0xe3, 0x6a, 0xfe, 0x1e, 0xed, 0x32, 0x94, 0x14, 0x62,
+	0xc0, 0x84, 0x7a, 0xdb, 0x4a, 0xee, 0x46, 0xe6, 0x66, 0x79, 0xe9, 0x8d, 0xfa, 0x88, 0xd3, 0x54,
+	0x6f, 0x4a, 0x98, 0x06, 0x9c, 0x1c, 0x57, 0x27, 0xd4, 0x6f, 0xd4, 0xd0, 0xe4, 0x3d, 0xc8, 0x7b,
+	0xa6, 0xdd, 0xa9, 0xe4, 0xa5, 0x8a, 0xd7, 0x47, 0x57, 0x61, 0xda, 0x9d, 0x46, 0x51, 0xbc, 0x81,
+	0xf8, 0x85, 0x12, 0x94, 0x7c, 0x3b, 0x03, 0x73, 0x86, 0x63, 0x73, 0x2a, 0x06, 0x6a, 0x87, 0x75,
+	0x7b, 0x16, 0xe5, 0xac, 0x52, 0x90, 0xaa, 0xee, 0x8c, 0xac, 0x6a, 0x25, 0x89, 0xd8, 0x78, 0xf6,
+	0xe4, 0xb8, 0x3a, 0x37, 0xd0, 0x8c, 0x83, 0xba, 0xc9, 0x03, 0xc8, 0xf5, 0x5b, 0xfb, 0x95, 0x09,
+	0xd9, 0x85, 0xaf, 0x8d, 0xdc, 0x85, 0xdd, 0xd5, 0xb5, 0xc6, 0xe4, 0xc9, 0x71, 0x35, 0xb7, 0xbb,
+	0xba, 0x86, 0x02, 0x91, 0x74, 0xa0, 0x28, 0x56, 0x59, 0x8b, 0x72, 0x5a, 0x99, 0x94, 0xe8, 0xcb,
+	0x23, 0xa3, 0x6f, 0x69, 0xa0, 0xc6, 0xd4, 0xc9, 0x71, 0xb5, 0xe8, 0x3f, 0x61, 0xa0, 0x80, 0xfc,
+	0x51, 0x06, 0xa6, 0x6c, 0xa7, 0xc5, 0x9a, 0xcc, 0x62, 0x06, 0x77, 0xdc, 0x4a, 0xf1, 0x46, 0xee,
+	0x66, 0x79, 0xe9, 0x9d, 0x91, 0x35, 0xc6, 0xd7, 0x66, 0xfd, 0x5e, 0x04, 0xfb, 0xb6, 0xcd, 0xdd,
+	0xa3, 0xc6, 0x35, 0xbd, 0x3e, 0xa7, 0xa2, 0x24, 0x8c, 0x75, 0x82, 0xec, 0x42, 0x99, 0x3b, 0x96,
+	0x58, 0xf7, 0xa6, 0x63, 0x7b, 0x95, 0x92, 0xec, 0xd3, 0x42, 0x5d, 0x6d, 0x19, 0xa1, 0xb9, 0x2e,
+	0xf6, 0x7c, 0xfd, 0xf0, 0xd5, 0xfa, 0x4e, 0xc0, 0xd6, 0xb8, 0xaa, 0x81, 0xcb, 0x61, 0x9b, 0x87,
+	0x51, 0x1c, 0xc2, 0x60, 0xc6, 0x63, 0x46, 0xdf, 0x35, 0xf9, 0x91, 0x98, 0x62, 0xf6, 0x88, 0x57,
+	0x40, 0x0e, 0xf0, 0x2b, 0x69, 0xd0, 0xdb, 0x4e, 0xab, 0x19, 0xe7, 0x6e, 0x5c, 0x3d, 0x39, 0xae,
+	0xce, 0x24, 0x1a, 0x31, 0x89, 0x49, 0x6c, 0x98, 0x35, 0xbb, 0xb4, 0xcd, 0xb6, 0xfb, 0x96, 0xd5,
+	0x64, 0x86, 0xcb, 0xb8, 0x57, 0x29, 0xcb, 0x57, 0xb8, 0x99, 0xa6, 0x67, 0xd3, 0x31, 0xa8, 0xf5,
+	0xf6, 0xde, 0x07, 0xcc, 0xe0, 0xc8, 0xf6, 0x99, 0xcb, 0x6c, 0x83, 0x35, 0x2a, 0xfa, 0x65, 0x66,
+	0x37, 0x12, 0x48, 0x38, 0x80, 0x4d, 0xd6, 0x61, 0xae, 0xe7, 0x9a, 0x8e, 0xec, 0x82, 0x45, 0x3d,
+	0x4f, 0x6c, 0xfc, 0xca, 0x94, 0x34, 0x06, 0xcf, 0x6b, 0x98, 0xb9, 0xed, 0x24, 0x03, 0x0e, 0xca,
+	0x90, 0x9b, 0x50, 0xf4, 0x1b, 0x2b, 0xd3, 0x37, 0x32, 0x37, 0x0b, 0x6a, 0xd9, 0xf8, 0xb2, 0x18,
+	0x50, 0xc9, 0x1a, 0x14, 0xe9, 0xfe, 0xbe, 0x69, 0x0b, 0xce, 0x2b, 0x72, 0x08, 0x5f, 0x48, 0x7b,
+	0xb5, 0x65, 0xcd, 0xa3, 0x70, 0xfc, 0x27, 0x0c, 0x64, 0xc9, 0x1d, 0x20, 0x1e, 0x73, 0x0f, 0x4d,
+	0x83, 0x2d, 0x1b, 0x86, 0xd3, 0xb7, 0xb9, 0xec, 0xfb, 0x8c, 0xec, 0xfb, 0xbc, 0xee, 0x3b, 0x69,
+	0x0e, 0x70, 0x60, 0x8a, 0x14, 0xb9, 0x0d, 0x93, 0x87, 0x8e, 0xd5, 0xef, 0x32, 0xaf, 0x32, 0x2b,
+	0x47, 0x7b, 0x3e, 0xad, 0x4b, 0xf7, 0x25, 0x4b, 0x63, 0x46, 0x83, 0x4f, 0xaa, 0x67, 0x0f, 0x7d,
+	0x59, 0x62, 0xc2, 0x84, 0x65, 0x76, 0x4d, 0xee, 0x55, 0xe6, 0xe4, 0x8b, 0xdd, 0x1e, 0x79, 0x2b,
+	0xa8, 0x2d, 0xb0, 0x29, 0xc1, 0x94, 0xc5, 0x54, 0xbf, 0x51, 0x2b, 0x20, 0x06, 0x14, 0x3c, 0x83,
+	0x5a, 0xac, 0x42, 0xa4, 0xa6, 0xaf, 0x8f, 0x6e, 0x32, 0x05, 0x4a, 0x63, 0x5a, 0xbf, 0x53, 0x41,
+	0x3e, 0xa2, 0xc2, 0x9e, 0x7f, 0x03, 0xe6, 0x06, 0x36, 0x21, 0x99, 0x85, 0x5c, 0x87, 0x1d, 0xa9,
+	0x13, 0x03, 0xc5, 0x4f, 0x72, 0x0d, 0x0a, 0x87, 0xd4, 0xea, 0xb3, 0x4a, 0x56, 0xb6, 0xa9, 0x87,
+	0x5f, 0xce, 0xde, 0xca, 0xd4, 0x1e, 0xc0, 0xf4, 0x72, 0x9f, 0x1f, 0x38, 0xae, 0xf9, 0xa1, 0xdc,
+	0x47, 0x64, 0x0d, 0x0a, 0xdc, 0xe9, 0x30, 0x5b, 0x8a, 0x97, 0x97, 0x5e, 0x4e, 0x1b, 0x66, 0xb5,
+	0x36, 0xef, 0xb2, 0x23, 0x5f, 0x6f, 0xa3, 0x24, 0x7a, 0xb6, 0x23, 0xe4, 0x50, 0x89, 0xd7, 0xde,
+	0x85, 0x89, 0x46, 0x7f, 0x7f, 0x9f, 0xb9, 0x67, 0x38, 0xc1, 0xea, 0x90, 0xe7, 0x47, 0x3d, 0xdd,
+	0xbb, 0x60, 0x69, 0xe4, 0x77, 0x8e, 0x7a, 0xec, 0xf1, 0x71, 0x15, 0x14, 0x8e, 0x78, 0x42, 0xc9,
+	0x57, 0xfb, 0x69, 0x06, 0xae, 0xaa, 0x46, 0xbd, 0x7a, 0x56, 0x1c, 0x7b, 0xdf, 0x6c, 0x13, 0x06,
+	0x05, 0x97, 0xb5, 0x4c, 0x4f, 0xf7, 0x7d, 0x75, 0xe4, 0x21, 0x47, 0x81, 0xa2, 0x40, 0xd5, 0xab,
+	0xc9, 0x06, 0x54, 0xe8, 0xa4, 0x0f, 0xa5, 0x0f, 0x18, 0xf7, 0xb8, 0xcb, 0x68, 0x57, 0xf6, 0xb9,
+	0xbc, 0xf4, 0xd6, 0xc8, 0xaa, 0xee, 0x30, 0xde, 0x94, 0x48, 0x5a, 0xdd, 0xf4, 0xc9, 0x71, 0xb5,
+	0x14, 0x34, 0x62, 0xa8, 0xa9, 0xf6, 0xef, 0x59, 0x28, 0x05, 0x87, 0x17, 0x79, 0x09, 0x0a, 0xd2,
+	0x56, 0xe8, 0x61, 0x0d, 0x96, 0x87, 0x34, 0x29, 0xa8, 0x68, 0xe4, 0x65, 0x98, 0x34, 0x9c, 0x6e,
+	0x97, 0xda, 0xad, 0x4a, 0xf6, 0x46, 0xee, 0x66, 0xa9, 0x51, 0x16, 0xbb, 0x62, 0x45, 0x35, 0xa1,
+	0x4f, 0x23, 0x2f, 0x40, 0x9e, 0xba, 0x6d, 0xaf, 0x92, 0x93, 0x3c, 0xf2, 0x74, 0x5e, 0x76, 0xdb,
+	0x1e, 0xca, 0x56, 0xf2, 0x55, 0xc8, 0x31, 0xfb, 0xb0, 0x92, 0x1f, 0xbe, 0xed, 0x6e, 0xdb, 0x87,
+	0xf7, 0xa9, 0xdb, 0x28, 0xeb, 0x3e, 0xe4, 0x6e, 0xdb, 0x87, 0x28, 0x64, 0xc8, 0x3b, 0x30, 0xa5,
+	0x76, 0xde, 0x96, 0xd8, 0xc8, 0x5e, 0xa5, 0x20, 0x31, 0xaa, 0xc3, 0xb7, 0xae, 0xe4, 0x0b, 0x4f,
+	0x91, 0x48, 0xa3, 0x87, 0x31, 0x28, 0xf2, 0x0e, 0x94, 0x7c, 0x2f, 0xcf, 0xd3, 0xe7, 0x74, 0xaa,
+	0x01, 0x46, 0xcd, 0x84, 0xec, 0x9b, 0x7d, 0xd3, 0x65, 0x5d, 0x66, 0x73, 0xaf, 0x31, 0xa7, 0x15,
+	0x94, 0x7c, 0xaa, 0x87, 0x21, 0x5a, 0xed, 0xbf, 0xb2, 0x30, 0xe8, 0x25, 0xc4, 0x15, 0x66, 0x2e,
+	0x52, 0x21, 0xd9, 0x83, 0x99, 0xc0, 0xee, 0x6f, 0x3b, 0x96, 0x69, 0x1c, 0xe9, 0xad, 0x70, 0x4b,
+	0x8b, 0xcd, 0x6c, 0xc4, 0xc9, 0x8f, 0x8f, 0xab, 0x2f, 0x0e, 0xfa, 0xc8, 0xf5, 0x90, 0x01, 0x93,
+	0x80, 0x42, 0x47, 0xf2, 0x78, 0x54, 0xee, 0xe2, 0x4b, 0x43, 0x76, 0xf8, 0x08, 0x67, 0xe3, 0xe8,
+	0x2b, 0xa5, 0xf6, 0xe7, 0x59, 0xc8, 0xdf, 0x6e, 0xb5, 0x99, 0xb0, 0x16, 0xfb, 0xae, 0xd3, 0x4d,
+	0x5a, 0x8b, 0x35, 0xd7, 0xe9, 0xa2, 0xa4, 0x90, 0x79, 0xc8, 0x72, 0x47, 0x0f, 0x10, 0x68, 0x7a,
+	0x76, 0xc7, 0xc1, 0x2c, 0x77, 0xc8, 0x87, 0x00, 0x86, 0x63, 0xb7, 0x4c, 0xe5, 0x5a, 0xe4, 0xc6,
+	0xf4, 0x20, 0xd7, 0x1c, 0xf7, 0x21, 0x75, 0x5b, 0x2b, 0x01, 0x62, 0xe3, 0xca, 0xc9, 0x71, 0x15,
+	0xc2, 0x67, 0x8c, 0x68, 0x23, 0xed, 0xe0, 0x6c, 0x51, 0x4e, 0xf2, 0xca, 0xc8, 0x7a, 0xc5, 0x40,
+	0x0c, 0x3f, 0x59, 0x6a, 0x7f, 0x98, 0x01, 0x08, 0x59, 0xc8, 0xeb, 0x30, 0xb3, 0x27, 0x8d, 0xe1,
+	0x16, 0x7d, 0xb4, 0xc9, 0xec, 0x36, 0x3f, 0x90, 0x83, 0x97, 0x57, 0x93, 0xd6, 0x88, 0x93, 0x30,
+	0xc9, 0x4b, 0xde, 0x84, 0x59, 0xd5, 0xb4, 0xeb, 0x51, 0x8d, 0x29, 0x07, 0x77, 0xba, 0x71, 0x4d,
+	0xb8, 0x28, 0x8d, 0x04, 0x0d, 0x07, 0xb8, 0x6b, 0xaf, 0xc1, 0xdc, 0xc0, 0x48, 0x91, 0x2a, 0x14,
+	0x3a, 0xec, 0x68, 0x43, 0x9c, 0x23, 0xc2, 0xa8, 0x48, 0x2b, 0x7a, 0x57, 0x34, 0xa0, 0x6a, 0xaf,
+	0xfd, 0x77, 0x06, 0x8a, 0x6b, 0x7d, 0xdb, 0x90, 0xa7, 0xce, 0x93, 0xcf, 0x08, 0xdf, 0x46, 0x65,
+	0x53, 0x6d, 0x54, 0x1f, 0x26, 0x3a, 0x0f, 0x03, 0x1b, 0x56, 0x5e, 0xda, 0x1a, 0x7d, 0xce, 0x75,
+	0x97, 0xea, 0x77, 0x25, 0x9e, 0x72, 0x6b, 0xaf, 0xe8, 0x0e, 0x4d, 0xdc, 0x7d, 0x20, 0x95, 0x6a,
+	0x65, 0xf3, 0x5f, 0x85, 0x72, 0x84, 0xed, 0x5c, 0x07, 0xef, 0x0f, 0x32, 0x30, 0xb3, 0xae, 0xc2,
+	0x3f, 0xc7, 0x55, 0xc1, 0x16, 0x79, 0x1e, 0x72, 0x6e, 0xaf, 0x2f, 0xe5, 0x73, 0x2a, 0x6e, 0xc0,
+	0xed, 0x5d, 0x14, 0x6d, 0xe4, 0xd7, 0xa0, 0xd8, 0xea, 0x2b, 0x57, 0x57, 0x1f, 0x39, 0xf5, 0xc8,
+	0xfe, 0x0a, 0x82, 0xcc, 0xf0, 0xcd, 0x44, 0x10, 0x20, 0x76, 0xdc, 0xaa, 0x96, 0x52, 0x5e, 0x9a,
+	0xff, 0x84, 0x01, 0x9a, 0x38, 0x23, 0xba, 0x5e, 0xbb, 0x69, 0x7e, 0xa8, 0xe2, 0xc7, 0x82, 0x3a,
+	0x23, 0xb6, 0x54, 0x13, 0xfa, 0xb4, 0xda, 0xb7, 0xb3, 0x70, 0x7d, 0x9d, 0xf1, 0x55, 0xca, 0xba,
+	0x8e, 0xbd, 0xca, 0x7a, 0x96, 0x73, 0x24, 0x4c, 0x1b, 0xb2, 0x6f, 0x92, 0x37, 0x01, 0x4c, 0x6f,
+	0xaf, 0x79, 0x68, 0x88, 0x23, 0x5a, 0x4f, 0xe1, 0x0d, 0x3d, 0x62, 0xb0, 0xd1, 0x6c, 0x68, 0xca,
+	0xe3, 0xd8, 0x13, 0x46, 0x64, 0xc2, 0xc3, 0x2c, 0x7b, 0xca, 0x61, 0xd6, 0x04, 0xe8, 0x85, 0x06,
+	0x32, 0x27, 0x39, 0x7f, 0xd1, 0x57, 0x73, 0x1e, 0xdb, 0x18, 0x81, 0x19, 0xc7, 0x64, 0xfd, 0x6d,
+	0x0e, 0xe6, 0xd7, 0x19, 0x0f, 0xce, 0x6a, 0xed, 0x8b, 0x34, 0x7b, 0xcc, 0x10, 0xa3, 0xf2, 0x51,
+	0x06, 0x26, 0x2c, 0xba, 0xc7, 0x2c, 0x4f, 0x6e, 0x81, 0xf2, 0xd2, 0xfb, 0x23, 0xaf, 0xc9, 0xe1,
+	0x5a, 0xea, 0x9b, 0x52, 0x43, 0x62, 0x95, 0xaa, 0x46, 0xd4, 0xea, 0xc9, 0x97, 0xa1, 0x6c, 0x58,
+	0x7d, 0x8f, 0x33, 0x77, 0xdb, 0x71, 0xd5, 0xe6, 0x2e, 0x84, 0x01, 0xd5, 0x4a, 0x48, 0xc2, 0x28,
+	0x1f, 0x59, 0x02, 0x30, 0x2c, 0x93, 0xd9, 0x5c, 0x4a, 0xa9, 0xb5, 0x41, 0xfc, 0xf1, 0x5e, 0x09,
+	0x28, 0x18, 0xe1, 0x12, 0xaa, 0xba, 0x8e, 0x6d, 0x72, 0x47, 0xa9, 0xca, 0xc7, 0x55, 0x6d, 0x85,
+	0x24, 0x8c, 0xf2, 0x49, 0x31, 0xc6, 0x5d, 0xd3, 0xf0, 0xa4, 0x58, 0x21, 0x21, 0x16, 0x92, 0x30,
+	0xca, 0x27, 0xb6, 0x5f, 0xe4, 0xfd, 0xcf, 0xb5, 0xfd, 0xfe, 0xae, 0x08, 0x0b, 0xb1, 0x61, 0xe5,
+	0x94, 0xb3, 0xfd, 0xbe, 0xd5, 0x64, 0xdc, 0x9f, 0xc0, 0x2f, 0x43, 0x59, 0x07, 0x22, 0xf7, 0x42,
+	0xd3, 0x14, 0x74, 0xaa, 0x19, 0x92, 0x30, 0xca, 0x47, 0x7e, 0x3f, 0x9c, 0xf7, 0xac, 0x9c, 0x77,
+	0xe3, 0x62, 0xe6, 0x7d, 0xa0, 0x83, 0x67, 0x9a, 0xfb, 0x45, 0x28, 0xd9, 0x94, 0x7b, 0x72, 0x23,
+	0xe9, 0x3d, 0x13, 0xf8, 0x22, 0xf7, 0x7c, 0x02, 0x86, 0x3c, 0x64, 0x1b, 0xae, 0xe9, 0x21, 0xbe,
+	0xfd, 0xa8, 0xe7, 0xb8, 0x9c, 0xb9, 0x4a, 0x36, 0x2f, 0x65, 0x5f, 0xd0, 0xb2, 0xd7, 0xb6, 0x52,
+	0x78, 0x30, 0x55, 0x92, 0x6c, 0xc1, 0x55, 0x43, 0xfa, 0xb6, 0xc8, 0x2c, 0x87, 0xb6, 0x7c, 0xc0,
+	0x82, 0x04, 0xfc, 0x82, 0x06, 0xbc, 0xba, 0x32, 0xc8, 0x82, 0x69, 0x72, 0xc9, 0xd5, 0x3c, 0x31,
+	0xd2, 0x6a, 0x9e, 0x1c, 0x65, 0x35, 0x17, 0x47, 0x5b, 0xcd, 0xa5, 0xb3, 0xad, 0x66, 0x31, 0xf2,
+	0x62, 0x1d, 0x31, 0x57, 0x04, 0x64, 0x2a, 0xc4, 0x92, 0x0b, 0x0f, 0xe2, 0x23, 0xdf, 0x4c, 0xe1,
+	0xc1, 0x54, 0x49, 0xb2, 0x07, 0xf3, 0xaa, 0xfd, 0xb6, 0x6d, 0xb8, 0x47, 0x3d, 0x61, 0xee, 0x23,
+	0xb8, 0x65, 0x89, 0x5b, 0xd3, 0xb8, 0xf3, 0xcd, 0xa1, 0x9c, 0x78, 0x0a, 0x0a, 0xf9, 0x15, 0x98,
+	0x56, 0xb3, 0xb4, 0x45, 0x7b, 0x91, 0xdc, 0xc4, 0xb3, 0x1a, 0x76, 0x7a, 0x25, 0x4a, 0xc4, 0x38,
+	0x2f, 0x59, 0x86, 0x99, 0xde, 0xa1, 0x21, 0x7e, 0x6e, 0xec, 0xdf, 0x63, 0xac, 0xc5, 0x5a, 0x32,
+	0x35, 0x51, 0x6a, 0x3c, 0xe7, 0x3b, 0xbe, 0xdb, 0x71, 0x32, 0x26, 0xf9, 0xc9, 0x2d, 0x98, 0xf2,
+	0x38, 0x75, 0xb9, 0x0e, 0x6a, 0x64, 0xc2, 0xa2, 0x14, 0x46, 0x10, 0xcd, 0x08, 0x0d, 0x63, 0x9c,
+	0xe3, 0x58, 0x8f, 0xc7, 0xea, 0x30, 0x94, 0x51, 0x61, 0xc2, 0xec, 0xff, 0x4e, 0xd2, 0xec, 0xbf,
+	0x37, 0xce, 0xf6, 0x4f, 0xd1, 0x70, 0xa6, 0x6d, 0x7f, 0x07, 0x88, 0xab, 0x63, 0x58, 0x15, 0xc6,
+	0x44, 0x2c, 0x7f, 0x90, 0x7a, 0xc1, 0x01, 0x0e, 0x4c, 0x91, 0x22, 0x4d, 0x78, 0xd6, 0x63, 0x36,
+	0x37, 0x6d, 0x66, 0xc5, 0xe1, 0xd4, 0x91, 0xf0, 0xa2, 0x86, 0x7b, 0xb6, 0x99, 0xc6, 0x84, 0xe9,
+	0xb2, 0xe3, 0x0c, 0xfe, 0x8f, 0x4a, 0xf2, 0xdc, 0x55, 0x43, 0x73, 0x61, 0x66, 0xfb, 0xa3, 0xa4,
+	0xd9, 0x7e, 0x7f, 0xfc, 0x79, 0x1b, 0xcd, 0x64, 0x2f, 0x01, 0xc8, 0x59, 0x88, 0xda, 0xec, 0xc0,
+	0x52, 0x61, 0x40, 0xc1, 0x08, 0x97, 0xd8, 0x85, 0xfe, 0x38, 0x47, 0xcd, 0x75, 0xb0, 0x0b, 0x9b,
+	0x51, 0x22, 0xc6, 0x79, 0x87, 0x9a, 0xfc, 0xc2, 0xc8, 0x26, 0xff, 0x0e, 0x10, 0xd3, 0x36, 0x79,
+	0x30, 0xe5, 0x0a, 0x6f, 0x22, 0x9e, 0xf9, 0xdb, 0x18, 0xe0, 0xc0, 0x14, 0xa9, 0x21, 0x4b, 0x79,
+	0xf2, 0x62, 0x97, 0x72, 0x71, 0xf4, 0xa5, 0x4c, 0xde, 0x87, 0xe7, 0xa5, 0x2a, 0x3d, 0x3e, 0x71,
+	0x60, 0x65, 0xfc, 0x7f, 0x46, 0x03, 0x3f, 0x8f, 0xc3, 0x18, 0x71, 0x38, 0x86, 0x98, 0x1f, 0xc3,
+	0x65, 0x2d, 0xa1, 0x9c, 0x5a, 0xc3, 0x0f, 0x86, 0x95, 0x14, 0x1e, 0x4c, 0x95, 0x14, 0x4b, 0x8c,
+	0x8b, 0x65, 0x48, 0xf7, 0x2c, 0xd6, 0x92, 0x07, 0x41, 0x31, 0x5c, 0x62, 0x3b, 0x9b, 0x4d, 0x4d,
+	0xc1, 0x08, 0x57, 0x9a, 0xad, 0x9e, 0x3a, 0xa7, 0xad, 0x5e, 0x97, 0x65, 0x9e, 0xfd, 0xd8, 0x91,
+	0xa0, 0x0d, 0x7e, 0x90, 0xcb, 0x5e, 0x49, 0x32, 0xe0, 0xa0, 0x8c, 0x3c, 0x2a, 0x0d, 0xd7, 0xec,
+	0x71, 0x2f, 0x8e, 0x75, 0x25, 0x71, 0x54, 0xa6, 0xf0, 0x60, 0xaa, 0xa4, 0x70, 0x52, 0x0e, 0x18,
+	0xb5, 0xf8, 0x41, 0x1c, 0x70, 0x26, 0xee, 0xa4, 0xbc, 0x35, 0xc8, 0x82, 0x69, 0x72, 0xe3, 0x98,
+	0xb7, 0x3f, 0xc8, 0xc2, 0xd5, 0x75, 0xa6, 0x4b, 0x2c, 0xdb, 0x4e, 0xcb, 0xb7, 0x6b, 0xff, 0x4f,
+	0xa3, 0xac, 0x3f, 0xc9, 0x00, 0xbc, 0xb5, 0xb3, 0xb3, 0xad, 0x43, 0xe4, 0x16, 0xe4, 0x69, 0x5f,
+	0x67, 0x38, 0xca, 0x4b, 0x6b, 0xa3, 0x57, 0xb2, 0xa2, 0x49, 0x6f, 0x9d, 0x4e, 0xe8, 0xf3, 0x03,
+	0x94, 0xe8, 0xe4, 0x67, 0x61, 0x52, 0x9f, 0x0d, 0x72, 0xac, 0x8a, 0x61, 0x45, 0x41, 0x9f, 0x1f,
+	0xe8, 0xd3, 0x6b, 0x3f, 0xc9, 0xc2, 0xf5, 0x0d, 0x9b, 0x33, 0xb7, 0xc9, 0x59, 0x2f, 0x96, 0x94,
+	0x26, 0xbf, 0x11, 0xa9, 0xf5, 0xa9, 0xfe, 0xfe, 0xc2, 0xd9, 0x62, 0x76, 0x55, 0x2f, 0xda, 0x62,
+	0x9c, 0x86, 0xbb, 0x32, 0x6c, 0x8b, 0x14, 0xf8, 0xfa, 0x90, 0xf7, 0x7a, 0xcc, 0xd0, 0x19, 0x81,
+	0xe6, 0xc8, 0xa3, 0x91, 0xfe, 0x02, 0x62, 0xe5, 0x85, 0xb9, 0x18, 0xb9, 0x0e, 0xa5, 0x3a, 0xf2,
+	0x2d, 0x98, 0xf0, 0x38, 0xe5, 0x7d, 0x3f, 0xc3, 0xb6, 0x7b, 0xd1, 0x8a, 0x25, 0x78, 0x78, 0x40,
+	0xaa, 0x67, 0xd4, 0x4a, 0x6b, 0x3f, 0xc9, 0xc0, 0x7c, 0xba, 0xe0, 0xa6, 0xe9, 0x71, 0xf2, 0x8d,
+	0x81, 0x61, 0x3f, 0x63, 0xaa, 0x44, 0x48, 0xcb, 0x41, 0x9f, 0xd5, 0x8a, 0x8b, 0x7e, 0x4b, 0x64,
+	0xc8, 0x39, 0x14, 0x4c, 0xce, 0xba, 0xbe, 0x97, 0xf0, 0xf6, 0x05, 0xbf, 0x7a, 0x64, 0x57, 0x0a,
+	0x2d, 0xa8, 0x94, 0xd5, 0x3e, 0xca, 0x0e, 0x7b, 0x65, 0x31, 0x2d, 0xa4, 0x13, 0x2f, 0x7c, 0xdc,
+	0x19, 0xaf, 0xf0, 0xd1, 0xe8, 0x47, 0xfa, 0x33, 0x58, 0xfe, 0xf8, 0xcd, 0xc1, 0xf2, 0xc7, 0xdb,
+	0xe3, 0x97, 0x3f, 0x12, 0xa3, 0x30, 0xb4, 0x0a, 0xf2, 0xa3, 0x2c, 0xbc, 0x70, 0xda, 0xaa, 0x21,
+	0xed, 0x60, 0x71, 0x66, 0xc6, 0xbd, 0x0e, 0x71, 0xea, 0x32, 0x24, 0x4b, 0x50, 0xe8, 0x1d, 0x50,
+	0xcf, 0x37, 0xa7, 0xfe, 0xa9, 0x53, 0xd8, 0x16, 0x8d, 0x8f, 0x8f, 0xab, 0x65, 0x65, 0x86, 0xe5,
+	0x23, 0x2a, 0x56, 0x61, 0x58, 0xba, 0xcc, 0xf3, 0x42, 0xc7, 0x2e, 0x30, 0x2c, 0x5b, 0xaa, 0x19,
+	0x7d, 0x3a, 0xe1, 0x30, 0xa1, 0x82, 0x25, 0x9d, 0x4e, 0xde, 0x1c, 0xf9, 0x3d, 0x52, 0x4a, 0x65,
+	0xe1, 0x4b, 0xe9, 0xb8, 0x5b, 0xeb, 0xaa, 0xfd, 0xe5, 0x15, 0xb8, 0x9e, 0x3e, 0x27, 0xa2, 0xef,
+	0x87, 0xcc, 0xf5, 0x4c, 0xc7, 0xd6, 0xa7, 0x4f, 0x58, 0x66, 0x55, 0xcd, 0xe8, 0xd3, 0xc9, 0x4d,
+	0x28, 0xba, 0xac, 0x67, 0x99, 0x06, 0xf5, 0x74, 0xd0, 0x21, 0xb3, 0x8f, 0xa8, 0xdb, 0x30, 0xa0,
+	0x0e, 0xb9, 0xfa, 0x91, 0xfb, 0x3f, 0xbc, 0xfa, 0xf1, 0x67, 0x19, 0xe1, 0xcf, 0xa9, 0x8c, 0xc3,
+	0x80, 0x80, 0x9e, 0x8b, 0x8b, 0xec, 0xd9, 0x8b, 0xca, 0x2f, 0x1c, 0xa2, 0x10, 0x87, 0xf7, 0x85,
+	0x7c, 0x3f, 0x03, 0x95, 0x6e, 0xc2, 0x61, 0xbc, 0xc4, 0xdb, 0x33, 0x2f, 0x9c, 0x1c, 0x57, 0x2b,
+	0x5b, 0x43, 0xf4, 0xe1, 0xd0, 0x9e, 0x90, 0xdf, 0x82, 0x72, 0x4f, 0xac, 0x0b, 0x8f, 0x33, 0xdb,
+	0x60, 0xba, 0x56, 0x37, 0xfa, 0x6a, 0xde, 0x0e, 0xb1, 0x9a, 0xdc, 0xa5, 0x9c, 0xb5, 0x8f, 0x1a,
+	0x33, 0x22, 0xb4, 0x8b, 0x10, 0x30, 0xaa, 0x31, 0x76, 0xe7, 0x66, 0xeb, 0xb2, 0xef, 0xdc, 0x7c,
+	0x37, 0xfd, 0xce, 0x0d, 0xbd, 0x60, 0x0b, 0xf9, 0xf4, 0xee, 0xcd, 0xd3, 0xbb, 0x37, 0x9f, 0xd7,
+	0xdd, 0x9b, 0x9b, 0x50, 0xf4, 0x18, 0xe7, 0xa6, 0xdd, 0xf6, 0x2a, 0xb3, 0xaa, 0x40, 0x27, 0xb4,
+	0x36, 0x75, 0x1b, 0x06, 0x54, 0xf2, 0xf3, 0x50, 0x92, 0x29, 0xb6, 0x65, 0xb7, 0xed, 0x55, 0xe6,
+	0x64, 0xa5, 0x4e, 0x9e, 0xe4, 0x4d, 0xbf, 0x11, 0x43, 0x3a, 0x79, 0x0d, 0xa6, 0x54, 0x29, 0x51,
+	0x1d, 0x41, 0xf2, 0x9e, 0x4c, 0xa9, 0x31, 0x2b, 0x56, 0x70, 0x23, 0xd2, 0x8e, 0x31, 0x2e, 0x11,
+	0xba, 0xb2, 0x20, 0x0f, 0x59, 0xb9, 0x1a, 0x0f, 0x5d, 0xc3, 0x0c, 0x25, 0x46, 0xb8, 0xc8, 0x8b,
+	0x90, 0xe3, 0x96, 0x57, 0xb9, 0x26, 0x99, 0x83, 0x10, 0x63, 0x67, 0xb3, 0x89, 0xa2, 0x7d, 0xfc,
+	0x4b, 0x34, 0xff, 0x93, 0x81, 0x99, 0xc4, 0x3d, 0x0e, 0xa1, 0xb3, 0xef, 0x5a, 0xfa, 0xa4, 0x0c,
+	0x74, 0xee, 0xe2, 0x26, 0x8a, 0x76, 0xf2, 0xbe, 0x8e, 0x63, 0xb2, 0x63, 0xda, 0xa3, 0x7b, 0xcb,
+	0x3b, 0x4d, 0x11, 0xb8, 0x0c, 0x84, 0x30, 0xb7, 0x12, 0xa3, 0x9b, 0x8b, 0xe7, 0x45, 0x4f, 0x1f,
+	0xe1, 0x48, 0x72, 0x20, 0x7f, 0x96, 0xe4, 0x40, 0xed, 0xd3, 0x0c, 0x94, 0xee, 0xd2, 0xfd, 0x0e,
+	0x6d, 0x9a, 0x76, 0x87, 0xbc, 0x0c, 0x93, 0x7b, 0xae, 0xd3, 0x61, 0xae, 0xa7, 0xab, 0xbf, 0xb2,
+	0xa4, 0xd8, 0x50, 0x4d, 0xe8, 0xd3, 0x44, 0x3c, 0xca, 0x9d, 0x9e, 0x69, 0x24, 0xe3, 0xd1, 0x1d,
+	0xd1, 0x88, 0x8a, 0x46, 0x1e, 0xa8, 0xb9, 0xcb, 0x8d, 0x79, 0x13, 0x73, 0x67, 0xb3, 0xa9, 0x2a,
+	0xaa, 0xfe, 0xac, 0x93, 0x57, 0x62, 0xfe, 0x55, 0x69, 0xa8, 0x47, 0xf4, 0xdd, 0x2c, 0x94, 0xd5,
+	0xab, 0xa9, 0x08, 0xf4, 0x22, 0x5f, 0xee, 0x0d, 0x99, 0x3c, 0xf7, 0xfa, 0x5d, 0xe6, 0xae, 0xbb,
+	0x4e, 0xbf, 0xa7, 0x67, 0x29, 0x9a, 0x0c, 0x09, 0x89, 0x41, 0x02, 0x3d, 0x6c, 0xf2, 0x47, 0x27,
+	0x7f, 0x89, 0xa3, 0x53, 0x38, 0x75, 0x74, 0xfe, 0x2a, 0x03, 0xa5, 0x4d, 0x73, 0x9f, 0x19, 0x47,
+	0x86, 0xc5, 0xc8, 0x37, 0xa0, 0xd2, 0x62, 0x16, 0xe3, 0x6c, 0xdd, 0xa5, 0x06, 0xdb, 0x66, 0xae,
+	0x29, 0x4d, 0xbd, 0x63, 0xb7, 0x94, 0x37, 0x5e, 0x08, 0x32, 0x16, 0x95, 0xd5, 0x21, 0x7c, 0x38,
+	0x14, 0x81, 0x6c, 0xc0, 0x54, 0x8b, 0x79, 0xa6, 0xcb, 0x5a, 0xdb, 0x11, 0xbf, 0xfb, 0x65, 0x7f,
+	0x49, 0xaf, 0x46, 0x68, 0x8f, 0x8f, 0xab, 0xd3, 0xdb, 0x66, 0x8f, 0x59, 0xa6, 0xcd, 0x94, 0x03,
+	0x1e, 0x13, 0xad, 0x15, 0x20, 0xb7, 0xe9, 0xb4, 0x6b, 0xbf, 0x9b, 0x83, 0xe0, 0x0c, 0x27, 0xbf,
+	0x97, 0x81, 0x32, 0xb5, 0x6d, 0x87, 0xeb, 0xc3, 0x51, 0xa5, 0xef, 0x71, 0x6c, 0x57, 0xa1, 0xbe,
+	0x1c, 0x82, 0xaa, 0x93, 0x3a, 0x38, 0x50, 0x23, 0x14, 0x8c, 0xea, 0x26, 0xfd, 0x44, 0x32, 0x7a,
+	0x6b, 0xfc, 0x5e, 0x9c, 0x21, 0xf5, 0x3c, 0xff, 0x75, 0x98, 0x4d, 0x76, 0xf6, 0x3c, 0x86, 0x70,
+	0x9c, 0xb4, 0xd7, 0x9f, 0x66, 0xa0, 0xe8, 0x1b, 0x33, 0xb2, 0x02, 0xf9, 0xbe, 0xc7, 0xdc, 0xf3,
+	0xdd, 0x41, 0x94, 0x16, 0x70, 0xd7, 0x63, 0x2e, 0x4a, 0x61, 0xf2, 0x36, 0x14, 0x7b, 0xd4, 0xf3,
+	0x1e, 0x3a, 0x6e, 0x4b, 0x9b, 0xd9, 0x33, 0x02, 0xa9, 0xb3, 0x59, 0x8b, 0x62, 0x00, 0x52, 0xfb,
+	0xe1, 0x34, 0x94, 0xef, 0x51, 0x6e, 0x1e, 0x32, 0x19, 0x0f, 0x5f, 0x4e, 0x40, 0xf4, 0xbd, 0x0c,
+	0x5c, 0x8f, 0x67, 0xae, 0x2f, 0x31, 0x2a, 0x9a, 0x3f, 0x39, 0xae, 0x5e, 0xc7, 0x54, 0x6d, 0x38,
+	0xa4, 0x17, 0x32, 0x3e, 0x1a, 0x48, 0x84, 0x5f, 0x76, 0x7c, 0xd4, 0x1c, 0xa6, 0x10, 0x87, 0xf7,
+	0xe5, 0x69, 0x7c, 0x34, 0x42, 0x7c, 0x74, 0xe9, 0xdf, 0x24, 0x7c, 0x27, 0x3d, 0x3e, 0xba, 0x3f,
+	0xba, 0x07, 0x14, 0xee, 0xc8, 0xa7, 0x41, 0xd1, 0xd3, 0xa0, 0xe8, 0xf3, 0x0a, 0x8a, 0x7a, 0x89,
+	0xa0, 0x68, 0x9c, 0x62, 0x84, 0xae, 0xf2, 0x2b, 0xb4, 0x61, 0xc1, 0xd5, 0xf8, 0x61, 0xca, 0x1f,
+	0x67, 0xe1, 0x6a, 0x8a, 0x75, 0x20, 0x6f, 0xc2, 0xac, 0xc7, 0x1d, 0x97, 0xb6, 0x59, 0x38, 0xa1,
+	0xea, 0x40, 0x93, 0x37, 0x40, 0x9b, 0x09, 0x1a, 0x0e, 0x70, 0x93, 0xf7, 0x01, 0xa8, 0x61, 0x30,
+	0xcf, 0xdb, 0x72, 0x5a, 0xbe, 0x5f, 0xf6, 0x86, 0x08, 0x17, 0x96, 0x83, 0xd6, 0xc7, 0xc7, 0xd5,
+	0x2f, 0xa5, 0x15, 0x8c, 0xfc, 0xfe, 0x70, 0x75, 0xd7, 0x3b, 0x14, 0xc0, 0x08, 0x24, 0xf9, 0x75,
+	0x00, 0x75, 0xfb, 0x3b, 0xb8, 0xa7, 0xf8, 0x84, 0xac, 0x7e, 0xdd, 0xbf, 0x5d, 0x5d, 0xff, 0xd5,
+	0x3e, 0xb5, 0xb9, 0x58, 0x15, 0xf2, 0xee, 0xee, 0xfd, 0x00, 0x05, 0x23, 0x88, 0xb5, 0x7f, 0xc8,
+	0x42, 0xd1, 0xf7, 0x17, 0x3f, 0x87, 0xba, 0x4d, 0x3b, 0x56, 0xb7, 0x19, 0xfd, 0x23, 0x14, 0xbf,
+	0xcb, 0x43, 0x2b, 0x35, 0x4e, 0xa2, 0x52, 0xb3, 0x3e, 0xbe, 0xaa, 0xd3, 0x6b, 0x33, 0x3f, 0xc8,
+	0xc2, 0x15, 0x9f, 0x55, 0xdf, 0x4f, 0xfe, 0x0a, 0x4c, 0xbb, 0x8c, 0xb6, 0x1a, 0x94, 0x1b, 0x07,
+	0x72, 0xfa, 0xd4, 0xed, 0xe4, 0xb9, 0x93, 0xe3, 0xea, 0x34, 0x46, 0x09, 0x18, 0xe7, 0x4b, 0xbb,
+	0xd8, 0x9c, 0x1d, 0xf3, 0x62, 0x73, 0xee, 0x3c, 0x17, 0x9b, 0x09, 0x85, 0xb2, 0xe8, 0xd1, 0x8e,
+	0xd9, 0x65, 0x4e, 0x9f, 0x6b, 0xdf, 0xe6, 0xbc, 0xf7, 0x6e, 0xe5, 0xd9, 0x8b, 0x21, 0x0c, 0x46,
+	0x31, 0x6b, 0xff, 0x9c, 0x81, 0xa9, 0x70, 0xbc, 0x2e, 0xbd, 0x7a, 0xb5, 0x1f, 0xaf, 0x5e, 0x2d,
+	0x8f, 0xbd, 0x1c, 0x86, 0xd4, 0xab, 0xbe, 0x5f, 0x08, 0x5f, 0x4b, 0x56, 0xa8, 0xf6, 0x60, 0xde,
+	0x4c, 0xad, 0xda, 0x44, 0xac, 0x4d, 0x70, 0x15, 0x6d, 0x63, 0x28, 0x27, 0x9e, 0x82, 0x42, 0xfa,
+	0x50, 0x3c, 0x64, 0x2e, 0x37, 0x0d, 0xe6, 0xbf, 0xdf, 0xfa, 0x05, 0x7d, 0xe9, 0x18, 0x8e, 0xe9,
+	0x7d, 0xad, 0x00, 0x03, 0x55, 0x64, 0x0f, 0x0a, 0xac, 0xd5, 0x66, 0xfe, 0xd5, 0xf3, 0xd7, 0xc7,
+	0xba, 0xf6, 0x1f, 0x8e, 0xa7, 0x78, 0xf2, 0x50, 0x41, 0x13, 0x0f, 0x4a, 0x96, 0x1f, 0x65, 0xeb,
+	0x75, 0xd8, 0x18, 0x59, 0x4f, 0x10, 0xaf, 0x87, 0x57, 0x41, 0x83, 0x26, 0x0c, 0xf5, 0x90, 0x4e,
+	0xf0, 0x41, 0x43, 0xe1, 0x82, 0x8c, 0xc7, 0x29, 0x9f, 0xcb, 0x79, 0x50, 0x7a, 0x48, 0x39, 0x73,
+	0xbb, 0xd4, 0xed, 0x68, 0x1f, 0x78, 0xf4, 0x37, 0x7c, 0xe0, 0x23, 0x85, 0x6f, 0x18, 0x34, 0x61,
+	0xa8, 0xa7, 0xf6, 0xc3, 0x88, 0xb5, 0xfa, 0xbc, 0xcb, 0x87, 0xaf, 0xc5, 0xcb, 0x87, 0x0b, 0xc9,
+	0xf2, 0x61, 0x22, 0x7f, 0x71, 0xfe, 0x02, 0x22, 0x85, 0xb2, 0x45, 0x3d, 0xbe, 0xdb, 0x6b, 0x51,
+	0xae, 0x13, 0x79, 0xe5, 0xa5, 0x9f, 0x3b, 0x9b, 0x31, 0x11, 0xe6, 0x29, 0x74, 0x71, 0x37, 0x43,
+	0x18, 0x8c, 0x62, 0xd6, 0xfe, 0x23, 0x03, 0x73, 0x03, 0x25, 0x63, 0x72, 0x00, 0x13, 0xb6, 0x74,
+	0xca, 0xc7, 0xfe, 0x0e, 0x2f, 0xe2, 0xdb, 0xab, 0x45, 0xa3, 0x1b, 0x34, 0x3e, 0xb1, 0xa1, 0xc8,
+	0x1e, 0x71, 0xe6, 0xda, 0xd4, 0xd2, 0x67, 0xe9, 0xc5, 0x7c, 0xf3, 0x27, 0x5d, 0xb0, 0xdb, 0x1a,
+	0x19, 0x03, 0x1d, 0xb5, 0x9f, 0x66, 0xa1, 0x1c, 0xe1, 0x7b, 0x52, 0x92, 0x57, 0x5e, 0x45, 0x54,
+	0xd1, 0xe9, 0xae, 0x6b, 0xe9, 0x89, 0x8e, 0x5c, 0x45, 0xd4, 0x24, 0xdc, 0xc4, 0x28, 0x1f, 0x59,
+	0x02, 0xe8, 0x52, 0x8f, 0x33, 0x57, 0xda, 0xc6, 0xc4, 0x05, 0xc0, 0xad, 0x80, 0x82, 0x11, 0x2e,
+	0x72, 0x43, 0x67, 0x4c, 0xf2, 0xf1, 0x0f, 0x68, 0x86, 0xa4, 0x43, 0x0a, 0x17, 0x90, 0x0e, 0x21,
+	0x6d, 0x98, 0xf5, 0x7b, 0xed, 0x53, 0xf5, 0xc6, 0x3d, 0x23, 0xb0, 0xf2, 0x2e, 0x13, 0x10, 0x38,
+	0x00, 0x5a, 0xfb, 0xeb, 0x0c, 0x4c, 0xc7, 0x5c, 0x64, 0xf2, 0x52, 0xf4, 0xbe, 0x43, 0x24, 0xb9,
+	0x1a, 0xbb, 0xa7, 0xf0, 0x0a, 0x4c, 0xa8, 0x01, 0xd2, 0x03, 0x1f, 0x6c, 0x44, 0x35, 0x84, 0xa8,
+	0xa9, 0x62, 0x4b, 0xe9, 0xec, 0x4b, 0x72, 0x4b, 0xe9, 0xf4, 0x0c, 0xfa, 0x74, 0xf2, 0x45, 0xe1,
+	0xf4, 0xab, 0xde, 0xe9, 0x91, 0x0e, 0x0e, 0x06, 0xff, 0x3d, 0x30, 0xe0, 0xa8, 0x7d, 0x2f, 0x0f,
+	0xea, 0x6b, 0x5d, 0x21, 0xd7, 0x32, 0x3d, 0x95, 0x50, 0xcf, 0xc8, 0x84, 0x7a, 0x20, 0xb7, 0xaa,
+	0xdb, 0x31, 0xe0, 0x20, 0xcf, 0x43, 0xae, 0x6b, 0xda, 0x3a, 0x4f, 0x24, 0xd3, 0xb2, 0x5b, 0xa6,
+	0x8d, 0xa2, 0x4d, 0x92, 0xe8, 0x23, 0x7d, 0xf3, 0x56, 0x91, 0xe8, 0x23, 0x14, 0x6d, 0xc2, 0x5b,
+	0xb2, 0x1c, 0xa7, 0xb3, 0x47, 0x8d, 0x8e, 0x9f, 0x72, 0xcd, 0x4b, 0x6f, 0x47, 0x7a, 0x4b, 0x9b,
+	0x71, 0x12, 0x26, 0x79, 0x85, 0xb8, 0xe1, 0x38, 0x56, 0xcb, 0x79, 0x68, 0xfb, 0xe2, 0x85, 0x50,
+	0x7c, 0x25, 0x4e, 0xc2, 0x24, 0x2f, 0xd9, 0x85, 0xe7, 0x3e, 0x64, 0xae, 0xa3, 0x47, 0xac, 0x69,
+	0x31, 0xd6, 0xf3, 0x61, 0x26, 0x24, 0xcc, 0x17, 0x4e, 0x8e, 0xab, 0xcf, 0xbd, 0x9b, 0xce, 0x82,
+	0xc3, 0x64, 0x05, 0x2c, 0xa7, 0x6e, 0x9b, 0xf1, 0x6d, 0xd7, 0x11, 0xc1, 0x80, 0x69, 0xb7, 0x7d,
+	0xd8, 0xc9, 0x10, 0x76, 0x27, 0x9d, 0x05, 0x87, 0xc9, 0x92, 0x15, 0x98, 0x53, 0xa4, 0x88, 0x13,
+	0x28, 0xef, 0x80, 0x4e, 0xab, 0x8b, 0x02, 0x3b, 0x49, 0x22, 0x0e, 0xf2, 0x0b, 0xff, 0xd2, 0xcf,
+	0xda, 0x6d, 0x33, 0x57, 0x4e, 0xb4, 0xbc, 0xee, 0xa9, 0xfd, 0x4b, 0x4c, 0xd0, 0x70, 0x80, 0x5b,
+	0x7e, 0xf4, 0x28, 0x0b, 0x26, 0x0f, 0x20, 0x67, 0x39, 0x6d, 0x6d, 0x2e, 0x47, 0x4f, 0xe3, 0x6f,
+	0x3a, 0x6d, 0xb5, 0x28, 0x36, 0x9d, 0x36, 0x0a, 0x44, 0x62, 0x40, 0xa1, 0x43, 0xf7, 0x3b, 0x54,
+	0x5b, 0xc7, 0xd1, 0x4f, 0xd4, 0xa0, 0xb8, 0xa3, 0xbf, 0xe4, 0x13, 0x8f, 0xa8, 0xb0, 0x89, 0x01,
+	0x13, 0xfd, 0x96, 0xfc, 0x77, 0x90, 0x71, 0xff, 0x80, 0x64, 0x77, 0x55, 0xaa, 0x90, 0xa6, 0x5e,
+	0xfd, 0x46, 0x0d, 0x5d, 0xfb, 0xc7, 0x2c, 0xe8, 0xff, 0x24, 0x21, 0x7d, 0x28, 0xb5, 0xfd, 0x2f,
+	0xe7, 0xf4, 0x98, 0xbd, 0x35, 0xc6, 0x65, 0xed, 0xd8, 0x37, 0x78, 0xaa, 0x5e, 0x19, 0x34, 0x62,
+	0xa8, 0x89, 0xb0, 0xf8, 0x58, 0xae, 0x8e, 0x39, 0x96, 0x4a, 0xdd, 0xe0, 0x68, 0x52, 0xc8, 0x1f,
+	0x70, 0xde, 0xd3, 0x63, 0x39, 0xfa, 0x47, 0xa4, 0xe1, 0xa5, 0x49, 0x95, 0x19, 0x17, 0xcf, 0x28,
+	0xa1, 0x6b, 0x5d, 0xd0, 0xce, 0x08, 0x31, 0x62, 0xdf, 0xcb, 0xaa, 0x8a, 0xc7, 0xe2, 0xd9, 0x5c,
+	0x84, 0xe0, 0x5b, 0xcf, 0xc8, 0x67, 0x34, 0xa9, 0x1f, 0xc6, 0xd6, 0xfe, 0x35, 0x0b, 0xb9, 0x9d,
+	0xcd, 0xa6, 0xba, 0x15, 0x2e, 0xd3, 0x57, 0xac, 0xd9, 0x31, 0x7b, 0xf7, 0x99, 0x6b, 0xee, 0x1f,
+	0x69, 0x7b, 0x18, 0xb9, 0x15, 0x9e, 0xe4, 0xc0, 0x14, 0x29, 0xf2, 0x1e, 0x4c, 0x19, 0x74, 0x85,
+	0xb9, 0x5c, 0x1d, 0x2b, 0xe7, 0x4b, 0xf0, 0xcb, 0x1a, 0xf3, 0xca, 0x72, 0x28, 0x8e, 0x31, 0x30,
+	0xb2, 0x0b, 0x60, 0x84, 0xd0, 0xb9, 0xf3, 0x40, 0xab, 0x0f, 0x84, 0x43, 0xe0, 0x08, 0x10, 0x41,
+	0x28, 0x75, 0x04, 0xab, 0x44, 0xcd, 0x9f, 0x07, 0x55, 0x2e, 0xca, 0xbb, 0xbe, 0x2c, 0x86, 0x30,
+	0xb5, 0x1f, 0x67, 0x20, 0xb7, 0xbb, 0xba, 0x46, 0x1c, 0x28, 0x05, 0x57, 0x99, 0xf4, 0x9e, 0x68,
+	0x8c, 0x9f, 0xdb, 0x56, 0x8a, 0x83, 0x47, 0x0c, 0x75, 0x90, 0x03, 0x98, 0xdc, 0xeb, 0x9b, 0x16,
+	0x37, 0x6d, 0x99, 0xfc, 0x1b, 0x27, 0x96, 0xf4, 0x3f, 0xb9, 0xd5, 0x95, 0x54, 0x85, 0x8a, 0x3e,
+	0x7c, 0xed, 0x5b, 0xa0, 0x6d, 0x81, 0x88, 0x11, 0x2e, 0xe3, 0x25, 0x83, 0x18, 0x21, 0xed, 0x45,
+	0x6b, 0x7f, 0x9f, 0x85, 0x09, 0xfd, 0x5f, 0x4c, 0x97, 0x9f, 0x18, 0x62, 0xb1, 0xc4, 0xd0, 0xca,
+	0x98, 0xff, 0x4e, 0x32, 0x34, 0x2d, 0xd4, 0x4d, 0xa4, 0x85, 0xc6, 0xfd, 0x1b, 0x94, 0x27, 0x24,
+	0x85, 0xfe, 0x29, 0x03, 0x57, 0x14, 0xe3, 0x86, 0xed, 0x71, 0x6a, 0x1b, 0xf2, 0x4f, 0xab, 0x0e,
+	0x65, 0xcb, 0xd8, 0x61, 0x96, 0x8e, 0xd0, 0xe5, 0x99, 0xa1, 0x7e, 0xa3, 0x86, 0x16, 0x6e, 0xd7,
+	0x81, 0xe3, 0x71, 0xf9, 0x65, 0x79, 0x36, 0xee, 0xae, 0xbd, 0xa5, 0xdb, 0x31, 0xe0, 0x48, 0xfa,
+	0x81, 0x85, 0xe1, 0x7e, 0x60, 0xed, 0x2f, 0x32, 0x30, 0x15, 0xfd, 0x03, 0x98, 0xd1, 0x73, 0x5c,
+	0x89, 0x14, 0x53, 0xf6, 0x12, 0x52, 0x4c, 0x9f, 0x64, 0x00, 0xfc, 0xce, 0x5e, 0x7a, 0x82, 0xa9,
+	0x15, 0x4f, 0x30, 0x8d, 0x3d, 0xad, 0xe9, 0xe9, 0xa5, 0xbf, 0xc9, 0xfb, 0xaf, 0x24, 0x93, 0x4b,
+	0x1f, 0x65, 0xe0, 0x0a, 0x8d, 0x25, 0x6c, 0xf4, 0x9b, 0x5d, 0x58, 0xfe, 0xe7, 0xba, 0xee, 0x46,
+	0xe2, 0xdf, 0xd9, 0x30, 0xa1, 0x96, 0xdc, 0x82, 0xa9, 0x9e, 0x0e, 0xdb, 0xef, 0x85, 0xab, 0x2e,
+	0x28, 0x42, 0x6d, 0x47, 0x68, 0x18, 0xe3, 0x7c, 0x42, 0x82, 0x2c, 0x77, 0x21, 0x09, 0xb2, 0x68,
+	0x15, 0x3a, 0x7f, 0x6a, 0x15, 0xda, 0x86, 0xd2, 0xbe, 0xeb, 0x74, 0x65, 0x0e, 0x4a, 0xff, 0x6b,
+	0xcb, 0x98, 0x79, 0xad, 0xc0, 0xc8, 0xae, 0xf9, 0xb8, 0x18, 0xaa, 0x10, 0xa7, 0x09, 0x77, 0x94,
+	0xb6, 0x89, 0x8b, 0xd0, 0x16, 0x6c, 0xdd, 0x1d, 0x85, 0x8a, 0x3e, 0x7c, 0xed, 0x5f, 0xb2, 0xfe,
+	0xd6, 0x6d, 0x26, 0xae, 0x71, 0x67, 0x86, 0x5c, 0xe3, 0xd6, 0x5f, 0xe2, 0x44, 0xb3, 0x30, 0xaf,
+	0xc0, 0x84, 0xcb, 0xa8, 0xe7, 0xd8, 0xfa, 0x9b, 0xb6, 0xc0, 0xf0, 0xa1, 0x6c, 0x45, 0x4d, 0x8d,
+	0x66, 0x6b, 0xb2, 0x4f, 0xc8, 0xd6, 0x7c, 0x31, 0x32, 0x37, 0x2a, 0x4b, 0x1d, 0x6c, 0xb3, 0x94,
+	0xf9, 0x91, 0x81, 0xa8, 0x2e, 0xa0, 0x16, 0x92, 0x81, 0xa8, 0x2e, 0x72, 0x06, 0x1c, 0xa4, 0x05,
+	0x53, 0x16, 0xf5, 0xb8, 0x0c, 0x3a, 0x5a, 0xcb, 0x7c, 0x84, 0x54, 0x50, 0xb0, 0x82, 0x37, 0x23,
+	0x38, 0x18, 0x43, 0xad, 0x7d, 0x0d, 0xc2, 0x24, 0x1b, 0x59, 0x84, 0x52, 0xcf, 0x75, 0x7a, 0xb4,
+	0x4d, 0x39, 0xd3, 0x2e, 0x5e, 0xb0, 0x02, 0xb6, 0x7d, 0x02, 0x86, 0x3c, 0x8d, 0xfa, 0xc7, 0x9f,
+	0x2d, 0x3c, 0xf3, 0xc9, 0x67, 0x0b, 0xcf, 0x7c, 0xfa, 0xd9, 0xc2, 0x33, 0xbf, 0x7d, 0xb2, 0x90,
+	0xf9, 0xf8, 0x64, 0x21, 0xf3, 0xc9, 0xc9, 0x42, 0xe6, 0xd3, 0x93, 0x85, 0xcc, 0x8f, 0x4f, 0x16,
+	0x32, 0xdf, 0xf9, 0xb7, 0x85, 0x67, 0xde, 0x2d, 0xfa, 0xd3, 0xfc, 0xbf, 0x01, 0x00, 0x00, 0xff,
+	0xff, 0xe1, 0x26, 0x2a, 0x0c, 0xd5, 0x52, 0x00, 0x00,
 }
 
 func (m *AbstractVertex) Marshal() (dAtA []byte, err error) {
@@ -4596,21 +4604,54 @@ func (m *Scale) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.ReplicasPerScale != nil {
+		i = encodeVarintGenerated(dAtA, i, uint64(*m.ReplicasPerScale))
+		i--
+		dAtA[i] = 0x48
+	}
+	if m.TargetBufferUsage != nil {
+		i = encodeVarintGenerated(dAtA, i, uint64(*m.TargetBufferUsage))
+		i--
+		dAtA[i] = 0x40
+	}
+	if m.TargetProcessingSeconds != nil {
+		i = encodeVarintGenerated(dAtA, i, uint64(*m.TargetProcessingSeconds))
+		i--
+		dAtA[i] = 0x38
+	}
+	if m.ZeroReplicaSleepSeconds != nil {
+		i = encodeVarintGenerated(dAtA, i, uint64(*m.ZeroReplicaSleepSeconds))
+		i--
+		dAtA[i] = 0x30
+	}
+	if m.CooldownSeconds != nil {
+		i = encodeVarintGenerated(dAtA, i, uint64(*m.CooldownSeconds))
+		i--
+		dAtA[i] = 0x28
+	}
 	if m.LookbackSeconds != nil {
 		i = encodeVarintGenerated(dAtA, i, uint64(*m.LookbackSeconds))
 		i--
-		dAtA[i] = 0x18
+		dAtA[i] = 0x20
 	}
 	if m.Max != nil {
 		i = encodeVarintGenerated(dAtA, i, uint64(*m.Max))
 		i--
-		dAtA[i] = 0x10
+		dAtA[i] = 0x18
 	}
 	if m.Min != nil {
 		i = encodeVarintGenerated(dAtA, i, uint64(*m.Min))
 		i--
-		dAtA[i] = 0x8
+		dAtA[i] = 0x10
 	}
+	i--
+	if m.Disabled {
+		dAtA[i] = 1
+	} else {
+		dAtA[i] = 0
+	}
+	i--
+	dAtA[i] = 0x8
 	return len(dAtA) - i, nil
 }
 
@@ -6266,6 +6307,7 @@ func (m *Scale) Size() (n int) {
 	}
 	var l int
 	_ = l
+	n += 2
 	if m.Min != nil {
 		n += 1 + sovGenerated(uint64(*m.Min))
 	}
@@ -6275,6 +6317,21 @@ func (m *Scale) Size() (n int) {
 	if m.LookbackSeconds != nil {
 		n += 1 + sovGenerated(uint64(*m.LookbackSeconds))
 	}
+	if m.CooldownSeconds != nil {
+		n += 1 + sovGenerated(uint64(*m.CooldownSeconds))
+	}
+	if m.ZeroReplicaSleepSeconds != nil {
+		n += 1 + sovGenerated(uint64(*m.ZeroReplicaSleepSeconds))
+	}
+	if m.TargetProcessingSeconds != nil {
+		n += 1 + sovGenerated(uint64(*m.TargetProcessingSeconds))
+	}
+	if m.TargetBufferUsage != nil {
+		n += 1 + sovGenerated(uint64(*m.TargetBufferUsage))
+	}
+	if m.ReplicasPerScale != nil {
+		n += 1 + sovGenerated(uint64(*m.ReplicasPerScale))
+	}
 	return n
 }
 
@@ -7252,9 +7309,15 @@ func (this *Scale) String() string {
 		return "nil"
 	}
 	s := strings.Join([]string{`&Scale{`,
+		`Disabled:` + fmt.Sprintf("%v", this.Disabled) + `,`,
 		`Min:` + valueToStringGenerated(this.Min) + `,`,
 		`Max:` + valueToStringGenerated(this.Max) + `,`,
 		`LookbackSeconds:` + valueToStringGenerated(this.LookbackSeconds) + `,`,
+		`CooldownSeconds:` + valueToStringGenerated(this.CooldownSeconds) + `,`,
+		`ZeroReplicaSleepSeconds:` + valueToStringGenerated(this.ZeroReplicaSleepSeconds) + `,`,
+		`TargetProcessingSeconds:` + valueToStringGenerated(this.TargetProcessingSeconds) + `,`,
+		`TargetBufferUsage:` + valueToStringGenerated(this.TargetBufferUsage) + `,`,
+		`ReplicasPerScale:` + valueToStringGenerated(this.ReplicasPerScale) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -16412,6 +16475,26 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
 		}
 		switch fieldNum {
 		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Disabled", 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.Disabled = bool(v != 0)
+		case 2:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Min", wireType)
 			}
@@ -16431,7 +16514,7 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
 				}
 			}
 			m.Min = &v
-		case 2:
+		case 3:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType)
 			}
@@ -16451,11 +16534,11 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
 				}
 			}
 			m.Max = &v
-		case 3:
+		case 4:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field LookbackSeconds", wireType)
 			}
-			var v int32
+			var v uint32
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowGenerated
@@ -16465,12 +16548,112 @@ func (m *Scale) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				v |= int32(b&0x7F) << shift
+				v |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
 			m.LookbackSeconds = &v
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field CooldownSeconds", 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.CooldownSeconds = &v
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ZeroReplicaSleepSeconds", 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.ZeroReplicaSleepSeconds = &v
+		case 7:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TargetProcessingSeconds", 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.TargetProcessingSeconds = &v
+		case 8:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TargetBufferUsage", 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.TargetBufferUsage = &v
+		case 9:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ReplicasPerScale", 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.ReplicasPerScale = &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 467d66c9..de26295b 100644
--- a/pkg/apis/numaflow/v1alpha1/generated.proto
+++ b/pkg/apis/numaflow/v1alpha1/generated.proto
@@ -108,6 +108,7 @@ message AbstractVertex {
   // +optional
   optional VertexLimits limits = 17;
 
+  // Settings for autoscaling
   // +optional
   optional Scale scale = 18;
 }
@@ -798,20 +799,47 @@ message RedisSettings {
 }
 
 message Scale {
-  // Minimal replicas
-  // +kubebuilder:default=1
+  // Whether to disable autoscaling.
+  // Set to "true" when using Kubernetes HPA or any other 3rd party autoscaling strategies.
+  // +optional
+  optional bool disabled = 1;
+
+  // Minimum replicas.
+  // +optional
+  optional int32 min = 2;
+
+  // Maximum replicas.
+  // +optional
+  optional int32 max = 3;
+
+  // Lookback seconds to calculate the average pending messages and processing rate.
+  // +optional
+  optional uint32 lookbackSeconds = 4;
+
+  // Cooldown seconds after a scaling operation before another one.
+  // +optional
+  optional uint32 cooldownSeconds = 5;
+
+  // After scaling down to 0, sleep how many seconds before scaling up to peek.
+  // +optional
+  optional uint32 zeroReplicaSleepSeconds = 6;
+
+  // 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 int32 min = 1;
+  optional uint32 targetProcessingSeconds = 7;
 
-  // Maximum replicas
-  // +kubebuilder:default=100
+  // TargetBufferUsage is used to define the target pencentage of usage of the buffer to be read.
+  // 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 as only they have buffers to read.
   // +optional
-  optional int32 max = 2;
+  optional uint32 targetBufferUsage = 8;
 
-  // Lookback seconds to calculate the average pending messages and processing rate
-  // +kubebuilder:default=180
+  // ReplicasPerScale defines maximum replicas can be scaled up or down at once.
+  // The is use to prevent too aggresive scaling operations
   // +optional
-  optional int32 lookbackSeconds = 3;
+  optional uint32 replicasPerScale = 9;
 }
 
 message Sink {
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types.go b/pkg/apis/numaflow/v1alpha1/vertex_types.go
index 287ac18e..4fff97c8 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types.go
@@ -64,10 +64,26 @@ func (v Vertex) IsASink() bool {
 	return v.Spec.Sink != nil
 }
 
-func (v Vertex) IsAnUDF() bool {
+func (v Vertex) IsUDF() bool {
 	return v.Spec.Sink == nil && v.Spec.Source == nil
 }
 
+func (v Vertex) Scalable() bool {
+	if v.Spec.Scale.Disabled {
+		return false
+	}
+	if v.IsASink() || v.IsUDF() {
+		return true
+	}
+	if v.IsASource() {
+		src := v.Spec.Source
+		if src.Kafka != nil {
+			return true
+		}
+	}
+	return false
+}
+
 func (v Vertex) GetHeadlessServiceName() string {
 	return v.Name + "-headless"
 }
@@ -361,23 +377,103 @@ type AbstractVertex struct {
 	// Limits define the limitations such as buffer read batch size for all the vertices of a pipleine, will override pipeline level settings
 	// +optional
 	Limits *VertexLimits `json:"limits,omitempty" protobuf:"bytes,17,opt,name=limits"`
+	// Settings for autoscaling
 	// +optional
 	Scale Scale `json:"scale,omitempty" protobuf:"bytes,18,opt,name=scale"`
 }
 
 type Scale struct {
-	// Minimal replicas
-	// +kubebuilder:default=1
+	// Whether to disable autoscaling.
+	// Set to "true" when using Kubernetes HPA or any other 3rd party autoscaling strategies.
+	// +optional
+	Disabled bool `json:"disabled,omitempty" protobuf:"bytes,1,opt,name=disabled"`
+	// Minimum replicas.
+	// +optional
+	Min *int32 `json:"min,omitempty" protobuf:"varint,2,opt,name=min"`
+	// Maximum replicas.
 	// +optional
-	Min *int32 `json:"min,omitempty" protobuf:"varint,1,opt,name=min"`
-	// Maximum replicas
-	// +kubebuilder:default=100
+	Max *int32 `json:"max,omitempty" protobuf:"varint,3,opt,name=max"`
+	// Lookback seconds to calculate the average pending messages and processing rate.
 	// +optional
-	Max *int32 `json:"max,omitempty" protobuf:"varint,2,opt,name=max"`
-	// Lookback seconds to calculate the average pending messages and processing rate
-	// +kubebuilder:default=180
+	LookbackSeconds *uint32 `json:"lookbackSeconds,omitempty" protobuf:"varint,4,opt,name=lookbackSeconds"`
+	// Cooldown seconds after a scaling operation before another one.
 	// +optional
-	LookbackSeconds *int32 `json:"lookbackSeconds,omitempty" protobuf:"varint,3,opt,name=lookbackSeconds"`
+	CooldownSeconds *uint32 `json:"cooldownSeconds,omitempty" protobuf:"varint,5,opt,name=cooldownSeconds"`
+	// After scaling down to 0, sleep how many seconds before scaling up to peek.
+	// +optional
+	ZeroReplicaSleepSeconds *uint32 `json:"zeroReplicaSleepSeconds,omitempty" protobuf:"varint,6,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"`
+	// TargetBufferUsage is used to define the target pencentage of usage of the buffer to be read.
+	// 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 as only they have buffers to read.
+	// +optional
+	TargetBufferUsage *uint32 `json:"targetBufferUsage,omitempty" protobuf:"varint,8,opt,name=targetBufferUsage"`
+	// ReplicasPerScale defines maximum replicas can be scaled up or down at once.
+	// The is use to prevent too aggresive scaling operations
+	// +optional
+	ReplicasPerScale *uint32 `json:"replicasPerScale,omitempty" protobuf:"varint,9,opt,name=replicasPerScale"`
+}
+
+func (s Scale) GetLookbackSeconds() int {
+	if s.LookbackSeconds != nil {
+		return int(*s.LookbackSeconds)
+	}
+	return DefaultLookbackSeconds
+}
+
+func (s Scale) GetCooldownSeconds() int {
+	if s.CooldownSeconds != nil {
+		return int(*s.CooldownSeconds)
+	}
+	return DefaultCooldownSeconds
+}
+
+func (s Scale) GetZeroReplicaSleepSeconds() int {
+	if s.ZeroReplicaSleepSeconds != nil {
+		return int(*s.ZeroReplicaSleepSeconds)
+	}
+	return DefaultZeroReplicaSleepSeconds
+}
+
+func (s Scale) GetTargetProcessingSeconds() int {
+	if s.TargetProcessingSeconds != nil {
+		return int(*s.TargetProcessingSeconds)
+	}
+	return DefaultTargetProcessingSeconds
+}
+
+func (s Scale) GetTargetBufferUsage() int {
+	if s.TargetBufferUsage != nil {
+		return int(*s.TargetBufferUsage)
+	}
+	return DefaultTargetBufferUsage
+}
+
+func (s Scale) GetReplicasPerScale() int {
+	if s.ReplicasPerScale != nil {
+		return int(*s.ReplicasPerScale)
+	}
+	return DefaultReplicasPerScale
+}
+
+func (s Scale) GetMinReplicas() int32 {
+	if x := s.Min; x == nil || *x < 0 {
+		return 0
+	} else {
+		return *x
+	}
+}
+
+func (s Scale) GetMaxReplicas() int32 {
+	if x := s.Max; x == nil || *x > DefaultMaxReplicas {
+		return DefaultMaxReplicas
+	} else {
+		return *x
+	}
 }
 
 type VertexLimits struct {
diff --git a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
index f1350b60..f2f999a9 100644
--- a/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/numaflow/v1alpha1/zz_generated.deepcopy.go
@@ -1251,7 +1251,32 @@ func (in *Scale) DeepCopyInto(out *Scale) {
 	}
 	if in.LookbackSeconds != nil {
 		in, out := &in.LookbackSeconds, &out.LookbackSeconds
-		*out = new(int32)
+		*out = new(uint32)
+		**out = **in
+	}
+	if in.CooldownSeconds != nil {
+		in, out := &in.CooldownSeconds, &out.CooldownSeconds
+		*out = new(uint32)
+		**out = **in
+	}
+	if in.ZeroReplicaSleepSeconds != nil {
+		in, out := &in.ZeroReplicaSleepSeconds, &out.ZeroReplicaSleepSeconds
+		*out = new(uint32)
+		**out = **in
+	}
+	if in.TargetProcessingSeconds != nil {
+		in, out := &in.TargetProcessingSeconds, &out.TargetProcessingSeconds
+		*out = new(uint32)
+		**out = **in
+	}
+	if in.TargetBufferUsage != nil {
+		in, out := &in.TargetBufferUsage, &out.TargetBufferUsage
+		*out = new(uint32)
+		**out = **in
+	}
+	if in.ReplicasPerScale != nil {
+		in, out := &in.ReplicasPerScale, &out.ReplicasPerScale
+		*out = new(uint32)
 		**out = **in
 	}
 	return
diff --git a/pkg/apis/proto/daemon/daemon.pb.go b/pkg/apis/proto/daemon/daemon.pb.go
index c1ef6065..2afecffd 100644
--- a/pkg/apis/proto/daemon/daemon.pb.go
+++ b/pkg/apis/proto/daemon/daemon.pb.go
@@ -164,7 +164,8 @@ func (m *BufferInfo) GetIsFull() bool {
 type VertexMetrics struct {
 	Pipeline             *string            `protobuf:"bytes,1,req,name=pipeline" json:"pipeline,omitempty"`
 	Vertex               *string            `protobuf:"bytes,2,req,name=vertex" json:"vertex,omitempty"`
-	ProcessingRates      map[string]float32 `protobuf:"bytes,3,rep,name=processingRates" json:"processingRates,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"fixed32,2,opt,name=value"`
+	ProcessingRates      map[string]float64 `protobuf:"bytes,3,rep,name=processingRates" json:"processingRates,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"fixed64,2,opt,name=value"`
+	Pendings             map[string]int64   `protobuf:"bytes,4,rep,name=pendings" json:"pendings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"`
 	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
 	XXX_unrecognized     []byte             `json:"-"`
 	XXX_sizecache        int32              `json:"-"`
@@ -217,13 +218,20 @@ func (m *VertexMetrics) GetVertex() string {
 	return ""
 }
 
-func (m *VertexMetrics) GetProcessingRates() map[string]float32 {
+func (m *VertexMetrics) GetProcessingRates() map[string]float64 {
 	if m != nil {
 		return m.ProcessingRates
 	}
 	return nil
 }
 
+func (m *VertexMetrics) GetPendings() map[string]int64 {
+	if m != nil {
+		return m.Pendings
+	}
+	return nil
+}
+
 type ListBuffersRequest struct {
 	Pipeline             *string  `protobuf:"bytes,1,req,name=pipeline" json:"pipeline,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -533,7 +541,8 @@ func (m *GetVertexMetricsResponse) GetVertex() *VertexMetrics {
 func init() {
 	proto.RegisterType((*BufferInfo)(nil), "daemon.BufferInfo")
 	proto.RegisterType((*VertexMetrics)(nil), "daemon.VertexMetrics")
-	proto.RegisterMapType((map[string]float32)(nil), "daemon.VertexMetrics.ProcessingRatesEntry")
+	proto.RegisterMapType((map[string]int64)(nil), "daemon.VertexMetrics.PendingsEntry")
+	proto.RegisterMapType((map[string]float64)(nil), "daemon.VertexMetrics.ProcessingRatesEntry")
 	proto.RegisterType((*ListBuffersRequest)(nil), "daemon.ListBuffersRequest")
 	proto.RegisterType((*ListBuffersResponse)(nil), "daemon.ListBuffersResponse")
 	proto.RegisterType((*GetBufferRequest)(nil), "daemon.GetBufferRequest")
@@ -547,52 +556,53 @@ func init() {
 }
 
 var fileDescriptor_93e327fd0d673221 = []byte{
-	// 705 bytes of a gzipped FileDescriptorProto
+	// 734 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xc1, 0x4e, 0xdb, 0x4a,
-	0x14, 0x95, 0xed, 0x47, 0x20, 0x37, 0x0f, 0xc1, 0x9b, 0xc7, 0xe3, 0x4d, 0x0d, 0x4a, 0x2d, 0x0b,
-	0x55, 0x16, 0x82, 0xb8, 0x45, 0xaa, 0x54, 0x75, 0xd3, 0x0a, 0x5a, 0x10, 0x52, 0x40, 0xc8, 0xa5,
-	0x5d, 0x74, 0x67, 0xc2, 0xc4, 0xb8, 0x89, 0x67, 0x5c, 0xcf, 0x38, 0x14, 0xa1, 0x6c, 0xf8, 0x85,
-	0x7e, 0x46, 0xfb, 0x21, 0x5d, 0x56, 0xea, 0xba, 0x52, 0x85, 0xfa, 0x21, 0x95, 0x67, 0xec, 0x60,
-	0x83, 0x41, 0xac, 0x32, 0xf7, 0xdc, 0x73, 0xef, 0x3d, 0x33, 0xe7, 0x26, 0x01, 0x3b, 0x1e, 0x04,
-	0xae, 0x1f, 0x87, 0xdc, 0x8d, 0x13, 0x26, 0x98, 0x7b, 0xec, 0x93, 0x88, 0xd1, 0xfc, 0xa3, 0x23,
-	0x31, 0xd4, 0x50, 0x91, 0xb9, 0x1c, 0x30, 0x16, 0x0c, 0x49, 0x46, 0x77, 0x7d, 0x4a, 0x99, 0xf0,
-	0x45, 0xc8, 0x28, 0x57, 0x2c, 0x73, 0x29, 0xcf, 0xca, 0xe8, 0x28, 0xed, 0xbb, 0x24, 0x8a, 0xc5,
-	0x99, 0x4a, 0xda, 0x17, 0x06, 0xc0, 0x66, 0xda, 0xef, 0x93, 0x64, 0x97, 0xf6, 0x19, 0x32, 0x61,
-	0x26, 0x0e, 0x63, 0x32, 0x0c, 0x29, 0xc1, 0x9a, 0xa5, 0x3b, 0x4d, 0x6f, 0x12, 0xa3, 0x36, 0x40,
-	0x3f, 0x61, 0xd1, 0x3b, 0x92, 0x08, 0xf2, 0x09, 0xeb, 0x32, 0x5b, 0x42, 0xb2, 0x5a, 0xc1, 0xf2,
-	0xac, 0xa1, 0x6a, 0x8b, 0x38, 0xab, 0x3d, 0x92, 0x53, 0xf6, 0xfd, 0x88, 0xe0, 0xbf, 0x54, 0xed,
-	0x15, 0x82, 0x6c, 0xf8, 0x3b, 0x26, 0xf4, 0x38, 0xa4, 0xc1, 0x16, 0x4b, 0xa9, 0xc0, 0x53, 0x96,
-	0xee, 0x18, 0x5e, 0x05, 0x43, 0x0e, 0xcc, 0xf9, 0xbd, 0xc1, 0x41, 0x99, 0xd6, 0x90, 0xb4, 0xeb,
-	0x30, 0x5a, 0x81, 0x59, 0xc1, 0x84, 0x3f, 0xdc, 0x23, 0x9c, 0xfb, 0x01, 0xe1, 0x78, 0x5a, 0xf2,
-	0xaa, 0x60, 0x36, 0x53, 0x29, 0xe8, 0x12, 0x1a, 0x88, 0x13, 0x3c, 0xa3, 0x66, 0x96, 0x31, 0xb4,
-	0x0a, 0xf3, 0x2a, 0x7e, 0x9b, 0xd5, 0x74, 0xc3, 0x28, 0x14, 0xb8, 0x69, 0xe9, 0x8e, 0xe6, 0xdd,
-	0xc0, 0x91, 0x05, 0xad, 0x12, 0x86, 0x41, 0xd2, 0xca, 0x10, 0x5a, 0x84, 0x46, 0xc8, 0xb7, 0xd3,
-	0xe1, 0x10, 0xb7, 0x2c, 0xdd, 0x99, 0xf1, 0xf2, 0xc8, 0xfe, 0xa9, 0xc1, 0xac, 0x7a, 0xa8, 0x3d,
-	0x22, 0x92, 0xb0, 0xc7, 0xef, 0xf4, 0x61, 0x11, 0x1a, 0xa3, 0xb2, 0x07, 0x79, 0x84, 0x0e, 0x61,
-	0x2e, 0x4e, 0x58, 0x8f, 0x70, 0x1e, 0xd2, 0xc0, 0xf3, 0x05, 0xe1, 0xd8, 0xb0, 0x0c, 0xa7, 0xb5,
-	0xb1, 0xda, 0xc9, 0xb7, 0xa6, 0x32, 0xa3, 0x73, 0x50, 0x25, 0xbf, 0xa6, 0x22, 0x39, 0xf3, 0xae,
-	0xb7, 0x30, 0x37, 0x61, 0xa1, 0x8e, 0x88, 0xe6, 0xc1, 0x18, 0x90, 0x33, 0xac, 0x59, 0x9a, 0xd3,
-	0xf4, 0xb2, 0x23, 0x5a, 0x80, 0xa9, 0x91, 0x3f, 0x4c, 0x09, 0xd6, 0x2d, 0xcd, 0xd1, 0x3d, 0x15,
-	0x3c, 0xd7, 0x9f, 0x69, 0xf6, 0x63, 0x40, 0xdd, 0x90, 0x0b, 0xb5, 0x67, 0xdc, 0x23, 0x1f, 0x53,
-	0xc2, 0xc5, 0x5d, 0x77, 0xb4, 0xb7, 0xe0, 0xdf, 0x4a, 0x05, 0x8f, 0x19, 0xe5, 0x04, 0xad, 0xc1,
-	0xb4, 0x7a, 0x4f, 0x8e, 0x35, 0x79, 0x35, 0x54, 0x5c, 0xed, 0x6a, 0x87, 0xbd, 0x82, 0x62, 0x6f,
-	0xc3, 0xfc, 0x0e, 0xc9, 0x7b, 0xdc, 0x63, 0x68, 0xf6, 0xb0, 0xaa, 0xb4, 0x78, 0x58, 0x15, 0xd9,
-	0x2f, 0xe0, 0x9f, 0x52, 0x9f, 0x5c, 0xca, 0xea, 0x84, 0x9c, 0xb5, 0xa9, 0x57, 0x52, 0x34, 0x18,
-	0xc0, 0xff, 0x3b, 0x44, 0x54, 0x5e, 0xbf, 0xd0, 0xb3, 0x0c, 0x4d, 0xea, 0x47, 0x84, 0xc7, 0x7e,
-	0xaf, 0x10, 0x74, 0x05, 0x54, 0xd4, 0xea, 0xb7, 0xae, 0x81, 0x51, 0x5e, 0x03, 0x7b, 0x17, 0xf0,
-	0xcd, 0x61, 0xb9, 0xe8, 0xf5, 0x49, 0x8d, 0x12, 0xfd, 0x5f, 0xed, 0x66, 0x14, 0xad, 0x36, 0xbe,
-	0x1a, 0x30, 0xfb, 0x4a, 0x12, 0xde, 0x90, 0x64, 0x14, 0xf6, 0x08, 0x12, 0xd0, 0x2a, 0xf9, 0x82,
-	0xcc, 0xa2, 0xfe, 0xa6, 0xbd, 0xe6, 0x52, 0x6d, 0x4e, 0x09, 0xb1, 0xd7, 0x2e, 0x7e, 0xfc, 0xfe,
-	0xac, 0x3f, 0x42, 0x2b, 0xf2, 0x37, 0x6b, 0xf4, 0xc4, 0x2d, 0xae, 0xc5, 0xdd, 0xf3, 0xe2, 0x38,
-	0x76, 0x73, 0x23, 0xd1, 0x29, 0x34, 0x27, 0x06, 0x20, 0x5c, 0xf4, 0xbd, 0xee, 0xad, 0xf9, 0xa0,
-	0x26, 0x93, 0xcf, 0x7b, 0x2a, 0xe7, 0xb9, 0x68, 0xfd, 0x3e, 0xf3, 0xdc, 0x73, 0x75, 0x18, 0xa3,
-	0x2f, 0x9a, 0x5c, 0xa1, 0xea, 0x77, 0xf3, 0x61, 0x69, 0x4c, 0x9d, 0xa7, 0xa6, 0x75, 0x3b, 0x21,
-	0x97, 0x73, 0x28, 0xe5, 0xec, 0xa3, 0x6e, 0x21, 0x67, 0x62, 0xb9, 0x7b, 0x3e, 0x39, 0x8e, 0xeb,
-	0x45, 0x66, 0xde, 0x84, 0xbd, 0x0c, 0x53, 0x2e, 0x8d, 0xdd, 0x48, 0x75, 0xdf, 0x7c, 0xf9, 0xed,
-	0xb2, 0xad, 0x7d, 0xbf, 0x6c, 0x6b, 0xbf, 0x2e, 0xdb, 0xda, 0xfb, 0x8d, 0x20, 0x14, 0x27, 0xe9,
-	0x51, 0xa7, 0xc7, 0x22, 0x97, 0xa6, 0x91, 0x1f, 0x27, 0xec, 0x83, 0x3c, 0xf4, 0x87, 0xec, 0xd4,
-	0xad, 0xfd, 0x77, 0xf9, 0x13, 0x00, 0x00, 0xff, 0xff, 0x82, 0xa0, 0x01, 0xc4, 0x75, 0x06, 0x00,
-	0x00,
+	0x14, 0x95, 0x6d, 0x08, 0xc9, 0xcd, 0x8b, 0xe0, 0xcd, 0xe3, 0xf1, 0xe6, 0x19, 0x94, 0x67, 0xf9,
+	0xa1, 0xca, 0x42, 0x10, 0xb7, 0x48, 0x95, 0xaa, 0x76, 0x41, 0x05, 0x2d, 0x08, 0x29, 0x20, 0xe4,
+	0xd2, 0x2e, 0xba, 0x33, 0x61, 0x62, 0xdc, 0xc4, 0x1e, 0xd7, 0x33, 0x0e, 0x45, 0x28, 0x1b, 0x7e,
+	0xa1, 0x9f, 0xd1, 0x7e, 0x48, 0x97, 0x95, 0xba, 0xec, 0xa6, 0x42, 0xfd, 0x90, 0xca, 0x33, 0x76,
+	0xb0, 0xc1, 0x50, 0x56, 0xcc, 0xbd, 0x73, 0xce, 0xb9, 0x87, 0x39, 0x17, 0x03, 0x66, 0x34, 0xf0,
+	0x6c, 0x37, 0xf2, 0x99, 0x1d, 0xc5, 0x94, 0x53, 0xfb, 0xd8, 0x25, 0x01, 0x0d, 0xb3, 0x1f, 0x1d,
+	0xd1, 0x43, 0x35, 0x59, 0xe9, 0x4b, 0x1e, 0xa5, 0xde, 0x90, 0xa4, 0x70, 0xdb, 0x0d, 0x43, 0xca,
+	0x5d, 0xee, 0xd3, 0x90, 0x49, 0x94, 0xbe, 0x98, 0xdd, 0x8a, 0xea, 0x28, 0xe9, 0xdb, 0x24, 0x88,
+	0xf8, 0x99, 0xbc, 0x34, 0x2f, 0x34, 0x80, 0xcd, 0xa4, 0xdf, 0x27, 0xf1, 0x6e, 0xd8, 0xa7, 0x48,
+	0x87, 0x7a, 0xe4, 0x47, 0x64, 0xe8, 0x87, 0x04, 0x2b, 0x86, 0x6a, 0x35, 0x9c, 0x49, 0x8d, 0xda,
+	0x00, 0xfd, 0x98, 0x06, 0x6f, 0x48, 0xcc, 0xc9, 0x07, 0xac, 0x8a, 0xdb, 0x42, 0x27, 0xe5, 0x72,
+	0x9a, 0xdd, 0x6a, 0x92, 0x9b, 0xd7, 0x29, 0xf7, 0x48, 0x4c, 0xd9, 0x77, 0x03, 0x82, 0xa7, 0x24,
+	0xf7, 0xaa, 0x83, 0x4c, 0xf8, 0x23, 0x22, 0xe1, 0xb1, 0x1f, 0x7a, 0x5b, 0x34, 0x09, 0x39, 0x9e,
+	0x36, 0x54, 0x4b, 0x73, 0x4a, 0x3d, 0x64, 0xc1, 0xac, 0xdb, 0x1b, 0x1c, 0x14, 0x61, 0x35, 0x01,
+	0xbb, 0xde, 0x46, 0xcb, 0xd0, 0xe2, 0x94, 0xbb, 0xc3, 0x3d, 0xc2, 0x98, 0xeb, 0x11, 0x86, 0x67,
+	0x04, 0xae, 0xdc, 0x4c, 0x67, 0x4a, 0x07, 0x5d, 0x12, 0x7a, 0xfc, 0x04, 0xd7, 0xe5, 0xcc, 0x62,
+	0x0f, 0xad, 0xc0, 0x9c, 0xac, 0x5f, 0xa7, 0x9c, 0xae, 0x1f, 0xf8, 0x1c, 0x37, 0x0c, 0xd5, 0x52,
+	0x9c, 0x1b, 0x7d, 0x64, 0x40, 0xb3, 0xd0, 0xc3, 0x20, 0x60, 0xc5, 0x16, 0x5a, 0x80, 0x9a, 0xcf,
+	0xb6, 0x93, 0xe1, 0x10, 0x37, 0x0d, 0xd5, 0xaa, 0x3b, 0x59, 0x65, 0x7e, 0x57, 0xa1, 0x25, 0x1f,
+	0x6a, 0x8f, 0xf0, 0xd8, 0xef, 0xb1, 0x3b, 0x73, 0x58, 0x80, 0xda, 0xa8, 0x98, 0x41, 0x56, 0xa1,
+	0x43, 0x98, 0x8d, 0x62, 0xda, 0x23, 0x8c, 0xf9, 0xa1, 0xe7, 0xb8, 0x9c, 0x30, 0xac, 0x19, 0x9a,
+	0xd5, 0x5c, 0x5f, 0xe9, 0x64, 0x5b, 0x53, 0x9a, 0xd1, 0x39, 0x28, 0x83, 0x5f, 0x86, 0x3c, 0x3e,
+	0x73, 0xae, 0x4b, 0xa0, 0x0d, 0xa8, 0x67, 0x29, 0x30, 0x3c, 0x25, 0xe4, 0xfe, 0xbf, 0x45, 0x2e,
+	0x43, 0x49, 0x9d, 0x09, 0x49, 0xdf, 0x84, 0xf9, 0xaa, 0x49, 0x68, 0x0e, 0xb4, 0x01, 0x39, 0xc3,
+	0x8a, 0xa1, 0x58, 0x0d, 0x27, 0x3d, 0xa2, 0x79, 0x98, 0x1e, 0xb9, 0xc3, 0x84, 0x60, 0xd5, 0x50,
+	0x2c, 0xc5, 0x91, 0xc5, 0x53, 0xf5, 0x89, 0xa2, 0x3f, 0x83, 0x56, 0x49, 0xfe, 0x77, 0x64, 0xad,
+	0x40, 0x36, 0x1f, 0x02, 0xea, 0xfa, 0x8c, 0xcb, 0x2d, 0x67, 0x0e, 0x79, 0x9f, 0x10, 0xc6, 0xef,
+	0x7a, 0x61, 0x73, 0x0b, 0xfe, 0x2a, 0x31, 0x58, 0x44, 0x43, 0x46, 0xd0, 0x2a, 0xcc, 0xc8, 0x34,
+	0x19, 0x56, 0xc4, 0x4b, 0xa0, 0xfc, 0x25, 0xae, 0xfe, 0x82, 0x9c, 0x1c, 0x62, 0x6e, 0xc3, 0xdc,
+	0x0e, 0xc9, 0x34, 0xee, 0x31, 0x34, 0x8d, 0x55, 0x52, 0xf3, 0x58, 0x65, 0x65, 0x6e, 0xc0, 0x9f,
+	0x05, 0x9d, 0xcc, 0xca, 0xca, 0x04, 0x9c, 0xca, 0x54, 0x3b, 0xc9, 0x05, 0x06, 0xf0, 0xcf, 0x0e,
+	0xe1, 0xa5, 0xb0, 0x72, 0x3f, 0x4b, 0xd0, 0x08, 0xdd, 0x80, 0xb0, 0xc8, 0xed, 0xe5, 0x86, 0xae,
+	0x1a, 0x25, 0xb7, 0xea, 0xad, 0x4b, 0xa8, 0x15, 0x97, 0xd0, 0xdc, 0x05, 0x7c, 0x73, 0x58, 0x66,
+	0x7a, 0x6d, 0xc2, 0x91, 0xa6, 0xff, 0xae, 0x5c, 0xa4, 0x5c, 0x6a, 0xfd, 0xb3, 0x06, 0xad, 0x17,
+	0x02, 0xf0, 0x8a, 0xc4, 0x23, 0xbf, 0x47, 0x10, 0x87, 0x66, 0x21, 0x17, 0xa4, 0xe7, 0xfc, 0x9b,
+	0xf1, 0xea, 0x8b, 0x95, 0x77, 0xd2, 0x88, 0xb9, 0x7a, 0xf1, 0xed, 0xe7, 0x47, 0xf5, 0x01, 0x5a,
+	0x16, 0x5f, 0xcc, 0xd1, 0x23, 0x3b, 0xff, 0xb5, 0x98, 0x7d, 0x9e, 0x1f, 0xc7, 0x76, 0x16, 0x24,
+	0x3a, 0x85, 0xc6, 0x24, 0x00, 0x84, 0x73, 0xdd, 0xeb, 0xd9, 0xea, 0xff, 0x56, 0xdc, 0x64, 0xf3,
+	0x1e, 0x8b, 0x79, 0x36, 0x5a, 0xbb, 0xcf, 0x3c, 0xfb, 0x5c, 0x1e, 0xc6, 0xe8, 0x93, 0x22, 0x56,
+	0xa8, 0xfc, 0x65, 0xf8, 0xaf, 0x30, 0xa6, 0x2a, 0x53, 0xdd, 0xb8, 0x1d, 0x90, 0xd9, 0x39, 0x14,
+	0x76, 0xf6, 0x51, 0x37, 0xb7, 0x33, 0x89, 0xdc, 0x3e, 0x9f, 0x1c, 0xc7, 0xd5, 0x26, 0xd3, 0x6c,
+	0xfc, 0x5e, 0xda, 0x93, 0x29, 0x8d, 0xed, 0x40, 0xaa, 0x6f, 0x3e, 0xff, 0x72, 0xd9, 0x56, 0xbe,
+	0x5e, 0xb6, 0x95, 0x1f, 0x97, 0x6d, 0xe5, 0xed, 0xba, 0xe7, 0xf3, 0x93, 0xe4, 0xa8, 0xd3, 0xa3,
+	0x81, 0x1d, 0x26, 0x81, 0x1b, 0xc5, 0xf4, 0x9d, 0x38, 0xf4, 0x87, 0xf4, 0xd4, 0xae, 0xfc, 0xdf,
+	0xf6, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xa2, 0x79, 0x8c, 0x62, 0xf3, 0x06, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -890,14 +900,31 @@ func (m *VertexMetrics) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if len(m.Pendings) > 0 {
+		for k := range m.Pendings {
+			v := m.Pendings[k]
+			baseI := i
+			i = encodeVarintDaemon(dAtA, i, uint64(v))
+			i--
+			dAtA[i] = 0x10
+			i -= len(k)
+			copy(dAtA[i:], k)
+			i = encodeVarintDaemon(dAtA, i, uint64(len(k)))
+			i--
+			dAtA[i] = 0xa
+			i = encodeVarintDaemon(dAtA, i, uint64(baseI-i))
+			i--
+			dAtA[i] = 0x22
+		}
+	}
 	if len(m.ProcessingRates) > 0 {
 		for k := range m.ProcessingRates {
 			v := m.ProcessingRates[k]
 			baseI := i
-			i -= 4
-			encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(v))))
+			i -= 8
+			encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(v))))
 			i--
-			dAtA[i] = 0x15
+			dAtA[i] = 0x11
 			i -= len(k)
 			copy(dAtA[i:], k)
 			i = encodeVarintDaemon(dAtA, i, uint64(len(k)))
@@ -1265,7 +1292,15 @@ func (m *VertexMetrics) Size() (n int) {
 		for k, v := range m.ProcessingRates {
 			_ = k
 			_ = v
-			mapEntrySize := 1 + len(k) + sovDaemon(uint64(len(k))) + 1 + 4
+			mapEntrySize := 1 + len(k) + sovDaemon(uint64(len(k))) + 1 + 8
+			n += mapEntrySize + 1 + sovDaemon(uint64(mapEntrySize))
+		}
+	}
+	if len(m.Pendings) > 0 {
+		for k, v := range m.Pendings {
+			_ = k
+			_ = v
+			mapEntrySize := 1 + len(k) + sovDaemon(uint64(len(k))) + 1 + sovDaemon(uint64(v))
 			n += mapEntrySize + 1 + sovDaemon(uint64(mapEntrySize))
 		}
 	}
@@ -1872,10 +1907,10 @@ func (m *VertexMetrics) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.ProcessingRates == nil {
-				m.ProcessingRates = make(map[string]float32)
+				m.ProcessingRates = make(map[string]float64)
 			}
 			var mapkey string
-			var mapvalue float32
+			var mapvalue float64
 			for iNdEx < postIndex {
 				entryPreIndex := iNdEx
 				var wire uint64
@@ -1924,13 +1959,13 @@ func (m *VertexMetrics) Unmarshal(dAtA []byte) error {
 					mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
 					iNdEx = postStringIndexmapkey
 				} else if fieldNum == 2 {
-					var mapvaluetemp uint32
-					if (iNdEx + 4) > l {
+					var mapvaluetemp uint64
+					if (iNdEx + 8) > l {
 						return io.ErrUnexpectedEOF
 					}
-					mapvaluetemp = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:]))
-					iNdEx += 4
-					mapvalue = math.Float32frombits(mapvaluetemp)
+					mapvaluetemp = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+					iNdEx += 8
+					mapvalue = math.Float64frombits(mapvaluetemp)
 				} else {
 					iNdEx = entryPreIndex
 					skippy, err := skipDaemon(dAtA[iNdEx:])
@@ -1948,6 +1983,119 @@ func (m *VertexMetrics) Unmarshal(dAtA []byte) error {
 			}
 			m.ProcessingRates[mapkey] = mapvalue
 			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Pendings", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowDaemon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthDaemon
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthDaemon
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Pendings == nil {
+				m.Pendings = make(map[string]int64)
+			}
+			var mapkey string
+			var mapvalue int64
+			for iNdEx < postIndex {
+				entryPreIndex := iNdEx
+				var wire uint64
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowDaemon
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					wire |= uint64(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				fieldNum := int32(wire >> 3)
+				if fieldNum == 1 {
+					var stringLenmapkey uint64
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowDaemon
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						stringLenmapkey |= uint64(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					intStringLenmapkey := int(stringLenmapkey)
+					if intStringLenmapkey < 0 {
+						return ErrInvalidLengthDaemon
+					}
+					postStringIndexmapkey := iNdEx + intStringLenmapkey
+					if postStringIndexmapkey < 0 {
+						return ErrInvalidLengthDaemon
+					}
+					if postStringIndexmapkey > l {
+						return io.ErrUnexpectedEOF
+					}
+					mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
+					iNdEx = postStringIndexmapkey
+				} else if fieldNum == 2 {
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowDaemon
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						mapvalue |= int64(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+				} else {
+					iNdEx = entryPreIndex
+					skippy, err := skipDaemon(dAtA[iNdEx:])
+					if err != nil {
+						return err
+					}
+					if (skippy < 0) || (iNdEx+skippy) < 0 {
+						return ErrInvalidLengthDaemon
+					}
+					if (iNdEx + skippy) > postIndex {
+						return io.ErrUnexpectedEOF
+					}
+					iNdEx += skippy
+				}
+			}
+			m.Pendings[mapkey] = mapvalue
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipDaemon(dAtA[iNdEx:])
diff --git a/pkg/apis/proto/daemon/daemon.proto b/pkg/apis/proto/daemon/daemon.proto
index b17d8fe3..6e8b52f3 100644
--- a/pkg/apis/proto/daemon/daemon.proto
+++ b/pkg/apis/proto/daemon/daemon.proto
@@ -27,7 +27,8 @@ message BufferInfo {
 message VertexMetrics {
   required string pipeline = 1;
   required string vertex = 2;
-  map<string, float> processingRates = 3;
+  map<string, double> processingRates = 3;
+  map<string, int64> pendings = 4;
 }
 
 message ListBuffersRequest {
diff --git a/pkg/apis/proto/daemon/daemon.swagger.json b/pkg/apis/proto/daemon/daemon.swagger.json
deleted file mode 100644
index a2f1ca28..00000000
--- a/pkg/apis/proto/daemon/daemon.swagger.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
-  "swagger": "2.0",
-  "info": {
-    "title": "pkg/apis/proto/daemon/daemon.proto",
-    "version": "version not set"
-  },
-  "consumes": [
-    "application/json"
-  ],
-  "produces": [
-    "application/json"
-  ],
-  "paths": {},
-  "definitions": {
-    "daemon.CanIdeletePipelineResponse": {
-      "type": "object",
-      "properties": {
-        "decision": {
-          "type": "boolean"
-        }
-      }
-    },
-    "google.protobuf.Any": {
-      "type": "object",
-      "properties": {
-        "type_url": {
-          "type": "string"
-        },
-        "value": {
-          "type": "string",
-          "format": "byte"
-        }
-      }
-    },
-    "grpc.gateway.runtime.Error": {
-      "type": "object",
-      "properties": {
-        "error": {
-          "type": "string"
-        },
-        "code": {
-          "type": "integer",
-          "format": "int32"
-        },
-        "message": {
-          "type": "string"
-        },
-        "details": {
-          "type": "array",
-          "items": {
-            "$ref": "#/definitions/google.protobuf.Any"
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/pkg/daemon/server/service/pipeline_metrics_query_service.go b/pkg/daemon/server/service/pipeline_metrics_query_service.go
index 5b735ff1..0120d796 100644
--- a/pkg/daemon/server/service/pipeline_metrics_query_service.go
+++ b/pkg/daemon/server/service/pipeline_metrics_query_service.go
@@ -14,6 +14,7 @@ import (
 	"github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/apis/proto/daemon"
 	"github.com/numaproj/numaflow/pkg/isbsvc"
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 )
 
@@ -24,7 +25,7 @@ type metricsHttpClient interface {
 }
 
 type pipelineMetricsQueryService struct {
-	isbsvcClient isbsvc.ISBService
+	isbSvcClient isbsvc.ISBService
 	pipeline     *v1alpha1.Pipeline
 	httpClient   metricsHttpClient
 }
@@ -32,12 +33,14 @@ type pipelineMetricsQueryService struct {
 // NewPipelineMetricsQueryService returns a new instance of pipelineMetricsQueryService
 func NewPipelineMetricsQueryService(isbSvcClient isbsvc.ISBService, pipeline *v1alpha1.Pipeline) *pipelineMetricsQueryService {
 	return &pipelineMetricsQueryService{
-		isbsvcClient: isbSvcClient,
+		isbSvcClient: isbSvcClient,
 		pipeline:     pipeline,
-		httpClient: &http.Client{Transport: &http.Transport{
-			TLSHandshakeTimeout: time.Second * 3,
-			TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
-		}},
+		httpClient: &http.Client{
+			Transport: &http.Transport{
+				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+			},
+			Timeout: time.Second * 3,
+		},
 	}
 }
 
@@ -49,7 +52,7 @@ func (ps *pipelineMetricsQueryService) ListBuffers(ctx context.Context, req *dae
 	buffers := []*daemon.BufferInfo{}
 	for _, edge := range ps.pipeline.Spec.Edges {
 		buffer := v1alpha1.GenerateEdgeBufferName(ps.pipeline.Namespace, ps.pipeline.Name, edge.From, edge.To)
-		bufferInfo, err := ps.isbsvcClient.GetBufferInfo(ctx, v1alpha1.Buffer{Name: buffer, Type: v1alpha1.EdgeBuffer})
+		bufferInfo, err := ps.isbSvcClient.GetBufferInfo(ctx, v1alpha1.Buffer{Name: buffer, Type: v1alpha1.EdgeBuffer})
 		if err != nil {
 			return nil, fmt.Errorf("failed to get information of buffer %q", buffer)
 		}
@@ -80,7 +83,7 @@ func (ps *pipelineMetricsQueryService) ListBuffers(ctx context.Context, req *dae
 
 // GetBuffer is used to obtain one buffer information of a pipeline
 func (ps *pipelineMetricsQueryService) GetBuffer(ctx context.Context, req *daemon.GetBufferRequest) (*daemon.GetBufferResponse, error) {
-	bufferInfo, err := ps.isbsvcClient.GetBufferInfo(ctx, v1alpha1.Buffer{Name: *req.Buffer, Type: v1alpha1.EdgeBuffer})
+	bufferInfo, err := ps.isbSvcClient.GetBufferInfo(ctx, v1alpha1.Buffer{Name: *req.Buffer, Type: v1alpha1.EdgeBuffer})
 	if err != nil {
 		return nil, fmt.Errorf("failed to get information of buffer %q:%v", *req.Buffer, err)
 	}
@@ -145,17 +148,29 @@ func (ps *pipelineMetricsQueryService) GetVertexMetrics(ctx context.Context, req
 		return nil, err
 	}
 
-	processingRates := make(map[string]float32, 0)
+	processingRates := make(map[string]float64, 0)
 	// Check if the resultant metrics list contains the processingRate, if it does look for the period label
-	if value, ok := result[v1alpha1.VertexProcessingRate]; ok {
+	if value, ok := result[metricspkg.VertexProcessingRate]; ok {
+		metrics := value.GetMetric()
+		for _, metric := range metrics {
+			labels := metric.GetLabel()
+			for _, label := range labels {
+				if label.GetName() == metricspkg.LabelPeriod {
+					lookback := label.GetValue()
+					processingRates[lookback] = metric.Gauge.GetValue()
+				}
+			}
+		}
+	}
+	pendings := make(map[string]int64, 0)
+	if value, ok := result[metricspkg.VertexPendingMessages]; ok {
 		metrics := value.GetMetric()
 		for _, metric := range metrics {
 			labels := metric.GetLabel()
 			for _, label := range labels {
-				if label.GetName() == v1alpha1.MetricPeriodLabel {
+				if label.GetName() == metricspkg.LabelPeriod {
 					lookback := label.GetValue()
-					rate := float32(metric.Gauge.GetValue())
-					processingRates[lookback] = rate
+					pendings[lookback] = int64(metric.Gauge.GetValue())
 				}
 			}
 		}
@@ -164,6 +179,7 @@ func (ps *pipelineMetricsQueryService) GetVertexMetrics(ctx context.Context, req
 		Pipeline:        &ps.pipeline.Name,
 		Vertex:          req.Vertex,
 		ProcessingRates: processingRates,
+		Pendings:        pendings,
 	}
 	resp.Vertex = v
 	return resp, nil
diff --git a/pkg/isb/forward/forward.go b/pkg/isb/forward/forward.go
index b4e23864..9a2f69ba 100644
--- a/pkg/isb/forward/forward.go
+++ b/pkg/isb/forward/forward.go
@@ -16,6 +16,7 @@ import (
 
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/isb"
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 	sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
 	udfapplier "github.com/numaproj/numaflow/pkg/udf/applier"
@@ -166,9 +167,9 @@ func (isdf *InterStepDataForward) forwardAChunk(ctx context.Context) {
 	readMessages, err := isdf.fromBuffer.Read(ctx, isdf.opts.readBatchSize)
 	if err != nil {
 		isdf.opts.logger.Warnw("failed to read fromBuffer", zap.Error(err))
-		readMessagesError.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Inc()
+		readMessagesError.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Inc()
 	}
-	readMessagesCount.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(readMessages)))
+	readMessagesCount.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(readMessages)))
 
 	// process only if we have any read messages. There is a natural looping here if there is an internal error while
 	// reading, and we are not able to proceed.
@@ -221,14 +222,14 @@ func (isdf *InterStepDataForward) forwardAChunk(ctx context.Context) {
 	// context.Done() is closed.
 	wg.Wait()
 	isdf.opts.logger.Debugw("concurrent applyUDF completed", zap.Int("concurrency", isdf.opts.udfConcurrency), zap.Duration("took", time.Since(concurrentUDFProcessingStart)))
-	concurrentUDFProcessingTime.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Observe(float64(time.Since(concurrentUDFProcessingStart).Microseconds()))
+	concurrentUDFProcessingTime.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Observe(float64(time.Since(concurrentUDFProcessingStart).Microseconds()))
 
 	// Now that we know the UDF processing is done, let's figure out which vertex to send the results to.
 	// Update the toBuffer(s) with writeMessages.
 	for _, m := range udfResults {
 		// look for errors in udf processing, if we see even 1 error let's return. handling partial retrying is not worth ATM.
 		if m.udfError != nil {
-			udfError.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Inc()
+			udfError.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Inc()
 			isdf.opts.logger.Errorw("failed to applyUDF", zap.Error(err))
 			return
 		}
@@ -267,13 +268,13 @@ func (isdf *InterStepDataForward) forwardAChunk(ctx context.Context) {
 	// implicit return for posterity :-)
 	if err != nil {
 		isdf.opts.logger.Errorw("failed to ack from buffer", zap.Error(err))
-		ackMessageError.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(readOffsets)))
+		ackMessageError.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(readOffsets)))
 		return
 	}
-	ackMessagesCount.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(readOffsets)))
+	ackMessagesCount.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(readOffsets)))
 
 	// ProcessingTimes of the entire forwardAChunk
-	forwardAChunkProcessingTime.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "from": isdf.fromBuffer.GetName(), "to": toBuffers}).Observe(float64(time.Since(start).Microseconds()))
+	forwardAChunkProcessingTime.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "from": isdf.fromBuffer.GetName(), "to": toBuffers}).Observe(float64(time.Since(start).Microseconds()))
 }
 
 // ackFromBuffer acknowledges an array of offsets back to fromBuffer and is a blocking call or until shutdown has been initiated.
@@ -328,11 +329,11 @@ retry:
 				needRetry = true
 				// we retry only failed messages
 				failedMessages = append(failedMessages, messages[idx])
-				writeMessagesError.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": toBuffer.GetName()}).Inc()
+				writeMessagesError.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": toBuffer.GetName()}).Inc()
 				// a shutdown can break the blocking loop caused due to InternalErr
 				if ok, _ := isdf.IsShuttingDown(); ok {
 					err := fmt.Errorf("writeToBuffer failed, Stop called while stuck on an internal error with failed messages:%d, %v", len(failedMessages), errs)
-					platformError.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName}).Inc()
+					platformError.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName}).Inc()
 					return writeOffsets, err
 				}
 			} else {
@@ -354,7 +355,7 @@ retry:
 		}
 	}
 
-	writeMessagesCount.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": toBuffer.GetName()}).Add(float64(len(messages)))
+	writeMessagesCount.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": toBuffer.GetName()}).Add(float64(len(messages)))
 
 	return writeOffsets, nil
 }
@@ -363,12 +364,12 @@ retry:
 func (isdf *InterStepDataForward) concurrentApplyUDF(ctx context.Context, readMessagePair <-chan *readWriteMessagePair) {
 	for message := range readMessagePair {
 		start := time.Now()
-		udfReadMessagesCount.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Inc()
+		udfReadMessagesCount.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Inc()
 		writeMessages, err := isdf.applyUDF(ctx, message.readMessage)
-		udfWriteMessagesCount.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(writeMessages)))
+		udfWriteMessagesCount.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Add(float64(len(writeMessages)))
 		message.writeMessages = append(message.writeMessages, writeMessages...)
 		message.udfError = err
-		udfProcessingTime.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Observe(float64(time.Since(start).Microseconds()))
+		udfProcessingTime.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName, "buffer": isdf.fromBuffer.GetName()}).Observe(float64(time.Since(start).Microseconds()))
 	}
 }
 
@@ -387,7 +388,7 @@ func (isdf *InterStepDataForward) applyUDF(ctx context.Context, readMessage *isb
 			// this does not mean we should prohibit this from a shutdown.
 			if ok, _ := isdf.IsShuttingDown(); ok {
 				isdf.opts.logger.Errorw("UDF.Apply, Stop called while stuck on an internal error", zap.Error(err))
-				platformError.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName}).Inc()
+				platformError.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName}).Inc()
 				return nil, err
 			}
 			continue
@@ -414,7 +415,7 @@ func (isdf *InterStepDataForward) whereToStep(writeMessage *isb.Message, message
 		// a shutdown can break the blocking loop caused due to InternalErr
 		if ok, _ := isdf.IsShuttingDown(); ok {
 			err := fmt.Errorf("whereToStep, Stop called while stuck on an internal error, %v", err)
-			platformError.With(map[string]string{"vertex": isdf.vertexName, "pipeline": isdf.pipelineName}).Inc()
+			platformError.With(map[string]string{metricspkg.LabelVertex: isdf.vertexName, metricspkg.LabelPipeline: isdf.pipelineName}).Inc()
 			return err
 		}
 		return err
diff --git a/pkg/isb/forward/metrics.go b/pkg/isb/forward/metrics.go
index ecfddbc6..d098b8cf 100644
--- a/pkg/isb/forward/metrics.go
+++ b/pkg/isb/forward/metrics.go
@@ -3,6 +3,8 @@ package forward
 import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
+
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 )
 
 // readMessagesCount is used to indicate the number of messages read
@@ -10,56 +12,56 @@ var readMessagesCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "read_total",
 	Help:      "Total number of Messages Read",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // readMessagesError is used to indicate the number of errors messages read
 var readMessagesError = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "read_error_total",
 	Help:      "Total number of Read Errors",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // writeMessagesCount is used to indicate the number of messages written
 var writeMessagesCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "write_total",
 	Help:      "Total number of Messages Written",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // writeMessagesError is used to indicate the number of errors messages written
 var writeMessagesError = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "write_error_total",
 	Help:      "Total number of Write Errors",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // ackMessagesCount is used to indicate the number of  messages acknowledged
 var ackMessagesCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "ack_total",
 	Help:      "Total number of Messages Acknowledged",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // ackMessageError is used to indicate the errors in the number of  messages acknowledged
 var ackMessageError = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "ack_error_total",
 	Help:      "Total number of Acknowledged Errors",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // udfError is used to indicate the number of UDF errors
 var udfError = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "udf_error_total",
 	Help:      "Total number of UDF Errors",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // platformError is used to indicate the number of Internal/Platform errors
 var platformError = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "platform_error_total",
 	Help:      "Total number of platform Errors",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
 
 // forwardAChunkProcessingTime is a histogram to Observe forwardAChunk Processing times as a whole
 var forwardAChunkProcessingTime = promauto.NewHistogramVec(prometheus.HistogramOpts{
@@ -67,7 +69,7 @@ var forwardAChunkProcessingTime = promauto.NewHistogramVec(prometheus.HistogramO
 	Name:      "forward_chunk_processing_time",
 	Help:      "Processing times of the entire forward a chunk (100 microseconds to 20 minutes)",
 	Buckets:   prometheus.ExponentialBucketsRange(100, 60000000*20, 60),
-}, []string{"vertex", "pipeline", "from", "to"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "from", "to"})
 
 // udfProcessingTime is a histogram to Observe UDF Processing times as a whole
 var udfProcessingTime = promauto.NewHistogramVec(prometheus.HistogramOpts{
@@ -75,7 +77,7 @@ var udfProcessingTime = promauto.NewHistogramVec(prometheus.HistogramOpts{
 	Name:      "udf_processing_time",
 	Help:      "Processing times of UDF (100 microseconds to 15 minutes)",
 	Buckets:   prometheus.ExponentialBucketsRange(100, 60000000*15, 60),
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // concurrentUDFProcessingTime is a histogram to Observe UDF Processing times as a whole
 var concurrentUDFProcessingTime = promauto.NewHistogramVec(prometheus.HistogramOpts{
@@ -83,18 +85,18 @@ var concurrentUDFProcessingTime = promauto.NewHistogramVec(prometheus.HistogramO
 	Name:      "concurrent_udf_processing_time",
 	Help:      "Processing times of Concurrent UDF (100 microseconds to 20 minutes)",
 	Buckets:   prometheus.ExponentialBucketsRange(100, 60000000*20, 60),
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // udfReadMessagesCount is used to indicate the number of messages read by UDF
 var udfReadMessagesCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "udf_read_total",
 	Help:      "Total number of Messages Read at UDF",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
 
 // udfWriteMessagesCount is used to indicate the number of messages read by UDF
 var udfWriteMessagesCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "forwarder",
 	Name:      "udf_write_total",
 	Help:      "Total number of Messages Written at UDF",
-}, []string{"vertex", "pipeline", "buffer"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline, "buffer"})
diff --git a/pkg/isb/interfaces.go b/pkg/isb/interfaces.go
index 0ba6cd37..5f324340 100644
--- a/pkg/isb/interfaces.go
+++ b/pkg/isb/interfaces.go
@@ -20,7 +20,7 @@ const (
 
 // Ratable is the interface that wraps the Rate method.
 type Ratable interface {
-	// Rate returns the rough rate (messages/second) in the past seconds, this can be used for auto-scaling calculation
+	// Rate returns the rough rate (messages/second) in the past seconds, this can be used for autoscaling calculation
 	Rate(ctx context.Context, seconds int64) (float64, error)
 }
 
diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go
index ccb8d53e..c73651ac 100644
--- a/pkg/metrics/metrics.go
+++ b/pkg/metrics/metrics.go
@@ -21,16 +21,25 @@ import (
 	sharedtls "github.com/numaproj/numaflow/pkg/shared/tls"
 )
 
+const (
+	LabelPipeline = "pipeline"
+	LabelVertex   = "vertex"
+	LabelPeriod   = "period"
+
+	VertexProcessingRate  = "vertex_processing_rate"
+	VertexPendingMessages = "vertex_pending_messages"
+)
+
 var (
 	processingRate = promauto.NewGaugeVec(prometheus.GaugeOpts{
-		Name: dfv1.VertexProcessingRate,
+		Name: VertexProcessingRate,
 		Help: "Message processing rate in the last period of seconds, tps. It represents the rate of a vertex instead of a pod.",
-	}, []string{dfv1.MetricPipelineLabel, dfv1.MetricVertexLabel, dfv1.MetricPeriodLabel})
+	}, []string{LabelPipeline, LabelVertex, LabelPeriod})
 
 	pending = promauto.NewGaugeVec(prometheus.GaugeOpts{
-		Name: dfv1.VertexPendingMessages,
+		Name: VertexPendingMessages,
 		Help: "Average pending messages in the last period of seconds. It is the pending messages of a vertex, not a pod.",
-	}, []string{dfv1.MetricPipelineLabel, dfv1.MetricVertexLabel, dfv1.MetricPeriodLabel})
+	}, []string{LabelPipeline, LabelVertex, LabelPeriod})
 
 	// fixedLookbackSeconds Always expose metrics of following lookback seconds (1m, 5m, 15m)
 	fixedLookbackSeconds = map[string]int64{"1m": 60, "5m": 300, "15m": 900}
@@ -47,7 +56,7 @@ type metricsServer struct {
 	vertex    *dfv1.Vertex
 	rater     isb.Ratable
 	lagReader isb.LagReader
-	// lookbackSeconds is the look back seconds for pending and rate calculation used for auto scaling
+	// lookbackSeconds is the look back seconds for pending and rate calculation used for autoscaling
 	lookbackSeconds     int64
 	lagCheckingInterval time.Duration
 	refreshInterval     time.Duration
@@ -85,9 +94,9 @@ func WithLookbackSeconds(seconds int64) Option {
 func NewMetricsServer(vertex *dfv1.Vertex, opts ...Option) *metricsServer {
 	m := new(metricsServer)
 	m.vertex = vertex
-	m.refreshInterval = 5 * time.Second     // Default refersh interval
-	m.lagCheckingInterval = 3 * time.Second // Default lag checking interval
-	m.lookbackSeconds = 180                 // Default
+	m.refreshInterval = 5 * time.Second             // Default refersh interval
+	m.lagCheckingInterval = 3 * time.Second         // Default lag checking interval
+	m.lookbackSeconds = dfv1.DefaultLookbackSeconds // Default
 	for _, opt := range opts {
 		if opt != nil {
 			opt(m)
@@ -130,7 +139,7 @@ func (ms *metricsServer) exposePendingAndRate(ctx context.Context) {
 		return
 	}
 	log := logging.FromContext(ctx)
-	lookbackSecondsMap := map[string]int64{"default": ms.lookbackSeconds}
+	lookbackSecondsMap := map[string]int64{"default": ms.lookbackSeconds} // Metrics for autoscaling use key "default"
 	for k, v := range fixedLookbackSeconds {
 		lookbackSecondsMap[k] = v
 	}
diff --git a/pkg/sinks/kafka/kafka.go b/pkg/sinks/kafka/kafka.go
index 1dccac55..e851bbe9 100644
--- a/pkg/sinks/kafka/kafka.go
+++ b/pkg/sinks/kafka/kafka.go
@@ -11,6 +11,7 @@ import (
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/isb"
 	"github.com/numaproj/numaflow/pkg/isb/forward"
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 	"github.com/numaproj/numaflow/pkg/shared/util"
 	"github.com/numaproj/numaflow/pkg/udf/applier"
@@ -144,7 +145,7 @@ func (tk *ToKafka) Write(_ context.Context, messages []isb.Message) ([]isb.Offse
 				// Need to close and recreate later because the successes and errors channels might be unclean
 				_ = tk.producer.Close()
 				tk.connected = false
-				kafkaSinkWriteTimeouts.With(map[string]string{"vertex": tk.name, "pipeline": tk.pipelineName}).Inc()
+				kafkaSinkWriteTimeouts.With(map[string]string{metricspkg.LabelVertex: tk.name, metricspkg.LabelPipeline: tk.pipelineName}).Inc()
 				close(done)
 				return
 			default:
@@ -162,9 +163,9 @@ func (tk *ToKafka) Write(_ context.Context, messages []isb.Message) ([]isb.Offse
 	<-done
 	for _, err := range errs {
 		if err != nil {
-			kafkaSinkWriteErrors.With(map[string]string{"vertex": tk.name, "pipeline": tk.pipelineName}).Inc()
+			kafkaSinkWriteErrors.With(map[string]string{metricspkg.LabelVertex: tk.name, metricspkg.LabelPipeline: tk.pipelineName}).Inc()
 		} else {
-			kafkaSinkWriteCount.With(map[string]string{"vertex": tk.name, "pipeline": tk.pipelineName}).Inc()
+			kafkaSinkWriteCount.With(map[string]string{metricspkg.LabelVertex: tk.name, metricspkg.LabelPipeline: tk.pipelineName}).Inc()
 		}
 	}
 	return nil, errs
diff --git a/pkg/sinks/kafka/metrics.go b/pkg/sinks/kafka/metrics.go
index 12cfb610..560b038a 100644
--- a/pkg/sinks/kafka/metrics.go
+++ b/pkg/sinks/kafka/metrics.go
@@ -3,6 +3,8 @@ package kafka
 import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
+
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 )
 
 // kafkaSinkWriteErrors is used to indicate the number of errors while while writing to kafka sink
@@ -10,17 +12,17 @@ var kafkaSinkWriteErrors = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "kafka_sink",
 	Name:      "write_error_total",
 	Help:      "Total number of Write Errors",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
 
 // kafkaSinkWriteCount is used to indicate the number of messages written to kafka
 var kafkaSinkWriteCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "kafka_sink",
 	Name:      "write_total",
 	Help:      "Total number of errors on NewToKafka",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
 
 var kafkaSinkWriteTimeouts = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "kafka_sink",
 	Name:      "write_timeout_total",
 	Help:      "Total number of write timeouts on NewToKafka",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
diff --git a/pkg/sinks/logger/log.go b/pkg/sinks/logger/log.go
index 1fd82c91..166dcf72 100644
--- a/pkg/sinks/logger/log.go
+++ b/pkg/sinks/logger/log.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/numaproj/numaflow/pkg/isb"
 	"github.com/numaproj/numaflow/pkg/isb/forward"
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 	"github.com/numaproj/numaflow/pkg/udf/applier"
 )
@@ -77,7 +78,7 @@ func (s *ToLog) IsFull() bool {
 func (s *ToLog) Write(_ context.Context, messages []isb.Message) ([]isb.Offset, []error) {
 	prefix := "(" + s.GetName() + ")"
 	for _, message := range messages {
-		logSinkWriteCount.With(map[string]string{"vertex": s.name, "pipeline": s.pipelineName}).Inc()
+		logSinkWriteCount.With(map[string]string{metricspkg.LabelVertex: s.name, metricspkg.LabelPipeline: s.pipelineName}).Inc()
 		log.Println(prefix, string(message.Payload))
 	}
 	return nil, make([]error, len(messages))
diff --git a/pkg/sinks/logger/metrics.go b/pkg/sinks/logger/metrics.go
index b99f429f..46fa3351 100644
--- a/pkg/sinks/logger/metrics.go
+++ b/pkg/sinks/logger/metrics.go
@@ -3,6 +3,8 @@ package logger
 import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
+
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 )
 
 // logSinkWriteCount is used to indicate the number of messages written to log sink
@@ -10,4 +12,4 @@ var logSinkWriteCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "log_sink",
 	Name:      "write_total",
 	Help:      "Total number of messages written to log sink",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
diff --git a/pkg/sinks/sink.go b/pkg/sinks/sink.go
index 251ba748..12269d99 100644
--- a/pkg/sinks/sink.go
+++ b/pkg/sinks/sink.go
@@ -3,13 +3,14 @@ package sinks
 import (
 	"context"
 	"fmt"
+	"sync"
+
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/isb"
 	jetstreamisb "github.com/numaproj/numaflow/pkg/isb/jetstream"
 	redisisb "github.com/numaproj/numaflow/pkg/isb/redis"
 	"github.com/numaproj/numaflow/pkg/isbsvc/clients"
 	"github.com/numaproj/numaflow/pkg/metrics"
-	"sync"
 
 	"go.uber.org/zap"
 
@@ -76,10 +77,7 @@ func (u *SinkProcessor) Start(ctx context.Context) error {
 		}
 	}()
 
-	metricsOpts := []metrics.Option{}
-	if s := u.VertexInstance.Vertex.Spec.Scale.LookbackSeconds; s != nil {
-		metricsOpts = append(metricsOpts, metrics.WithLookbackSeconds(int64(*s)))
-	}
+	metricsOpts := []metrics.Option{metrics.WithLookbackSeconds(int64(u.VertexInstance.Vertex.Spec.Scale.GetLookbackSeconds()))}
 	if x, ok := reader.(isb.LagReader); ok {
 		metricsOpts = append(metricsOpts, metrics.WithLagReader(x))
 	}
diff --git a/pkg/sources/generator/metrics.go b/pkg/sources/generator/metrics.go
index 05ee1140..d06b344a 100644
--- a/pkg/sources/generator/metrics.go
+++ b/pkg/sources/generator/metrics.go
@@ -3,6 +3,8 @@ package generator
 import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
+
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 )
 
 // tickgenSourceReadCount is used to indicate the number of messages read by tick generator
@@ -10,11 +12,11 @@ var tickgenSourceReadCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "tickgen_source",
 	Name:      "read_total",
 	Help:      "Total number of messages Read",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
 
 // tickgenSourceCount is used to indicate the number of times tickgen has ticked
 var tickgenSourceCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "tickgen_source",
 	Name:      "total",
 	Help:      "Total number of times tickgen source has ticked",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
diff --git a/pkg/sources/generator/tickgen.go b/pkg/sources/generator/tickgen.go
index a0881dbc..e202acb9 100644
--- a/pkg/sources/generator/tickgen.go
+++ b/pkg/sources/generator/tickgen.go
@@ -10,16 +10,18 @@ import (
 	"sync/atomic"
 	"time"
 
+	"go.uber.org/zap"
+
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/isb"
 	"github.com/numaproj/numaflow/pkg/isb/forward"
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 	"github.com/numaproj/numaflow/pkg/udf/applier"
 	"github.com/numaproj/numaflow/pkg/watermark/fetch"
 	"github.com/numaproj/numaflow/pkg/watermark/generic"
 	"github.com/numaproj/numaflow/pkg/watermark/processor"
 	"github.com/numaproj/numaflow/pkg/watermark/publish"
-	"go.uber.org/zap"
 )
 
 var log = logging.NewLogger()
@@ -206,7 +208,7 @@ func (mg *memgen) Read(ctx context.Context, count int64) ([]*isb.ReadMessage, er
 		// we implement Read With Wait semantics
 		select {
 		case r := <-mg.srcchan:
-			tickgenSourceReadCount.With(map[string]string{"vertex": mg.name, "pipeline": mg.pipelineName}).Inc()
+			tickgenSourceReadCount.With(map[string]string{metricspkg.LabelVertex: mg.name, metricspkg.LabelPipeline: mg.pipelineName}).Inc()
 			msgs = append(msgs, newreadmessage(r.data, r.offset))
 			// publish the last message's offset with watermark, this is an optimization to avoid too many insert calls
 			// into the offset timeline store.
@@ -269,7 +271,7 @@ func (mg *memgen) generator(ctx context.Context, rate int, timeunit time.Duratio
 				log.Info("Context.Done is called. exiting generator loop.")
 				return
 			case <-ticker.C:
-				tickgenSourceCount.With(map[string]string{"vertex": mg.name, "pipeline": mg.pipelineName})
+				tickgenSourceCount.With(map[string]string{metricspkg.LabelVertex: mg.name, metricspkg.LabelPipeline: mg.pipelineName})
 				// swapped implies that the rcount is at limit
 				if !atomic.CompareAndSwapInt32(&rcount, limit-1, limit) {
 					go func() {
diff --git a/pkg/sources/http/http.go b/pkg/sources/http/http.go
index e7f172a8..c2b0c017 100644
--- a/pkg/sources/http/http.go
+++ b/pkg/sources/http/http.go
@@ -14,6 +14,7 @@ import (
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/isb"
 	"github.com/numaproj/numaflow/pkg/isb/forward"
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 	sharedtls "github.com/numaproj/numaflow/pkg/shared/tls"
 	sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
@@ -186,7 +187,7 @@ func (h *httpSource) Read(ctx context.Context, count int64) ([]*isb.ReadMessage,
 			return msgs, nil
 		}
 	}
-	httpSourceReadCount.With(map[string]string{"vertex": h.name, "pipeline": h.pipelineName}).Inc()
+	httpSourceReadCount.With(map[string]string{metricspkg.LabelVertex: h.name, metricspkg.LabelPipeline: h.pipelineName}).Inc()
 	h.logger.Debugf("Read %d messages.", len(msgs))
 	return msgs, nil
 }
diff --git a/pkg/sources/http/metrics.go b/pkg/sources/http/metrics.go
index f90fd49e..aeaf7944 100644
--- a/pkg/sources/http/metrics.go
+++ b/pkg/sources/http/metrics.go
@@ -3,6 +3,8 @@ package http
 import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
+
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 )
 
 // httpSourceReadCount is used to indicate the number of messages read by the http source vertex
@@ -10,4 +12,4 @@ var httpSourceReadCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "http_source",
 	Name:      "read_total",
 	Help:      "Total number of messages Read",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
diff --git a/pkg/sources/kafka/metrics.go b/pkg/sources/kafka/metrics.go
index a78e0fa2..efb46496 100644
--- a/pkg/sources/kafka/metrics.go
+++ b/pkg/sources/kafka/metrics.go
@@ -3,6 +3,8 @@ package kafka
 import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
+
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 )
 
 // kafkaSourceReadCount is used to indicate the number of messages read
@@ -10,18 +12,18 @@ var kafkaSourceReadCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "kafka_source",
 	Name:      "read_total",
 	Help:      "Total number of messages Read",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
 
 // kafkaSourceAckCount is used to indicate the number of messages Acknowledged
 var kafkaSourceAckCount = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "kafka_source",
 	Name:      "ack_total",
 	Help:      "Total number of messages Acknowledged",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
 
 // kafkaSourceOffsetAckErrors is used to indicate the number of errors while reading from kafka source with offsets
 var kafkaSourceOffsetAckErrors = promauto.NewCounterVec(prometheus.CounterOpts{
 	Subsystem: "kafka_source",
 	Name:      "ack_error_total",
 	Help:      "Total number of Kafka ID Errors",
-}, []string{"vertex", "pipeline"})
+}, []string{metricspkg.LabelVertex, metricspkg.LabelPipeline})
diff --git a/pkg/sources/kafka/reader.go b/pkg/sources/kafka/reader.go
index 07839b98..2e24b441 100644
--- a/pkg/sources/kafka/reader.go
+++ b/pkg/sources/kafka/reader.go
@@ -15,6 +15,7 @@ import (
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/numaproj/numaflow/pkg/isb"
 	"github.com/numaproj/numaflow/pkg/isb/forward"
+	metricspkg "github.com/numaproj/numaflow/pkg/metrics"
 	"github.com/numaproj/numaflow/pkg/shared/logging"
 	"github.com/numaproj/numaflow/pkg/shared/util"
 	"github.com/numaproj/numaflow/pkg/udf/applier"
@@ -104,7 +105,7 @@ func (r *KafkaSource) Read(_ context.Context, count int64) ([]*isb.ReadMessage,
 	for i = 0; i < count; i++ {
 		select {
 		case m := <-r.handler.messages:
-			kafkaSourceReadCount.With(map[string]string{"vertex": r.name, "pipeline": r.pipelineName}).Inc()
+			kafkaSourceReadCount.With(map[string]string{metricspkg.LabelVertex: r.name, metricspkg.LabelPipeline: r.pipelineName}).Inc()
 			msgs = append(msgs, toReadMessage(m))
 
 		case <-time.After(r.readTimeout):
@@ -126,14 +127,14 @@ func (r *KafkaSource) Ack(_ context.Context, offsets []isb.Offset) []error {
 	for _, offset := range offsets {
 		topic, partition, poffset, err := offsetFrom(offset.String())
 		if err != nil {
-			kafkaSourceOffsetAckErrors.With(map[string]string{"vertex": r.name, "pipeline": r.pipelineName}).Inc()
+			kafkaSourceOffsetAckErrors.With(map[string]string{metricspkg.LabelVertex: r.name, metricspkg.LabelPipeline: r.pipelineName}).Inc()
 			r.logger.Errorw("Unable to extract partition offset of type int64 from the supplied offset. skipping and continuing", zap.String("suppliedoffset", offset.String()), zap.Error(err))
 			continue
 		}
 
 		// we need to mark the offset of the next message to read
 		r.handler.sess.MarkOffset(topic, partition, poffset+1, "")
-		kafkaSourceAckCount.With(map[string]string{"vertex": r.name, "pipeline": r.pipelineName}).Inc()
+		kafkaSourceAckCount.With(map[string]string{metricspkg.LabelVertex: r.name, metricspkg.LabelPipeline: r.pipelineName}).Inc()
 
 	}
 	// How come it does not return errors at all?
diff --git a/pkg/sources/source.go b/pkg/sources/source.go
index 1377dce6..f823bcbc 100644
--- a/pkg/sources/source.go
+++ b/pkg/sources/source.go
@@ -104,10 +104,7 @@ func (sp *SourceProcessor) Start(ctx context.Context) error {
 		}
 	}()
 
-	metricsOpts := []metrics.Option{}
-	if s := sp.VertexInstance.Vertex.Spec.Scale.LookbackSeconds; s != nil {
-		metricsOpts = append(metricsOpts, metrics.WithLookbackSeconds(int64(*s)))
-	}
+	metricsOpts := []metrics.Option{metrics.WithLookbackSeconds(int64(sp.VertexInstance.Vertex.Spec.Scale.GetLookbackSeconds()))}
 	if x, ok := sourcer.(isb.LagReader); ok {
 		metricsOpts = append(metricsOpts, metrics.WithLagReader(x))
 	}
diff --git a/pkg/udf/udf.go b/pkg/udf/udf.go
index 8a847d76..f9bb2d3e 100644
--- a/pkg/udf/udf.go
+++ b/pkg/udf/udf.go
@@ -149,10 +149,7 @@ func (u *UDFProcessor) Start(ctx context.Context) error {
 		}
 	}()
 
-	metricsOpts := []metrics.Option{}
-	if s := u.VertexInstance.Vertex.Spec.Scale.LookbackSeconds; s != nil {
-		metricsOpts = append(metricsOpts, metrics.WithLookbackSeconds(int64(*s)))
-	}
+	metricsOpts := []metrics.Option{metrics.WithLookbackSeconds(int64(u.VertexInstance.Vertex.Spec.Scale.GetLookbackSeconds()))}
 	if x, ok := reader.(isb.LagReader); ok {
 		metricsOpts = append(metricsOpts, metrics.WithLagReader(x))
 	}


Test patch:
diff --git a/controllers/pipeline/validate_test.go b/controllers/pipeline/validate_test.go
index 469fd3ca..ae33e8de 100644
--- a/controllers/pipeline/validate_test.go
+++ b/controllers/pipeline/validate_test.go
@@ -185,13 +185,13 @@ func TestValidateVertex(t *testing.T) {
 	t.Run("bad min", func(t *testing.T) {
 		v := dfv1.AbstractVertex{
 			Scale: dfv1.Scale{
-				Min: pointer.Int32(0),
+				Min: pointer.Int32(-1),
 				Max: pointer.Int32(1),
 			},
 		}
 		err := validateVertex(v)
 		assert.Error(t, err)
-		assert.Contains(t, err.Error(), "greater than 0")
+		assert.Contains(t, err.Error(), "not be smaller than 0")
 	})
 
 	t.Run("min > max", func(t *testing.T) {
diff --git a/controllers/vertex/controller_test.go b/controllers/vertex/controller_test.go
index c2ca698d..4c3f10db 100644
--- a/controllers/vertex/controller_test.go
+++ b/controllers/vertex/controller_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/numaproj/numaflow/controllers"
+	"github.com/numaproj/numaflow/controllers/vertex/scaling"
 	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
 	"github.com/stretchr/testify/assert"
 	"go.uber.org/zap/zaptest"
@@ -154,7 +155,7 @@ func init() {
 
 func Test_NewReconciler(t *testing.T) {
 	cl := fake.NewClientBuilder().Build()
-	r := NewReconciler(cl, scheme.Scheme, fakeConfig, testFlowImage, zaptest.NewLogger(t).Sugar())
+	r := NewReconciler(cl, scheme.Scheme, fakeConfig, testFlowImage, scaling.NewScaler(cl), zaptest.NewLogger(t).Sugar())
 	_, ok := r.(*vertexReconciler)
 	assert.True(t, ok)
 }
@@ -331,6 +332,7 @@ func Test_reconcile(t *testing.T) {
 			scheme: scheme.Scheme,
 			config: fakeConfig,
 			image:  testFlowImage,
+			scaler: scaling.NewScaler(cl),
 			logger: zaptest.NewLogger(t).Sugar(),
 		}
 		testObj := testVertex.DeepCopy()
@@ -376,6 +378,7 @@ func Test_reconcile(t *testing.T) {
 			scheme: scheme.Scheme,
 			config: fakeConfig,
 			image:  testFlowImage,
+			scaler: scaling.NewScaler(cl),
 			logger: zaptest.NewLogger(t).Sugar(),
 		}
 		testObj := testVertex.DeepCopy()
@@ -407,6 +410,7 @@ func Test_reconcile(t *testing.T) {
 			scheme: scheme.Scheme,
 			config: fakeConfig,
 			image:  testFlowImage,
+			scaler: scaling.NewScaler(cl),
 			logger: zaptest.NewLogger(t).Sugar(),
 		}
 		testObj := testVertex.DeepCopy()
diff --git a/controllers/vertex/scaling/scaling_test.go b/controllers/vertex/scaling/scaling_test.go
new file mode 100644
index 00000000..ef59cdce
--- /dev/null
+++ b/controllers/vertex/scaling/scaling_test.go
@@ -0,0 +1,73 @@
+package scaling
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"k8s.io/utils/pointer"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	dfv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
+)
+
+func Test_BadicOperations(t *testing.T) {
+	cl := fake.NewClientBuilder().Build()
+	s := NewScaler(cl)
+	assert.NotNil(t, s)
+	s.StartWatching("key1")
+	assert.True(t, s.Contains("key1"))
+	assert.Equal(t, 1, s.Length())
+	s.StopWatching("key1")
+	assert.False(t, s.Contains("key1"))
+}
+
+func Test_desiredReplicas(t *testing.T) {
+	cl := fake.NewClientBuilder().Build()
+	s := NewScaler(cl)
+	one := uint32(1)
+	src := &dfv1.Vertex{
+		Spec: dfv1.VertexSpec{
+			Replicas: pointer.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(0), s.desiredReplicas(context.TODO(), src, 0, 0, 10000, 5000))
+	assert.Equal(t, int32(8), s.desiredReplicas(context.TODO(), src, 2500, 10010, 30000, 20000))
+	assert.Equal(t, int32(8), s.desiredReplicas(context.TODO(), src, 2500, 9950, 30000, 20000))
+	assert.Equal(t, int32(7), s.desiredReplicas(context.TODO(), src, 2500, 8751, 30000, 20000))
+	assert.Equal(t, int32(7), s.desiredReplicas(context.TODO(), src, 2500, 8749, 30000, 20000))
+	assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), src, 0, 9950, 30000, 20000))
+	assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, 2500, 2, 30000, 20000))
+	assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), src, 2500, 0, 30000, 20000))
+
+	udf := &dfv1.Vertex{
+		Spec: dfv1.VertexSpec{
+			Replicas: pointer.Int32(2),
+			AbstractVertex: dfv1.AbstractVertex{
+				UDF: &dfv1.UDF{},
+			},
+		},
+		Status: dfv1.VertexStatus{
+			Replicas: uint32(2),
+		},
+	}
+	assert.Equal(t, int32(0), s.desiredReplicas(context.TODO(), udf, 0, 0, 10000, 5000))
+	assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, 250, 10000, 20000, 5000))
+	assert.Equal(t, int32(1), s.desiredReplicas(context.TODO(), udf, 250, 10000, 20000, 6000))
+	assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, 250, 10000, 20000, 7500))
+	assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, 250, 10000, 20000, 7900))
+	assert.Equal(t, int32(2), s.desiredReplicas(context.TODO(), udf, 250, 10000, 20000, 10000))
+	assert.Equal(t, int32(3), s.desiredReplicas(context.TODO(), udf, 250, 10000, 20000, 12500))
+	assert.Equal(t, int32(3), s.desiredReplicas(context.TODO(), udf, 250, 10000, 20000, 12550))
+}
diff --git a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
index c386484c..b9ce79a3 100644
--- a/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
+++ b/pkg/apis/numaflow/v1alpha1/vertex_types_test.go
@@ -351,3 +351,60 @@ func Test_VertexGetInitContainer(t *testing.T) {
 func TestGenerateEdgeBufferName(t *testing.T) {
 	assert.Equal(t, "a-b-c-d", GenerateEdgeBufferName("a", "b", "c", "d"))
 }
+
+func TestScalable(t *testing.T) {
+	v := Vertex{}
+	v.Spec.Scale.Disabled = true
+	assert.False(t, v.Scalable())
+	v.Spec.Scale.Disabled = false
+	v.Spec.Sink = &Sink{}
+	assert.True(t, v.Scalable())
+	v.Spec.Sink = nil
+	v.Spec.UDF = &UDF{}
+	assert.True(t, v.Scalable())
+	v.Spec.UDF = nil
+	v.Spec.Source = &Source{
+		HTTP: &HTTPSource{},
+	}
+	assert.False(t, v.Scalable())
+	v.Spec.Source = &Source{
+		Kafka: &KafkaSource{},
+	}
+	assert.True(t, v.Scalable())
+}
+
+func Test_Scale_Parameters(t *testing.T) {
+	s := Scale{}
+	assert.Equal(t, int32(0), s.GetMinReplicas())
+	assert.Equal(t, int32(DefaultMaxReplicas), s.GetMaxReplicas())
+	assert.Equal(t, DefaultCooldownSeconds, s.GetCooldownSeconds())
+	assert.Equal(t, DefaultLookbackSeconds, s.GetLookbackSeconds())
+	assert.Equal(t, DefaultReplicasPerScale, s.GetReplicasPerScale())
+	assert.Equal(t, DefaultTargetBufferUsage, s.GetTargetBufferUsage())
+	assert.Equal(t, DefaultTargetProcessingSeconds, s.GetTargetProcessingSeconds())
+	assert.Equal(t, DefaultZeroReplicaSleepSeconds, s.GetZeroReplicaSleepSeconds())
+	cds := uint32(100)
+	lbs := uint32(101)
+	rps := uint32(3)
+	tps := uint32(102)
+	tbu := uint32(33)
+	zrss := uint32(44)
+	s = Scale{
+		Min:                     pointer.Int32(2),
+		Max:                     pointer.Int32(4),
+		CooldownSeconds:         &cds,
+		LookbackSeconds:         &lbs,
+		ReplicasPerScale:        &rps,
+		TargetProcessingSeconds: &tps,
+		TargetBufferUsage:       &tbu,
+		ZeroReplicaSleepSeconds: &zrss,
+	}
+	assert.Equal(t, int32(2), s.GetMinReplicas())
+	assert.Equal(t, int32(4), s.GetMaxReplicas())
+	assert.Equal(t, int(cds), s.GetCooldownSeconds())
+	assert.Equal(t, int(lbs), s.GetLookbackSeconds())
+	assert.Equal(t, int(rps), s.GetReplicasPerScale())
+	assert.Equal(t, int(tbu), s.GetTargetBufferUsage())
+	assert.Equal(t, int(tps), s.GetTargetProcessingSeconds())
+	assert.Equal(t, int(zrss), s.GetZeroReplicaSleepSeconds())
+}
diff --git a/pkg/daemon/server/service/pipeline_metrics_query_service_test.go b/pkg/daemon/server/service/pipeline_metrics_query_service_test.go
index 9af36298..8f1e515f 100644
--- a/pkg/daemon/server/service/pipeline_metrics_query_service_test.go
+++ b/pkg/daemon/server/service/pipeline_metrics_query_service_test.go
@@ -64,6 +64,13 @@ vertex_processing_rate{period="15m",pipeline="simple-pipeline",vertex="cat"} 4.8
 vertex_processing_rate{period="1m",pipeline="simple-pipeline",vertex="cat"} 5.084745762711864
 vertex_processing_rate{period="5m",pipeline="simple-pipeline",vertex="cat"} 4.894736842105263
 vertex_processing_rate{period="default",pipeline="simple-pipeline",vertex="cat"} 4.894736842105263
+
+# HELP vertex_pending_messages Average pending messages in the last period of seconds. It is the pending messages of a vertex, not a pod.
+# TYPE vertex_pending_messages gauge
+vertex_pending_messages{period="15m",pipeline="simple-pipeline",vertex="cat"} 4.011
+vertex_pending_messages{period="1m",pipeline="simple-pipeline",vertex="cat"} 5.333
+vertex_pending_messages{period="5m",pipeline="simple-pipeline",vertex="cat"} 6.002
+vertex_pending_messages{period="default",pipeline="simple-pipeline",vertex="cat"} 7.00002
 `
 	ioReader := ioutil.NopCloser(bytes.NewReader([]byte(metricsResponse)))
 
@@ -84,13 +91,20 @@ vertex_processing_rate{period="default",pipeline="simple-pipeline",vertex="cat"}
 	resp, err := pipelineMetricsQueryService.GetVertexMetrics(context.Background(), req)
 	assert.NoError(t, err)
 
-	processingRates := make(map[string]float32)
+	processingRates := make(map[string]float64)
 
-	processingRates["15m"] = 4.894737
-	processingRates["1m"] = 5.084746
-	processingRates["5m"] = 4.894737
-	processingRates["default"] = 4.894737
+	processingRates["15m"] = 4.894736842105263
+	processingRates["1m"] = 5.084745762711864
+	processingRates["5m"] = 4.894736842105263
+	processingRates["default"] = 4.894736842105263
 	assert.Equal(t, resp.Vertex.GetProcessingRates(), processingRates)
+
+	pendings := make(map[string]int64)
+	pendings["15m"] = 4
+	pendings["1m"] = 5
+	pendings["5m"] = 6
+	pendings["default"] = 7
+	assert.Equal(t, resp.Vertex.GetPendings(), pendings)
 }
 
 func TestGetBuffer(t *testing.T) {
diff --git a/test/manifests/kustomization.yaml b/test/manifests/kustomization.yaml
index ad09daf8..1b71ca91 100644
--- a/test/manifests/kustomization.yaml
+++ b/test/manifests/kustomization.yaml
@@ -9,15 +9,16 @@ patches:
       - op: add
         path: /spec/template/spec/containers/0/imagePullPolicy
         value: IfNotPresent
-    target:
-      kind: Deployment
-      name: numaflow-controller
-  - patch: |-
       - op: add
         path: /spec/template/spec/containers/0/env/-
         value:
           name: NUMAFLOW_IMAGE_PULL_POLICY
           value: IfNotPresent
+      - op: add
+        path: /spec/template/spec/containers/0/env/-
+        value:
+          name: NUMAFLOW_DEBUG
+          value: "true"
     target:
       kind: Deployment
       name: numaflow-controller