Repository: matrixorigin/matrixone
Pull request #11993 (2023-10-07 09:28:35)

[feature] proxy: add heartbeat to hakeeper to support upload configs.
## What type of PR is this?

- [ ] API-change
- [ ] BUG
- [ ] Improvement
- [ ] Documentation
- [x] Feature
- [ ] Test and CI
- [ ] Code Refactoring

## Which issue(s) this PR fixes:

issue #11921 

## What this PR does / why we need it:
Add heartbeat to HAKeeper for proxy to upload configurations.
Then configuration fetched from HAKeeper is supported.


Patch:
diff --git a/pkg/hakeeper/checkers/coordinator.go b/pkg/hakeeper/checkers/coordinator.go
index 4a9889aea..85188047b 100644
--- a/pkg/hakeeper/checkers/coordinator.go
+++ b/pkg/hakeeper/checkers/coordinator.go
@@ -20,6 +20,7 @@ import (
 	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/cnservice"
 	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/dnservice"
 	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/logservice"
+	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/proxy"
 	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/syshealth"
 	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/util"
 	"github.com/matrixorigin/matrixone/pkg/hakeeper/operator"
@@ -51,6 +52,7 @@ func (c *Coordinator) Check(alloc util.IDAllocator, state pb.CheckerState) []pb.
 	logState := state.LogState
 	tnState := state.TNState
 	cnState := state.CNState
+	proxyState := state.ProxyState
 	cluster := state.ClusterInfo
 	currentTick := state.Tick
 	user := state.TaskTableUser
@@ -68,18 +70,18 @@ func (c *Coordinator) Check(alloc util.IDAllocator, state pb.CheckerState) []pb.
 		}
 	}()
 
-	c.OperatorController.RemoveFinishedOperator(logState, tnState, cnState)
+	c.OperatorController.RemoveFinishedOperator(logState, tnState, cnState, proxyState)
 
 	// if we've discovered unhealthy already, no need to keep alive anymore.
 	if c.teardown {
-		return c.OperatorController.Dispatch(c.teardownOps, logState, tnState, cnState)
+		return c.OperatorController.Dispatch(c.teardownOps, logState, tnState, cnState, proxyState)
 	}
 
 	// check whether system health or not.
 	if operators, health := syshealth.Check(c.cfg, cluster, tnState, logState, currentTick); !health {
 		c.teardown = true
 		c.teardownOps = operators
-		return c.OperatorController.Dispatch(c.teardownOps, logState, tnState, cnState)
+		return c.OperatorController.Dispatch(c.teardownOps, logState, tnState, cnState, proxyState)
 	}
 
 	// system health, try to keep alive.
@@ -89,6 +91,7 @@ func (c *Coordinator) Check(alloc util.IDAllocator, state pb.CheckerState) []pb.
 	operators = append(operators, logservice.Check(alloc, c.cfg, cluster, logState, executing, user, currentTick)...)
 	operators = append(operators, dnservice.Check(alloc, c.cfg, cluster, tnState, user, currentTick)...)
 	operators = append(operators, cnservice.Check(c.cfg, cnState, user, currentTick)...)
+	operators = append(operators, proxy.Check(c.cfg, proxyState, currentTick)...)
 
-	return c.OperatorController.Dispatch(operators, logState, tnState, cnState)
+	return c.OperatorController.Dispatch(operators, logState, tnState, cnState, proxyState)
 }
diff --git a/pkg/hakeeper/checkers/proxy/check.go b/pkg/hakeeper/checkers/proxy/check.go
new file mode 100644
index 000000000..e97503c02
--- /dev/null
+++ b/pkg/hakeeper/checkers/proxy/check.go
@@ -0,0 +1,43 @@
+// Copyright 2021 - 2023 Matrix Origin
+//
+// 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 proxy
+
+import (
+	"github.com/matrixorigin/matrixone/pkg/hakeeper"
+	"github.com/matrixorigin/matrixone/pkg/hakeeper/operator"
+	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
+)
+
+func Check(cfg hakeeper.Config, infos pb.ProxyState, currentTick uint64) (operators []*operator.Operator) {
+	_, expired := parseProxyStores(cfg, infos, currentTick)
+	for _, uuid := range expired {
+		operators = append(operators, operator.CreateDeleteProxyOp("", uuid))
+	}
+	return operators
+}
+
+// parseProxyStores returns all working and expired stores' ids.
+func parseProxyStores(cfg hakeeper.Config, infos pb.ProxyState, currentTick uint64) ([]string, []string) {
+	working := make([]string, 0)
+	expired := make([]string, 0)
+	for uuid, storeInfo := range infos.Stores {
+		if cfg.ProxyStoreExpired(storeInfo.Tick, currentTick) {
+			expired = append(expired, uuid)
+		} else {
+			working = append(working, uuid)
+		}
+	}
+	return working, expired
+}
diff --git a/pkg/hakeeper/config.go b/pkg/hakeeper/config.go
index a1b788e0d..bee0db221 100644
--- a/pkg/hakeeper/config.go
+++ b/pkg/hakeeper/config.go
@@ -19,10 +19,11 @@ import (
 )
 
 const (
-	DefaultTickPerSecond   = 10
-	DefaultLogStoreTimeout = 5 * time.Minute
-	DefaultTNStoreTimeout  = 10 * time.Second
-	DefaultCNStoreTimeout  = 30 * time.Second
+	DefaultTickPerSecond     = 10
+	DefaultLogStoreTimeout   = 5 * time.Minute
+	DefaultTNStoreTimeout    = 10 * time.Second
+	DefaultCNStoreTimeout    = 30 * time.Second
+	DefaultProxyStoreTimeout = 30 * time.Second
 )
 
 type Config struct {
@@ -45,6 +46,11 @@ type Config struct {
 	// If HAKeeper does not receive two heartbeat within CNStoreTimeout,
 	// it regards the tn store as down.
 	CNStoreTimeout time.Duration
+
+	// ProxyStoreTimeout is the actual time limit between a proxy store's heartbeat.
+	// If HAKeeper does not receive two heartbeat within ProxyStoreTimeout,
+	// it regards the proxy store as down.
+	ProxyStoreTimeout time.Duration
 }
 
 func (cfg Config) Validate() error {
@@ -64,6 +70,9 @@ func (cfg *Config) Fill() {
 	if cfg.CNStoreTimeout == 0 {
 		cfg.CNStoreTimeout = DefaultCNStoreTimeout
 	}
+	if cfg.ProxyStoreTimeout == 0 {
+		cfg.ProxyStoreTimeout = DefaultProxyStoreTimeout
+	}
 }
 
 func (cfg Config) LogStoreExpired(start, current uint64) bool {
@@ -78,6 +87,10 @@ func (cfg Config) CNStoreExpired(start, current uint64) bool {
 	return uint64(int(cfg.CNStoreTimeout/time.Second)*cfg.TickPerSecond)+start < current
 }
 
+func (cfg Config) ProxyStoreExpired(start, current uint64) bool {
+	return uint64(int(cfg.ProxyStoreTimeout/time.Second)*cfg.TickPerSecond)+start < current
+}
+
 func (cfg Config) ExpiredTick(start uint64, timeout time.Duration) uint64 {
 	return uint64(timeout/time.Second)*uint64(cfg.TickPerSecond) + start
 }
diff --git a/pkg/hakeeper/operator/controller.go b/pkg/hakeeper/operator/controller.go
index cdf0f984e..05af556bd 100644
--- a/pkg/hakeeper/operator/controller.go
+++ b/pkg/hakeeper/operator/controller.go
@@ -93,10 +93,12 @@ func (c *Controller) GetExecutingReplicas() ExecutingReplicas {
 	return executing
 }
 
-func (c *Controller) RemoveFinishedOperator(logState pb.LogState, tnState pb.TNState, cnState pb.CNState) {
+func (c *Controller) RemoveFinishedOperator(
+	logState pb.LogState, tnState pb.TNState, cnState pb.CNState, proxyState pb.ProxyState,
+) {
 	for _, ops := range c.operators {
 		for _, op := range ops {
-			op.Check(logState, tnState, cnState)
+			op.Check(logState, tnState, cnState, proxyState)
 			switch op.Status() {
 			case SUCCESS, EXPIRED:
 				c.RemoveOperator(op)
@@ -106,10 +108,10 @@ func (c *Controller) RemoveFinishedOperator(logState pb.LogState, tnState pb.TNS
 }
 
 func (c *Controller) Dispatch(ops []*Operator, logState pb.LogState,
-	tnState pb.TNState, cnState pb.CNState) (commands []pb.ScheduleCommand) {
+	tnState pb.TNState, cnState pb.CNState, proxyState pb.ProxyState) (commands []pb.ScheduleCommand) {
 	for _, op := range ops {
 		c.operators[op.shardID] = append(c.operators[op.shardID], op)
-		if step := op.Check(logState, tnState, cnState); step != nil {
+		if step := op.Check(logState, tnState, cnState, proxyState); step != nil {
 			commands = append(commands, generateScheduleCommand(step))
 		}
 	}
@@ -142,6 +144,8 @@ func generateScheduleCommand(step OpStep) pb.ScheduleCommand {
 		return deleteCNStore(st)
 	case JoinGossipCluster:
 		return joinGossipCluster(st)
+	case DeleteProxyStore:
+		return deleteProxyStore(st)
 	}
 	panic("invalid schedule command")
 }
@@ -306,3 +310,13 @@ func joinGossipCluster(st JoinGossipCluster) pb.ScheduleCommand {
 		},
 	}
 }
+
+func deleteProxyStore(st DeleteProxyStore) pb.ScheduleCommand {
+	return pb.ScheduleCommand{
+		UUID:        st.StoreID,
+		ServiceType: pb.ProxyService,
+		DeleteProxyStore: &pb.DeleteProxyStore{
+			StoreID: st.StoreID,
+		},
+	}
+}
diff --git a/pkg/hakeeper/operator/create_operator.go b/pkg/hakeeper/operator/create_operator.go
index fabe912f7..505c0ae42 100644
--- a/pkg/hakeeper/operator/create_operator.go
+++ b/pkg/hakeeper/operator/create_operator.go
@@ -62,3 +62,7 @@ func JoinGossipClusterOp(brief, uuid string, existing []string) *Operator {
 		JoinGossipCluster{StoreID: uuid, Existing: existing},
 	)
 }
+
+func CreateDeleteProxyOp(brief, uuid string) *Operator {
+	return NewOperator(brief, 0, 0, DeleteProxyStore{StoreID: uuid})
+}
diff --git a/pkg/hakeeper/operator/operator.go b/pkg/hakeeper/operator/operator.go
index 8ca6b143f..0a10eed30 100644
--- a/pkg/hakeeper/operator/operator.go
+++ b/pkg/hakeeper/operator/operator.go
@@ -113,14 +113,21 @@ func (o *Operator) CheckExpired() bool {
 	return o.status.CheckExpired(ExpireTime)
 }
 
-func (o *Operator) Check(logState pb.LogState, tnState pb.TNState, cnState pb.CNState) OpStep {
+func (o *Operator) Check(
+	logState pb.LogState, tnState pb.TNState, cnState pb.CNState, proxyState pb.ProxyState,
+) OpStep {
 	if o.IsEnd() {
 		return nil
 	}
 	// CheckExpired will call CheckSuccess first
 	defer func() { _ = o.CheckExpired() }()
 	for step := o.currentStep; int(step) < len(o.steps); step++ {
-		if o.steps[int(step)].IsFinish(logState, tnState, cnState) {
+		if o.steps[int(step)].IsFinish(ClusterState{
+			LogState:   logState,
+			TNState:    tnState,
+			CNState:    cnState,
+			ProxyState: proxyState,
+		}) {
 			o.currentStep = step + 1
 		} else {
 			return o.steps[int(step)]
diff --git a/pkg/hakeeper/operator/steps.go b/pkg/hakeeper/operator/steps.go
index a97403efa..c7ca5be43 100644
--- a/pkg/hakeeper/operator/steps.go
+++ b/pkg/hakeeper/operator/steps.go
@@ -26,10 +26,17 @@ import (
 	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
 )
 
+type ClusterState struct {
+	LogState   pb.LogState
+	TNState    pb.TNState
+	CNState    pb.CNState
+	ProxyState pb.ProxyState
+}
+
 type OpStep interface {
 	fmt.Stringer
 
-	IsFinish(state pb.LogState, tnState pb.TNState, cnState pb.CNState) bool
+	IsFinish(state ClusterState) bool
 }
 
 type Replica struct {
@@ -48,11 +55,11 @@ func (a AddLogService) String() string {
 	return fmt.Sprintf("adding %v:%v(at epoch %v) to %s", a.ShardID, a.ReplicaID, a.Epoch, a.UUID)
 }
 
-func (a AddLogService) IsFinish(state pb.LogState, _ pb.TNState, _ pb.CNState) bool {
-	if _, ok := state.Shards[a.ShardID]; !ok {
+func (a AddLogService) IsFinish(state ClusterState) bool {
+	if _, ok := state.LogState.Shards[a.ShardID]; !ok {
 		return true
 	}
-	if _, ok := state.Shards[a.ShardID].Replicas[a.ReplicaID]; ok {
+	if _, ok := state.LogState.Shards[a.ShardID].Replicas[a.ReplicaID]; ok {
 		return true
 	}
 
@@ -68,8 +75,8 @@ func (a RemoveLogService) String() string {
 	return fmt.Sprintf("removing %v:%v(at epoch %v) on log store %s", a.ShardID, a.ReplicaID, a.Epoch, a.UUID)
 }
 
-func (a RemoveLogService) IsFinish(state pb.LogState, _ pb.TNState, _ pb.CNState) bool {
-	if shard, ok := state.Shards[a.ShardID]; ok {
+func (a RemoveLogService) IsFinish(state ClusterState) bool {
+	if shard, ok := state.LogState.Shards[a.ShardID]; ok {
 		if _, ok := shard.Replicas[a.ReplicaID]; ok {
 			return false
 		}
@@ -86,11 +93,11 @@ func (a StartLogService) String() string {
 	return fmt.Sprintf("starting %v:%v on %s", a.ShardID, a.ReplicaID, a.UUID)
 }
 
-func (a StartLogService) IsFinish(state pb.LogState, _ pb.TNState, _ pb.CNState) bool {
-	if _, ok := state.Stores[a.UUID]; !ok {
+func (a StartLogService) IsFinish(state ClusterState) bool {
+	if _, ok := state.LogState.Stores[a.UUID]; !ok {
 		return true
 	}
-	for _, replicaInfo := range state.Stores[a.UUID].Replicas {
+	for _, replicaInfo := range state.LogState.Stores[a.UUID].Replicas {
 		if replicaInfo.ShardID == a.ShardID {
 			return true
 		}
@@ -107,8 +114,8 @@ func (a StopLogService) String() string {
 	return fmt.Sprintf("stopping %v on %s", a.ShardID, a.UUID)
 }
 
-func (a StopLogService) IsFinish(state pb.LogState, _ pb.TNState, _ pb.CNState) bool {
-	if store, ok := state.Stores[a.UUID]; ok {
+func (a StopLogService) IsFinish(state ClusterState) bool {
+	if store, ok := state.LogState.Stores[a.UUID]; ok {
 		for _, replicaInfo := range store.Replicas {
 			if replicaInfo.ShardID == a.ShardID {
 				return false
@@ -127,8 +134,8 @@ func (a KillLogZombie) String() string {
 	return fmt.Sprintf("killing zombie on %s", a.UUID)
 }
 
-func (a KillLogZombie) IsFinish(state pb.LogState, _ pb.TNState, _ pb.CNState) bool {
-	if store, ok := state.Stores[a.UUID]; ok {
+func (a KillLogZombie) IsFinish(state ClusterState) bool {
+	if store, ok := state.LogState.Stores[a.UUID]; ok {
 		for _, replicaInfo := range store.Replicas {
 			if replicaInfo.ShardID == a.ShardID {
 				return false
@@ -151,8 +158,8 @@ func (a AddTnReplica) String() string {
 	)
 }
 
-func (a AddTnReplica) IsFinish(_ pb.LogState, state pb.TNState, _ pb.CNState) bool {
-	for _, info := range state.Stores[a.StoreID].Shards {
+func (a AddTnReplica) IsFinish(state ClusterState) bool {
+	for _, info := range state.TNState.Stores[a.StoreID].Shards {
 		if a.ShardID == info.GetShardID() && a.ReplicaID == info.GetReplicaID() {
 			return true
 		}
@@ -172,8 +179,8 @@ func (a RemoveTnReplica) String() string {
 	)
 }
 
-func (a RemoveTnReplica) IsFinish(_ pb.LogState, state pb.TNState, _ pb.CNState) bool {
-	for _, info := range state.Stores[a.StoreID].Shards {
+func (a RemoveTnReplica) IsFinish(state ClusterState) bool {
+	for _, info := range state.TNState.Stores[a.StoreID].Shards {
 		if a.ShardID == info.GetShardID() && a.ReplicaID == info.GetReplicaID() {
 			return false
 		}
@@ -190,8 +197,8 @@ func (a StopTnStore) String() string {
 	return fmt.Sprintf("stopping tn store %s", a.StoreID)
 }
 
-func (a StopTnStore) IsFinish(_ pb.LogState, state pb.TNState, _ pb.CNState) bool {
-	if _, ok := state.Stores[a.StoreID]; ok {
+func (a StopTnStore) IsFinish(state ClusterState) bool {
+	if _, ok := state.TNState.Stores[a.StoreID]; ok {
 		return false
 	}
 	return true
@@ -206,8 +213,8 @@ func (a StopLogStore) String() string {
 	return fmt.Sprintf("stopping log store %s", a.StoreID)
 }
 
-func (a StopLogStore) IsFinish(state pb.LogState, _ pb.TNState, _ pb.CNState) bool {
-	if _, ok := state.Stores[a.StoreID]; ok {
+func (a StopLogStore) IsFinish(state ClusterState) bool {
+	if _, ok := state.LogState.Stores[a.StoreID]; ok {
 		return false
 	}
 	return true
@@ -223,16 +230,16 @@ func (a CreateTaskService) String() string {
 	return fmt.Sprintf("create task service on %s(%s)", a.StoreID, a.StoreType)
 }
 
-func (a CreateTaskService) IsFinish(logState pb.LogState, tnState pb.TNState, cnState pb.CNState) bool {
-	if state, ok := logState.Stores[a.StoreID]; ok {
+func (a CreateTaskService) IsFinish(state ClusterState) bool {
+	if state, ok := state.LogState.Stores[a.StoreID]; ok {
 		return state.GetTaskServiceCreated()
 	}
 
-	if state, ok := tnState.Stores[a.StoreID]; ok {
+	if state, ok := state.TNState.Stores[a.StoreID]; ok {
 		return state.GetTaskServiceCreated()
 	}
 
-	if state, ok := cnState.Stores[a.StoreID]; ok {
+	if state, ok := state.CNState.Stores[a.StoreID]; ok {
 		return state.GetTaskServiceCreated()
 	}
 
@@ -247,8 +254,8 @@ func (a DeleteCNStore) String() string {
 	return fmt.Sprintf("deleting cn store %s", a.StoreID)
 }
 
-func (a DeleteCNStore) IsFinish(_ pb.LogState, _ pb.TNState, state pb.CNState) bool {
-	if _, ok := state.Stores[a.StoreID]; ok {
+func (a DeleteCNStore) IsFinish(state ClusterState) bool {
+	if _, ok := state.CNState.Stores[a.StoreID]; ok {
 		return false
 	}
 	return true
@@ -263,9 +270,24 @@ func (a JoinGossipCluster) String() string {
 	return fmt.Sprintf("join gossip cluster for %s", a.StoreID)
 }
 
-func (a JoinGossipCluster) IsFinish(_ pb.LogState, _ pb.TNState, state pb.CNState) bool {
-	if state, ok := state.Stores[a.StoreID]; ok {
+func (a JoinGossipCluster) IsFinish(state ClusterState) bool {
+	if state, ok := state.CNState.Stores[a.StoreID]; ok {
 		return state.GetGossipJoined()
 	}
 	return true
 }
+
+type DeleteProxyStore struct {
+	StoreID string
+}
+
+func (a DeleteProxyStore) String() string {
+	return fmt.Sprintf("deleting proxy store %s", a.StoreID)
+}
+
+func (a DeleteProxyStore) IsFinish(state ClusterState) bool {
+	if _, ok := state.ProxyState.Stores[a.StoreID]; ok {
+		return false
+	}
+	return true
+}
diff --git a/pkg/hakeeper/rsm.go b/pkg/hakeeper/rsm.go
index 188d01254..b64636d40 100644
--- a/pkg/hakeeper/rsm.go
+++ b/pkg/hakeeper/rsm.go
@@ -240,6 +240,10 @@ func GetTNStoreHeartbeatCmd(data []byte) []byte {
 	return getHeartbeatCmd(data, pb.TNHeartbeatUpdate)
 }
 
+func GetProxyHeartbeatCmd(data []byte) []byte {
+	return getHeartbeatCmd(data, pb.ProxyHeartbeatUpdate)
+}
+
 func getHeartbeatCmd(data []byte, tag pb.HAKeeperUpdateType) []byte {
 	cmd := make([]byte, headerSize+len(data))
 	binaryEnc.PutUint32(cmd, uint32(tag))
@@ -348,6 +352,10 @@ func (s *stateMachine) handleUpdateCommandsCmd(cmd []byte) sm.Result {
 			s.handleDeleteCNCmd(c.UUID)
 			continue
 		}
+		if c.DeleteProxyStore != nil {
+			s.handleDeleteProxyCmd(c.UUID)
+			continue
+		}
 		l, ok := s.state.ScheduleCommands[c.UUID]
 		if !ok {
 			l = pb.CommandBatch{
@@ -516,6 +524,21 @@ func (s *stateMachine) handleDeleteCNCmd(uuid string) sm.Result {
 	return sm.Result{}
 }
 
+func (s *stateMachine) handleDeleteProxyCmd(uuid string) sm.Result {
+	delete(s.state.ProxyState.Stores, uuid)
+	return sm.Result{}
+}
+
+func (s *stateMachine) handleProxyHeartbeat(cmd []byte) sm.Result {
+	data := parseHeartbeatCmd(cmd)
+	var hb pb.ProxyHeartbeat
+	if err := hb.Unmarshal(data); err != nil {
+		panic(err)
+	}
+	s.state.ProxyState.Update(hb, s.state.Tick)
+	return s.getCommandBatch(hb.UUID)
+}
+
 // FIXME: NextID should be set to K8SIDRangeEnd once HAKeeper state is
 // set to HAKeeperBootstrapping.
 func (s *stateMachine) handleInitialClusterRequestCmd(cmd []byte) sm.Result {
@@ -619,6 +642,8 @@ func (s *stateMachine) Update(e sm.Entry) (sm.Result, error) {
 		return s.handlePatchCNStore(cmd), nil
 	case pb.RemoveCNStore:
 		return s.handleDeleteCNCmd(parseDeleteCNStoreCmd(cmd).StoreID), nil
+	case pb.ProxyHeartbeatUpdate:
+		return s.handleProxyHeartbeat(cmd), nil
 	default:
 		panic(moerr.NewInvalidInputNoCtx("unknown haKeeper cmd '%v'", cmd))
 	}
@@ -631,6 +656,7 @@ func (s *stateMachine) handleStateQuery() interface{} {
 		TNState:            s.state.TNState,
 		LogState:           s.state.LogState,
 		CNState:            s.state.CNState,
+		ProxyState:         s.state.ProxyState,
 		State:              s.state.State,
 		TaskSchedulerState: s.state.TaskSchedulerState,
 		TaskTableUser:      s.state.TaskTableUser,
@@ -655,9 +681,10 @@ func (s *stateMachine) handleScheduleCommandQuery(uuid string) *pb.CommandBatch
 func (s *stateMachine) handleClusterDetailsQuery(cfg Config) *pb.ClusterDetails {
 	cfg.Fill()
 	cd := &pb.ClusterDetails{
-		CNStores:  make([]pb.CNStore, 0, len(s.state.CNState.Stores)),
-		TNStores:  make([]pb.TNStore, 0, len(s.state.TNState.Stores)),
-		LogStores: make([]pb.LogStore, 0, len(s.state.LogState.Stores)),
+		CNStores:    make([]pb.CNStore, 0, len(s.state.CNState.Stores)),
+		TNStores:    make([]pb.TNStore, 0, len(s.state.TNState.Stores)),
+		LogStores:   make([]pb.LogStore, 0, len(s.state.LogState.Stores)),
+		ProxyStores: make([]pb.ProxyStore, 0, len(s.state.ProxyState.Stores)),
 	}
 	for uuid, info := range s.state.CNState.Stores {
 		state := pb.NormalState
@@ -712,6 +739,14 @@ func (s *stateMachine) handleClusterDetailsQuery(cfg Config) *pb.ClusterDetails
 		}
 		cd.LogStores = append(cd.LogStores, n)
 	}
+	for uuid, info := range s.state.ProxyState.Stores {
+		cd.ProxyStores = append(cd.ProxyStores, pb.ProxyStore{
+			UUID:          uuid,
+			Tick:          info.Tick,
+			ListenAddress: info.ListenAddress,
+			ConfigData:    info.ConfigData,
+		})
+	}
 	return cd
 }
 
diff --git a/pkg/logservice/hakeeper_client.go b/pkg/logservice/hakeeper_client.go
index 28f235367..828ac6975 100644
--- a/pkg/logservice/hakeeper_client.go
+++ b/pkg/logservice/hakeeper_client.go
@@ -90,6 +90,8 @@ type ProxyHAKeeperClient interface {
 	PatchCNStore(ctx context.Context, stateLabel pb.CNStateLabel) error
 	// DeleteCNStore deletes a CN store from HAKeeper.
 	DeleteCNStore(ctx context.Context, cnStore pb.DeleteCNStore) error
+	// SendProxyHeartbeat sends the heartbeat of proxy to HAKeeper.
+	SendProxyHeartbeat(ctx context.Context, hb pb.ProxyHeartbeat) (pb.CommandBatch, error)
 }
 
 // BRHAKeeperClient is the HAKeeper client for backup and restore.
@@ -441,6 +443,23 @@ func (c *managedHAKeeperClient) DeleteCNStore(ctx context.Context, cnStore pb.De
 	}
 }
 
+// SendProxyHeartbeat implements the ProxyHAKeeperClient interface.
+func (c *managedHAKeeperClient) SendProxyHeartbeat(ctx context.Context, hb pb.ProxyHeartbeat) (pb.CommandBatch, error) {
+	for {
+		if err := c.prepareClient(ctx); err != nil {
+			return pb.CommandBatch{}, err
+		}
+		cb, err := c.getClient().sendProxyHeartbeat(ctx, hb)
+		if err != nil {
+			c.resetClient()
+		}
+		if c.isRetryableError(err) {
+			continue
+		}
+		return cb, err
+	}
+}
+
 // GetBackupData implements the BRHAKeeperClient interface.
 func (c *managedHAKeeperClient) GetBackupData(ctx context.Context) ([]byte, error) {
 	for {
@@ -742,6 +761,18 @@ func (c *hakeeperClient) deleteCNStore(ctx context.Context, cnStore pb.DeleteCNS
 	return nil
 }
 
+func (c *hakeeperClient) sendProxyHeartbeat(ctx context.Context, hb pb.ProxyHeartbeat) (pb.CommandBatch, error) {
+	req := pb.Request{
+		Method:         pb.PROXY_HEARTBEAT,
+		ProxyHeartbeat: &hb,
+	}
+	cb, err := c.sendHeartbeat(ctx, req)
+	if err != nil {
+		return pb.CommandBatch{}, err
+	}
+	return cb, nil
+}
+
 func (c *hakeeperClient) checkIsHAKeeper(ctx context.Context) (bool, error) {
 	req := pb.Request{
 		Method: pb.CHECK_HAKEEPER,
diff --git a/pkg/logservice/service.go b/pkg/logservice/service.go
index 7b7727778..da12e647e 100644
--- a/pkg/logservice/service.go
+++ b/pkg/logservice/service.go
@@ -291,6 +291,8 @@ func (s *Service) handle(ctx context.Context, req pb.Request,
 		return s.handlePatchCNStore(ctx, req), pb.LogRecordResponse{}
 	case pb.DELETE_CN_STORE:
 		return s.handleDeleteCNStore(ctx, req), pb.LogRecordResponse{}
+	case pb.PROXY_HEARTBEAT:
+		return s.handleProxyHeartbeat(ctx, req), pb.LogRecordResponse{}
 	default:
 		resp := getResponse(req)
 		resp.ErrorCode, resp.ErrorMessage = toErrorCode(
@@ -508,6 +510,17 @@ func (s *Service) handleDeleteCNStore(ctx context.Context, req pb.Request) pb.Re
 	return resp
 }
 
+func (s *Service) handleProxyHeartbeat(ctx context.Context, req pb.Request) pb.Response {
+	resp := getResponse(req)
+	if cb, err := s.store.addProxyHeartbeat(ctx, *req.ProxyHeartbeat); err != nil {
+		resp.ErrorCode, resp.ErrorMessage = toErrorCode(err)
+		return resp
+	} else {
+		resp.CommandBatch = &cb
+	}
+	return resp
+}
+
 func (s *Service) getBackendOptions() []morpc.BackendOption {
 	return []morpc.BackendOption{
 		morpc.WithBackendFilter(func(msg morpc.Message, backendAddr string) bool {
diff --git a/pkg/logservice/store.go b/pkg/logservice/store.go
index 70e250987..f3ce2d612 100644
--- a/pkg/logservice/store.go
+++ b/pkg/logservice/store.go
@@ -616,6 +616,20 @@ func (l *store) deleteCNStore(ctx context.Context, cnStore pb.DeleteCNStore) err
 	}
 }
 
+func (l *store) addProxyHeartbeat(ctx context.Context, hb pb.ProxyHeartbeat) (pb.CommandBatch, error) {
+	data := MustMarshal(&hb)
+	cmd := hakeeper.GetProxyHeartbeatCmd(data)
+	session := l.nh.GetNoOPSession(hakeeper.DefaultHAKeeperShardID)
+	if result, err := l.propose(ctx, session, cmd); err != nil {
+		l.runtime.Logger().Error("propose failed", zap.Error(err))
+		return pb.CommandBatch{}, handleNotHAKeeperError(ctx, err)
+	} else {
+		var cb pb.CommandBatch
+		MustUnmarshal(&cb, result.Data)
+		return cb, nil
+	}
+}
+
 func (l *store) decodeCmd(ctx context.Context, e raftpb.Entry) []byte {
 	if e.Type == raftpb.ApplicationEntry {
 		panic(moerr.NewInvalidState(ctx, "unexpected entry type"))
diff --git a/pkg/pb/logservice/logservice.go b/pkg/pb/logservice/logservice.go
index 3c2d328e2..2c4cbcea9 100644
--- a/pkg/pb/logservice/logservice.go
+++ b/pkg/pb/logservice/logservice.go
@@ -49,6 +49,7 @@ func NewRSMState() HAKeeperRSMState {
 		CNState:          NewCNState(),
 		TNState:          NewTNState(),
 		LogState:         NewLogState(),
+		ProxyState:       NewProxyState(),
 		ClusterInfo:      newClusterInfo(),
 	}
 }
@@ -285,3 +286,24 @@ func (m *ScheduleCommand) LogString() string {
 
 	return s
 }
+
+// NewProxyState creates a new ProxyState.
+func NewProxyState() ProxyState {
+	return ProxyState{
+		Stores: make(map[string]ProxyStore),
+	}
+}
+
+func (s *ProxyState) Update(hb ProxyHeartbeat, tick uint64) {
+	storeInfo, ok := s.Stores[hb.UUID]
+	if !ok {
+		storeInfo = ProxyStore{}
+	}
+	storeInfo.UUID = hb.UUID
+	storeInfo.Tick = tick
+	storeInfo.ListenAddress = hb.ListenAddress
+	if hb.ConfigData != nil {
+		storeInfo.ConfigData = hb.ConfigData
+	}
+	s.Stores[hb.UUID] = storeInfo
+}
diff --git a/pkg/pb/logservice/logservice.pb.go b/pkg/pb/logservice/logservice.pb.go
index 22626b7eb..6a5f59f78 100644
--- a/pkg/pb/logservice/logservice.pb.go
+++ b/pkg/pb/logservice/logservice.pb.go
@@ -103,6 +103,7 @@ const (
 	UPDATE_CN_WORK_STATE MethodType = 16
 	PATCH_CN_STORE       MethodType = 17
 	DELETE_CN_STORE      MethodType = 18
+	PROXY_HEARTBEAT      MethodType = 19
 )
 
 var MethodType_name = map[int32]string{
@@ -125,6 +126,7 @@ var MethodType_name = map[int32]string{
 	16: "UPDATE_CN_WORK_STATE",
 	17: "PATCH_CN_STORE",
 	18: "DELETE_CN_STORE",
+	19: "PROXY_HEARTBEAT",
 }
 
 var MethodType_value = map[string]int32{
@@ -147,6 +149,7 @@ var MethodType_value = map[string]int32{
 	"UPDATE_CN_WORK_STATE": 16,
 	"PATCH_CN_STORE":       17,
 	"DELETE_CN_STORE":      18,
+	"PROXY_HEARTBEAT":      19,
 }
 
 func (x MethodType) String() string {
@@ -230,6 +233,7 @@ const (
 	UpdateCNWorkState           HAKeeperUpdateType = 11
 	PatchCNStore                HAKeeperUpdateType = 12
 	RemoveCNStore               HAKeeperUpdateType = 13
+	ProxyHeartbeatUpdate        HAKeeperUpdateType = 14
 )
 
 var HAKeeperUpdateType_name = map[int32]string{
@@ -247,6 +251,7 @@ var HAKeeperUpdateType_name = map[int32]string{
 	11: "UpdateCNWorkState",
 	12: "PatchCNStore",
 	13: "RemoveCNStore",
+	14: "ProxyHeartbeatUpdate",
 }
 
 var HAKeeperUpdateType_value = map[string]int32{
@@ -264,6 +269,7 @@ var HAKeeperUpdateType_value = map[string]int32{
 	"UpdateCNWorkState":           11,
 	"PatchCNStore":                12,
 	"RemoveCNStore":               13,
+	"ProxyHeartbeatUpdate":        14,
 }
 
 func (x HAKeeperUpdateType) String() string {
@@ -404,21 +410,24 @@ func (ConfigChangeType) EnumDescriptor() ([]byte, []int) {
 type ServiceType int32
 
 const (
-	LogService ServiceType = 0
-	TNService  ServiceType = 1
-	CNService  ServiceType = 2
+	LogService   ServiceType = 0
+	TNService    ServiceType = 1
+	CNService    ServiceType = 2
+	ProxyService ServiceType = 3
 )
 
 var ServiceType_name = map[int32]string{
 	0: "LogService",
 	1: "TNService",
 	2: "CNService",
+	3: "ProxyService",
 }
 
 var ServiceType_value = map[string]int32{
-	"LogService": 0,
-	"TNService":  1,
-	"CNService":  2,
+	"LogService":   0,
+	"TNService":    1,
+	"CNService":    2,
+	"ProxyService": 3,
 }
 
 func (x ServiceType) String() string {
@@ -1846,6 +1855,7 @@ type Request struct {
 	CNWorkState          *CNWorkState       `protobuf:"bytes,10,opt,name=CNWorkState,proto3" json:"CNWorkState,omitempty"`
 	CNStateLabel         *CNStateLabel      `protobuf:"bytes,11,opt,name=CNStateLabel,proto3" json:"CNStateLabel,omitempty"`
 	DeleteCNStore        *DeleteCNStore     `protobuf:"bytes,12,opt,name=DeleteCNStore,proto3" json:"DeleteCNStore,omitempty"`
+	ProxyHeartbeat       *ProxyHeartbeat    `protobuf:"bytes,13,opt,name=ProxyHeartbeat,proto3" json:"ProxyHeartbeat,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
 	XXX_unrecognized     []byte             `json:"-"`
 	XXX_sizecache        int32              `json:"-"`
@@ -1968,6 +1978,13 @@ func (m *Request) GetDeleteCNStore() *DeleteCNStore {
 	return nil
 }
 
+func (m *Request) GetProxyHeartbeat() *ProxyHeartbeat {
+	if m != nil {
+		return m.ProxyHeartbeat
+	}
+	return nil
+}
+
 type LogResponse struct {
 	ShardID              uint64   `protobuf:"varint,1,opt,name=ShardID,proto3" json:"ShardID,omitempty"`
 	Lsn                  uint64   `protobuf:"varint,2,opt,name=Lsn,proto3" json:"Lsn,omitempty"`
@@ -2570,6 +2587,7 @@ type ScheduleCommand struct {
 	CreateTaskService    *CreateTaskService `protobuf:"bytes,6,opt,name=CreateTaskService,proto3" json:"CreateTaskService,omitempty"`
 	DeleteCNStore        *DeleteCNStore     `protobuf:"bytes,7,opt,name=DeleteCNStore,proto3" json:"DeleteCNStore,omitempty"`
 	JoinGossipCluster    *JoinGossipCluster `protobuf:"bytes,8,opt,name=JoinGossipCluster,proto3" json:"JoinGossipCluster,omitempty"`
+	DeleteProxyStore     *DeleteProxyStore  `protobuf:"bytes,9,opt,name=DeleteProxyStore,proto3" json:"DeleteProxyStore,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
 	XXX_unrecognized     []byte             `json:"-"`
 	XXX_sizecache        int32              `json:"-"`
@@ -2664,6 +2682,13 @@ func (m *ScheduleCommand) GetJoinGossipCluster() *JoinGossipCluster {
 	return nil
 }
 
+func (m *ScheduleCommand) GetDeleteProxyStore() *DeleteProxyStore {
+	if m != nil {
+		return m.DeleteProxyStore
+	}
+	return nil
+}
+
 type JoinGossipCluster struct {
 	Existing             []string `protobuf:"bytes,1,rep,name=Existing,proto3" json:"Existing,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -2817,6 +2842,54 @@ func (m *DeleteCNStore) GetStoreID() string {
 	return ""
 }
 
+// DeleteProxyStore deletes Proxy store from ProxyState to save hakeeper's memory.
+type DeleteProxyStore struct {
+	StoreID              string   `protobuf:"bytes,1,opt,name=StoreID,proto3" json:"StoreID,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *DeleteProxyStore) Reset()         { *m = DeleteProxyStore{} }
+func (m *DeleteProxyStore) String() string { return proto.CompactTextString(m) }
+func (*DeleteProxyStore) ProtoMessage()    {}
+func (*DeleteProxyStore) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fd1040c5381ab5a7, []int{31}
+}
+func (m *DeleteProxyStore) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *DeleteProxyStore) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_DeleteProxyStore.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *DeleteProxyStore) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DeleteProxyStore.Merge(m, src)
+}
+func (m *DeleteProxyStore) XXX_Size() int {
+	return m.Size()
+}
+func (m *DeleteProxyStore) XXX_DiscardUnknown() {
+	xxx_messageInfo_DeleteProxyStore.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DeleteProxyStore proto.InternalMessageInfo
+
+func (m *DeleteProxyStore) GetStoreID() string {
+	if m != nil {
+		return m.StoreID
+	}
+	return ""
+}
+
 type CommandBatch struct {
 	Term                 uint64            `protobuf:"varint,1,opt,name=Term,proto3" json:"Term,omitempty"`
 	Commands             []ScheduleCommand `protobuf:"bytes,2,rep,name=Commands,proto3" json:"Commands"`
@@ -2829,7 +2902,7 @@ func (m *CommandBatch) Reset()         { *m = CommandBatch{} }
 func (m *CommandBatch) String() string { return proto.CompactTextString(m) }
 func (*CommandBatch) ProtoMessage()    {}
 func (*CommandBatch) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{31}
+	return fileDescriptor_fd1040c5381ab5a7, []int{32}
 }
 func (m *CommandBatch) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2896,7 +2969,7 @@ func (m *CNStoreInfo) Reset()         { *m = CNStoreInfo{} }
 func (m *CNStoreInfo) String() string { return proto.CompactTextString(m) }
 func (*CNStoreInfo) ProtoMessage()    {}
 func (*CNStoreInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{32}
+	return fileDescriptor_fd1040c5381ab5a7, []int{33}
 }
 func (m *CNStoreInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3029,7 +3102,7 @@ func (m *CNState) Reset()         { *m = CNState{} }
 func (m *CNState) String() string { return proto.CompactTextString(m) }
 func (*CNState) ProtoMessage()    {}
 func (*CNState) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{33}
+	return fileDescriptor_fd1040c5381ab5a7, []int{34}
 }
 func (m *CNState) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3086,7 +3159,7 @@ func (m *TNStoreInfo) Reset()         { *m = TNStoreInfo{} }
 func (m *TNStoreInfo) String() string { return proto.CompactTextString(m) }
 func (*TNStoreInfo) ProtoMessage()    {}
 func (*TNStoreInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{34}
+	return fileDescriptor_fd1040c5381ab5a7, []int{35}
 }
 func (m *TNStoreInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3184,7 +3257,7 @@ func (m *TNState) Reset()         { *m = TNState{} }
 func (m *TNState) String() string { return proto.CompactTextString(m) }
 func (*TNState) ProtoMessage()    {}
 func (*TNState) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{35}
+	return fileDescriptor_fd1040c5381ab5a7, []int{36}
 }
 func (m *TNState) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3220,20 +3293,202 @@ func (m *TNState) GetStores() map[string]TNStoreInfo {
 	return nil
 }
 
+type ProxyStore struct {
+	UUID                 string      `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"`
+	Tick                 uint64      `protobuf:"varint,2,opt,name=Tick,proto3" json:"Tick,omitempty"`
+	ListenAddress        string      `protobuf:"bytes,3,opt,name=ListenAddress,proto3" json:"ListenAddress,omitempty"`
+	ConfigData           *ConfigData `protobuf:"bytes,4,opt,name=ConfigData,proto3" json:"ConfigData,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *ProxyStore) Reset()         { *m = ProxyStore{} }
+func (m *ProxyStore) String() string { return proto.CompactTextString(m) }
+func (*ProxyStore) ProtoMessage()    {}
+func (*ProxyStore) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fd1040c5381ab5a7, []int{37}
+}
+func (m *ProxyStore) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ProxyStore) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ProxyStore.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *ProxyStore) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ProxyStore.Merge(m, src)
+}
+func (m *ProxyStore) XXX_Size() int {
+	return m.Size()
+}
+func (m *ProxyStore) XXX_DiscardUnknown() {
+	xxx_messageInfo_ProxyStore.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ProxyStore proto.InternalMessageInfo
+
+func (m *ProxyStore) GetUUID() string {
+	if m != nil {
+		return m.UUID
+	}
+	return ""
+}
+
+func (m *ProxyStore) GetTick() uint64 {
+	if m != nil {
+		return m.Tick
+	}
+	return 0
+}
+
+func (m *ProxyStore) GetListenAddress() string {
+	if m != nil {
+		return m.ListenAddress
+	}
+	return ""
+}
+
+func (m *ProxyStore) GetConfigData() *ConfigData {
+	if m != nil {
+		return m.ConfigData
+	}
+	return nil
+}
+
+type ProxyState struct {
+	Stores               map[string]ProxyStore `protobuf:"bytes,1,rep,name=Stores,proto3" json:"Stores" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
+	XXX_unrecognized     []byte                `json:"-"`
+	XXX_sizecache        int32                 `json:"-"`
+}
+
+func (m *ProxyState) Reset()         { *m = ProxyState{} }
+func (m *ProxyState) String() string { return proto.CompactTextString(m) }
+func (*ProxyState) ProtoMessage()    {}
+func (*ProxyState) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fd1040c5381ab5a7, []int{38}
+}
+func (m *ProxyState) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ProxyState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ProxyState.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *ProxyState) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ProxyState.Merge(m, src)
+}
+func (m *ProxyState) XXX_Size() int {
+	return m.Size()
+}
+func (m *ProxyState) XXX_DiscardUnknown() {
+	xxx_messageInfo_ProxyState.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ProxyState proto.InternalMessageInfo
+
+func (m *ProxyState) GetStores() map[string]ProxyStore {
+	if m != nil {
+		return m.Stores
+	}
+	return nil
+}
+
+type ProxyHeartbeat struct {
+	UUID                 string      `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"`
+	ListenAddress        string      `protobuf:"bytes,2,opt,name=ListenAddress,proto3" json:"ListenAddress,omitempty"`
+	ConfigData           *ConfigData `protobuf:"bytes,3,opt,name=ConfigData,proto3" json:"ConfigData,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *ProxyHeartbeat) Reset()         { *m = ProxyHeartbeat{} }
+func (m *ProxyHeartbeat) String() string { return proto.CompactTextString(m) }
+func (*ProxyHeartbeat) ProtoMessage()    {}
+func (*ProxyHeartbeat) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fd1040c5381ab5a7, []int{39}
+}
+func (m *ProxyHeartbeat) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ProxyHeartbeat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ProxyHeartbeat.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *ProxyHeartbeat) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ProxyHeartbeat.Merge(m, src)
+}
+func (m *ProxyHeartbeat) XXX_Size() int {
+	return m.Size()
+}
+func (m *ProxyHeartbeat) XXX_DiscardUnknown() {
+	xxx_messageInfo_ProxyHeartbeat.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ProxyHeartbeat proto.InternalMessageInfo
+
+func (m *ProxyHeartbeat) GetUUID() string {
+	if m != nil {
+		return m.UUID
+	}
+	return ""
+}
+
+func (m *ProxyHeartbeat) GetListenAddress() string {
+	if m != nil {
+		return m.ListenAddress
+	}
+	return ""
+}
+
+func (m *ProxyHeartbeat) GetConfigData() *ConfigData {
+	if m != nil {
+		return m.ConfigData
+	}
+	return nil
+}
+
 type ClusterDetails struct {
-	TNStores             []TNStore  `protobuf:"bytes,1,rep,name=TNStores,proto3" json:"TNStores"`
-	CNStores             []CNStore  `protobuf:"bytes,2,rep,name=CNStores,proto3" json:"CNStores"`
-	LogStores            []LogStore `protobuf:"bytes,3,rep,name=LogStores,proto3" json:"LogStores"`
-	XXX_NoUnkeyedLiteral struct{}   `json:"-"`
-	XXX_unrecognized     []byte     `json:"-"`
-	XXX_sizecache        int32      `json:"-"`
+	TNStores             []TNStore    `protobuf:"bytes,1,rep,name=TNStores,proto3" json:"TNStores"`
+	CNStores             []CNStore    `protobuf:"bytes,2,rep,name=CNStores,proto3" json:"CNStores"`
+	LogStores            []LogStore   `protobuf:"bytes,3,rep,name=LogStores,proto3" json:"LogStores"`
+	ProxyStores          []ProxyStore `protobuf:"bytes,4,rep,name=ProxyStores,proto3" json:"ProxyStores"`
+	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
+	XXX_unrecognized     []byte       `json:"-"`
+	XXX_sizecache        int32        `json:"-"`
 }
 
 func (m *ClusterDetails) Reset()         { *m = ClusterDetails{} }
 func (m *ClusterDetails) String() string { return proto.CompactTextString(m) }
 func (*ClusterDetails) ProtoMessage()    {}
 func (*ClusterDetails) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{36}
+	return fileDescriptor_fd1040c5381ab5a7, []int{40}
 }
 func (m *ClusterDetails) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3283,6 +3538,13 @@ func (m *ClusterDetails) GetLogStores() []LogStore {
 	return nil
 }
 
+func (m *ClusterDetails) GetProxyStores() []ProxyStore {
+	if m != nil {
+		return m.ProxyStores
+	}
+	return nil
+}
+
 // ClusterInfo provides a global view of all shards in the cluster. It
 // describes the logical sharding of the system, rather than physical
 // distribution of all replicas that belong to those shards.
@@ -3298,7 +3560,7 @@ func (m *ClusterInfo) Reset()         { *m = ClusterInfo{} }
 func (m *ClusterInfo) String() string { return proto.CompactTextString(m) }
 func (*ClusterInfo) ProtoMessage()    {}
 func (*ClusterInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{37}
+	return fileDescriptor_fd1040c5381ab5a7, []int{41}
 }
 func (m *ClusterInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3356,7 +3618,7 @@ func (m *InitialClusterRequest) Reset()         { *m = InitialClusterRequest{} }
 func (m *InitialClusterRequest) String() string { return proto.CompactTextString(m) }
 func (*InitialClusterRequest) ProtoMessage()    {}
 func (*InitialClusterRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{38}
+	return fileDescriptor_fd1040c5381ab5a7, []int{42}
 }
 func (m *InitialClusterRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3438,7 +3700,7 @@ func (m *LogStoreInfo) Reset()         { *m = LogStoreInfo{} }
 func (m *LogStoreInfo) String() string { return proto.CompactTextString(m) }
 func (*LogStoreInfo) ProtoMessage()    {}
 func (*LogStoreInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{39}
+	return fileDescriptor_fd1040c5381ab5a7, []int{43}
 }
 func (m *LogStoreInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3535,7 +3797,7 @@ func (m *LogState) Reset()         { *m = LogState{} }
 func (m *LogState) String() string { return proto.CompactTextString(m) }
 func (*LogState) ProtoMessage()    {}
 func (*LogState) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{40}
+	return fileDescriptor_fd1040c5381ab5a7, []int{44}
 }
 func (m *LogState) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3591,6 +3853,7 @@ type CheckerState struct {
 	TaskTableUser        TaskTableUser      `protobuf:"bytes,8,opt,name=TaskTableUser,proto3" json:"TaskTableUser"`
 	NextId               uint64             `protobuf:"varint,9,opt,name=NextId,proto3" json:"NextId,omitempty"`
 	NextIDByKey          map[string]uint64  `protobuf:"bytes,10,rep,name=NextIDByKey,proto3" json:"NextIDByKey,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+	ProxyState           ProxyState         `protobuf:"bytes,11,opt,name=ProxyState,proto3" json:"ProxyState"`
 	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
 	XXX_unrecognized     []byte             `json:"-"`
 	XXX_sizecache        int32              `json:"-"`
@@ -3600,7 +3863,7 @@ func (m *CheckerState) Reset()         { *m = CheckerState{} }
 func (m *CheckerState) String() string { return proto.CompactTextString(m) }
 func (*CheckerState) ProtoMessage()    {}
 func (*CheckerState) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{41}
+	return fileDescriptor_fd1040c5381ab5a7, []int{45}
 }
 func (m *CheckerState) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3699,6 +3962,13 @@ func (m *CheckerState) GetNextIDByKey() map[string]uint64 {
 	return nil
 }
 
+func (m *CheckerState) GetProxyState() ProxyState {
+	if m != nil {
+		return m.ProxyState
+	}
+	return ProxyState{}
+}
+
 // HAKeeperRSMState contains state maintained by HAKeeper's RSM.
 type HAKeeperRSMState struct {
 	Index uint64 `protobuf:"varint,1,opt,name=Index,proto3" json:"Index,omitempty"`
@@ -3717,6 +3987,7 @@ type HAKeeperRSMState struct {
 	LogState             LogState                `protobuf:"bytes,12,opt,name=LogState,proto3" json:"LogState"`
 	ClusterInfo          ClusterInfo             `protobuf:"bytes,13,opt,name=ClusterInfo,proto3" json:"ClusterInfo"`
 	TaskTableUser        TaskTableUser           `protobuf:"bytes,14,opt,name=TaskTableUser,proto3" json:"TaskTableUser"`
+	ProxyState           ProxyState              `protobuf:"bytes,15,opt,name=ProxyState,proto3" json:"ProxyState"`
 	XXX_NoUnkeyedLiteral struct{}                `json:"-"`
 	XXX_unrecognized     []byte                  `json:"-"`
 	XXX_sizecache        int32                   `json:"-"`
@@ -3726,7 +3997,7 @@ func (m *HAKeeperRSMState) Reset()         { *m = HAKeeperRSMState{} }
 func (m *HAKeeperRSMState) String() string { return proto.CompactTextString(m) }
 func (*HAKeeperRSMState) ProtoMessage()    {}
 func (*HAKeeperRSMState) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{42}
+	return fileDescriptor_fd1040c5381ab5a7, []int{46}
 }
 func (m *HAKeeperRSMState) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3853,6 +4124,13 @@ func (m *HAKeeperRSMState) GetTaskTableUser() TaskTableUser {
 	return TaskTableUser{}
 }
 
+func (m *HAKeeperRSMState) GetProxyState() ProxyState {
+	if m != nil {
+		return m.ProxyState
+	}
+	return ProxyState{}
+}
+
 // ReplicaInfo contains details of a replica
 type ReplicaInfo struct {
 	UUID                 string   `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"`
@@ -3866,7 +4144,7 @@ func (m *ReplicaInfo) Reset()         { *m = ReplicaInfo{} }
 func (m *ReplicaInfo) String() string { return proto.CompactTextString(m) }
 func (*ReplicaInfo) ProtoMessage()    {}
 func (*ReplicaInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{43}
+	return fileDescriptor_fd1040c5381ab5a7, []int{47}
 }
 func (m *ReplicaInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3925,7 +4203,7 @@ func (m *ShardInfoQueryResult) Reset()         { *m = ShardInfoQueryResult{} }
 func (m *ShardInfoQueryResult) String() string { return proto.CompactTextString(m) }
 func (*ShardInfoQueryResult) ProtoMessage()    {}
 func (*ShardInfoQueryResult) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{44}
+	return fileDescriptor_fd1040c5381ab5a7, []int{48}
 }
 func (m *ShardInfoQueryResult) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4005,7 +4283,7 @@ func (m *BackupData) Reset()         { *m = BackupData{} }
 func (m *BackupData) String() string { return proto.CompactTextString(m) }
 func (*BackupData) ProtoMessage()    {}
 func (*BackupData) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{45}
+	return fileDescriptor_fd1040c5381ab5a7, []int{49}
 }
 func (m *BackupData) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4066,7 +4344,7 @@ func (m *ConfigItem) Reset()         { *m = ConfigItem{} }
 func (m *ConfigItem) String() string { return proto.CompactTextString(m) }
 func (*ConfigItem) ProtoMessage()    {}
 func (*ConfigItem) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{46}
+	return fileDescriptor_fd1040c5381ab5a7, []int{50}
 }
 func (m *ConfigItem) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4136,7 +4414,7 @@ func (m *ConfigData) Reset()         { *m = ConfigData{} }
 func (m *ConfigData) String() string { return proto.CompactTextString(m) }
 func (*ConfigData) ProtoMessage()    {}
 func (*ConfigData) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fd1040c5381ab5a7, []int{47}
+	return fileDescriptor_fd1040c5381ab5a7, []int{51}
 }
 func (m *ConfigData) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4220,6 +4498,7 @@ func init() {
 	proto.RegisterType((*JoinGossipCluster)(nil), "logservice.JoinGossipCluster")
 	proto.RegisterType((*CreateTaskService)(nil), "logservice.CreateTaskService")
 	proto.RegisterType((*DeleteCNStore)(nil), "logservice.DeleteCNStore")
+	proto.RegisterType((*DeleteProxyStore)(nil), "logservice.DeleteProxyStore")
 	proto.RegisterType((*CommandBatch)(nil), "logservice.CommandBatch")
 	proto.RegisterType((*CNStoreInfo)(nil), "logservice.CNStoreInfo")
 	proto.RegisterMapType((map[string]metadata.LabelList)(nil), "logservice.CNStoreInfo.LabelsEntry")
@@ -4228,6 +4507,10 @@ func init() {
 	proto.RegisterType((*TNStoreInfo)(nil), "logservice.TNStoreInfo")
 	proto.RegisterType((*TNState)(nil), "logservice.TNState")
 	proto.RegisterMapType((map[string]TNStoreInfo)(nil), "logservice.TNState.StoresEntry")
+	proto.RegisterType((*ProxyStore)(nil), "logservice.ProxyStore")
+	proto.RegisterType((*ProxyState)(nil), "logservice.ProxyState")
+	proto.RegisterMapType((map[string]ProxyStore)(nil), "logservice.ProxyState.StoresEntry")
+	proto.RegisterType((*ProxyHeartbeat)(nil), "logservice.ProxyHeartbeat")
 	proto.RegisterType((*ClusterDetails)(nil), "logservice.ClusterDetails")
 	proto.RegisterType((*ClusterInfo)(nil), "logservice.ClusterInfo")
 	proto.RegisterType((*InitialClusterRequest)(nil), "logservice.InitialClusterRequest")
@@ -4255,224 +4538,236 @@ func init() {
 func init() { proto.RegisterFile("logservice.proto", fileDescriptor_fd1040c5381ab5a7) }
 
 var fileDescriptor_fd1040c5381ab5a7 = []byte{
-	// 3469 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x1b, 0x4d, 0x6f, 0x1b, 0xc7,
-	0x55, 0x4b, 0x52, 0x22, 0xf9, 0x28, 0xc9, 0xeb, 0x91, 0x6c, 0x33, 0x8c, 0x2b, 0xab, 0x1b, 0x37,
-	0xb5, 0x95, 0x84, 0x46, 0x65, 0x24, 0x48, 0x1a, 0xc5, 0x06, 0x45, 0xd2, 0x16, 0x2d, 0x9a, 0x52,
-	0x86, 0xeb, 0x04, 0x08, 0x10, 0xa8, 0x2b, 0x72, 0x2c, 0xb1, 0x22, 0xb9, 0xec, 0xee, 0xd2, 0xb1,
-	0x7b, 0x2e, 0x0a, 0x14, 0xfd, 0x03, 0x41, 0x51, 0xb4, 0x3f, 0xa1, 0x97, 0x1e, 0x82, 0x02, 0x45,
-	0x6f, 0x45, 0x8e, 0xfe, 0x05, 0x41, 0x92, 0x63, 0x5b, 0xa0, 0x68, 0x0e, 0x05, 0x8a, 0xa2, 0x40,
-	0x31, 0x5f, 0xbb, 0x33, 0xdc, 0xa5, 0x24, 0x3b, 0x4e, 0x53, 0xb4, 0x39, 0x89, 0xf3, 0xbe, 0x76,
-	0xe6, 0xcd, 0xfb, 0x9a, 0x37, 0x23, 0x30, 0xfb, 0xee, 0x81, 0x4f, 0xbc, 0x07, 0xbd, 0x0e, 0x29,
-	0x8f, 0x3c, 0x37, 0x70, 0x11, 0x44, 0x90, 0xd2, 0x2b, 0x07, 0xbd, 0xe0, 0x70, 0xbc, 0x5f, 0xee,
-	0xb8, 0x83, 0x6b, 0x07, 0xee, 0x81, 0x7b, 0x8d, 0x91, 0xec, 0x8f, 0xef, 0xb3, 0x11, 0x1b, 0xb0,
-	0x5f, 0x9c, 0xb5, 0xb4, 0x38, 0x20, 0x81, 0xd3, 0x75, 0x02, 0x87, 0x8f, 0xad, 0xdf, 0x65, 0x20,
-	0x5b, 0x6d, 0xb5, 0x03, 0xd7, 0x23, 0x08, 0x41, 0xe6, 0xde, 0xbd, 0x46, 0xad, 0x68, 0xac, 0x1a,
-	0x57, 0xf2, 0x98, 0xfd, 0x46, 0x2f, 0xc2, 0x62, 0x9b, 0x7f, 0xa9, 0xd2, 0xed, 0x7a, 0xc4, 0xf7,
-	0x8b, 0x29, 0x86, 0x9d, 0x80, 0xa2, 0x15, 0x80, 0xf6, 0xdb, 0x4d, 0x49, 0x93, 0x66, 0x34, 0x0a,
-	0x04, 0x95, 0x01, 0x35, 0xdd, 0xce, 0xd1, 0x84, 0xac, 0x0c, 0xa3, 0x4b, 0xc0, 0x50, 0x79, 0xd5,
-	0xa0, 0x2f, 0xe9, 0x66, 0xb9, 0xbc, 0x08, 0x82, 0x2e, 0x43, 0x06, 0xbb, 0x7d, 0x52, 0x9c, 0x5b,
-	0x35, 0xae, 0x2c, 0xae, 0x9b, 0xe5, 0x70, 0x59, 0xd5, 0x16, 0x85, 0x63, 0x86, 0xa5, 0x2b, 0xb2,
-	0x7b, 0x9d, 0xa3, 0x62, 0x76, 0xd5, 0xb8, 0x92, 0xc1, 0xec, 0x37, 0x7a, 0x09, 0x66, 0xdb, 0x81,
-	0x13, 0x90, 0x62, 0x8e, 0xb1, 0x9e, 0x2b, 0x2b, 0xea, 0x6d, 0xb9, 0x5d, 0xc2, 0x90, 0x98, 0xd3,
-	0xa0, 0xb7, 0x60, 0xae, 0xe9, 0xec, 0x93, 0xbe, 0x5f, 0xcc, 0xaf, 0xa6, 0xaf, 0x14, 0xd6, 0x2f,
-	0xa9, 0xd4, 0x42, 0x6f, 0x65, 0x4e, 0x51, 0x1f, 0x06, 0xde, 0xa3, 0xcd, 0xcc, 0xc7, 0x9f, 0x5c,
-	0x9a, 0xc1, 0x82, 0x09, 0x7d, 0x0f, 0xf2, 0xef, 0xba, 0xde, 0x11, 0xff, 0x1e, 0xb0, 0xef, 0x2d,
-	0x45, 0x53, 0x0d, 0x51, 0x38, 0xa2, 0x42, 0x16, 0xcc, 0xbf, 0x3d, 0x26, 0xde, 0x23, 0xb9, 0xf4,
-	0x02, 0x5b, 0xba, 0x06, 0x43, 0xaf, 0x01, 0x54, 0xdd, 0xe1, 0xfd, 0xde, 0x41, 0xcd, 0x09, 0x9c,
-	0xe2, 0xfc, 0xaa, 0x71, 0xa5, 0xb0, 0x7e, 0x5e, 0x9b, 0x59, 0x88, 0xc5, 0x0a, 0x65, 0xa9, 0x05,
-	0x05, 0x65, 0xae, 0xc8, 0x84, 0xf4, 0x11, 0x79, 0x24, 0xb6, 0x9b, 0xfe, 0x44, 0x57, 0x61, 0xf6,
-	0x81, 0xd3, 0x1f, 0x13, 0xb6, 0xc9, 0x05, 0x75, 0xae, 0x8c, 0xaf, 0xd9, 0xf3, 0x03, 0xcc, 0x29,
-	0xbe, 0x9f, 0x7a, 0xdd, 0xb0, 0xfe, 0x91, 0x82, 0xac, 0xfd, 0x0c, 0x8c, 0x47, 0x6e, 0x53, 0x3a,
-	0x69, 0x9b, 0x32, 0xa7, 0xd8, 0xa6, 0x57, 0x61, 0xae, 0x7d, 0xe8, 0x78, 0x5d, 0x6a, 0x29, 0x74,
-	0x9b, 0x2e, 0xa8, 0xd4, 0x76, 0x8b, 0xe1, 0x1a, 0xc3, 0xfb, 0xae, 0xdc, 0x1e, 0x4e, 0x8c, 0xd6,
-	0x61, 0xb9, 0xe9, 0x1e, 0x04, 0x4e, 0xaf, 0x4f, 0x27, 0x44, 0x3c, 0x39, 0xcb, 0x39, 0x36, 0xcb,
-	0x44, 0xdc, 0x14, 0x43, 0xce, 0x9e, 0xd2, 0x90, 0x73, 0x31, 0x43, 0xd6, 0xf7, 0x32, 0x7f, 0xda,
-	0xbd, 0xb4, 0xfe, 0x65, 0x40, 0xae, 0xe9, 0x1e, 0xfc, 0x17, 0x28, 0x7f, 0x03, 0x72, 0x98, 0x8c,
-	0xfa, 0xbd, 0x8e, 0x23, 0xd5, 0x5f, 0x52, 0xe9, 0x9b, 0xee, 0x81, 0x40, 0x2b, 0x3b, 0x10, 0x72,
-	0x4c, 0xac, 0x7f, 0xee, 0xd4, 0xeb, 0xff, 0xab, 0x01, 0xf3, 0x74, 0xfd, 0x72, 0x6b, 0x51, 0x11,
-	0xb2, 0x7c, 0xc0, 0xd5, 0x90, 0xc1, 0x72, 0x88, 0x36, 0x95, 0x09, 0xa6, 0xd8, 0x04, 0x5f, 0x9c,
-	0x98, 0x60, 0x28, 0xa5, 0x2c, 0x09, 0x99, 0x87, 0x28, 0xd3, 0x5c, 0x86, 0xd9, 0xfa, 0xc8, 0xed,
-	0x1c, 0x0a, 0x35, 0xf1, 0x01, 0x2a, 0x41, 0xae, 0x49, 0x9c, 0x2e, 0xf1, 0x1a, 0x35, 0xa6, 0xaa,
-	0x0c, 0x0e, 0xc7, 0x4c, 0xaf, 0xc4, 0x1b, 0xb0, 0xd8, 0x45, 0xf5, 0x4a, 0xbc, 0x41, 0xe9, 0x4d,
-	0x58, 0xd0, 0x3e, 0xa0, 0xba, 0x60, 0x86, 0xbb, 0xe0, 0xb2, 0xea, 0x82, 0x79, 0xd5, 0xdb, 0x1e,
-	0xc0, 0xa2, 0xae, 0x4b, 0x74, 0x4b, 0x57, 0x01, 0x13, 0x53, 0x58, 0x2f, 0x4e, 0x5b, 0xdc, 0x66,
-	0x8e, 0xea, 0xfe, 0xf1, 0x27, 0x97, 0x0c, 0xac, 0xab, 0xee, 0x22, 0xe4, 0xa5, 0xd8, 0x1a, 0xfb,
-	0x6e, 0x06, 0x47, 0x00, 0xeb, 0xd3, 0x34, 0x98, 0x22, 0xd4, 0x6d, 0x11, 0xc7, 0x0b, 0xf6, 0x89,
-	0x13, 0xfc, 0x0f, 0xe6, 0x8a, 0x32, 0x20, 0xdb, 0xf1, 0xa5, 0xec, 0xaa, 0x47, 0x9c, 0x80, 0x74,
-	0x99, 0x63, 0xe7, 0x70, 0x02, 0x26, 0x16, 0xa8, 0x73, 0x09, 0x81, 0xfa, 0x32, 0x2c, 0x34, 0x86,
-	0xbd, 0x20, 0xca, 0x01, 0x79, 0x46, 0xa4, 0x03, 0x29, 0xd5, 0x6d, 0xd7, 0xf7, 0x7b, 0x23, 0x29,
-	0x0a, 0x38, 0x95, 0x06, 0xa4, 0xdf, 0xe3, 0x80, 0x3b, 0x6e, 0x6f, 0x48, 0xba, 0x2c, 0x31, 0xe4,
-	0xb0, 0x06, 0x7b, 0xda, 0xc4, 0x60, 0xbd, 0x06, 0xf3, 0xd5, 0x56, 0xa5, 0xdf, 0x77, 0x3b, 0x4e,
-	0x40, 0x1a, 0xb5, 0x84, 0xcc, 0xb0, 0x0c, 0xb3, 0x9b, 0x4e, 0xd0, 0x39, 0x14, 0xe6, 0xc1, 0x07,
-	0xd6, 0xef, 0x53, 0x70, 0x56, 0x06, 0xa1, 0xe3, 0x6d, 0x63, 0x15, 0x0a, 0xd8, 0xb9, 0x1f, 0xe8,
-	0x86, 0xa1, 0x82, 0x12, 0xac, 0x27, 0x9d, 0x68, 0x3d, 0x31, 0x6d, 0x65, 0x92, 0xb4, 0xf5, 0xe5,
-	0x82, 0x52, 0xb2, 0x2d, 0xcc, 0x4d, 0xb5, 0x05, 0x5d, 0xef, 0xd9, 0x53, 0xeb, 0xbd, 0x0e, 0x05,
-	0x25, 0x3b, 0x1d, 0x13, 0xc2, 0x8e, 0xf7, 0xd0, 0xbf, 0xa5, 0xc0, 0xb4, 0x9f, 0xa5, 0x87, 0x46,
-	0xf9, 0x34, 0xfd, 0x24, 0xf9, 0x34, 0x59, 0x6d, 0x99, 0xa9, 0x6a, 0x9b, 0x96, 0x7f, 0x67, 0x9f,
-	0x38, 0xff, 0xce, 0x9d, 0x32, 0x38, 0x64, 0x4f, 0xc8, 0xbf, 0xb9, 0x53, 0x6f, 0xdd, 0xcf, 0x52,
-	0x90, 0xc3, 0xed, 0xbb, 0xdc, 0x83, 0x4d, 0x48, 0xdb, 0xbe, 0x2b, 0xc3, 0xb8, 0xed, 0xbb, 0xd4,
-	0x5f, 0x1a, 0xc3, 0x2e, 0x79, 0x28, 0xfd, 0x85, 0x0d, 0xa8, 0xed, 0x36, 0x89, 0xe3, 0x93, 0x2d,
-	0xb7, 0xcf, 0x93, 0x06, 0xcf, 0x26, 0x3a, 0x90, 0x7a, 0xba, 0xed, 0x8d, 0x87, 0xd4, 0x17, 0xbb,
-	0x4d, 0x7f, 0x28, 0x32, 0x8b, 0x06, 0x43, 0x77, 0x60, 0x9e, 0x33, 0xf5, 0xfc, 0xc0, 0xf5, 0x1e,
-	0x09, 0x1b, 0xd7, 0xf2, 0x9a, 0x9c, 0x5d, 0x59, 0x25, 0xe4, 0x79, 0x4d, 0xe3, 0x2d, 0xdd, 0x84,
-	0xb3, 0x31, 0x92, 0x93, 0x32, 0x53, 0x46, 0xcd, 0x4c, 0xef, 0x43, 0x9e, 0x39, 0x54, 0xc7, 0xf5,
-	0xba, 0x94, 0x91, 0x4e, 0x5a, 0x30, 0xd2, 0xb9, 0xae, 0x41, 0xc6, 0x7e, 0x34, 0xe2, 0x7c, 0x8b,
-	0xba, 0x72, 0x39, 0x0f, 0xc5, 0x62, 0x46, 0x43, 0xad, 0x96, 0x6d, 0x04, 0x55, 0xcc, 0x3c, 0x66,
-	0xbf, 0xad, 0x0f, 0x0d, 0x00, 0x26, 0xff, 0x47, 0x63, 0xe2, 0x33, 0xc3, 0x6e, 0x39, 0x03, 0x22,
-	0x0d, 0x9b, 0xfe, 0x56, 0x3d, 0x27, 0xa5, 0x7b, 0x8e, 0x98, 0x4e, 0x3a, 0x9a, 0x4e, 0x11, 0xb2,
-	0x77, 0x9d, 0x87, 0xed, 0xde, 0x8f, 0x89, 0xd0, 0xac, 0x1c, 0x52, 0x2f, 0x93, 0xc6, 0x5d, 0x13,
-	0x79, 0x3b, 0x02, 0xb0, 0x84, 0xde, 0x6a, 0xd4, 0x98, 0xad, 0xd1, 0x84, 0xde, 0x6a, 0xd4, 0x2c,
-	0x0b, 0xc0, 0xf6, 0x5d, 0x39, 0xb3, 0x65, 0x98, 0xad, 0xba, 0xe3, 0x61, 0x20, 0x16, 0xcf, 0x07,
-	0xd6, 0x9f, 0x0d, 0x1a, 0x5d, 0x99, 0x77, 0xb2, 0x2a, 0x3a, 0xd1, 0x33, 0xaf, 0x43, 0x7e, 0x67,
-	0x44, 0x3c, 0x27, 0xe8, 0xb9, 0x43, 0xa1, 0xa8, 0x73, 0xfa, 0x59, 0x83, 0xf1, 0xee, 0x8c, 0x70,
-	0x44, 0x87, 0x36, 0xc3, 0xd3, 0x09, 0x77, 0xd3, 0xcb, 0x09, 0xa7, 0x13, 0x46, 0x30, 0xfd, 0x88,
-	0xf2, 0xcc, 0xcf, 0x04, 0x4d, 0x28, 0x54, 0x5b, 0x51, 0x6e, 0x4b, 0x5a, 0xeb, 0x55, 0x59, 0x5d,
-	0xa6, 0xa6, 0x9f, 0x88, 0x38, 0x85, 0xf5, 0x99, 0xd0, 0x9d, 0x13, 0x1c, 0xa3, 0xbb, 0xd3, 0xcb,
-	0x3b, 0x59, 0x63, 0xf2, 0x43, 0xff, 0x41, 0x8d, 0xfd, 0x71, 0x16, 0xb2, 0xd2, 0x82, 0x58, 0x9c,
-	0x67, 0x3f, 0xc3, 0x1c, 0x10, 0x01, 0x50, 0x19, 0xe6, 0xee, 0x92, 0xe0, 0xd0, 0xed, 0x26, 0xb9,
-	0x12, 0xc7, 0x30, 0x57, 0x12, 0x54, 0x68, 0x43, 0xf5, 0x1b, 0xe6, 0x02, 0x13, 0xb1, 0x2d, 0xc2,
-	0x8a, 0x35, 0xaa, 0x7e, 0x56, 0x61, 0xd5, 0x65, 0x98, 0x50, 0x98, 0xb3, 0x14, 0xd6, 0xbf, 0x35,
-	0x59, 0x5d, 0x6a, 0x59, 0x07, 0x6b, 0x2c, 0xe8, 0x06, 0x35, 0x86, 0x48, 0xc2, 0x2c, 0x93, 0x70,
-	0x31, 0xc1, 0x4a, 0x23, 0x01, 0x2a, 0x03, 0xe5, 0xb7, 0x15, 0xfe, 0xb9, 0x38, 0xbf, 0x1d, 0xe3,
-	0x57, 0x18, 0x68, 0x70, 0x8f, 0xdc, 0x33, 0x29, 0x2f, 0x47, 0x58, 0xac, 0x3a, 0xf2, 0x86, 0x5e,
-	0x0f, 0x89, 0xb4, 0x50, 0xd4, 0x27, 0x1e, 0xe1, 0xb1, 0x5e, 0x3d, 0x6d, 0xe8, 0xfe, 0x2e, 0x0e,
-	0x75, 0xc5, 0x69, 0xce, 0x89, 0xf5, 0xe8, 0xf0, 0x86, 0xe6, 0x40, 0xac, 0x16, 0x9c, 0x48, 0xc0,
-	0x0a, 0x1a, 0x6b, 0xce, 0xb6, 0xa1, 0x3b, 0x0b, 0x2b, 0x11, 0x13, 0x3e, 0x2c, 0xf1, 0x58, 0x77,
-	0xad, 0x9b, 0xb0, 0x50, 0x23, 0x7d, 0x12, 0x10, 0x31, 0x1d, 0x51, 0x3f, 0x3e, 0xa7, 0xb2, 0x6b,
-	0x04, 0x58, 0xa7, 0xb7, 0xda, 0x50, 0x60, 0xe6, 0xe3, 0x8f, 0xdc, 0xa1, 0x4f, 0x8e, 0xa9, 0x66,
-	0x44, 0x4c, 0x4e, 0x69, 0x31, 0xb9, 0xe9, 0xf8, 0x41, 0x14, 0xa9, 0xe5, 0xd0, 0x2a, 0x03, 0x52,
-	0x14, 0xad, 0xc8, 0xbe, 0xd5, 0xf3, 0x14, 0x2f, 0x91, 0x43, 0xeb, 0xef, 0x19, 0x5a, 0xf9, 0x09,
-	0xb2, 0x67, 0xeb, 0x4e, 0x17, 0x21, 0x5f, 0xf7, 0x3c, 0xd7, 0xab, 0xba, 0x5d, 0xc2, 0xa6, 0xb9,
-	0x80, 0x23, 0x00, 0xcd, 0xda, 0x6c, 0x70, 0x97, 0xf8, 0xbe, 0x73, 0x40, 0x44, 0x59, 0xaa, 0xc1,
-	0x68, 0x31, 0xd2, 0xf0, 0xb7, 0x2a, 0xdb, 0x84, 0x8c, 0x88, 0xc7, 0xdc, 0x21, 0x87, 0x15, 0x08,
-	0xba, 0xa9, 0x69, 0x50, 0xd8, 0xfb, 0x85, 0x98, 0xc7, 0x72, 0xb4, 0x70, 0x59, 0x4d, 0xe7, 0xd4,
-	0x02, 0xdc, 0xc1, 0xc0, 0x19, 0x76, 0x79, 0xb5, 0x9e, 0x4d, 0xb0, 0x00, 0x05, 0x8f, 0x35, 0x6a,
-	0x6a, 0x7a, 0xcc, 0x09, 0xc4, 0xe7, 0x73, 0xf1, 0xcf, 0x2b, 0x68, 0xac, 0xd2, 0xa2, 0x4d, 0x58,
-	0xac, 0xf6, 0xc7, 0x7e, 0x40, 0xbc, 0x1a, 0xa1, 0x45, 0x9b, 0x2f, 0xac, 0x5e, 0xab, 0xba, 0x75,
-	0x0a, 0x3c, 0xc1, 0x81, 0x6e, 0x40, 0x3e, 0x3a, 0xcb, 0x72, 0xbb, 0x5f, 0x55, 0xd9, 0x43, 0x24,
-	0x3b, 0x63, 0x61, 0xe2, 0x8f, 0xfb, 0x01, 0x8e, 0x58, 0xd0, 0x0d, 0x00, 0xc5, 0x67, 0xb9, 0xf1,
-	0xaf, 0xa8, 0x02, 0xe2, 0x86, 0x84, 0x61, 0xc2, 0x6f, 0x0f, 0x49, 0xe7, 0x88, 0x78, 0xdc, 0xf5,
-	0xe6, 0x13, 0x94, 0xa7, 0xe0, 0xb1, 0x46, 0x6d, 0xdd, 0x61, 0x47, 0x21, 0x5e, 0xd0, 0x84, 0x6a,
-	0x79, 0x95, 0x86, 0x76, 0x0a, 0xf1, 0x8b, 0x06, 0x4b, 0x38, 0xe7, 0x62, 0x9b, 0x49, 0xb1, 0x62,
-	0x2b, 0x25, 0xad, 0xf5, 0x82, 0xb6, 0x11, 0xb4, 0xae, 0x78, 0x87, 0x25, 0x14, 0x51, 0x57, 0xb0,
-	0x81, 0x75, 0x1b, 0x16, 0x68, 0x4d, 0x6d, 0x3b, 0xfb, 0x7d, 0x72, 0xcf, 0x27, 0x1e, 0x2a, 0x41,
-	0x8e, 0xfe, 0x1d, 0x46, 0xc5, 0x51, 0x38, 0xa6, 0xb8, 0x5d, 0xc7, 0xf7, 0x3f, 0x70, 0xbd, 0xae,
-	0xa8, 0xf9, 0xc3, 0xb1, 0xf5, 0x73, 0x83, 0xce, 0x92, 0x1d, 0x26, 0x12, 0xf3, 0xeb, 0xf4, 0xe2,
-	0x4a, 0x3b, 0x96, 0xa4, 0x27, 0x8e, 0x25, 0x51, 0xcf, 0x24, 0xa3, 0xf6, 0x4c, 0x56, 0x58, 0x52,
-	0xd2, 0xab, 0x2c, 0x05, 0x62, 0xfd, 0x22, 0x45, 0x6d, 0x98, 0xd6, 0xd9, 0xd5, 0x43, 0x67, 0x78,
-	0x40, 0xd0, 0xf5, 0x70, 0x76, 0xa2, 0xc1, 0xb1, 0xa4, 0x57, 0x90, 0x0c, 0x15, 0x69, 0x90, 0xaf,
-	0x63, 0x03, 0x80, 0xb3, 0x2b, 0x95, 0xe7, 0xc5, 0x78, 0x59, 0x1f, 0xd1, 0x60, 0x85, 0x1e, 0xd9,
-	0xb0, 0x48, 0x8f, 0xe8, 0x3d, 0xa7, 0x7f, 0x97, 0x0c, 0xf6, 0x89, 0x27, 0xcb, 0x85, 0x97, 0xa7,
-	0x49, 0x28, 0xeb, 0xe4, 0xbc, 0xca, 0x9e, 0x90, 0x51, 0xaa, 0xc0, 0x52, 0x02, 0xd9, 0x13, 0xf5,
-	0x80, 0xae, 0xc2, 0x42, 0xfb, 0x70, 0x1c, 0x74, 0xdd, 0x0f, 0x86, 0xbc, 0xf3, 0x47, 0xf7, 0x86,
-	0xfe, 0x08, 0xb7, 0x4c, 0x0e, 0xad, 0x4f, 0xd3, 0x70, 0xa6, 0xdd, 0x39, 0x24, 0xdd, 0x71, 0x9f,
-	0x08, 0x2f, 0x4f, 0xdc, 0xdd, 0xcb, 0xb0, 0xb0, 0xe9, 0xba, 0x81, 0x1f, 0x78, 0xce, 0x68, 0xd4,
-	0x1b, 0x1e, 0xb0, 0x8f, 0xe6, 0xb0, 0x0e, 0xa4, 0xa1, 0x41, 0x1c, 0xac, 0x98, 0x42, 0xd3, 0x4c,
-	0xa1, 0x5a, 0x68, 0x50, 0xd0, 0x58, 0xa5, 0xe5, 0x31, 0x29, 0x52, 0x95, 0xa8, 0x23, 0x8a, 0xd3,
-	0x54, 0x89, 0xf5, 0xdd, 0xbf, 0x39, 0xb1, 0x62, 0x51, 0x44, 0x3c, 0xa7, 0x07, 0x06, 0x85, 0x00,
-	0x4f, 0x68, 0x68, 0x1b, 0xce, 0xf2, 0xf3, 0xa6, 0x72, 0x00, 0x15, 0x91, 0x55, 0xab, 0x65, 0x62,
-	0x44, 0x38, 0xce, 0x17, 0xcf, 0x91, 0xd9, 0x27, 0xcb, 0x91, 0x74, 0x36, 0x77, 0xdc, 0xde, 0x90,
-	0x37, 0x2b, 0x44, 0xfc, 0x13, 0x81, 0x56, 0x9b, 0x4d, 0x8c, 0x08, 0xc7, 0xf9, 0xac, 0x6b, 0x09,
-	0xc2, 0xa8, 0xa7, 0xd7, 0x1f, 0xf6, 0xfc, 0x80, 0x6e, 0x25, 0x8d, 0x39, 0x79, 0x1c, 0x8e, 0xad,
-	0x7e, 0x82, 0x2e, 0xd0, 0x75, 0xc8, 0xd0, 0x30, 0x21, 0x9c, 0x4b, 0x5b, 0x8a, 0x16, 0x5f, 0x84,
-	0x8b, 0x31, 0x62, 0x76, 0x46, 0x75, 0xfc, 0x23, 0x7a, 0x3e, 0xdb, 0x77, 0x7c, 0x69, 0xa9, 0x1a,
-	0x8c, 0x1a, 0xab, 0xbe, 0xf8, 0xe9, 0xc6, 0xea, 0xe8, 0x79, 0x2b, 0x6c, 0x9e, 0x1a, 0x51, 0xf3,
-	0x14, 0xbd, 0x05, 0x39, 0x41, 0x23, 0xdb, 0xb8, 0xcf, 0x6b, 0x46, 0xa0, 0xdb, 0xba, 0xec, 0xe9,
-	0x48, 0x16, 0xeb, 0x9f, 0x19, 0x5a, 0x58, 0xf1, 0x0f, 0xd2, 0x6c, 0x21, 0xfb, 0xde, 0x86, 0xd2,
-	0xf7, 0xfe, 0xff, 0xea, 0x60, 0x56, 0xc2, 0xc3, 0x50, 0x8e, 0xa9, 0xf3, 0x85, 0x84, 0x0a, 0x95,
-	0x35, 0xc5, 0x4f, 0x79, 0xc1, 0x95, 0x7f, 0xaa, 0x0b, 0x2e, 0x48, 0xee, 0x9b, 0xea, 0x3d, 0xbe,
-	0xc2, 0x69, 0x3a, 0xa2, 0xf3, 0x27, 0x76, 0x44, 0x17, 0xbe, 0xb6, 0xab, 0xb2, 0x5f, 0x1a, 0xfc,
-	0x9e, 0x55, 0x5c, 0x2a, 0x32, 0xdd, 0xca, 0x9a, 0xe0, 0x52, 0x42, 0x81, 0x5e, 0xe6, 0x14, 0x9a,
-	0xce, 0x39, 0xa8, 0x84, 0xa1, 0xa0, 0x20, 0x13, 0xa6, 0xf6, 0x8a, 0x3e, 0xb5, 0x0b, 0x53, 0xb6,
-	0x55, 0x9d, 0xde, 0x5f, 0x52, 0xac, 0x13, 0xf9, 0x4c, 0x9c, 0xe3, 0x9b, 0xe6, 0xe1, 0x89, 0xcd,
-	0x43, 0x6a, 0x0d, 0xf6, 0x69, 0xac, 0xc1, 0xfe, 0x6a, 0xad, 0xc1, 0x4e, 0xb6, 0x86, 0x8f, 0x8c,
-	0xc9, 0x6a, 0x1e, 0xbd, 0x0a, 0x39, 0x41, 0x2c, 0xe7, 0xb9, 0x94, 0x20, 0x48, 0x06, 0x5d, 0x49,
-	0x4a, 0xd9, 0xaa, 0x92, 0x2d, 0x15, 0x67, 0xab, 0xea, 0x6c, 0x92, 0x14, 0xbd, 0xce, 0x1a, 0x8a,
-	0x82, 0x8f, 0x5b, 0xd1, 0x72, 0x52, 0xdf, 0x41, 0x30, 0x46, 0xc4, 0xd6, 0x4f, 0x0d, 0x28, 0x88,
-	0xa9, 0x33, 0x43, 0x7e, 0x83, 0xcd, 0x9b, 0x9b, 0xa3, 0x21, 0xcc, 0x31, 0xf4, 0x54, 0x81, 0xd1,
-	0x6a, 0xf0, 0x90, 0x1c, 0x6d, 0xf0, 0x49, 0x70, 0x5e, 0x3e, 0xf9, 0xa2, 0xe2, 0xe5, 0x02, 0xa5,
-	0x31, 0x47, 0x0c, 0xd6, 0x1f, 0x52, 0x70, 0x4e, 0x54, 0x7b, 0x32, 0x83, 0x8b, 0xe6, 0xc2, 0x8b,
-	0xb0, 0xd8, 0x1a, 0x0f, 0x76, 0xee, 0x47, 0xc2, 0xb9, 0x97, 0x4d, 0x40, 0x69, 0x10, 0x64, 0x90,
-	0x70, 0xfe, 0xbc, 0xf8, 0xd6, 0x81, 0x68, 0x0d, 0x4c, 0xc9, 0x17, 0x5e, 0x78, 0xf0, 0x4a, 0x3c,
-	0x06, 0x47, 0xe7, 0x61, 0xae, 0x45, 0x1e, 0x06, 0xe1, 0x65, 0xa5, 0x18, 0x21, 0x1b, 0x0a, 0xfc,
-	0xd7, 0xe6, 0xa3, 0x6d, 0x22, 0x7b, 0xc9, 0xeb, 0xaa, 0xc2, 0x13, 0x57, 0x52, 0x56, 0x98, 0x78,
-	0xc5, 0xab, 0x8a, 0x29, 0xdd, 0x00, 0x73, 0x92, 0x20, 0xf9, 0x62, 0x69, 0x4a, 0x57, 0xf9, 0xa3,
-	0x14, 0xbf, 0xde, 0x3c, 0x36, 0x28, 0x7d, 0x73, 0xaf, 0x94, 0x14, 0x5f, 0x7e, 0x9b, 0x12, 0x8f,
-	0x03, 0x68, 0x80, 0xb9, 0x11, 0xc6, 0x63, 0xee, 0x00, 0xab, 0x31, 0x4f, 0x62, 0x11, 0x86, 0x91,
-	0xe8, 0x11, 0x86, 0x5b, 0xd8, 0x8d, 0x30, 0x40, 0xa5, 0x8e, 0xe3, 0x9f, 0x1a, 0xa1, 0xda, 0x50,
-	0x50, 0x84, 0x27, 0x1c, 0x77, 0xca, 0x7a, 0x84, 0x9a, 0x7a, 0x7f, 0xad, 0x18, 0x07, 0x13, 0x7a,
-	0x6c, 0xd8, 0x3b, 0x49, 0x68, 0x52, 0xdc, 0xfb, 0x22, 0xa3, 0x77, 0x00, 0x12, 0x2d, 0xee, 0xa6,
-	0x16, 0x60, 0x12, 0x73, 0x6c, 0x84, 0x96, 0x3d, 0x1a, 0x35, 0x24, 0x5d, 0x0f, 0x63, 0xbf, 0x68,
-	0xc9, 0x2e, 0x25, 0x44, 0x7c, 0x79, 0x9e, 0x95, 0x59, 0xe2, 0xb5, 0x68, 0x43, 0xc5, 0x01, 0x6a,
-	0x39, 0x69, 0x1b, 0xa4, 0xc5, 0x85, 0x9b, 0x7f, 0x3d, 0x2c, 0x3b, 0xc4, 0xc1, 0x69, 0x29, 0xa1,
-	0xd8, 0x90, 0x1f, 0x93, 0x05, 0xca, 0x35, 0xd9, 0x50, 0xe7, 0xf5, 0xa6, 0x76, 0x24, 0x90, 0xbd,
-	0x2a, 0xad, 0xad, 0xde, 0x12, 0x76, 0x2d, 0x4a, 0x70, 0xd1, 0x3f, 0xc9, 0x32, 0xee, 0x95, 0xc9,
-	0x03, 0x85, 0x4e, 0x85, 0x13, 0x38, 0x51, 0x7d, 0xa2, 0xb5, 0x21, 0x52, 0xeb, 0x89, 0x67, 0x93,
-	0x89, 0x86, 0x88, 0x8c, 0x77, 0x5d, 0x56, 0x9a, 0xca, 0x78, 0xd7, 0x45, 0xdb, 0x7a, 0xbc, 0x03,
-	0x66, 0xd6, 0x57, 0xa7, 0xf5, 0x79, 0xbe, 0xe2, 0x30, 0xf7, 0x45, 0x16, 0x4c, 0xa9, 0xd4, 0xf0,
-	0x42, 0x31, 0xbc, 0x3e, 0x34, 0xd4, 0xeb, 0x43, 0x69, 0x8e, 0x29, 0xc5, 0x1c, 0xa3, 0x98, 0x9e,
-	0xd6, 0x62, 0xfa, 0x8e, 0xbe, 0xc6, 0x0c, 0x5b, 0xe3, 0x2b, 0x49, 0x3b, 0x19, 0xde, 0x13, 0x1e,
-	0xbb, 0xce, 0xa4, 0xf7, 0x2c, 0x5f, 0xbf, 0xa1, 0x74, 0xc1, 0x9c, 0x38, 0xf7, 0xc9, 0xc3, 0xcc,
-	0xfa, 0xb1, 0x4b, 0x9d, 0x64, 0x52, 0xe3, 0x56, 0x4c, 0x22, 0x6a, 0xa8, 0x95, 0x00, 0x7f, 0x08,
-	0xf8, 0xd2, 0xb1, 0xe2, 0x43, 0x6a, 0xae, 0xc7, 0x88, 0x5b, 0xf5, 0x47, 0x38, 0xb5, 0x3f, 0x2a,
-	0x11, 0xa3, 0xf0, 0x54, 0x11, 0x63, 0xfe, 0x09, 0x22, 0xc6, 0x44, 0x7c, 0x5b, 0x78, 0xe2, 0xf8,
-	0x16, 0x73, 0xde, 0xc5, 0xa7, 0x71, 0xde, 0x2f, 0xeb, 0x57, 0xa5, 0xf7, 0xe1, 0x5c, 0xe2, 0x2e,
-	0x3f, 0x61, 0xae, 0xd0, 0xda, 0xe5, 0x8a, 0xf8, 0x0d, 0xf6, 0x1a, 0x6b, 0x4a, 0x62, 0x3b, 0xd1,
-	0xe9, 0x1b, 0x50, 0x50, 0x1f, 0x72, 0x7d, 0x89, 0xb7, 0x1a, 0xd6, 0xaf, 0x52, 0xb0, 0x9c, 0xd4,
-	0x19, 0x3f, 0xe6, 0xfe, 0x65, 0x37, 0xf6, 0x20, 0xae, 0x7c, 0x52, 0x9f, 0x5d, 0x7f, 0x18, 0x17,
-	0x2b, 0x6c, 0x9e, 0xcd, 0xf3, 0x38, 0xfb, 0xe4, 0xe7, 0x71, 0xc7, 0x9d, 0x66, 0x14, 0x8d, 0xaa,
-	0xba, 0xfe, 0x8d, 0x01, 0xb0, 0xe9, 0x74, 0x8e, 0xc6, 0x23, 0x5a, 0x1b, 0x29, 0x01, 0xd3, 0xd0,
-	0x02, 0x66, 0x43, 0x0f, 0x98, 0x5c, 0x2f, 0xdf, 0x55, 0xe5, 0x47, 0x42, 0xbe, 0xe2, 0x94, 0xf0,
-	0x13, 0x43, 0xd6, 0x7d, 0x8d, 0x80, 0x0c, 0x12, 0x1f, 0x3c, 0x58, 0x30, 0x5f, 0x1d, 0x7b, 0x1e,
-	0x19, 0x06, 0xef, 0x28, 0x9d, 0x62, 0x0d, 0x46, 0x69, 0x6a, 0xe4, 0xbe, 0x33, 0xee, 0x0b, 0x1a,
-	0x5e, 0xf7, 0x6a, 0x30, 0xba, 0x45, 0x8d, 0x61, 0x40, 0xbc, 0xa1, 0xd3, 0x17, 0x05, 0x6f, 0x38,
-	0xb6, 0x7e, 0x6d, 0xa8, 0xe5, 0x27, 0x7a, 0x0b, 0xb2, 0x55, 0x77, 0x18, 0x10, 0xf6, 0xbe, 0x21,
-	0xde, 0x2f, 0x0a, 0x09, 0xcb, 0x82, 0x8a, 0x2b, 0x46, 0xf2, 0x94, 0x30, 0x6b, 0x03, 0x87, 0x88,
-	0x04, 0x85, 0xbc, 0xac, 0xef, 0x6d, 0x42, 0xa1, 0x4b, 0xd5, 0xa1, 0x28, 0x6a, 0xed, 0x07, 0x00,
-	0xf7, 0x46, 0x5d, 0x27, 0xe0, 0x8d, 0xe6, 0x0b, 0xb0, 0xa4, 0x3d, 0xa4, 0xe1, 0x28, 0x73, 0x06,
-	0x9d, 0x83, 0xb3, 0xf2, 0xf1, 0x4c, 0xb3, 0xdd, 0x12, 0x60, 0x03, 0x2d, 0xc1, 0x19, 0x1a, 0x69,
-	0xd8, 0x74, 0x04, 0x30, 0x85, 0x16, 0x20, 0x6f, 0xb7, 0x77, 0xc4, 0x30, 0xbd, 0x56, 0x86, 0x7c,
-	0xf8, 0xe0, 0x15, 0x9d, 0x81, 0x42, 0xcb, 0xf5, 0x06, 0x4e, 0x9f, 0x0d, 0xcd, 0x19, 0x64, 0xc2,
-	0xbc, 0xdd, 0x1b, 0x10, 0x77, 0x1c, 0x70, 0x88, 0xb1, 0xf6, 0xa7, 0x14, 0x40, 0x74, 0x75, 0x88,
-	0x16, 0x01, 0xec, 0xf6, 0xce, 0xde, 0xbd, 0xdd, 0x5a, 0xc5, 0xae, 0x9b, 0x33, 0x08, 0x60, 0xae,
-	0xb2, 0xbb, 0x5b, 0x6f, 0xd5, 0x4c, 0x03, 0xe5, 0x20, 0x83, 0xeb, 0x95, 0x9a, 0x99, 0x42, 0xf3,
-	0x90, 0xb3, 0xf1, 0xbd, 0x56, 0x95, 0xd2, 0xa4, 0xa9, 0xd0, 0xdb, 0x75, 0x7b, 0x2f, 0x84, 0x64,
-	0x50, 0x01, 0xb2, 0xd5, 0x9d, 0x56, 0xab, 0x5e, 0xb5, 0xcd, 0x59, 0x2a, 0x52, 0x0c, 0xf6, 0xf0,
-	0x8e, 0x39, 0x87, 0xce, 0xc2, 0x42, 0x73, 0xe7, 0xf6, 0xde, 0x56, 0xbd, 0x82, 0xed, 0xcd, 0x7a,
-	0xc5, 0x36, 0xb3, 0x54, 0x42, 0xb5, 0xa5, 0x40, 0x72, 0x6c, 0xa2, 0x2a, 0x24, 0x8f, 0x10, 0x2c,
-	0x56, 0xb7, 0xea, 0xd5, 0xed, 0xbd, 0xad, 0xca, 0x76, 0xbd, 0xbe, 0x5b, 0xc7, 0x26, 0x50, 0x05,
-	0xd2, 0x2f, 0x57, 0x9b, 0xf7, 0xda, 0x76, 0x1d, 0xef, 0xd5, 0xea, 0x76, 0xa5, 0xd1, 0x6c, 0x9b,
-	0x05, 0x4a, 0x4c, 0x11, 0xed, 0xad, 0x0a, 0xae, 0xed, 0x35, 0x5a, 0xb7, 0x76, 0xcc, 0x79, 0x26,
-	0xa0, 0xb5, 0x57, 0x69, 0x36, 0x77, 0xe8, 0x2c, 0xf7, 0x1a, 0x35, 0x73, 0x81, 0x2a, 0x5a, 0x15,
-	0xd0, 0xb6, 0xe9, 0xfc, 0x17, 0x99, 0xa2, 0x99, 0x06, 0xf6, 0xaa, 0xad, 0xbd, 0x66, 0x65, 0xb3,
-	0xde, 0x34, 0xcf, 0xa0, 0x22, 0x2c, 0x47, 0xc0, 0x77, 0x77, 0xf0, 0xb6, 0x20, 0x37, 0xa9, 0xe4,
-	0xdd, 0x8a, 0x5d, 0xdd, 0xa2, 0x88, 0xb6, 0xbd, 0x83, 0xeb, 0xe6, 0x59, 0x2a, 0xa2, 0x56, 0x6f,
-	0xd6, 0x39, 0x35, 0x07, 0xa2, 0xb5, 0x16, 0x40, 0xf4, 0x80, 0x88, 0x2a, 0x86, 0x6e, 0x27, 0x87,
-	0x98, 0x33, 0x54, 0xab, 0xd2, 0x94, 0x4d, 0x83, 0xee, 0x1d, 0x33, 0x8e, 0x70, 0xa3, 0xcf, 0x8a,
-	0xb7, 0x58, 0x98, 0xfc, 0x90, 0x74, 0x02, 0xd2, 0x35, 0xd3, 0x6b, 0x6b, 0x90, 0x0f, 0xdf, 0xd9,
-	0x50, 0xf6, 0x36, 0x09, 0xd8, 0xc8, 0x9c, 0xa1, 0xec, 0xbc, 0x97, 0xcd, 0x01, 0xc6, 0xda, 0xe3,
-	0x14, 0x20, 0x99, 0xf7, 0x15, 0x1b, 0xa4, 0x1b, 0xde, 0xeb, 0x1c, 0xa9, 0xa6, 0xa7, 0x3c, 0x68,
-	0x08, 0x4d, 0x8f, 0x5a, 0x64, 0x0c, 0x9c, 0x42, 0xe7, 0x01, 0xa9, 0xef, 0x27, 0xa4, 0x15, 0xd2,
-	0xaf, 0xdf, 0x26, 0x41, 0x68, 0xd1, 0x19, 0xf4, 0x5c, 0x2c, 0xb9, 0x09, 0xd4, 0x2c, 0xd5, 0x5e,
-	0x9b, 0x70, 0x7b, 0x14, 0xb0, 0x39, 0xaa, 0x6b, 0xfd, 0x04, 0x2f, 0x30, 0x59, 0x74, 0x09, 0x9e,
-	0x6f, 0x93, 0x20, 0x5e, 0x59, 0x09, 0x82, 0x1c, 0x2a, 0xc1, 0x79, 0x41, 0x10, 0xa6, 0x66, 0x81,
-	0xcb, 0x53, 0x15, 0xf2, 0xdf, 0x42, 0x6b, 0x26, 0xd0, 0x85, 0x49, 0x50, 0xd8, 0xf2, 0x35, 0x0b,
-	0xd4, 0xfe, 0x76, 0x69, 0x06, 0x15, 0x1d, 0x1e, 0x73, 0x9e, 0xf2, 0x62, 0x32, 0x70, 0x1f, 0xc8,
-	0xcb, 0x01, 0x73, 0x61, 0xed, 0x43, 0x03, 0x16, 0xb4, 0xaa, 0x91, 0xee, 0xba, 0x04, 0x88, 0x23,
-	0xb1, 0x39, 0x43, 0xd7, 0x2e, 0x81, 0xda, 0x1d, 0x95, 0x69, 0xa0, 0xef, 0xc0, 0xb7, 0x63, 0x28,
-	0x99, 0xfc, 0x31, 0xe9, 0x90, 0xde, 0x03, 0xd2, 0x35, 0x53, 0xe8, 0x79, 0xb8, 0x10, 0x23, 0xbb,
-	0xe5, 0xf4, 0xfa, 0xd4, 0x08, 0xd4, 0x6f, 0xe2, 0xf1, 0x70, 0x48, 0x05, 0x67, 0xd6, 0xf6, 0x93,
-	0xea, 0x56, 0xaa, 0x56, 0x0d, 0x1a, 0xcd, 0x71, 0x12, 0x23, 0x25, 0x19, 0x31, 0x4c, 0x3b, 0x70,
-	0x47, 0x23, 0x3a, 0xab, 0xb5, 0x43, 0x30, 0x27, 0x2f, 0x25, 0xa9, 0x39, 0x55, 0xba, 0x5d, 0x91,
-	0xd8, 0xcc, 0x99, 0x48, 0x6b, 0x12, 0x64, 0x50, 0xd5, 0xb6, 0x03, 0xc7, 0x0b, 0x24, 0x24, 0x45,
-	0xad, 0x85, 0x4a, 0x95, 0x80, 0x34, 0x95, 0xb2, 0xdd, 0xeb, 0xf7, 0xdf, 0x73, 0x07, 0xfb, 0x3d,
-	0x62, 0x66, 0xd6, 0xde, 0xd4, 0x2e, 0xf3, 0x28, 0x9a, 0x96, 0x32, 0x1c, 0x62, 0xce, 0xb0, 0x10,
-	0xd8, 0x92, 0x43, 0x83, 0x0e, 0xab, 0xe1, 0x30, 0xb5, 0x59, 0x7f, 0xfc, 0xd9, 0xca, 0xcc, 0xc7,
-	0x9f, 0xaf, 0x18, 0x8f, 0x3f, 0x5f, 0x31, 0x3e, 0xfd, 0x7c, 0xc5, 0x78, 0xef, 0xba, 0xf2, 0x2f,
-	0x48, 0x03, 0x27, 0xf0, 0x7a, 0x0f, 0x5d, 0xaf, 0x77, 0xd0, 0x1b, 0xca, 0xc1, 0x90, 0x5c, 0x1b,
-	0x1d, 0x1d, 0x5c, 0x1b, 0xed, 0x5f, 0x8b, 0x02, 0xfa, 0xfe, 0x1c, 0xfb, 0xff, 0xa3, 0xeb, 0xff,
-	0x0e, 0x00, 0x00, 0xff, 0xff, 0x57, 0x3d, 0x4e, 0x4d, 0xde, 0x34, 0x00, 0x00,
+	// 3653 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3b, 0xcf, 0x6f, 0x1b, 0xc7,
+	0xb9, 0x5a, 0x92, 0x12, 0xc9, 0x8f, 0xa2, 0xbc, 0x1a, 0xc9, 0x36, 0xa3, 0xf8, 0xc9, 0x7a, 0x1b,
+	0xbf, 0x3c, 0x5b, 0x49, 0x68, 0x3c, 0x19, 0x09, 0x92, 0xf7, 0x14, 0xfb, 0x51, 0x24, 0x6d, 0xd1,
+	0xa2, 0x29, 0x65, 0xb8, 0x4e, 0xde, 0x0b, 0x10, 0xe8, 0xad, 0xc4, 0xb1, 0xc4, 0x27, 0x8a, 0xcb,
+	0xee, 0x2e, 0x1d, 0xbb, 0xc7, 0xa2, 0x28, 0xd0, 0xf6, 0xd6, 0x53, 0x50, 0x14, 0x2d, 0x7a, 0xed,
+	0xa5, 0x39, 0xf4, 0x50, 0x14, 0x28, 0x7a, 0xcd, 0x31, 0x7f, 0x41, 0x90, 0xe4, 0xd8, 0x16, 0x28,
+	0x7a, 0x29, 0x5a, 0x14, 0x45, 0x8b, 0xf9, 0xb5, 0x3b, 0xc3, 0x5d, 0x52, 0x92, 0xe3, 0x24, 0x45,
+	0x9b, 0x93, 0x38, 0xdf, 0xaf, 0x9d, 0xf9, 0xe6, 0xfb, 0x39, 0x33, 0x02, 0xb3, 0xe7, 0x1e, 0xf8,
+	0xc4, 0x7b, 0xd8, 0xdd, 0x27, 0xe5, 0x81, 0xe7, 0x06, 0x2e, 0x82, 0x08, 0xb2, 0xf4, 0xd2, 0x41,
+	0x37, 0x38, 0x1c, 0xee, 0x95, 0xf7, 0xdd, 0xe3, 0xeb, 0x07, 0xee, 0x81, 0x7b, 0x9d, 0x91, 0xec,
+	0x0d, 0x1f, 0xb0, 0x11, 0x1b, 0xb0, 0x5f, 0x9c, 0x75, 0x69, 0xee, 0x98, 0x04, 0x4e, 0xc7, 0x09,
+	0x1c, 0x3e, 0xb6, 0x7e, 0x91, 0x81, 0x6c, 0xb5, 0xd5, 0x0e, 0x5c, 0x8f, 0x20, 0x04, 0x99, 0xfb,
+	0xf7, 0x1b, 0xb5, 0x92, 0xb1, 0x62, 0x5c, 0xcd, 0x63, 0xf6, 0x1b, 0x3d, 0x0f, 0x73, 0x6d, 0xfe,
+	0xa5, 0x4a, 0xa7, 0xe3, 0x11, 0xdf, 0x2f, 0xa5, 0x18, 0x76, 0x04, 0x8a, 0x96, 0x01, 0xda, 0x6f,
+	0x34, 0x25, 0x4d, 0x9a, 0xd1, 0x28, 0x10, 0x54, 0x06, 0xd4, 0x74, 0xf7, 0x8f, 0x46, 0x64, 0x65,
+	0x18, 0x5d, 0x02, 0x86, 0xca, 0xab, 0x06, 0x3d, 0x49, 0x37, 0xcd, 0xe5, 0x45, 0x10, 0x74, 0x05,
+	0x32, 0xd8, 0xed, 0x91, 0xd2, 0xcc, 0x8a, 0x71, 0x75, 0x6e, 0xcd, 0x2c, 0x87, 0xcb, 0xaa, 0xb6,
+	0x28, 0x1c, 0x33, 0x2c, 0x5d, 0x91, 0xdd, 0xdd, 0x3f, 0x2a, 0x65, 0x57, 0x8c, 0xab, 0x19, 0xcc,
+	0x7e, 0xa3, 0x17, 0x60, 0xba, 0x1d, 0x38, 0x01, 0x29, 0xe5, 0x18, 0xeb, 0xf9, 0xb2, 0xa2, 0xde,
+	0x96, 0xdb, 0x21, 0x0c, 0x89, 0x39, 0x0d, 0x7a, 0x1d, 0x66, 0x9a, 0xce, 0x1e, 0xe9, 0xf9, 0xa5,
+	0xfc, 0x4a, 0xfa, 0x6a, 0x61, 0xed, 0xb2, 0x4a, 0x2d, 0xf4, 0x56, 0xe6, 0x14, 0xf5, 0x7e, 0xe0,
+	0x3d, 0xde, 0xc8, 0x7c, 0xf0, 0xd1, 0xe5, 0x29, 0x2c, 0x98, 0xd0, 0x7f, 0x40, 0xfe, 0x2d, 0xd7,
+	0x3b, 0xe2, 0xdf, 0x03, 0xf6, 0xbd, 0x85, 0x68, 0xaa, 0x21, 0x0a, 0x47, 0x54, 0xc8, 0x82, 0xd9,
+	0x37, 0x86, 0xc4, 0x7b, 0x2c, 0x97, 0x5e, 0x60, 0x4b, 0xd7, 0x60, 0xe8, 0x15, 0x80, 0xaa, 0xdb,
+	0x7f, 0xd0, 0x3d, 0xa8, 0x39, 0x81, 0x53, 0x9a, 0x5d, 0x31, 0xae, 0x16, 0xd6, 0x2e, 0x68, 0x33,
+	0x0b, 0xb1, 0x58, 0xa1, 0x5c, 0x6a, 0x41, 0x41, 0x99, 0x2b, 0x32, 0x21, 0x7d, 0x44, 0x1e, 0x8b,
+	0xed, 0xa6, 0x3f, 0xd1, 0x35, 0x98, 0x7e, 0xe8, 0xf4, 0x86, 0x84, 0x6d, 0x72, 0x41, 0x9d, 0x2b,
+	0xe3, 0x6b, 0x76, 0xfd, 0x00, 0x73, 0x8a, 0xff, 0x4c, 0xbd, 0x6a, 0x58, 0x7f, 0x4a, 0x41, 0xd6,
+	0x7e, 0x0a, 0xc6, 0x23, 0xb7, 0x29, 0x9d, 0xb4, 0x4d, 0x99, 0x53, 0x6c, 0xd3, 0xcb, 0x30, 0xd3,
+	0x3e, 0x74, 0xbc, 0x0e, 0xb5, 0x14, 0xba, 0x4d, 0x17, 0x55, 0x6a, 0xbb, 0xc5, 0x70, 0x8d, 0xfe,
+	0x03, 0x57, 0x6e, 0x0f, 0x27, 0x46, 0x6b, 0xb0, 0xd8, 0x74, 0x0f, 0x02, 0xa7, 0xdb, 0xa3, 0x13,
+	0x22, 0x9e, 0x9c, 0xe5, 0x0c, 0x9b, 0x65, 0x22, 0x6e, 0x8c, 0x21, 0x67, 0x4f, 0x69, 0xc8, 0xb9,
+	0x98, 0x21, 0xeb, 0x7b, 0x99, 0x3f, 0xed, 0x5e, 0x5a, 0x7f, 0x31, 0x20, 0xd7, 0x74, 0x0f, 0xfe,
+	0x0e, 0x94, 0xbf, 0x0e, 0x39, 0x4c, 0x06, 0xbd, 0xee, 0xbe, 0x23, 0xd5, 0xbf, 0xa4, 0xd2, 0x37,
+	0xdd, 0x03, 0x81, 0x56, 0x76, 0x20, 0xe4, 0x18, 0x59, 0xff, 0xcc, 0xa9, 0xd7, 0xff, 0x3b, 0x03,
+	0x66, 0xe9, 0xfa, 0xe5, 0xd6, 0xa2, 0x12, 0x64, 0xf9, 0x80, 0xab, 0x21, 0x83, 0xe5, 0x10, 0x6d,
+	0x28, 0x13, 0x4c, 0xb1, 0x09, 0x3e, 0x3f, 0x32, 0xc1, 0x50, 0x4a, 0x59, 0x12, 0x32, 0x0f, 0x51,
+	0xa6, 0xb9, 0x08, 0xd3, 0xf5, 0x81, 0xbb, 0x7f, 0x28, 0xd4, 0xc4, 0x07, 0x68, 0x09, 0x72, 0x4d,
+	0xe2, 0x74, 0x88, 0xd7, 0xa8, 0x31, 0x55, 0x65, 0x70, 0x38, 0x66, 0x7a, 0x25, 0xde, 0x31, 0x8b,
+	0x5d, 0x54, 0xaf, 0xc4, 0x3b, 0x5e, 0xfa, 0x2f, 0x28, 0x6a, 0x1f, 0x50, 0x5d, 0x30, 0xc3, 0x5d,
+	0x70, 0x51, 0x75, 0xc1, 0xbc, 0xea, 0x6d, 0x0f, 0x61, 0x4e, 0xd7, 0x25, 0xba, 0xad, 0xab, 0x80,
+	0x89, 0x29, 0xac, 0x95, 0xc6, 0x2d, 0x6e, 0x23, 0x47, 0x75, 0xff, 0xe1, 0x47, 0x97, 0x0d, 0xac,
+	0xab, 0xee, 0x12, 0xe4, 0xa5, 0xd8, 0x1a, 0xfb, 0x6e, 0x06, 0x47, 0x00, 0xeb, 0xe3, 0x34, 0x98,
+	0x22, 0xd4, 0x6d, 0x12, 0xc7, 0x0b, 0xf6, 0x88, 0x13, 0xfc, 0x03, 0xe6, 0x8a, 0x32, 0x20, 0xdb,
+	0xf1, 0xa5, 0xec, 0xaa, 0x47, 0x9c, 0x80, 0x74, 0x98, 0x63, 0xe7, 0x70, 0x02, 0x26, 0x16, 0xa8,
+	0x73, 0x09, 0x81, 0xfa, 0x0a, 0x14, 0x1b, 0xfd, 0x6e, 0x10, 0xe5, 0x80, 0x3c, 0x23, 0xd2, 0x81,
+	0x94, 0xea, 0x8e, 0xeb, 0xfb, 0xdd, 0x81, 0x14, 0x05, 0x9c, 0x4a, 0x03, 0xd2, 0xef, 0x71, 0xc0,
+	0x5d, 0xb7, 0xdb, 0x27, 0x1d, 0x96, 0x18, 0x72, 0x58, 0x83, 0x3d, 0x69, 0x62, 0xb0, 0x5e, 0x81,
+	0xd9, 0x6a, 0xab, 0xd2, 0xeb, 0xb9, 0xfb, 0x4e, 0x40, 0x1a, 0xb5, 0x84, 0xcc, 0xb0, 0x08, 0xd3,
+	0x1b, 0x4e, 0xb0, 0x7f, 0x28, 0xcc, 0x83, 0x0f, 0xac, 0x5f, 0xa6, 0x60, 0x5e, 0x06, 0xa1, 0xc9,
+	0xb6, 0xb1, 0x02, 0x05, 0xec, 0x3c, 0x08, 0x74, 0xc3, 0x50, 0x41, 0x09, 0xd6, 0x93, 0x4e, 0xb4,
+	0x9e, 0x98, 0xb6, 0x32, 0x49, 0xda, 0xfa, 0x6c, 0x41, 0x29, 0xd9, 0x16, 0x66, 0xc6, 0xda, 0x82,
+	0xae, 0xf7, 0xec, 0xa9, 0xf5, 0x5e, 0x87, 0x82, 0x92, 0x9d, 0x26, 0x84, 0xb0, 0xc9, 0x1e, 0xfa,
+	0xfb, 0x14, 0x98, 0xf6, 0xd3, 0xf4, 0xd0, 0x28, 0x9f, 0xa6, 0xcf, 0x92, 0x4f, 0x93, 0xd5, 0x96,
+	0x19, 0xab, 0xb6, 0x71, 0xf9, 0x77, 0xfa, 0xcc, 0xf9, 0x77, 0xe6, 0x94, 0xc1, 0x21, 0x7b, 0x42,
+	0xfe, 0xcd, 0x9d, 0x7a, 0xeb, 0xbe, 0x9d, 0x82, 0x1c, 0x6e, 0xdf, 0xe3, 0x1e, 0x6c, 0x42, 0xda,
+	0xf6, 0x5d, 0x19, 0xc6, 0x6d, 0xdf, 0xa5, 0xfe, 0xd2, 0xe8, 0x77, 0xc8, 0x23, 0xe9, 0x2f, 0x6c,
+	0x40, 0x6d, 0xb7, 0x49, 0x1c, 0x9f, 0x6c, 0xba, 0x3d, 0x9e, 0x34, 0x78, 0x36, 0xd1, 0x81, 0xd4,
+	0xd3, 0x6d, 0x6f, 0xd8, 0xa7, 0xbe, 0xd8, 0x69, 0xfa, 0x7d, 0x91, 0x59, 0x34, 0x18, 0xba, 0x0b,
+	0xb3, 0x9c, 0xa9, 0xeb, 0x07, 0xae, 0xf7, 0x58, 0xd8, 0xb8, 0x96, 0xd7, 0xe4, 0xec, 0xca, 0x2a,
+	0x21, 0xcf, 0x6b, 0x1a, 0xef, 0xd2, 0x2d, 0x98, 0x8f, 0x91, 0x9c, 0x94, 0x99, 0x32, 0x6a, 0x66,
+	0x7a, 0x07, 0xf2, 0xcc, 0xa1, 0xf6, 0x5d, 0xaf, 0x43, 0x19, 0xe9, 0xa4, 0x05, 0x23, 0x9d, 0xeb,
+	0x2a, 0x64, 0xec, 0xc7, 0x03, 0xce, 0x37, 0xa7, 0x2b, 0x97, 0xf3, 0x50, 0x2c, 0x66, 0x34, 0xd4,
+	0x6a, 0xd9, 0x46, 0x50, 0xc5, 0xcc, 0x62, 0xf6, 0xdb, 0x7a, 0xcf, 0x00, 0x60, 0xf2, 0xbf, 0x36,
+	0x24, 0x3e, 0x33, 0xec, 0x96, 0x73, 0x4c, 0xa4, 0x61, 0xd3, 0xdf, 0xaa, 0xe7, 0xa4, 0x74, 0xcf,
+	0x11, 0xd3, 0x49, 0x47, 0xd3, 0x29, 0x41, 0xf6, 0x9e, 0xf3, 0xa8, 0xdd, 0xfd, 0x3a, 0x11, 0x9a,
+	0x95, 0x43, 0xea, 0x65, 0xd2, 0xb8, 0x6b, 0x22, 0x6f, 0x47, 0x00, 0x96, 0xd0, 0x5b, 0x8d, 0x1a,
+	0xb3, 0x35, 0x9a, 0xd0, 0x5b, 0x8d, 0x9a, 0x65, 0x01, 0xd8, 0xbe, 0x2b, 0x67, 0xb6, 0x08, 0xd3,
+	0x55, 0x77, 0xd8, 0x0f, 0xc4, 0xe2, 0xf9, 0xc0, 0xfa, 0x8d, 0x41, 0xa3, 0x2b, 0xf3, 0x4e, 0x56,
+	0x45, 0x27, 0x7a, 0xe6, 0x0d, 0xc8, 0x6f, 0x0f, 0x88, 0xe7, 0x04, 0x5d, 0xb7, 0x2f, 0x14, 0x75,
+	0x5e, 0xef, 0x35, 0x18, 0xef, 0xf6, 0x00, 0x47, 0x74, 0x68, 0x23, 0xec, 0x4e, 0xb8, 0x9b, 0x5e,
+	0x49, 0xe8, 0x4e, 0x18, 0xc1, 0xf8, 0x16, 0xe5, 0xa9, 0xf7, 0x04, 0x4d, 0x28, 0x54, 0x5b, 0x51,
+	0x6e, 0x4b, 0x5a, 0xeb, 0x35, 0x59, 0x5d, 0xa6, 0xc6, 0x77, 0x44, 0x9c, 0xc2, 0xfa, 0x44, 0xe8,
+	0xce, 0x09, 0x26, 0xe8, 0xee, 0xf4, 0xf2, 0x4e, 0xd6, 0x98, 0xfc, 0xd0, 0x17, 0xa8, 0xb1, 0xef,
+	0xcc, 0x40, 0x56, 0x5a, 0x10, 0x8b, 0xf3, 0xec, 0x67, 0x98, 0x03, 0x22, 0x00, 0x2a, 0xc3, 0xcc,
+	0x3d, 0x12, 0x1c, 0xba, 0x9d, 0x24, 0x57, 0xe2, 0x18, 0xe6, 0x4a, 0x82, 0x0a, 0xad, 0xab, 0x7e,
+	0xc3, 0x5c, 0x60, 0x24, 0xb6, 0x45, 0x58, 0xb1, 0x46, 0xd5, 0xcf, 0x2a, 0xac, 0xba, 0x0c, 0x13,
+	0x0a, 0x73, 0x96, 0xc2, 0xda, 0xbf, 0x8c, 0x56, 0x97, 0x5a, 0xd6, 0xc1, 0x1a, 0x0b, 0xba, 0x49,
+	0x8d, 0x21, 0x92, 0x30, 0xcd, 0x24, 0x5c, 0x4a, 0xb0, 0xd2, 0x48, 0x80, 0xca, 0x40, 0xf9, 0x6d,
+	0x85, 0x7f, 0x26, 0xce, 0x6f, 0xc7, 0xf8, 0x15, 0x06, 0x1a, 0xdc, 0x23, 0xf7, 0x4c, 0xca, 0xcb,
+	0x11, 0x16, 0xab, 0x8e, 0xbc, 0xae, 0xd7, 0x43, 0x22, 0x2d, 0x94, 0xf4, 0x89, 0x47, 0x78, 0xac,
+	0x57, 0x4f, 0xeb, 0xba, 0xbf, 0x8b, 0xa6, 0xae, 0x34, 0xce, 0x39, 0xb1, 0x1e, 0x1d, 0x5e, 0xd3,
+	0x1c, 0x88, 0xd5, 0x82, 0x23, 0x09, 0x58, 0x41, 0x63, 0xcd, 0xd9, 0xd6, 0x75, 0x67, 0x61, 0x25,
+	0x62, 0xc2, 0x87, 0x25, 0x1e, 0xeb, 0xae, 0x75, 0x0b, 0x8a, 0x35, 0xd2, 0x23, 0x01, 0x11, 0xd3,
+	0x11, 0xf5, 0xe3, 0x33, 0x2a, 0xbb, 0x46, 0x80, 0x75, 0x7a, 0xb4, 0x01, 0x73, 0x3b, 0x9e, 0xfb,
+	0xe8, 0x71, 0xb4, 0x61, 0x45, 0x26, 0x41, 0xab, 0xbc, 0x74, 0x0a, 0x3c, 0xc2, 0x61, 0xb5, 0xa1,
+	0xc0, 0x4c, 0xd0, 0x1f, 0xb8, 0x7d, 0x9f, 0x4c, 0xa8, 0x88, 0x44, 0x5c, 0x4f, 0x69, 0x71, 0xbd,
+	0xe9, 0xf8, 0x41, 0x14, 0xed, 0xe5, 0xd0, 0x2a, 0x03, 0x52, 0x36, 0x4b, 0x91, 0x7d, 0xbb, 0xeb,
+	0x29, 0x9e, 0x26, 0x87, 0xd6, 0x1f, 0x32, 0xb4, 0x7a, 0x14, 0x64, 0x4f, 0xd7, 0x25, 0x2f, 0x41,
+	0xbe, 0xee, 0x79, 0xae, 0x57, 0x75, 0x3b, 0x84, 0x4d, 0xb3, 0x88, 0x23, 0x00, 0xcd, 0xfc, 0x6c,
+	0x70, 0x8f, 0xf8, 0xbe, 0x73, 0x40, 0x44, 0x69, 0xab, 0xc1, 0x68, 0x41, 0xd3, 0xf0, 0x37, 0x2b,
+	0x5b, 0x84, 0x0c, 0x88, 0xc7, 0x5c, 0x2a, 0x87, 0x15, 0x08, 0xba, 0xa5, 0x69, 0x50, 0xf8, 0xcc,
+	0xc5, 0x98, 0xd7, 0x73, 0xb4, 0x70, 0x7b, 0x4d, 0xe7, 0xd4, 0x8a, 0xdc, 0xe3, 0x63, 0xa7, 0xdf,
+	0xe1, 0x15, 0x7f, 0x36, 0xc1, 0x8a, 0x14, 0x3c, 0xd6, 0xa8, 0xa9, 0xf9, 0x32, 0x47, 0x12, 0x9f,
+	0xcf, 0xc5, 0x3f, 0xaf, 0xa0, 0xb1, 0x4a, 0x4b, 0xed, 0xa7, 0xda, 0x1b, 0xfa, 0x01, 0xf1, 0x6a,
+	0x84, 0x16, 0x7e, 0xbe, 0xf0, 0x1c, 0xcd, 0x7e, 0x74, 0x0a, 0x3c, 0xc2, 0x81, 0x6e, 0x42, 0x3e,
+	0xea, 0x87, 0xb9, 0xef, 0xac, 0xa8, 0xec, 0x21, 0x92, 0xf5, 0x69, 0x98, 0xf8, 0xc3, 0x5e, 0x80,
+	0x23, 0x16, 0x74, 0x13, 0x40, 0xf1, 0x7b, 0xee, 0x40, 0xcb, 0xaa, 0x80, 0xb8, 0x21, 0x61, 0x18,
+	0xf1, 0xfd, 0x43, 0xb2, 0x7f, 0x44, 0x3c, 0xee, 0xbe, 0xb3, 0x09, 0xca, 0x53, 0xf0, 0x58, 0xa3,
+	0xb6, 0xee, 0xb2, 0x76, 0x8a, 0x17, 0x45, 0xa1, 0x5a, 0x5e, 0xa6, 0xe9, 0x81, 0x42, 0xfc, 0x92,
+	0xc1, 0x92, 0xd6, 0xf9, 0xd8, 0x66, 0x52, 0xac, 0xd8, 0x4a, 0x49, 0x6b, 0x3d, 0xa7, 0x6d, 0x04,
+	0xad, 0x4d, 0xde, 0x64, 0x49, 0x49, 0xd4, 0x26, 0x6c, 0x60, 0xdd, 0x81, 0x22, 0xad, 0xcb, 0x6d,
+	0x67, 0xaf, 0x47, 0xee, 0xfb, 0xc4, 0x43, 0x4b, 0x90, 0xa3, 0x7f, 0xfb, 0x51, 0x81, 0x15, 0x8e,
+	0x29, 0x6e, 0xc7, 0xf1, 0xfd, 0x77, 0x5d, 0xaf, 0x23, 0xfa, 0x86, 0x70, 0x6c, 0x7d, 0xd7, 0xa0,
+	0xb3, 0x64, 0x0d, 0x49, 0x62, 0x8e, 0x1e, 0x5f, 0xa0, 0x69, 0xad, 0x4d, 0x7a, 0xa4, 0xb5, 0x89,
+	0xce, 0x5d, 0x32, 0xea, 0xb9, 0xcb, 0x32, 0x4b, 0x6c, 0x7a, 0xa5, 0xa6, 0x40, 0xac, 0xef, 0xa7,
+	0xa8, 0x0d, 0xd3, 0x5a, 0xbd, 0x7a, 0xe8, 0xf4, 0x0f, 0x08, 0xba, 0x11, 0xce, 0x4e, 0x1c, 0x92,
+	0x2c, 0xe8, 0x55, 0x28, 0x43, 0x45, 0x1a, 0xe4, 0xeb, 0x58, 0x07, 0xe0, 0xec, 0x4a, 0xf5, 0x7a,
+	0x29, 0xde, 0x1a, 0x44, 0x34, 0x58, 0xa1, 0x47, 0x36, 0xcc, 0xd1, 0x36, 0xbf, 0xeb, 0xf4, 0xee,
+	0x91, 0xe3, 0x3d, 0xe2, 0xc9, 0x92, 0xe3, 0xc5, 0x71, 0x12, 0xca, 0x3a, 0x39, 0xaf, 0xd4, 0x47,
+	0x64, 0x2c, 0x55, 0x60, 0x21, 0x81, 0xec, 0x4c, 0xe7, 0x48, 0xd7, 0xa0, 0xd8, 0x3e, 0x1c, 0x06,
+	0x1d, 0xf7, 0xdd, 0x3e, 0x8f, 0xdb, 0x74, 0x6f, 0xe8, 0x8f, 0x70, 0xcb, 0xe4, 0xd0, 0xfa, 0x49,
+	0x06, 0xce, 0xb5, 0xf7, 0x0f, 0x49, 0x67, 0xd8, 0x23, 0xc2, 0xcb, 0x13, 0x77, 0xf7, 0x0a, 0x14,
+	0x37, 0x5c, 0x37, 0xf0, 0x03, 0xcf, 0x19, 0x0c, 0xba, 0xfd, 0x03, 0xf6, 0xd1, 0x1c, 0xd6, 0x81,
+	0x34, 0x34, 0x88, 0xe6, 0x8c, 0x29, 0x34, 0xcd, 0x14, 0xaa, 0x85, 0x06, 0x05, 0x8d, 0x55, 0x5a,
+	0x1e, 0x93, 0x22, 0x55, 0x89, 0x5a, 0xa4, 0x34, 0x4e, 0x95, 0x58, 0xdf, 0xfd, 0x5b, 0x23, 0x2b,
+	0x16, 0x85, 0xc8, 0x33, 0x7a, 0x60, 0x50, 0x08, 0xf0, 0x88, 0x86, 0xb6, 0x60, 0x9e, 0xf7, 0xac,
+	0x4a, 0x13, 0x2b, 0x22, 0xab, 0x56, 0x0f, 0xc5, 0x88, 0x70, 0x9c, 0x2f, 0x9e, 0x67, 0xb3, 0x67,
+	0xcc, 0xb3, 0x5b, 0x30, 0x7f, 0xd7, 0xed, 0xf6, 0xf9, 0x81, 0x87, 0x88, 0x7f, 0x22, 0xd0, 0x6a,
+	0xb3, 0x89, 0x11, 0xe1, 0x38, 0x1f, 0xda, 0x04, 0x93, 0x4b, 0x67, 0x89, 0x98, 0x4f, 0x28, 0x1f,
+	0xaf, 0xb3, 0x46, 0x69, 0x70, 0x8c, 0xcb, 0xba, 0x9e, 0x30, 0x2d, 0x1a, 0x33, 0xea, 0x8f, 0xba,
+	0x7e, 0x40, 0x8d, 0x82, 0x46, 0xaf, 0x3c, 0x0e, 0xc7, 0x56, 0x2f, 0x41, 0xab, 0xe8, 0x06, 0x64,
+	0x68, 0xc0, 0x11, 0x6e, 0xaa, 0x29, 0x45, 0x8b, 0x54, 0xc2, 0x59, 0x19, 0x31, 0xeb, 0x98, 0x1d,
+	0xff, 0x88, 0x76, 0x8b, 0x7b, 0x8e, 0x2f, 0x6d, 0x5e, 0x83, 0x51, 0xb3, 0xd7, 0xd5, 0x38, 0xde,
+	0xec, 0x5f, 0x8c, 0xeb, 0x64, 0x02, 0xb5, 0xa3, 0xe7, 0xcb, 0xf0, 0xe0, 0xd7, 0x88, 0x0e, 0x7e,
+	0xd1, 0xeb, 0x90, 0x13, 0x34, 0xf2, 0x08, 0xfa, 0x59, 0xcd, 0xf8, 0x74, 0x1f, 0x93, 0xe7, 0x51,
+	0x92, 0xc5, 0xfa, 0x73, 0x86, 0x16, 0x85, 0xfc, 0x83, 0x34, 0x4b, 0xc9, 0x33, 0x7b, 0x43, 0x39,
+	0xb3, 0xff, 0xe7, 0x3a, 0x7d, 0xad, 0x84, 0x8d, 0x5c, 0x8e, 0xa9, 0xf3, 0xb9, 0x84, 0xea, 0x9a,
+	0x1d, 0xe8, 0x9f, 0xf2, 0x72, 0x2e, 0xff, 0x44, 0x97, 0x73, 0x90, 0x7c, 0xe6, 0xab, 0x9f, 0x4f,
+	0x16, 0x4e, 0x73, 0x9a, 0x3b, 0x7b, 0xe2, 0x69, 0x6e, 0xf1, 0x4b, 0xbb, 0xe6, 0xfb, 0x81, 0xc1,
+	0xef, 0x88, 0xc5, 0x85, 0x28, 0xd3, 0xad, 0xac, 0x45, 0x2e, 0x27, 0x34, 0x17, 0x65, 0x4e, 0xa1,
+	0xe9, 0x9c, 0x83, 0x96, 0x30, 0x14, 0x14, 0x64, 0xc2, 0xd4, 0x5e, 0xd2, 0xa7, 0x76, 0x71, 0xcc,
+	0xb6, 0xaa, 0xd3, 0xfb, 0x6d, 0x8a, 0x9d, 0xa2, 0x3e, 0x15, 0xe7, 0xf8, 0xea, 0xe0, 0xf3, 0xc4,
+	0x83, 0x4f, 0x6a, 0x0d, 0xf6, 0x69, 0xac, 0xc1, 0xfe, 0x7c, 0xad, 0xc1, 0x4e, 0xb6, 0x86, 0xef,
+	0x19, 0x00, 0x4a, 0xd8, 0x4e, 0xaa, 0x56, 0xa4, 0x81, 0xa4, 0x14, 0x03, 0xb9, 0x02, 0x45, 0x6a,
+	0xf6, 0xa4, 0xaf, 0x07, 0x46, 0x1d, 0x38, 0xa2, 0xb3, 0xcc, 0xa9, 0x75, 0xf6, 0xe3, 0x68, 0x52,
+	0x54, 0x6d, 0xff, 0x3d, 0xa2, 0x36, 0x2b, 0xd6, 0x20, 0x9f, 0xa4, 0xb9, 0x37, 0x4e, 0xd2, 0xdc,
+	0x8b, 0xba, 0xe6, 0x2e, 0x24, 0x7c, 0x81, 0x66, 0x71, 0x45, 0x71, 0xdf, 0x30, 0x46, 0xdb, 0xf7,
+	0x71, 0xa5, 0x9e, 0xae, 0xa8, 0xd4, 0xc9, 0x8a, 0x4a, 0x9f, 0x5a, 0x51, 0x7f, 0x34, 0x46, 0x7b,
+	0x40, 0xf4, 0x32, 0xe4, 0xc4, 0x56, 0x4b, 0x75, 0x2d, 0x24, 0x98, 0x81, 0x4c, 0x99, 0x92, 0x94,
+	0xb2, 0x55, 0x25, 0x5b, 0x2a, 0xce, 0x56, 0xd5, 0xd9, 0x24, 0x29, 0x7a, 0x95, 0x1d, 0x65, 0x0b,
+	0x3e, 0x1e, 0x03, 0x16, 0x93, 0x4e, 0xbc, 0x04, 0x63, 0x44, 0x8c, 0x6e, 0x42, 0x21, 0x52, 0x2c,
+	0x4d, 0x98, 0xe9, 0xf1, 0x7a, 0x97, 0x6d, 0xb7, 0xc2, 0x60, 0x7d, 0xcb, 0x80, 0x82, 0x58, 0x3a,
+	0x0b, 0x63, 0xaf, 0xb1, 0x75, 0xf3, 0x60, 0x64, 0x88, 0x60, 0x14, 0xc6, 0x69, 0x81, 0xd1, 0x3a,
+	0xbf, 0x90, 0x1c, 0xad, 0xf3, 0x45, 0x70, 0x5e, 0xbe, 0xf8, 0x92, 0x12, 0xe3, 0x05, 0x4a, 0x63,
+	0x8e, 0x18, 0xac, 0x5f, 0xa5, 0xe0, 0xbc, 0xe8, 0x31, 0x64, 0xdd, 0x28, 0x8e, 0xc5, 0x9e, 0x87,
+	0xb9, 0xd6, 0xf0, 0x78, 0xfb, 0x41, 0x24, 0x9c, 0xc7, 0xd8, 0x11, 0x28, 0xb5, 0x11, 0x06, 0x09,
+	0xe7, 0xcf, 0x3d, 0x4d, 0x07, 0xa2, 0x55, 0x30, 0x25, 0x5f, 0x78, 0x55, 0xc7, 0xfb, 0xbf, 0x18,
+	0x1c, 0x5d, 0x80, 0x99, 0x16, 0x79, 0x14, 0x84, 0xd7, 0xec, 0x62, 0x84, 0x6c, 0x28, 0xf0, 0x5f,
+	0x1b, 0x8f, 0xb7, 0x88, 0xbc, 0x05, 0x59, 0x53, 0x95, 0x9e, 0xb8, 0x92, 0xb2, 0xc2, 0xc4, 0xfb,
+	0x2c, 0x55, 0xcc, 0xd2, 0x4d, 0x30, 0x47, 0x09, 0x92, 0xaf, 0x44, 0xc7, 0xdc, 0x87, 0xfc, 0x3c,
+	0xc5, 0x2f, 0xe6, 0x27, 0xa6, 0xa4, 0xaf, 0x6e, 0x44, 0x93, 0x02, 0xc0, 0xcf, 0x52, 0xe2, 0x59,
+	0x0b, 0x8d, 0x93, 0x37, 0xc3, 0x6c, 0xcc, 0x1d, 0x60, 0x25, 0xe6, 0x89, 0x2c, 0x4a, 0x32, 0x12,
+	0x3d, 0x4a, 0x72, 0x0b, 0xbb, 0x19, 0xc6, 0xd9, 0xd4, 0x24, 0xfe, 0xb1, 0x51, 0xb6, 0x0d, 0x05,
+	0x45, 0x78, 0x42, 0x93, 0x5d, 0xd6, 0xa3, 0xec, 0xd8, 0x97, 0x17, 0x8a, 0x71, 0x30, 0xa1, 0x13,
+	0x43, 0xf7, 0x49, 0x42, 0x93, 0xb2, 0xde, 0xfb, 0xd3, 0xfa, 0xb9, 0x53, 0xa2, 0xc5, 0xdd, 0xd2,
+	0x02, 0x4c, 0x62, 0x85, 0x15, 0xa1, 0x65, 0x88, 0x52, 0x43, 0xd2, 0x8d, 0x30, 0xf3, 0x8b, 0x90,
+	0xbe, 0x90, 0x90, 0xef, 0xe5, 0x29, 0x8a, 0xac, 0x11, 0x5e, 0x89, 0x36, 0x54, 0x64, 0xcc, 0xc5,
+	0xa4, 0x6d, 0x90, 0x16, 0x17, 0x6e, 0xfe, 0x8d, 0xb0, 0xe8, 0x14, 0xed, 0xfa, 0x42, 0x42, 0xa9,
+	0x29, 0x3f, 0x26, 0xcb, 0xd3, 0xeb, 0xf2, 0x2a, 0x88, 0x77, 0x1b, 0x5a, 0xfb, 0x28, 0x4f, 0x48,
+	0xb5, 0x0b, 0xa1, 0x96, 0xb0, 0x6b, 0xd1, 0x80, 0x89, 0x53, 0xbb, 0x2c, 0xe3, 0x5e, 0x1e, 0x6d,
+	0x3e, 0x75, 0x2a, 0x9c, 0xc0, 0x89, 0xea, 0x23, 0x07, 0x6a, 0xa2, 0xb0, 0x3a, 0xb1, 0x8f, 0x1d,
+	0x39, 0x86, 0x93, 0xf1, 0xae, 0xc3, 0x1a, 0x13, 0x19, 0xef, 0x3a, 0x68, 0x4b, 0x8f, 0x77, 0xc0,
+	0xcc, 0xfa, 0xda, 0xb8, 0xd3, 0xc5, 0xc9, 0x61, 0x0e, 0xad, 0xab, 0x45, 0x89, 0x38, 0xeb, 0xbc,
+	0x90, 0x5c, 0x8a, 0xc8, 0xeb, 0xa1, 0x08, 0xf2, 0x99, 0x83, 0xe4, 0xfb, 0x39, 0x30, 0xe5, 0x96,
+	0x84, 0x17, 0xe9, 0xe1, 0xb5, 0xb9, 0xa1, 0x5e, 0x9b, 0x27, 0x15, 0x6c, 0x51, 0x46, 0x48, 0x6b,
+	0x19, 0x61, 0x5b, 0xd7, 0x10, 0x4f, 0xc3, 0x2f, 0x25, 0xd9, 0x41, 0x78, 0x3f, 0x3e, 0x59, 0x4b,
+	0x09, 0xef, 0xb8, 0xbe, 0x7c, 0x33, 0xeb, 0x80, 0x39, 0x72, 0x66, 0x20, 0x1b, 0xe1, 0xb5, 0x89,
+	0x4b, 0x1d, 0x65, 0x52, 0xa3, 0x5e, 0x4c, 0x22, 0x6a, 0xa8, 0x75, 0x04, 0x7f, 0x00, 0xfb, 0xc2,
+	0x44, 0xf1, 0x21, 0x35, 0xd7, 0x63, 0xc4, 0xad, 0x7a, 0x33, 0x9c, 0xda, 0x9b, 0x95, 0x78, 0x53,
+	0x78, 0xa2, 0x78, 0x33, 0x7b, 0x86, 0x78, 0x33, 0x12, 0x1d, 0x8b, 0x67, 0x8e, 0x8e, 0x31, 0xd7,
+	0x9f, 0x7b, 0x22, 0xd7, 0xd7, 0xbd, 0xf2, 0xdc, 0x17, 0xeb, 0x95, 0x4b, 0xef, 0xc0, 0xf9, 0x44,
+	0x1b, 0x39, 0x63, 0x9e, 0xd2, 0x2e, 0x88, 0x14, 0xf1, 0xeb, 0xec, 0x0d, 0xe3, 0x98, 0xa4, 0x7a,
+	0x62, 0xc8, 0x68, 0x40, 0x41, 0x7d, 0xfe, 0xf8, 0x19, 0x5e, 0x38, 0x59, 0x3f, 0x4c, 0xc1, 0x62,
+	0xd2, 0x5d, 0xd0, 0x84, 0x1b, 0xc7, 0x9d, 0xd8, 0x33, 0xd2, 0xf2, 0x49, 0x37, 0x4b, 0xfa, 0x73,
+	0xd2, 0x58, 0x51, 0xf5, 0x74, 0x1e, 0x95, 0xda, 0x27, 0x3f, 0x2a, 0x9d, 0xd4, 0x47, 0x2b, 0x1a,
+	0x55, 0x75, 0xfd, 0x53, 0x03, 0x60, 0xc3, 0xd9, 0x3f, 0x1a, 0x0e, 0x68, 0x5d, 0xa6, 0x84, 0x5b,
+	0x43, 0x0b, 0xb7, 0x0d, 0x3d, 0xdc, 0x72, 0xbd, 0xfc, 0xbb, 0x2a, 0x3f, 0x12, 0xf2, 0x39, 0x57,
+	0xdd, 0xdf, 0x34, 0x64, 0xcd, 0xd9, 0x08, 0xc8, 0x71, 0xe2, 0x33, 0x21, 0x0b, 0x66, 0xab, 0x43,
+	0xcf, 0x23, 0xfd, 0xe0, 0x4d, 0xe5, 0x6e, 0x44, 0x83, 0x51, 0x9a, 0x1a, 0x79, 0xe0, 0x0c, 0x7b,
+	0x82, 0x86, 0xd7, 0xdc, 0x1a, 0x8c, 0x6e, 0x51, 0xa3, 0x1f, 0x10, 0xaf, 0xef, 0xf4, 0x44, 0xb1,
+	0x1d, 0x8e, 0xad, 0x1f, 0x19, 0x6a, 0xe9, 0x8b, 0x5e, 0x87, 0x6c, 0xd5, 0xed, 0x07, 0x84, 0xbd,
+	0x0a, 0x8a, 0x9f, 0x54, 0x86, 0x84, 0x65, 0x41, 0xc5, 0x15, 0x23, 0x79, 0x96, 0x30, 0xbb, 0xf8,
+	0x08, 0x11, 0x67, 0xec, 0xf4, 0x23, 0x75, 0x28, 0x8a, 0x5a, 0xfd, 0x3f, 0x80, 0xfb, 0x83, 0x8e,
+	0x13, 0xf0, 0xab, 0x95, 0x8b, 0xb0, 0xa0, 0x3d, 0x3f, 0xe3, 0x28, 0x73, 0x0a, 0x9d, 0x87, 0x79,
+	0xf9, 0xe4, 0xac, 0xd9, 0x6e, 0x09, 0xb0, 0x81, 0x16, 0xe0, 0x1c, 0x8d, 0x53, 0x6c, 0x3a, 0x02,
+	0x98, 0x42, 0x45, 0xc8, 0xdb, 0xed, 0x6d, 0x31, 0x4c, 0xaf, 0x96, 0x21, 0x1f, 0x3e, 0x13, 0x47,
+	0xe7, 0xa0, 0xd0, 0x72, 0xbd, 0x63, 0xa7, 0xc7, 0x86, 0xe6, 0x14, 0x32, 0x61, 0xd6, 0xee, 0x1e,
+	0x13, 0x77, 0x18, 0x70, 0x88, 0xb1, 0xfa, 0xd7, 0x14, 0x40, 0x74, 0x59, 0x8e, 0xe6, 0x00, 0xec,
+	0xf6, 0xf6, 0xee, 0xfd, 0x9d, 0x5a, 0xc5, 0xae, 0x9b, 0x53, 0x08, 0x60, 0xa6, 0xb2, 0xb3, 0x53,
+	0x6f, 0xd5, 0x4c, 0x03, 0xe5, 0x20, 0x83, 0xeb, 0x95, 0x9a, 0x99, 0x42, 0xb3, 0x90, 0xb3, 0xf1,
+	0xfd, 0x56, 0x95, 0xd2, 0xa4, 0xa9, 0xd0, 0x3b, 0x75, 0x7b, 0x37, 0x84, 0x64, 0x50, 0x01, 0xb2,
+	0xd5, 0xed, 0x56, 0xab, 0x5e, 0xb5, 0xcd, 0x69, 0x2a, 0x52, 0x0c, 0x76, 0xf1, 0xb6, 0x39, 0x83,
+	0xe6, 0xa1, 0xd8, 0xdc, 0xbe, 0xb3, 0xbb, 0x59, 0xaf, 0x60, 0x7b, 0xa3, 0x5e, 0xb1, 0xcd, 0x2c,
+	0x95, 0x50, 0x6d, 0x29, 0x90, 0x1c, 0x9b, 0xa8, 0x0a, 0xc9, 0x23, 0x04, 0x73, 0xd5, 0xcd, 0x7a,
+	0x75, 0x6b, 0x77, 0xb3, 0xb2, 0x55, 0xaf, 0xef, 0xd4, 0xb1, 0x09, 0x54, 0x81, 0xf4, 0xcb, 0xd5,
+	0xe6, 0xfd, 0xb6, 0x5d, 0xc7, 0xbb, 0xb5, 0xba, 0x5d, 0x69, 0x34, 0xdb, 0x66, 0x81, 0x12, 0x53,
+	0x44, 0x7b, 0xb3, 0x82, 0x6b, 0xbb, 0x8d, 0xd6, 0xed, 0x6d, 0x73, 0x96, 0x09, 0x68, 0xed, 0x56,
+	0x9a, 0xcd, 0x6d, 0x3a, 0xcb, 0xdd, 0x46, 0xcd, 0x2c, 0x52, 0x45, 0xab, 0x02, 0xda, 0x36, 0x9d,
+	0xff, 0x1c, 0x53, 0x34, 0xd3, 0xc0, 0x6e, 0xb5, 0xb5, 0xdb, 0xac, 0x6c, 0xd4, 0x9b, 0xe6, 0x39,
+	0x54, 0x82, 0xc5, 0x08, 0xf8, 0xd6, 0x36, 0xde, 0x12, 0xe4, 0x26, 0x95, 0xbc, 0x53, 0xb1, 0xab,
+	0x9b, 0x14, 0xd1, 0xb6, 0xb7, 0x71, 0xdd, 0x9c, 0xa7, 0x22, 0x6a, 0xf5, 0x66, 0x9d, 0x53, 0x73,
+	0x20, 0xa2, 0xc0, 0x1d, 0xbc, 0xfd, 0x3f, 0xff, 0xab, 0x2c, 0x6c, 0x61, 0xb5, 0x05, 0x10, 0xbd,
+	0xc5, 0xa3, 0xda, 0xa2, 0x7b, 0xcc, 0x21, 0xe6, 0x14, 0x55, 0xb5, 0xb4, 0x6f, 0xd3, 0xa0, 0x1b,
+	0xca, 0x2c, 0x26, 0xdc, 0xfd, 0x79, 0xf1, 0xac, 0x11, 0x93, 0xff, 0x27, 0xfb, 0x01, 0xe9, 0x98,
+	0xe9, 0xd5, 0x55, 0xc8, 0x87, 0x4f, 0xd6, 0x28, 0x7b, 0x9b, 0x04, 0x6c, 0x64, 0x4e, 0x51, 0x76,
+	0x7e, 0xbb, 0xc2, 0x01, 0xc6, 0xea, 0xaf, 0x53, 0x80, 0x64, 0x29, 0xa1, 0x18, 0x26, 0xb5, 0x82,
+	0xee, 0xfe, 0x91, 0x6a, 0x8f, 0xca, 0xdb, 0xa0, 0xd0, 0x1e, 0xa9, 0x99, 0xc6, 0xc0, 0x29, 0x74,
+	0x01, 0x90, 0xfa, 0x14, 0x49, 0x9a, 0x26, 0xfd, 0xfa, 0x1d, 0x12, 0x84, 0x66, 0x9e, 0x41, 0xcf,
+	0xc4, 0x32, 0x9e, 0x40, 0x4d, 0x53, 0x95, 0xb6, 0x09, 0x37, 0x52, 0x01, 0x9b, 0xa1, 0x1b, 0xa0,
+	0x1f, 0x29, 0x08, 0x4c, 0x16, 0x5d, 0x86, 0x67, 0xdb, 0x24, 0x88, 0x17, 0x6b, 0x82, 0x20, 0x87,
+	0x96, 0xe0, 0x82, 0x20, 0x08, 0xb3, 0xbd, 0xc0, 0xe5, 0xa9, 0x0a, 0xf9, 0x6f, 0xa1, 0x35, 0x13,
+	0xe8, 0xc2, 0x24, 0x28, 0xbc, 0x81, 0x30, 0x0b, 0xd4, 0x28, 0x77, 0x68, 0x5a, 0x15, 0x47, 0x56,
+	0xe6, 0x2c, 0xe5, 0xc5, 0xe4, 0xd8, 0x7d, 0x28, 0x6f, 0xb6, 0xcc, 0x22, 0x9d, 0xa5, 0x7e, 0x96,
+	0x27, 0x3e, 0x34, 0xb7, 0xfa, 0x9e, 0x01, 0x45, 0xad, 0x44, 0xa5, 0xf6, 0x20, 0x01, 0xa2, 0x7b,
+	0x37, 0xa7, 0xa8, 0x56, 0x24, 0x50, 0xbb, 0xc4, 0x35, 0x0d, 0xf4, 0x6f, 0xf0, 0xaf, 0x31, 0x94,
+	0xac, 0x15, 0x30, 0xd9, 0x27, 0xdd, 0x87, 0xa4, 0x63, 0xa6, 0xd0, 0xb3, 0x70, 0x31, 0x46, 0x76,
+	0xdb, 0xe9, 0xf6, 0xa8, 0x79, 0xa8, 0xdf, 0xc4, 0xc3, 0x7e, 0x9f, 0x0a, 0xce, 0xac, 0xee, 0x25,
+	0x15, 0xc9, 0x74, 0x29, 0x1a, 0x34, 0x9a, 0xe3, 0x28, 0x46, 0x4a, 0x32, 0x62, 0x98, 0x76, 0xe0,
+	0x0e, 0x06, 0x74, 0x56, 0xab, 0x87, 0x60, 0x8e, 0xde, 0xda, 0x53, 0x43, 0xab, 0x74, 0x3a, 0x22,
+	0x0f, 0x9a, 0x53, 0x91, 0x3e, 0x25, 0xc8, 0xa0, 0x4a, 0x6f, 0x07, 0x8e, 0x17, 0x48, 0x48, 0x8a,
+	0xda, 0x11, 0x95, 0x2a, 0x01, 0x69, 0x2a, 0x65, 0xab, 0xdb, 0xeb, 0xbd, 0xed, 0x1e, 0xef, 0x75,
+	0x89, 0x99, 0x59, 0xbd, 0xa7, 0xdd, 0x76, 0x53, 0x34, 0xad, 0x7c, 0x38, 0xc4, 0x9c, 0x62, 0x11,
+	0xb3, 0x25, 0x87, 0x06, 0x1d, 0x56, 0xc3, 0x61, 0x8a, 0x6d, 0x32, 0x2b, 0xea, 0x04, 0x24, 0xbd,
+	0x51, 0xff, 0xf0, 0x93, 0xe5, 0xa9, 0x0f, 0x3e, 0x5d, 0x36, 0x3e, 0xfc, 0x74, 0xd9, 0xf8, 0xf8,
+	0xd3, 0x65, 0xe3, 0xed, 0x1b, 0xca, 0x7f, 0xfe, 0x1d, 0x3b, 0x81, 0xd7, 0x7d, 0xe4, 0x7a, 0xdd,
+	0x83, 0x6e, 0x5f, 0x0e, 0xfa, 0xe4, 0xfa, 0xe0, 0xe8, 0xe0, 0xfa, 0x60, 0xef, 0x7a, 0x94, 0x11,
+	0xf6, 0x66, 0xd8, 0xbf, 0xfd, 0xdd, 0xf8, 0x5b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x76, 0x6e,
+	0x91, 0x55, 0x38, 0x00, 0x00,
 }
 
 func (m *CNStore) Marshal() (dAtA []byte, err error) {
@@ -5652,6 +5947,18 @@ func (m *Request) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if m.ProxyHeartbeat != nil {
+		{
+			size, err := m.ProxyHeartbeat.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintLogservice(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x6a
+	}
 	if m.DeleteCNStore != nil {
 		{
 			size, err := m.DeleteCNStore.MarshalToSizedBuffer(dAtA[:i])
@@ -6283,6 +6590,18 @@ func (m *ScheduleCommand) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if m.DeleteProxyStore != nil {
+		{
+			size, err := m.DeleteProxyStore.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintLogservice(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x4a
+	}
 	if m.JoinGossipCluster != nil {
 		{
 			size, err := m.JoinGossipCluster.MarshalToSizedBuffer(dAtA[:i])
@@ -6482,7 +6801,7 @@ func (m *DeleteCNStore) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
-func (m *CommandBatch) Marshal() (dAtA []byte, err error) {
+func (m *DeleteProxyStore) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -6492,12 +6811,12 @@ func (m *CommandBatch) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *CommandBatch) MarshalTo(dAtA []byte) (int, error) {
+func (m *DeleteProxyStore) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *CommandBatch) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *DeleteProxyStore) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
@@ -6506,29 +6825,63 @@ func (m *CommandBatch) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
-	if len(m.Commands) > 0 {
-		for iNdEx := len(m.Commands) - 1; iNdEx >= 0; iNdEx-- {
-			{
-				size, err := m.Commands[iNdEx].MarshalToSizedBuffer(dAtA[:i])
-				if err != nil {
-					return 0, err
-				}
-				i -= size
-				i = encodeVarintLogservice(dAtA, i, uint64(size))
-			}
-			i--
-			dAtA[i] = 0x12
-		}
-	}
-	if m.Term != 0 {
-		i = encodeVarintLogservice(dAtA, i, uint64(m.Term))
+	if len(m.StoreID) > 0 {
+		i -= len(m.StoreID)
+		copy(dAtA[i:], m.StoreID)
+		i = encodeVarintLogservice(dAtA, i, uint64(len(m.StoreID)))
 		i--
-		dAtA[i] = 0x8
+		dAtA[i] = 0xa
 	}
 	return len(dAtA) - i, nil
 }
 
-func (m *CNStoreInfo) Marshal() (dAtA []byte, err error) {
+func (m *CommandBatch) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *CommandBatch) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *CommandBatch) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Commands) > 0 {
+		for iNdEx := len(m.Commands) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.Commands[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintLogservice(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x12
+		}
+	}
+	if m.Term != 0 {
+		i = encodeVarintLogservice(dAtA, i, uint64(m.Term))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *CNStoreInfo) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -6866,6 +7219,168 @@ func (m *TNState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *ProxyStore) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *ProxyStore) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ProxyStore) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if m.ConfigData != nil {
+		{
+			size, err := m.ConfigData.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintLogservice(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x22
+	}
+	if len(m.ListenAddress) > 0 {
+		i -= len(m.ListenAddress)
+		copy(dAtA[i:], m.ListenAddress)
+		i = encodeVarintLogservice(dAtA, i, uint64(len(m.ListenAddress)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.Tick != 0 {
+		i = encodeVarintLogservice(dAtA, i, uint64(m.Tick))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.UUID) > 0 {
+		i -= len(m.UUID)
+		copy(dAtA[i:], m.UUID)
+		i = encodeVarintLogservice(dAtA, i, uint64(len(m.UUID)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *ProxyState) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *ProxyState) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ProxyState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Stores) > 0 {
+		for k := range m.Stores {
+			v := m.Stores[k]
+			baseI := i
+			{
+				size, err := (&v).MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintLogservice(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x12
+			i -= len(k)
+			copy(dAtA[i:], k)
+			i = encodeVarintLogservice(dAtA, i, uint64(len(k)))
+			i--
+			dAtA[i] = 0xa
+			i = encodeVarintLogservice(dAtA, i, uint64(baseI-i))
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *ProxyHeartbeat) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *ProxyHeartbeat) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ProxyHeartbeat) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if m.ConfigData != nil {
+		{
+			size, err := m.ConfigData.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintLogservice(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.ListenAddress) > 0 {
+		i -= len(m.ListenAddress)
+		copy(dAtA[i:], m.ListenAddress)
+		i = encodeVarintLogservice(dAtA, i, uint64(len(m.ListenAddress)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.UUID) > 0 {
+		i -= len(m.UUID)
+		copy(dAtA[i:], m.UUID)
+		i = encodeVarintLogservice(dAtA, i, uint64(len(m.UUID)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *ClusterDetails) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -6890,6 +7405,20 @@ func (m *ClusterDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if len(m.ProxyStores) > 0 {
+		for iNdEx := len(m.ProxyStores) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.ProxyStores[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintLogservice(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x22
+		}
+	}
 	if len(m.LogStores) > 0 {
 		for iNdEx := len(m.LogStores) - 1; iNdEx >= 0; iNdEx-- {
 			{
@@ -7240,6 +7769,16 @@ func (m *CheckerState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	{
+		size, err := m.ProxyState.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintLogservice(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x5a
 	if len(m.NextIDByKey) > 0 {
 		for k := range m.NextIDByKey {
 			v := m.NextIDByKey[k]
@@ -7354,6 +7893,16 @@ func (m *HAKeeperRSMState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	{
+		size, err := m.ProxyState.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintLogservice(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x7a
 	{
 		size, err := m.TaskTableUser.MarshalToSizedBuffer(dAtA[:i])
 		if err != nil {
@@ -8372,6 +8921,10 @@ func (m *Request) Size() (n int) {
 		l = m.DeleteCNStore.Size()
 		n += 1 + l + sovLogservice(uint64(l))
 	}
+	if m.ProxyHeartbeat != nil {
+		l = m.ProxyHeartbeat.Size()
+		n += 1 + l + sovLogservice(uint64(l))
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -8626,6 +9179,10 @@ func (m *ScheduleCommand) Size() (n int) {
 		l = m.JoinGossipCluster.Size()
 		n += 1 + l + sovLogservice(uint64(l))
 	}
+	if m.DeleteProxyStore != nil {
+		l = m.DeleteProxyStore.Size()
+		n += 1 + l + sovLogservice(uint64(l))
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -8684,6 +9241,22 @@ func (m *DeleteCNStore) Size() (n int) {
 	return n
 }
 
+func (m *DeleteProxyStore) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StoreID)
+	if l > 0 {
+		n += 1 + l + sovLogservice(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
 func (m *CommandBatch) Size() (n int) {
 	if m == nil {
 		return 0
@@ -8855,26 +9428,104 @@ func (m *TNState) Size() (n int) {
 	return n
 }
 
-func (m *ClusterDetails) Size() (n int) {
+func (m *ProxyStore) Size() (n int) {
 	if m == nil {
 		return 0
 	}
 	var l int
 	_ = l
-	if len(m.TNStores) > 0 {
-		for _, e := range m.TNStores {
-			l = e.Size()
-			n += 1 + l + sovLogservice(uint64(l))
-		}
+	l = len(m.UUID)
+	if l > 0 {
+		n += 1 + l + sovLogservice(uint64(l))
 	}
-	if len(m.CNStores) > 0 {
-		for _, e := range m.CNStores {
-			l = e.Size()
-			n += 1 + l + sovLogservice(uint64(l))
-		}
+	if m.Tick != 0 {
+		n += 1 + sovLogservice(uint64(m.Tick))
 	}
-	if len(m.LogStores) > 0 {
-		for _, e := range m.LogStores {
+	l = len(m.ListenAddress)
+	if l > 0 {
+		n += 1 + l + sovLogservice(uint64(l))
+	}
+	if m.ConfigData != nil {
+		l = m.ConfigData.Size()
+		n += 1 + l + sovLogservice(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *ProxyState) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.Stores) > 0 {
+		for k, v := range m.Stores {
+			_ = k
+			_ = v
+			l = v.Size()
+			mapEntrySize := 1 + len(k) + sovLogservice(uint64(len(k))) + 1 + l + sovLogservice(uint64(l))
+			n += mapEntrySize + 1 + sovLogservice(uint64(mapEntrySize))
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *ProxyHeartbeat) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.UUID)
+	if l > 0 {
+		n += 1 + l + sovLogservice(uint64(l))
+	}
+	l = len(m.ListenAddress)
+	if l > 0 {
+		n += 1 + l + sovLogservice(uint64(l))
+	}
+	if m.ConfigData != nil {
+		l = m.ConfigData.Size()
+		n += 1 + l + sovLogservice(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *ClusterDetails) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.TNStores) > 0 {
+		for _, e := range m.TNStores {
+			l = e.Size()
+			n += 1 + l + sovLogservice(uint64(l))
+		}
+	}
+	if len(m.CNStores) > 0 {
+		for _, e := range m.CNStores {
+			l = e.Size()
+			n += 1 + l + sovLogservice(uint64(l))
+		}
+	}
+	if len(m.LogStores) > 0 {
+		for _, e := range m.LogStores {
+			l = e.Size()
+			n += 1 + l + sovLogservice(uint64(l))
+		}
+	}
+	if len(m.ProxyStores) > 0 {
+		for _, e := range m.ProxyStores {
 			l = e.Size()
 			n += 1 + l + sovLogservice(uint64(l))
 		}
@@ -9047,6 +9698,8 @@ func (m *CheckerState) Size() (n int) {
 			n += mapEntrySize + 1 + sovLogservice(uint64(mapEntrySize))
 		}
 	}
+	l = m.ProxyState.Size()
+	n += 1 + l + sovLogservice(uint64(l))
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -9112,6 +9765,8 @@ func (m *HAKeeperRSMState) Size() (n int) {
 	n += 1 + l + sovLogservice(uint64(l))
 	l = m.TaskTableUser.Size()
 	n += 1 + l + sovLogservice(uint64(l))
+	l = m.ProxyState.Size()
+	n += 1 + l + sovLogservice(uint64(l))
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -13370,6 +14025,42 @@ func (m *Request) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 13:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ProxyHeartbeat", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.ProxyHeartbeat == nil {
+				m.ProxyHeartbeat = &ProxyHeartbeat{}
+			}
+			if err := m.ProxyHeartbeat.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipLogservice(dAtA[iNdEx:])
@@ -14987,6 +15678,42 @@ func (m *ScheduleCommand) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 9:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DeleteProxyStore", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.DeleteProxyStore == nil {
+				m.DeleteProxyStore = &DeleteProxyStore{}
+			}
+			if err := m.DeleteProxyStore.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipLogservice(dAtA[iNdEx:])
@@ -15291,6 +16018,89 @@ func (m *DeleteCNStore) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *DeleteProxyStore) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLogservice
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: DeleteProxyStore: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: DeleteProxyStore: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StoreID", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StoreID = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLogservice(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *CommandBatch) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
@@ -16276,11 +17086,361 @@ func (m *TNStoreInfo) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.LockServiceAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
-		case 7:
+			m.LockServiceAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 7:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field CtlAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.CtlAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 8:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ConfigData", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.ConfigData == nil {
+				m.ConfigData = &ConfigData{}
+			}
+			if err := m.ConfigData.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLogservice(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *TNState) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLogservice
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: TNState: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: TNState: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Stores", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Stores == nil {
+				m.Stores = make(map[string]TNStoreInfo)
+			}
+			var mapkey string
+			mapvalue := &TNStoreInfo{}
+			for iNdEx < postIndex {
+				entryPreIndex := iNdEx
+				var wire uint64
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowLogservice
+					}
+					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 ErrIntOverflowLogservice
+						}
+						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 ErrInvalidLengthLogservice
+					}
+					postStringIndexmapkey := iNdEx + intStringLenmapkey
+					if postStringIndexmapkey < 0 {
+						return ErrInvalidLengthLogservice
+					}
+					if postStringIndexmapkey > l {
+						return io.ErrUnexpectedEOF
+					}
+					mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
+					iNdEx = postStringIndexmapkey
+				} else if fieldNum == 2 {
+					var mapmsglen int
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowLogservice
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						mapmsglen |= int(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					if mapmsglen < 0 {
+						return ErrInvalidLengthLogservice
+					}
+					postmsgIndex := iNdEx + mapmsglen
+					if postmsgIndex < 0 {
+						return ErrInvalidLengthLogservice
+					}
+					if postmsgIndex > l {
+						return io.ErrUnexpectedEOF
+					}
+					mapvalue = &TNStoreInfo{}
+					if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil {
+						return err
+					}
+					iNdEx = postmsgIndex
+				} else {
+					iNdEx = entryPreIndex
+					skippy, err := skipLogservice(dAtA[iNdEx:])
+					if err != nil {
+						return err
+					}
+					if (skippy < 0) || (iNdEx+skippy) < 0 {
+						return ErrInvalidLengthLogservice
+					}
+					if (iNdEx + skippy) > postIndex {
+						return io.ErrUnexpectedEOF
+					}
+					iNdEx += skippy
+				}
+			}
+			m.Stores[mapkey] = *mapvalue
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLogservice(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *ProxyStore) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLogservice
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: ProxyStore: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ProxyStore: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field UUID", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.UUID = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Tick", wireType)
+			}
+			m.Tick = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Tick |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field CtlAddress", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field ListenAddress", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -16308,9 +17468,9 @@ func (m *TNStoreInfo) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.CtlAddress = string(dAtA[iNdEx:postIndex])
+			m.ListenAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 8:
+		case 4:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ConfigData", wireType)
 			}
@@ -16368,7 +17528,7 @@ func (m *TNStoreInfo) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *TNState) Unmarshal(dAtA []byte) error {
+func (m *ProxyState) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -16391,10 +17551,10 @@ func (m *TNState) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: TNState: wiretype end group for non-group")
+			return fmt.Errorf("proto: ProxyState: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: TNState: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: ProxyState: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
@@ -16427,10 +17587,10 @@ func (m *TNState) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Stores == nil {
-				m.Stores = make(map[string]TNStoreInfo)
+				m.Stores = make(map[string]ProxyStore)
 			}
 			var mapkey string
-			mapvalue := &TNStoreInfo{}
+			mapvalue := &ProxyStore{}
 			for iNdEx < postIndex {
 				entryPreIndex := iNdEx
 				var wire uint64
@@ -16504,7 +17664,7 @@ func (m *TNState) Unmarshal(dAtA []byte) error {
 					if postmsgIndex > l {
 						return io.ErrUnexpectedEOF
 					}
-					mapvalue = &TNStoreInfo{}
+					mapvalue = &ProxyStore{}
 					if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil {
 						return err
 					}
@@ -16548,6 +17708,157 @@ func (m *TNState) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *ProxyHeartbeat) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLogservice
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: ProxyHeartbeat: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ProxyHeartbeat: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field UUID", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.UUID = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ListenAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ListenAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ConfigData", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.ConfigData == nil {
+				m.ConfigData = &ConfigData{}
+			}
+			if err := m.ConfigData.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLogservice(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *ClusterDetails) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
@@ -16679,6 +17990,40 @@ func (m *ClusterDetails) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ProxyStores", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ProxyStores = append(m.ProxyStores, ProxyStore{})
+			if err := m.ProxyStores[len(m.ProxyStores)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipLogservice(dAtA[iNdEx:])
@@ -17994,6 +19339,39 @@ func (m *CheckerState) Unmarshal(dAtA []byte) error {
 			}
 			m.NextIDByKey[mapkey] = mapvalue
 			iNdEx = postIndex
+		case 11:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ProxyState", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.ProxyState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipLogservice(dAtA[iNdEx:])
@@ -18679,6 +20057,39 @@ func (m *HAKeeperRSMState) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 15:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ProxyState", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthLogservice
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.ProxyState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipLogservice(dAtA[iNdEx:])
diff --git a/pkg/proxy/bootstrap.go b/pkg/proxy/bootstrap.go
index 3ca247615..f52851ef6 100644
--- a/pkg/proxy/bootstrap.go
+++ b/pkg/proxy/bootstrap.go
@@ -18,6 +18,7 @@ import (
 	"context"
 	"time"
 
+	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
 	"github.com/matrixorigin/matrixone/pkg/util"
 	db_holder "github.com/matrixorigin/matrixone/pkg/util/export/etl/db"
 )
@@ -28,29 +29,31 @@ const (
 )
 
 func (h *handler) bootstrap(ctx context.Context) {
-	ctx, cancel := context.WithTimeout(ctx, time.Second*3)
-	defer cancel()
-	state, err := h.haKeeperClient.GetClusterState(ctx)
-	if err != nil {
-		panic(err)
-	}
 	ticker := time.NewTicker(time.Millisecond * 200)
 	defer ticker.Stop()
 	retry := 0
 	getClient := func() util.HAKeeperClient {
 		return h.haKeeperClient
 	}
+	var state pb.CheckerState
+	var err error
 	for retry < int(BootstrapTimeout/BootstrapInterval) {
 		select {
 		case <-ticker.C:
+			func(ctx context.Context) {
+				ctx, cancel := context.WithTimeout(ctx, time.Second*3)
+				defer cancel()
+				state, err = h.haKeeperClient.GetClusterState(ctx)
+				if err != nil {
+					panic(err)
+				}
+			}(ctx)
 			if state.TaskTableUser.GetUsername() != "" && state.TaskTableUser.GetPassword() != "" {
 				db_holder.SetSQLWriterDBUser(db_holder.MOLoggerUser, state.TaskTableUser.GetPassword())
 				db_holder.SetSQLWriterDBAddressFunc(util.AddressFunc(getClient))
-				h.logger.Info("proxy bootstrap succeeded")
 				return
 			}
 		case <-ctx.Done():
-			h.logger.Info("proxy bootstrap interrupt for context done.")
 			return
 		}
 		retry += 1
diff --git a/pkg/proxy/config.go b/pkg/proxy/config.go
index dc295bf07..2fe1ae03c 100644
--- a/pkg/proxy/config.go
+++ b/pkg/proxy/config.go
@@ -21,6 +21,8 @@ import (
 	"github.com/matrixorigin/matrixone/pkg/common/moerr"
 	"github.com/matrixorigin/matrixone/pkg/common/runtime"
 	"github.com/matrixorigin/matrixone/pkg/logservice"
+	logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
+	"github.com/matrixorigin/matrixone/pkg/util"
 	"github.com/matrixorigin/matrixone/pkg/util/errutil"
 	"github.com/matrixorigin/matrixone/pkg/util/toml"
 )
@@ -34,6 +36,10 @@ var (
 	defaultRebalanceInterval = 30 * time.Second
 	// The default value of rebalnce tolerance.
 	defaultRebalanceTolerance = 0.3
+	// The default value of heartbeat interval.
+	defaultHeartbeatInterval = time.Second * 3
+	// The default value of heartbeat timeout.
+	defaultHeartbeatTimeout = time.Second * 3
 )
 
 // Config is the configuration of proxy server.
@@ -71,6 +77,10 @@ type Config struct {
 	HAKeeper struct {
 		// ClientConfig is HAKeeper client configuration.
 		ClientConfig logservice.HAKeeperClientConfig
+		// HeartbeatInterval heartbeat interval to send message to HAKeeper. Default is 1s.
+		HeartbeatInterval toml.Duration `toml:"hakeeper-heartbeat-interval"`
+		// HeartbeatTimeout heartbeat request timeout. Default is 3s.
+		HeartbeatTimeout toml.Duration `toml:"hakeeper-heartbeat-timeout"`
 	}
 	// Cluster is the configuration of MO Cluster.
 	Cluster struct {
@@ -131,10 +141,9 @@ func WithTLSKeyFile(f string) Option {
 }
 
 // WithConfigData saves the data from the config file
-func WithConfigData(data []byte) Option {
+func WithConfigData(data map[string]*logservicepb.ConfigItem) Option {
 	return func(s *Server) {
-		s.configData = make([]byte, len(data))
-		copy(s.configData, data)
+		s.configData = util.NewConfigData(data)
 	}
 }
 
@@ -157,6 +166,12 @@ func (c *Config) FillDefault() {
 			c.Plugin.Timeout = time.Second
 		}
 	}
+	if c.HAKeeper.HeartbeatInterval.Duration == 0 {
+		c.HAKeeper.HeartbeatInterval.Duration = defaultHeartbeatInterval
+	}
+	if c.HAKeeper.HeartbeatTimeout.Duration == 0 {
+		c.HAKeeper.HeartbeatTimeout.Duration = defaultHeartbeatTimeout
+	}
 }
 
 // Validate validates the configuration of proxy server.
@@ -172,3 +187,8 @@ func (c *Config) Validate() error {
 	}
 	return nil
 }
+
+func dumpProxyConfig(cfg Config) (map[string]*logservicepb.ConfigItem, error) {
+	defCfg := Config{}
+	return util.DumpConfig(cfg, defCfg)
+}
diff --git a/pkg/proxy/handler.go b/pkg/proxy/handler.go
index 516c75054..33b63cceb 100644
--- a/pkg/proxy/handler.go
+++ b/pkg/proxy/handler.go
@@ -16,9 +16,6 @@ package proxy
 
 import (
 	"context"
-	"net"
-	"time"
-
 	"github.com/fagongzi/goetty/v2"
 	"github.com/matrixorigin/matrixone/pkg/clusterservice"
 	"github.com/matrixorigin/matrixone/pkg/common/log"
@@ -27,6 +24,7 @@ import (
 	"github.com/matrixorigin/matrixone/pkg/common/stopper"
 	"github.com/matrixorigin/matrixone/pkg/logservice"
 	"go.uber.org/zap"
+	"net"
 )
 
 // handler is the proxy service handler.
@@ -43,7 +41,7 @@ type handler struct {
 	// counterSet counts the events in proxy.
 	counterSet *counterSet
 	// haKeeperClient is the client to communicate with HAKeeper.
-	haKeeperClient logservice.ClusterHAKeeperClient
+	haKeeperClient logservice.ProxyHAKeeperClient
 	// ipNetList is the list of ip net, which is parsed from CIDRs.
 	ipNetList []*net.IPNet
 }
@@ -57,22 +55,10 @@ func newProxyHandler(
 	cfg Config,
 	st *stopper.Stopper,
 	cs *counterSet,
-	testHAKeeperClient logservice.ClusterHAKeeperClient,
+	haKeeperClient logservice.ProxyHAKeeperClient,
 ) (*handler, error) {
-	ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
-	defer cancel()
-
-	var err error
-	c := testHAKeeperClient
-	if c == nil {
-		c, err = logservice.NewProxyHAKeeperClient(ctx, cfg.HAKeeper.ClientConfig)
-		if err != nil {
-			return nil, err
-		}
-	}
-
 	// Create the MO cluster.
-	mc := clusterservice.NewMOCluster(c, cfg.Cluster.RefreshInterval.Duration)
+	mc := clusterservice.NewMOCluster(haKeeperClient, cfg.Cluster.RefreshInterval.Duration)
 	rt.SetGlobalVariables(runtime.ClusterService, mc)
 
 	// Create the rebalancer.
@@ -112,14 +98,14 @@ func newProxyHandler(
 		}
 	}
 	return &handler{
-		ctx:            context.Background(),
+		ctx:            ctx,
 		logger:         rt.Logger(),
 		config:         cfg,
 		stopper:        st,
 		moCluster:      mc,
 		counterSet:     cs,
 		router:         ru,
-		haKeeperClient: c,
+		haKeeperClient: haKeeperClient,
 		ipNetList:      ipNetList,
 	}, nil
 }
diff --git a/pkg/proxy/heartbeat.go b/pkg/proxy/heartbeat.go
new file mode 100644
index 000000000..41b9c82e5
--- /dev/null
+++ b/pkg/proxy/heartbeat.go
@@ -0,0 +1,58 @@
+// Copyright 2021 - 2023 Matrix Origin
+//
+// 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 proxy
+
+import (
+	"context"
+	"time"
+
+	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
+	"go.uber.org/zap"
+)
+
+func (s *Server) heartbeat(ctx context.Context) {
+	if s.config.HAKeeper.HeartbeatInterval.Duration == 0 {
+		panic("invalid heartbeat interval")
+	}
+	ticker := time.NewTicker(s.config.HAKeeper.HeartbeatInterval.Duration)
+	defer ticker.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-ticker.C:
+			s.doHeartbeat(ctx)
+			select {
+			case <-ctx.Done():
+				return
+			default:
+			}
+		}
+	}
+}
+
+func (s *Server) doHeartbeat(ctx context.Context) {
+	ctx, cancel := context.WithTimeout(ctx, s.config.HAKeeper.HeartbeatTimeout.Duration)
+	defer cancel()
+	_, err := s.haKeeperClient.SendProxyHeartbeat(ctx, pb.ProxyHeartbeat{
+		UUID:          s.config.UUID,
+		ListenAddress: s.config.ListenAddress,
+		ConfigData:    s.configData.GetData(),
+	})
+	if err != nil {
+		s.runtime.Logger().Error("failed to send heartbeat", zap.Error(err))
+	}
+	s.configData.DecrCount()
+}
diff --git a/pkg/proxy/server.go b/pkg/proxy/server.go
index 9f25e3845..150ac5879 100644
--- a/pkg/proxy/server.go
+++ b/pkg/proxy/server.go
@@ -16,6 +16,8 @@ package proxy
 
 import (
 	"context"
+	"github.com/matrixorigin/matrixone/pkg/util"
+	"time"
 
 	"github.com/fagongzi/goetty/v2"
 	"github.com/matrixorigin/matrixone/pkg/common/runtime"
@@ -37,10 +39,10 @@ type Server struct {
 	// handler handles the client connection.
 	handler *handler
 	// counterSet counts the events in proxy.
-	counterSet *counterSet
-	// for test.
-	testHAKeeperClient logservice.ClusterHAKeeperClient
-	configData         []byte
+	counterSet     *counterSet
+	haKeeperClient logservice.ProxyHAKeeperClient
+	// configData will be sent to HAKeeper.
+	configData *util.ConfigData
 }
 
 // NewServer creates the proxy server.
@@ -54,6 +56,9 @@ func NewServer(ctx context.Context, config Config, opts ...Option) (*Server, err
 
 	frontend.InitServerVersion(version.Version)
 
+	configKVMap, _ := dumpProxyConfig(config)
+	opts = append(opts, WithConfigData(configKVMap))
+
 	s := &Server{
 		config:     config,
 		counterSet: newCounterSet(),
@@ -65,17 +70,31 @@ func NewServer(ctx context.Context, config Config, opts ...Option) (*Server, err
 		panic("runtime of proxy is not set")
 	}
 
+	var err error
+	if s.haKeeperClient == nil {
+		ctx, cancel := context.WithTimeout(ctx, time.Second*3)
+		defer cancel()
+		s.haKeeperClient, err = logservice.NewProxyHAKeeperClient(ctx, config.HAKeeper.ClientConfig)
+		if err != nil {
+			return nil, err
+		}
+	}
+
 	logExporter := newCounterLogExporter(s.counterSet)
 	stats.Register(statsFamilyName, stats.WithLogExporter(logExporter))
 
 	s.stopper = stopper.NewStopper("mo-proxy", stopper.WithLogger(s.runtime.Logger().RawLogger()))
-	h, err := newProxyHandler(ctx, s.runtime, s.config, s.stopper, s.counterSet, s.testHAKeeperClient)
+	h, err := newProxyHandler(ctx, s.runtime, s.config, s.stopper, s.counterSet, s.haKeeperClient)
 	if err != nil {
 		return nil, err
 	}
 
 	go h.bootstrap(ctx)
 
+	if err := s.stopper.RunNamedTask("proxy heartbeat", s.heartbeat); err != nil {
+		return nil, err
+	}
+
 	s.handler = h
 	app, err := goetty.NewApplication(config.ListenAddress, nil,
 		goetty.WithAppLogger(s.runtime.Logger().RawLogger()),
diff --git a/pkg/sql/colexec/table_function/system_view.go b/pkg/sql/colexec/table_function/system_view.go
index 896745534..09ea1e50a 100644
--- a/pkg/sql/colexec/table_function/system_view.go
+++ b/pkg/sql/colexec/table_function/system_view.go
@@ -317,6 +317,23 @@ func moConfigurationsCall(_ int, proc *process.Process, arg *Argument) (bool, er
 			}
 		}
 
+		// fill batch for proxy
+		for _, proxyStore := range details.GetProxyStores() {
+			if proxyStore.GetConfigData() != nil {
+				err = fillMapToBatch(
+					"proxy",
+					proxyStore.GetUUID(),
+					arg.Attrs,
+					proxyStore.GetConfigData().GetContent(),
+					bat,
+					mp,
+				)
+				if err != nil {
+					return false, err
+				}
+			}
+		}
+
 		bat.SetRowCount(bat.Vecs[0].Length())
 		proc.SetInputBatch(bat)
 		arg.ctr.state = dataFinished
diff --git a/proto/logservice.proto b/proto/logservice.proto
index 5a4316bdc..c138baaca 100644
--- a/proto/logservice.proto
+++ b/proto/logservice.proto
@@ -204,6 +204,7 @@ enum MethodType {
   UPDATE_CN_WORK_STATE = 16;
   PATCH_CN_STORE = 17;
   DELETE_CN_STORE = 18;
+  PROXY_HEARTBEAT = 19;
 };
 
 enum RecordType {
@@ -277,6 +278,7 @@ message Request {
   CNWorkState CNWorkState        = 10;
   CNStateLabel CNStateLabel      = 11;
   DeleteCNStore DeleteCNStore    = 12;
+  ProxyHeartbeat ProxyHeartbeat  = 13;
 };
 
 message LogResponse {
@@ -331,6 +333,7 @@ enum HAKeeperUpdateType {
   UpdateCNWorkState = 11;
   PatchCNStore = 12;
   RemoveCNStore = 13;
+  ProxyHeartbeatUpdate = 14;
 }
 
 // HAKeeperState state transition diagram
@@ -422,6 +425,7 @@ enum ServiceType {
   LogService = 0;
   TNService  = 1;
   CNService  = 2;
+  ProxyService = 3;
 }
 
 // ScheduleCommand contains a shard schedule command.
@@ -435,6 +439,7 @@ message ScheduleCommand {
   CreateTaskService CreateTaskService = 6;
   DeleteCNStore     DeleteCNStore     = 7;
   JoinGossipCluster JoinGossipCluster = 8;
+  DeleteProxyStore  DeleteProxyStore  = 9;
 }
 
 message JoinGossipCluster {
@@ -454,6 +459,11 @@ message DeleteCNStore {
   string StoreID = 1;
 }
 
+// DeleteProxyStore deletes Proxy store from ProxyState to save hakeeper's memory.
+message DeleteProxyStore {
+  string StoreID = 1;
+}
+
 message CommandBatch {
   uint64 Term                       = 1;
   repeated ScheduleCommand Commands = 2 [(gogoproto.nullable) = false];
@@ -505,10 +515,28 @@ message TNState {
   map<string, TNStoreInfo> Stores = 1 [(gogoproto.nullable) = false];
 }
 
+message ProxyStore {
+  string UUID = 1;
+  uint64 Tick = 2;
+  string ListenAddress = 3;
+  ConfigData ConfigData = 4;
+}
+
+message ProxyState {
+  map<string, ProxyStore> Stores = 1 [(gogoproto.nullable) = false];
+}
+
+message ProxyHeartbeat {
+  string UUID = 1;
+  string ListenAddress = 2;
+  ConfigData ConfigData = 3;
+};
+
 message ClusterDetails {
   repeated TNStore  TNStores    = 1 [(gogoproto.nullable) = false];
   repeated CNStore  CNStores    = 2 [(gogoproto.nullable) = false];
   repeated LogStore LogStores   = 3 [(gogoproto.nullable) = false];
+  repeated ProxyStore ProxyStores = 4 [(gogoproto.nullable) = false];
 }
 
 // ClusterInfo provides a global view of all shards in the cluster. It
@@ -564,6 +592,7 @@ message CheckerState {
   TaskTableUser TaskTableUser           = 8 [(gogoproto.nullable) = false];
   uint64 NextId                         = 9;
   map<string, uint64> NextIDByKey       = 10;
+  ProxyState ProxyState = 11 [(gogoproto.nullable) = false];
 }
 
 // HAKeeperRSMState contains state maintained by HAKeeper's RSM.
@@ -584,6 +613,7 @@ message HAKeeperRSMState {
   LogState LogState             = 12 [(gogoproto.nullable) = false];
   ClusterInfo ClusterInfo       = 13 [(gogoproto.nullable) = false];
   TaskTableUser TaskTableUser   = 14 [(gogoproto.nullable) = false];
+  ProxyState ProxyState         = 15 [(gogoproto.nullable) = false];
 }
 
 // ReplicaInfo contains details of a replica


Test patch:
diff --git a/pkg/hakeeper/checkers/proxy/check_test.go b/pkg/hakeeper/checkers/proxy/check_test.go
new file mode 100644
index 000000000..8f78049ed
--- /dev/null
+++ b/pkg/hakeeper/checkers/proxy/check_test.go
@@ -0,0 +1,101 @@
+// Copyright 2021 - 2023 Matrix Origin
+//
+// 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 proxy
+
+import (
+	"testing"
+	"time"
+
+	"github.com/matrixorigin/matrixone/pkg/hakeeper"
+	"github.com/matrixorigin/matrixone/pkg/hakeeper/operator"
+	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
+	"github.com/stretchr/testify/assert"
+)
+
+var expiredTick = uint64(hakeeper.DefaultProxyStoreTimeout / time.Second * hakeeper.DefaultTickPerSecond)
+
+func TestParseProxyStores(t *testing.T) {
+	cases := []struct {
+		infos       pb.ProxyState
+		currentTick uint64
+
+		working []string
+		expired []string
+	}{
+		{
+			infos: pb.ProxyState{
+				Stores: map[string]pb.ProxyStore{"a": {
+					Tick: 0,
+				}},
+			},
+			currentTick: 0,
+			working:     []string{"a"},
+			expired:     []string{},
+		},
+		{
+			infos: pb.ProxyState{
+				Stores: map[string]pb.ProxyStore{"a": {
+					Tick: 0,
+				}},
+			},
+			currentTick: expiredTick + 1,
+			working:     []string{},
+			expired:     []string{"a"},
+		},
+		{
+			infos: pb.ProxyState{
+				Stores: map[string]pb.ProxyStore{
+					"a": {
+						Tick: expiredTick,
+					},
+					"b": {
+						Tick: 0,
+					},
+				},
+			},
+			currentTick: expiredTick + 1,
+			working:     []string{"a"},
+			expired:     []string{"b"},
+		},
+	}
+
+	cfg := hakeeper.Config{}
+	cfg.Fill()
+
+	for _, c := range cases {
+		working, expired := parseProxyStores(cfg, c.infos, c.currentTick)
+		assert.Equal(t, c.working, working)
+		assert.Equal(t, c.expired, expired)
+	}
+}
+
+func TestCheck(t *testing.T) {
+	infos := pb.ProxyState{
+		Stores: map[string]pb.ProxyStore{"a": {
+			Tick: 0,
+		}},
+	}
+	currentTick := expiredTick + 1
+	cfg := hakeeper.Config{}
+	cfg.Fill()
+	ops := Check(cfg, infos, currentTick)
+	assert.Equal(t, 1, len(ops))
+	steps := ops[0].OpSteps()
+	assert.Equal(t, 1, len(steps))
+	s, ok := steps[0].(operator.DeleteProxyStore)
+	assert.True(t, ok)
+	assert.NotNil(t, s)
+	assert.Equal(t, "a", s.StoreID)
+}
diff --git a/pkg/hakeeper/operator/controller_test.go b/pkg/hakeeper/operator/controller_test.go
index 92f3709d5..93b0c39f8 100644
--- a/pkg/hakeeper/operator/controller_test.go
+++ b/pkg/hakeeper/operator/controller_test.go
@@ -33,13 +33,13 @@ func TestDispatchAndRemoveOperator(t *testing.T) {
 	operator2 := &Operator{shardID: 1}
 	operator3 := &Operator{shardID: 2}
 
-	c.Dispatch([]*Operator{operator1}, pb.LogState{}, pb.TNState{}, pb.CNState{})
+	c.Dispatch([]*Operator{operator1}, pb.LogState{}, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 	assert.Equal(t, []*Operator{operator1}, c.operators[1])
 
-	c.Dispatch([]*Operator{operator2}, pb.LogState{}, pb.TNState{}, pb.CNState{})
+	c.Dispatch([]*Operator{operator2}, pb.LogState{}, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 	assert.Equal(t, []*Operator{operator1, operator2}, c.operators[1])
 
-	c.Dispatch([]*Operator{operator3}, pb.LogState{}, pb.TNState{}, pb.CNState{})
+	c.Dispatch([]*Operator{operator3}, pb.LogState{}, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 	assert.Equal(t, []*Operator{operator3}, c.operators[2])
 
 	c.RemoveOperator(operator1)
@@ -69,7 +69,7 @@ func TestRemoveFinishedOperator(t *testing.T) {
 		}},
 	}
 
-	c.Dispatch([]*Operator{op1}, logState, pb.TNState{}, pb.CNState{})
+	c.Dispatch([]*Operator{op1}, logState, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 	assert.Equal(t, []*Operator{op1}, c.GetOperators(1))
 
 	logState = pb.LogState{
@@ -79,6 +79,6 @@ func TestRemoveFinishedOperator(t *testing.T) {
 			Epoch:    0,
 		}},
 	}
-	c.RemoveFinishedOperator(logState, pb.TNState{}, pb.CNState{})
+	c.RemoveFinishedOperator(logState, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 	assert.Equal(t, []*Operator(nil), c.GetOperators(1))
 }
diff --git a/pkg/hakeeper/operator/operator_test.go b/pkg/hakeeper/operator/operator_test.go
index 16811bc43..f15d54ab0 100644
--- a/pkg/hakeeper/operator/operator_test.go
+++ b/pkg/hakeeper/operator/operator_test.go
@@ -55,7 +55,7 @@ func TestCheck(t *testing.T) {
 			Epoch:    0,
 		}},
 	}
-	currentStep := op.Check(logState, pb.TNState{}, pb.CNState{})
+	currentStep := op.Check(logState, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 
 	assert.Equal(t,
 		AddLogService{"a", Replica{"d", 1, 4, 1}},
@@ -69,7 +69,7 @@ func TestCheck(t *testing.T) {
 			Epoch:    0,
 		}},
 	}
-	currentStep = op.Check(logState, pb.TNState{}, pb.CNState{})
+	currentStep = op.Check(logState, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 
 	assert.Equal(t,
 		RemoveLogService{"a", Replica{"c", 1, 3, 1}},
@@ -83,10 +83,10 @@ func TestCheck(t *testing.T) {
 			Epoch:    0,
 		}},
 	}
-	currentStep = op.Check(logState, pb.TNState{}, pb.CNState{})
+	currentStep = op.Check(logState, pb.TNState{}, pb.CNState{}, pb.ProxyState{})
 
 	assert.Equal(t, nil, currentStep)
 	assert.Equal(t, SUCCESS, op.Status())
 
-	assert.Equal(t, nil, op.Check(pb.LogState{}, pb.TNState{}, pb.CNState{}))
+	assert.Equal(t, nil, op.Check(pb.LogState{}, pb.TNState{}, pb.CNState{}, pb.ProxyState{}))
 }
diff --git a/pkg/hakeeper/operator/steps_test.go b/pkg/hakeeper/operator/steps_test.go
index a156bca87..76f8c95c2 100644
--- a/pkg/hakeeper/operator/steps_test.go
+++ b/pkg/hakeeper/operator/steps_test.go
@@ -40,11 +40,40 @@ func TestIsFinish(t *testing.T) {
 
 	tnState := pb.TNState{}
 	cnState := pb.CNState{}
+	proxyState := pb.ProxyState{}
 
-	assert.False(t, AddLogService{Replica: Replica{UUID: "d", ShardID: 1, ReplicaID: 4}}.IsFinish(logState, tnState, cnState))
-	assert.True(t, AddLogService{Replica: Replica{UUID: "c", ShardID: 1, ReplicaID: 3}}.IsFinish(logState, tnState, cnState))
-	assert.False(t, RemoveLogService{Replica: Replica{UUID: "c", ShardID: 1, ReplicaID: 3}}.IsFinish(logState, tnState, cnState))
-	assert.True(t, RemoveLogService{Replica: Replica{UUID: "d", ShardID: 1, ReplicaID: 4}}.IsFinish(logState, tnState, cnState))
+	assert.False(t, AddLogService{Replica: Replica{UUID: "d", ShardID: 1, ReplicaID: 4}}.
+		IsFinish(ClusterState{
+			LogState:   logState,
+			TNState:    tnState,
+			CNState:    cnState,
+			ProxyState: proxyState,
+		}),
+	)
+	assert.True(t, AddLogService{Replica: Replica{UUID: "c", ShardID: 1, ReplicaID: 3}}.
+		IsFinish(ClusterState{
+			LogState:   logState,
+			TNState:    tnState,
+			CNState:    cnState,
+			ProxyState: proxyState,
+		}),
+	)
+	assert.False(t, RemoveLogService{Replica: Replica{UUID: "c", ShardID: 1, ReplicaID: 3}}.
+		IsFinish(ClusterState{
+			LogState:   logState,
+			TNState:    tnState,
+			CNState:    cnState,
+			ProxyState: proxyState,
+		}),
+	)
+	assert.True(t, RemoveLogService{Replica: Replica{UUID: "d", ShardID: 1, ReplicaID: 4}}.
+		IsFinish(ClusterState{
+			LogState:   logState,
+			TNState:    tnState,
+			CNState:    cnState,
+			ProxyState: proxyState,
+		}),
+	)
 }
 
 func TestAddLogService(t *testing.T) {
@@ -88,7 +117,12 @@ func TestAddLogService(t *testing.T) {
 
 	for i, c := range cases {
 		fmt.Printf("case %v: %s\n", i, c.desc)
-		assert.Equal(t, c.expected, c.command.IsFinish(c.state, pb.TNState{}, pb.CNState{}))
+		assert.Equal(t, c.expected, c.command.IsFinish(ClusterState{
+			LogState:   c.state,
+			TNState:    pb.TNState{},
+			CNState:    pb.CNState{},
+			ProxyState: pb.ProxyState{},
+		}))
 	}
 }
 
@@ -134,7 +168,12 @@ func TestRemoveLogService(t *testing.T) {
 
 	for i, c := range cases {
 		fmt.Printf("case %v: %s\n", i, c.desc)
-		assert.Equal(t, c.expected, c.command.IsFinish(c.state, pb.TNState{}, pb.CNState{}))
+		assert.Equal(t, c.expected, c.command.IsFinish(ClusterState{
+			LogState:   c.state,
+			TNState:    pb.TNState{},
+			CNState:    pb.CNState{},
+			ProxyState: pb.ProxyState{},
+		}))
 	}
 }
 
@@ -187,7 +226,12 @@ func TestStartLogService(t *testing.T) {
 
 	for i, c := range cases {
 		fmt.Printf("case %v: %s\n", i, c.desc)
-		assert.Equal(t, c.expected, c.command.IsFinish(c.state, pb.TNState{}, pb.CNState{}))
+		assert.Equal(t, c.expected, c.command.IsFinish(ClusterState{
+			LogState:   c.state,
+			TNState:    pb.TNState{},
+			CNState:    pb.CNState{},
+			ProxyState: pb.ProxyState{},
+		}))
 	}
 }
 
@@ -238,7 +282,12 @@ func TestStopLogService(t *testing.T) {
 
 	for i, c := range cases {
 		fmt.Printf("case %v: %s\n", i, c.desc)
-		assert.Equal(t, c.expected, c.command.IsFinish(c.state, pb.TNState{}, pb.CNState{}))
+		assert.Equal(t, c.expected, c.command.IsFinish(ClusterState{
+			LogState:   c.state,
+			TNState:    pb.TNState{},
+			CNState:    pb.CNState{},
+			ProxyState: pb.ProxyState{},
+		}))
 	}
 }
 
@@ -286,7 +335,12 @@ func TestAddTnReplica(t *testing.T) {
 
 	for i, c := range cases {
 		fmt.Printf("case %v: %s\n", i, c.desc)
-		assert.Equal(t, c.expected, c.command.IsFinish(pb.LogState{}, c.state, pb.CNState{}))
+		assert.Equal(t, c.expected, c.command.IsFinish(ClusterState{
+			LogState:   pb.LogState{},
+			TNState:    c.state,
+			CNState:    pb.CNState{},
+			ProxyState: pb.ProxyState{},
+		}))
 	}
 }
 
@@ -334,6 +388,53 @@ func TestRemoveTnReplica(t *testing.T) {
 
 	for i, c := range cases {
 		fmt.Printf("case %v: %s\n", i, c.desc)
-		assert.Equal(t, c.expected, c.command.IsFinish(pb.LogState{}, c.state, pb.CNState{}))
+		assert.Equal(t, c.expected, c.command.IsFinish(ClusterState{
+			LogState:   pb.LogState{},
+			TNState:    c.state,
+			CNState:    pb.CNState{},
+			ProxyState: pb.ProxyState{},
+		}))
+	}
+}
+
+func TestDeleteProxyStore(t *testing.T) {
+	cases := []struct {
+		desc     string
+		command  DeleteProxyStore
+		state    pb.ProxyState
+		expected bool
+	}{
+		{
+			desc: "delete proxy store not finished",
+			command: DeleteProxyStore{
+				StoreID: "a",
+			},
+			state: pb.ProxyState{
+				Stores: map[string]pb.ProxyStore{"a": {
+					UUID: "a",
+				}},
+			},
+			expected: false,
+		},
+		{
+			desc: "delete proxy store finished",
+			command: DeleteProxyStore{
+				StoreID: "a",
+			},
+			state: pb.ProxyState{
+				Stores: map[string]pb.ProxyStore{},
+			},
+			expected: true,
+		},
+	}
+
+	for i, c := range cases {
+		fmt.Printf("case %v: %s\n", i, c.desc)
+		assert.Equal(t, c.expected, c.command.IsFinish(ClusterState{
+			LogState:   pb.LogState{},
+			TNState:    pb.TNState{},
+			CNState:    pb.CNState{},
+			ProxyState: c.state,
+		}))
 	}
 }
diff --git a/pkg/hakeeper/rsm_test.go b/pkg/hakeeper/rsm_test.go
index f04621d33..f9fc7bc75 100644
--- a/pkg/hakeeper/rsm_test.go
+++ b/pkg/hakeeper/rsm_test.go
@@ -421,6 +421,11 @@ func TestClusterDetailsQuery(t *testing.T) {
 			}, ReplicaID: 3,
 		}},
 	}
+	tsm.state.ProxyState.Stores["store-4"] = pb.ProxyStore{
+		UUID:          "store-4",
+		Tick:          100,
+		ListenAddress: "proxy-addr1",
+	}
 
 	v, err := tsm.Lookup(&ClusterDetailsQuery{})
 	require.NoError(t, err)
@@ -492,6 +497,13 @@ func TestClusterDetailsQuery(t *testing.T) {
 				}},
 			},
 		},
+		ProxyStores: []pb.ProxyStore{
+			{
+				UUID:          "store-4",
+				Tick:          100,
+				ListenAddress: "proxy-addr1",
+			},
+		},
 	}
 	result := v.(*pb.ClusterDetails)
 	sort.Slice(result.CNStores, func(i, j int) bool {
@@ -941,3 +953,28 @@ func TestHandleDeleteCNStore(t *testing.T) {
 	s = tsm1.state.CNState
 	assert.Equal(t, 0, len(s.Stores))
 }
+
+func TestHandleProxyHeartbeat(t *testing.T) {
+	tsm1 := NewStateMachine(0, 1).(*stateMachine)
+	cmd := GetTickCmd()
+	_, err := tsm1.Update(sm.Entry{Cmd: cmd})
+	assert.NoError(t, err)
+	_, err = tsm1.Update(sm.Entry{Cmd: cmd})
+	assert.NoError(t, err)
+	_, err = tsm1.Update(sm.Entry{Cmd: cmd})
+	assert.NoError(t, err)
+
+	hb := pb.ProxyHeartbeat{
+		UUID: "uuid1",
+	}
+	data, err := hb.Marshal()
+	require.NoError(t, err)
+	cmd = GetProxyHeartbeatCmd(data)
+	_, err = tsm1.Update(sm.Entry{Cmd: cmd})
+	assert.NoError(t, err)
+	s := tsm1.state.ProxyState
+	assert.Equal(t, 1, len(s.Stores))
+	info, ok := s.Stores[hb.UUID]
+	assert.True(t, ok)
+	assert.Equal(t, uint64(3), info.Tick)
+}
diff --git a/pkg/logservice/hakeeper_client_test.go b/pkg/logservice/hakeeper_client_test.go
index d4e4d4c4e..215293ea7 100644
--- a/pkg/logservice/hakeeper_client_test.go
+++ b/pkg/logservice/hakeeper_client_test.go
@@ -697,3 +697,35 @@ func TestHAKeeperClientDeleteCNStore(t *testing.T) {
 	}
 	runServiceTest(t, true, true, fn)
 }
+
+func TestHAKeeperClientSendProxyHeartbeat(t *testing.T) {
+	fn := func(t *testing.T, s *Service) {
+		cfg := HAKeeperClientConfig{
+			ServiceAddresses: []string{testServiceAddress},
+		}
+		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+		defer cancel()
+		c1, err := NewProxyHAKeeperClient(ctx, cfg)
+		require.NoError(t, err)
+		defer func() {
+			assert.NoError(t, c1.Close())
+		}()
+
+		hb := pb.ProxyHeartbeat{
+			UUID:          s.ID(),
+			ListenAddress: "addr1",
+		}
+		cb, err := c1.SendProxyHeartbeat(ctx, hb)
+		require.NoError(t, err)
+		assert.Equal(t, 0, len(cb.Commands))
+
+		cd, err := c1.GetClusterDetails(ctx)
+		require.NoError(t, err)
+		p := pb.ProxyStore{
+			UUID:          s.ID(),
+			ListenAddress: "addr1",
+		}
+		assert.Equal(t, []pb.ProxyStore{p}, cd.ProxyStores)
+	}
+	runServiceTest(t, true, true, fn)
+}
diff --git a/pkg/logservice/service_test.go b/pkg/logservice/service_test.go
index ff906d0ab..70d71b418 100644
--- a/pkg/logservice/service_test.go
+++ b/pkg/logservice/service_test.go
@@ -1147,3 +1147,20 @@ func TestServiceHandleCNDeleteStore(t *testing.T) {
 	}
 	runServiceTest(t, true, true, fn)
 }
+
+func TestServiceHandleProxyHeartbeat(t *testing.T) {
+	fn := func(t *testing.T, s *Service) {
+		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+		defer cancel()
+		req := pb.Request{
+			Method: pb.PROXY_HEARTBEAT,
+			ProxyHeartbeat: &pb.ProxyHeartbeat{
+				UUID: "uuid1",
+			},
+		}
+		resp := s.handleProxyHeartbeat(ctx, req)
+		assert.Equal(t, &pb.CommandBatch{}, resp.CommandBatch)
+		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
+	}
+	runServiceTest(t, true, true, fn)
+}
diff --git a/pkg/logservice/store_test.go b/pkg/logservice/store_test.go
index a972622b7..a5692395c 100644
--- a/pkg/logservice/store_test.go
+++ b/pkg/logservice/store_test.go
@@ -488,6 +488,12 @@ func TestAddHeartbeat(t *testing.T) {
 		tnMsg.Shards = append(tnMsg.Shards, pb.TNShardInfo{ShardID: 2, ReplicaID: 3})
 		_, err = store.addTNStoreHeartbeat(ctx, tnMsg)
 		assert.NoError(t, err)
+
+		proxyMsg := pb.ProxyHeartbeat{
+			UUID: store.id(),
+		}
+		_, err = store.addProxyHeartbeat(ctx, proxyMsg)
+		assert.NoError(t, err)
 	}
 	runStoreTest(t, fn)
 }
diff --git a/pkg/pb/logservice/logservice_test.go b/pkg/pb/logservice/logservice_test.go
index 39486de01..059b0780e 100644
--- a/pkg/pb/logservice/logservice_test.go
+++ b/pkg/pb/logservice/logservice_test.go
@@ -580,3 +580,33 @@ func TestCNStateLabelPatch(t *testing.T) {
 		},
 	})
 }
+
+func TestProxyStateUpdate(t *testing.T) {
+	state := ProxyState{Stores: map[string]ProxyStore{}}
+
+	hb1 := ProxyHeartbeat{
+		UUID:          "proxy-1",
+		ListenAddress: "addr-a",
+	}
+	tick1 := uint64(100)
+
+	state.Update(hb1, tick1)
+	assert.Equal(t, state.Stores[hb1.UUID], ProxyStore{
+		UUID:          hb1.UUID,
+		Tick:          tick1,
+		ListenAddress: hb1.ListenAddress,
+	})
+
+	hb2 := ProxyHeartbeat{
+		UUID:          "proxy-1",
+		ListenAddress: "addr-a",
+	}
+	tick2 := uint64(200)
+
+	state.Update(hb2, tick2)
+	assert.Equal(t, state.Stores[hb2.UUID], ProxyStore{
+		UUID:          hb1.UUID,
+		Tick:          tick2,
+		ListenAddress: hb1.ListenAddress,
+	})
+}
diff --git a/pkg/proxy/label_info_test.go b/pkg/proxy/label_info_test.go
index 6a2a15b05..0fc868feb 100644
--- a/pkg/proxy/label_info_test.go
+++ b/pkg/proxy/label_info_test.go
@@ -58,20 +58,20 @@ func (c *mockHAKeeperClient) updateCN(uuid string, addr string, labels map[strin
 	c.value.CNStores = append(c.value.CNStores, *cs)
 }
 
-func (c *mockHAKeeperClient) Close() error                                   { return nil }
-func (c *mockHAKeeperClient) AllocateID(ctx context.Context) (uint64, error) { return 0, nil }
-func (c *mockHAKeeperClient) AllocateIDByKey(ctx context.Context, key string) (uint64, error) {
+func (c *mockHAKeeperClient) Close() error                                 { return nil }
+func (c *mockHAKeeperClient) AllocateID(_ context.Context) (uint64, error) { return 0, nil }
+func (c *mockHAKeeperClient) AllocateIDByKey(_ context.Context, _ string) (uint64, error) {
 	return uint64(nextClientConnID()), nil
 }
-func (c *mockHAKeeperClient) AllocateIDByKeyWithBatch(ctx context.Context, key string, batch uint64) (uint64, error) {
+func (c *mockHAKeeperClient) AllocateIDByKeyWithBatch(_ context.Context, _ string, _ uint64) (uint64, error) {
 	return uint64(nextClientConnID()), nil
 }
-func (c *mockHAKeeperClient) GetClusterDetails(ctx context.Context) (logpb.ClusterDetails, error) {
+func (c *mockHAKeeperClient) GetClusterDetails(_ context.Context) (logpb.ClusterDetails, error) {
 	c.RLock()
 	defer c.RUnlock()
 	return c.value, c.err
 }
-func (c *mockHAKeeperClient) GetClusterState(ctx context.Context) (logpb.CheckerState, error) {
+func (c *mockHAKeeperClient) GetClusterState(_ context.Context) (logpb.CheckerState, error) {
 	return logpb.CheckerState{
 		TaskTableUser: logpb.TaskTableUser{
 			Username: "u1",
@@ -80,7 +80,31 @@ func (c *mockHAKeeperClient) GetClusterState(ctx context.Context) (logpb.Checker
 	}, nil
 }
 
-func (c *mockHAKeeperClient) updateCNWorkState(ctx context.Context, state logpb.CNWorkState) error {
+func (c *mockHAKeeperClient) GetCNState(_ context.Context) (logpb.CNState, error) {
+	return logpb.CNState{}, nil
+}
+
+func (c *mockHAKeeperClient) UpdateCNLabel(_ context.Context, _ logpb.CNStoreLabel) error {
+	return nil
+}
+
+func (c *mockHAKeeperClient) UpdateCNWorkState(_ context.Context, _ logpb.CNWorkState) error {
+	return nil
+}
+
+func (c *mockHAKeeperClient) PatchCNStore(_ context.Context, _ logpb.CNStateLabel) error {
+	return nil
+}
+
+func (c *mockHAKeeperClient) DeleteCNStore(_ context.Context, _ logpb.DeleteCNStore) error {
+	return nil
+}
+
+func (c *mockHAKeeperClient) SendProxyHeartbeat(_ context.Context, _ logpb.ProxyHeartbeat) (logpb.CommandBatch, error) {
+	return logpb.CommandBatch{}, nil
+}
+
+func (c *mockHAKeeperClient) updateCNWorkState(_ context.Context, state logpb.CNWorkState) error {
 	c.Lock()
 	defer c.Unlock()
 	for i := range c.value.CNStores {
diff --git a/pkg/proxy/server_test.go b/pkg/proxy/server_test.go
index a72959f16..9e35d42b5 100644
--- a/pkg/proxy/server_test.go
+++ b/pkg/proxy/server_test.go
@@ -27,9 +27,9 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func WithHAKeeperClient(c logservice.ClusterHAKeeperClient) Option {
+func WithHAKeeperClient(c logservice.ProxyHAKeeperClient) Option {
 	return func(s *Server) {
-		s.testHAKeeperClient = c
+		s.haKeeperClient = c
 	}
 }